From 9c3a8f9a44b4cfc4d35fc107f944b84556fc1ddb Mon Sep 17 00:00:00 2001 From: Yauhen Kirylau Date: Tue, 29 Dec 2015 01:50:06 +0100 Subject: [PATCH 0001/1329] chore(readme): add the link to arch linux aur --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 03a7e5d2..0024a553 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,12 @@ This README is being written. (TODO write some notes on make variables and cross-compiling) +## Installation + +#### Arch Linux + +Can be built from AUR: https://aur.archlinux.org/packages/libui-git/ + ## Documentation Needs to be written. Consult ui.h and the examples for details for now. From 34259636065e5bbe1969890578b6c14f65371104 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 13 May 2016 13:36:27 -0400 Subject: [PATCH 0002/1329] Updated the darwin screenshot with our new Auto Layout fixes. --- examples/controlgallery/darwin.png | Bin 110552 -> 137948 bytes 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 examples/controlgallery/darwin.png diff --git a/examples/controlgallery/darwin.png b/examples/controlgallery/darwin.png old mode 100755 new mode 100644 index 99d0e8b0bc88f5419ca57ec01059025dfeab67d6..6514507f595e25dc5c539f5b8d91c085c7f34575 GIT binary patch delta 132388 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

IRv!Vus4Nwv3UC6EO{ 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 FV1oF}f-e97 From 03337f61f1debec2854091c85339e3134599410b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 13 May 2016 15:13:41 -0400 Subject: [PATCH 0003/1329] Fixed Visual Studio 2015 builds. --- build/GNUbasemsvc.mk | 5 +++-- windows/areadraw.cpp | 1 + windows/d2dscratch.cpp | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/build/GNUbasemsvc.mk b/build/GNUbasemsvc.mk index 69bcbe8c..1ef6b911 100644 --- a/build/GNUbasemsvc.mk +++ b/build/GNUbasemsvc.mk @@ -26,12 +26,13 @@ # TODO loads of warnings in the system header files # TODO /analyze requires us to write annotations everywhere # TODO undecided flags from qo? +# -RTCc is not supplied because it's discouraged as of VS2015; see https://www.reddit.com/r/cpp/comments/46mhne/rtcc_rejects_conformant_code_with_visual_c_2015/d06auq5 CFLAGS += \ -W4 \ -wd4100 \ -TC \ -bigobj -nologo \ - -RTC1 -RTCc -RTCs -RTCu + -RTC1 -RTCs -RTCu # TODO prune these # -EHsc is to shut the compiler up in some cases @@ -40,7 +41,7 @@ CXXFLAGS += \ -wd4100 \ -TP \ -bigobj -nologo \ - -RTC1 -RTCc -RTCs -RTCu \ + -RTC1 -RTCs -RTCu \ -EHsc # TODO warnings on undefined symbols diff --git a/windows/areadraw.cpp b/windows/areadraw.cpp index 90a8fe28..77e9aeaf 100644 --- a/windows/areadraw.cpp +++ b/windows/areadraw.cpp @@ -44,6 +44,7 @@ static HRESULT doPaint(uiArea *a, ID2D1RenderTarget *rt, RECT *clip) bgcolor.r = ((float) GetRValue(bgcolorref)) / 255.0; // due to utter apathy on Microsoft's part, GetGValue() does not work with MSVC's Run-Time Error Checks // it has not worked since 2008 and they have *never* fixed it + // TODO now that -RTCc has just been deprecated entirely, should we switch back? bgcolor.g = ((float) ((BYTE) ((bgcolorref & 0xFF00) >> 8))) / 255.0; bgcolor.b = ((float) GetBValue(bgcolorref)) / 255.0; bgcolor.a = 1.0; diff --git a/windows/d2dscratch.cpp b/windows/d2dscratch.cpp index 06f49a41..f9504317 100644 --- a/windows/d2dscratch.cpp +++ b/windows/d2dscratch.cpp @@ -26,6 +26,7 @@ static HRESULT d2dScratchDoPaint(HWND hwnd, ID2D1RenderTarget *rt) bgcolor.r = ((float) GetRValue(bgcolorref)) / 255.0; // due to utter apathy on Microsoft's part, GetGValue() does not work with MSVC's Run-Time Error Checks // it has not worked since 2008 and they have *never* fixed it + // TODO now that -RTCc has just been deprecated entirely, should we switch back? bgcolor.g = ((float) ((BYTE) ((bgcolorref & 0xFF00) >> 8))) / 255.0; bgcolor.b = ((float) GetBValue(bgcolorref)) / 255.0; bgcolor.a = 1.0; From 94587b660f8847b60503c13371aa10812b25f136 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 13 May 2016 17:54:10 -0400 Subject: [PATCH 0004/1329] Some TODO resolution and stale TODO removal. --- common/matrix.c | 2 +- common/uipriv.h | 1 - darwin/button.m | 2 -- darwin/checkbox.m | 6 ------ darwin/draw.m | 6 ------ darwin/group.m | 2 -- darwin/label.m | 2 -- darwin/radiobuttons.m | 2 -- darwin/uipriv_darwin.h | 3 --- unix/drawmatrix.c | 5 ----- windows/drawmatrix.cpp | 5 ----- 11 files changed, 1 insertion(+), 35 deletions(-) diff --git a/common/matrix.c b/common/matrix.c index 7af2eb07..ce802d3c 100644 --- a/common/matrix.c +++ b/common/matrix.c @@ -3,7 +3,7 @@ #include "../ui.h" #include "uipriv.h" -void setIdentity(uiDrawMatrix *m) +void uiDrawMatrixSetIdentity(uiDrawMatrix *m) { m->M11 = 1; m->M12 = 0; diff --git a/common/uipriv.h b/common/uipriv.h index c8b6b6f0..673f7a36 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -40,7 +40,6 @@ extern void clickCounterReset(clickCounter *); extern int fromScancode(uintptr_t, uiAreaKeyEvent *); // matrix.c -extern void setIdentity(uiDrawMatrix *); extern void fallbackSkew(uiDrawMatrix *, double, double, double, double); extern void fallbackTranslate(uiDrawMatrix *, double, double); extern void scaleCenter(double, double, double *, double *); diff --git a/darwin/button.m b/darwin/button.m index 01ba55f8..be45dba9 100644 --- a/darwin/button.m +++ b/darwin/button.m @@ -76,8 +76,6 @@ char *uiButtonText(uiButton *b) void uiButtonSetText(uiButton *b, const char *text) { [b->button setTitle:toNSString(text)]; - // this may result in the size of the button changing - uiDarwinControlTriggerRelayout(uiDarwinControl(b)); } void uiButtonOnClicked(uiButton *b, void (*f)(uiButton *, void *), void *data) diff --git a/darwin/checkbox.m b/darwin/checkbox.m index 73aab165..fea82abb 100644 --- a/darwin/checkbox.m +++ b/darwin/checkbox.m @@ -1,8 +1,6 @@ // 14 august 2015 #import "uipriv_darwin.h" -// TODO the intrinsic height of this seems to be wacky - struct uiCheckbox { uiDarwinControl c; NSButton *button; @@ -78,10 +76,6 @@ char *uiCheckboxText(uiCheckbox *c) void uiCheckboxSetText(uiCheckbox *c, const char *text) { [c->button setTitle:toNSString(text)]; - // this may result in the size of the checkbox changing - // TODO something somewhere is causing this to corrupt some memory so that, for instance, page7b's mouseExited: never triggers on 10.11; figure out what - // TODO is this related to map-related crashes? - uiDarwinControlTriggerRelayout(uiDarwinControl(c)); } void uiCheckboxOnToggled(uiCheckbox *c, void (*f)(uiCheckbox *, void *), void *data) diff --git a/darwin/draw.m b/darwin/draw.m index c94c073b..a147283c 100644 --- a/darwin/draw.m +++ b/darwin/draw.m @@ -312,12 +312,6 @@ static void c2m(CGAffineTransform *c, uiDrawMatrix *m) m->M32 = c->ty; } -// TODO get rid of the separate setIdentity() -void uiDrawMatrixSetIdentity(uiDrawMatrix *m) -{ - setIdentity(m); -} - void uiDrawMatrixTranslate(uiDrawMatrix *m, double x, double y) { CGAffineTransform c; diff --git a/darwin/group.m b/darwin/group.m index 6a2cbe62..e20171d4 100644 --- a/darwin/group.m +++ b/darwin/group.m @@ -127,8 +127,6 @@ char *uiGroupTitle(uiGroup *g) void uiGroupSetTitle(uiGroup *g, const char *title) { [g->box setTitle:toNSString(title)]; - // changing the text might necessitate a change in the groupbox's size - uiDarwinControlTriggerRelayout(uiDarwinControl(g)); } void uiGroupSetChild(uiGroup *g, uiControl *child) diff --git a/darwin/label.m b/darwin/label.m index 3e90a5f8..e2b9d540 100644 --- a/darwin/label.m +++ b/darwin/label.m @@ -16,8 +16,6 @@ char *uiLabelText(uiLabel *l) void uiLabelSetText(uiLabel *l, const char *text) { [l->textfield setStringValue:toNSString(text)]; - // changing the text might necessitate a change in the label's size - uiDarwinControlTriggerRelayout(uiDarwinControl(l)); } uiLabel *uiNewLabel(const char *text) diff --git a/darwin/radiobuttons.m b/darwin/radiobuttons.m index 633c1494..59218ad8 100644 --- a/darwin/radiobuttons.m +++ b/darwin/radiobuttons.m @@ -34,8 +34,6 @@ void uiRadioButtonsAppend(uiRadioButtons *r, const char *text) [r->matrix setAllowsEmptySelection:YES]; [r->matrix selectCellAtRow:prevSelection column:0]; [r->matrix setAllowsEmptySelection:NO]; - - uiDarwinControlTriggerRelayout(uiDarwinControl(r)); } uiRadioButtons *uiNewRadioButtons(void) diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 028ae8a2..effbfbd4 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -106,6 +106,3 @@ extern void doDrawText(CGContextRef c, CGFloat cheight, double x, double y, uiDr // fontbutton.m extern BOOL fontButtonInhibitSendAction(SEL sel, id from, id to); extern BOOL fontButtonOverrideTargetForAction(SEL sel, id from, id to, id *override); - -// MASSIVE TODO -#define uiDarwinControlTriggerRelayout(...) diff --git a/unix/drawmatrix.c b/unix/drawmatrix.c index 815caf49..807b60d9 100644 --- a/unix/drawmatrix.c +++ b/unix/drawmatrix.c @@ -22,11 +22,6 @@ static void c2m(cairo_matrix_t *c, uiDrawMatrix *m) m->M32 = c->y0; } -void uiDrawMatrixSetIdentity(uiDrawMatrix *m) -{ - setIdentity(m); -} - void uiDrawMatrixTranslate(uiDrawMatrix *m, double x, double y) { cairo_matrix_t c; diff --git a/windows/drawmatrix.cpp b/windows/drawmatrix.cpp index e2f29d1d..807b41ad 100644 --- a/windows/drawmatrix.cpp +++ b/windows/drawmatrix.cpp @@ -22,11 +22,6 @@ static void d2m(D2D1_MATRIX_3X2_F *d, uiDrawMatrix *m) m->M32 = d->_32; } -void uiDrawMatrixSetIdentity(uiDrawMatrix *m) -{ - setIdentity(m); -} - void uiDrawMatrixTranslate(uiDrawMatrix *m, double x, double y) { D2D1_MATRIX_3X2_F dm; From 329fff82f6819beab7ea834b8ecb715bc98f4341 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 13 May 2016 18:10:43 -0400 Subject: [PATCH 0005/1329] Some more fixups and cleanups, especially in the matrix code. --- common/matrix.c | 79 +++++++-------------------------------------- common/uipriv.h | 6 ---- darwin/autolayout.m | 1 - 3 files changed, 12 insertions(+), 74 deletions(-) diff --git a/common/matrix.c b/common/matrix.c index ce802d3c..676885d1 100644 --- a/common/matrix.c +++ b/common/matrix.c @@ -13,34 +13,22 @@ void uiDrawMatrixSetIdentity(uiDrawMatrix *m) m->M32 = 0; } -// TODO don't default to fallback functions within the fallback functions +// The rest of this file provides basic utilities in case the platform doesn't provide any of its own for these tasks. +// Keep these as minimal as possible. They should generally not call other fallbacks. // see https://msdn.microsoft.com/en-us/library/windows/desktop/ff684171%28v=vs.85%29.aspx#skew_transform -// TODO if Windows 7 is ever dropped change this so we can pass in D2D1Tan() +// TODO see if there's a way we can avoid the multiplication void fallbackSkew(uiDrawMatrix *m, double x, double y, double xamount, double yamount) { uiDrawMatrix n; - setIdentity(&n); + uiDrawMatrixSetIdentity(&n); // TODO explain this n.M12 = tan(yamount); n.M21 = tan(xamount); n.M31 = -y * tan(xamount); n.M32 = -x * tan(yamount); - fallbackMultiply(m, &n); -} - -// see windows/draw.c for more information -// TODO we don't need to do this if we can bypass the multiplication somehow - -void fallbackTranslate(uiDrawMatrix *m, double x, double y) -{ - uiDrawMatrix m2; - - setIdentity(&m2); - m2.M31 = x; - m2.M32 = y; - fallbackMultiply(m, &m2); + uiDrawMatrixMultiply(m, &n); } void scaleCenter(double xCenter, double yCenter, double *x, double *y) @@ -49,57 +37,14 @@ void scaleCenter(double xCenter, double yCenter, double *x, double *y) *y = yCenter - (*y * yCenter); } -void fallbackScale(uiDrawMatrix *m, double xCenter, double yCenter, double x, double y) +// the basic algorithm is from cairo +// but it's the same algorithm as the transform point, just without M31 and M32 taken into account, so let's just do that instead +void fallbackTransformSize(uiDrawMatrix *m, double *x, double *y) { uiDrawMatrix m2; - setIdentity(&m2); - m2.M11 = x; - m2.M22 = y; - scaleCenter(xCenter, yCenter, &x, &y); - m2.M31 = x; - m2.M32 = y; - fallbackMultiply(m, &m2); -} - -void fallbackMultiply(uiDrawMatrix *dest, uiDrawMatrix *src) -{ - uiDrawMatrix out; - - out.M11 = (dest->M11 * src->M11) + - (dest->M12 * src->M21); - out.M12 = (dest->M11 * src->M12) + - (dest->M12 * src->M22); - out.M21 = (dest->M21 * src->M11) + - (dest->M22 * src->M21); - out.M22 = (dest->M21 * src->M12) + - (dest->M22 * src->M22); - out.M31 = (dest->M31 * src->M11) + - (dest->M32 * src->M21) + - src->M31; - out.M32 = (dest->M31 * src->M12) + - (dest->M32 * src->M22) + - src->M32; - *dest = out; -} - -void fallbackTransformPoint(uiDrawMatrix *m, double *x, double *y) -{ - double xout, yout; - - xout = (*x * m->M11) + (*y * m->M21) + m->M31; - yout = (*x * m->M12) + (*y * m->M22) + m->M32; - *x = xout; - *y = yout; -} - -// and this algorithm is according to cairo -void fallbackTransformSize(uiDrawMatrix *m, double *x, double *y) -{ - double xout, yout; - - xout = (*x * m->M11) + (*y * m->M21); - yout = (*x * m->M12) + (*y * m->M22); - *x = xout; - *y = yout; + m2 = *m; + m2.M31 = 0; + m2.M32 = 0; + uiDrawMatrixTransformPoint(&m2, x, y); } diff --git a/common/uipriv.h b/common/uipriv.h index 673f7a36..37496384 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -3,8 +3,6 @@ extern "C" { #endif -// TODO stdlib.h needed? -#include #include "controlsigs.h" extern uiInitOptions options; @@ -41,11 +39,7 @@ extern int fromScancode(uintptr_t, uiAreaKeyEvent *); // matrix.c extern void fallbackSkew(uiDrawMatrix *, double, double, double, double); -extern void fallbackTranslate(uiDrawMatrix *, double, double); extern void scaleCenter(double, double, double *, double *); -extern void fallbackScale(uiDrawMatrix *, double, double, double, double); -extern void fallbackMultiply(uiDrawMatrix *, uiDrawMatrix *); -extern void fallbackTransformPoint(uiDrawMatrix *, double *, double *); extern void fallbackTransformSize(uiDrawMatrix *, double *, double *); #ifdef __cplusplus diff --git a/darwin/autolayout.m b/darwin/autolayout.m index 1b1203b7..19bb26ad 100644 --- a/darwin/autolayout.m +++ b/darwin/autolayout.m @@ -37,7 +37,6 @@ static CGFloat margins(int margined) void singleChildConstraintsEstablish(struct singleChildConstraints *c, NSView *contentView, NSView *childView, BOOL hugsTrailing, BOOL hugsBottom, int margined, NSString *desc) { CGFloat margin; - NSLayoutRelation relation; margin = margins(margined); From a4f9d082815871231d056ded1c2296c788b09606 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 13 May 2016 18:27:08 -0400 Subject: [PATCH 0006/1329] Began replacing complain() with the more appropriate implbug() and userbug(). --- common/uipriv.h | 5 ++++- windows/uipriv_windows.hpp | 2 -- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/common/uipriv.h b/common/uipriv.h index 37496384..8bffda4e 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -12,7 +12,10 @@ extern void *uiAlloc(size_t, const char *); extern void *uiRealloc(void *, size_t, const char *); extern void uiFree(void *); -extern void complain(const char *, ...); +extern void _implbug(const char *file, const char *line, const char *func, const char *format, ...); +#define implbug(...) _implbug(__FILE__, #__LINE__, __func__, __VA_ARGS__) +extern void _userbug(const char *file, const char *line, const char *func, const char *format, ...); +#define userbug(...) _implbug(__FILE__, #__LINE__, __func__, __VA_ARGS__) // control.c extern uiControl *newControl(size_t size, uint32_t OSsig, uint32_t typesig, const char *typenamestr); diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index 984ed0af..4f66bea5 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -53,8 +53,6 @@ extern HRESULT _logLastError(debugargs, const WCHAR *s); #define logLastError(s) _logLastError(_ws(__FILE__), _wsn(__LINE__), _ws(__FUNCTION__), s) extern HRESULT _logHRESULT(debugargs, const WCHAR *s, HRESULT hr); #define logHRESULT(s, hr) _logHRESULT(_ws(__FILE__), _wsn(__LINE__), _ws(__FUNCTION__), s, hr) -extern void _implbug(debugargs, const WCHAR *format, ...); -#define implbug(...) _implbug(_ws(__FILE__), _wsn(__LINE__), _ws(__FUNCTION__), __VA_ARGS__) // winutil.cpp extern int windowClassOf(HWND hwnd, ...); From 1fede348ef823792783a345521ed3bc19ace6280 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 13 May 2016 19:46:59 -0400 Subject: [PATCH 0007/1329] More complain() migration; common/* handled. --- common/control.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/common/control.c b/common/control.c index d6da78bf..64da9e03 100644 --- a/common/control.c +++ b/common/control.c @@ -75,12 +75,10 @@ void uiFreeControl(uiControl *c) uiFree(c); } -// TODO except where noted, replace complain() with userbug() - void uiControlVerifyDestroy(uiControl *c) { if (uiControlParent(c) != NULL) - complain("attempt to destroy uiControl %p while it has a parent", c); + userbug("You cannot destroy a uiControl while it still has a parent. (control: %p)", c); } void uiControlVerifySetParent(uiControl *c, uiControl *parent) @@ -88,13 +86,12 @@ void uiControlVerifySetParent(uiControl *c, uiControl *parent) uiControl *curParent; if (uiControlToplevel(c)) - complain("cannot set a parent on a toplevel (uiWindow)"); + userbug("You cannot give a toplevel uiControl a parent. (control: %p)", c); curParent = uiControlParent(c); if (parent != NULL && curParent != NULL) - complain("attempt to reparent uiControl %p (has parent %p, attempt to give parent %p)", c, curParent, parent); + userbug("You cannot give a uiControl a parent while it already has one. (control: %p; current parent: %p; new parent: %p)", c, curParent, parent); if (parent == NULL && curParent == NULL) - // TODO implbug() - complain("attempt to double unparent uiControl %p — likely an implementation bug ", c); + implbug("attempt to double unparent uiControl %p", c); } int uiControlEnabledToUser(uiControl *c) From d52c92d2f86e2fb2a353eb147584dddc939a1d4d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 13 May 2016 20:14:46 -0400 Subject: [PATCH 0008/1329] Cleaned out complain()s in the OS X backend. Affects everything *except* drawtext.m, which will need its own migration. --- common/uipriv.h | 6 ++++-- darwin/GNUfiles.mk | 1 + darwin/alloc.m | 16 ++++++++-------- darwin/area.m | 4 ++-- darwin/debug.m | 37 +++++++++++++++++++++++++++++++++++++ darwin/draw.m | 26 +++++++++++++++----------- darwin/drawtext.m | 5 ++++- darwin/main.m | 2 +- darwin/map.m | 2 +- darwin/menu.m | 12 ++++++------ darwin/progressbar.m | 2 +- darwin/spinbox.m | 3 ++- darwin/util.m | 12 ------------ 13 files changed, 82 insertions(+), 46 deletions(-) create mode 100644 darwin/debug.m diff --git a/common/uipriv.h b/common/uipriv.h index 8bffda4e..a36be4eb 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -12,10 +12,12 @@ extern void *uiAlloc(size_t, const char *); extern void *uiRealloc(void *, size_t, const char *); extern void uiFree(void *); +#define _ns2(s) #s +#define _ns(s) _ns2(s) extern void _implbug(const char *file, const char *line, const char *func, const char *format, ...); -#define implbug(...) _implbug(__FILE__, #__LINE__, __func__, __VA_ARGS__) +#define implbug(...) _implbug(__FILE__, _ns(__LINE__), __func__, __VA_ARGS__) extern void _userbug(const char *file, const char *line, const char *func, const char *format, ...); -#define userbug(...) _implbug(__FILE__, #__LINE__, __func__, __VA_ARGS__) +#define userbug(...) _implbug(__FILE__, _ns(__LINE__), __func__, __VA_ARGS__) // control.c extern uiControl *newControl(size_t size, uint32_t OSsig, uint32_t typesig, const char *typenamestr); diff --git a/darwin/GNUfiles.mk b/darwin/GNUfiles.mk index 49acd1d4..85d89615 100644 --- a/darwin/GNUfiles.mk +++ b/darwin/GNUfiles.mk @@ -13,6 +13,7 @@ MFILES += \ darwin/datetimepicker.m \ darwin/draw.m \ darwin/drawtext.m \ + darwin/debug.m \ darwin/entry.m \ darwin/fontbutton.m \ darwin/group.m \ diff --git a/darwin/alloc.m b/darwin/alloc.m index 5fd42f3b..9f0531ba 100644 --- a/darwin/alloc.m +++ b/darwin/alloc.m @@ -22,7 +22,9 @@ void initAlloc(void) void uninitAlloc(void) { + NSMutableString *str; NSUInteger i; + NSValue *v; // delegates might have mapTables allocated // TODO verify they are empty @@ -33,16 +35,14 @@ void uninitAlloc(void) [allocations release]; return; } - fprintf(stderr, "[libui] leaked allocations:\n"); - [allocations enumerateObjectsUsingBlock:^(id obj, NSUInteger index, BOOL *stop) { - NSValue *v; + str = [NSMutableString new]; + for (v in allocations) { void *ptr; - v = (NSValue *) obj; ptr = [v pointerValue]; - fprintf(stderr, "[libui] %p %s\n", ptr, *TYPE(ptr)); - }]; - complain("either you left something around or there's a bug in libui"); + [str appendString:[NSString stringWithFormat:@"%p %s\n", ptr, *TYPE(ptr)]]; + } + userbug("Some data was leaked; either you left a uiControl lying around or there's a bug in libui itself. Leaked data:\n%s", [str UTF8String]); } void *uiAlloc(size_t size, const char *type) @@ -86,7 +86,7 @@ void *uiRealloc(void *p, size_t new, const char *type) void uiFree(void *p) { if (p == NULL) - complain("attempt to uiFree(NULL); there's a bug somewhere"); + implbug("attempt to uiFree(NULL)"); p = BASE(p); free(p); [allocations removeObject:[NSValue valueWithPointer:p]]; diff --git a/darwin/area.m b/darwin/area.m index 72aee13c..ada4ae6d 100644 --- a/darwin/area.m +++ b/darwin/area.m @@ -335,7 +335,7 @@ int sendAreaEvents(NSEvent *e) void uiAreaSetSize(uiArea *a, intmax_t width, intmax_t height) { if (!a->scrolling) - complain("attempt to call uiAreaSetSize() on a non-scrolling uiArea"); + userbug("You cannot call uiAreaSetSize() on a non-scrolling uiArea. (uiArea: %p)", a); [a->area setFrameSize:NSMakeSize(width, height)]; } @@ -347,7 +347,7 @@ void uiAreaQueueRedrawAll(uiArea *a) void uiAreaScrollTo(uiArea *a, double x, double y, double width, double height) { if (!a->scrolling) - complain("attempt to call uiAreaScrollTo() on a non-scrolling uiArea"); + userbug("You cannot call uiAreaScrollTo() on a non-scrolling uiArea. (uiArea: %p)", a); [a->area scrollRectToVisible:NSMakeRect(x, y, width, height)]; // don't worry about the return value; it just says whether scrolling was needed } diff --git a/darwin/debug.m b/darwin/debug.m new file mode 100644 index 00000000..ec55a84b --- /dev/null +++ b/darwin/debug.m @@ -0,0 +1,37 @@ +// 13 may 2016 +#import "uipriv_darwin.h" + +// TODO don't halt on release builds + +static void bug(const char *file, const char *line, const char *func, const char *prefix, const char *format, va_list ap) +{ + NSMutableString *str; + NSString *formatted; + + str = [NSMutableString new]; + [str appendString:[NSString stringWithFormat:@"[libui] %s:%s:%s %s", file, line, func, prefix]]; + formatted = [[NSString alloc] initWithFormat:[NSString stringWithUTF8String:format] arguments:ap]; + [str appendString:formatted]; + [formatted release]; + NSLog(@"%@", str); + [str release]; + __builtin_trap(); +} + +void _implbug(const char *file, const char *line, const char *func, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + bug(file, line, func, "POSSIBLE IMPLEMENTATION BUG; CONTACT ANDLABS:\n", format, ap); + va_end(ap); +} + +void _userbug(const char *file, const char *line, const char *func, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + bug(file, line, func, "You have a bug: ", format, ap); + va_end(ap); +} diff --git a/darwin/draw.m b/darwin/draw.m index a147283c..6661d6a1 100644 --- a/darwin/draw.m +++ b/darwin/draw.m @@ -26,7 +26,7 @@ void uiDrawFreePath(uiDrawPath *p) void uiDrawPathNewFigure(uiDrawPath *p, double x, double y) { if (p->ended) - complain("attempt to add figure to ended path in uiDrawPathNewFigure()"); + userbug("You cannot call uiDrawPathNewFigure() on a uiDrawPath that has already been ended. (path; %p)", p); CGPathMoveToPoint(p->path, NULL, x, y); } @@ -36,7 +36,7 @@ void uiDrawPathNewFigureWithArc(uiDrawPath *p, double xCenter, double yCenter, d double startx, starty; if (p->ended) - complain("attempt to add figure to ended path in uiDrawPathNewFigureWithArc()"); + userbug("You cannot call uiDrawPathNewFigureWithArc() on a uiDrawPath that has already been ended. (path; %p)", p); sinStart = sin(startAngle); cosStart = cos(startAngle); startx = xCenter + radius * cosStart; @@ -47,8 +47,9 @@ void uiDrawPathNewFigureWithArc(uiDrawPath *p, double xCenter, double yCenter, d void uiDrawPathLineTo(uiDrawPath *p, double x, double y) { + // TODO refine this to require being in a path if (p->ended) - complain("attempt to add line to ended path in uiDrawPathLineTo()"); + implbug("attempt to add line to ended path in uiDrawPathLineTo()"); CGPathAddLineToPoint(p->path, NULL, x, y); } @@ -56,8 +57,9 @@ void uiDrawPathArcTo(uiDrawPath *p, double xCenter, double yCenter, double radiu { bool cw; + // TODO likewise if (p->ended) - complain("attempt to add arc to ended path in uiDrawPathArcTo()"); + implbug("attempt to add arc to ended path in uiDrawPathArcTo()"); if (sweep > 2 * M_PI) sweep = 2 * M_PI; cw = false; @@ -72,8 +74,9 @@ void uiDrawPathArcTo(uiDrawPath *p, double xCenter, double yCenter, double radiu void uiDrawPathBezierTo(uiDrawPath *p, double c1x, double c1y, double c2x, double c2y, double endX, double endY) { + // TODO likewise if (p->ended) - complain("attempt to add bezier to ended path in uiDrawPathBezierTo()"); + implbug("attempt to add bezier to ended path in uiDrawPathBezierTo()"); CGPathAddCurveToPoint(p->path, NULL, c1x, c1y, c2x, c2y, @@ -82,15 +85,16 @@ void uiDrawPathBezierTo(uiDrawPath *p, double c1x, double c1y, double c2x, doubl void uiDrawPathCloseFigure(uiDrawPath *p) { + // TODO likewise if (p->ended) - complain("attempt to close figure of ended path in uiDrawPathCloseFigure()"); + implbug("attempt to close figure of ended path in uiDrawPathCloseFigure()"); CGPathCloseSubpath(p->path); } void uiDrawPathAddRectangle(uiDrawPath *p, double x, double y, double width, double height) { if (p->ended) - complain("attempt to add rectangle to ended path in uiDrawPathAddRectangle()"); + userbug("You cannot call uiDrawPathAddRectangle() on a uiDrawPath that has already been ended. (path; %p)", p); CGPathAddRect(p->path, NULL, CGRectMake(x, y, width, height)); } @@ -132,7 +136,7 @@ void uiDrawStroke(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b, uiDrawStro uiDrawPath p2; if (!path->ended) - complain("path not ended in uiDrawStroke()"); + userbug("You cannot call uiDrawStroke() on a uiDrawPath that has not been ended. (path: %p)", path); switch (p->Cap) { case uiDrawLineCapFlat: @@ -275,7 +279,7 @@ static void fillGradient(CGContextRef ctxt, uiDrawPath *p, uiDrawBrush *b) void uiDrawFill(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b) { if (!path->ended) - complain("path not ended in uiDrawFill()"); + userbug("You cannot call uiDrawStroke() on a uiDrawPath that has not been ended. (path: %p)", path); CGContextAddPath(c->c, (CGPathRef) (path->path)); switch (b->Type) { case uiDrawBrushTypeSolid: @@ -289,7 +293,7 @@ void uiDrawFill(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b) // TODO return; } - complain("unknown brush type %d in uiDrawFill()", b->Type); + userbug("Unknown brush type %d passed to uiDrawFill().", b->Type); } static void m2c(uiDrawMatrix *m, CGAffineTransform *c) @@ -421,7 +425,7 @@ void uiDrawTransform(uiDrawContext *c, uiDrawMatrix *m) void uiDrawClip(uiDrawContext *c, uiDrawPath *path) { if (!path->ended) - complain("path not ended in uiDrawClip()"); + userbug("You cannot call uiDrawCilp() on a uiDrawPath that has not been ended. (path: %p)", path); CGContextAddPath(c->c, (CGPathRef) (path->path)); switch (path->fillMode) { case uiDrawFillModeWinding: diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 86fcb753..dbbe4db6 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -1,6 +1,9 @@ // 6 september 2015 #import "uipriv_darwin.h" +// TODO +#define complain(...) implbug(__VA_ARGS__) + // TODO for all relevant routines, make sure we are freeing memory correctly // TODO make sure allocation failures throw exceptions? struct uiDrawFontFamilies { @@ -15,7 +18,7 @@ uiDrawFontFamilies *uiDrawListFontFamilies(void) // TODO is there a way to get an error reason? ff->fonts = CTFontManagerCopyAvailableFontFamilyNames(); if (ff->fonts == NULL) - complain("error getting available font names (no reason specified)"); + implbug("error getting available font names (no reason specified) (TODO)"); return ff; } diff --git a/darwin/main.m b/darwin/main.m index 07db4459..e40c0616 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -48,7 +48,7 @@ static BOOL canQuit = NO; NSEvent *e; if (!canQuit) - complain("call to [NSApp terminate:] when not ready to terminate"); + implbug("call to [NSApp terminate:] when not ready to terminate; definitely contact andlabs"); [realNSApp() stop:realNSApp()]; // stop: won't register until another event has passed; let's synthesize one diff --git a/darwin/map.m b/darwin/map.m index 9fa2de96..67b5edb6 100644 --- a/darwin/map.m +++ b/darwin/map.m @@ -22,7 +22,7 @@ struct mapTable *newMap(void) void mapDestroy(struct mapTable *m) { if ([m->m count] != 0) - complain("attempt to destroy map with items inside; did you forget to deallocate something?"); + implbug("attempt to destroy map with items inside"); [m->m release]; uiFree(m); } diff --git a/darwin/menu.m b/darwin/menu.m index 49bf2cdc..0f33df1d 100644 --- a/darwin/menu.m +++ b/darwin/menu.m @@ -69,17 +69,17 @@ enum { switch (smi->type) { case typeQuit: if (self->hasQuit) - complain("attempt to add multiple Quit menu items"); + userbug("You can't have multiple Quit menu items in one program."); self->hasQuit = YES; break; case typePreferences: if (self->hasPreferences) - complain("attempt to add multiple Preferences menu items"); + userbug("You can't have multiple Preferences menu items in one program."); self->hasPreferences = YES; break; case typeAbout: if (self->hasAbout) - complain("attempt to add multiple About menu items"); + userbug("You can't have multiple About menu items in one program."); self->hasAbout = YES; break; } @@ -200,7 +200,7 @@ void uiMenuItemDisable(uiMenuItem *item) void uiMenuItemOnClicked(uiMenuItem *item, void (*f)(uiMenuItem *, uiWindow *, void *), void *data) { if (item->type == typeQuit) - complain("attempt to call uiMenuItemOnClicked() on a Quit item; use uiOnShouldQuit() instead"); + userbug("You can't call uiMenuItemOnClicked() on a Quit item; use uiOnShouldQuit() instead."); item->onClicked = f; item->onClickedData = data; } @@ -225,7 +225,7 @@ static uiMenuItem *newItem(uiMenu *m, int type, const char *name) uiMenuItem *item; if (menusFinalized) - complain("attempt to create a new menu item after menus have been finalized"); + userbug("You can't create a new menu item after menus have been finalized."); item = uiNew(uiMenuItem); @@ -297,7 +297,7 @@ uiMenu *uiNewMenu(const char *name) uiMenu *m; if (menusFinalized) - complain("attempt to create a new menu after menus have been finalized"); + userbug("You can't create a new menu after menus have been finalized."); if (menus == nil) menus = [NSMutableArray new]; diff --git a/darwin/progressbar.m b/darwin/progressbar.m index bb64ab2e..d8c0b060 100644 --- a/darwin/progressbar.m +++ b/darwin/progressbar.m @@ -11,7 +11,7 @@ uiDarwinControlAllDefaults(uiProgressBar, pi) void uiProgressBarSetValue(uiProgressBar *p, int value) { if (value < 0 || value > 100) - complain("value %d out of range in progressbarSetValue()", value); + userbug("Value %d out of range for a uiProgressBar.", value); // on 10.8 there's an animation when the progress bar increases, just like with Aero if (value == 100) { [p->pi setMaxValue:101]; diff --git a/darwin/spinbox.m b/darwin/spinbox.m index 409bd5e0..5817efae 100644 --- a/darwin/spinbox.m +++ b/darwin/spinbox.m @@ -183,8 +183,9 @@ uiSpinbox *uiNewSpinbox(intmax_t min, intmax_t max) { uiSpinbox *s; + // TODO implicitly swap instead? if (min >= max) - complain("error: min >= max in uiNewSpinbox()"); + userbug("min >= max is invalid for a uiSpinbox."); uiDarwinNewControl(uiSpinbox, s); diff --git a/darwin/util.m b/darwin/util.m index fa6f5994..7031e9f8 100644 --- a/darwin/util.m +++ b/darwin/util.m @@ -13,15 +13,3 @@ void disableAutocorrect(NSTextView *tv) [tv setAutomaticLinkDetectionEnabled:NO]; [tv setSmartInsertDeleteEnabled:NO]; } - -void complain(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - fprintf(stderr, "[libui] "); - vfprintf(stderr, fmt, ap); - fprintf(stderr, "\n"); - va_end(ap); - abort(); -} From 59eebb1e48bad4ea0c7d51afbc3e04e327408d96 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 13 May 2016 20:20:15 -0400 Subject: [PATCH 0009/1329] Cleaned up the debugging functions slightly. Now to convert the other backends. --- common/GNUfiles.mk | 1 + common/debug.c | 21 +++++++++++++++++++++ common/uipriv.h | 2 ++ darwin/GNUfiles.mk | 2 +- darwin/debug.m | 20 +------------------- 5 files changed, 26 insertions(+), 20 deletions(-) create mode 100644 common/debug.c diff --git a/common/GNUfiles.mk b/common/GNUfiles.mk index 14416f4e..58f901c6 100644 --- a/common/GNUfiles.mk +++ b/common/GNUfiles.mk @@ -3,6 +3,7 @@ CFILES += \ common/areaevents.c \ common/control.c \ + common/debug.c \ common/matrix.c \ common/shouldquit.c diff --git a/common/debug.c b/common/debug.c new file mode 100644 index 00000000..97280b47 --- /dev/null +++ b/common/debug.c @@ -0,0 +1,21 @@ +// 13 may 2016 +#include "../ui.h" +#include "uipriv.h" + +void _implbug(const char *file, const char *line, const char *func, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + realbug(file, line, func, "POSSIBLE IMPLEMENTATION BUG; CONTACT ANDLABS:\n", format, ap); + va_end(ap); +} + +void _userbug(const char *file, const char *line, const char *func, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + realbug(file, line, func, "You have a bug: ", format, ap); + va_end(ap); +} diff --git a/common/uipriv.h b/common/uipriv.h index a36be4eb..e39bcf86 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -3,6 +3,7 @@ extern "C" { #endif +#include #include "controlsigs.h" extern uiInitOptions options; @@ -12,6 +13,7 @@ extern void *uiAlloc(size_t, const char *); extern void *uiRealloc(void *, size_t, const char *); extern void uiFree(void *); +extern void realbug(const char *file, const char *line, const char *func, const char *prefix, const char *format, va_list ap); #define _ns2(s) #s #define _ns(s) _ns2(s) extern void _implbug(const char *file, const char *line, const char *func, const char *format, ...); diff --git a/darwin/GNUfiles.mk b/darwin/GNUfiles.mk index 85d89615..1ddbeebc 100644 --- a/darwin/GNUfiles.mk +++ b/darwin/GNUfiles.mk @@ -11,9 +11,9 @@ MFILES += \ darwin/combobox.m \ darwin/control.m \ darwin/datetimepicker.m \ + darwin/debug.m \ darwin/draw.m \ darwin/drawtext.m \ - darwin/debug.m \ darwin/entry.m \ darwin/fontbutton.m \ darwin/group.m \ diff --git a/darwin/debug.m b/darwin/debug.m index ec55a84b..87467add 100644 --- a/darwin/debug.m +++ b/darwin/debug.m @@ -3,7 +3,7 @@ // TODO don't halt on release builds -static void bug(const char *file, const char *line, const char *func, const char *prefix, const char *format, va_list ap) +void realbug(const char *file, const char *line, const char *func, const char *prefix, const char *format, va_list ap) { NSMutableString *str; NSString *formatted; @@ -17,21 +17,3 @@ static void bug(const char *file, const char *line, const char *func, const char [str release]; __builtin_trap(); } - -void _implbug(const char *file, const char *line, const char *func, const char *format, ...) -{ - va_list ap; - - va_start(ap, format); - bug(file, line, func, "POSSIBLE IMPLEMENTATION BUG; CONTACT ANDLABS:\n", format, ap); - va_end(ap); -} - -void _userbug(const char *file, const char *line, const char *func, const char *format, ...) -{ - va_list ap; - - va_start(ap, format); - bug(file, line, func, "You have a bug: ", format, ap); - va_end(ap); -} From 0205f2e5ca885b74b753bd6f4f9fa9b9726dd96d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 13 May 2016 21:00:12 -0400 Subject: [PATCH 0010/1329] Got rid of complain() on GTK+. --- build/GNUmakefile.example | 3 +++ common/uipriv.h | 2 +- darwin/alloc.m | 1 + darwin/area.m | 4 ++-- darwin/debug.m | 2 +- unix/GNUfiles.mk | 1 + unix/alloc.c | 23 ++++++++++++++++------- unix/area.c | 2 +- unix/debug.c | 14 ++++++++++++++ unix/draw.c | 2 +- unix/drawpath.c | 4 ++-- unix/menu.c | 17 +++++++++-------- unix/progressbar.c | 2 +- unix/spinbox.c | 3 ++- unix/util.c | 11 ----------- 15 files changed, 55 insertions(+), 36 deletions(-) create mode 100644 unix/debug.c diff --git a/build/GNUmakefile.example b/build/GNUmakefile.example index f2653865..31be42ca 100644 --- a/build/GNUmakefile.example +++ b/build/GNUmakefile.example @@ -37,6 +37,9 @@ ifeq ($(TOOLCHAIN),gcc) else LDFLAGS += -Wl,-rpath,'$$ORIGIN' endif + ifneq ($(findstring cpp-,$(EXAMPLE)),) + LDFLAGS += -pthread + endif else # TODO is there an equivalent to -L? LDFLAGS += $(OUTDIR)/libui.lib diff --git a/common/uipriv.h b/common/uipriv.h index e39bcf86..2c6756bd 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -19,7 +19,7 @@ extern void realbug(const char *file, const char *line, const char *func, const extern void _implbug(const char *file, const char *line, const char *func, const char *format, ...); #define implbug(...) _implbug(__FILE__, _ns(__LINE__), __func__, __VA_ARGS__) extern void _userbug(const char *file, const char *line, const char *func, const char *format, ...); -#define userbug(...) _implbug(__FILE__, _ns(__LINE__), __func__, __VA_ARGS__) +#define userbug(...) _userbug(__FILE__, _ns(__LINE__), __func__, __VA_ARGS__) // control.c extern uiControl *newControl(size_t size, uint32_t OSsig, uint32_t typesig, const char *typenamestr); diff --git a/darwin/alloc.m b/darwin/alloc.m index 9f0531ba..0ce46404 100644 --- a/darwin/alloc.m +++ b/darwin/alloc.m @@ -43,6 +43,7 @@ void uninitAlloc(void) [str appendString:[NSString stringWithFormat:@"%p %s\n", ptr, *TYPE(ptr)]]; } userbug("Some data was leaked; either you left a uiControl lying around or there's a bug in libui itself. Leaked data:\n%s", [str UTF8String]); + [str release]; } void *uiAlloc(size_t size, const char *type) diff --git a/darwin/area.m b/darwin/area.m index ada4ae6d..3caf1599 100644 --- a/darwin/area.m +++ b/darwin/area.m @@ -335,7 +335,7 @@ int sendAreaEvents(NSEvent *e) void uiAreaSetSize(uiArea *a, intmax_t width, intmax_t height) { if (!a->scrolling) - userbug("You cannot call uiAreaSetSize() on a non-scrolling uiArea. (uiArea: %p)", a); + userbug("You cannot call uiAreaSetSize() on a non-scrolling uiArea. (area: %p)", a); [a->area setFrameSize:NSMakeSize(width, height)]; } @@ -347,7 +347,7 @@ void uiAreaQueueRedrawAll(uiArea *a) void uiAreaScrollTo(uiArea *a, double x, double y, double width, double height) { if (!a->scrolling) - userbug("You cannot call uiAreaScrollTo() on a non-scrolling uiArea. (uiArea: %p)", a); + userbug("You cannot call uiAreaScrollTo() on a non-scrolling uiArea. (area: %p)", a); [a->area scrollRectToVisible:NSMakeRect(x, y, width, height)]; // don't worry about the return value; it just says whether scrolling was needed } diff --git a/darwin/debug.m b/darwin/debug.m index 87467add..de6d9406 100644 --- a/darwin/debug.m +++ b/darwin/debug.m @@ -9,7 +9,7 @@ void realbug(const char *file, const char *line, const char *func, const char *p NSString *formatted; str = [NSMutableString new]; - [str appendString:[NSString stringWithFormat:@"[libui] %s:%s:%s %s", file, line, func, prefix]]; + [str appendString:[NSString stringWithFormat:@"[libui] %s:%s:%s() %s", file, line, func, prefix]]; formatted = [[NSString alloc] initWithFormat:[NSString stringWithUTF8String:format] arguments:ap]; [str appendString:formatted]; [formatted release]; diff --git a/unix/GNUfiles.mk b/unix/GNUfiles.mk index 4987f177..ef83c13f 100644 --- a/unix/GNUfiles.mk +++ b/unix/GNUfiles.mk @@ -10,6 +10,7 @@ CFILES += \ unix/combobox.c \ unix/control.c \ unix/datetimepicker.c \ + unix/debug.c \ unix/draw.c \ unix/drawmatrix.c \ unix/drawpath.c \ diff --git a/unix/alloc.c b/unix/alloc.c index cb37fa10..3c43ee25 100644 --- a/unix/alloc.c +++ b/unix/alloc.c @@ -20,18 +20,27 @@ void initAlloc(void) static void uninitComplain(gpointer ptr, gpointer data) { - fprintf(stderr, "[libui] %p %s\n", ptr, *TYPE(ptr)); + char **str = (char **) data; + char *str2; + + if (*str == NULL) + *str = g_strdup_printf(""); + str2 = g_strdup_printf("%s%p %s\n", *str, ptr, *TYPE(ptr)); + g_free(*str); + *str = str2; } void uninitAlloc(void) { + char *str = NULL; + if (allocations->len == 0) { g_ptr_array_free(allocations, TRUE); return; } - fprintf(stderr, "[libui] leaked allocations:\n"); - g_ptr_array_foreach(allocations, uninitComplain, NULL); - complain("either you left something around or there's a bug in libui"); + g_ptr_array_foreach(allocations, uninitComplain, &str); + userbug("Some data was leaked; either you left a uiControl lying around or there's a bug in libui itself. Leaked data:\n%s", str); + g_free(str); } void *uiAlloc(size_t size, const char *type) @@ -59,7 +68,7 @@ void *uiRealloc(void *p, size_t new, const char *type) memset(((uint8_t *) DATA(out)) + *s, 0, new - *s); *s = new; if (g_ptr_array_remove(allocations, p) == FALSE) - complain("%p not found in allocations array in uiRealloc()", p); + implbug("%p not found in allocations array in uiRealloc()", p); g_ptr_array_add(allocations, out); return DATA(out); } @@ -67,9 +76,9 @@ void *uiRealloc(void *p, size_t new, const char *type) void uiFree(void *p) { if (p == NULL) - complain("attempt to uiFree(NULL); there's a bug somewhere"); + implbug("attempt to uiFree(NULL); there's a bug somewhere"); p = BASE(p); g_free(p); if (g_ptr_array_remove(allocations, p) == FALSE) - complain("%p not found in allocations array in uiFree()", p); + implbug("%p not found in allocations array in uiFree()", p); } diff --git a/unix/area.c b/unix/area.c index ee4e081c..3a420f00 100644 --- a/unix/area.c +++ b/unix/area.c @@ -485,7 +485,7 @@ uiUnixControlAllDefaults(uiArea) void uiAreaSetSize(uiArea *a, intmax_t width, intmax_t height) { if (!a->scrolling) - complain("attempt to call uiAreaSetSize() on a non-scrolling uiArea"); + userbug("You cannot call uiAreaSetSize() on a non-scrolling uiArea. (area: %p)", a); a->scrollWidth = width; a->scrollHeight = height; gtk_widget_queue_resize(a->areaWidget); diff --git a/unix/debug.c b/unix/debug.c new file mode 100644 index 00000000..20a4e2d0 --- /dev/null +++ b/unix/debug.c @@ -0,0 +1,14 @@ +// 13 may 2016 +#include "uipriv_unix.h" + +// TODO don't halt on release builds + +void realbug(const char *file, const char *line, const char *func, const char *prefix, const char *format, va_list ap) +{ + char *a, *b; + + a = g_strdup_printf("[libui] %s:%s:%s() %s", file, line, func, prefix); + b = g_strdup_vprintf(format, ap); + g_critical("%s%s", a, b); + G_BREAKPOINT(); +} diff --git a/unix/draw.c b/unix/draw.c index 388de7be..2d7a6367 100644 --- a/unix/draw.c +++ b/unix/draw.c @@ -37,7 +37,7 @@ static cairo_pattern_t *mkbrush(uiDrawBrush *b) // case uiDrawBrushTypeImage: } if (cairo_pattern_status(pat) != CAIRO_STATUS_SUCCESS) - complain("error creating pattern in mkbrush(): %s", + implbug("error creating pattern in mkbrush(): %s", cairo_status_to_string(cairo_pattern_status(pat))); switch (b->Type) { case uiDrawBrushTypeLinearGradient: diff --git a/unix/drawpath.c b/unix/drawpath.c index 1204315b..acfcc953 100644 --- a/unix/drawpath.c +++ b/unix/drawpath.c @@ -43,7 +43,7 @@ void uiDrawFreePath(uiDrawPath *p) static void add(uiDrawPath *p, struct piece *piece) { if (p->ended) - complain("path ended in add()"); + userbug("You cannot modify a uiDrawPath that has been ended. (path: %p)", p); g_array_append_vals(p->pieces, piece, 1); } @@ -145,7 +145,7 @@ void runPath(uiDrawPath *p, cairo_t *cr) void (*arc)(cairo_t *, double, double, double, double, double); if (!p->ended) - complain("path not ended in runPath()"); + userbug("You cannot draw with a uiDrawPath that has not been ended. (path: %p)", p); cairo_new_path(cr); for (i = 0; i < p->pieces->len; i++) { piece = &g_array_index(p->pieces, struct piece, i); diff --git a/unix/menu.c b/unix/menu.c index cf276dac..5ccb4a51 100644 --- a/unix/menu.c +++ b/unix/menu.c @@ -109,7 +109,7 @@ void uiMenuItemDisable(uiMenuItem *item) void uiMenuItemOnClicked(uiMenuItem *item, void (*f)(uiMenuItem *, uiWindow *, void *), void *data) { if (item->type == typeQuit) - complain("attempt to call uiMenuItemOnClicked() on a Quit item; use uiOnShouldQuit() instead"); + userbug("You cannot call uiMenuItemOnClicked() on a Quit item; use uiOnShouldQuit() instead."); item->onClicked = f; item->onClickedData = data; } @@ -135,7 +135,7 @@ static uiMenuItem *newItem(uiMenu *m, int type, const char *name) uiMenuItem *item; if (menusFinalized) - complain("attempt to create a new menu item after menus have been finalized"); + userbug("You cannot create a new menu item after menus have been finalized."); item = uiNew(uiMenuItem); @@ -196,7 +196,7 @@ uiMenuItem *uiMenuAppendCheckItem(uiMenu *m, const char *name) uiMenuItem *uiMenuAppendQuitItem(uiMenu *m) { if (hasQuit) - complain("attempt to add multiple Quit menu items"); + userbug("You cannot have multiple Quit menu items in the same program."); hasQuit = TRUE; newItem(m, typeSeparator, NULL); return newItem(m, typeQuit, NULL); @@ -205,7 +205,7 @@ uiMenuItem *uiMenuAppendQuitItem(uiMenu *m) uiMenuItem *uiMenuAppendPreferencesItem(uiMenu *m) { if (hasPreferences) - complain("attempt to add multiple Preferences menu items"); + userbug("You cannot have multiple Preferences menu items in the same program."); hasPreferences = TRUE; newItem(m, typeSeparator, NULL); return newItem(m, typePreferences, NULL); @@ -214,7 +214,7 @@ uiMenuItem *uiMenuAppendPreferencesItem(uiMenu *m) uiMenuItem *uiMenuAppendAboutItem(uiMenu *m) { if (hasAbout) - complain("attempt to add multiple About menu items"); + userbug("You cannot have multiple About menu items in the same program."); hasAbout = TRUE; newItem(m, typeSeparator, NULL); return newItem(m, typeAbout, NULL); @@ -230,7 +230,7 @@ uiMenu *uiNewMenu(const char *name) uiMenu *m; if (menusFinalized) - complain("attempt to create a new menu after menus have been finalized"); + userbug("You cannot create a new menu after menus have been finalized."); if (menus == NULL) menus = g_array_new(FALSE, TRUE, sizeof (uiMenu *)); @@ -308,7 +308,7 @@ static void freeMenuItem(GtkWidget *widget, gpointer data) item = g_array_index(fmi->items, uiMenuItem *, fmi->i); w = (struct menuItemWindow *) g_hash_table_lookup(item->windows, widget); if (g_hash_table_remove(item->windows, widget) == FALSE) - complain("GtkMenuItem %p not in menu item's item/window map", widget); + implbug("GtkMenuItem %p not in menu item's item/window map", widget); uiFree(w); fmi->i++; } @@ -353,7 +353,8 @@ void uninitMenus(void) for (j = 0; j < m->items->len; j++) { item = g_array_index(m->items, uiMenuItem *, j); if (g_hash_table_size(item->windows) != 0) - complain("menu item %p (%s) still has uiWindows attached; did you forget to destroy some windows?", item, item->name); + // TODO is this really a userbug()? + implbug("menu item %p (%s) still has uiWindows attached; did you forget to destroy some windows?", item, item->name); g_free(item->name); g_hash_table_destroy(item->windows); uiFree(item); diff --git a/unix/progressbar.c b/unix/progressbar.c index 587bc8ad..40306e6c 100644 --- a/unix/progressbar.c +++ b/unix/progressbar.c @@ -12,7 +12,7 @@ uiUnixControlAllDefaults(uiProgressBar) void uiProgressBarSetValue(uiProgressBar *p, int value) { if (value < 0 || value > 100) - complain("value %d out of range in progressbarSetValue()", value); + userbug("Value %d is out of range for a uiProgressBar.", value); gtk_progress_bar_set_fraction(p->pbar, ((gdouble) value) / 100); } diff --git a/unix/spinbox.c b/unix/spinbox.c index 0f1a4f5e..433c3965 100644 --- a/unix/spinbox.c +++ b/unix/spinbox.c @@ -49,8 +49,9 @@ uiSpinbox *uiNewSpinbox(intmax_t min, intmax_t max) { uiSpinbox *s; + // TODO just swap? if (min >= max) - complain("error: min >= max in uiNewSpinbox()"); + userbug("min >= max not allowed in uiNewSpinbox()."); uiUnixNewControl(uiSpinbox, s); diff --git a/unix/util.c b/unix/util.c index 86d89a3e..7f4f43fb 100644 --- a/unix/util.c +++ b/unix/util.c @@ -1,17 +1,6 @@ // 18 april 2015 #include "uipriv_unix.h" -void complain(const char *fmt, ...) -{ - va_list ap; - char *msg; - - va_start(ap, fmt); - msg = g_strdup_vprintf(fmt, ap); - va_end(ap); - g_error("[libui] %s\n", msg); -} - void setMargined(GtkContainer *c, int margined) { if (margined) From 15b370bc366d4ff2140f8f5a1d4a941ae0f1b8a5 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 13 May 2016 21:07:48 -0400 Subject: [PATCH 0011/1329] Started migrating out implbug() in the windows backend. I'm gonna need a cstrf() to go along with wstrf(). --- windows/events.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/windows/events.cpp b/windows/events.cpp index 56ef78e7..a6da31a8 100644 --- a/windows/events.cpp +++ b/windows/events.cpp @@ -22,7 +22,7 @@ static std::map handlers; void uiWindowsRegisterWM_COMMANDHandler(HWND hwnd, BOOL (*handler)(uiControl *, HWND, WORD, LRESULT *), uiControl *c) { if (handlers[hwnd].commandHandler != NULL) - implbug(L"already registered a WM_COMMAND handler to window handle %p", hwnd); + implbug("already registered a WM_COMMAND handler to window handle %p", hwnd); handlers[hwnd].commandHandler = handler; handlers[hwnd].c = c; } @@ -30,7 +30,7 @@ void uiWindowsRegisterWM_COMMANDHandler(HWND hwnd, BOOL (*handler)(uiControl *, void uiWindowsRegisterWM_NOTIFYHandler(HWND hwnd, BOOL (*handler)(uiControl *, HWND, NMHDR *, LRESULT *), uiControl *c) { if (handlers[hwnd].notifyHandler != NULL) - implbug(L"already registered a WM_NOTIFY handler to window handle %p", hwnd); + implbug("already registered a WM_NOTIFY handler to window handle %p", hwnd); handlers[hwnd].notifyHandler = handler; handlers[hwnd].c = c; } @@ -38,7 +38,7 @@ void uiWindowsRegisterWM_NOTIFYHandler(HWND hwnd, BOOL (*handler)(uiControl *, H void uiWindowsRegisterWM_HSCROLLHandler(HWND hwnd, BOOL (*handler)(uiControl *, HWND, WORD, LRESULT *), uiControl *c) { if (handlers[hwnd].hscrollHandler != NULL) - implbug(L"already registered a WM_HSCROLL handler to window handle %p", hwnd); + implbug("already registered a WM_HSCROLL handler to window handle %p", hwnd); handlers[hwnd].hscrollHandler = handler; handlers[hwnd].c = c; } @@ -46,21 +46,21 @@ void uiWindowsRegisterWM_HSCROLLHandler(HWND hwnd, BOOL (*handler)(uiControl *, void uiWindowsUnregisterWM_COMMANDHandler(HWND hwnd) { if (handlers[hwnd].commandHandler == NULL) - implbug(L"window handle %p not registered to receive WM_COMMAND events", hwnd); + implbug("window handle %p not registered to receive WM_COMMAND events", hwnd); handlers[hwnd].commandHandler = NULL; } void uiWindowsUnregisterWM_NOTIFYHandler(HWND hwnd) { if (handlers[hwnd].notifyHandler == NULL) - implbug(L"window handle %p not registered to receive WM_NOTIFY events", hwnd); + implbug("window handle %p not registered to receive WM_NOTIFY events", hwnd); handlers[hwnd].notifyHandler = NULL; } void uiWindowsUnregisterWM_HSCROLLHandler(HWND hwnd) { if (handlers[hwnd].hscrollHandler == NULL) - implbug(L"window handle %p not registered to receive WM_HSCROLL events", hwnd); + implbug("window handle %p not registered to receive WM_HSCROLL events", hwnd); handlers[hwnd].hscrollHandler = NULL; } @@ -130,14 +130,14 @@ static std::map wininichanges; void uiWindowsRegisterReceiveWM_WININICHANGE(HWND hwnd) { if (wininichanges[hwnd]) - implbug(L"window handle %p already subscribed to receive WM_WINICHANGEs", hwnd); + implbug("window handle %p already subscribed to receive WM_WINICHANGEs", hwnd); wininichanges[hwnd] = true; } void uiWindowsUnregisterReceiveWM_WININICHANGE(HWND hwnd) { if (!wininichanges[hwnd]) - implbug(L"window handle %p not registered to receive WM_WININICHANGEs", hwnd); + implbug("window handle %p not registered to receive WM_WININICHANGEs", hwnd); wininichanges[hwnd] = false; } From 6d2d97736369665f42b20d508bed872378d48b59 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 13 May 2016 22:05:02 -0400 Subject: [PATCH 0012/1329] Cleaned up the debugging infrastructure on Windows (we stopped using the strsafe functions so there's no useful failure case anymore) and implemented the new one. --- unix/alloc.c | 2 +- windows/alloc.cpp | 2 +- windows/debug.cpp | 89 ++++++++++---------------------------- windows/uipriv_windows.hpp | 2 - windows/utf16.cpp | 47 +++++--------------- 5 files changed, 36 insertions(+), 106 deletions(-) diff --git a/unix/alloc.c b/unix/alloc.c index 3c43ee25..2561efa6 100644 --- a/unix/alloc.c +++ b/unix/alloc.c @@ -76,7 +76,7 @@ void *uiRealloc(void *p, size_t new, const char *type) void uiFree(void *p) { if (p == NULL) - implbug("attempt to uiFree(NULL); there's a bug somewhere"); + implbug("attempt to uiFree(NULL)"); p = BASE(p); g_free(p); if (g_ptr_array_remove(allocations, p) == FALSE) diff --git a/windows/alloc.cpp b/windows/alloc.cpp index ed1d4ee1..ec8b9f3c 100644 --- a/windows/alloc.cpp +++ b/windows/alloc.cpp @@ -60,7 +60,7 @@ void uiFree(void *_p) uint8_t *p = (uint8_t *) _p; if (p == NULL) - complain("attempt to uiFree(NULL); there's a bug somewhere"); + implbug("attempt to uiFree(NULL)"); types.erase(heap[p]); delete heap[p]; heap.erase(p); diff --git a/windows/debug.cpp b/windows/debug.cpp index 7b0f35f8..0b0d1e03 100644 --- a/windows/debug.cpp +++ b/windows/debug.cpp @@ -1,14 +1,6 @@ // 25 february 2015 #include "uipriv_windows.hpp" -// TODO -void complain(const char *format, ...) -{ - OutputDebugStringA(format); - DebugBreak(); - abort(); -} - // TODO disable logging and stopping on no-debug builds // TODO are the newlines needed? @@ -17,8 +9,6 @@ static void printDebug(const WCHAR *msg) OutputDebugStringW(msg); } -#define debugfmt L"%s:%s:%s()" - HRESULT _logLastError(debugargs, const WCHAR *s) { DWORD le; @@ -31,21 +21,13 @@ HRESULT _logLastError(debugargs, const WCHAR *s) useFormatted = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, le, 0, (LPWSTR) (&formatted), 0, NULL) != 0; if (!useFormatted) formatted = L"\n"; - msg = debugstrf(L"[libui] " debugfmt L" %s: GetLastError() == %I32u %s", + msg = strf(L"[libui] %s:%s:%s() %s: GetLastError() == %I32u %s", file, line, func, s, le, formatted); if (useFormatted) LocalFree(formatted); // ignore error - if (msg == NULL) { - printDebug(L"[libui] (debugstrf() failed; printing raw) "); - printDebug(file); - printDebug(func); - printDebug(s); - printDebug(L"\n"); - } else { - printDebug(msg); - uiFree(msg); - } + printDebug(msg); + uiFree(msg); DebugBreak(); SetLastError(le); @@ -67,65 +49,38 @@ HRESULT _logHRESULT(debugargs, const WCHAR *s, HRESULT hr) useFormatted = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, hr, 0, (LPWSTR) (&formatted), 0, NULL) != 0; if (!useFormatted) formatted = L"\n"; - msg = debugstrf(L"[libui] " debugfmt L" %s: HRESULT == 0x%08I32X %s", + msg = strf(L"[libui] %s:%s:%s() %s: HRESULT == 0x%08I32X %s", file, line, func, s, hr, formatted); if (useFormatted) LocalFree(formatted); // ignore error - if (msg == NULL) { - printDebug(L"[libui] (debugstrf() failed; printing raw) "); - printDebug(file); - printDebug(func); - printDebug(s); - printDebug(L"\n"); - } else { - printDebug(msg); - uiFree(msg); - } + printDebug(msg); + uiFree(msg); DebugBreak(); return hr; } -#define implbugmsg L"either you have or libui has a bug in a control implementation; if libui does, contact andlabs" - -void _implbug(debugargs, const WCHAR *format, ...) +void realbug(const char *file, const char *line, const char *func, const char *prefix, const char *format, va_list ap) { - va_list ap; - WCHAR *formatted; - WCHAR *full; - const WCHAR *onerr; + va_list ap2; + char *msg; + size_t n; + WCHAR *final; - va_start(ap, format); - formatted = debugvstrf(format, ap); - va_end(ap); - if (formatted == NULL) { - onerr = format; - goto bad; - } + va_copy(ap2, ap); + n = _vscprintf(format, ap2); + va_end(ap2); + n++; // terminating L'\0' - full = debugstrf(L"[libui] " debugfmt L" " implbugmsg L" — %s\n", - file, line, func, - formatted); - if (full == NULL) { - onerr = formatted; - goto bad; - } + buf = (char *) uiAlloc(n * sizeof (char), "char[]"); + // includes terminating L'\0' according to example in https://msdn.microsoft.com/en-us/library/xa1a1a6z.aspx + vsprintf_s(msg, n, format, ap); - printDebug(full); - uiFree(full); - uiFree(formatted); - goto after; + final = strf(L"[libui] %hs:%hs:%hs() %hs%hs\n", file, line, func, prefix, msg); + uiFree(msg); + printDebug(final); + uiFree(final); -bad: - printDebug(L"[libui] (debugstrf() failed; printing raw) "); - printDebug(implbugmsg); - printDebug(file); - printDebug(func); - printDebug(onerr); - printDebug(L"\n"); - -after: DebugBreak(); - abort(); } diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index 4f66bea5..1b54e17e 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -35,8 +35,6 @@ extern char *toUTF8(const WCHAR *wstr); extern WCHAR *utf16dup(const WCHAR *orig); extern WCHAR *strf(const WCHAR *format, ...); extern WCHAR *vstrf(const WCHAR *format, va_list ap); -extern WCHAR *debugstrf(const WCHAR *format, ...); -extern WCHAR *debugvstrf(const WCHAR *format, va_list ap); extern char *LFtoCRLF(const char *lfonly); extern void CRLFtoLF(const char *s); diff --git a/windows/utf16.cpp b/windows/utf16.cpp index 00abc6fd..374cec9b 100644 --- a/windows/utf16.cpp +++ b/windows/utf16.cpp @@ -60,13 +60,22 @@ WCHAR *utf16dup(const WCHAR *orig) return out; } -// if recursing is TRUE, do NOT recursively call wstrf() in logHRESULT() -static WCHAR *strfcore(BOOL recursing, const WCHAR *format, va_list ap) +WCHAR *strf(const WCHAR *format, ...) +{ + va_list ap; + WCHAR *str; + + va_start(ap, format); + str = vstrf(format, ap); + va_end(ap); + return str; +} + +WCHAR *vstrf(const WCHAR *format, va_list ap) { va_list ap2; WCHAR *buf; size_t n; - HRESULT hr; if (*format == L'\0') return emptyUTF16(); @@ -83,38 +92,6 @@ static WCHAR *strfcore(BOOL recursing, const WCHAR *format, va_list ap) return buf; } -WCHAR *strf(const WCHAR *format, ...) -{ - va_list ap; - WCHAR *str; - - va_start(ap, format); - str = vstrf(format, ap); - va_end(ap); - return str; -} - -WCHAR *vstrf(const WCHAR *format, va_list ap) -{ - return strfcore(FALSE, format, ap); -} - -WCHAR *debugstrf(const WCHAR *format, ...) -{ - va_list ap; - WCHAR *str; - - va_start(ap, format); - str = debugvstrf(format, ap); - va_end(ap); - return str; -} - -WCHAR *debugvstrf(const WCHAR *format, va_list ap) -{ - return strfcore(TRUE, format, ap); -} - // Let's shove these utility routines here too. // Prerequisite: lfonly is UTF-8. char *LFtoCRLF(const char *lfonly) From 8067dc76b7c59b3458620710cdd4fb73da7e7c96 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 13 May 2016 22:53:56 -0400 Subject: [PATCH 0013/1329] Got rid of most of Windows's complain()s. --- windows/datetimepicker.cpp | 2 +- windows/debug.cpp | 4 ++-- windows/draw.cpp | 8 +++++--- windows/drawpath.cpp | 3 +-- windows/drawtext.cpp | 14 ++++++++------ windows/menu.cpp | 20 +++++++++++--------- windows/progressbar.cpp | 2 +- windows/spinbox.cpp | 3 ++- 8 files changed, 31 insertions(+), 25 deletions(-) diff --git a/windows/datetimepicker.cpp b/windows/datetimepicker.cpp index b81cfe6f..4f9a00fa 100644 --- a/windows/datetimepicker.cpp +++ b/windows/datetimepicker.cpp @@ -44,7 +44,7 @@ static WCHAR *expandYear(WCHAR *dts, int n) if (*p == L'\'') break; if (*p == L'\0') - complain("unterminated quote in system-provided locale date string in expandYear()"); + implbug("unterminated quote in system-provided locale date string in expandYear()"); *q++ = *p; } // and fall through to copy the closing quote diff --git a/windows/debug.cpp b/windows/debug.cpp index 0b0d1e03..7af79e69 100644 --- a/windows/debug.cpp +++ b/windows/debug.cpp @@ -71,10 +71,10 @@ void realbug(const char *file, const char *line, const char *func, const char *p va_copy(ap2, ap); n = _vscprintf(format, ap2); va_end(ap2); - n++; // terminating L'\0' + n++; // terminating '\0' buf = (char *) uiAlloc(n * sizeof (char), "char[]"); - // includes terminating L'\0' according to example in https://msdn.microsoft.com/en-us/library/xa1a1a6z.aspx + // includes terminating '\0' according to example in https://msdn.microsoft.com/en-us/library/xa1a1a6z.aspx vsprintf_s(msg, n, format, ap); final = strf(L"[libui] %hs:%hs:%hs() %hs%hs\n", file, line, func, prefix, msg); diff --git a/windows/draw.cpp b/windows/draw.cpp index 6e278fc5..dec6c18f 100644 --- a/windows/draw.cpp +++ b/windows/draw.cpp @@ -95,8 +95,8 @@ void freeContext(uiDrawContext *c) if (c->currentClip != NULL) c->currentClip->Release(); if (c->states->size() != 0) - // TODO userbug() - complain("unbalanced save/restore"); + // TODO do this on other platforms + userbug("You did not balance uiDrawSave() and uiDrawRestore() calls."); delete c->states; uiFree(c); } @@ -228,7 +228,9 @@ static ID2D1Brush *makeBrush(uiDrawBrush *b, ID2D1RenderTarget *rt) // TODO } - complain("invalid brush type %d in makeBrush()", b->Type); + // TODO do this on all platforms + userbug("Invalid brush type %d given to drawing operation.", b->Type); + // TODO dummy brush? return NULL; // make compiler happy } diff --git a/windows/drawpath.cpp b/windows/drawpath.cpp index 1ae2d8e5..be02b826 100644 --- a/windows/drawpath.cpp +++ b/windows/drawpath.cpp @@ -241,7 +241,6 @@ void uiDrawPathEnd(uiDrawPath *p) ID2D1PathGeometry *pathGeometry(uiDrawPath *p) { if (p->sink != NULL) - // TODO userbug() - complain("path not ended"); + userbug("You cannot draw with a uiDrawPath that was not ended. (path: %p)", p); return p->path; } diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index b2bd81da..453b4c10 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -139,7 +139,7 @@ void attrToDWriteAttr(struct dwriteAttr *attr) break; } if (!found) - complain("invalid weight %d passed to attrToDWriteAttr()", attr->weight); + userbug("Invalid text weight %d passed to text function.", attr->weight); found = false; for (i = 0; ; i++) { @@ -152,7 +152,7 @@ void attrToDWriteAttr(struct dwriteAttr *attr) break; } if (!found) - complain("invalid italic %d passed to attrToDWriteAttr()", attr->italic); + userbug("Invalid text italic %d passed to text function.", attr->italic); found = false; for (i = 0; ; i++) { @@ -165,7 +165,8 @@ void attrToDWriteAttr(struct dwriteAttr *attr) break; } if (!found) - complain("invalid stretch %d passed to attrToDWriteAttr()", attr->stretch); + // TODO on other platforms too + userbug("Invalid text stretch %d passed to text function.", attr->stretch); } void dwriteAttrToAttr(struct dwriteAttr *attr) @@ -204,7 +205,8 @@ void dwriteAttrToAttr(struct dwriteAttr *attr) break; } if (!found) - complain("invalid italic %d passed to dwriteAttrToAttr()", attr->ditalic); + // these are implbug()s because users shouldn't be able to get here directly; TODO? + implbug("invalid italic %d passed to dwriteAttrToAttr()", attr->ditalic); found = false; for (i = 0; ; i++) { @@ -217,7 +219,7 @@ void dwriteAttrToAttr(struct dwriteAttr *attr) break; } if (!found) - complain("invalid stretch %d passed to dwriteAttrToAttr()", attr->dstretch); + implbug("invalid stretch %d passed to dwriteAttrToAttr()", attr->dstretch); } uiDrawTextFont *uiDrawLoadClosestFont(const uiDrawTextFontDescriptor *desc) @@ -242,7 +244,7 @@ uiDrawTextFont *uiDrawLoadClosestFont(const uiDrawTextFontDescriptor *desc) if (hr != S_OK) logHRESULT(L"error finding font family", hr); if (!exists) - complain("TODO family not found in uiDrawLoadClosestFont()", hr); + implbug("TODO family not found in uiDrawLoadClosestFont()", hr); hr = collection->GetFontFamily(index, &family); if (hr != S_OK) logHRESULT(L"error loading font family", hr); diff --git a/windows/menu.cpp b/windows/menu.cpp index 89406d7f..484bf756 100644 --- a/windows/menu.cpp +++ b/windows/menu.cpp @@ -87,7 +87,7 @@ void uiMenuItemDisable(uiMenuItem *i) void uiMenuItemOnClicked(uiMenuItem *i, void (*f)(uiMenuItem *, uiWindow *, void *), void *data) { if (i->type == typeQuit) - complain("attempt to call uiMenuItemOnClicked() on a Quit item; use uiOnShouldQuit() instead"); + userbug("You can not call uiMenuItemOnClicked() on a Quit item; use uiOnShouldQuit() instead."); i->onClicked = f; i->onClickedData = data; } @@ -111,7 +111,7 @@ static uiMenuItem *newItem(uiMenu *m, int type, const char *name) uiMenuItem *item; if (menusFinalized) - complain("attempt to create a new menu item after menus have been finalized"); + userbug("You can not create a new menu item after menus have been finalized."); if (m->len >= m->cap) { m->cap += grow; @@ -167,7 +167,7 @@ uiMenuItem *uiMenuAppendCheckItem(uiMenu *m, const char *name) uiMenuItem *uiMenuAppendQuitItem(uiMenu *m) { if (hasQuit) - complain("attempt to add multiple Quit menu items"); + userbug("You can not have multiple Quit menu items in a program."); hasQuit = TRUE; newItem(m, typeSeparator, NULL); return newItem(m, typeQuit, NULL); @@ -176,7 +176,7 @@ uiMenuItem *uiMenuAppendQuitItem(uiMenu *m) uiMenuItem *uiMenuAppendPreferencesItem(uiMenu *m) { if (hasPreferences) - complain("attempt to add multiple Preferences menu items"); + userbug("You can not have multiple Preferences menu items in a program."); hasPreferences = TRUE; newItem(m, typeSeparator, NULL); return newItem(m, typePreferences, NULL); @@ -185,7 +185,8 @@ uiMenuItem *uiMenuAppendPreferencesItem(uiMenu *m) uiMenuItem *uiMenuAppendAboutItem(uiMenu *m) { if (hasAbout) - complain("attempt to add multiple About menu items"); + // TODO place these userbug strings in a header? + userbug("You can not have multiple About menu items in a program."); hasAbout = TRUE; newItem(m, typeSeparator, NULL); return newItem(m, typeAbout, NULL); @@ -201,7 +202,7 @@ uiMenu *uiNewMenu(const char *name) uiMenu *m; if (menusFinalized) - complain("attempt to create a new menu after menus have been finalized"); + userbug("You can not create a new menu after menus have been finalized."); if (len >= cap) { cap += grow; menus = (uiMenu **) uiRealloc(menus, cap * sizeof (uiMenu *), "uiMenu *[]"); @@ -290,7 +291,7 @@ void runMenuEvent(WORD id, uiWindow *w) } } // no match - complain("unknown menu ID %hu in runMenuEvent()", id); + implbug("unknown menu ID %hu in runMenuEvent()", id); found: // first toggle checkboxes, if any @@ -313,7 +314,7 @@ static void freeMenu(uiMenu *m, HMENU submenu) if (item->hmenus[j] == submenu) break; if (j >= item->len) - complain("submenu handle %p not found in freeMenu()", submenu); + implbug("submenu handle %p not found in freeMenu()", submenu); for (; j < item->len - 1; j++) item->hmenus[j] = item->hmenus[j + 1]; item->hmenus[j] = NULL; @@ -349,7 +350,8 @@ void uninitMenus(void) for (j = 0; j < m->len; j++) { item = m->items[j]; if (item->len != 0) - complain("menu item %p (%ws) still has uiWindows attached; did you forget to destroy some windows?", item, item->name); + // TODO userbug()? + implbug("menu item %p (%ws) still has uiWindows attached; did you forget to destroy some windows?", item, item->name); if (item->name != NULL) uiFree(item->name); if (item->hmenus != NULL) diff --git a/windows/progressbar.cpp b/windows/progressbar.cpp index 26153f78..2700a790 100644 --- a/windows/progressbar.cpp +++ b/windows/progressbar.cpp @@ -33,7 +33,7 @@ static void uiProgressBarMinimumSize(uiWindowsControl *c, intmax_t *width, intma void uiProgressBarSetValue(uiProgressBar *p, int value) { if (value < 0 || value > 100) - complain("value %d out of range in uiProgressBarSetValue()", value); + userbug("Value %d is out of range for uiProgressBars.", value); if (value == 100) { // because we can't 101 SendMessageW(p->hwnd, PBM_SETRANGE32, 0, 101); SendMessageW(p->hwnd, PBM_SETPOS, 101, 0); diff --git a/windows/spinbox.cpp b/windows/spinbox.cpp index 9118d1d7..a9a9ed4b 100644 --- a/windows/spinbox.cpp +++ b/windows/spinbox.cpp @@ -184,7 +184,8 @@ uiSpinbox *uiNewSpinbox(intmax_t min, intmax_t max) uiSpinbox *s; if (min >= max) - complain("error: min >= max in uiNewSpinbox()"); + // TODO + implbug("error: min >= max in uiNewSpinbox()"); uiWindowsNewControl(uiSpinbox, s); From aafb27cb2c014917542a0fef5e5e2261e350d615 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 13 May 2016 23:29:41 -0400 Subject: [PATCH 0014/1329] Finished the complain() migration. --- common/uipriv.h | 4 ++++ windows/alloc.cpp | 21 ++++++++------------- windows/debug.cpp | 3 +-- windows/init.cpp | 4 +--- windows/winapi.hpp | 1 + 5 files changed, 15 insertions(+), 18 deletions(-) diff --git a/common/uipriv.h b/common/uipriv.h index 2c6756bd..73210719 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -13,6 +13,10 @@ extern void *uiAlloc(size_t, const char *); extern void *uiRealloc(void *, size_t, const char *); extern void uiFree(void *); +// ugh, this was only introduced in MSVC 2015... +#ifdef _MSC_VER +#define __func__ __FUNCTION__ +#endif extern void realbug(const char *file, const char *line, const char *func, const char *prefix, const char *format, va_list ap); #define _ns2(s) #s #define _ns(s) _ns2(s) diff --git a/windows/alloc.cpp b/windows/alloc.cpp index ec8b9f3c..9511c881 100644 --- a/windows/alloc.cpp +++ b/windows/alloc.cpp @@ -13,20 +13,15 @@ void initAlloc(void) void uninitAlloc(void) { - BOOL hasEntry; + std::ostringstream oss; - hasEntry = FALSE; - for (const auto &alloc : heap) { - if (!hasEntry) { - fprintf(stderr, "[libui] leaked allocations:\n"); - hasEntry = TRUE; - } - fprintf(stderr, "[libui] %p %s\n", - alloc.first, - types[alloc.second]); - } - if (hasEntry) - complain("either you left something around or there's a bug in libui"); + if (heap.size() == 0) + return; + for (const auto &alloc : heap) + // note the void * cast; otherwise it'll be treated as a string + oss << (void *) (alloc.first) << " " << types[alloc.second] << "\n"; + // TODO keep oss.str() alive? + userbug("Some data was leaked; either you left a uiControl lying around or there's a bug in libui itself. Leaked data:\n%s", oss.str().c_str()); } #define rawBytes(pa) (&((*pa)[0])) diff --git a/windows/debug.cpp b/windows/debug.cpp index 7af79e69..6671717e 100644 --- a/windows/debug.cpp +++ b/windows/debug.cpp @@ -41,7 +41,6 @@ HRESULT _logLastError(debugargs, const WCHAR *s) HRESULT _logHRESULT(debugargs, const WCHAR *s, HRESULT hr) { - DWORD le; WCHAR *msg; WCHAR *formatted; BOOL useFormatted; @@ -73,7 +72,7 @@ void realbug(const char *file, const char *line, const char *func, const char *p va_end(ap2); n++; // terminating '\0' - buf = (char *) uiAlloc(n * sizeof (char), "char[]"); + msg = (char *) uiAlloc(n * sizeof (char), "char[]"); // includes terminating '\0' according to example in https://msdn.microsoft.com/en-us/library/xa1a1a6z.aspx vsprintf_s(msg, n, format, ap); diff --git a/windows/init.cpp b/windows/init.cpp index 15dd4edd..2de75c4f 100644 --- a/windows/init.cpp +++ b/windows/init.cpp @@ -23,15 +23,13 @@ static const char *initerr(const char *message, const WCHAR *label, DWORD value) if (!hassysmsg) sysmsg = L""; wmessage = toUTF16(message + 1); - wout = debugstrf(L"-error initializing libui: %s; code %I32d (0x%08I32X) %s", + wout = strf(L"-error initializing libui: %s; code %I32d (0x%08I32X) %s", wmessage, value, value, sysmsg); uiFree(wmessage); if (hassysmsg) LocalFree(sysmsg); // ignore error - if (wout == NULL) // debugstrf() failed; return message raw - return message + 1; out = toUTF8(wout); uiFree(wout); return out + 1; diff --git a/windows/winapi.hpp b/windows/winapi.hpp index ebd92271..5763f297 100644 --- a/windows/winapi.hpp +++ b/windows/winapi.hpp @@ -47,4 +47,5 @@ #include #include +#include #endif From 7ebb4cea9a11436e6a23044b8848b2f3be4db75e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 14 May 2016 11:12:45 -0400 Subject: [PATCH 0015/1329] Changed the release flag from NODEBUG to RELEASE. --- GNUmakefile | 6 +++--- build/GNUbasegcc.mk | 2 +- build/GNUbasemsvc.mk | 6 +++--- buildnotes | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/GNUmakefile b/GNUmakefile index ce74d750..39b91cea 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -25,8 +25,8 @@ ifndef OS endif # default is to build with debug symbols -ifndef NODEBUG - NODEBUG = 0 +ifndef RELEASE + RELEASE = 0 endif # parameters @@ -36,7 +36,7 @@ export CFLAGS export CXXFLAGS # TODO RCFLAGS export LDFLAGS -export NODEBUG +export RELEASE export EXAMPLE export PREFIX diff --git a/build/GNUbasegcc.mk b/build/GNUbasegcc.mk index dfbfb6c3..757d8fd7 100644 --- a/build/GNUbasegcc.mk +++ b/build/GNUbasegcc.mk @@ -23,7 +23,7 @@ CXXFLAGS += \ LDFLAGS += \ -fPIC -ifneq ($(NODEBUG),1) +ifneq ($(RELEASE),1) CFLAGS += -g CXXFLAGS += -g LDFLAGS += -g diff --git a/build/GNUbasemsvc.mk b/build/GNUbasemsvc.mk index 1ef6b911..7e1bb6bf 100644 --- a/build/GNUbasemsvc.mk +++ b/build/GNUbasemsvc.mk @@ -48,7 +48,7 @@ CXXFLAGS += \ LDFLAGS += \ -largeaddressaware -nologo -incremental:no -ifneq ($(NODEBUG),1) +ifneq ($(RELEASE),1) CFLAGS += -Zi CXXFLAGS += -Zi LDFLAGS += -debug @@ -76,7 +76,7 @@ $(OUT): $(OFILES) | $(OUTDIR) # TODO can we put /Fd$@.pdb in a variable? $(OBJDIR)/%.c.o: $$(subst _,/,%).c $(HFILES) | $(OBJDIR) -ifeq ($(NODEBUG),1) +ifeq ($(RELEASE),1) @cl -Fo:$@ -c $< $(CFLAGS) else @cl -Fo:$@ -c $< $(CFLAGS) -Fd$@.pdb @@ -84,7 +84,7 @@ endif @echo ====== Compiled $< $(OBJDIR)/%.cpp.o: $$(subst _,/,%).cpp $(HFILES) | $(OBJDIR) -ifeq ($(NODEBUG),1) +ifeq ($(RELEASE),1) @cl -Fo:$@ -c $< $(CXXFLAGS) else @cl -Fo:$@ -c $< $(CXXFLAGS) -Fd$@.pdb diff --git a/buildnotes b/buildnotes index bc33bd1b..8c5d9fb6 100644 --- a/buildnotes +++ b/buildnotes @@ -19,7 +19,7 @@ The build-time settings are compiler flags this is where you can specify -m32 or -m64, for instance Objective-C uses $(CFLAGS) - NODEBUG=xxx + RELEASE=xxx set to 1 to disable debug symbols must be 1, any other value means "include debug symbols" PREFIX=xxx From 2f6329adcf39dfed7802c226436c15173dc44635 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 14 May 2016 11:16:04 -0400 Subject: [PATCH 0016/1329] Added the _UI_RELEASE macro for turning off debugging. We won't actually turn off debugging just yet. --- build/GNUmakefile.libui | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/build/GNUmakefile.libui b/build/GNUmakefile.libui index 393f054f..41b106ae 100644 --- a/build/GNUmakefile.libui +++ b/build/GNUmakefile.libui @@ -42,6 +42,11 @@ else -D "_UI_EXTERN=__declspec(dllexport) extern" endif +ifeq ($(RELEASE),1) + CFLAGS += -D_UI_RELEASE + CXXFLAGS += -D_UI_RELEASE +endif + ifeq ($(USESSONAME),1) LDFLAGS += $(SONAMEFLAG)$(NAME)$(SUFFIX) endif From f0d6f84083c3308e909a8a650af364f1821f70f0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 14 May 2016 11:18:53 -0400 Subject: [PATCH 0017/1329] More TODOs. --- buildnotes | 1 + 1 file changed, 1 insertion(+) diff --git a/buildnotes b/buildnotes index 8c5d9fb6..8a8c811b 100644 --- a/buildnotes +++ b/buildnotes @@ -22,6 +22,7 @@ The build-time settings are RELEASE=xxx set to 1 to disable debug symbols must be 1, any other value means "include debug symbols" + TODO separate debug symbols from debug runtime PREFIX=xxx TODO DESTDIR=xxx From 232839020fa5cbec6c744edd32e67d0cc5acc9bd Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 14 May 2016 11:39:51 -0400 Subject: [PATCH 0018/1329] More TODOs. --- darwin/draw.m | 1 + 1 file changed, 1 insertion(+) diff --git a/darwin/draw.m b/darwin/draw.m index 6661d6a1..40c47a99 100644 --- a/darwin/draw.m +++ b/darwin/draw.m @@ -197,6 +197,7 @@ void uiDrawStroke(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b, uiDrawStro // for a solid fill, we can merely have Core Graphics fill directly static void fillSolid(CGContextRef ctxt, uiDrawPath *p, uiDrawBrush *b) { + // TODO this uses DeviceRGB; switch to sRGB? CGContextSetRGBFillColor(ctxt, b->R, b->G, b->B, b->A); switch (p->fillMode) { case uiDrawFillModeWinding: From 79a522efb3e53e1f4390f18378e4d255998c7ef9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 14 May 2016 21:29:44 -0400 Subject: [PATCH 0019/1329] More TODO resolution. Thanks to someone (mikeash?) in irc.freenode.org/#macdev. --- darwin/fontbutton.m | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/darwin/fontbutton.m b/darwin/fontbutton.m index 3a61c823..ac3561fa 100644 --- a/darwin/fontbutton.m +++ b/darwin/fontbutton.m @@ -123,10 +123,10 @@ struct uiFontButton { fm = (NSFontManager *) sender; old = self->libui_font; self->libui_font = [sender convertFont:self->libui_font]; - // TODO do we get it back retained? - // TODO is it even retained when we get it, regardless of value? - if (self->libui_font != old) - [old release]; + // do this even if it returns the same; we don't own anything that isn't from a new or alloc/init + [self->libui_font retain]; + // do this second just in case + [old release]; [self updateFontButtonLabel]; (*(b->onChanged))(b, b->onChangedData); } From 0e785d886bdf3889c93243e470abed2bd6a710c3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 14 May 2016 21:53:24 -0400 Subject: [PATCH 0020/1329] Started tests for truly empty uiGroups and uiTabs; started banning NULL in uiBox. --- darwin/box.m | 3 +++ test/GNUfiles.mk | 1 + test/main.c | 10 +++++++++- test/test.h | 3 +++ 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/darwin/box.m b/darwin/box.m index 0c05d070..a5cf41b0 100644 --- a/darwin/box.m +++ b/darwin/box.m @@ -404,6 +404,9 @@ uiDarwinControlDefaultSetHuggingPriority(uiBox, view) void uiBoxAppend(uiBox *b, uiControl *c, int stretchy) { + // TODO on other platforms + if (c == NULL) + userbug("You cannot add NULL to a uiBox."); [b->view append:c stretchy:stretchy]; } diff --git a/test/GNUfiles.mk b/test/GNUfiles.mk index 2b9501b9..5072771e 100644 --- a/test/GNUfiles.mk +++ b/test/GNUfiles.mk @@ -17,6 +17,7 @@ CFILES += \ test/page8.c \ test/page9.c \ test/page10.c \ + test/page11.c \ test/spaced.c HFILES += \ diff --git a/test/main.c b/test/main.c index cc52e8e2..add52e99 100644 --- a/test/main.c +++ b/test/main.c @@ -45,7 +45,9 @@ int main(int argc, char *argv[]) int i; const char *err; uiWindow *w; - uiBox *page2, *page3, *page4, *page5, *page6, *page7, *page8, *page9, *page10; + uiBox *page2, *page3, *page4, *page5; + uiBox *page6, *page7, *page8, *page9, *page10; + uiBox *page11; uiTab *outerTab; uiTab *innerTab; int nomenus = 0; @@ -130,6 +132,12 @@ int main(int argc, char *argv[]) page10 = makePage10(); uiTabAppend(innerTab, "Page 10", uiControl(page10)); + innerTab = newTab(); + uiTabAppend(outerTab, "Pages 11-?", uiControl(innerTab)); + + page11 = makePage11(); + uiTabAppend(innerTab, "Page 11", uiControl(page11)); + if (startspaced) setSpaced(1); diff --git a/test/test.h b/test/test.h index 89d41695..be37440e 100644 --- a/test/test.h +++ b/test/test.h @@ -74,3 +74,6 @@ extern uiBox *makePage9(void); // page10.c extern uiBox *makePage10(void); + +// page11.c +extern uiBox *makePage11(void); From fd9f6cea6a603954619ef880ce6d2f5c63daf4b9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 14 May 2016 22:09:02 -0400 Subject: [PATCH 0021/1329] Allowed uiGroups and uiTabs to have no and NULL controls with defined behavior on OS X. Actually added the test code this time. --- darwin/tab.m | 44 +++++++++++++++++++++++++----------------- test/page11.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 17 deletions(-) create mode 100644 test/page11.c diff --git a/darwin/tab.m b/darwin/tab.m index 0e7a0d19..a98bf0fd 100644 --- a/darwin/tab.m +++ b/darwin/tab.m @@ -58,6 +58,7 @@ struct uiTab { { [self removeChildConstraints]; if (self.c == NULL) + // TODO make sure we need the margins return; singleChildConstraintsEstablish(&(self->constraints), self->view, [self childView], @@ -97,9 +98,11 @@ static void uiTabDestroy(uiControl *c) // then destroy all the children for (page in t->pages) { [page removeChildConstraints]; - uiControlSetParent(page.c, NULL); - uiDarwinControlSetSuperview(uiDarwinControl(page.c), nil); - uiControlDestroy(page.c); + if (page.c != NULL) { + uiControlSetParent(page.c, NULL); + uiDarwinControlSetSuperview(uiDarwinControl(page.c), nil); + uiControlDestroy(page.c); + } } // and finally destroy ourselves [t->pages release]; @@ -187,23 +190,26 @@ void uiTabInsertAt(uiTab *t, const char *name, uintmax_t n, uiControl *child) NSTabViewItem *i; NSObject *pageID; - uiControlSetParent(child, uiControl(t)); - view = [[NSView alloc] initWithFrame:NSZeroRect]; - // TODO if we turn off the autoresizing mask, nothing shows up; didn't this get documented somewhere? - uiDarwinControlSetSuperview(uiDarwinControl(child), view); - uiDarwinControlSyncEnableState(uiDarwinControl(child), uiControlEnabledToUser(uiControl(t))); + // don't turn off the autoresizing mask on view; if we do, nothing shows up + if (child != NULL) { + uiControlSetParent(child, uiControl(t)); + uiDarwinControlSetSuperview(uiDarwinControl(child), view); + uiDarwinControlSyncEnableState(uiDarwinControl(child), uiControlEnabledToUser(uiControl(t))); + } // the documentation says these can be nil but the headers say these must not be; let's be safe and make them non-nil anyway pageID = [NSObject new]; page = [[tabPage alloc] initWithView:view pageID:pageID]; page.c = child; - // don't hug, just in case we're a stretchy tab - page.oldHorzHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(page.c), NSLayoutConstraintOrientationHorizontal); - page.oldVertHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(page.c), NSLayoutConstraintOrientationVertical); - uiDarwinControlSetHuggingPriority(uiDarwinControl(page.c), NSLayoutPriorityDefaultLow, NSLayoutConstraintOrientationHorizontal); - uiDarwinControlSetHuggingPriority(uiDarwinControl(page.c), NSLayoutPriorityDefaultLow, NSLayoutConstraintOrientationVertical); + if (page.c != NULL) { + // don't hug, just in case we're a stretchy tab + page.oldHorzHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(page.c), NSLayoutConstraintOrientationHorizontal); + page.oldVertHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(page.c), NSLayoutConstraintOrientationVertical); + uiDarwinControlSetHuggingPriority(uiDarwinControl(page.c), NSLayoutPriorityDefaultLow, NSLayoutConstraintOrientationHorizontal); + uiDarwinControlSetHuggingPriority(uiDarwinControl(page.c), NSLayoutPriorityDefaultLow, NSLayoutConstraintOrientationVertical); + } [t->pages insertObject:page atIndex:n]; [page release]; // no need for initial reference @@ -228,15 +234,19 @@ void uiTabDelete(uiTab *t, uintmax_t n) page = (tabPage *) [t->pages objectAtIndex:n]; - uiDarwinControlSetHuggingPriority(uiDarwinControl(page.c), page.oldHorzHuggingPri, NSLayoutConstraintOrientationHorizontal); - uiDarwinControlSetHuggingPriority(uiDarwinControl(page.c), page.oldVertHuggingPri, NSLayoutConstraintOrientationVertical); + if (page.c != NULL) { + uiDarwinControlSetHuggingPriority(uiDarwinControl(page.c), page.oldHorzHuggingPri, NSLayoutConstraintOrientationHorizontal); + uiDarwinControlSetHuggingPriority(uiDarwinControl(page.c), page.oldVertHuggingPri, NSLayoutConstraintOrientationVertical); + } child = page.c; [page removeChildConstraints]; [t->pages removeObjectAtIndex:n]; - uiControlSetParent(child, NULL); - uiDarwinControlSetSuperview(uiDarwinControl(child), nil); + if (child != NULL) { + uiControlSetParent(child, NULL); + uiDarwinControlSetSuperview(uiDarwinControl(child), nil); + } i = [t->tabview tabViewItemAtIndex:n]; [t->tabview removeTabViewItem:i]; diff --git a/test/page11.c b/test/page11.c new file mode 100644 index 00000000..cbe3d6cd --- /dev/null +++ b/test/page11.c @@ -0,0 +1,53 @@ +// 14 may 2016 +#include "test.h" + +// TODO add a test for childless windows + +static uiGroup *newg(const char *n, int s) +{ + uiGroup *g; + + g = uiNewGroup(n); + if (s) + uiGroupSetChild(g, NULL); + return g; +} + +static uiTab *newt(int tt) +{ + uiTab *t; + + t = uiNewTab(); + if (tt) + uiTabAppend(t, "Test", NULL); + return t; +} + +uiBox *makePage11(void) +{ + uiBox *page11; + uiBox *ns; + uiBox *s; + + page11 = newHorizontalBox(); + + ns = newVerticalBox(); + uiBoxAppend(ns, uiControl(newg("", 0)), 0); + uiBoxAppend(ns, uiControl(newg("", 1)), 0); + uiBoxAppend(ns, uiControl(newg("Group", 0)), 0); + uiBoxAppend(ns, uiControl(newg("Group", 1)), 0); + uiBoxAppend(ns, uiControl(newt(0)), 0); + uiBoxAppend(ns, uiControl(newt(1)), 0); + uiBoxAppend(page11, uiControl(ns), 1); + + s = newVerticalBox(); + uiBoxAppend(s, uiControl(newg("", 0)), 1); + uiBoxAppend(s, uiControl(newg("", 1)), 1); + uiBoxAppend(s, uiControl(newg("Group", 0)), 1); + uiBoxAppend(s, uiControl(newg("Group", 1)), 1); + uiBoxAppend(s, uiControl(newt(0)), 1); + uiBoxAppend(s, uiControl(newt(1)), 1); + uiBoxAppend(page11, uiControl(s), 1); + + return page11; +} From 2c692eda366a6e61b05f89d9d54b3008adceba7f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 14 May 2016 22:10:43 -0400 Subject: [PATCH 0022/1329] More TODOs. --- darwin/group.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/darwin/group.m b/darwin/group.m index e20171d4..4baca240 100644 --- a/darwin/group.m +++ b/darwin/group.m @@ -1,7 +1,7 @@ // 14 august 2015 #import "uipriv_darwin.h" -// TODO test empty groups +// TODO just outright ban passing NULL to any parents? that still won't fix the initial case struct uiGroup { uiDarwinControl c; From 717486b7a20133478cd342d7e4fc35f915386567 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 14 May 2016 23:44:07 -0400 Subject: [PATCH 0023/1329] Fixed some OS X 10.7 build errors. --- build/GNUmakefile.example | 2 +- build/GNUmakefile.test | 3 ++- darwin/uipriv_darwin.h | 3 +++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/build/GNUmakefile.example b/build/GNUmakefile.example index 31be42ca..1219ac74 100644 --- a/build/GNUmakefile.example +++ b/build/GNUmakefile.example @@ -33,7 +33,7 @@ ifeq ($(TOOLCHAIN),gcc) LDFLAGS += -L$(OUTDIR) -lui # see build/GNUmakefile.test ifeq ($(OS),darwin) - LDFLAGS += -Wl,-rpath,@executable_path + LDFLAGS += -Wl,-rpath,@executable_path/ else LDFLAGS += -Wl,-rpath,'$$ORIGIN' endif diff --git a/build/GNUmakefile.test b/build/GNUmakefile.test index 6b992021..66e1921c 100644 --- a/build/GNUmakefile.test +++ b/build/GNUmakefile.test @@ -18,8 +18,9 @@ ifeq ($(TOOLCHAIN),gcc) # tell the dynamic loader to search in the executable path for shared objects # note: OS X's linker complains if we say -rpath= instead of -rpath, # also note that OS X doesn't use $ORIGIN - see http://jorgen.tjer.no/post/2014/05/20/dt-rpath-ld-and-at-rpath-dyld/ + # the extra slash is needed on OS X 10.7 ifeq ($(OS),darwin) - LDFLAGS += -Wl,-rpath,@executable_path + LDFLAGS += -Wl,-rpath,@executable_path/ else LDFLAGS += -Wl,-rpath,'$$ORIGIN' endif diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index effbfbd4..6494e066 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -10,6 +10,9 @@ #error Sorry, libui cannot be compiled with ARC. #endif +// 10.7 fixups +#define NSEventModifierFlags NSUInteger + #define toNSString(str) [NSString stringWithUTF8String:(str)] #define fromNSString(str) [(str) UTF8String] From 2f2db46109211bc85017e75d98a6e1b89fffb734 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 May 2016 13:56:53 -0400 Subject: [PATCH 0024/1329] Reverted the uiTab changes to allow NULL. Still debating whether to allow NULL or not. --- darwin/tab.m | 44 +++++++++++++++++--------------------------- 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/darwin/tab.m b/darwin/tab.m index a98bf0fd..0e7a0d19 100644 --- a/darwin/tab.m +++ b/darwin/tab.m @@ -58,7 +58,6 @@ struct uiTab { { [self removeChildConstraints]; if (self.c == NULL) - // TODO make sure we need the margins return; singleChildConstraintsEstablish(&(self->constraints), self->view, [self childView], @@ -98,11 +97,9 @@ static void uiTabDestroy(uiControl *c) // then destroy all the children for (page in t->pages) { [page removeChildConstraints]; - if (page.c != NULL) { - uiControlSetParent(page.c, NULL); - uiDarwinControlSetSuperview(uiDarwinControl(page.c), nil); - uiControlDestroy(page.c); - } + uiControlSetParent(page.c, NULL); + uiDarwinControlSetSuperview(uiDarwinControl(page.c), nil); + uiControlDestroy(page.c); } // and finally destroy ourselves [t->pages release]; @@ -190,26 +187,23 @@ void uiTabInsertAt(uiTab *t, const char *name, uintmax_t n, uiControl *child) NSTabViewItem *i; NSObject *pageID; + uiControlSetParent(child, uiControl(t)); + view = [[NSView alloc] initWithFrame:NSZeroRect]; - // don't turn off the autoresizing mask on view; if we do, nothing shows up - if (child != NULL) { - uiControlSetParent(child, uiControl(t)); - uiDarwinControlSetSuperview(uiDarwinControl(child), view); - uiDarwinControlSyncEnableState(uiDarwinControl(child), uiControlEnabledToUser(uiControl(t))); - } + // TODO if we turn off the autoresizing mask, nothing shows up; didn't this get documented somewhere? + uiDarwinControlSetSuperview(uiDarwinControl(child), view); + uiDarwinControlSyncEnableState(uiDarwinControl(child), uiControlEnabledToUser(uiControl(t))); // the documentation says these can be nil but the headers say these must not be; let's be safe and make them non-nil anyway pageID = [NSObject new]; page = [[tabPage alloc] initWithView:view pageID:pageID]; page.c = child; - if (page.c != NULL) { - // don't hug, just in case we're a stretchy tab - page.oldHorzHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(page.c), NSLayoutConstraintOrientationHorizontal); - page.oldVertHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(page.c), NSLayoutConstraintOrientationVertical); - uiDarwinControlSetHuggingPriority(uiDarwinControl(page.c), NSLayoutPriorityDefaultLow, NSLayoutConstraintOrientationHorizontal); - uiDarwinControlSetHuggingPriority(uiDarwinControl(page.c), NSLayoutPriorityDefaultLow, NSLayoutConstraintOrientationVertical); - } + // don't hug, just in case we're a stretchy tab + page.oldHorzHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(page.c), NSLayoutConstraintOrientationHorizontal); + page.oldVertHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(page.c), NSLayoutConstraintOrientationVertical); + uiDarwinControlSetHuggingPriority(uiDarwinControl(page.c), NSLayoutPriorityDefaultLow, NSLayoutConstraintOrientationHorizontal); + uiDarwinControlSetHuggingPriority(uiDarwinControl(page.c), NSLayoutPriorityDefaultLow, NSLayoutConstraintOrientationVertical); [t->pages insertObject:page atIndex:n]; [page release]; // no need for initial reference @@ -234,19 +228,15 @@ void uiTabDelete(uiTab *t, uintmax_t n) page = (tabPage *) [t->pages objectAtIndex:n]; - if (page.c != NULL) { - uiDarwinControlSetHuggingPriority(uiDarwinControl(page.c), page.oldHorzHuggingPri, NSLayoutConstraintOrientationHorizontal); - uiDarwinControlSetHuggingPriority(uiDarwinControl(page.c), page.oldVertHuggingPri, NSLayoutConstraintOrientationVertical); - } + uiDarwinControlSetHuggingPriority(uiDarwinControl(page.c), page.oldHorzHuggingPri, NSLayoutConstraintOrientationHorizontal); + uiDarwinControlSetHuggingPriority(uiDarwinControl(page.c), page.oldVertHuggingPri, NSLayoutConstraintOrientationVertical); child = page.c; [page removeChildConstraints]; [t->pages removeObjectAtIndex:n]; - if (child != NULL) { - uiControlSetParent(child, NULL); - uiDarwinControlSetSuperview(uiDarwinControl(child), nil); - } + uiControlSetParent(child, NULL); + uiDarwinControlSetSuperview(uiDarwinControl(child), nil); i = [t->tabview tabViewItemAtIndex:n]; [t->tabview removeTabViewItem:i]; From 20994639c000daee748b2a0cf13cf79e32c76740 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 May 2016 16:26:43 -0400 Subject: [PATCH 0025/1329] Made other windows get events when a dialog is running on OS X. Of course I only now realize this creates a recursiion problem... --- darwin/stddialogs.m | 17 +++++++++++++---- darwin/window.m | 28 ++++++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/darwin/stddialogs.m b/darwin/stddialogs.m index c9639a5e..e32389b2 100644 --- a/darwin/stddialogs.m +++ b/darwin/stddialogs.m @@ -1,8 +1,6 @@ // 26 june 2015 #import "uipriv_darwin.h" -// TODO while a dialog is running no other window receives events - #define windowWindow(w) ((NSWindow *) uiControlHandle(uiControl(w))) // source of code modal logic: http://stackoverflow.com/questions/604768/wait-for-nsalert-beginsheetmodalforwindow @@ -20,11 +18,16 @@ static void setupSavePanel(NSSavePanel *s) static char *runSavePanel(NSWindow *parent, NSSavePanel *s) { char *filename; + NSInteger res; + // TODO formalize in headers + [(id)parent setWorksWhenModal:NO]; [s beginSheetModalForWindow:parent completionHandler:^(NSInteger result) { [realNSApp() stopModalWithCode:result]; }]; - if ([realNSApp() runModalForWindow:s] != NSFileHandlingPanelOKButton) + res = [realNSApp() runModalForWindow:s]; + [(id)parent setWorksWhenModal:YES]; + if (res != NSFileHandlingPanelOKButton) return NULL; filename = uiDarwinNSStringToText([[s URL] path]); return filename; @@ -78,11 +81,17 @@ char *uiSaveFile(uiWindow *parent) - (NSInteger)run { + NSInteger res; + + // TODO like above + [(id)(self->parent) setWorksWhenModal:NO]; [self->panel beginSheetModalForWindow:self->parent modalDelegate:self didEndSelector:@selector(panelEnded:result:data:) contextInfo:NULL]; - return [realNSApp() runModalForWindow:[self->panel window]]; + res = [realNSApp() runModalForWindow:[self->panel window]]; + [(id)(self->parent) setWorksWhenModal:YES]; + return res; } - (void)panelEnded:(NSAlert *)panel result:(NSInteger)result data:(void *)data diff --git a/darwin/window.m b/darwin/window.m index a36cff83..3a7e4678 100644 --- a/darwin/window.m +++ b/darwin/window.m @@ -1,9 +1,16 @@ // 15 august 2015 #import "uipriv_darwin.h" +// don't stop other windows from working when one is modal +@interface modalWindow : NSWindow { + BOOL worksModal; +} +- (BOOL)setWorksWhenModal:(BOOL)wwm; +@end + struct uiWindow { uiDarwinControl c; - NSWindow *window; + modalWindow *window; uiControl *child; int margined; int (*onClosing)(uiWindow *, void *); @@ -11,6 +18,20 @@ struct uiWindow { struct singleChildConstraints constraints; }; +@implementation modalWindow + +- (BOOL)worksWhenModal +{ + return self->worksModal; +} + +- (void)setWorksWhenModal:(BOOL)wwm +{ + self->worksModal = wwm; +} + +@end + @interface windowDelegateClass : NSObject { struct mapTable *windows; } @@ -242,12 +263,15 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) uiDarwinNewControl(uiWindow, w); - w->window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, (CGFloat) width, (CGFloat) height) + w->window = [[modalWindow alloc] initWithContentRect:NSMakeRect(0, 0, (CGFloat) width, (CGFloat) height) styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask) backing:NSBackingStoreBuffered defer:YES]; [w->window setTitle:toNSString(title)]; + // default is to work when modal; only the modal owner window should not work when modal + [w->window setWorksWhenModal:YES]; + // explicitly release when closed // the only thing that closes the window is us anyway [w->window setReleasedWhenClosed:YES]; From 52fff1520dbdd7818e86aa6a349e33444f11044e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 May 2016 16:34:14 -0400 Subject: [PATCH 0026/1329] Disabled page 11 for now. --- test/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/main.c b/test/main.c index add52e99..d978f055 100644 --- a/test/main.c +++ b/test/main.c @@ -135,8 +135,8 @@ int main(int argc, char *argv[]) innerTab = newTab(); uiTabAppend(outerTab, "Pages 11-?", uiControl(innerTab)); - page11 = makePage11(); - uiTabAppend(innerTab, "Page 11", uiControl(page11)); +// page11 = makePage11(); +// uiTabAppend(innerTab, "Page 11", uiControl(page11)); if (startspaced) setSpaced(1); From 0552e7c4a182b96618416849a30e54c281e4fdcd Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 May 2016 19:04:35 -0400 Subject: [PATCH 0027/1329] Revert "Made other windows get events when a dialog is running on OS X. Of course I only now realize this creates a recursiion problem..." This isn't going to work. The only real solution is to disable every window like we're already doing here, make sure it happens on GTK+, and re-add the dialog helper stuff on Windows. This reverts commit 20994639c000daee748b2a0cf13cf79e32c76740. --- darwin/stddialogs.m | 17 ++++------------- darwin/window.m | 28 ++-------------------------- 2 files changed, 6 insertions(+), 39 deletions(-) diff --git a/darwin/stddialogs.m b/darwin/stddialogs.m index e32389b2..c9639a5e 100644 --- a/darwin/stddialogs.m +++ b/darwin/stddialogs.m @@ -1,6 +1,8 @@ // 26 june 2015 #import "uipriv_darwin.h" +// TODO while a dialog is running no other window receives events + #define windowWindow(w) ((NSWindow *) uiControlHandle(uiControl(w))) // source of code modal logic: http://stackoverflow.com/questions/604768/wait-for-nsalert-beginsheetmodalforwindow @@ -18,16 +20,11 @@ static void setupSavePanel(NSSavePanel *s) static char *runSavePanel(NSWindow *parent, NSSavePanel *s) { char *filename; - NSInteger res; - // TODO formalize in headers - [(id)parent setWorksWhenModal:NO]; [s beginSheetModalForWindow:parent completionHandler:^(NSInteger result) { [realNSApp() stopModalWithCode:result]; }]; - res = [realNSApp() runModalForWindow:s]; - [(id)parent setWorksWhenModal:YES]; - if (res != NSFileHandlingPanelOKButton) + if ([realNSApp() runModalForWindow:s] != NSFileHandlingPanelOKButton) return NULL; filename = uiDarwinNSStringToText([[s URL] path]); return filename; @@ -81,17 +78,11 @@ char *uiSaveFile(uiWindow *parent) - (NSInteger)run { - NSInteger res; - - // TODO like above - [(id)(self->parent) setWorksWhenModal:NO]; [self->panel beginSheetModalForWindow:self->parent modalDelegate:self didEndSelector:@selector(panelEnded:result:data:) contextInfo:NULL]; - res = [realNSApp() runModalForWindow:[self->panel window]]; - [(id)(self->parent) setWorksWhenModal:YES]; - return res; + return [realNSApp() runModalForWindow:[self->panel window]]; } - (void)panelEnded:(NSAlert *)panel result:(NSInteger)result data:(void *)data diff --git a/darwin/window.m b/darwin/window.m index 3a7e4678..a36cff83 100644 --- a/darwin/window.m +++ b/darwin/window.m @@ -1,16 +1,9 @@ // 15 august 2015 #import "uipriv_darwin.h" -// don't stop other windows from working when one is modal -@interface modalWindow : NSWindow { - BOOL worksModal; -} -- (BOOL)setWorksWhenModal:(BOOL)wwm; -@end - struct uiWindow { uiDarwinControl c; - modalWindow *window; + NSWindow *window; uiControl *child; int margined; int (*onClosing)(uiWindow *, void *); @@ -18,20 +11,6 @@ struct uiWindow { struct singleChildConstraints constraints; }; -@implementation modalWindow - -- (BOOL)worksWhenModal -{ - return self->worksModal; -} - -- (void)setWorksWhenModal:(BOOL)wwm -{ - self->worksModal = wwm; -} - -@end - @interface windowDelegateClass : NSObject { struct mapTable *windows; } @@ -263,15 +242,12 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) uiDarwinNewControl(uiWindow, w); - w->window = [[modalWindow alloc] initWithContentRect:NSMakeRect(0, 0, (CGFloat) width, (CGFloat) height) + w->window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, (CGFloat) width, (CGFloat) height) styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask) backing:NSBackingStoreBuffered defer:YES]; [w->window setTitle:toNSString(title)]; - // default is to work when modal; only the modal owner window should not work when modal - [w->window setWorksWhenModal:YES]; - // explicitly release when closed // the only thing that closes the window is us anyway [w->window setReleasedWhenClosed:YES]; From f855453aab345643f247b9ce6d775942ce0eeaae Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 May 2016 19:06:20 -0400 Subject: [PATCH 0028/1329] More TODOs. --- darwin/stddialogs.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/darwin/stddialogs.m b/darwin/stddialogs.m index c9639a5e..eed19ad5 100644 --- a/darwin/stddialogs.m +++ b/darwin/stddialogs.m @@ -1,7 +1,8 @@ // 26 june 2015 #import "uipriv_darwin.h" -// TODO while a dialog is running no other window receives events +// TODO restructure this whole file +// TODO explicitly document this works as we want #define windowWindow(w) ((NSWindow *) uiControlHandle(uiControl(w))) From c82942a81bcf3b648d049bf7d64ed15db7dff7de Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 May 2016 19:15:00 -0400 Subject: [PATCH 0029/1329] More TODO stuff. --- unix/stddialogs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unix/stddialogs.c b/unix/stddialogs.c index 341e0168..3e23b920 100644 --- a/unix/stddialogs.c +++ b/unix/stddialogs.c @@ -1,7 +1,7 @@ // 26 june 2015 #include "uipriv_unix.h" -// TODO while this runs, other windows don't get /any/ events +// TODO figure out why, and describe, that this is the desired behavior #define windowWindow(w) (GTK_WINDOW(uiControlHandle(uiControl(w)))) From 1d08521cb761b577f5957c897677c8775268a895 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 May 2016 19:22:15 -0400 Subject: [PATCH 0030/1329] Started reimplementing the old dialog helper stuff. Now that we're C++ on Windows, we can do this directly in window.cpp and save time. --- windows/uipriv_windows.hpp | 2 ++ windows/window.cpp | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index 1b54e17e..2929fd2b 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -106,6 +106,8 @@ extern BOOL areaFilter(MSG *); extern ATOM registerWindowClass(HICON, HCURSOR); extern void unregisterWindowClass(void); extern void ensureMinimumWindowSize(uiWindow *); +extern void disableAllWindowsExcept(uiWindow *which); +extern void enableAllWindowsExcept(uiWindow *which); // container.cpp #define containerClass L"libui_uiContainerClass" diff --git a/windows/window.cpp b/windows/window.cpp index ca0e5c07..465fd367 100644 --- a/windows/window.cpp +++ b/windows/window.cpp @@ -138,6 +138,8 @@ static int defaultOnClosing(uiWindow *w, void *data) return 0; } +static std::map windows; + static void uiWindowDestroy(uiControl *c) { uiWindow *w = uiWindow(c); @@ -154,6 +156,7 @@ static void uiWindowDestroy(uiControl *c) if (w->menubar != NULL) freeMenubar(w->menubar); // and finally free ourselves + windows.erase(w); uiWindowsEnsureDestroyWindow(w->hwnd); uiFreeControl(uiControl(w)); } @@ -361,6 +364,7 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) uiWindowOnClosing(w, defaultOnClosing, NULL); + windows[w] = true; return w; } @@ -380,3 +384,23 @@ void ensureMinimumWindowSize(uiWindow *w) if (SetWindowPos(w->hwnd, NULL, 0, 0, width, height, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER) == 0) logLastError(L"error resizing window"); } + +void disableAllWindowsExcept(uiWindow *which) +{ + for (auto &w : windows) { + if (w.first == which) + continue; + EnableWindow(w.first->hwnd, FALSE); + } +} + +void enableAllWindowsExcept(uiWindow *which) +{ + for (auto &w : windows) { + if (w.first == which) + continue; + if (!uiControlEnabled(uiControl(w.first)) + continue; + EnableWindow(w.first->hwnd, TRUE); + } +} From 6a81921c1af5f3088cbee386ec033e057e76e94b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 May 2016 19:26:58 -0400 Subject: [PATCH 0031/1329] More TODOs. --- darwin/stddialogs.m | 1 + unix/stddialogs.c | 1 + 2 files changed, 2 insertions(+) diff --git a/darwin/stddialogs.m b/darwin/stddialogs.m index eed19ad5..40f108c8 100644 --- a/darwin/stddialogs.m +++ b/darwin/stddialogs.m @@ -3,6 +3,7 @@ // TODO restructure this whole file // TODO explicitly document this works as we want +// TODO explicitly disable font and color dialogs this way #define windowWindow(w) ((NSWindow *) uiControlHandle(uiControl(w))) diff --git a/unix/stddialogs.c b/unix/stddialogs.c index 3e23b920..626508de 100644 --- a/unix/stddialogs.c +++ b/unix/stddialogs.c @@ -2,6 +2,7 @@ #include "uipriv_unix.h" // TODO figure out why, and describe, that this is the desired behavior +// TODO also make font and color buttons modal #define windowWindow(w) (GTK_WINDOW(uiControlHandle(uiControl(w)))) From 1db302761976b22d7d06d0813a1d2cb87f25b5f3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 May 2016 19:38:45 -0400 Subject: [PATCH 0032/1329] Finished the re-disabling of all windows on Windows dialogs. --- windows/stddialogs.cpp | 21 +++++++++++++++++++-- windows/window.cpp | 2 +- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/windows/stddialogs.cpp b/windows/stddialogs.cpp index dfc372c7..89d26bac 100644 --- a/windows/stddialogs.cpp +++ b/windows/stddialogs.cpp @@ -1,6 +1,9 @@ // 22 may 2015 #include "uipriv_windows.hpp" +// TODO document all this is what we want +// TODO do the same for font and color buttons + // notes: // - FOS_SUPPORTSTREAMABLEITEMS doesn't seem to be supported on windows vista, or at least not with the flags we use // - even with FOS_NOVALIDATE the dialogs will reject invalid filenames (at least on Vista, anyway) @@ -75,16 +78,26 @@ out: char *uiOpenFile(uiWindow *parent) { - return commonItemDialog(windowHWND(parent), + char *res; + + disableAllWindowsExcept(parent); + res = commonItemDialog(windowHWND(parent), CLSID_FileOpenDialog, IID_IFileOpenDialog, FOS_NOCHANGEDIR | FOS_ALLNONSTORAGEITEMS | FOS_NOVALIDATE | FOS_PATHMUSTEXIST | FOS_FILEMUSTEXIST | FOS_SHAREAWARE | FOS_NOTESTFILECREATE | FOS_NODEREFERENCELINKS | FOS_FORCESHOWHIDDEN | FOS_DEFAULTNOMINIMODE); + enableAllWindowsExcept(parent); + return res; } char *uiSaveFile(uiWindow *parent) { - return commonItemDialog(windowHWND(parent), + char *res; + + disableAllWindowsExcept(parent); + res = commonItemDialog(windowHWND(parent), CLSID_FileSaveDialog, IID_IFileSaveDialog, FOS_OVERWRITEPROMPT | FOS_NOCHANGEDIR | FOS_ALLNONSTORAGEITEMS | FOS_NOVALIDATE | FOS_SHAREAWARE | FOS_NOTESTFILECREATE | FOS_NODEREFERENCELINKS | FOS_FORCESHOWHIDDEN | FOS_DEFAULTNOMINIMODE); + enableAllWindowsExcept(parent); + return res; } // TODO switch to TaskDialogIndirect()? @@ -107,10 +120,14 @@ static void msgbox(HWND parent, const char *title, const char *description, TASK void uiMsgBox(uiWindow *parent, const char *title, const char *description) { + disableAllWindowsExcept(parent); msgbox(windowHWND(parent), title, description, TDCBF_OK_BUTTON, NULL); + enableAllWindowsExcept(parent); } void uiMsgBoxError(uiWindow *parent, const char *title, const char *description) { + disableAllWindowsExcept(parent); msgbox(windowHWND(parent), title, description, TDCBF_OK_BUTTON, TD_ERROR_ICON); + enableAllWindowsExcept(parent); } diff --git a/windows/window.cpp b/windows/window.cpp index 465fd367..100008d8 100644 --- a/windows/window.cpp +++ b/windows/window.cpp @@ -399,7 +399,7 @@ void enableAllWindowsExcept(uiWindow *which) for (auto &w : windows) { if (w.first == which) continue; - if (!uiControlEnabled(uiControl(w.first)) + if (!uiControlEnabled(uiControl(w.first))) continue; EnableWindow(w.first->hwnd, TRUE); } From 2226c544308e29d27ccd042ea5acb8e4d2864727 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 May 2016 19:56:01 -0400 Subject: [PATCH 0033/1329] Made the font dialog respect our new modality rules on OS X. --- darwin/fontbutton.m | 19 +++++++++++++++++++ darwin/main.m | 2 ++ darwin/stddialogs.m | 2 +- darwin/uipriv_darwin.h | 1 + 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/darwin/fontbutton.m b/darwin/fontbutton.m index ac3561fa..22bc6465 100644 --- a/darwin/fontbutton.m +++ b/darwin/fontbutton.m @@ -168,6 +168,25 @@ BOOL fontButtonOverrideTargetForAction(SEL sel, id from, id to, id *override) return YES; } +// we also don't want the panel to be usable when there's a dialog running; see stddialogs.m for more details on that +// unfortunately the panel seems to ignore -setWorksWhenModal: so we'll have to do things ourselves +@interface nonModalFontPanel : NSFontPanel +@end + +@implementation nonModalFontPanel + +- (BOOL)worksWhenModal +{ + return NO; +} + +@end + +void setupFontPanel(void) +{ + [NSFontManager setFontPanelFactory:[nonModalFontPanel class]]; +} + static void defaultOnChanged(uiFontButton *b, void *data) { // do nothing diff --git a/darwin/main.m b/darwin/main.m index e40c0616..bd939d4a 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -110,6 +110,8 @@ const char *uiInit(uiInitOptions *o) appDelegate().menuManager = [menuManager new]; [realNSApp() setMainMenu:[appDelegate().menuManager makeMenubar]]; + setupFontPanel(); + return NULL; } diff --git a/darwin/stddialogs.m b/darwin/stddialogs.m index 40f108c8..07f4ab03 100644 --- a/darwin/stddialogs.m +++ b/darwin/stddialogs.m @@ -3,7 +3,7 @@ // TODO restructure this whole file // TODO explicitly document this works as we want -// TODO explicitly disable font and color dialogs this way +// TODO explicitly disable color dialogs this way #define windowWindow(w) ((NSWindow *) uiControlHandle(uiControl(w))) diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 6494e066..2bfaaf20 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -109,3 +109,4 @@ extern void doDrawText(CGContextRef c, CGFloat cheight, double x, double y, uiDr // fontbutton.m extern BOOL fontButtonInhibitSendAction(SEL sel, id from, id to); extern BOOL fontButtonOverrideTargetForAction(SEL sel, id from, id to, id *override); +extern void setupFontPanel(void); From 68ad5f53fab95c98ead5dedba52241c5d218fa38 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 May 2016 20:06:57 -0400 Subject: [PATCH 0034/1329] Added uiColorButton. Let's implement this on OS X first. --- test/page10.c | 16 ++++++++++++++++ ui.h | 7 +++++++ 2 files changed, 23 insertions(+) diff --git a/test/page10.c b/test/page10.c index 4af59cbc..69191860 100644 --- a/test/page10.c +++ b/test/page10.c @@ -6,6 +6,7 @@ static uiEntry *textString; static uiFontButton *textFontButton; +static uiFontButton *textColorButton; static uiEntry *textWidth; static uiButton *textApply; static uiCheckbox *addLeading; @@ -27,6 +28,7 @@ static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *dp) { uiDrawTextFont *font; uiDrawTextLayout *layout; + double, r, g, b, a; font = uiFontButtonFont(textFontButton); @@ -37,6 +39,10 @@ static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *dp) uiDrawTextLayoutSetColor(layout, 8, 14, 1, 0, 0.5, 0.5); + uiColorButtonColor(textColorButton, &r, &g, &b, &a); + uiDrawTextLayoutSetColor(layout, + 14, 18, + r, g, b, a); uiDrawText(dp->Context, 10, 10, layout); uiDrawFreeTextLayout(layout); @@ -69,6 +75,11 @@ static void onFontChanged(uiFontButton *b, void *data) uiAreaQueueRedrawAll(textArea); } +static void onColorChanged(uiColorButton *b, void *data) +{ + uiAreaQueueRedrawAll(textArea); +} + static void onTextApply(uiButton *b, void *data) { uiAreaQueueRedrawAll(textArea); @@ -95,6 +106,10 @@ uiBox *makePage10(void) uiFontButtonOnChanged(textFontButton, onFontChanged, NULL); uiBoxAppend(hbox, uiControl(textFontButton), 1); + textColorButton = uiNewColorButton(); + uiColorButtonOnChanged(textColorButton, onColorChanged, NULL); + uiBoxAppend(hbox, uiControl(textColorButton), 1); + hbox = newHorizontalBox(); uiBoxAppend(vbox, uiControl(hbox), 0); @@ -122,6 +137,7 @@ uiBox *makePage10(void) hbox = newHorizontalBox(); uiBoxAppend(vbox, uiControl(hbox), 0); uiBoxAppend(hbox, uiControl(uiNewFontButton()), 1); + uiBoxAppend(hbox, uiControl(uiNewColorButton()), 1); return page10; } diff --git a/ui.h b/ui.h index 26da72df..935c09a1 100644 --- a/ui.h +++ b/ui.h @@ -590,6 +590,13 @@ _UI_EXTERN uiDrawTextFont *uiFontButtonFont(uiFontButton *b); _UI_EXTERN void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data); _UI_EXTERN uiFontButton *uiNewFontButton(void); +typedef struct uiColorButton uiColorButton; +#define uiColorButton(this) ((uiColorButton *) (this)) +_UI_EXTERN void uiColorButtonColor(uiColorButton *b, double *r, double *g, double *bl, double *a); +_UI_EXTERN void uiColorButtonSetColor(uiColorButton *b, double r, double g, double b, double a); +_UI_EXTERN void uiColorButtonOnChanged(uiColorButton *b, void (*f)(uiColorButton *, void *), void *data); +_UI_EXTERN uiColorButton *uiNewColorButton(void); + #ifdef __cplusplus } #endif From bf411e787e8c20d55e0351fe463e8619f226ddf5 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 May 2016 20:51:33 -0400 Subject: [PATCH 0035/1329] Implemented uiColorButton on OS X. --- common/controlsigs.h | 1 + darwin/GNUfiles.mk | 1 + darwin/colorbutton.m | 148 +++++++++++++++++++++++++++++++++++++++++ darwin/main.m | 2 + darwin/uipriv_darwin.h | 3 + test/page10.c | 8 +-- ui.h | 2 +- 7 files changed, 160 insertions(+), 5 deletions(-) create mode 100644 darwin/colorbutton.m diff --git a/common/controlsigs.h b/common/controlsigs.h index 598e7342..dc984329 100644 --- a/common/controlsigs.h +++ b/common/controlsigs.h @@ -4,6 +4,7 @@ #define uiBoxSignature 0x426F784C #define uiButtonSignature 0x42746F6E #define uiCheckboxSignature 0x43686B62 +#define uiColorButtonSignature 0x436F6C42 #define uiComboboxSignature 0x436F6D62 #define uiDateTimePickerSignature 0x44545069 #define uiEntrySignature 0x456E7472 diff --git a/darwin/GNUfiles.mk b/darwin/GNUfiles.mk index 1ddbeebc..810ddd16 100644 --- a/darwin/GNUfiles.mk +++ b/darwin/GNUfiles.mk @@ -8,6 +8,7 @@ MFILES += \ darwin/box.m \ darwin/button.m \ darwin/checkbox.m \ + darwin/colorbutton.m \ darwin/combobox.m \ darwin/control.m \ darwin/datetimepicker.m \ diff --git a/darwin/colorbutton.m b/darwin/colorbutton.m new file mode 100644 index 00000000..47f8511e --- /dev/null +++ b/darwin/colorbutton.m @@ -0,0 +1,148 @@ +// 15 may 2016 +#import "uipriv_darwin.h" + +@interface colorButton : NSColorWell { + uiColorButton *libui_b; + BOOL libui_changing; +} +- (id)initWithFrame:(NSRect)frame libuiColorButton:(uiColorButton *)b; +- (void)deactivateOnClose:(NSNotification *)note; +- (void)libuiColor:(double *)r g:(double *)g b:(double *)b a:(double *)a; +- (void)libuiSetColor:(double)r g:(double)g b:(double)b a:(double)a; +@end + +// only one may be active at one time +static colorButton *activeColorButton = nil; + +struct uiColorButton { + uiDarwinControl c; + colorButton *button; + void (*onChanged)(uiColorButton *, void *); + void *onChangedData; +}; + +@implementation colorButton + +- (id)initWithFrame:(NSRect)frame libuiColorButton:(uiColorButton *)b +{ + self = [super initWithFrame:frame]; + if (self) { + // the default color is white; set it to black first (see -setColor: below for why we do it first) + [self libuiSetColor:0.0 g:0.0 b:0.0 a:1.0]; + + self->libui_b = b; + self->libui_changing = NO; + } + return self; +} + +- (void)activate:(BOOL)exclusive +{ + if (activeColorButton != nil) + activeColorButton->libui_changing = YES; + [NSColorPanel setPickerMask:NSColorPanelAllModesMask]; + [[NSColorPanel sharedColorPanel] setShowsAlpha:YES]; + [super activate:YES]; + activeColorButton = self; + // TODO try setWorksWhenModal (I'd need a color well there) + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(deactivateOnClose:) + name:NSWindowWillCloseNotification + object:[NSColorPanel sharedColorPanel]]; +} + +- (void)deactivate +{ + [super deactivate]; + activeColorButton = nil; + if (!self->libui_changing) + [[NSColorPanel sharedColorPanel] orderOut:nil]; + [[NSNotificationCenter defaultCenter] removeObserver:self + name:NSWindowWillCloseNotification + object:[NSColorPanel sharedColorPanel]]; + self->libui_changing = NO; +} + +- (void)deactivateOnClose:(NSNotification *)note +{ + [self deactivate]; +} + +- (void)setColor:(NSColor *)color +{ + uiColorButton *b = self->libui_b; + + [super setColor:color]; + // this is called by NSColorWell's init, so we have to guard + if (b != nil) + (*(b->onChanged))(b, b->onChangedData); +} + +- (void)libuiColor:(double *)r g:(double *)g b:(double *)b a:(double *)a +{ + NSColor *rgba; + CGFloat cr, cg, cb, ca; + + // the given color may not be an RGBA color, which will cause the -getRed:green:blue:alpha: call to throw an exception + // TODO device RGB space? + rgba = [[self color] colorUsingColorSpaceName:NSCalibratedRGBColorSpace]; + [rgba getRed:&cr green:&cg blue:&cb alpha:&ca]; + *r = cr; + *g = cg; + *b = cb; + *a = ca; + // rgba will be autoreleased since it isn't a new or init call +} + +- (void)libuiSetColor:(double)r g:(double)g b:(double)b a:(double)a +{ + // TODO does this set the panel color? does it send a signal? + [self setColor:[NSColor colorWithRed:r green:g blue:b alpha:a]]; +} + +@end + +uiDarwinControlAllDefaults(uiColorButton, button) + +// we do not want color change events to be sent to any controls other than the color buttons +// see main.m for more details +BOOL colorButtonInhibitSendAction(SEL sel, id from, id to) +{ + if (sel != @selector(changeColor:)) + return NO; + return ![to isKindOfClass:[colorButton class]]; +} + +static void defaultOnChanged(uiColorButton *b, void *data) +{ + // do nothing +} + +void uiColorButtonColor(uiColorButton *b, double *r, double *g, double *bl, double *a) +{ + [b->button libuiColor:r g:g b:bl a:a]; +} + +void uiColorButtonSetColor(uiColorButton *b, double r, double g, double bl, double a) +{ + [b->button libuiSetColor:r g:g b:bl a:a]; +} + +void uiColorButtonOnChanged(uiColorButton *b, void (*f)(uiColorButton *, void *), void *data) +{ + b->onChanged = f; + b->onChangedData = data; +} + +uiColorButton *uiNewColorButton(void) +{ + uiColorButton *b; + + uiDarwinNewControl(uiColorButton, b); + + b->button = [[colorButton alloc] initWithFrame:NSZeroRect libuiColorButton:b]; + + uiColorButtonOnChanged(b, defaultOnChanged, NULL); + + return b; +} diff --git a/darwin/main.m b/darwin/main.m index bd939d4a..fa0e5782 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -18,6 +18,8 @@ static BOOL canQuit = NO; // it turns out NSFontManager also sends changeFont: through this; let's inhibit that here too (see fontbutton.m) - (BOOL)sendAction:(SEL)sel to:(id)to from:(id)from { + if (colorButtonInhibitSendAction(sel, from, to)) + return NO; if (fontButtonInhibitSendAction(sel, from, to)) return NO; return [super sendAction:sel to:to from:from]; diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 2bfaaf20..2afded8f 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -110,3 +110,6 @@ extern void doDrawText(CGContextRef c, CGFloat cheight, double x, double y, uiDr extern BOOL fontButtonInhibitSendAction(SEL sel, id from, id to); extern BOOL fontButtonOverrideTargetForAction(SEL sel, id from, id to, id *override); extern void setupFontPanel(void); + +// colorbutton.m +extern BOOL colorButtonInhibitSendAction(SEL sel, id from, id to); diff --git a/test/page10.c b/test/page10.c index 69191860..0262c815 100644 --- a/test/page10.c +++ b/test/page10.c @@ -6,7 +6,7 @@ static uiEntry *textString; static uiFontButton *textFontButton; -static uiFontButton *textColorButton; +static uiColorButton *textColorButton; static uiEntry *textWidth; static uiButton *textApply; static uiCheckbox *addLeading; @@ -28,7 +28,7 @@ static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *dp) { uiDrawTextFont *font; uiDrawTextLayout *layout; - double, r, g, b, a; + double r, g, b, al; font = uiFontButtonFont(textFontButton); @@ -39,10 +39,10 @@ static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *dp) uiDrawTextLayoutSetColor(layout, 8, 14, 1, 0, 0.5, 0.5); - uiColorButtonColor(textColorButton, &r, &g, &b, &a); + uiColorButtonColor(textColorButton, &r, &g, &b, &al); uiDrawTextLayoutSetColor(layout, 14, 18, - r, g, b, a); + r, g, b, al); uiDrawText(dp->Context, 10, 10, layout); uiDrawFreeTextLayout(layout); diff --git a/ui.h b/ui.h index 935c09a1..3968f01c 100644 --- a/ui.h +++ b/ui.h @@ -593,7 +593,7 @@ _UI_EXTERN uiFontButton *uiNewFontButton(void); typedef struct uiColorButton uiColorButton; #define uiColorButton(this) ((uiColorButton *) (this)) _UI_EXTERN void uiColorButtonColor(uiColorButton *b, double *r, double *g, double *bl, double *a); -_UI_EXTERN void uiColorButtonSetColor(uiColorButton *b, double r, double g, double b, double a); +_UI_EXTERN void uiColorButtonSetColor(uiColorButton *b, double r, double g, double bl, double a); _UI_EXTERN void uiColorButtonOnChanged(uiColorButton *b, void (*f)(uiColorButton *, void *), void *data); _UI_EXTERN uiColorButton *uiNewColorButton(void); From a038923060f4183ff6d62faa94d2c56e49f52831 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 May 2016 21:02:35 -0400 Subject: [PATCH 0036/1329] Added a color well to the histogram example. --- darwin/colorbutton.m | 2 ++ examples/histogram/main.c | 31 +++++++++++++++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/darwin/colorbutton.m b/darwin/colorbutton.m index 47f8511e..af6d1faa 100644 --- a/darwin/colorbutton.m +++ b/darwin/colorbutton.m @@ -1,6 +1,8 @@ // 15 may 2016 #import "uipriv_darwin.h" +// TODO no intrinsic height? + @interface colorButton : NSColorWell { uiColorButton *libui_b; BOOL libui_changing; diff --git a/examples/histogram/main.c b/examples/histogram/main.c index 669a9033..f2b0e793 100644 --- a/examples/histogram/main.c +++ b/examples/histogram/main.c @@ -9,6 +9,7 @@ uiWindow *mainwin; uiArea *histogram; uiAreaHandler handler; uiSpinbox *datapoints[10]; +uiColorButton *colorButton; int currentPoint = -1; // some metrics @@ -94,6 +95,7 @@ static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *p) uiDrawStrokeParams sp; uiDrawMatrix m; double graphWidth, graphHeight; + double graphR, graphG, graphB, graphA; // fill the area with white setSolidBrush(&brush, colorWhite, 1.0); @@ -134,15 +136,23 @@ static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *p) uiDrawMatrixTranslate(&m, xoffLeft, yoffTop); uiDrawTransform(p->Context, &m); + // now get the color for the graph itself and set up the brush + uiColorButtonColor(colorButton, &graphR, &graphG, &graphB, &graphA); + brush.Type = uiDrawBrushTypeSolid; + brush.R = graphR; + brush.G = graphG; + brush.B = graphB; + // we set brush->A below to different values for the fill and stroke + // now create the fill for the graph below the graph line path = constructGraph(graphWidth, graphHeight, 1); - setSolidBrush(&brush, colorDodgerBlue, 0.5); + brush.A = graphA / 2; uiDrawFill(p->Context, path, &brush); uiDrawFreePath(path); // now draw the histogram line path = constructGraph(graphWidth, graphHeight, 0); - setSolidBrush(&brush, colorDodgerBlue, 1.0); + brush.A = graphA; uiDrawStroke(p->Context, path, &brush, &sp); uiDrawFreePath(path); @@ -216,6 +226,11 @@ static void onDatapointChanged(uiSpinbox *s, void *data) uiAreaQueueRedrawAll(histogram); } +static void onColorChanged(uiColorButton *b, void *data) +{ + uiAreaQueueRedrawAll(histogram); +} + static int onClosing(uiWindow *w, void *data) { uiControlDestroy(uiControl(mainwin)); @@ -235,6 +250,7 @@ int main(void) const char *err; uiBox *hbox, *vbox; int i; + uiDrawBrush brush; handler.Draw = handlerDraw; handler.MouseEvent = handlerMouseEvent; @@ -272,6 +288,17 @@ int main(void) uiBoxAppend(vbox, uiControl(datapoints[i]), 0); } + colorButton = uiNewColorButton(); + // TODO inline these + setSolidBrush(&brush, colorDodgerBlue, 1.0); + uiColorButtonSetColor(colorButton, + brush.R, + brush.G, + brush.B, + brush.A); + uiColorButtonOnChanged(colorButton, onColorChanged, NULL); + uiBoxAppend(vbox, uiControl(colorButton), 0); + histogram = uiNewArea(&handler); uiBoxAppend(hbox, uiControl(histogram), 1); From b47689090e60611f93d10759cd1cbde28db26269 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 May 2016 23:18:11 -0400 Subject: [PATCH 0037/1329] Implemented uiColorButton on GTK+. --- unix/GNUfiles.mk | 1 + unix/colorbutton.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 unix/colorbutton.c diff --git a/unix/GNUfiles.mk b/unix/GNUfiles.mk index ef83c13f..7b35e890 100644 --- a/unix/GNUfiles.mk +++ b/unix/GNUfiles.mk @@ -7,6 +7,7 @@ CFILES += \ unix/button.c \ unix/checkbox.c \ unix/child.c \ + unix/colorbutton.c \ unix/combobox.c \ unix/control.c \ unix/datetimepicker.c \ diff --git a/unix/colorbutton.c b/unix/colorbutton.c new file mode 100644 index 00000000..393b16f1 --- /dev/null +++ b/unix/colorbutton.c @@ -0,0 +1,80 @@ +// 15 may 2016 +#include "uipriv_unix.h" + +struct uiColorButton { + uiUnixControl c; + GtkWidget *widget; + GtkButton *button; + GtkColorButton *cb; + GtkColorChooser *cc; + void (*onChanged)(uiColorButton *, void *); + void *onChangedData; +}; + +uiUnixControlAllDefaults(uiColorButton) + +static void onColorSet(GtkColorButton *button, gpointer data) +{ + uiColorButton *b = uiColorButton(data); + + (*(b->onChanged))(b, b->onChangedData); +} + +static void defaultOnChanged(uiColorButton *b, void *data) +{ + // do nothing +} + +void uiColorButtonColor(uiColorButton *b, double *r, double *g, double *bl, double *a) +{ + GdkRGBA rgba; + + gtk_color_chooser_get_rgba(b->cc, &rgba); + *r = rgba.red; + *g = rgba.green; + *bl = rgba.blue; + *a = rgba.alpha; +} + +void uiColorButtonSetColor(uiColorButton *b, double r, double g, double bl, double a) +{ + GdkRGBA rgba; + + rgba.red = r; + rgba.green = g; + rgba.blue = bl; + rgba.alpha = a; + // no need to inhibit the signal; color-set is documented as only being sent when the user changes the color + gtk_color_chooser_set_rgba(b->cc, &rgba); +} + +void uiColorButtonOnChanged(uiColorButton *b, void (*f)(uiColorButton *, void *), void *data) +{ + b->onChanged = f; + b->onChangedData = data; +} + +uiColorButton *uiNewColorButton(void) +{ + uiColorButton *b; + GdkRGBA black; + + uiUnixNewControl(uiColorButton, b); + + // I'm not sure what the initial color is; set up a real one + black.red = 0.0; + black.green = 0.0; + black.blue = 0.0; + black.alpha = 1.0; + b->widget = gtk_color_button_new_with_rgba(&black); + b->button = GTK_BUTTON(b->widget); + b->cb = GTK_COLOR_BUTTON(b->widget); + b->cc = GTK_COLOR_CHOOSER(b->widget); + + gtk_color_chooser_set_use_alpha(b->cc, TRUE); + + g_signal_connect(b->widget, "color-set", G_CALLBACK(onColorSet), b); + uiColorButtonOnChanged(b, defaultOnChanged, NULL); + + return b; +} From 695bca9033be7741d746cb7eacbf9518c8d66e10 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 16 May 2016 09:40:02 -0400 Subject: [PATCH 0038/1329] Tested the worksWhenModal stuff for NSColorPanel; it works. --- darwin/colorbutton.m | 3 ++- darwin/stddialogs.m | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/darwin/colorbutton.m b/darwin/colorbutton.m index af6d1faa..5c33228f 100644 --- a/darwin/colorbutton.m +++ b/darwin/colorbutton.m @@ -46,7 +46,8 @@ struct uiColorButton { [[NSColorPanel sharedColorPanel] setShowsAlpha:YES]; [super activate:YES]; activeColorButton = self; - // TODO try setWorksWhenModal (I'd need a color well there) + // see stddialogs.m for details + [[NSColorPanel sharedColorPanel] setWorksWhenModal:NO]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deactivateOnClose:) name:NSWindowWillCloseNotification diff --git a/darwin/stddialogs.m b/darwin/stddialogs.m index 07f4ab03..f6fdc519 100644 --- a/darwin/stddialogs.m +++ b/darwin/stddialogs.m @@ -3,7 +3,7 @@ // TODO restructure this whole file // TODO explicitly document this works as we want -// TODO explicitly disable color dialogs this way +// TODO note that font and color buttons also do this #define windowWindow(w) ((NSWindow *) uiControlHandle(uiControl(w))) From 0ede964a3dfa181351cb70eeac757ce7f8294288 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 16 May 2016 09:58:16 -0400 Subject: [PATCH 0039/1329] TODO updates. --- unix/stddialogs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unix/stddialogs.c b/unix/stddialogs.c index 626508de..e4653442 100644 --- a/unix/stddialogs.c +++ b/unix/stddialogs.c @@ -2,7 +2,7 @@ #include "uipriv_unix.h" // TODO figure out why, and describe, that this is the desired behavior -// TODO also make font and color buttons modal +// TODO also point out that font and color buttons also work like this #define windowWindow(w) (GTK_WINDOW(uiControlHandle(uiControl(w)))) From 475326b111cdc3979e836112ef0170e138cedabc Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 16 May 2016 13:59:11 -0400 Subject: [PATCH 0040/1329] Started implementing uiColorButton on Windows. This implements the color button itself. --- windows/GNUfiles.mk | 1 + windows/colorbutton.cpp | 163 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 164 insertions(+) create mode 100644 windows/colorbutton.cpp diff --git a/windows/GNUfiles.mk b/windows/GNUfiles.mk index 312b3b42..1f691503 100644 --- a/windows/GNUfiles.mk +++ b/windows/GNUfiles.mk @@ -10,6 +10,7 @@ CXXFILES += \ windows/box.cpp \ windows/button.cpp \ windows/checkbox.cpp \ + windows/colorbutton.cpp \ windows/combobox.cpp \ windows/container.cpp \ windows/control.cpp \ diff --git a/windows/colorbutton.cpp b/windows/colorbutton.cpp new file mode 100644 index 00000000..0ec59d16 --- /dev/null +++ b/windows/colorbutton.cpp @@ -0,0 +1,163 @@ +// 16 may 2016 +#include "uipriv_windows.hpp" + +struct uiColorButton { + uiWindowsControl c; + HWND hwnd; + double r; + double g; + double b; + double a; + void (*onChanged)(uiColorButton *, void *); + void *onChangedData; +}; + +static void uiColorButtonDestroy(uiControl *c) +{ + uiColorButton *b = uiColorButton(c); + + uiWindowsUnregisterWM_COMMANDHandler(b->hwnd); + uiWindowsUnregisterWM_NOTIFYHandler(b->hwnd); + uiWindowsEnsureDestroyWindow(b->hwnd); + uiFreeControl(uiControl(b)); +} + +static BOOL onWM_COMMAND(uiControl *c, HWND hwnd, WORD code, LRESULT *lResult) +{ + uiColorButton *b = uiColorButton(c); + HWND parent; + + if (code != BN_CLICKED) + return FALSE; + +/* TODO + parent = GetAncestor(b->hwnd, GA_ROOT); // TODO didn't we have a function for this + if (showColorDialog(parent, &(b->params))) { + updateColorButtonLabel(b); + (*(b->onChanged))(b, b->onChangedData); + } +*/ + + *lResult = 0; + return TRUE; +} + +static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult) +{ + uiColorButton *b = uiColorButton(c); + NMCUSTOMDRAW *nm = (NMCUSTOMDRAW *) nmhdr; + RECT r; + uiWindowsSizing sizing; + int x, y; + + if (nmhdr->code != NM_CUSTOMDRAW) + return FALSE; + // and allow the button to draw its background + if (nm->dwDrawStage != CDDS_PREPAINT) + return FALSE; + + // TODO use Direct2D? either way, draw alpha + uiWindowsEnsureGetClientRect(b->hwnd, &r); + uiWindowsGetSizing(b->hwnd, &sizing); + x = 3; // should be enough + y = 3; + uiWindowsSizingDlgUnitsToPixels(&sizing, &x, &y); + r.left += x; + r.top += y; + r.right -= x; + r.bottom -= y; + // TODO error check +#define comp(x) ((BYTE) (x * 255)) + // TODO free brush + FillRect(nm->hdc, &r, CreateSolidBrush(RGB(comp(b->r), comp(b->g), comp(b->b)))); +#undef comp + + // skip default processing (don't draw text) + *lResult = CDRF_SKIPDEFAULT; + return TRUE; +} + +uiWindowsControlAllDefaultsExceptDestroy(uiColorButton) + +// from http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing +#define buttonHeight 14 + +static void uiColorButtonMinimumSize(uiWindowsControl *c, intmax_t *width, intmax_t *height) +{ + uiColorButton *b = uiColorButton(c); + SIZE size; + uiWindowsSizing sizing; + int y; + + // try the comctl32 version 6 way + size.cx = 0; // explicitly ask for ideal size + size.cy = 0; + if (SendMessageW(b->hwnd, BCM_GETIDEALSIZE, 0, (LPARAM) (&size)) != FALSE) { + *width = size.cx; + *height = size.cy; + return; + } + + // that didn't work; fall back to using Microsoft's metrics + // Microsoft says to use a fixed width for all buttons; this isn't good enough + // use the text width instead, with some edge padding + *width = uiWindowsWindowTextWidth(b->hwnd) + (2 * GetSystemMetrics(SM_CXEDGE)); + y = buttonHeight; + uiWindowsGetSizing(b->hwnd, &sizing); + uiWindowsSizingDlgUnitsToPixels(&sizing, NULL, &y); + *height = y; +} + +static void defaultOnChanged(uiColorButton *b, void *data) +{ + // do nothing +} + +void uiColorButtonColor(uiColorButton *b, double *r, double *g, double *bl, double *a) +{ + *r = b->r; + *g = b->g; + *bl = b->b; + *a = b->a; +} + +void uiColorButtonSetColor(uiColorButton *b, double r, double g, double bl, double a) +{ + b->r = r; + b->g = g; + b->b = bl; + b->a = a; + // TODO don't we have a helper function for this? + InvalidateRect(b->hwnd, NULL, TRUE); +} + +void uiColorButtonOnChanged(uiColorButton *b, void (*f)(uiColorButton *, void *), void *data) +{ + b->onChanged = f; + b->onChangedData = data; +} + +uiColorButton *uiNewColorButton(void) +{ + uiColorButton *b; + + uiWindowsNewControl(uiColorButton, b); + + // initial color is black + b->r = 0.0; + b->g = 0.0; + b->b = 0.0; + b->a = 1.0; + + b->hwnd = uiWindowsEnsureCreateControlHWND(0, + L"button", L" ", // TODO; can't use "" TODO + BS_PUSHBUTTON | WS_TABSTOP, + hInstance, NULL, + TRUE); + + uiWindowsRegisterWM_COMMANDHandler(b->hwnd, onWM_COMMAND, uiControl(b)); + uiWindowsRegisterWM_NOTIFYHandler(b->hwnd, onWM_NOTIFY, uiControl(b)); + uiColorButtonOnChanged(b, defaultOnChanged, NULL); + + return b; +} From 6e7a74928d125eb735270b4beec070d4fd371eb5 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 16 May 2016 14:00:37 -0400 Subject: [PATCH 0041/1329] More TODOs. --- windows/colorbutton.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/windows/colorbutton.cpp b/windows/colorbutton.cpp index 0ec59d16..2c6313c8 100644 --- a/windows/colorbutton.cpp +++ b/windows/colorbutton.cpp @@ -82,6 +82,7 @@ uiWindowsControlAllDefaultsExceptDestroy(uiColorButton) // from http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing #define buttonHeight 14 +// TODO check widths static void uiColorButtonMinimumSize(uiWindowsControl *c, intmax_t *width, intmax_t *height) { uiColorButton *b = uiColorButton(c); From 4b4a5c335f53c8f81d03b161a3cc86efe7dab1d5 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 16 May 2016 17:07:30 -0400 Subject: [PATCH 0042/1329] Laid out the Windows color dialog. --- windows/colorbutton.cpp | 4 ++++ windows/resources.hpp | 26 ++++++++++++++++++++++++ windows/resources.rc | 44 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+) diff --git a/windows/colorbutton.cpp b/windows/colorbutton.cpp index 2c6313c8..dd82340f 100644 --- a/windows/colorbutton.cpp +++ b/windows/colorbutton.cpp @@ -22,6 +22,8 @@ static void uiColorButtonDestroy(uiControl *c) uiFreeControl(uiControl(b)); } +static INT_PTR TODO(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){return uMsg == WM_INITDIALOG;} + static BOOL onWM_COMMAND(uiControl *c, HWND hwnd, WORD code, LRESULT *lResult) { uiColorButton *b = uiColorButton(c); @@ -38,6 +40,8 @@ static BOOL onWM_COMMAND(uiControl *c, HWND hwnd, WORD code, LRESULT *lResult) } */ + DialogBox(hInstance, MAKEINTRESOURCE(rcColorDialog), GetAncestor(b->hwnd, GA_ROOT), TODO); + *lResult = 0; return TRUE; } diff --git a/windows/resources.hpp b/windows/resources.hpp index 5bcbb3d9..96bd5cf8 100644 --- a/windows/resources.hpp +++ b/windows/resources.hpp @@ -2,8 +2,34 @@ #define rcTabPageDialog 100 #define rcFontDialog 101 +#define rcColorDialog 102 #define rcFontFamilyCombobox 1000 #define rcFontStyleCombobox 1001 #define rcFontSizeCombobox 1002 #define rcFontSamplePlacement 1003 + +#define rcColorSVChooser 1100 +#define rcColorHSlider 1101 +#define rcPreview 1102 +#define rcOpacitySlider 1103 +#define rcH 1104 +#define rcS 1105 +#define rcV 1106 +#define rcRDouble 1107 +#define rcRInt 1108 +#define rcGDouble 1109 +#define rcGInt 1110 +#define rcBDouble 1111 +#define rcBInt 1112 +#define rcADouble 1113 +#define rcAInt 1114 +#define rcHex 1115 +#define rcHLabel 1116 +#define rcSLabel 1117 +#define rcVLabel 1118 +#define rcRLabel 1119 +#define rcGLabel 1120 +#define rcBLabel 1121 +#define rcALabel 1122 +#define rcHexLabel 1123 diff --git a/windows/resources.rc b/windows/resources.rc index e5958dab..7029385b 100644 --- a/windows/resources.rc +++ b/windows/resources.rc @@ -47,3 +47,47 @@ BEGIN DEFPUSHBUTTON "OK", IDOK, 141, 181, 45, 14, WS_GROUP PUSHBUTTON "Cancel", IDCANCEL, 190, 181, 45, 14, WS_GROUP END + +rcColorDialog DIALOGEX 13, 54, 344, 209 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU | DS_3DLOOK +CAPTION "Color" +FONT 9, "Segoe UI" +BEGIN + // this size should be big enough to get at least 256x256 on font sizes >= 8 pt + CTEXT "AaBbYyZz", rcColorSVChooser, 7, 7, 195, 195, SS_NOPREFIX | SS_BLACKRECT + + // width is the suggested slider height since this is vertical + CTEXT "AaBbYyZz", rcColorHSlider, 206, 7, 15, 195, SS_NOPREFIX | SS_BLACKRECT + + LTEXT "Preview:", -1, 230, 7, 107, 9, SS_NOPREFIX + CTEXT "AaBbYyZz", rcPreview, 230, 16, 107, 20, SS_NOPREFIX | SS_BLACKRECT + + LTEXT "Opacity:", -1, 230, 45, 107, 9, SS_NOPREFIX + CTEXT "AaBbYyZz", rcOpacitySlider, 230, 54, 107, 15, SS_NOPREFIX | SS_BLACKRECT + + LTEXT "&H:", rcHLabel, 230, 81, 8, 8 + EDITTEXT rcH, 238, 78, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + LTEXT "&S:", rcSLabel, 230, 95, 8, 8 + EDITTEXT rcS, 238, 92, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + LTEXT "&V:", rcVLabel, 230, 109, 8, 8 + EDITTEXT rcV, 238, 106, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + + LTEXT "&R:", rcRLabel, 277, 81, 8, 8 + EDITTEXT rcRDouble, 285, 78, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + EDITTEXT rcRInt, 315, 78, 20, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + LTEXT "&G:", rcGLabel, 277, 95, 8, 8 + EDITTEXT rcGDouble, 285, 92, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + EDITTEXT rcGInt, 315, 92, 20, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + LTEXT "&B:", rcBLabel, 277, 109, 8, 8 + EDITTEXT rcBDouble, 285, 106, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + EDITTEXT rcBInt, 315, 106, 20, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + LTEXT "&A:", rcALabel, 277, 123, 8, 8 + EDITTEXT rcADouble, 285, 120, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + EDITTEXT rcAInt, 315, 120, 20, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + + LTEXT "He&x:", rcHexLabel, 269, 146, 16, 8 + EDITTEXT rcHex, 285, 143, 50, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + + DEFPUSHBUTTON "OK", IDOK, 243, 188, 45, 14, WS_GROUP + PUSHBUTTON "Cancel", IDCANCEL, 292, 188, 45, 14, WS_GROUP +END From 2c160bb5eb82b94be4573776773eec9223848d7a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 16 May 2016 19:28:30 -0400 Subject: [PATCH 0043/1329] Started work on the color dialog itself; positioned controls correctly to start. --- windows/GNUfiles.mk | 1 + windows/colorbutton.cpp | 14 ++- windows/colordialog.cpp | 207 +++++++++++++++++++++++++++++++++++++ windows/fontdialog.cpp | 16 +-- windows/resources.rc | 8 +- windows/uipriv_windows.hpp | 10 ++ windows/winutil.cpp | 10 ++ 7 files changed, 246 insertions(+), 20 deletions(-) create mode 100644 windows/colordialog.cpp diff --git a/windows/GNUfiles.mk b/windows/GNUfiles.mk index 1f691503..2791405b 100644 --- a/windows/GNUfiles.mk +++ b/windows/GNUfiles.mk @@ -11,6 +11,7 @@ CXXFILES += \ windows/button.cpp \ windows/checkbox.cpp \ windows/colorbutton.cpp \ + windows/colordialog.cpp \ windows/combobox.cpp \ windows/container.cpp \ windows/control.cpp \ diff --git a/windows/colorbutton.cpp b/windows/colorbutton.cpp index dd82340f..8c356950 100644 --- a/windows/colorbutton.cpp +++ b/windows/colorbutton.cpp @@ -28,17 +28,23 @@ static BOOL onWM_COMMAND(uiControl *c, HWND hwnd, WORD code, LRESULT *lResult) { uiColorButton *b = uiColorButton(c); HWND parent; + struct colorDialogRGBA rgba; if (code != BN_CLICKED) return FALSE; -/* TODO parent = GetAncestor(b->hwnd, GA_ROOT); // TODO didn't we have a function for this - if (showColorDialog(parent, &(b->params))) { - updateColorButtonLabel(b); + rgba.r = b->r; + rgba.g = b->g; + rgba.b = b->b; + rgba.a = b->a; + if (showColorDialog(parent, &rgba)) { + b->r = rgba.r; + b->g = rgba.g; + b->b = rgba.b; + b->a = rgba.a; (*(b->onChanged))(b, b->onChangedData); } -*/ DialogBox(hInstance, MAKEINTRESOURCE(rcColorDialog), GetAncestor(b->hwnd, GA_ROOT), TODO); diff --git a/windows/colordialog.cpp b/windows/colordialog.cpp new file mode 100644 index 00000000..742cb3be --- /dev/null +++ b/windows/colordialog.cpp @@ -0,0 +1,207 @@ +// 16 may 2016 +#include "uipriv_windows.hpp" + +struct colorDialog { + HWND hwnd; + + HWND svChooser; + HWND hSlider; + HWND opacitySlider; + HWND editH; + HWND editS; + HWND editV; + HWND editRDouble, editRInt; + HWND editGDouble, editGInt; + HWND editBDouble, editBInt; + HWND editADouble, editAInt; + HWND editHex; + + double r; + double g; + double b; + double a; + struct colorDialogRGBA *out; +}; + +static void endColorDialog(struct colorDialog *c, INT_PTR code) +{ + if (EndDialog(c->hwnd, code) == 0) + logLastError(L"error ending color dialog"); + uiFree(c); +} + +static BOOL tryFinishDialog(struct colorDialog *c, WPARAM wParam) +{ + // cancelling + if (LOWORD(wParam) != IDOK) { + endColorDialog(c, 1); + return TRUE; + } + + // OK + c->out->r = c->r; + c->out->g = c->g; + c->out->b = c->b; + c->out->a = c->a; + endColorDialog(c, 2); + return TRUE; +} + +// a few issues: +// - some controls are positioned wrong; see http://stackoverflow.com/questions/37263267/why-are-some-of-my-controls-positioned-slightly-off-in-a-dialog-template-in-a-re +// - labels are too low; need to adjust them by the font's internal leading +// fixupControlPositions() and the following helper routines fix that for us + +static LONG offsetTo(HWND a, HWND b) +{ + RECT ra, rb; + + uiWindowsEnsureGetWindowRect(a, &ra); + uiWindowsEnsureGetWindowRect(b, &rb); + return rb.top - ra.bottom; +} + +static void moveWindowsUp(struct colorDialog *c, LONG by, ...) +{ + va_list ap; + HWND cur; + RECT r; + + va_start(ap, by); + for (;;) { + cur = va_arg(ap, HWND); + if (cur == NULL) + break; + uiWindowsEnsureGetWindowRect(cur, &r); + mapWindowRect(NULL, c->hwnd, &r); + r.top -= by; + r.bottom -= by; + // TODO this isn't technically during a resize + uiWindowsEnsureMoveWindowDuringResize(cur, + r.left, r.top, + r.right - r.left, r.bottom - r.top); + } + va_end(ap); +} + +static void fixupControlPositions(struct colorDialog *c) +{ + HWND labelH; + HWND labelS; + HWND labelV; + HWND labelR; + HWND labelG; + HWND labelB; + HWND labelA; + HWND labelHex; + LONG offset; + uiWindowsSizing sizing; + + labelH = getDlgItem(c->hwnd, rcHLabel); + labelS = getDlgItem(c->hwnd, rcSLabel); + labelV = getDlgItem(c->hwnd, rcVLabel); + labelR = getDlgItem(c->hwnd, rcRLabel); + labelG = getDlgItem(c->hwnd, rcGLabel); + labelB = getDlgItem(c->hwnd, rcBLabel); + labelA = getDlgItem(c->hwnd, rcALabel); + labelHex = getDlgItem(c->hwnd, rcHexLabel); + + offset = offsetTo(c->editH, c->editS); + moveWindowsUp(c, offset, + labelS, c->editS, + labelG, c->editGDouble, c->editGInt, + NULL); + offset = offsetTo(c->editS, c->editV); + moveWindowsUp(c, offset, + labelV, c->editV, + labelB, c->editBDouble, c->editBInt, + NULL); + offset = offsetTo(c->editBDouble, c->editADouble); + moveWindowsUp(c, offset, + labelA, c->editADouble, c->editAInt, + NULL); + + // TODO this uses the message font, not the dialog font + uiWindowsGetSizing(c->hwnd, &sizing); + offset = sizing.InternalLeading; + moveWindowsUp(c, offset, + labelH, labelS, labelV, + labelR, labelG, labelB, labelA, + labelHex, + NULL); +} + +static struct colorDialog *beginColorDialog(HWND hwnd, LPARAM lParam) +{ + struct colorDialog *c; + + c = uiNew(struct colorDialog); + c->hwnd = hwnd; + c->out = (struct colorDialogRGBA *) lParam; + c->r = c->out->r; // load initial values now + c->g = c->out->g; + c->b = c->out->b; + c->a = c->out->a; + + // TODO set up d2dscratches + + c->editH = getDlgItem(c->hwnd, rcH); + c->editS = getDlgItem(c->hwnd, rcS); + c->editV = getDlgItem(c->hwnd, rcV); + c->editRDouble = getDlgItem(c->hwnd, rcRDouble); + c->editRInt = getDlgItem(c->hwnd, rcRInt); + c->editGDouble = getDlgItem(c->hwnd, rcGDouble); + c->editGInt = getDlgItem(c->hwnd, rcGInt); + c->editBDouble = getDlgItem(c->hwnd, rcBDouble); + c->editBInt = getDlgItem(c->hwnd, rcBInt); + c->editADouble = getDlgItem(c->hwnd, rcADouble); + c->editAInt = getDlgItem(c->hwnd, rcAInt); + c->editHex = getDlgItem(c->hwnd, rcHex); + + fixupControlPositions(c); + + return c; +} + +static INT_PTR CALLBACK colorDialogDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + struct colorDialog *c; + + c = (struct colorDialog *) GetWindowLongPtrW(hwnd, DWLP_USER); + if (c == NULL) { + if (uMsg == WM_INITDIALOG) { + c = beginColorDialog(hwnd, lParam); + SetWindowLongPtrW(hwnd, DWLP_USER, (LONG_PTR) c); + return TRUE; + } + return FALSE; + } + + switch (uMsg) { + case WM_COMMAND: + SetWindowLongPtrW(c->hwnd, DWLP_MSGRESULT, 0); // just in case + switch (LOWORD(wParam)) { + case IDOK: + case IDCANCEL: + if (HIWORD(wParam) != BN_CLICKED) + return FALSE; + return tryFinishDialog(c, wParam); + } + return FALSE; + } + return FALSE; +} + +BOOL showColorDialog(HWND parent, struct colorDialogRGBA *c) +{ + switch (DialogBoxParamW(hInstance, MAKEINTRESOURCE(rcColorDialog), parent, colorDialogDlgProc, (LPARAM) c)) { + case 1: // cancel + return FALSE; + case 2: // ok + // make the compiler happy by putting the return after the switch + break; + default: + logLastError(L"error running color dialog"); + } + return TRUE; +} diff --git a/windows/fontdialog.cpp b/windows/fontdialog.cpp index feb264f0..ef1fae11 100644 --- a/windows/fontdialog.cpp +++ b/windows/fontdialog.cpp @@ -467,15 +467,9 @@ static struct fontDialog *beginFontDialog(HWND hwnd, LPARAM lParam) f->hwnd = hwnd; f->params = (struct fontDialogParams *) lParam; - f->familyCombobox = GetDlgItem(f->hwnd, rcFontFamilyCombobox); - if (f->familyCombobox == NULL) - logLastError(L"error getting font family combobox handle"); - f->styleCombobox = GetDlgItem(f->hwnd, rcFontStyleCombobox); - if (f->styleCombobox == NULL) - logLastError(L"error getting font style combobox handle"); - f->sizeCombobox = GetDlgItem(f->hwnd, rcFontSizeCombobox); - if (f->sizeCombobox == NULL) - logLastError(L"error getting font size combobox handle"); + f->familyCombobox = getDlgItem(f->hwnd, rcFontFamilyCombobox); + f->styleCombobox = getDlgItem(f->hwnd, rcFontStyleCombobox); + f->sizeCombobox = getDlgItem(f->hwnd, rcFontSizeCombobox); f->fc = loadFontCollection(); nFamilies = f->fc->fonts->GetFontFamilyCount(); @@ -492,9 +486,7 @@ static struct fontDialog *beginFontDialog(HWND hwnd, LPARAM lParam) for (i = 0; defaultSizes[i].text != NULL; i++) cbInsertString(f->sizeCombobox, defaultSizes[i].text, (WPARAM) i); - samplePlacement = GetDlgItem(f->hwnd, rcFontSamplePlacement); - if (samplePlacement == NULL) - logLastError(L"error getting sample placement static control handle"); + samplePlacement = getDlgItem(f->hwnd, rcFontSamplePlacement); uiWindowsEnsureGetWindowRect(samplePlacement, &(f->sampleRect)); mapWindowRect(NULL, f->hwnd, &(f->sampleRect)); uiWindowsEnsureDestroyWindow(samplePlacement); diff --git a/windows/resources.rc b/windows/resources.rc index 7029385b..a0acfcbd 100644 --- a/windows/resources.rc +++ b/windows/resources.rc @@ -74,16 +74,16 @@ BEGIN LTEXT "&R:", rcRLabel, 277, 81, 8, 8 EDITTEXT rcRDouble, 285, 78, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE - EDITTEXT rcRInt, 315, 78, 20, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + EDITTEXT rcRInt, 315, 78, 20, 14, ES_LEFT | ES_AUTOHSCROLL | ES_NUMBER | WS_TABSTOP, WS_EX_CLIENTEDGE LTEXT "&G:", rcGLabel, 277, 95, 8, 8 EDITTEXT rcGDouble, 285, 92, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE - EDITTEXT rcGInt, 315, 92, 20, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + EDITTEXT rcGInt, 315, 92, 20, 14, ES_LEFT | ES_AUTOHSCROLL | ES_NUMBER | WS_TABSTOP, WS_EX_CLIENTEDGE LTEXT "&B:", rcBLabel, 277, 109, 8, 8 EDITTEXT rcBDouble, 285, 106, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE - EDITTEXT rcBInt, 315, 106, 20, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + EDITTEXT rcBInt, 315, 106, 20, 14, ES_LEFT | ES_AUTOHSCROLL | ES_NUMBER | WS_TABSTOP, WS_EX_CLIENTEDGE LTEXT "&A:", rcALabel, 277, 123, 8, 8 EDITTEXT rcADouble, 285, 120, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE - EDITTEXT rcAInt, 315, 120, 20, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + EDITTEXT rcAInt, 315, 120, 20, 14, ES_LEFT | ES_AUTOHSCROLL | ES_NUMBER | WS_TABSTOP, WS_EX_CLIENTEDGE LTEXT "He&x:", rcHexLabel, 269, 146, 16, 8 EDITTEXT rcHex, 285, 143, 50, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index 2929fd2b..e207f1bc 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -63,6 +63,7 @@ extern void clientSizeToWindowSize(HWND hwnd, intmax_t *width, intmax_t *height, extern HWND parentOf(HWND child); extern HWND parentToplevel(HWND child); extern void setWindowInsertAfter(HWND hwnd, HWND insertAfter); +extern HWND getDlgItem(HWND hwnd, int id); // text.cpp extern WCHAR *windowTextAndLen(HWND hwnd, LRESULT *len); @@ -124,6 +125,15 @@ extern struct tabPage *newTabPage(uiControl *child); extern void tabPageDestroy(struct tabPage *tp); extern void tabPageMinimumSize(struct tabPage *tp, intmax_t *width, intmax_t *height); +// colordialog.cpp +struct colorDialogRGBA { + double r; + double g; + double b; + double a; +}; +extern BOOL showColorDialog(HWND parent, struct colorDialogRGBA *c); + diff --git a/windows/winutil.cpp b/windows/winutil.cpp index b4fa692e..024260f2 100644 --- a/windows/winutil.cpp +++ b/windows/winutil.cpp @@ -120,3 +120,13 @@ void setWindowInsertAfter(HWND hwnd, HWND insertAfter) if (SetWindowPos(hwnd, insertAfter, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOSIZE) == 0) logLastError(L"error reordering window"); } + +HWND getDlgItem(HWND hwnd, int id) +{ + HWND out; + + out = GetDlgItem(hwnd, id); + if (out == NULL) + logLastError(L"error getting dialog item handle"); + return out; +} From 3128e58c5b70492d3ffc2af85ee5455652481fe7 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 16 May 2016 23:54:28 -0400 Subject: [PATCH 0044/1329] Started drawing the SV chooser part of the Windows color dialog. --- windows/colorbutton.cpp | 4 - windows/colordialog.cpp | 229 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 211 insertions(+), 22 deletions(-) diff --git a/windows/colorbutton.cpp b/windows/colorbutton.cpp index 8c356950..21c56cf6 100644 --- a/windows/colorbutton.cpp +++ b/windows/colorbutton.cpp @@ -22,8 +22,6 @@ static void uiColorButtonDestroy(uiControl *c) uiFreeControl(uiControl(b)); } -static INT_PTR TODO(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){return uMsg == WM_INITDIALOG;} - static BOOL onWM_COMMAND(uiControl *c, HWND hwnd, WORD code, LRESULT *lResult) { uiColorButton *b = uiColorButton(c); @@ -46,8 +44,6 @@ static BOOL onWM_COMMAND(uiControl *c, HWND hwnd, WORD code, LRESULT *lResult) (*(b->onChanged))(b, b->onChangedData); } - DialogBox(hInstance, MAKEINTRESOURCE(rcColorDialog), GetAncestor(b->hwnd, GA_ROOT), TODO); - *lResult = 0; return TRUE; } diff --git a/windows/colordialog.cpp b/windows/colordialog.cpp index 742cb3be..7b83f78c 100644 --- a/windows/colordialog.cpp +++ b/windows/colordialog.cpp @@ -23,28 +23,194 @@ struct colorDialog { struct colorDialogRGBA *out; }; -static void endColorDialog(struct colorDialog *c, INT_PTR code) +// both of these are from the wiki +static void rgb2HSV(double r, double g, double b, double *h, double *s, double *v) { - if (EndDialog(c->hwnd, code) == 0) - logLastError(L"error ending color dialog"); - uiFree(c); -} + double M, m; + int whichmax; + double c; -static BOOL tryFinishDialog(struct colorDialog *c, WPARAM wParam) -{ - // cancelling - if (LOWORD(wParam) != IDOK) { - endColorDialog(c, 1); - return TRUE; + M = r; + whichmax = 0; + if (M < g) { + M = g; + whichmax = 1; + } + if (M < b) { + M = b; + whichmax = 2; + } + m = r; + if (m > g) + m = g; + if (m > b) + m = b; + c = M - m; + + if (c == 0) + *h = 0; + else { + switch (whichmax) { + case 0: + *h = ((g - b) / c); + if (*h > 6) + *h -= 6; + break; + case 1: + *h = ((b - r) / c) + 2; + break; + case 2: + *h = ((r - g) / c) + 4; + break; + } + *h /= 6; // put in range [0,1) } - // OK - c->out->r = c->r; - c->out->g = c->g; - c->out->b = c->b; - c->out->a = c->a; - endColorDialog(c, 2); - return TRUE; + *v = M; + + if (c == 0) + *s = 0; + else + *s = c / *v; +} + +static void hsv2RGB(double h, double s, double v, double *r, double *g, double *b) +{ + double c; + int hPrime; + double x; + double m; + double c1, c2; + + c = v * s; + hPrime = (int) (h * 6); // equivalent to splitting into 60° chunks + x = c * (1 - abs(hPrime % 2 - 1)); + m = v - c; + switch (hPrime) { + case 0: + *r = c + m; + *g = x + m; + *b = m; + return; + case 1: + *r = x + m; + *g = c + m; + *b = m; + return; + case 2: + *r = m; + *g = c + m; + *b = x + m; + return; + case 3: + *r = m; + *g = x + m; + *b = c + m; + return; + case 4: + *r = x + m; + *g = m; + *b = c + m; + return; + case 5: + *r = c + m; + *g = m; + *b = x + m; + return; + } + // TODO +} + +// this interesting approach comes from xxxx +static void drawSVChooser(struct colorDialog *c, ID2D1RenderTarget *rt) +{ + D2D1_SIZE_F size; + D2D1_RECT_F rect; + double h, s, v; + double rTop, gTop, bTop; + D2D1_GRADIENT_STOP stops[2]; + ID2D1GradientStopCollection *collection; + D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES lprop; + D2D1_BRUSH_PROPERTIES bprop; + ID2D1LinearGradientBrush *brush; + HRESULT hr; + + size = rt->GetSize(); + rect.left = 0; + rect.top = 0; + rect.right = size.width; + rect.bottom = size.height; + + // TODO draw checkerboard + + // first, draw a vertical gradient from the current hue at max S/V to black + // the source example draws it upside down; let's do so too just to be safe + rgb2HSV(c->r, c->g, c->b, &h, &s, &v); + hsv2RGB(h, 1.0, 1.0, &rTop, &gTop, &bTop); + stops[0].position = 0; + stops[0].color.r = 0.0; + stops[0].color.g = 0.0; + stops[0].color.b = 0.0; + stops[0].color.a = 1.0; + stops[1].position = 1; + stops[1].color.r = rTop; + stops[1].color.g = gTop; + stops[1].color.b = bTop; + stops[1].color.a = 1.0; + hr = rt->CreateGradientStopCollection(stops, 2, + D2D1_GAMMA_2_2, D2D1_EXTEND_MODE_CLAMP, + &collection); + if (hr != S_OK) + logHRESULT(L"error making gradient stop collection for first gradient in SV chooser", hr); + ZeroMemory(&lprop, sizeof (D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES)); + lprop.startPoint.x = size.width / 2; + lprop.startPoint.y = size.height; + lprop.endPoint.x = size.width / 2; + lprop.endPoint.y = 0; + ZeroMemory(&bprop, sizeof (D2D1_BRUSH_PROPERTIES)); + bprop.opacity = 1.0; + bprop.transform._11 = 1; + bprop.transform._22 = 1; + hr = rt->CreateLinearGradientBrush(&lprop, &bprop, + collection, &brush); + if (hr != S_OK) + logHRESULT(L"error making gradient brush for first gradient in SV chooser", hr); + rt->FillRectangle(&rect, brush); + brush->Release(); + collection->Release(); +} + +static LRESULT CALLBACK svChooserSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) +{ + ID2D1RenderTarget *rt; + struct colorDialog *c; + + switch (uMsg) { + case msgD2DScratchPaint: + rt = (ID2D1RenderTarget *) lParam; + c = (struct colorDialog *) dwRefData; + drawSVChooser(c, rt); + return 0; + case WM_NCDESTROY: + if (RemoveWindowSubclass(hwnd, svChooserSubProc, uIdSubclass) == FALSE) + logLastError(L"error removing color dialog SV chooser subclass"); + break; + } + return DefSubclassProc(hwnd, uMsg, wParam, lParam); +} + +// TODO extract into d2dscratch.cpp, use in font dialog +HWND replaceWithD2DScratch(HWND parent, int id, SUBCLASSPROC subproc, void *data) +{ + HWND replace; + RECT r; + + replace = getDlgItem(parent, id); + uiWindowsEnsureGetWindowRect(replace, &r); + mapWindowRect(NULL, parent, &r); + uiWindowsEnsureDestroyWindow(replace); + return newD2DScratch(parent, &r, (HMENU) id, subproc, (DWORD_PTR) data); + // TODO preserve Z-order } // a few issues: @@ -145,6 +311,7 @@ static struct colorDialog *beginColorDialog(HWND hwnd, LPARAM lParam) // TODO set up d2dscratches + // TODO prefix all these with rcColor instead of just rc c->editH = getDlgItem(c->hwnd, rcH); c->editS = getDlgItem(c->hwnd, rcS); c->editV = getDlgItem(c->hwnd, rcV); @@ -158,11 +325,37 @@ static struct colorDialog *beginColorDialog(HWND hwnd, LPARAM lParam) c->editAInt = getDlgItem(c->hwnd, rcAInt); c->editHex = getDlgItem(c->hwnd, rcHex); + c->svChooser = replaceWithD2DScratch(c->hwnd, rcColorSVChooser, svChooserSubProc, c); + fixupControlPositions(c); return c; } +static void endColorDialog(struct colorDialog *c, INT_PTR code) +{ + if (EndDialog(c->hwnd, code) == 0) + logLastError(L"error ending color dialog"); + uiFree(c); +} + +static BOOL tryFinishDialog(struct colorDialog *c, WPARAM wParam) +{ + // cancelling + if (LOWORD(wParam) != IDOK) { + endColorDialog(c, 1); + return TRUE; + } + + // OK + c->out->r = c->r; + c->out->g = c->g; + c->out->b = c->b; + c->out->a = c->a; + endColorDialog(c, 2); + return TRUE; +} + static INT_PTR CALLBACK colorDialogDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { struct colorDialog *c; From 027bb6782b89d780f5122ad7863ba6f17fb302e1 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 17 May 2016 12:18:36 -0400 Subject: [PATCH 0045/1329] Drew the marker on the color panel. Now for the hue slider. --- windows/colordialog.cpp | 111 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 109 insertions(+), 2 deletions(-) diff --git a/windows/colordialog.cpp b/windows/colordialog.cpp index 7b83f78c..78875636 100644 --- a/windows/colordialog.cpp +++ b/windows/colordialog.cpp @@ -23,7 +23,7 @@ struct colorDialog { struct colorDialogRGBA *out; }; -// both of these are from the wiki +// both of these are from the wikipedia page on HSV static void rgb2HSV(double r, double g, double b, double *h, double *s, double *v) { double M, m; @@ -121,7 +121,7 @@ static void hsv2RGB(double h, double s, double v, double *r, double *g, double * // TODO } -// this interesting approach comes from xxxx +// this interesting approach comes from http://blogs.msdn.com/b/wpfsdk/archive/2006/10/26/uncommon-dialogs--font-chooser-and-color-picker-dialogs.aspx static void drawSVChooser(struct colorDialog *c, ID2D1RenderTarget *rt) { D2D1_SIZE_F size; @@ -133,6 +133,12 @@ static void drawSVChooser(struct colorDialog *c, ID2D1RenderTarget *rt) D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES lprop; D2D1_BRUSH_PROPERTIES bprop; ID2D1LinearGradientBrush *brush; + ID2D1LinearGradientBrush *opacity; + ID2D1Layer *layer; + D2D1_LAYER_PARAMETERS layerparams; + D2D1_ELLIPSE mparam; + D2D1_COLOR_F mcolor; + ID2D1SolidColorBrush *markerBrush; HRESULT hr; size = rt->GetSize(); @@ -178,6 +184,107 @@ static void drawSVChooser(struct colorDialog *c, ID2D1RenderTarget *rt) rt->FillRectangle(&rect, brush); brush->Release(); collection->Release(); + + // second, create an opacity mask for the third step: a horizontal gradientthat goes from opaque to translucent + stops[0].position = 0; + stops[0].color.r = 0.0; + stops[0].color.g = 0.0; + stops[0].color.b = 0.0; + stops[0].color.a = 1.0; + stops[1].position = 1; + stops[1].color.r = 0.0; + stops[1].color.g = 0.0; + stops[1].color.b = 0.0; + stops[1].color.a = 0.0; + hr = rt->CreateGradientStopCollection(stops, 2, + D2D1_GAMMA_2_2, D2D1_EXTEND_MODE_CLAMP, + &collection); + if (hr != S_OK) + logHRESULT(L"error making gradient stop collection for opacity mask gradient in SV chooser", hr); + ZeroMemory(&lprop, sizeof (D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES)); + lprop.startPoint.x = 0; + lprop.startPoint.y = size.height / 2; + lprop.endPoint.x = size.width; + lprop.endPoint.y = size.height / 2; + ZeroMemory(&bprop, sizeof (D2D1_BRUSH_PROPERTIES)); + bprop.opacity = 1.0; + bprop.transform._11 = 1; + bprop.transform._22 = 1; + hr = rt->CreateLinearGradientBrush(&lprop, &bprop, + collection, &opacity); + if (hr != S_OK) + logHRESULT(L"error making gradient brush for opacity mask gradient in SV chooser", hr); + collection->Release(); + + // finally, make a vertical gradient from white at the top to black at the bottom (right side up this time) and with the previous opacity mask + stops[0].position = 0; + stops[0].color.r = 1.0; + stops[0].color.g = 1.0; + stops[0].color.b = 1.0; + stops[0].color.a = 1.0; + stops[1].position = 1; + stops[1].color.r = 0.0; + stops[1].color.g = 0.0; + stops[1].color.b = 0.0; + stops[1].color.a = 1.0; + hr = rt->CreateGradientStopCollection(stops, 2, + D2D1_GAMMA_2_2, D2D1_EXTEND_MODE_CLAMP, + &collection); + if (hr != S_OK) + logHRESULT(L"error making gradient stop collection for second gradient in SV chooser", hr); + ZeroMemory(&lprop, sizeof (D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES)); + lprop.startPoint.x = size.width / 2; + lprop.startPoint.y = 0; + lprop.endPoint.x = size.width / 2; + lprop.endPoint.y = size.height; + ZeroMemory(&bprop, sizeof (D2D1_BRUSH_PROPERTIES)); + bprop.opacity = 1.0; + bprop.transform._11 = 1; + bprop.transform._22 = 1; + hr = rt->CreateLinearGradientBrush(&lprop, &bprop, + collection, &brush); + if (hr != S_OK) + logHRESULT(L"error making gradient brush for second gradient in SV chooser", hr); + // oh but wait we can't use FillRectangle() with an opacity mask + // and we can't use FillGeometry() with both an opacity mask and a non-bitmap + // layers it is! + hr = rt->CreateLayer(&size, &layer); + if (hr != S_OK) + logHRESULT(L"error making layer for second gradient in SV chooser", hr); + ZeroMemory(&layerparams, sizeof (D2D1_LAYER_PARAMETERS)); + layerparams.contentBounds = rect; + // TODO make sure these are right + layerparams.geometricMask = NULL; + layerparams.maskAntialiasMode = D2D1_ANTIALIAS_MODE_PER_PRIMITIVE; + layerparams.maskTransform._11 = 1; + layerparams.maskTransform._22 = 1; + layerparams.opacity = 1.0; + layerparams.opacityBrush = opacity; + layerparams.layerOptions = D2D1_LAYER_OPTIONS_NONE; + rt->PushLayer(&layerparams, layer); + rt->FillRectangle(&rect, brush); + rt->PopLayer(); + layer->Release(); + brush->Release(); + collection->Release(); + opacity->Release(); + + // and now we just draw the marker + ZeroMemory(&mparam, sizeof (D2D1_ELLIPSE)); + mparam.point.x = s * size.width; + mparam.point.y = (1 - v) * size.height; + mparam.radiusX = 7; + mparam.radiusY = 7; + // TODO make the color contrast? + mcolor.r = 1.0; + mcolor.g = 1.0; + mcolor.b = 1.0; + mcolor.a = 1.0; + hr = rt->CreateSolidColorBrush(&mcolor, &bprop, &markerBrush); + if (hr != S_OK) + logHRESULT(L"error creating brush for SV chooser", hr); + rt->DrawEllipse(&mparam, markerBrush, 2, NULL); + markerBrush->Release(); } static LRESULT CALLBACK svChooserSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) From 473e0c9b693c375178e77dcbf85d1b4c2c87f2d0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 17 May 2016 12:35:44 -0400 Subject: [PATCH 0046/1329] Actually first let's do event handling. This adds a ftoutf16() function we can use here. --- windows/uipriv_windows.hpp | 1 + windows/utf16.cpp | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index e207f1bc..555c687b 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -37,6 +37,7 @@ extern WCHAR *strf(const WCHAR *format, ...); extern WCHAR *vstrf(const WCHAR *format, va_list ap); extern char *LFtoCRLF(const char *lfonly); extern void CRLFtoLF(const char *s); +extern WCHAR *ftoutf16(double d); // debug.cpp // see http://stackoverflow.com/questions/14421656/is-there-widely-available-wide-character-variant-of-file diff --git a/windows/utf16.cpp b/windows/utf16.cpp index 374cec9b..07fd4363 100644 --- a/windows/utf16.cpp +++ b/windows/utf16.cpp @@ -128,3 +128,15 @@ void CRLFtoLF(char *s) while (t != s) *t++ = '\0'; } + +// std::to_string() always uses %f; we want %g +// fortunately std::iostream seems to use %g by default so +WCHAR *ftoutf16(double d) +{ + std::wostringstream ss; + std::wstring s; + + ss << d; + s = ss.str(); // to be safe + return utf16dup(s.c_str()); +} From 8a1fe1f48af13155cdf99418d29a1e0124a9ca99 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 17 May 2016 12:41:41 -0400 Subject: [PATCH 0047/1329] And added a proper invalidateRect(). --- windows/area.cpp | 3 +-- windows/areadraw.cpp | 3 +-- windows/areascroll.cpp | 3 +-- windows/colorbutton.cpp | 3 +-- windows/fontdialog.cpp | 3 +-- windows/uipriv_windows.hpp | 1 + windows/winutil.cpp | 6 ++++++ 7 files changed, 12 insertions(+), 10 deletions(-) diff --git a/windows/area.cpp b/windows/area.cpp index 2d301855..bdad93f5 100644 --- a/windows/area.cpp +++ b/windows/area.cpp @@ -89,8 +89,7 @@ void uiAreaSetSize(uiArea *a, intmax_t width, intmax_t height) void uiAreaQueueRedrawAll(uiArea *a) { // don't erase the background; we do that ourselves in doPaint() - if (InvalidateRect(a->hwnd, NULL, FALSE) == 0) - logLastError(L"error queueing uiArea redraw"); + invalidateRect(a->hwnd, NULL, FALSE); } void uiAreaScrollTo(uiArea *a, double x, double y, double width, double height) diff --git a/windows/areadraw.cpp b/windows/areadraw.cpp index 77e9aeaf..17b64705 100644 --- a/windows/areadraw.cpp +++ b/windows/areadraw.cpp @@ -127,6 +127,5 @@ void areaDrawOnResize(uiArea *a, RECT *newClient) // according to Rick Brewster, we must always redraw the entire client area after calling ID2D1RenderTarget::Resize() (see http://stackoverflow.com/a/33222983/3408572) // we used to have a uiAreaHandler.RedrawOnResize() method to decide this; now you know why we don't anymore - if (InvalidateRect(a->hwnd, NULL, TRUE) == 0) - logLastError(L"error redrawing area on resize"); + invalidateRect(a->hwnd, NULL, TRUE); } diff --git a/windows/areascroll.cpp b/windows/areascroll.cpp index 782755a9..3e05d0aa 100644 --- a/windows/areascroll.cpp +++ b/windows/areascroll.cpp @@ -33,8 +33,7 @@ static void scrollto(uiArea *a, int which, struct scrollParams *p, intmax_t pos) // Direct2D doesn't have a method for scrolling the existing contents of a render target. // We'll have to just invalidate everything and hope for the best. - if (InvalidateRect(a->hwnd, NULL, FALSE) == 0) - logLastError(L"error invalidating uiArea after scrolling"); + invalidateRect(a->hwnd, NULL, FALSE); *(p->pos) = pos; diff --git a/windows/colorbutton.cpp b/windows/colorbutton.cpp index 21c56cf6..edbba35d 100644 --- a/windows/colorbutton.cpp +++ b/windows/colorbutton.cpp @@ -134,8 +134,7 @@ void uiColorButtonSetColor(uiColorButton *b, double r, double g, double bl, doub b->g = g; b->b = bl; b->a = a; - // TODO don't we have a helper function for this? - InvalidateRect(b->hwnd, NULL, TRUE); + invalidateRect(b->hwnd, NULL, TRUE); } void uiColorButtonOnChanged(uiColorButton *b, void (*f)(uiColorButton *, void *), void *data) diff --git a/windows/fontdialog.cpp b/windows/fontdialog.cpp index ef1fae11..c30230f0 100644 --- a/windows/fontdialog.cpp +++ b/windows/fontdialog.cpp @@ -174,8 +174,7 @@ static WCHAR *fontStyleName(struct fontCollection *fc, IDWriteFont *font) static void queueRedrawSampleText(struct fontDialog *f) { // TODO TRUE? - if (InvalidateRect(f->sampleBox, NULL, TRUE) == 0) - logLastError(L"error queueing a redraw of the font dialog's sample text"); + invalidateRect(f->sampleBox, NULL, TRUE); } static void styleChanged(struct fontDialog *f) diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index 555c687b..7c43874b 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -65,6 +65,7 @@ extern HWND parentOf(HWND child); extern HWND parentToplevel(HWND child); extern void setWindowInsertAfter(HWND hwnd, HWND insertAfter); extern HWND getDlgItem(HWND hwnd, int id); +extern void invalidateRect(HWND hwnd, RECT *r, BOOL erase); // text.cpp extern WCHAR *windowTextAndLen(HWND hwnd, LRESULT *len); diff --git a/windows/winutil.cpp b/windows/winutil.cpp index 024260f2..2faf8192 100644 --- a/windows/winutil.cpp +++ b/windows/winutil.cpp @@ -130,3 +130,9 @@ HWND getDlgItem(HWND hwnd, int id) logLastError(L"error getting dialog item handle"); return out; } + +void invalidateRect(HWND hwnd, RECT *r, BOOL erase) +{ + if (InvalidateRect(hwnd, r, erase) == 0) + logLastError(L"error invalidating window rect"); +} From 7c34acc2b79b18eab1b19724fc5ad67fd36d7bdd Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 17 May 2016 12:44:43 -0400 Subject: [PATCH 0048/1329] And an itoutf16() too, because why not. Okay, NOW for updating the labels. --- windows/uipriv_windows.hpp | 1 + windows/utf16.cpp | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index 7c43874b..d0b84a8c 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -38,6 +38,7 @@ extern WCHAR *vstrf(const WCHAR *format, va_list ap); extern char *LFtoCRLF(const char *lfonly); extern void CRLFtoLF(const char *s); extern WCHAR *ftoutf16(double d); +extern WCHAR *itoutf16(intmax_t i); // debug.cpp // see http://stackoverflow.com/questions/14421656/is-there-widely-available-wide-character-variant-of-file diff --git a/windows/utf16.cpp b/windows/utf16.cpp index 07fd4363..9acc5e82 100644 --- a/windows/utf16.cpp +++ b/windows/utf16.cpp @@ -140,3 +140,14 @@ WCHAR *ftoutf16(double d) s = ss.str(); // to be safe return utf16dup(s.c_str()); } + +// to complement the above +WCHAR *itoutf16(intmax_t i) +{ + std::wostringstream ss; + std::wstring s; + + ss << i; + s = ss.str(); // to be safe + return utf16dup(s.c_str()); +} From d892a8f710fd8d4bb5c1cc4c766fcdf6ec53d0df Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 17 May 2016 13:44:14 -0400 Subject: [PATCH 0049/1329] Started making the color dialog editable. I'm going to have to store HSV and alpha instead of RGB and alpha... --- windows/colordialog.cpp | 159 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 151 insertions(+), 8 deletions(-) diff --git a/windows/colordialog.cpp b/windows/colordialog.cpp index 78875636..3db3241a 100644 --- a/windows/colordialog.cpp +++ b/windows/colordialog.cpp @@ -6,6 +6,7 @@ struct colorDialog { HWND svChooser; HWND hSlider; + HWND preview; HWND opacitySlider; HWND editH; HWND editS; @@ -21,6 +22,8 @@ struct colorDialog { double b; double a; struct colorDialogRGBA *out; + + BOOL updating; }; // both of these are from the wikipedia page on HSV @@ -77,16 +80,18 @@ static void rgb2HSV(double r, double g, double b, double *h, double *s, double * static void hsv2RGB(double h, double s, double v, double *r, double *g, double *b) { double c; - int hPrime; + double hPrime; + int h60; double x; double m; double c1, c2; c = v * s; - hPrime = (int) (h * 6); // equivalent to splitting into 60° chunks - x = c * (1 - abs(hPrime % 2 - 1)); + hPrime = h * 6; + h60 = (int) hPrime; // equivalent to splitting into 60° chunks + x = c * (1.0 - fabs(fmod(hPrime, 2) - 1.0)); m = v - c; - switch (hPrime) { + switch (h60) { case 0: *r = c + m; *g = x + m; @@ -121,6 +126,75 @@ static void hsv2RGB(double h, double s, double v, double *r, double *g, double * // TODO } +static void updateDouble(HWND hwnd, double d, HWND whichChanged) +{ + WCHAR *str; + + if (whichChanged == hwnd) + return; + str = ftoutf16(d); + setWindowText(hwnd, str); + uiFree(str); +} + +static void updateDialog(struct colorDialog *c, HWND whichChanged) +{ + double h, s, v; + uint8_t rb, gb, bb, ab; + WCHAR *str; + + c->updating = TRUE; + + rgb2HSV(c->r, c->g, c->b, &h, &s, &v); + + updateDouble(c->editH, h, whichChanged); + updateDouble(c->editS, s, whichChanged); + updateDouble(c->editV, v, whichChanged); + + updateDouble(c->editRDouble, c->r, whichChanged); + updateDouble(c->editGDouble, c->g, whichChanged); + updateDouble(c->editBDouble, c->b, whichChanged); + updateDouble(c->editADouble, c->a, whichChanged); + + rb = (uint8_t) (c->r * 255); + gb = (uint8_t) (c->g * 255); + bb = (uint8_t) (c->b * 255); + ab = (uint8_t) (c->a * 255); + + if (whichChanged != c->editRInt) { + str = itoutf16(rb); + setWindowText(c->editRInt, str); + uiFree(str); + } + if (whichChanged != c->editGInt) { + str = itoutf16(gb); + setWindowText(c->editGInt, str); + uiFree(str); + } + if (whichChanged != c->editBInt) { + str = itoutf16(bb); + setWindowText(c->editBInt, str); + uiFree(str); + } + if (whichChanged != c->editAInt) { + str = itoutf16(ab); + setWindowText(c->editAInt, str); + uiFree(str); + } + + if (whichChanged != c->editHex) { + // TODO hex + } + + // TODO TRUE? + invalidateRect(c->svChooser, NULL, TRUE); +//TODO invalidateRect(c->hSlider, NULL, TRUE); +//TODO invalidateRect(c->preview, NULL, TRUE); +//TODO invalidateRect(c->opacitySlider, NULL, TRUE); + + c->updating = FALSE; +} + // this interesting approach comes from http://blogs.msdn.com/b/wpfsdk/archive/2006/10/26/uncommon-dialogs--font-chooser-and-color-picker-dialogs.aspx static void drawSVChooser(struct colorDialog *c, ID2D1RenderTarget *rt) { @@ -436,9 +510,60 @@ static struct colorDialog *beginColorDialog(HWND hwnd, LPARAM lParam) fixupControlPositions(c); + // and get the ball rolling + updateDialog(c, NULL); return c; } +static double editDouble(HWND hwnd) +{ + WCHAR *s; + double d; + + s = windowText(hwnd); + d = _wtof(s); + uiFree(s); + return d; +} + +static void hChanged(struct colorDialog *c) +{ + double h, s, v; + + rgb2HSV(c->r, c->g, c->b, &h, &s, &v); + h = editDouble(c->editH); + if (h < 0 || h >= 1.0) // note the >= + return; +printf("%g %g %g | ", c->r, c->g, c->b); + hsv2RGB(h, s, v, &(c->r), &(c->g), &(c->b)); +printf("%g %g %g\n", c->r, c->g, c->b); + updateDialog(c, c->editH); +} + +static void sChanged(struct colorDialog *c) +{ + double h, s, v; + + rgb2HSV(c->r, c->g, c->b, &h, &s, &v); + s = editDouble(c->editS); + if (s < 0 || s > 1) + return; + hsv2RGB(h, s, v, &(c->r), &(c->g), &(c->b)); + updateDialog(c, c->editS); +} + +static void vChanged(struct colorDialog *c) +{ + double h, s, v; + + rgb2HSV(c->r, c->g, c->b, &h, &s, &v); + v = editDouble(c->editV); + if (v < 0 || v > 1) + return; + hsv2RGB(h, s, v, &(c->r), &(c->g), &(c->b)); + updateDialog(c, c->editV); +} + static void endColorDialog(struct colorDialog *c, INT_PTR code) { if (EndDialog(c->hwnd, code) == 0) @@ -446,12 +571,13 @@ static void endColorDialog(struct colorDialog *c, INT_PTR code) uiFree(c); } -static BOOL tryFinishDialog(struct colorDialog *c, WPARAM wParam) +// TODO make this void on the font dialog too +static void tryFinishDialog(struct colorDialog *c, WPARAM wParam) { // cancelling if (LOWORD(wParam) != IDOK) { endColorDialog(c, 1); - return TRUE; + return; } // OK @@ -460,9 +586,16 @@ static BOOL tryFinishDialog(struct colorDialog *c, WPARAM wParam) c->out->b = c->b; c->out->a = c->a; endColorDialog(c, 2); - return TRUE; } +// TODO change fontdialog to use this +// note that if we make this const, we get lots of weird compiler errors +static std::map changed = { + { rcH, hChanged }, + { rcS, sChanged }, + { rcV, vChanged }, +}; + static INT_PTR CALLBACK colorDialogDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { struct colorDialog *c; @@ -485,7 +618,17 @@ static INT_PTR CALLBACK colorDialogDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, case IDCANCEL: if (HIWORD(wParam) != BN_CLICKED) return FALSE; - return tryFinishDialog(c, wParam); + tryFinishDialog(c, wParam); + return TRUE; + case rcH: + case rcS: + case rcV: + if (HIWORD(wParam) != EN_CHANGE) + return FALSE; + if (c->updating) // prevent infinite recursion during an update + return FALSE; + (*(changed[LOWORD(wParam)]))(c); + return TRUE; } return FALSE; } From 9654ca793db9d444b1d66b66dd90fd1e68edc013 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 17 May 2016 13:52:56 -0400 Subject: [PATCH 0050/1329] Changed the color dialog to edit HSV instead of RGB. This fixes issues with conversion that we had in the previous commit. The real question: will this break entering RGB values? --- windows/colordialog.cpp | 62 +++++++++++++++++------------------------ 1 file changed, 26 insertions(+), 36 deletions(-) diff --git a/windows/colordialog.cpp b/windows/colordialog.cpp index 3db3241a..096135dc 100644 --- a/windows/colordialog.cpp +++ b/windows/colordialog.cpp @@ -17,9 +17,9 @@ struct colorDialog { HWND editADouble, editAInt; HWND editHex; - double r; - double g; - double b; + double h; + double s; + double v; double a; struct colorDialogRGBA *out; @@ -139,26 +139,26 @@ static void updateDouble(HWND hwnd, double d, HWND whichChanged) static void updateDialog(struct colorDialog *c, HWND whichChanged) { - double h, s, v; + double r, g, b; uint8_t rb, gb, bb, ab; WCHAR *str; c->updating = TRUE; - rgb2HSV(c->r, c->g, c->b, &h, &s, &v); + updateDouble(c->editH, c->h, whichChanged); + updateDouble(c->editS, c->s, whichChanged); + updateDouble(c->editV, c->v, whichChanged); - updateDouble(c->editH, h, whichChanged); - updateDouble(c->editS, s, whichChanged); - updateDouble(c->editV, v, whichChanged); + hsv2RGB(c->h, c->s, c->v, &r, &g, &b); - updateDouble(c->editRDouble, c->r, whichChanged); - updateDouble(c->editGDouble, c->g, whichChanged); - updateDouble(c->editBDouble, c->b, whichChanged); + updateDouble(c->editRDouble, r, whichChanged); + updateDouble(c->editGDouble, g, whichChanged); + updateDouble(c->editBDouble, b, whichChanged); updateDouble(c->editADouble, c->a, whichChanged); - rb = (uint8_t) (c->r * 255); - gb = (uint8_t) (c->g * 255); - bb = (uint8_t) (c->b * 255); + rb = (uint8_t) (r * 255); + gb = (uint8_t) (g * 255); + bb = (uint8_t) (b * 255); ab = (uint8_t) (c->a * 255); if (whichChanged != c->editRInt) { @@ -200,7 +200,6 @@ static void drawSVChooser(struct colorDialog *c, ID2D1RenderTarget *rt) { D2D1_SIZE_F size; D2D1_RECT_F rect; - double h, s, v; double rTop, gTop, bTop; D2D1_GRADIENT_STOP stops[2]; ID2D1GradientStopCollection *collection; @@ -225,8 +224,7 @@ static void drawSVChooser(struct colorDialog *c, ID2D1RenderTarget *rt) // first, draw a vertical gradient from the current hue at max S/V to black // the source example draws it upside down; let's do so too just to be safe - rgb2HSV(c->r, c->g, c->b, &h, &s, &v); - hsv2RGB(h, 1.0, 1.0, &rTop, &gTop, &bTop); + hsv2RGB(c->h, 1.0, 1.0, &rTop, &gTop, &bTop); stops[0].position = 0; stops[0].color.r = 0.0; stops[0].color.g = 0.0; @@ -345,8 +343,8 @@ static void drawSVChooser(struct colorDialog *c, ID2D1RenderTarget *rt) // and now we just draw the marker ZeroMemory(&mparam, sizeof (D2D1_ELLIPSE)); - mparam.point.x = s * size.width; - mparam.point.y = (1 - v) * size.height; + mparam.point.x = c->s * size.width; + mparam.point.y = (1 - c->v) * size.height; mparam.radiusX = 7; mparam.radiusY = 7; // TODO make the color contrast? @@ -485,9 +483,8 @@ static struct colorDialog *beginColorDialog(HWND hwnd, LPARAM lParam) c = uiNew(struct colorDialog); c->hwnd = hwnd; c->out = (struct colorDialogRGBA *) lParam; - c->r = c->out->r; // load initial values now - c->g = c->out->g; - c->b = c->out->b; + // load initial values now + rgb2HSV(c->out->r, c->out->g, c->out->b, &(c->h), &(c->s), &(c->v)); c->a = c->out->a; // TODO set up d2dscratches @@ -528,39 +525,34 @@ static double editDouble(HWND hwnd) static void hChanged(struct colorDialog *c) { - double h, s, v; + double h; - rgb2HSV(c->r, c->g, c->b, &h, &s, &v); h = editDouble(c->editH); if (h < 0 || h >= 1.0) // note the >= return; -printf("%g %g %g | ", c->r, c->g, c->b); - hsv2RGB(h, s, v, &(c->r), &(c->g), &(c->b)); -printf("%g %g %g\n", c->r, c->g, c->b); + c->h = h; updateDialog(c, c->editH); } static void sChanged(struct colorDialog *c) { - double h, s, v; + double s; - rgb2HSV(c->r, c->g, c->b, &h, &s, &v); s = editDouble(c->editS); if (s < 0 || s > 1) return; - hsv2RGB(h, s, v, &(c->r), &(c->g), &(c->b)); + c->s = s; updateDialog(c, c->editS); } static void vChanged(struct colorDialog *c) { - double h, s, v; + double v; - rgb2HSV(c->r, c->g, c->b, &h, &s, &v); v = editDouble(c->editV); if (v < 0 || v > 1) return; - hsv2RGB(h, s, v, &(c->r), &(c->g), &(c->b)); + c->v = v; updateDialog(c, c->editV); } @@ -581,9 +573,7 @@ static void tryFinishDialog(struct colorDialog *c, WPARAM wParam) } // OK - c->out->r = c->r; - c->out->g = c->g; - c->out->b = c->b; + hsv2RGB(c->h, c->s, c->v, &(c->out->r), &(c->out->g), &(c->out->b)); c->out->a = c->a; endColorDialog(c, 2); } From 571faf95820b789843d8ce9a699ad1995864212e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 17 May 2016 14:44:57 -0400 Subject: [PATCH 0051/1329] Implemented mouse control of the SV area; updated the color button immediately after a change. --- windows/colorbutton.cpp | 1 + windows/colordialog.cpp | 11 ++++++++++- windows/d2dscratch.cpp | 32 ++++++++++++++++++++++++++++++++ windows/uipriv_windows.hpp | 2 +- 4 files changed, 44 insertions(+), 2 deletions(-) diff --git a/windows/colorbutton.cpp b/windows/colorbutton.cpp index edbba35d..b6c712c4 100644 --- a/windows/colorbutton.cpp +++ b/windows/colorbutton.cpp @@ -41,6 +41,7 @@ static BOOL onWM_COMMAND(uiControl *c, HWND hwnd, WORD code, LRESULT *lResult) b->g = rgba.g; b->b = rgba.b; b->a = rgba.a; + invalidateRect(b->hwnd, NULL, TRUE); (*(b->onChanged))(b, b->onChangedData); } diff --git a/windows/colordialog.cpp b/windows/colordialog.cpp index 096135dc..0c17dd28 100644 --- a/windows/colordialog.cpp +++ b/windows/colordialog.cpp @@ -363,13 +363,22 @@ static LRESULT CALLBACK svChooserSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LP { ID2D1RenderTarget *rt; struct colorDialog *c; + D2D1_POINT_2F *pos; + D2D1_SIZE_F *size; + c = (struct colorDialog *) dwRefData; switch (uMsg) { case msgD2DScratchPaint: rt = (ID2D1RenderTarget *) lParam; - c = (struct colorDialog *) dwRefData; drawSVChooser(c, rt); return 0; + case msgD2DScratchLButtonDown: + pos = (D2D1_POINT_2F *) wParam; + size = (D2D1_SIZE_F *) lParam; + c->s = pos->x / size->width; + c->v = 1 - (pos->y / size->height); + updateDialog(c, NULL); + return 0; case WM_NCDESTROY: if (RemoveWindowSubclass(hwnd, svChooserSubProc, uIdSubclass) == FALSE) logLastError(L"error removing color dialog SV chooser subclass"); diff --git a/windows/d2dscratch.cpp b/windows/d2dscratch.cpp index f9504317..146eea8b 100644 --- a/windows/d2dscratch.cpp +++ b/windows/d2dscratch.cpp @@ -6,6 +6,10 @@ // - wParam - 0 // - lParam - ID2D1RenderTarget * // - lResult - 0 +// You can optionally also handle msgD2DScratchLButtonDown, which is sent when the left mouse button is either pressed for the first time or held while the mouse is moving. +// - wParam - position in DIPs, as D2D1_POINT_2F * +// - lParam - size of render target in DIPs, as D2D1_SIZE_F * +// - lResult - 0 // Other messages can also be handled here. // TODO allow resize @@ -37,6 +41,26 @@ static HRESULT d2dScratchDoPaint(HWND hwnd, ID2D1RenderTarget *rt) return rt->EndDraw(NULL, NULL); } +static void d2dScratchDoLButtonDown(HWND hwnd, ID2D1RenderTarget *rt, LPARAM lParam) +{ + double xpix, ypix; + FLOAT dpix, dpiy; + D2D1_POINT_2F pos; + D2D1_SIZE_F size; + + xpix = (double) GET_X_LPARAM(lParam); + ypix = (double) GET_Y_LPARAM(lParam); + // these are in pixels; we need points + // TODO separate the function from areautil.cpp? + rt->GetDpi(&dpix, &dpiy); + pos.x = (xpix * 96) / dpix; + pos.y = (ypix * 96) / dpiy; + + size = rt->GetSize(); + + SendMessageW(hwnd, msgD2DScratchLButtonDown, (WPARAM) (&pos), (LPARAM) (&size)); +} + static LRESULT CALLBACK d2dScratchWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { LONG_PTR init; @@ -83,6 +107,14 @@ static LRESULT CALLBACK d2dScratchWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, L case WM_PRINTCLIENT: // TODO break; + case WM_LBUTTONDOWN: + d2dScratchDoLButtonDown(hwnd, rt, lParam); + return 0; + case WM_MOUSEMOVE: + // also send LButtonDowns when dragging + if ((wParam & MK_LBUTTON) != 0) + d2dScratchDoLButtonDown(hwnd, rt, lParam); + return 0; } return DefWindowProcW(hwnd, uMsg, wParam, lParam); } diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index d0b84a8c..d1380af3 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -13,8 +13,8 @@ enum { msgNOTIFY, msgHSCROLL, msgQueued, - // TODO convert to a function like with container? msgD2DScratchPaint, + msgD2DScratchLButtonDown, }; // alloc.cpp From d42864c696bb3632b6392105c27f6ff7c7359798 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 17 May 2016 16:46:47 -0400 Subject: [PATCH 0052/1329] Implemented the other double entry fields and drawing alpha on the SV chooser. --- windows/colordialog.cpp | 90 +++++++++++++++++++++++++++++++++-------- 1 file changed, 73 insertions(+), 17 deletions(-) diff --git a/windows/colordialog.cpp b/windows/colordialog.cpp index 0c17dd28..cf9c8199 100644 --- a/windows/colordialog.cpp +++ b/windows/colordialog.cpp @@ -246,7 +246,7 @@ static void drawSVChooser(struct colorDialog *c, ID2D1RenderTarget *rt) lprop.endPoint.x = size.width / 2; lprop.endPoint.y = 0; ZeroMemory(&bprop, sizeof (D2D1_BRUSH_PROPERTIES)); - bprop.opacity = 1.0; + bprop.opacity = c->a; // note this part; we also use it below for the layer bprop.transform._11 = 1; bprop.transform._22 = 1; hr = rt->CreateLinearGradientBrush(&lprop, &bprop, @@ -330,7 +330,7 @@ static void drawSVChooser(struct colorDialog *c, ID2D1RenderTarget *rt) layerparams.maskAntialiasMode = D2D1_ANTIALIAS_MODE_PER_PRIMITIVE; layerparams.maskTransform._11 = 1; layerparams.maskTransform._22 = 1; - layerparams.opacity = 1.0; + layerparams.opacity = c->a; // here's the other use of c->a to note layerparams.opacityBrush = opacity; layerparams.layerOptions = D2D1_LAYER_OPTIONS_NONE; rt->PushLayer(&layerparams, layer); @@ -352,6 +352,7 @@ static void drawSVChooser(struct colorDialog *c, ID2D1RenderTarget *rt) mcolor.g = 1.0; mcolor.b = 1.0; mcolor.a = 1.0; + bprop.opacity = 1.0; // the marker should always be opaque hr = rt->CreateSolidColorBrush(&mcolor, &bprop, &markerBrush); if (hr != S_OK) logHRESULT(L"error creating brush for SV chooser", hr); @@ -521,6 +522,28 @@ static struct colorDialog *beginColorDialog(HWND hwnd, LPARAM lParam) return c; } +static void endColorDialog(struct colorDialog *c, INT_PTR code) +{ + if (EndDialog(c->hwnd, code) == 0) + logLastError(L"error ending color dialog"); + uiFree(c); +} + +// TODO make this void on the font dialog too +static void tryFinishDialog(struct colorDialog *c, WPARAM wParam) +{ + // cancelling + if (LOWORD(wParam) != IDOK) { + endColorDialog(c, 1); + return; + } + + // OK + hsv2RGB(c->h, c->s, c->v, &(c->out->r), &(c->out->g), &(c->out->b)); + c->out->a = c->a; + endColorDialog(c, 2); +} + static double editDouble(HWND hwnd) { WCHAR *s; @@ -565,26 +588,51 @@ static void vChanged(struct colorDialog *c) updateDialog(c, c->editV); } -static void endColorDialog(struct colorDialog *c, INT_PTR code) +static void rDoubleChanged(struct colorDialog *c) { - if (EndDialog(c->hwnd, code) == 0) - logLastError(L"error ending color dialog"); - uiFree(c); + double r, g, b; + + hsv2RGB(c->h, c->s, c->v, &r, &g, &b); + r = editDouble(c->editRDouble); + if (r < 0 || r > 1) + return; + rgb2HSV(r, g, b, &(c->h), &(c->s), &(c->v)); + updateDialog(c, c->editRDouble); } -// TODO make this void on the font dialog too -static void tryFinishDialog(struct colorDialog *c, WPARAM wParam) +static void gDoubleChanged(struct colorDialog *c) { - // cancelling - if (LOWORD(wParam) != IDOK) { - endColorDialog(c, 1); - return; - } + double r, g, b; - // OK - hsv2RGB(c->h, c->s, c->v, &(c->out->r), &(c->out->g), &(c->out->b)); - c->out->a = c->a; - endColorDialog(c, 2); + hsv2RGB(c->h, c->s, c->v, &r, &g, &b); + g = editDouble(c->editGDouble); + if (g < 0 || g > 1) + return; + rgb2HSV(r, g, b, &(c->h), &(c->s), &(c->v)); + updateDialog(c, c->editGDouble); +} + +static void bDoubleChanged(struct colorDialog *c) +{ + double r, g, b; + + hsv2RGB(c->h, c->s, c->v, &r, &g, &b); + b = editDouble(c->editBDouble); + if (b < 0 || b > 1) + return; + rgb2HSV(r, g, b, &(c->h), &(c->s), &(c->v)); + updateDialog(c, c->editBDouble); +} + +static void aDoubleChanged(struct colorDialog *c) +{ + double a; + + a = editDouble(c->editADouble); + if (a < 0 || a > 1) + return; + c->a = a; + updateDialog(c, c->editADouble); } // TODO change fontdialog to use this @@ -593,6 +641,10 @@ static std::map changed = { { rcH, hChanged }, { rcS, sChanged }, { rcV, vChanged }, + { rcRDouble, rDoubleChanged }, + { rcGDouble, gDoubleChanged }, + { rcBDouble, bDoubleChanged }, + { rcADouble, aDoubleChanged }, }; static INT_PTR CALLBACK colorDialogDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) @@ -622,6 +674,10 @@ static INT_PTR CALLBACK colorDialogDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, case rcH: case rcS: case rcV: + case rcRDouble: + case rcGDouble: + case rcBDouble: + case rcADouble: if (HIWORD(wParam) != EN_CHANGE) return FALSE; if (c->updating) // prevent infinite recursion during an update From 70635858bd1b3038ecc5173d963ef05563625c09 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 17 May 2016 17:00:00 -0400 Subject: [PATCH 0053/1329] Implemented the integer entries. --- windows/colordialog.cpp | 76 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 74 insertions(+), 2 deletions(-) diff --git a/windows/colordialog.cpp b/windows/colordialog.cpp index cf9c8199..7e43635e 100644 --- a/windows/colordialog.cpp +++ b/windows/colordialog.cpp @@ -27,6 +27,7 @@ struct colorDialog { }; // both of these are from the wikipedia page on HSV +// TODO what to do about negative h? static void rgb2HSV(double r, double g, double b, double *h, double *s, double *v) { double M, m; @@ -56,8 +57,7 @@ static void rgb2HSV(double r, double g, double b, double *h, double *s, double * switch (whichmax) { case 0: *h = ((g - b) / c); - if (*h > 6) - *h -= 6; + *h = fmod(*h, 6); break; case 1: *h = ((b - r) / c) + 2; @@ -635,6 +635,70 @@ static void aDoubleChanged(struct colorDialog *c) updateDialog(c, c->editADouble); } +static int editInt(HWND hwnd) +{ + WCHAR *s; + int i; + + s = windowText(hwnd); + i = _wtoi(s); + uiFree(s); + return i; +} + +static void rIntChanged(struct colorDialog *c) +{ + double r, g, b; + int i; + + hsv2RGB(c->h, c->s, c->v, &r, &g, &b); + i = editInt(c->editRInt); + if (i < 0 || i > 255) + return; + r = ((double) i) / 255.0; + rgb2HSV(r, g, b, &(c->h), &(c->s), &(c->v)); + updateDialog(c, c->editRInt); +} + +static void gIntChanged(struct colorDialog *c) +{ + double r, g, b; + int i; + + hsv2RGB(c->h, c->s, c->v, &r, &g, &b); + i = editInt(c->editGInt); + if (i < 0 || i > 255) + return; + g = ((double) i) / 255.0; + rgb2HSV(r, g, b, &(c->h), &(c->s), &(c->v)); + updateDialog(c, c->editGInt); +} + +static void bIntChanged(struct colorDialog *c) +{ + double r, g, b; + int i; + + hsv2RGB(c->h, c->s, c->v, &r, &g, &b); + i = editInt(c->editBInt); + if (i < 0 || i > 255) + return; + b = ((double) i) / 255.0; + rgb2HSV(r, g, b, &(c->h), &(c->s), &(c->v)); + updateDialog(c, c->editBInt); +} + +static void aIntChanged(struct colorDialog *c) +{ + int a; + + a = editInt(c->editAInt); + if (a < 0 || a > 255) + return; + c->a = ((double) a) / 255; + updateDialog(c, c->editAInt); +} + // TODO change fontdialog to use this // note that if we make this const, we get lots of weird compiler errors static std::map changed = { @@ -645,6 +709,10 @@ static std::map changed = { { rcGDouble, gDoubleChanged }, { rcBDouble, bDoubleChanged }, { rcADouble, aDoubleChanged }, + { rcRInt, rIntChanged }, + { rcGInt, gIntChanged }, + { rcBInt, bIntChanged }, + { rcAInt, aIntChanged }, }; static INT_PTR CALLBACK colorDialogDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) @@ -678,6 +746,10 @@ static INT_PTR CALLBACK colorDialogDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, case rcGDouble: case rcBDouble: case rcADouble: + case rcRInt: + case rcGInt: + case rcBInt: + case rcAInt: if (HIWORD(wParam) != EN_CHANGE) return FALSE; if (c->updating) // prevent infinite recursion during an update From bc69da86afe7d43d7a9a0d86e3c6f26ce8abc84d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 17 May 2016 17:41:38 -0400 Subject: [PATCH 0054/1329] Implemented the hex entry. That just leaves the other Direct2D views! --- windows/colordialog.cpp | 114 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 113 insertions(+), 1 deletion(-) diff --git a/windows/colordialog.cpp b/windows/colordialog.cpp index 7e43635e..474a8616 100644 --- a/windows/colordialog.cpp +++ b/windows/colordialog.cpp @@ -77,6 +77,7 @@ static void rgb2HSV(double r, double g, double b, double *h, double *s, double * *s = c / *v; } +// TODO negative R values? static void hsv2RGB(double h, double s, double v, double *r, double *g, double *b) { double c; @@ -126,6 +127,97 @@ static void hsv2RGB(double h, double s, double v, double *r, double *g, double * // TODO } +#define hexd L"0123456789ABCDEF" + +static void rgba2Hex(uint8_t r, uint8_t g, uint8_t b, uint8_t a, WCHAR *buf) +{ + buf[0] = L'#'; + buf[1] = hexd[(a >> 4) & 0xF]; + buf[2] = hexd[a & 0xF]; + buf[3] = hexd[(r >> 4) & 0xF]; + buf[4] = hexd[r & 0xF]; + buf[5] = hexd[(g >> 4) & 0xF]; + buf[6] = hexd[g & 0xF]; + buf[7] = hexd[(b >> 4) & 0xF]; + buf[8] = hexd[b & 0xF]; + buf[9] = L'\0'; +} + +static int convHexDigit(WCHAR c) +{ + if (c >= L'0' && c <= L'9') + return c - L'0'; + if (c >= L'A' && c <= L'F') + return c - L'A' + 0xA; + if (c >= L'a' && c <= L'f') + return c - L'a' + 0xA; + return -1; +} + +// TODO allow #NNN shorthand +static BOOL hex2RGBA(WCHAR *buf, double *r, double *g, double *b, double *a) +{ + uint8_t component; + int i; + + if (*buf == L'#') + buf++; + + component = 0; + i = convHexDigit(*buf++); + if (i < 0) + return FALSE; + component |= ((uint8_t) i) << 4; + i = convHexDigit(*buf++); + if (i < 0) + return FALSE; + component |= ((uint8_t) i); + *a = ((double) component) / 255; + + component = 0; + i = convHexDigit(*buf++); + if (i < 0) + return FALSE; + component |= ((uint8_t) i) << 4; + i = convHexDigit(*buf++); + if (i < 0) + return FALSE; + component |= ((uint8_t) i); + *r = ((double) component) / 255; + + component = 0; + i = convHexDigit(*buf++); + if (i < 0) + return FALSE; + component |= ((uint8_t) i) << 4; + i = convHexDigit(*buf++); + if (i < 0) + return FALSE; + component |= ((uint8_t) i); + *g = ((double) component) / 255; + + if (*buf == L'\0') { // #NNNNNN syntax + *b = *g; + *g = *r; + *r = *a; + *a = 1; + return TRUE; + } + + component = 0; + i = convHexDigit(*buf++); + if (i < 0) + return FALSE; + component |= ((uint8_t) i) << 4; + i = convHexDigit(*buf++); + if (i < 0) + return FALSE; + component |= ((uint8_t) i); + *b = ((double) component) / 255; + + return *buf == L'\0'; +} + static void updateDouble(HWND hwnd, double d, HWND whichChanged) { WCHAR *str; @@ -142,6 +234,7 @@ static void updateDialog(struct colorDialog *c, HWND whichChanged) double r, g, b; uint8_t rb, gb, bb, ab; WCHAR *str; + WCHAR hexbuf[16]; // more than enough c->updating = TRUE; @@ -183,7 +276,8 @@ static void updateDialog(struct colorDialog *c, HWND whichChanged) } if (whichChanged != c->editHex) { - // TODO hex + rgba2Hex(rb, gb, bb, ab, hexbuf); + setWindowText(c->editHex, hexbuf); } // TODO TRUE? @@ -699,6 +793,22 @@ static void aIntChanged(struct colorDialog *c) updateDialog(c, c->editAInt); } +static void hexChanged(struct colorDialog *c) +{ + WCHAR *buf; + double r, g, b, a; + BOOL is; + + buf = windowText(c->editHex); + is = hex2RGBA(buf, &r, &g, &b, &a); + uiFree(buf); + if (!is) + return; + rgb2HSV(r, g, b, &(c->h), &(c->s), &(c->v)); + c->a = a; + updateDialog(c, c->editHex); +} + // TODO change fontdialog to use this // note that if we make this const, we get lots of weird compiler errors static std::map changed = { @@ -713,6 +823,7 @@ static std::map changed = { { rcGInt, gIntChanged }, { rcBInt, bIntChanged }, { rcAInt, aIntChanged }, + { rcHex, hexChanged }, }; static INT_PTR CALLBACK colorDialogDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) @@ -750,6 +861,7 @@ static INT_PTR CALLBACK colorDialogDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, case rcGInt: case rcBInt: case rcAInt: + case rcHex: if (HIWORD(wParam) != EN_CHANGE) return FALSE; if (c->updating) // prevent infinite recursion during an update From b2262885845456847effdbd8bf0cc56e91eac895 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 17 May 2016 20:46:28 -0400 Subject: [PATCH 0055/1329] Implemented the H slider. --- windows/colordialog.cpp | 137 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 135 insertions(+), 2 deletions(-) diff --git a/windows/colordialog.cpp b/windows/colordialog.cpp index 474a8616..f2f52ed7 100644 --- a/windows/colordialog.cpp +++ b/windows/colordialog.cpp @@ -1,6 +1,8 @@ // 16 may 2016 #include "uipriv_windows.hpp" +// TODO should the d2dscratch programs capture mouse? + struct colorDialog { HWND hwnd; @@ -282,7 +284,7 @@ static void updateDialog(struct colorDialog *c, HWND whichChanged) // TODO TRUE? invalidateRect(c->svChooser, NULL, TRUE); -//TODO invalidateRect(c->hSlider, NULL, TRUE); + invalidateRect(c->hSlider, NULL, TRUE); //TODO invalidateRect(c->preview, NULL, TRUE); //TODO invalidateRect(c->opacitySlider, NULL, TRUE); @@ -449,7 +451,7 @@ static void drawSVChooser(struct colorDialog *c, ID2D1RenderTarget *rt) bprop.opacity = 1.0; // the marker should always be opaque hr = rt->CreateSolidColorBrush(&mcolor, &bprop, &markerBrush); if (hr != S_OK) - logHRESULT(L"error creating brush for SV chooser", hr); + logHRESULT(L"error creating brush for SV chooser marker", hr); rt->DrawEllipse(&mparam, markerBrush, 2, NULL); markerBrush->Release(); } @@ -482,6 +484,136 @@ static LRESULT CALLBACK svChooserSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LP return DefSubclassProc(hwnd, uMsg, wParam, lParam); } +// the gradient stuff also comes from http://blogs.msdn.com/b/wpfsdk/archive/2006/10/26/uncommon-dialogs--font-chooser-and-color-picker-dialogs.aspx +#define nStops (30) +#define degPerStop (360 / nStops) +#define stopIncr (1.0 / ((double) nStops)) + +static void drawHSlider(struct colorDialog *c, ID2D1RenderTarget *rt) +{ + D2D1_SIZE_F size; + D2D1_RECT_F rect; + D2D1_GRADIENT_STOP stops[nStops]; + double r, g, b; + int i; + double h; + ID2D1GradientStopCollection *collection; + D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES lprop; + D2D1_BRUSH_PROPERTIES bprop; + ID2D1LinearGradientBrush *brush; + double hypot, leg; + D2D1_POINT_2F center; + D2D1_MATRIX_3X2_F oldtf, rotate; + D2D1_COLOR_F mcolor; + ID2D1SolidColorBrush *markerBrush; + HRESULT hr; + + size = rt->GetSize(); + rect.left = size.width / 6; // leftmost sixth for arrow + rect.top = 0; + rect.right = size.width; + rect.bottom = size.height; + + for (i = 0; i < nStops; i++) { + h = ((double) (i * degPerStop)) / 360.0; + if (i == (nStops - 1)) + h = 0; + hsv2RGB(h, 1.0, 1.0, &r, &g, &b); + stops[i].position = ((double) i) * stopIncr; + stops[i].color.r = r; + stops[i].color.g = g; + stops[i].color.b = b; + stops[i].color.a = 1.0; + } + // and pin the last one + stops[i - 1].position = 1.0; + + hr = rt->CreateGradientStopCollection(stops, nStops, + // note that in this case this gamma is explicitly specified by the original + D2D1_GAMMA_2_2, D2D1_EXTEND_MODE_CLAMP, + &collection); + if (hr != S_OK) + logHRESULT(L"error creating stop collection for H slider gradient", hr); + ZeroMemory(&lprop, sizeof (D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES)); + lprop.startPoint.x = (rect.right - rect.left) / 2; + lprop.startPoint.y = 0; + lprop.endPoint.x = (rect.right - rect.left) / 2; + lprop.endPoint.y = size.height; + ZeroMemory(&bprop, sizeof (D2D1_BRUSH_PROPERTIES)); + bprop.opacity = 1.0; + bprop.transform._11 = 1; + bprop.transform._22 = 1; + hr = rt->CreateLinearGradientBrush(&lprop, &bprop, + collection, &brush); + if (hr != S_OK) + logHRESULT(L"error creating gradient brush for H slider", hr); + rt->FillRectangle(&rect, brush); + brush->Release(); + collection->Release(); + + // now draw a black arrow + // to avoid needing a geometry, this will just be a rotated square + center.x = 0; + center.y = c->h * size.height; + // compute the length of each side; the diagonal of the square is 2 * offset to gradient + hypot = rect.left; + // a^2 + a^2 = c^2 -> 2a^2 = c^2 + // a = sqrt(c^2/2) + hypot *= hypot; + hypot /= 2; + leg = sqrt(hypot); + rect.left = -leg; + rect.top = center.y - leg; + rect.right = leg; + rect.bottom = center.y + leg; + + // now we need to rotate the render target 45° (either way works) about the center point + rt->GetTransform(&oldtf); + rotate = oldtf * D2D1::Matrix3x2F::Rotation(45, center); + rt->SetTransform(&rotate); + + // and draw + mcolor.r = 0.0; + mcolor.g = 0.0; + mcolor.b = 0.0; + mcolor.a = 1.0; + hr = rt->CreateSolidColorBrush(&mcolor, &bprop, &markerBrush); + if (hr != S_OK) + logHRESULT(L"error creating brush for H slider marker", hr); + rt->FillRectangle(&rect, markerBrush); + markerBrush->Release(); + + // clean up + rt->SetTransform(&oldtf); +} + +static LRESULT CALLBACK hSliderSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) +{ + ID2D1RenderTarget *rt; + struct colorDialog *c; + D2D1_POINT_2F *pos; + D2D1_SIZE_F *size; + + c = (struct colorDialog *) dwRefData; + switch (uMsg) { + case msgD2DScratchPaint: + rt = (ID2D1RenderTarget *) lParam; + drawHSlider(c, rt); + return 0; + case msgD2DScratchLButtonDown: + pos = (D2D1_POINT_2F *) wParam; + size = (D2D1_SIZE_F *) lParam; + c->h = pos->y / size->height; + updateDialog(c, NULL); + return 0; + case WM_NCDESTROY: + if (RemoveWindowSubclass(hwnd, hSliderSubProc, uIdSubclass) == FALSE) + logLastError(L"error removing color dialog H slider subclass"); + break; + } + return DefSubclassProc(hwnd, uMsg, wParam, lParam); +} + // TODO extract into d2dscratch.cpp, use in font dialog HWND replaceWithD2DScratch(HWND parent, int id, SUBCLASSPROC subproc, void *data) { @@ -608,6 +740,7 @@ static struct colorDialog *beginColorDialog(HWND hwnd, LPARAM lParam) c->editHex = getDlgItem(c->hwnd, rcHex); c->svChooser = replaceWithD2DScratch(c->hwnd, rcColorSVChooser, svChooserSubProc, c); + c->hSlider = replaceWithD2DScratch(c->hwnd, rcColorHSlider, hSliderSubProc, c); fixupControlPositions(c); From 22a1525d240c5127312a074d0d891daf67ce4491 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 17 May 2016 22:55:35 -0400 Subject: [PATCH 0056/1329] Implemented the grid-drawing function. Now we can write the other two views. --- windows/colordialog.cpp | 76 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/windows/colordialog.cpp b/windows/colordialog.cpp index f2f52ed7..862192ba 100644 --- a/windows/colordialog.cpp +++ b/windows/colordialog.cpp @@ -291,6 +291,79 @@ static void updateDialog(struct colorDialog *c, HWND whichChanged) c->updating = FALSE; } +// this imitates http://blogs.msdn.com/b/wpfsdk/archive/2006/10/26/uncommon-dialogs--font-chooser-and-color-picker-dialogs.aspx +static void drawGrid(ID2D1RenderTarget *rt, D2D1_RECT_F *fillRect) +{ + D2D1_SIZE_F size; + D2D1_PIXEL_FORMAT pformat; + ID2D1BitmapRenderTarget *brt; + D2D1_COLOR_F color; + D2D1_BRUSH_PROPERTIES bprop; + ID2D1SolidColorBrush *brush; + D2D1_RECT_F rect; + ID2D1Bitmap *bitmap; + D2D1_BITMAP_BRUSH_PROPERTIES bbp; + ID2D1BitmapBrush *bb; + HRESULT hr; + + // mind the divisions; they represent the fact the original uses a viewport + size.width = 100 / 10; + size.height = 100 / 10; + pformat = rt->GetPixelFormat(); + hr = rt->CreateCompatibleRenderTarget(&size, NULL, + &pformat, D2D1_COMPATIBLE_RENDER_TARGET_OPTIONS_NONE, + &brt); + if (hr != S_OK) + logHRESULT(L"error creating render target for grid", hr); + + brt->BeginDraw(); + + color.r = 1.0; + color.g = 1.0; + color.b = 1.0; + color.a = 1.0; + brt->Clear(&color); + + color = D2D1::ColorF(D2D1::ColorF::LightGray, 1.0); + ZeroMemory(&bprop, sizeof (D2D1_BRUSH_PROPERTIES)); + bprop.opacity = 1.0; + bprop.transform._11 = 1; + bprop.transform._22 = 1; + hr = brt->CreateSolidColorBrush(&color, &bprop, &brush); + if (hr != S_OK) + logHRESULT(L"error creating brush for grid", hr); + rect.left = 0; + rect.top = 0; + rect.right = 50 / 10; + rect.bottom = 50 / 10; + brt->FillRectangle(&rect, brush); + rect.left = 50 / 10; + rect.top = 50 / 10; + rect.right = 100 / 10; + rect.bottom = 100 / 10; + brt->FillRectangle(&rect, brush); + brush->Release(); + + hr = brt->EndDraw(NULL, NULL); + if (hr != S_OK) + logHRESULT(L"error finalizing render target for grid", hr); + hr = brt->GetBitmap(&bitmap); + if (hr != S_OK) + logHRESULT(L"error getting bitmap for grid", hr); + brt->Release(); + + ZeroMemory(&bbp, sizeof (D2D1_BITMAP_BRUSH_PROPERTIES)); + bbp.extendModeX = D2D1_EXTEND_MODE_WRAP; + bbp.extendModeY = D2D1_EXTEND_MODE_WRAP; + bbp.interpolationMode = D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR; + hr = rt->CreateBitmapBrush(bitmap, &bbp, &bprop, &bb); + if (hr != S_OK) + logHRESULT(L"error creating bitmap brush for grid", hr); + rt->FillRectangle(fillRect, bb); + bb->Release(); + bitmap->Release(); +} + // this interesting approach comes from http://blogs.msdn.com/b/wpfsdk/archive/2006/10/26/uncommon-dialogs--font-chooser-and-color-picker-dialogs.aspx static void drawSVChooser(struct colorDialog *c, ID2D1RenderTarget *rt) { @@ -316,7 +389,7 @@ static void drawSVChooser(struct colorDialog *c, ID2D1RenderTarget *rt) rect.right = size.width; rect.bottom = size.height; - // TODO draw checkerboard + drawGrid(rt, &rect); // first, draw a vertical gradient from the current hue at max S/V to black // the source example draws it upside down; let's do so too just to be safe @@ -341,6 +414,7 @@ static void drawSVChooser(struct colorDialog *c, ID2D1RenderTarget *rt) lprop.startPoint.y = size.height; lprop.endPoint.x = size.width / 2; lprop.endPoint.y = 0; + // TODO decide what to do about the duplication of this ZeroMemory(&bprop, sizeof (D2D1_BRUSH_PROPERTIES)); bprop.opacity = c->a; // note this part; we also use it below for the layer bprop.transform._11 = 1; From 341f8373d37a9c978a87c74a109789d7c93076ac Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 18 May 2016 00:20:41 -0400 Subject: [PATCH 0057/1329] Implemented the previewer. That just leaves the opacity slider! --- windows/colordialog.cpp | 56 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/windows/colordialog.cpp b/windows/colordialog.cpp index 862192ba..91d807a8 100644 --- a/windows/colordialog.cpp +++ b/windows/colordialog.cpp @@ -285,7 +285,7 @@ static void updateDialog(struct colorDialog *c, HWND whichChanged) // TODO TRUE? invalidateRect(c->svChooser, NULL, TRUE); invalidateRect(c->hSlider, NULL, TRUE); -//TODO invalidateRect(c->preview, NULL, TRUE); + invalidateRect(c->preview, NULL, TRUE); //TODO invalidateRect(c->opacitySlider, NULL, TRUE); c->updating = FALSE; @@ -688,6 +688,59 @@ static LRESULT CALLBACK hSliderSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPAR return DefSubclassProc(hwnd, uMsg, wParam, lParam); } +static void drawPreview(struct colorDialog *c, ID2D1RenderTarget *rt) +{ + D2D1_SIZE_F size; + D2D1_RECT_F rect; + double r, g, b; + D2D1_COLOR_F color; + D2D1_BRUSH_PROPERTIES bprop; + ID2D1SolidColorBrush *brush; + HRESULT hr; + + size = rt->GetSize(); + rect.left = 0; + rect.top = 0; + rect.right = size.width; + rect.bottom = size.height; + + drawGrid(rt, &rect); + + hsv2RGB(c->h, c->s, c->v, &r, &g, &b); + color.r = r; + color.g = g; + color.b = b; + color.a = c->a; + ZeroMemory(&bprop, sizeof (D2D1_BRUSH_PROPERTIES)); + bprop.opacity = 1.0; + bprop.transform._11 = 1; + bprop.transform._22 = 1; + hr = rt->CreateSolidColorBrush(&color, &bprop, &brush); + if (hr != S_OK) + logHRESULT(L"error creating brush for preview", hr); + rt->FillRectangle(&rect, brush); + brush->Release(); +} + +static LRESULT CALLBACK previewSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) +{ + ID2D1RenderTarget *rt; + struct colorDialog *c; + + c = (struct colorDialog *) dwRefData; + switch (uMsg) { + case msgD2DScratchPaint: + rt = (ID2D1RenderTarget *) lParam; + drawPreview(c, rt); + return 0; + case WM_NCDESTROY: + if (RemoveWindowSubclass(hwnd, previewSubProc, uIdSubclass) == FALSE) + logLastError(L"error removing color dialog previewer subclass"); + break; + } + return DefSubclassProc(hwnd, uMsg, wParam, lParam); +} + // TODO extract into d2dscratch.cpp, use in font dialog HWND replaceWithD2DScratch(HWND parent, int id, SUBCLASSPROC subproc, void *data) { @@ -815,6 +868,7 @@ static struct colorDialog *beginColorDialog(HWND hwnd, LPARAM lParam) c->svChooser = replaceWithD2DScratch(c->hwnd, rcColorSVChooser, svChooserSubProc, c); c->hSlider = replaceWithD2DScratch(c->hwnd, rcColorHSlider, hSliderSubProc, c); + c->preview = replaceWithD2DScratch(c->hwnd, rcPreview, previewSubProc, c); fixupControlPositions(c); From de1571cd1ff1083211cc851ef76e9d954aca6b6c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 18 May 2016 00:46:14 -0400 Subject: [PATCH 0058/1329] And implemented the opacity slider. We're done with this dialog! --- windows/colordialog.cpp | 174 ++++++++++++++++++++++++++++++++-------- 1 file changed, 139 insertions(+), 35 deletions(-) diff --git a/windows/colordialog.cpp b/windows/colordialog.cpp index 91d807a8..159593e0 100644 --- a/windows/colordialog.cpp +++ b/windows/colordialog.cpp @@ -286,7 +286,7 @@ static void updateDialog(struct colorDialog *c, HWND whichChanged) invalidateRect(c->svChooser, NULL, TRUE); invalidateRect(c->hSlider, NULL, TRUE); invalidateRect(c->preview, NULL, TRUE); -//TODO invalidateRect(c->opacitySlider, NULL, TRUE); + invalidateRect(c->opacitySlider, NULL, TRUE); c->updating = FALSE; } @@ -558,6 +558,52 @@ static LRESULT CALLBACK svChooserSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LP return DefSubclassProc(hwnd, uMsg, wParam, lParam); } +static void drawArrow(ID2D1RenderTarget *rt, D2D1_POINT_2F center, double hypot) +{ + double leg; + D2D1_RECT_F rect; + D2D1_MATRIX_3X2_F oldtf, rotate; + D2D1_COLOR_F color; + D2D1_BRUSH_PROPERTIES bprop; + ID2D1SolidColorBrush *brush; + HRESULT hr; + + // to avoid needing a geometry, this will just be a rotated square + // compute the length of each side; the diagonal of the square is 2 * offset to gradient + // a^2 + a^2 = c^2 -> 2a^2 = c^2 + // a = sqrt(c^2/2) + hypot *= hypot; + hypot /= 2; + leg = sqrt(hypot); + rect.left = center.x - leg; + rect.top = center.y - leg; + rect.right = center.x + leg; + rect.bottom = center.y + leg; + + // now we need to rotate the render target 45° (either way works) about the center point + rt->GetTransform(&oldtf); + rotate = oldtf * D2D1::Matrix3x2F::Rotation(45, center); + rt->SetTransform(&rotate); + + // and draw + color.r = 0.0; + color.g = 0.0; + color.b = 0.0; + color.a = 1.0; + ZeroMemory(&bprop, sizeof (D2D1_BRUSH_PROPERTIES)); + bprop.opacity = 1.0; + bprop.transform._11 = 1; + bprop.transform._22 = 1; + hr = rt->CreateSolidColorBrush(&color, &bprop, &brush); + if (hr != S_OK) + logHRESULT(L"error creating brush for arrow", hr); + rt->FillRectangle(&rect, brush); + brush->Release(); + + // clean up + rt->SetTransform(&oldtf); +} + // the gradient stuff also comes from http://blogs.msdn.com/b/wpfsdk/archive/2006/10/26/uncommon-dialogs--font-chooser-and-color-picker-dialogs.aspx #define nStops (30) #define degPerStop (360 / nStops) @@ -575,11 +621,8 @@ static void drawHSlider(struct colorDialog *c, ID2D1RenderTarget *rt) D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES lprop; D2D1_BRUSH_PROPERTIES bprop; ID2D1LinearGradientBrush *brush; - double hypot, leg; + double hypot; D2D1_POINT_2F center; - D2D1_MATRIX_3X2_F oldtf, rotate; - D2D1_COLOR_F mcolor; - ID2D1SolidColorBrush *markerBrush; HRESULT hr; size = rt->GetSize(); @@ -626,39 +669,10 @@ static void drawHSlider(struct colorDialog *c, ID2D1RenderTarget *rt) collection->Release(); // now draw a black arrow - // to avoid needing a geometry, this will just be a rotated square center.x = 0; center.y = c->h * size.height; - // compute the length of each side; the diagonal of the square is 2 * offset to gradient hypot = rect.left; - // a^2 + a^2 = c^2 -> 2a^2 = c^2 - // a = sqrt(c^2/2) - hypot *= hypot; - hypot /= 2; - leg = sqrt(hypot); - rect.left = -leg; - rect.top = center.y - leg; - rect.right = leg; - rect.bottom = center.y + leg; - - // now we need to rotate the render target 45° (either way works) about the center point - rt->GetTransform(&oldtf); - rotate = oldtf * D2D1::Matrix3x2F::Rotation(45, center); - rt->SetTransform(&rotate); - - // and draw - mcolor.r = 0.0; - mcolor.g = 0.0; - mcolor.b = 0.0; - mcolor.a = 1.0; - hr = rt->CreateSolidColorBrush(&mcolor, &bprop, &markerBrush); - if (hr != S_OK) - logHRESULT(L"error creating brush for H slider marker", hr); - rt->FillRectangle(&rect, markerBrush); - markerBrush->Release(); - - // clean up - rt->SetTransform(&oldtf); + drawArrow(rt, center, hypot); } static LRESULT CALLBACK hSliderSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) @@ -741,6 +755,95 @@ static LRESULT CALLBACK previewSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPAR return DefSubclassProc(hwnd, uMsg, wParam, lParam); } +// once again, this is based on the Microsoft sample above +static void drawOpacitySlider(struct colorDialog *c, ID2D1RenderTarget *rt) +{ + D2D1_SIZE_F size; + D2D1_RECT_F rect; + D2D1_GRADIENT_STOP stops[2]; + ID2D1GradientStopCollection *collection; + D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES lprop; + D2D1_BRUSH_PROPERTIES bprop; + ID2D1LinearGradientBrush *brush; + double hypot; + D2D1_POINT_2F center; + HRESULT hr; + + size = rt->GetSize(); + rect.left = 0; + rect.top = 0; + rect.right = size.width; + rect.bottom = size.height * (5.0 / 6.0); // bottommost sixth for arrow + + drawGrid(rt, &rect); + + stops[0].position = 0.0; + stops[0].color.r = 0.0; + stops[0].color.g = 0.0; + stops[0].color.b = 0.0; + stops[0].color.a = 1.0; + stops[1].position = 1.0; + stops[1].color.r = 1.0; // this is the XAML color Transparent, as in the source + stops[1].color.g = 1.0; + stops[1].color.b = 1.0; + stops[1].color.a = 0.0; + hr = rt->CreateGradientStopCollection(stops, 2, + // note that in this case this gamma is explicitly specified by the original + D2D1_GAMMA_2_2, D2D1_EXTEND_MODE_CLAMP, + &collection); + if (hr != S_OK) + logHRESULT(L"error creating stop collection for opacity slider gradient", hr); + ZeroMemory(&lprop, sizeof (D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES)); + lprop.startPoint.x = 0; + lprop.startPoint.y = (rect.bottom - rect.top) / 2; + lprop.endPoint.x = size.width; + lprop.endPoint.y = (rect.bottom - rect.top) / 2; + ZeroMemory(&bprop, sizeof (D2D1_BRUSH_PROPERTIES)); + bprop.opacity = 1.0; + bprop.transform._11 = 1; + bprop.transform._22 = 1; + hr = rt->CreateLinearGradientBrush(&lprop, &bprop, + collection, &brush); + if (hr != S_OK) + logHRESULT(L"error creating gradient brush for opacity slider", hr); + rt->FillRectangle(&rect, brush); + brush->Release(); + collection->Release(); + + // now draw a black arrow + center.x = (1 - c->a) * size.width; + center.y = size.height; + hypot = size.height - rect.bottom; + drawArrow(rt, center, hypot); +} + +static LRESULT CALLBACK opacitySliderSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) +{ + ID2D1RenderTarget *rt; + struct colorDialog *c; + D2D1_POINT_2F *pos; + D2D1_SIZE_F *size; + + c = (struct colorDialog *) dwRefData; + switch (uMsg) { + case msgD2DScratchPaint: + rt = (ID2D1RenderTarget *) lParam; + drawOpacitySlider(c, rt); + return 0; + case msgD2DScratchLButtonDown: + pos = (D2D1_POINT_2F *) wParam; + size = (D2D1_SIZE_F *) lParam; + c->a = 1 - (pos->x / size->width); + updateDialog(c, NULL); + return 0; + case WM_NCDESTROY: + if (RemoveWindowSubclass(hwnd, opacitySliderSubProc, uIdSubclass) == FALSE) + logLastError(L"error removing color dialog opacity slider subclass"); + break; + } + return DefSubclassProc(hwnd, uMsg, wParam, lParam); +} + // TODO extract into d2dscratch.cpp, use in font dialog HWND replaceWithD2DScratch(HWND parent, int id, SUBCLASSPROC subproc, void *data) { @@ -869,6 +972,7 @@ static struct colorDialog *beginColorDialog(HWND hwnd, LPARAM lParam) c->svChooser = replaceWithD2DScratch(c->hwnd, rcColorSVChooser, svChooserSubProc, c); c->hSlider = replaceWithD2DScratch(c->hwnd, rcColorHSlider, hSliderSubProc, c); c->preview = replaceWithD2DScratch(c->hwnd, rcPreview, previewSubProc, c); + c->opacitySlider = replaceWithD2DScratch(c->hwnd, rcOpacitySlider, opacitySliderSubProc, c); fixupControlPositions(c); From 33f4a427c3cfa4b914586be94a11d858f092bd6a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 18 May 2016 14:40:03 -0400 Subject: [PATCH 0059/1329] Added a uiColorButton to the control gallery. --- examples/controlgallery/main.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/examples/controlgallery/main.c b/examples/controlgallery/main.c index 711db1bd..f29033e6 100644 --- a/examples/controlgallery/main.c +++ b/examples/controlgallery/main.c @@ -165,6 +165,10 @@ int main(void) uiControl(uiNewFontButton()), 0); + uiBoxAppend(inner, + uiControl(uiNewColorButton()), + 0); + inner2 = uiNewVerticalBox(); uiBoxSetPadded(inner2, 1); uiBoxAppend(hbox, uiControl(inner2), 1); From 6575f448185ee4cb8bd5e386bef4c92831528d1c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 20 May 2016 15:06:02 -0400 Subject: [PATCH 0060/1329] Removed some dummy debug code. Fixes #24. Fixes #33. --- darwin/multilineentry.m | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/darwin/multilineentry.m b/darwin/multilineentry.m index f4ee69b8..9033cc49 100644 --- a/darwin/multilineentry.m +++ b/darwin/multilineentry.m @@ -173,6 +173,8 @@ uiMultilineEntry *uiNewMultilineEntry(void) return e; } +#if 0 + NSMutableString *s; static void add(const char *fmt, ...) { @@ -322,3 +324,5 @@ _s.tv = tv; fprintf(stdout, "%s", [s UTF8String]); } + +#endif From 46a280cf46df0c68dacf9e15880b56c9fe695a33 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 20 May 2016 15:07:10 -0400 Subject: [PATCH 0061/1329] More TODOs. --- darwin/multilineentry.m | 1 + 1 file changed, 1 insertion(+) diff --git a/darwin/multilineentry.m b/darwin/multilineentry.m index 9033cc49..23e3d5c4 100644 --- a/darwin/multilineentry.m +++ b/darwin/multilineentry.m @@ -173,6 +173,7 @@ uiMultilineEntry *uiNewMultilineEntry(void) return e; } +// TODO #if 0 NSMutableString *s; From 2c0e333ca0891d37c77360c6035d43c1ed156e8d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 21 May 2016 02:00:08 -0400 Subject: [PATCH 0062/1329] Converted uiRadioButtons on OS X to use a NSView of NSButtons instead of NSMatrix; the latter was deprecated on 10.8 and has lots of little quirks that made it annoying to use. --- darwin/checkbox.m | 1 + darwin/radiobuttons.m | 140 ++++++++++++++++++++++++++++++------------ 2 files changed, 102 insertions(+), 39 deletions(-) diff --git a/darwin/checkbox.m b/darwin/checkbox.m index fea82abb..3914cfd4 100644 --- a/darwin/checkbox.m +++ b/darwin/checkbox.m @@ -110,6 +110,7 @@ uiCheckbox *uiNewCheckbox(const char *text) uiDarwinNewControl(uiCheckbox, c); + // TODO bezel style? transparent? c->button = [[NSButton alloc] initWithFrame:NSZeroRect]; [c->button setTitle:toNSString(text)]; [c->button setButtonType:NSSwitchButton]; diff --git a/darwin/radiobuttons.m b/darwin/radiobuttons.m index 59218ad8..eb46e6f4 100644 --- a/darwin/radiobuttons.m +++ b/darwin/radiobuttons.m @@ -1,68 +1,130 @@ // 14 august 2015 #import "uipriv_darwin.h" -// TODO the selection should NOT be lost when starting a new drag +// In the old days you would use a NSMatrix for this; as of OS X 10.8 this was deprecated and now you need just a bunch of NSButtons with the same superview AND same action method. +// This is documented on the NSMatrix page, but the rest of the OS X documentation says to still use NSMatrix. +// NSMatrix has weird quirks anyway... +// TODO verify this works on 10.7. + +// TODO +// - check that multiple radio buttons on the same parent container work right +// - 6 units of spacing between buttons, as suggested by Interface Builder? struct uiRadioButtons { uiDarwinControl c; - NSMatrix *matrix; + NSView *view; + NSMutableArray *buttons; + NSMutableArray *constraints; + NSLayoutConstraint *lastv; }; -uiDarwinControlAllDefaults(uiRadioButtons, matrix) +uiDarwinControlAllDefaultsExceptDestroy(uiRadioButtons, view) -static NSButtonCell *cellAt(uiRadioButtons *r, uintmax_t n) +static void uiRadioButtonsDestroy(uiControl *c) { - return (NSButtonCell *) [r->matrix cellAtRow:n column:0]; + uiRadioButtons *r = uiRadioButtons(c); + NSButton *b; + + // drop the constraints + [r->view removeConstraints:r->constraints]; + [r->constraints release]; + if (r->lastv != nil) + [r->lastv release]; + // destroy the buttons + for (b in r->buttons) + [b removeFromSuperview]; + [r->buttons release]; + // and destroy ourselves + [r->view release]; + uiFreeControl(uiControl(r)); +} + +static NSButton *buttonAt(uiRadioButtons *r, uintmax_t n) +{ + return (NSButton *) [r->buttons objectAtIndex:n]; } void uiRadioButtonsAppend(uiRadioButtons *r, const char *text) { - intmax_t prevSelection; + NSButton *b, *b2; + NSLayoutConstraint *constraint; - // renewRows:columns: will reset the selection - prevSelection = [r->matrix selectedRow]; + // TODO bezel style? transparent? + b = [[NSButton alloc] initWithFrame:NSZeroRect]; + [b setTitle:toNSString(text)]; + [b setButtonType:NSRadioButton]; + [b setBordered:NO]; + uiDarwinSetControlFont(b, NSRegularControlSize); + [b setTranslatesAutoresizingMaskIntoConstraints:NO]; - [r->matrix renewRows:([r->matrix numberOfRows] + 1) columns:1]; - [cellAt(r, [r->matrix numberOfRows] - 1) setTitle:toNSString(text)]; + // TODO set target + [b setAction:@selector(onClicked:)]; - // this will definitely cause a resize in at least the vertical direction, even if not in the horizontal - // DO NOT CALL sizeToCells! this will glitch out; see http://stackoverflow.com/questions/32162562/dynamically-adding-cells-to-a-nsmatrix-laid-out-with-auto-layout-has-weird-effec + [r->buttons addObject:b]; + [r->view addSubview:b]; - // and renew the previous selection - // we need to turn on allowing empty selection for this to work properly on the initial state - // TODO this doesn't actually work - [r->matrix setAllowsEmptySelection:YES]; - [r->matrix selectCellAtRow:prevSelection column:0]; - [r->matrix setAllowsEmptySelection:NO]; + // pin horizontally to the edges of the superview + constraint = mkConstraint(b, NSLayoutAttributeLeading, + NSLayoutRelationEqual, + r->view, NSLayoutAttributeLeading, + 1, 0, + @"uiRadioButtons button leading constraint"); + [r->view addConstraint:constraint]; + [r->constraints addObject:constraint]; + constraint = mkConstraint(b, NSLayoutAttributeTrailing, + NSLayoutRelationEqual, + r->view, NSLayoutAttributeTrailing, + 1, 0, + @"uiRadioButtons button trailing constraint"); + [r->view addConstraint:constraint]; + [r->constraints addObject:constraint]; + + // if this is the first view, pin it to the top + // otherwise pin to the bottom of the last + if ([r->buttons count] == 1) + constraint = mkConstraint(b, NSLayoutAttributeTop, + NSLayoutRelationEqual, + r->view, NSLayoutAttributeTop, + 1, 0, + @"uiRadioButtons first button top constraint"); + else { + b2 = buttonAt(r, [r->buttons count] - 2); + constraint = mkConstraint(b, NSLayoutAttributeTop, + NSLayoutRelationEqual, + b2, NSLayoutAttributeBottom, + 1, 0, + @"uiRadioButtons non-first button top constraint"); + } + [r->view addConstraint:constraint]; + [r->constraints addObject:constraint]; + + // if there is a previous bottom constraint, remove it + if (r->lastv != nil) { + [r->view removeConstraint:r->lastv]; + [r->constraints removeObject:r->lastv]; + [r->lastv release]; + } + + // and make the new bottom constraint + r->lastv = mkConstraint(b, NSLayoutAttributeBottom, + NSLayoutRelationEqual, + r->view, NSLayoutAttributeBottom, + 1, 0, + @"uiRadioButtons last button bottom constraint"); + [r->view addConstraint:r->lastv]; + [r->constraints addObject:r->lastv]; + [r->lastv retain]; } uiRadioButtons *uiNewRadioButtons(void) { uiRadioButtons *r; - NSButtonCell *cell; uiDarwinNewControl(uiRadioButtons, r); - // we have to set up the NSMatrix this way (prototype first) - // otherwise we won't be able to change its properties (such as the button type) - cell = [NSButtonCell new]; - [cell setButtonType:NSRadioButton]; - // works on NSCells too (same selector) - uiDarwinSetControlFont((NSControl *) cell, NSRegularControlSize); - - r->matrix = [[NSMatrix alloc] initWithFrame:NSZeroRect - mode:NSRadioModeMatrix - prototype:cell - numberOfRows:0 - numberOfColumns:0]; - // we manually twiddle this property to allow programmatic non-selection state - [r->matrix setAllowsEmptySelection:NO]; - [r->matrix setSelectionByRect:YES]; - [r->matrix setIntercellSpacing:NSMakeSize(4, 2)]; - [r->matrix setAutorecalculatesCellSize:YES]; - [r->matrix setDrawsBackground:NO]; - [r->matrix setDrawsCellBackground:NO]; - [r->matrix setAutosizesCells:YES]; + r->view = [[NSView alloc] initWithFrame:NSZeroRect]; + r->buttons = [NSMutableArray new]; + r->constraints = [NSMutableArray new]; return r; } From 7b04d974ac92095a2fb9b7ba37b4f086c6d74439 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 21 May 2016 11:28:11 -0400 Subject: [PATCH 0063/1329] More OS X 10.7 fixes and TODOs. --- darwin/colorbutton.m | 3 +-- darwin/radiobuttons.m | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/darwin/colorbutton.m b/darwin/colorbutton.m index 5c33228f..0766a9d2 100644 --- a/darwin/colorbutton.m +++ b/darwin/colorbutton.m @@ -87,7 +87,6 @@ struct uiColorButton { CGFloat cr, cg, cb, ca; // the given color may not be an RGBA color, which will cause the -getRed:green:blue:alpha: call to throw an exception - // TODO device RGB space? rgba = [[self color] colorUsingColorSpaceName:NSCalibratedRGBColorSpace]; [rgba getRed:&cr green:&cg blue:&cb alpha:&ca]; *r = cr; @@ -100,7 +99,7 @@ struct uiColorButton { - (void)libuiSetColor:(double)r g:(double)g b:(double)b a:(double)a { // TODO does this set the panel color? does it send a signal? - [self setColor:[NSColor colorWithRed:r green:g blue:b alpha:a]]; + [self setColor:[NSColor colorWithSRGBRed:r green:g blue:b alpha:a]]; } @end diff --git a/darwin/radiobuttons.m b/darwin/radiobuttons.m index eb46e6f4..5af0bdea 100644 --- a/darwin/radiobuttons.m +++ b/darwin/radiobuttons.m @@ -4,7 +4,7 @@ // In the old days you would use a NSMatrix for this; as of OS X 10.8 this was deprecated and now you need just a bunch of NSButtons with the same superview AND same action method. // This is documented on the NSMatrix page, but the rest of the OS X documentation says to still use NSMatrix. // NSMatrix has weird quirks anyway... -// TODO verify this works on 10.7. +// TODO this does not work on 10.7 // TODO // - check that multiple radio buttons on the same parent container work right From f5aa8cd32d558a247b2fa7a1de3dd52897bd6a16 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 21 May 2016 11:34:30 -0400 Subject: [PATCH 0064/1329] Consistency: calibrated color space -> sRGB color space. --- darwin/colorbutton.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/darwin/colorbutton.m b/darwin/colorbutton.m index 0766a9d2..7483fda1 100644 --- a/darwin/colorbutton.m +++ b/darwin/colorbutton.m @@ -87,7 +87,7 @@ struct uiColorButton { CGFloat cr, cg, cb, ca; // the given color may not be an RGBA color, which will cause the -getRed:green:blue:alpha: call to throw an exception - rgba = [[self color] colorUsingColorSpaceName:NSCalibratedRGBColorSpace]; + rgba = [[self color] colorUsingColorSpace:[NSColorSpace sRGBColorSpace]]; [rgba getRed:&cr green:&cg blue:&cb alpha:&ca]; *r = cr; *g = cg; From da9ee6a91377cccae93ec445d317668baa258673 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 21 May 2016 18:07:36 -0400 Subject: [PATCH 0065/1329] Vast README updates. --- README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/README.md b/README.md index 50736524..6d9b535d 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,17 @@ This README is being written.
[![Build Status](https://travis-ci.org/andlabs/libui.png)](https://travis-ci.org/andlabs/libui) +## Announcements + +**21 May 2016** +* I will now post announcements and updates here. +* Now that Ubuntu 16.04 LTS is here, no earlier than next Saturday, 28 May 2016 at noon EDT, **I will bump the minimum GTK+ version from 3.4 to 3.10**. This will add a lot of new features that I can now add to libui, such as search-oriented uiEntries, lists of arbitrary control layouts, and more. If you are still running a Linux distribution that doesn't come with 3.10, you will either need to upgrade or use jhbuild to set up a newer version of GTK+ in a private environment. +* You can decide if I should also drop OS X 10.7 at #46. + +## Updates + +(none yet) + ## Runtime Requirements * Windows: Windows Vista SP2 with Platform Update or newer @@ -26,6 +37,13 @@ This README is being written.
Needs to be written. Consult ui.h and the examples for details for now. +## Language Bindings + +libui was originally written as part of my [package ui for Go](https://github.com/andlabs/ui). Now that libui is separate, package ui has become a binding to libui. As such, package ui is the only official binding. + +Other people have made bindings to other languages: +* TODO list them here + ## Screenshots From examples/controlgallery: From 772caf19f308bc3fb842eeb95dc211a2b1d32d82 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 21 May 2016 18:09:09 -0400 Subject: [PATCH 0066/1329] Link to issue because for some reason github doesn't auto-link. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6d9b535d..7a1bd319 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ This README is being written.
**21 May 2016** * I will now post announcements and updates here. * Now that Ubuntu 16.04 LTS is here, no earlier than next Saturday, 28 May 2016 at noon EDT, **I will bump the minimum GTK+ version from 3.4 to 3.10**. This will add a lot of new features that I can now add to libui, such as search-oriented uiEntries, lists of arbitrary control layouts, and more. If you are still running a Linux distribution that doesn't come with 3.10, you will either need to upgrade or use jhbuild to set up a newer version of GTK+ in a private environment. -* You can decide if I should also drop OS X 10.7 at #46. +* You can decide if I should also drop OS X 10.7 [here](https://github.com/andlabs/libui/issues/46). ## Updates From a8fe3004a3c55c533c55d289ba263a0307bb64ab Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 21 May 2016 22:17:29 -0400 Subject: [PATCH 0067/1329] Fixed uiRadioButtons rendering on Windows. --- windows/radiobuttons.cpp | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/windows/radiobuttons.cpp b/windows/radiobuttons.cpp index d68aabe7..d2b80b53 100644 --- a/windows/radiobuttons.cpp +++ b/windows/radiobuttons.cpp @@ -74,8 +74,6 @@ static void uiRadioButtonsMinimumSize(uiWindowsControl *c, intmax_t *width, intm x = radiobuttonXFromLeftOfBoxToLeftOfLabel; y = radiobuttonHeight; - // get it for the radio button itself since that's what counts - // TODO for all of them? uiWindowsGetSizing((*(r->hwnds))[0], &sizing); uiWindowsSizingDlgUnitsToPixels(&sizing, &x, &y); @@ -86,28 +84,23 @@ static void uiRadioButtonsMinimumSize(uiWindowsControl *c, intmax_t *width, intm static void radiobuttonsRelayout(uiRadioButtons *r) { RECT client; - intmax_t height1; - intmax_t h; intmax_t x, y, width, height; + int height1; + uiWindowsSizing sizing; + if (r->hwnds->size() == 0) + return; uiWindowsEnsureGetClientRect(r->hwnd, &client); x = client.left; y = client.top; width = client.right - client.left; - height = client.bottom - client.top; - // TODO compute the real height1 - height1 = 25; + height1 = radiobuttonHeight; + uiWindowsGetSizing((*(r->hwnds))[0], &sizing); + uiWindowsSizingDlgUnitsToPixels(&sizing, NULL, &height1); + height = height1; for (const HWND &hwnd : *(r->hwnds)) { - h = height1; - if (h > height) // clip to height - h = height; - uiWindowsEnsureMoveWindowDuringResize(hwnd, x, y, width, h); - y += height1; - height -= height1; - if (height <= 0) // clip to height - break; - // TODO don't do the above to avoid overlap - // TODO in fact, only do this on add/remove/change labels/etc. + uiWindowsEnsureMoveWindowDuringResize(hwnd, x, y, width, height); + y += height; } } From 5891b764cb59e0619526d451a475f1d8ae64e109 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 21 May 2016 22:36:21 -0400 Subject: [PATCH 0068/1329] Forgot to take a reference on GtkWindows; this led to weird GObject warnings on the command line when closing a window. Update #40. --- unix/window.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/unix/window.c b/unix/window.c index ba2c9700..1ebe1eb7 100644 --- a/unix/window.c +++ b/unix/window.c @@ -158,5 +158,9 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) g_signal_connect(w->widget, "delete-event", G_CALLBACK(onClosing), w); uiWindowOnClosing(w, defaultOnClosing, NULL); + // normally it's SetParent() that does this, but we can't call SetParent() on a uiWindow + // TODO we really need to clean this up + g_object_ref(w->widget); + return w; } From 1c2acf59a38025a1b75452b0ca95502e68d1a3d2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 21 May 2016 22:42:55 -0400 Subject: [PATCH 0069/1329] Implemented uiControlDestroy() for uiRadioButtons on GTK+. Fixes #40. --- unix/radiobuttons.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/unix/radiobuttons.c b/unix/radiobuttons.c index 9a6cb42c..7956f5f6 100644 --- a/unix/radiobuttons.c +++ b/unix/radiobuttons.c @@ -15,7 +15,17 @@ uiUnixControlAllDefaultsExceptDestroy(uiRadioButtons) static void uiRadioButtonsDestroy(uiControl *c) { - // TODO + uiRadioButtons *r = uiRadioButtons(c); + GtkWidget *b; + + while (r->buttons->len != 0) { + b = GTK_WIDGET(g_ptr_array_remove_index(r->buttons, 0)); + gtk_widget_destroy(b); + } + g_ptr_array_free(r->buttons, TRUE); + // and free ourselves + g_object_unref(r->widget); + uiFreeControl(uiControl(r)); } void uiRadioButtonsAppend(uiRadioButtons *r, const char *text) From 1d8ea79e4593a4fdf25bc0ebabf22609ddab51c6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 22 May 2016 01:07:25 -0400 Subject: [PATCH 0070/1329] Removed uiControlVerifyDestroy(); we could have just had it in uiFreeControl(). --- README.md | 5 ++++- common/control.c | 6 +----- ui.h | 1 - ui_darwin.h | 1 - ui_unix.h | 1 - ui_windows.h | 1 - windows/window.cpp | 1 - 7 files changed, 5 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 7a1bd319..374eae61 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,10 @@ This README is being written.
## Updates -(none yet) +> Note that today's entry may be updated later today. + +* **22 May 2016** +** Removed `uiControlVerifyDestroy()`; that is now part of `uiFreeControl()` itself. ## Runtime Requirements diff --git a/common/control.c b/common/control.c index 64da9e03..28066461 100644 --- a/common/control.c +++ b/common/control.c @@ -71,14 +71,10 @@ uiControl *uiAllocControl(size_t size, uint32_t OSsig, uint32_t typesig, const c } void uiFreeControl(uiControl *c) -{ - uiFree(c); -} - -void uiControlVerifyDestroy(uiControl *c) { if (uiControlParent(c) != NULL) userbug("You cannot destroy a uiControl while it still has a parent. (control: %p)", c); + uiFree(c); } void uiControlVerifySetParent(uiControl *c, uiControl *parent) diff --git a/ui.h b/ui.h index 3968f01c..2302b7b4 100644 --- a/ui.h +++ b/ui.h @@ -77,7 +77,6 @@ _UI_EXTERN uiControl *uiAllocControl(size_t n, uint32_t OSsig, uint32_t typesig, _UI_EXTERN void uiFreeControl(uiControl *); // TODO make sure all controls have these -_UI_EXTERN void uiControlVerifyDestroy(uiControl *); _UI_EXTERN void uiControlVerifySetParent(uiControl *, uiControl *); _UI_EXTERN int uiControlEnabledToUser(uiControl *); diff --git a/ui_darwin.h b/ui_darwin.h index fde55b42..387e6829 100644 --- a/ui_darwin.h +++ b/ui_darwin.h @@ -38,7 +38,6 @@ _UI_EXTERN void uiDarwinControlSetHuggingPriority(uiDarwinControl *, NSLayoutPri #define uiDarwinControlDefaultDestroy(type, handlefield) \ static void type ## Destroy(uiControl *c) \ { \ - uiControlVerifyDestroy(c); \ [type(c)->handlefield release]; \ uiFreeControl(c); \ } diff --git a/ui_unix.h b/ui_unix.h index 6ea475a6..f8cdf3dd 100644 --- a/ui_unix.h +++ b/ui_unix.h @@ -25,7 +25,6 @@ _UI_EXTERN void uiUnixControlSetContainer(uiUnixControl *, GtkContainer *, gbool #define uiUnixControlDefaultDestroy(type) \ static void type ## Destroy(uiControl *c) \ { \ - uiControlVerifyDestroy(c); \ /* TODO is this safe on floating refs? */ \ g_object_unref(type(c)->widget); \ uiFreeControl(c); \ diff --git a/ui_windows.h b/ui_windows.h index b1a6362f..81185f51 100644 --- a/ui_windows.h +++ b/ui_windows.h @@ -41,7 +41,6 @@ _UI_EXTERN void uiWindowsControlAssignControlIDZOrder(uiWindowsControl *, LONG_P #define uiWindowsControlDefaultDestroy(type) \ static void type ## Destroy(uiControl *c) \ { \ - uiControlVerifyDestroy(c); \ uiWindowsEnsureDestroyWindow(type(c)->hwnd); \ uiFreeControl(c); \ } diff --git a/windows/window.cpp b/windows/window.cpp index 100008d8..f9806c72 100644 --- a/windows/window.cpp +++ b/windows/window.cpp @@ -144,7 +144,6 @@ static void uiWindowDestroy(uiControl *c) { uiWindow *w = uiWindow(c); - // TODO make sure all ports have the necessary verifications // first hide ourselves ShowWindow(w->hwnd, SW_HIDE); // now destroy the child From 5a4b6aa6f00119a485e5e2f336683a2860424e32 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 22 May 2016 12:00:44 -0400 Subject: [PATCH 0071/1329] Switched from using M_PI to a named constant uiPi. --- README.md | 1 + TODO.md | 8 ---- darwin/draw.m | 4 +- test/drawtests.c | 90 +++++++++++++++++++++--------------------- test/page7a.c | 2 +- test/page7c.c | 2 +- test/test.h | 3 -- ui.h | 4 ++ unix/GNUosspecific.mk | 2 +- unix/drawpath.c | 8 ++-- unix/uipriv_unix.h | 2 - windows/drawmatrix.cpp | 2 +- windows/drawpath.cpp | 12 +++--- windows/winapi.hpp | 3 -- 14 files changed, 66 insertions(+), 77 deletions(-) diff --git a/README.md b/README.md index 374eae61..b7a6b562 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ This README is being written.
* **22 May 2016** ** Removed `uiControlVerifyDestroy()`; that is now part of `uiFreeControl()` itself. +** Added `uiPi`, a constant for π. This is provided for C and C++ programmers, where there is no standard named constant for π; bindings authors shouldn't need to worry about this. ## Runtime Requirements diff --git a/TODO.md b/TODO.md index 8f9fdc1d..9b599845 100644 --- a/TODO.md +++ b/TODO.md @@ -19,14 +19,6 @@ - provide a way to get the currently selected uiTab page? set? -- add uiPi for portability; compare against: - - M_PI on all systems with different requirements - - _GNU_SOURCE on unix - - _USE_MATH_DEFINES on windows - - G_PI on GLib - - XM_PI from DirectX - - Go math.Pi - - make it so that the windows cntrols only register a resize if their new minimum size is larger than their current size to easen the effect of flicker - it won't remove that outright, but it'll help diff --git a/darwin/draw.m b/darwin/draw.m index 40c47a99..0552c07d 100644 --- a/darwin/draw.m +++ b/darwin/draw.m @@ -60,8 +60,8 @@ void uiDrawPathArcTo(uiDrawPath *p, double xCenter, double yCenter, double radiu // TODO likewise if (p->ended) implbug("attempt to add arc to ended path in uiDrawPathArcTo()"); - if (sweep > 2 * M_PI) - sweep = 2 * M_PI; + if (sweep > 2 * uiPi) + sweep = 2 * uiPi; cw = false; if (negative) cw = true; diff --git a/test/drawtests.c b/test/drawtests.c index ab1ca8bc..d821d495 100644 --- a/test/drawtests.c +++ b/test/drawtests.c @@ -87,16 +87,16 @@ static void drawOriginal(uiAreaDrawParams *p) uiDrawPathArcTo(path, 400, 100, 50, - 30. * (M_PI / 180.), - 300. * (M_PI / 180.), + 30. * (uiPi / 180.), + 300. * (uiPi / 180.), 0); // the sweep test below doubles as a clockwise test so a checkbox isn't needed anymore uiDrawPathLineTo(path, 400, 100); uiDrawPathNewFigureWithArc(path, 510, 100, 50, - 30. * (M_PI / 180.), - 300. * (M_PI / 180.), + 30. * (uiPi / 180.), + 300. * (uiPi / 180.), 0); uiDrawPathCloseFigure(path); // and now with 330 to make sure sweeps work properly @@ -104,15 +104,15 @@ static void drawOriginal(uiAreaDrawParams *p) uiDrawPathArcTo(path, 400, 210, 50, - 30. * (M_PI / 180.), - 330. * (M_PI / 180.), + 30. * (uiPi / 180.), + 330. * (uiPi / 180.), 0); uiDrawPathLineTo(path, 400, 210); uiDrawPathNewFigureWithArc(path, 510, 210, 50, - 30. * (M_PI / 180.), - 330. * (M_PI / 180.), + 30. * (uiPi / 180.), + 330. * (uiPi / 180.), 0); uiDrawPathCloseFigure(path); uiDrawPathEnd(path); @@ -160,7 +160,7 @@ static void drawArcs(uiAreaDrawParams *p) path = uiDrawNewPath(uiDrawFillModeWinding); - add = (2.0 * M_PI) / 12; + add = (2.0 * uiPi) / 12; x = start + rad; y = start + rad; @@ -196,7 +196,7 @@ static void drawArcs(uiAreaDrawParams *p) uiDrawPathNewFigureWithArc(path, x, y, rad, - (M_PI / 4), angle, + (uiPi / 4), angle, 0); angle += add; x += 2 * rad + step; @@ -210,7 +210,7 @@ static void drawArcs(uiAreaDrawParams *p) uiDrawPathArcTo(path, x, y, rad, - (M_PI / 4), angle, + (uiPi / 4), angle, 0); angle += add; x += 2 * rad + step; @@ -223,7 +223,7 @@ static void drawArcs(uiAreaDrawParams *p) uiDrawPathNewFigureWithArc(path, x, y, rad, - M_PI + (M_PI / 5), angle, + uiPi + (uiPi / 5), angle, 0); angle += add; x += 2 * rad + step; @@ -237,7 +237,7 @@ static void drawArcs(uiAreaDrawParams *p) uiDrawPathArcTo(path, x, y, rad, - M_PI + (M_PI / 5), angle, + uiPi + (uiPi / 5), angle, 0); angle += add; x += 2 * rad + step; @@ -519,7 +519,7 @@ static void drawD2DRadialBrush(uiAreaDrawParams *p) 75, 75, 75, 0, - 2 * M_PI, + 2 * uiPi, 0); uiDrawPathEnd(path); @@ -596,7 +596,7 @@ static void drawD2DPathGeometries(uiAreaDrawParams *p) uiDrawPathNewFigureWithArc(sun, (440.0 - 270.0) / 2 + 270.0, 255, 85, - M_PI, M_PI, + uiPi, uiPi, 0); uiDrawPathCloseFigure(sun); uiDrawPathEnd(sun); @@ -730,22 +730,22 @@ static void drawD2DGeometryGroup(uiAreaDrawParams *p) uiDrawPathNewFigureWithArc(alternate, 105, 105, 25, - 0, 2 * M_PI, + 0, 2 * uiPi, 0); uiDrawPathNewFigureWithArc(alternate, 105, 105, 50, - 0, 2 * M_PI, + 0, 2 * uiPi, 0); uiDrawPathNewFigureWithArc(alternate, 105, 105, 75, - 0, 2 * M_PI, + 0, 2 * uiPi, 0); uiDrawPathNewFigureWithArc(alternate, 105, 105, 100, - 0, 2 * M_PI, + 0, 2 * uiPi, 0); uiDrawPathEnd(alternate); @@ -753,22 +753,22 @@ static void drawD2DGeometryGroup(uiAreaDrawParams *p) uiDrawPathNewFigureWithArc(winding, 105, 105, 25, - 0, 2 * M_PI, + 0, 2 * uiPi, 0); uiDrawPathNewFigureWithArc(winding, 105, 105, 50, - 0, 2 * M_PI, + 0, 2 * uiPi, 0); uiDrawPathNewFigureWithArc(winding, 105, 105, 75, - 0, 2 * M_PI, + 0, 2 * uiPi, 0); uiDrawPathNewFigureWithArc(winding, 105, 105, 100, - 0, 2 * M_PI, + 0, 2 * uiPi, 0); uiDrawPathEnd(winding); @@ -850,7 +850,7 @@ static void drawD2DRotate(uiAreaDrawParams *p) uiDrawMatrixSetIdentity(&m); uiDrawMatrixRotate(&m, 468.0, 331.5, - 45.0 * (M_PI / 180)); + 45.0 * (uiPi / 180)); uiDrawTransform(p->Context, &m); uiDrawFill(p->Context, path, &fill); @@ -868,7 +868,7 @@ static void drawD2DRotate(uiAreaDrawParams *p) uiDrawMatrixSetIdentity(&m); uiDrawMatrixRotate(&m, 438.0, 301.5, - 45.0 * (M_PI / 180)); + 45.0 * (uiPi / 180)); uiDrawTransform(p->Context, &m); uiDrawFill(p->Context, path, &fill); @@ -993,7 +993,7 @@ void drawD2DSkew(uiAreaDrawParams *p) uiDrawMatrixSetIdentity(&m); uiDrawMatrixSkew(&m, 126.0, 301.5, - 45.0 * (M_PI / 180), 0); + 45.0 * (uiPi / 180), 0); uiDrawTransform(p->Context, &m); uiDrawFill(p->Context, path, &fill); @@ -1011,7 +1011,7 @@ void drawD2DSkew(uiAreaDrawParams *p) uiDrawMatrixSetIdentity(&m); uiDrawMatrixSkew(&m, 0, 0, - 45.0 * (M_PI / 180), 0); + 45.0 * (uiPi / 180), 0); uiDrawTransform(p->Context, &m); uiDrawFill(p->Context, path, &fill); @@ -1111,7 +1111,7 @@ static void drawD2DMultiTransforms(uiAreaDrawParams *p) uiDrawMatrixSetIdentity(&mrotate); uiDrawMatrixRotate(&mrotate, 330.0, 70.0, - 45.0 * (M_PI / 180)); + 45.0 * (uiPi / 180)); // save for when we do the opposite one uiDrawSave(p->Context); @@ -1136,7 +1136,7 @@ static void drawD2DMultiTransforms(uiAreaDrawParams *p) uiDrawMatrixSetIdentity(&mrotate); uiDrawMatrixRotate(&mrotate, 70.0, 70.0, - 45.0 * (M_PI / 180)); + 45.0 * (uiPi / 180)); uiDrawStroke(p->Context, path, &original, &originalsp); @@ -1256,8 +1256,8 @@ static void drawCSArc(uiAreaDrawParams *p) double xc = 128.0; double yc = 128.0; double radius = 100.0; - double angle1 = 45.0 * (M_PI / 180.0); - double angle2 = 180.0 * (M_PI / 180.0); + double angle1 = 45.0 * (uiPi / 180.0); + double angle2 = 180.0 * (uiPi / 180.0); uiDrawBrush source; uiDrawStrokeParams sp; uiDrawPath *path; @@ -1289,7 +1289,7 @@ static void drawCSArc(uiAreaDrawParams *p) uiDrawPathNewFigureWithArc(path, xc, yc, 10.0, - 0, 2 * M_PI, + 0, 2 * uiPi, 0); uiDrawPathEnd(path); uiDrawFill(p->Context, path, &source); @@ -1319,8 +1319,8 @@ static void drawCSArcNegative(uiAreaDrawParams *p) double xc = 128.0; double yc = 128.0; double radius = 100.0; - double angle1 = 45.0 * (M_PI / 180.0); - double angle2 = 180.0 * (M_PI / 180.0); + double angle1 = 45.0 * (uiPi / 180.0); + double angle2 = 180.0 * (uiPi / 180.0); uiDrawBrush source; uiDrawStrokeParams sp; uiDrawPath *path; @@ -1352,7 +1352,7 @@ static void drawCSArcNegative(uiAreaDrawParams *p) uiDrawPathNewFigureWithArc(path, xc, yc, 10.0, - 0, 2 * M_PI, + 0, 2 * uiPi, 0); uiDrawPathEnd(path); uiDrawFill(p->Context, path, &source); @@ -1396,7 +1396,7 @@ static void drawCSClip(uiAreaDrawParams *p) uiDrawPathNewFigureWithArc(path, 128.0, 128.0, 76.8, - 0, 2 * M_PI, + 0, 2 * uiPi, 0); uiDrawPathEnd(path); uiDrawClip(p->Context, path); @@ -1639,12 +1639,12 @@ static void drawCSFillStyle(uiAreaDrawParams *p) uiDrawPathNewFigureWithArc(path, 64, 64, 40, - 0, 2*M_PI, + 0, 2*uiPi, 0); uiDrawPathNewFigureWithArc(path, 192, 64, 40, - 0, -2*M_PI, + 0, -2*uiPi, 1); uiDrawPathEnd(path); @@ -1663,12 +1663,12 @@ static void drawCSFillStyle(uiAreaDrawParams *p) uiDrawPathNewFigureWithArc(path, 64, 64, 40, - 0, 2*M_PI, + 0, 2*uiPi, 0); uiDrawPathNewFigureWithArc(path, 192, 64, 40, - 0, -2*M_PI, + 0, -2*uiPi, 1); uiDrawPathEnd(path); @@ -1728,7 +1728,7 @@ static void drawCSRoundRect(uiAreaDrawParams *p) corner_radius = height / 10.0; /* and corner curvature radius */ double radius = corner_radius / aspect; - double degrees = M_PI / 180.0; + double degrees = uiPi / 180.0; uiDrawBrush source; uiDrawStrokeParams sp; @@ -1748,25 +1748,25 @@ static void drawCSRoundRect(uiAreaDrawParams *p) uiDrawPathNewFigureWithArc(path, x + width - radius, y + radius, radius, - -90 * degrees, M_PI / 2, + -90 * degrees, uiPi / 2, 0); // bottom right corner uiDrawPathArcTo(path, x + width - radius, y + height - radius, radius, - 0 * degrees, M_PI / 2, + 0 * degrees, uiPi / 2, 0); // bottom left corner uiDrawPathArcTo(path, x + radius, y + height - radius, radius, - 90 * degrees, M_PI / 2, + 90 * degrees, uiPi / 2, 0); // top left corner uiDrawPathArcTo(path, x + radius, y + radius, radius, - 180 * degrees, M_PI / 2, + 180 * degrees, uiPi / 2, 0); uiDrawPathCloseFigure(path); uiDrawPathEnd(path); diff --git a/test/page7a.c b/test/page7a.c index ff0db201..72e03216 100644 --- a/test/page7a.c +++ b/test/page7a.c @@ -41,7 +41,7 @@ static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *p) startText = uiEntryText(startAngle); sweepText = uiEntryText(sweep); - factor = M_PI / 180; + factor = uiPi / 180; if (uiCheckboxChecked(radians)) factor = 1; diff --git a/test/page7c.c b/test/page7c.c index 1ebddad1..ac6a316c 100644 --- a/test/page7c.c +++ b/test/page7c.c @@ -65,7 +65,7 @@ static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *dp) uiDrawPathNewFigureWithArc(path, areaSize / 2, areaSize / 2, circleRadius, - 0, 2 * M_PI, + 0, 2 * uiPi, 0); uiDrawPathEnd(path); stops[0].Pos =0.0; diff --git a/test/test.h b/test/test.h index be37440e..1866b30c 100644 --- a/test/test.h +++ b/test/test.h @@ -1,7 +1,4 @@ // 22 april 2015 -// TODO -#define _GNU_SOURCE -#define _USE_MATH_DEFINES #include #include #include diff --git a/ui.h b/ui.h index 2302b7b4..b4a97023 100644 --- a/ui.h +++ b/ui.h @@ -21,6 +21,10 @@ extern "C" { // This has the advantage of being ABI-able should we ever need an ABI... #define _UI_ENUM(s) typedef unsigned int s; enum +// This constant is provided because M_PI is nonstandard. +// This comes from Go's math.Pi, which in turn comes from http://oeis.org/A000796. +#define uiPi 3.14159265358979323846264338327950288419716939937510582097494459 + typedef struct uiInitOptions uiInitOptions; struct uiInitOptions { diff --git a/unix/GNUosspecific.mk b/unix/GNUosspecific.mk index b5fa75e5..7d8cb726 100644 --- a/unix/GNUosspecific.mk +++ b/unix/GNUosspecific.mk @@ -10,4 +10,4 @@ USESSONAME = 1 SOVERSION = $(SOVERSION0) SONAMEEXT = $(LIBSUFFIX).$(SOVERSION) # this is not gcc-global because OS X uses a different filename format -SONAMEFLAG = -Wl,-soname, \ No newline at end of file +SONAMEFLAG = -Wl,-soname, diff --git a/unix/drawpath.c b/unix/drawpath.c index acfcc953..a0165fb8 100644 --- a/unix/drawpath.c +++ b/unix/drawpath.c @@ -61,8 +61,8 @@ void uiDrawPathNewFigureWithArc(uiDrawPath *p, double xCenter, double yCenter, d { struct piece piece; - if (sweep > 2 * M_PI) - sweep = 2 * M_PI; + if (sweep > 2 * uiPi) + sweep = 2 * uiPi; piece.type = newFigureArc; piece.d[0] = xCenter; piece.d[1] = yCenter; @@ -87,8 +87,8 @@ void uiDrawPathArcTo(uiDrawPath *p, double xCenter, double yCenter, double radiu { struct piece piece; - if (sweep > 2 * M_PI) - sweep = 2 * M_PI; + if (sweep > 2 * uiPi) + sweep = 2 * uiPi; piece.type = arcTo; piece.d[0] = xCenter; piece.d[1] = yCenter; diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index 6a260830..67e48a85 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -3,8 +3,6 @@ #define GLIB_VERSION_MAX_ALLOWED GLIB_VERSION_2_32 #define GDK_VERSION_MIN_REQUIRED GDK_VERSION_3_4 #define GDK_VERSION_MAX_ALLOWED GDK_VERSION_3_4 -// TODO make this unnecessary -#define _GNU_SOURCE #include #include #include // see drawtext.c diff --git a/windows/drawmatrix.cpp b/windows/drawmatrix.cpp index 807b41ad..090972a5 100644 --- a/windows/drawmatrix.cpp +++ b/windows/drawmatrix.cpp @@ -43,7 +43,7 @@ void uiDrawMatrixScale(uiDrawMatrix *m, double xCenter, double yCenter, double x d2m(&dm, m); } -#define r2d(x) (x * (180.0 / M_PI)) +#define r2d(x) (x * (180.0 / uiPi)) void uiDrawMatrixRotate(uiDrawMatrix *m, double x, double y, double amount) { diff --git a/windows/drawpath.cpp b/windows/drawpath.cpp index be02b826..49855be6 100644 --- a/windows/drawpath.cpp +++ b/windows/drawpath.cpp @@ -96,20 +96,20 @@ static void drawArc(uiDrawPath *p, struct arc *a, void (*startFunction)(uiDrawPa fullCircle = FALSE; // use the absolute value to tackle both ≥2π and ≤-2π at the same time absSweep = fabs(a->sweep); - if (absSweep > (2 * M_PI)) // this part is easy + if (absSweep > (2 * uiPi)) // this part is easy fullCircle = TRUE; else { double aerDiff; - aerDiff = fabs(absSweep - (2 * M_PI)); + aerDiff = fabs(absSweep - (2 * uiPi)); // if we got here then we know a->sweep is larger (or the same!) fullCircle = aerDiff <= absSweep * aerMax; } // TODO make sure this works right for the negative direction if (fullCircle) { - a->sweep = M_PI; + a->sweep = uiPi; drawArc(p, a, startFunction); - a->startAngle += M_PI; + a->startAngle += uiPi; drawArc(p, a, NULL); return; } @@ -144,13 +144,13 @@ static void drawArc(uiDrawPath *p, struct arc *a, void (*startFunction)(uiDrawPa as.sweepDirection = D2D1_SWEEP_DIRECTION_CLOCKWISE; // TODO explain the outer if if (!a->negative) - if (a->sweep > M_PI) + if (a->sweep > uiPi) as.arcSize = D2D1_ARC_SIZE_LARGE; else as.arcSize = D2D1_ARC_SIZE_SMALL; else // TODO especially this part - if (a->sweep > M_PI) + if (a->sweep > uiPi) as.arcSize = D2D1_ARC_SIZE_SMALL; else as.arcSize = D2D1_ARC_SIZE_LARGE; diff --git a/windows/winapi.hpp b/windows/winapi.hpp index 5763f297..ed30b782 100644 --- a/windows/winapi.hpp +++ b/windows/winapi.hpp @@ -8,9 +8,6 @@ // TODO get rid of this #define INITGUID -// TODO see if we can remove this -#define _USE_MATH_DEFINES - // for the manifest #define ISOLATION_AWARE_ENABLED 1 From 3705ef05d1b3bc2883d0f05ed400e38fe51c9bda Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 22 May 2016 12:15:50 -0400 Subject: [PATCH 0072/1329] Fixed up uiWindow ownership mechanics in the Darwin backend. --- darwin/window.m | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/darwin/window.m b/darwin/window.m index a36cff83..1ece3f1e 100644 --- a/darwin/window.m +++ b/darwin/window.m @@ -93,7 +93,6 @@ static void uiWindowDestroy(uiControl *c) uiControlDestroy(w->child); } [windowDelegate unregisterWindow:w]; - // TODO make sure this next line is right [w->window release]; uiFreeControl(uiControl(w)); } @@ -248,9 +247,9 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) defer:YES]; [w->window setTitle:toNSString(title)]; - // explicitly release when closed - // the only thing that closes the window is us anyway - [w->window setReleasedWhenClosed:YES]; + // do NOT release when closed + // we manually do this in uiWindowDestroy() above + [w->window setReleasedWhenClosed:NO]; if (windowDelegate == nil) { windowDelegate = [windowDelegateClass new]; From d060744f879d9a76ee395daf4738a67bb4862cf2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 22 May 2016 13:09:13 -0400 Subject: [PATCH 0073/1329] Some TODO resolution. --- windows/colordialog.cpp | 3 +-- windows/sizing.cpp | 11 ++++++++--- windows/uipriv_windows.hpp | 3 +++ windows/window.cpp | 1 - 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/windows/colordialog.cpp b/windows/colordialog.cpp index 159593e0..a8beca30 100644 --- a/windows/colordialog.cpp +++ b/windows/colordialog.cpp @@ -932,8 +932,7 @@ static void fixupControlPositions(struct colorDialog *c) labelA, c->editADouble, c->editAInt, NULL); - // TODO this uses the message font, not the dialog font - uiWindowsGetSizing(c->hwnd, &sizing); + getSizing(c->hwnd, &sizing, (HFONT) SendMessageW(labelH, WM_GETFONT, 0, 0)); offset = sizing.InternalLeading; moveWindowsUp(c, offset, labelH, labelS, labelV, diff --git a/windows/sizing.cpp b/windows/sizing.cpp index a0abdb46..a6d25d6e 100644 --- a/windows/sizing.cpp +++ b/windows/sizing.cpp @@ -2,7 +2,7 @@ #include "uipriv_windows.hpp" // TODO rework the error handling -void uiWindowsGetSizing(HWND hwnd, uiWindowsSizing *sizing) +void getSizing(HWND hwnd, uiWindowsSizing *sizing, HFONT font) { HDC dc; HFONT prevfont; @@ -12,7 +12,7 @@ void uiWindowsGetSizing(HWND hwnd, uiWindowsSizing *sizing) dc = GetDC(hwnd); if (dc == NULL) logLastError(L"error getting DC"); - prevfont = (HFONT) SelectObject(dc, hMessageFont); + prevfont = (HFONT) SelectObject(dc, font); if (prevfont == NULL) logLastError(L"error loading control font into device context"); @@ -26,12 +26,17 @@ void uiWindowsGetSizing(HWND hwnd, uiWindowsSizing *sizing) sizing->BaseY = (int) tm.tmHeight; sizing->InternalLeading = tm.tmInternalLeading; - if (SelectObject(dc, prevfont) != hMessageFont) + if (SelectObject(dc, prevfont) != font) logLastError(L"error restoring previous font into device context"); if (ReleaseDC(hwnd, dc) == 0) logLastError(L"error releasing DC"); } +void uiWindowsGetSizing(HWND hwnd, uiWindowsSizing *sizing) +{ + return getSizing(hwnd, sizing, hMessageFont); +} + #define dlgUnitsToX(dlg, baseX) MulDiv((dlg), (baseX), 4) #define dlgUnitsToY(dlg, baseY) MulDiv((dlg), (baseY), 8) diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index d1380af3..dcd31617 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -137,6 +137,9 @@ struct colorDialogRGBA { }; extern BOOL showColorDialog(HWND parent, struct colorDialogRGBA *c); +// sizing.cpp +extern void getSizing(HWND hwnd, uiWindowsSizing *sizing, HFONT font); + diff --git a/windows/window.cpp b/windows/window.cpp index f9806c72..42475b6d 100644 --- a/windows/window.cpp +++ b/windows/window.cpp @@ -185,7 +185,6 @@ static void uiWindowShow(uiControl *c) w->visible = 1; // just in case the window's minimum size wasn't recalculated already - // TODO is it needed? ensureMinimumWindowSize(w); if (w->shownOnce) { ShowWindow(w->hwnd, SW_SHOW); From f3dad94039bf79dfc08a10693c32aa62fcc9cd44 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 22 May 2016 13:42:37 -0400 Subject: [PATCH 0074/1329] Added CRLF translation to uiMultilineEntry on Windows. More TODOs. --- README.md | 1 + windows/multilineentry.cpp | 16 ++++++++++++++-- windows/uipriv_windows.hpp | 2 +- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b7a6b562..0f602090 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ This README is being written.
* **22 May 2016** ** Removed `uiControlVerifyDestroy()`; that is now part of `uiFreeControl()` itself. ** Added `uiPi`, a constant for π. This is provided for C and C++ programmers, where there is no standard named constant for π; bindings authors shouldn't need to worry about this. +** Fixed uiMultilineEntry not properly having line breaks on Windows. ## Runtime Requirements diff --git a/windows/multilineentry.cpp b/windows/multilineentry.cpp index 38908f38..afb5ceaa 100644 --- a/windows/multilineentry.cpp +++ b/windows/multilineentry.cpp @@ -63,15 +63,23 @@ static void defaultOnChanged(uiMultilineEntry *e, void *data) // TODO apply crlf conversion char *uiMultilineEntryText(uiMultilineEntry *e) { - return uiWindowsWindowText(e->hwnd); + char *out; + + out = uiWindowsWindowText(e->hwnd); + CRLFtoLF(out); + return out; } // TODO apply crlf conversion void uiMultilineEntrySetText(uiMultilineEntry *e, const char *text) { + char *crlf; + // doing this raises an EN_CHANGED e->inhibitChanged = TRUE; + crlf = LFtoCRLF(text); uiWindowsSetWindowText(e->hwnd, text); + uiFree(crlf); e->inhibitChanged = FALSE; // don't queue the control for resize; entry sizes are independent of their contents } @@ -80,14 +88,18 @@ void uiMultilineEntrySetText(uiMultilineEntry *e, const char *text) void uiMultilineEntryAppend(uiMultilineEntry *e, const char *text) { LRESULT n; + char *crlf; WCHAR *wtext; // TODO does doing this raise EN_CHANGED? // TODO preserve selection? caret? what if caret used to be at end? // TODO scroll to bottom? + // TODO overdraw issues n = SendMessageW(e->hwnd, WM_GETTEXTLENGTH, 0, 0); SendMessageW(e->hwnd, EM_SETSEL, n, n); - wtext = toUTF16(text); + crlf = LFtoCRLF(text); + wtext = toUTF16(crlf); + uiFree(crlf); SendMessageW(e->hwnd, EM_REPLACESEL, FALSE, (LPARAM) wtext); uiFree(wtext); } diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index dcd31617..7cd2be62 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -36,7 +36,7 @@ extern WCHAR *utf16dup(const WCHAR *orig); extern WCHAR *strf(const WCHAR *format, ...); extern WCHAR *vstrf(const WCHAR *format, va_list ap); extern char *LFtoCRLF(const char *lfonly); -extern void CRLFtoLF(const char *s); +extern void CRLFtoLF(char *s); extern WCHAR *ftoutf16(double d); extern WCHAR *itoutf16(intmax_t i); From 313ce47833fa84336c1e97f7efad45f46d7f4322 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 22 May 2016 13:56:36 -0400 Subject: [PATCH 0075/1329] Started non-wrapping multiline entries. Implemented on Windows. --- test/GNUfiles.mk | 1 + test/main.c | 5 ++++- test/page12.c | 14 ++++++++++++++ test/test.h | 3 +++ ui.h | 4 +--- windows/multilineentry.cpp | 14 ++++++++++++-- 6 files changed, 35 insertions(+), 6 deletions(-) create mode 100644 test/page12.c diff --git a/test/GNUfiles.mk b/test/GNUfiles.mk index 5072771e..6cb4b0e3 100644 --- a/test/GNUfiles.mk +++ b/test/GNUfiles.mk @@ -18,6 +18,7 @@ CFILES += \ test/page9.c \ test/page10.c \ test/page11.c \ + test/page12.c \ test/spaced.c HFILES += \ diff --git a/test/main.c b/test/main.c index d978f055..5b049120 100644 --- a/test/main.c +++ b/test/main.c @@ -47,7 +47,7 @@ int main(int argc, char *argv[]) uiWindow *w; uiBox *page2, *page3, *page4, *page5; uiBox *page6, *page7, *page8, *page9, *page10; - uiBox *page11; + uiBox *page11, *page12; uiTab *outerTab; uiTab *innerTab; int nomenus = 0; @@ -138,6 +138,9 @@ int main(int argc, char *argv[]) // page11 = makePage11(); // uiTabAppend(innerTab, "Page 11", uiControl(page11)); + page12 = makePage12(); + uiTabAppend(innerTab, "Page 12", uiControl(page12)); + if (startspaced) setSpaced(1); diff --git a/test/page12.c b/test/page12.c new file mode 100644 index 00000000..89203632 --- /dev/null +++ b/test/page12.c @@ -0,0 +1,14 @@ +// 22 may 2016 +#include "test.h" + +uiBox *makePage12(void) +{ + uiBox *page12; + + page12 = newHorizontalBox(); + + uiBoxAppend(page12, uiControl(uiNewMultilineEntry()), 1); + uiBoxAppend(page12, uiControl(uiNewNonWrappingMultilineEntry()), 1); + + return page12; +} diff --git a/test/test.h b/test/test.h index 1866b30c..b7257e5d 100644 --- a/test/test.h +++ b/test/test.h @@ -74,3 +74,6 @@ extern uiBox *makePage10(void); // page11.c extern uiBox *makePage11(void); + +// page12.c +extern uiBox *makePage12(void); diff --git a/ui.h b/ui.h index b4a97023..6febeaa4 100644 --- a/ui.h +++ b/ui.h @@ -202,9 +202,6 @@ _UI_EXTERN uiDateTimePicker *uiNewDateTimePicker(void); _UI_EXTERN uiDateTimePicker *uiNewDatePicker(void); _UI_EXTERN uiDateTimePicker *uiNewTimePicker(void); -// TODO merge with uiEntry? some things can't be shared (for instance, the future Invalid() -// TODO how are line endings converted? -// TODO provide a facility for allowing horizontal scrolling // TODO provide a facility for entering tab stops? typedef struct uiMultilineEntry uiMultilineEntry; #define uiMultilineEntry(this) ((uiMultilineEntry *) (this)) @@ -215,6 +212,7 @@ _UI_EXTERN void uiMultilineEntryOnChanged(uiMultilineEntry *e, void (*f)(uiMulti _UI_EXTERN int uiMultilineEntryReadOnly(uiMultilineEntry *e); _UI_EXTERN void uiMultilineEntrySetReadOnly(uiMultilineEntry *e, int readonly); _UI_EXTERN uiMultilineEntry *uiNewMultilineEntry(void); +_UI_EXTERN uiMultilineEntry *uiNewNonWrappingMultilineEntry(void); typedef struct uiMenuItem uiMenuItem; #define uiMenuItem(this) ((uiMenuItem *) (this)) diff --git a/windows/multilineentry.cpp b/windows/multilineentry.cpp index afb5ceaa..bac05752 100644 --- a/windows/multilineentry.cpp +++ b/windows/multilineentry.cpp @@ -126,7 +126,7 @@ void uiMultilineEntrySetReadOnly(uiMultilineEntry *e, int readonly) logLastError(L"error making uiMultilineEntry read-only"); } -uiMultilineEntry *uiNewMultilineEntry(void) +static uiMultilineEntry *finishMultilineEntry(DWORD style) { uiMultilineEntry *e; @@ -134,7 +134,7 @@ uiMultilineEntry *uiNewMultilineEntry(void) e->hwnd = uiWindowsEnsureCreateControlHWND(WS_EX_CLIENTEDGE, L"edit", L"", - ES_AUTOVSCROLL | ES_LEFT | ES_MULTILINE | ES_NOHIDESEL | ES_WANTRETURN | WS_TABSTOP | WS_VSCROLL, + ES_AUTOVSCROLL | ES_LEFT | ES_MULTILINE | ES_NOHIDESEL | ES_WANTRETURN | WS_TABSTOP | WS_VSCROLL | style, hInstance, NULL, TRUE); @@ -143,3 +143,13 @@ uiMultilineEntry *uiNewMultilineEntry(void) return e; } + +uiMultilineEntry *uiNewMultilineEntry(void) +{ + return finishMultilineEntry(0); +} + +uiMultilineEntry *uiNewNonWrappingMultilineEntry(void) +{ + return finishMultilineEntry(WS_HSCROLL | ES_AUTOHSCROLL); +} From 07cd03452dd576d7943801102a0079e319f1d8b9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 22 May 2016 14:20:54 -0400 Subject: [PATCH 0076/1329] Implemented non-wrapping multiline entries in GTK+ and started implementing them in OS X. --- darwin/multilineentry.m | 14 ++++++++++++-- unix/multilineentry.c | 16 +++++++++++++--- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/darwin/multilineentry.m b/darwin/multilineentry.m index 23e3d5c4..7b248385 100644 --- a/darwin/multilineentry.m +++ b/darwin/multilineentry.m @@ -101,7 +101,7 @@ void uiMultilineEntrySetReadOnly(uiMultilineEntry *e, int readonly) [e->tv setEditable:editable]; } -uiMultilineEntry *uiNewMultilineEntry(void) +static uiMultilineEntry *finishMultilineEntry(BOOL hscroll) { uiMultilineEntry *e; NSFont *font; @@ -110,7 +110,7 @@ uiMultilineEntry *uiNewMultilineEntry(void) e->sv = [[NSScrollView alloc] initWithFrame:NSZeroRect]; // TODO verify against Interface Builder - [e->sv setHasHorizontalScroller:NO]; + [e->sv setHasHorizontalScroller:hscroll]; [e->sv setHasVerticalScroller:YES]; [e->sv setAutohidesScrollers:YES]; [e->sv setBorderType:NSBezelBorder]; @@ -173,6 +173,16 @@ uiMultilineEntry *uiNewMultilineEntry(void) return e; } +uiMultilineEntry *uiNewMultilineEntry(void) +{ + return finishMultilineEntry(NO); +} + +uiMultilineEntry *uiNewNonWrappingMultilineEntry(void) +{ + return finishMultilineEntry(YES); +} + // TODO #if 0 diff --git a/unix/multilineentry.c b/unix/multilineentry.c index bcd56a74..169d129f 100644 --- a/unix/multilineentry.c +++ b/unix/multilineentry.c @@ -81,7 +81,7 @@ void uiMultilineEntrySetReadOnly(uiMultilineEntry *e, int readonly) gtk_text_view_set_editable(e->textview, editable); } -uiMultilineEntry *uiNewMultilineEntry(void) +static uiMultilineEntry *finishMultilineEntry(GtkPolicyType hpolicy, GtkWrapMode wrapMode) { uiMultilineEntry *e; @@ -91,13 +91,13 @@ uiMultilineEntry *uiNewMultilineEntry(void) e->scontainer = GTK_CONTAINER(e->widget); e->sw = GTK_SCROLLED_WINDOW(e->widget); gtk_scrolled_window_set_policy(e->sw, - GTK_POLICY_NEVER, + hpolicy, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type(e->sw, GTK_SHADOW_IN); e->textviewWidget = gtk_text_view_new(); e->textview = GTK_TEXT_VIEW(e->textviewWidget); - gtk_text_view_set_wrap_mode(e->textview, GTK_WRAP_WORD); + gtk_text_view_set_wrap_mode(e->textview, wrapMode); gtk_container_add(e->scontainer, e->textviewWidget); // and make the text view visible; only the scrolled window's visibility is controlled by libui @@ -110,3 +110,13 @@ uiMultilineEntry *uiNewMultilineEntry(void) return e; } + +uiMultilineEntry *uiNewMultilineEntry(void) +{ + return finishMultilineEntry(GTK_POLICY_NEVER, GTK_WRAP_WORD); +} + +uiMultilineEntry *uiNewNonWrappingMultilineEntry(void) +{ + return finishMultilineEntry(GTK_POLICY_AUTOMATIC, GTK_WRAP_NONE); +} From 6c6843dac6383753ebd2e739be8223314e9006d7 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 22 May 2016 14:37:02 -0400 Subject: [PATCH 0077/1329] Fixed non-wrapping uiMultlineEntries. --- README.md | 1 + darwin/autolayout.m | 39 ++++++++++++++++++++------------------- darwin/multilineentry.m | 10 +++++++++- darwin/uipriv_darwin.h | 2 +- 4 files changed, 31 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 0f602090..9fe5da4a 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ This README is being written.
** Removed `uiControlVerifyDestroy()`; that is now part of `uiFreeControl()` itself. ** Added `uiPi`, a constant for π. This is provided for C and C++ programmers, where there is no standard named constant for π; bindings authors shouldn't need to worry about this. ** Fixed uiMultilineEntry not properly having line breaks on Windows. +** Added `uiNewNonWrappingMultilineEntry()`, which creates a uiMultilineEntry that scrolls horizontally instead of wrapping lines. (This is not documented as being changeable after the fact on Windows, hence it's a creation-time choice.) ## Runtime Requirements diff --git a/darwin/autolayout.m b/darwin/autolayout.m index 19bb26ad..81530e75 100644 --- a/darwin/autolayout.m +++ b/darwin/autolayout.m @@ -150,9 +150,8 @@ void singleChildConstraintsSetMargined(struct singleChildConstraints *c, int mar [c->bottomConstraintEqual setConstant:margin]; } -// from http://blog.bjhomer.com/2014/08/nsscrollview-and-autolayout.html because (as pointed out there) Apple's official guide is really only for iOS -// TODO this doesn't quite work with NSTextView; it *mostly* works -void scrollViewConstraintsEstablish(struct scrollViewConstraints *c, NSScrollView *sv, NSString *desc) +// based on http://blog.bjhomer.com/2014/08/nsscrollview-and-autolayout.html because (as pointed out there) Apple's official guide is really only for iOS +void scrollViewConstraintsEstablish(struct scrollViewConstraints *c, NSScrollView *sv, BOOL hscroll, BOOL vscroll, NSString *desc) { NSView *cv, *dv; @@ -176,23 +175,25 @@ void scrollViewConstraintsEstablish(struct scrollViewConstraints *c, NSScrollVie [sv addConstraint:c->documentTop]; [c->documentTop retain]; - c->documentTrailing = mkConstraint(dv, NSLayoutAttributeTrailing, - NSLayoutRelationEqual, - cv, NSLayoutAttributeTrailing, - 1, 0, - [desc stringByAppendingString:@"document trailing constraint"]); - [sv addConstraint:c->documentTrailing]; - [c->documentTrailing retain]; + if (!hscroll) { + c->documentTrailing = mkConstraint(dv, NSLayoutAttributeTrailing, + NSLayoutRelationEqual, + cv, NSLayoutAttributeTrailing, + 1, 0, + [desc stringByAppendingString:@"document trailing constraint"]); + [sv addConstraint:c->documentTrailing]; + [c->documentTrailing retain]; + } -#if 0 - c->documentBottom = mkConstraint(dv, NSLayoutAttributeBottom, - NSLayoutRelationEqual, - sv, NSLayoutAttributeBottom, - 1, 0, - [desc stringByAppendingString:@"document bottom constraint"]); - [sv addConstraint:c->documentBottom]; - [c->documentBottom retain]; -#endif + if (!vscroll) { + c->documentBottom = mkConstraint(dv, NSLayoutAttributeBottom, + NSLayoutRelationEqual, + sv, NSLayoutAttributeBottom, + 1, 0, + [desc stringByAppendingString:@"document bottom constraint"]); + [sv addConstraint:c->documentBottom]; + [c->documentBottom retain]; + } } void scrollViewConstraintsRemove(struct scrollViewConstraints *c, NSScrollView *sv) diff --git a/darwin/multilineentry.m b/darwin/multilineentry.m index 7b248385..34611071 100644 --- a/darwin/multilineentry.m +++ b/darwin/multilineentry.m @@ -1,6 +1,8 @@ // 8 december 2015 #import "uipriv_darwin.h" +// TODO you actually have to click on parts with a line in them in order to start editing; clicking below the last line doesn't give focus + // NSTextView has no intrinsic content size by default, which wreaks havoc on a pure-Auto Layout system // we'll have to take over to get it to work // see also http://stackoverflow.com/questions/24210153/nstextview-not-properly-resizing-with-auto-layout and http://stackoverflow.com/questions/11237622/using-autolayout-with-expanding-nstextviews @@ -149,6 +151,12 @@ static uiMultilineEntry *finishMultilineEntry(BOOL hscroll) disableAutocorrect(e->tv); // this option is complex; just set it to the Interface Builder default [[e->tv layoutManager] setAllowsNonContiguousLayout:YES]; + if (hscroll) { + // TODO this is a giant mess + [e->tv setHorizontallyResizable:YES]; + [[e->tv textContainer] setWidthTracksTextView:NO]; + [[e->tv textContainer] setContainerSize:NSMakeSize(CGFLOAT_MAX, CGFLOAT_MAX)]; + } // don't use uiDarwinSetControlFont() directly; we have to do a little extra work to set the font font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]]; [e->tv setTypingAttributes:[NSDictionary @@ -160,7 +168,7 @@ static uiMultilineEntry *finishMultilineEntry(BOOL hscroll) [e->sv setDocumentView:e->tv]; [e->tv setTranslatesAutoresizingMaskIntoConstraints:NO]; - scrollViewConstraintsEstablish(&(e->constraints), e->sv, @"uiMultilineEntry"); + scrollViewConstraintsEstablish(&(e->constraints), e->sv, hscroll, YES, @"uiMultilineEntry"); // needed to allow horizontal shrinking // TODO what about vertical text? [e->tv setContentCompressionResistancePriority:NSLayoutPriorityDefaultLow forOrientation:NSLayoutConstraintOrientationHorizontal]; diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 2afded8f..4a8b1bdd 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -80,7 +80,7 @@ struct scrollViewConstraints { NSLayoutConstraint *documentWidth; NSLayoutConstraint *documentHeight; }; -extern void scrollViewConstraintsEstablish(struct scrollViewConstraints *c, NSScrollView *sv, NSString *desc); +extern void scrollViewConstraintsEstablish(struct scrollViewConstraints *c, NSScrollView *sv, BOOL hscroll, BOOL vscroll, NSString *desc); extern void scrollViewConstraintsRemove(struct scrollViewConstraints *c, NSScrollView *sv); // map.m From d9f133d81b0e1e83cac62e8fa31c2feaad8f57e6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 22 May 2016 14:39:14 -0400 Subject: [PATCH 0078/1329] Fixed the README. --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 9fe5da4a..44012e0f 100644 --- a/README.md +++ b/README.md @@ -12,13 +12,13 @@ This README is being written.
## Updates -> Note that today's entry may be updated later today. +*Note that today's entry may be updated later today.* * **22 May 2016** -** Removed `uiControlVerifyDestroy()`; that is now part of `uiFreeControl()` itself. -** Added `uiPi`, a constant for π. This is provided for C and C++ programmers, where there is no standard named constant for π; bindings authors shouldn't need to worry about this. -** Fixed uiMultilineEntry not properly having line breaks on Windows. -** Added `uiNewNonWrappingMultilineEntry()`, which creates a uiMultilineEntry that scrolls horizontally instead of wrapping lines. (This is not documented as being changeable after the fact on Windows, hence it's a creation-time choice.) + * Removed `uiControlVerifyDestroy()`; that is now part of `uiFreeControl()` itself. + * Added `uiPi`, a constant for π. This is provided for C and C++ programmers, where there is no standard named constant for π; bindings authors shouldn't need to worry about this. + * Fixed uiMultilineEntry not properly having line breaks on Windows. + * Added `uiNewNonWrappingMultilineEntry()`, which creates a uiMultilineEntry that scrolls horizontally instead of wrapping lines. (This is not documented as being changeable after the fact on Windows, hence it's a creation-time choice.) ## Runtime Requirements From bbae4478f681901d4137569906fc91ff383f22d2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 22 May 2016 14:41:42 -0400 Subject: [PATCH 0079/1329] More TODOs. --- test/page12.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/page12.c b/test/page12.c index 89203632..dc06e279 100644 --- a/test/page12.c +++ b/test/page12.c @@ -1,6 +1,8 @@ // 22 may 2016 #include "test.h" +// TODO add buttons for event testing and Append scroll/selection changing. + uiBox *makePage12(void) { uiBox *page12; From 44cdc82fc7e7f40268672b41ba808b9c4ff33ac9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 22 May 2016 14:55:12 -0400 Subject: [PATCH 0080/1329] Adjusted the intrinsic size of NSColorWell. --- darwin/colorbutton.m | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/darwin/colorbutton.m b/darwin/colorbutton.m index 7483fda1..26100a0d 100644 --- a/darwin/colorbutton.m +++ b/darwin/colorbutton.m @@ -102,6 +102,23 @@ struct uiColorButton { [self setColor:[NSColor colorWithSRGBRed:r green:g blue:b alpha:a]]; } +// NSColorWell has no intrinsic size by default; give it at least the height of the equivalent button's +- (NSSize)intrinsicContentSize +{ + NSSize ss; + NSButtonCell *bc; + + ss = [super intrinsicContentSize]; + bc = [NSButtonCell new]; + [bc setButtonType:NSPushOnPushOffButton]; + [bc setBordered:YES]; + [bc setBezelStyle:NSShadowlessSquareBezelStyle]; + [bc setTitle:@" "]; + ss.height = [bc cellSize].height; + [bc release]; + return ss; +} + @end uiDarwinControlAllDefaults(uiColorButton, button) From 531f8ea19c6fd1462927073fc4574a4ed35a897c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 22 May 2016 15:59:23 -0400 Subject: [PATCH 0081/1329] Finally wrote makeDCRenderTarget() for the Windows backend. Now to use it. --- windows/draw.cpp | 26 +++++++++++++++++++++++++- windows/uipriv_windows.hpp | 3 +++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/windows/draw.cpp b/windows/draw.cpp index dec6c18f..5f4d29f1 100644 --- a/windows/draw.cpp +++ b/windows/draw.cpp @@ -63,7 +63,31 @@ ID2D1HwndRenderTarget *makeHWNDRenderTarget(HWND hwnd) &hprops, &rt); if (hr != S_OK) - logHRESULT(L"error creating area HWND render target", hr); + logHRESULT(L"error creating HWND render target", hr); + return rt; +} + +ID2D1DCRenderTarget *makeHDCRenderTarget(HDC dc, RECT *r) +{ + D2D1_RENDER_TARGET_PROPERTIES props; + ID2D1DCRenderTarget *rt; + HRESULT hr; + + ZeroMemory(&props, sizeof (D2D1_RENDER_TARGET_PROPERTIES)); + props.type = D2D1_RENDER_TARGET_TYPE_DEFAULT; + props.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM; + props.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED; + props.dpiX = GetDeviceCaps(dc, LOGPIXELSX); + props.dpiY = GetDeviceCaps(dc, LOGPIXELSY); + props.usage = D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE; + props.minLevel = D2D1_FEATURE_LEVEL_DEFAULT; + + hr = d2dfactory->CreateDCRenderTarget(&props, &rt); + if (hr != S_OK) + logHRESULT(L"error creating DC render target", hr); + hr = rt->BindDC(dc, r); + if (hr != S_OK) + logHRESULT(L"error binding DC to DC render target", hr); return rt; } diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index 7cd2be62..f08ae042 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -145,3 +145,6 @@ extern void getSizing(HWND hwnd, uiWindowsSizing *sizing, HFONT font); // TODO #include "_uipriv_migrate.hpp" + +// draw.cpp +extern ID2D1DCRenderTarget *makeHDCRenderTarget(HDC dc, RECT *r); From 585872839dee9248a3a250eb165a414224ab02e1 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 22 May 2016 16:07:31 -0400 Subject: [PATCH 0082/1329] Made uiColorButton on Windows draw with actual alpha values. --- windows/colorbutton.cpp | 46 ++++++++++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/windows/colorbutton.cpp b/windows/colorbutton.cpp index b6c712c4..c4c91701 100644 --- a/windows/colorbutton.cpp +++ b/windows/colorbutton.cpp @@ -53,9 +53,15 @@ static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult) { uiColorButton *b = uiColorButton(c); NMCUSTOMDRAW *nm = (NMCUSTOMDRAW *) nmhdr; - RECT r; + RECT client; + ID2D1DCRenderTarget *rt; + D2D1_RECT_F r; + D2D1_COLOR_F color; + D2D1_BRUSH_PROPERTIES bprop; + ID2D1SolidColorBrush *brush; uiWindowsSizing sizing; int x, y; + HRESULT hr; if (nmhdr->code != NM_CUSTOMDRAW) return FALSE; @@ -63,21 +69,37 @@ static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult) if (nm->dwDrawStage != CDDS_PREPAINT) return FALSE; - // TODO use Direct2D? either way, draw alpha - uiWindowsEnsureGetClientRect(b->hwnd, &r); + uiWindowsEnsureGetClientRect(b->hwnd, &client); + rt = makeHDCRenderTarget(nm->hdc, &client); + rt->BeginDraw(); + uiWindowsGetSizing(b->hwnd, &sizing); x = 3; // should be enough y = 3; uiWindowsSizingDlgUnitsToPixels(&sizing, &x, &y); - r.left += x; - r.top += y; - r.right -= x; - r.bottom -= y; - // TODO error check -#define comp(x) ((BYTE) (x * 255)) - // TODO free brush - FillRect(nm->hdc, &r, CreateSolidBrush(RGB(comp(b->r), comp(b->g), comp(b->b)))); -#undef comp + r.left = client.left + x; + r.top = client.top + y; + r.right = client.right - x; + r.bottom = client.bottom - y; + + color.r = b->r; + color.g = b->g; + color.b = b->b; + color.a = b->a; + ZeroMemory(&bprop, sizeof (D2D1_BRUSH_PROPERTIES)); + bprop.opacity = 1.0; + bprop.transform._11 = 1; + bprop.transform._22 = 1; + hr = rt->CreateSolidColorBrush(&color, &bprop, &brush); + if (hr != S_OK) + logHRESULT(L"error creating brush for color button", hr); + rt->FillRectangle(&r, brush); + brush->Release(); + + hr = rt->EndDraw(NULL, NULL); + if (hr != S_OK) + logHRESULT(L"error drawing color on color button", hr); + rt->Release(); // skip default processing (don't draw text) *lResult = CDRF_SKIPDEFAULT; From 878778c683e966dab377c6adc677d6153016fdf3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 22 May 2016 16:16:20 -0400 Subject: [PATCH 0083/1329] Implemented WM_PRINTCLIENT for uiArea and the Direct2D scratch windows. --- README.md | 9 +++++---- windows/areadraw.cpp | 14 ++++++++++---- windows/d2dscratch.cpp | 11 +++++++++-- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 44012e0f..c7cf33f0 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,10 @@ This README is being written.
## Announcements -**21 May 2016** -* I will now post announcements and updates here. -* Now that Ubuntu 16.04 LTS is here, no earlier than next Saturday, 28 May 2016 at noon EDT, **I will bump the minimum GTK+ version from 3.4 to 3.10**. This will add a lot of new features that I can now add to libui, such as search-oriented uiEntries, lists of arbitrary control layouts, and more. If you are still running a Linux distribution that doesn't come with 3.10, you will either need to upgrade or use jhbuild to set up a newer version of GTK+ in a private environment. -* You can decide if I should also drop OS X 10.7 [here](https://github.com/andlabs/libui/issues/46). +* **21 May 2016** + * I will now post announcements and updates here. + * Now that Ubuntu 16.04 LTS is here, no earlier than next Saturday, 28 May 2016 at noon EDT, **I will bump the minimum GTK+ version from 3.4 to 3.10**. This will add a lot of new features that I can now add to libui, such as search-oriented uiEntries, lists of arbitrary control layouts, and more. If you are still running a Linux distribution that doesn't come with 3.10, you will either need to upgrade or use jhbuild to set up a newer version of GTK+ in a private environment. + * You can decide if I should also drop OS X 10.7 [here](https://github.com/andlabs/libui/issues/46). ## Updates @@ -19,6 +19,7 @@ This README is being written.
* Added `uiPi`, a constant for π. This is provided for C and C++ programmers, where there is no standard named constant for π; bindings authors shouldn't need to worry about this. * Fixed uiMultilineEntry not properly having line breaks on Windows. * Added `uiNewNonWrappingMultilineEntry()`, which creates a uiMultilineEntry that scrolls horizontally instead of wrapping lines. (This is not documented as being changeable after the fact on Windows, hence it's a creation-time choice.) + * uiArea and some internal Direct2D windows now respond to `WM_PRINTCLIENT` properly, which should hopefully increase the quality of screenshots. ## Runtime Requirements diff --git a/windows/areadraw.cpp b/windows/areadraw.cpp index 17b64705..7b3dc696 100644 --- a/windows/areadraw.cpp +++ b/windows/areadraw.cpp @@ -72,7 +72,7 @@ static void onWM_PAINT(uiArea *a) clip.right = 0; clip.bottom = 0; } - hr = doPaint(a, (ID2D1RenderTarget *) (a->rt), &clip); + hr = doPaint(a, a->rt, &clip); switch (hr) { case S_OK: if (ValidateRect(a->hwnd, NULL) == 0) @@ -91,12 +91,18 @@ static void onWM_PAINT(uiArea *a) } } -static void onWM_PRINTCLIENT(uiArea *a) +static void onWM_PRINTCLIENT(uiArea *a, HDC dc) { + ID2D1DCRenderTarget *rt; RECT client; + HRESULT hr; uiWindowsEnsureGetClientRect(a->hwnd, &client); -//TODO doPaint(a, (HDC) wParam, &client); + rt = makeHDCRenderTarget(dc, &client); + hr = doPaint(a, rt, &client); + if (hr != S_OK) + logHRESULT(L"error printing uiArea client area", hr); + rt->Release(); } BOOL areaDoDraw(uiArea *a, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *lResult) @@ -107,7 +113,7 @@ BOOL areaDoDraw(uiArea *a, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *lRe *lResult = 0; return TRUE; case WM_PRINTCLIENT: - onWM_PRINTCLIENT(a); + onWM_PRINTCLIENT(a, (HDC) wParam); *lResult = 0; return TRUE; } diff --git a/windows/d2dscratch.cpp b/windows/d2dscratch.cpp index 146eea8b..63218adc 100644 --- a/windows/d2dscratch.cpp +++ b/windows/d2dscratch.cpp @@ -65,6 +65,8 @@ static LRESULT CALLBACK d2dScratchWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, L { LONG_PTR init; ID2D1HwndRenderTarget *rt; + ID2D1DCRenderTarget *dcrt; + RECT client; HRESULT hr; init = GetWindowLongPtrW(hwnd, 0); @@ -105,8 +107,13 @@ static LRESULT CALLBACK d2dScratchWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, L } return 0; case WM_PRINTCLIENT: - // TODO - break; + uiWindowsEnsureGetClientRect(hwnd, &client); + dcrt = makeHDCRenderTarget((HDC) wParam, &client); + hr = d2dScratchDoPaint(hwnd, dcrt); + if (hr != S_OK) + logHRESULT(L"error printing D2D scratch window client area", hr); + dcrt->Release(); + return 0; case WM_LBUTTONDOWN: d2dScratchDoLButtonDown(hwnd, rt, lParam); return 0; From ea37e13c676ec1d714cb8335cdac1035e88b16c0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 22 May 2016 16:19:19 -0400 Subject: [PATCH 0084/1329] Quick README clarification. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c7cf33f0..e4534997 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ This README is being written.
* Added `uiPi`, a constant for π. This is provided for C and C++ programmers, where there is no standard named constant for π; bindings authors shouldn't need to worry about this. * Fixed uiMultilineEntry not properly having line breaks on Windows. * Added `uiNewNonWrappingMultilineEntry()`, which creates a uiMultilineEntry that scrolls horizontally instead of wrapping lines. (This is not documented as being changeable after the fact on Windows, hence it's a creation-time choice.) - * uiArea and some internal Direct2D windows now respond to `WM_PRINTCLIENT` properly, which should hopefully increase the quality of screenshots. + * uiAreas on Windows and some internal Direct2D areas now respond to `WM_PRINTCLIENT` properly, which should hopefully increase the quality of screenshots. ## Runtime Requirements From 6d421e9349e6f50cdd06a02d341af87f05d8f4f8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 22 May 2016 17:01:18 -0400 Subject: [PATCH 0085/1329] Implemented a test of non-BMP characters that need surrogate pairs on UTF-16-based systems on test page 10. --- test/page10.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/test/page10.c b/test/page10.c index 0262c815..dc312ab8 100644 --- a/test/page10.c +++ b/test/page10.c @@ -1,9 +1,6 @@ // 22 december 2015 #include "test.h" -// TODO figure out how the various backends handle non-BMP characters/surrogate pairs -// use: F0 90 8C 88 (surrogates D800 DF08) - static uiEntry *textString; static uiFontButton *textFontButton; static uiColorButton *textColorButton; @@ -29,6 +26,8 @@ static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *dp) uiDrawTextFont *font; uiDrawTextLayout *layout; double r, g, b, al; + char surrogates[1 + 4 + 1 + 1]; + double width, height; font = uiFontButtonFont(textFontButton); @@ -44,6 +43,22 @@ static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *dp) 14, 18, r, g, b, al); uiDrawText(dp->Context, 10, 10, layout); + uiDrawTextLayoutExtents(layout, &width, &height); + uiDrawFreeTextLayout(layout); + + surrogates[0] = 'x'; + surrogates[1] = 0xF0; // surrogates D800 DF08 + surrogates[2] = 0x90; + surrogates[3] = 0x8C; + surrogates[4] = 0x88; + surrogates[5] = 'y'; + surrogates[6] = '\0'; + + layout = uiDrawNewTextLayout(surrogates, font, -1); + uiDrawTextLayoutSetColor(layout, + 1, 2, + 1, 0, 0.5, 0.5); + uiDrawText(dp->Context, 10, 10 + height, layout); uiDrawFreeTextLayout(layout); uiDrawFreeTextFont(font); From b73a96ad27c0657ef71e439ce90ff0000b662745 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 22 May 2016 17:49:41 -0400 Subject: [PATCH 0086/1329] More TODOs. --- test/page10.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/page10.c b/test/page10.c index dc312ab8..ba8399da 100644 --- a/test/page10.c +++ b/test/page10.c @@ -1,6 +1,8 @@ // 22 december 2015 #include "test.h" +// TODO test composed diacritic marks + static uiEntry *textString; static uiFontButton *textFontButton; static uiColorButton *textColorButton; From a641a7eda81e8e9c99c7eb841b2fd758c24e4800 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 22 May 2016 18:37:53 -0400 Subject: [PATCH 0087/1329] Some TODO cleanup. Started changing long-term TODOs to say LONGTERM instead. --- test/drawtests.c | 2 -- windows/debug.cpp | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/test/drawtests.c b/test/drawtests.c index d821d495..09ecac45 100644 --- a/test/drawtests.c +++ b/test/drawtests.c @@ -1932,8 +1932,6 @@ static void drawQ2DCreateWindowGC(uiAreaDrawParams *p) // TODO Text page, if any? -// TODO Paths page - static const struct drawtest tests[] = { { "Original uiArea test", drawOriginal }, { "Arc test", drawArcs }, diff --git a/windows/debug.cpp b/windows/debug.cpp index 6671717e..cfafffdc 100644 --- a/windows/debug.cpp +++ b/windows/debug.cpp @@ -1,9 +1,8 @@ // 25 february 2015 #include "uipriv_windows.hpp" -// TODO disable logging and stopping on no-debug builds +// LONGTERM disable logging and stopping on no-debug builds -// TODO are the newlines needed? static void printDebug(const WCHAR *msg) { OutputDebugStringW(msg); From f0011d6227e16bc3f1d41877e2e99d00f38e4bb9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 22 May 2016 19:17:42 -0400 Subject: [PATCH 0088/1329] Started resolving TODOs in the GTK+ backend, marking some LONGTERM. In particular, uiDateTimePicker no longer will be localized, as there doesn't seem to be a way to get that info out. --- TODO.md | 1 + darwin/debug.m | 2 +- unix/datetimepicker.c | 21 +++++++++++++++------ unix/debug.c | 2 +- unix/multilineentry.c | 2 -- 5 files changed, 18 insertions(+), 10 deletions(-) diff --git a/TODO.md b/TODO.md index 9b599845..0df9f24a 100644 --- a/TODO.md +++ b/TODO.md @@ -81,6 +81,7 @@ notes to self +don't forget LONGTERMs as well notes - http://blogs.msdn.com/b/oldnewthing/archive/2004/03/29/101121.aspx on accelerators diff --git a/darwin/debug.m b/darwin/debug.m index de6d9406..c91c6a73 100644 --- a/darwin/debug.m +++ b/darwin/debug.m @@ -1,7 +1,7 @@ // 13 may 2016 #import "uipriv_darwin.h" -// TODO don't halt on release builds +// LONGTERM don't halt on release builds void realbug(const char *file, const char *line, const char *func, const char *prefix, const char *format, va_list ap) { diff --git a/unix/datetimepicker.c b/unix/datetimepicker.c index 2093e0e7..c48d09d7 100644 --- a/unix/datetimepicker.c +++ b/unix/datetimepicker.c @@ -1,9 +1,7 @@ // 4 september 2015 #include "uipriv_unix.h" -// TODO imitate gnome-calendar's day/month/year entries? -// TODO 24-hour time -// TODO don't assume all locales use : +// LONGTERM imitate gnome-calendar's day/month/year entries above the calendar #define dateTimePickerWidgetType (dateTimePickerWidget_get_type()) #define dateTimePickerWidget(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), dateTimePickerWidgetType, dateTimePickerWidget)) @@ -151,8 +149,19 @@ static gboolean startGrab(dateTimePickerWidget *d) GdkDevice *keyboard, *mouse; dev = gtk_get_current_event_device(); - if (dev == NULL) - return FALSE; // TODO + if (dev == NULL) { + // this is what GtkComboBox does + // since no device was set, just use the first available "master device" + GdkDisplay *disp; + GdkDeviceManager *dm; + GList *list; + + disp = gtk_widget_get_display(GTK_WIDGET(d)); + dm = gdk_display_get_device_manager(disp); + list = gdk_device_manager_list_devices(dm, GDK_DEVICE_TYPE_MASTER); + dev = (GdkDevice *) (list->data); + g_list_free(list); + } time = gtk_get_current_event_time(); keyboard = dev; @@ -441,7 +450,7 @@ static void dateTimePickerWidget_init(dateTimePickerWidget *d) d->seconds = newSpinbox(d, 0, 59, NULL, zeroPadSpinbox, &(d->secondsBlock)); gtk_container_add(GTK_CONTAINER(d->timebox), d->seconds); - // TODO this should be the case, but that interferes with grabs + // LONGTERM this should be the case, but that interferes with grabs // switch to it when we can drop GTK+ 3.10 and use popovers #if 0 d->ampm = gtk_combo_box_text_new(); diff --git a/unix/debug.c b/unix/debug.c index 20a4e2d0..c948db62 100644 --- a/unix/debug.c +++ b/unix/debug.c @@ -1,7 +1,7 @@ // 13 may 2016 #include "uipriv_unix.h" -// TODO don't halt on release builds +// LONGTERM don't halt on release builds void realbug(const char *file, const char *line, const char *func, const char *prefix, const char *format, va_list ap) { diff --git a/unix/multilineentry.c b/unix/multilineentry.c index 169d129f..92f0e27e 100644 --- a/unix/multilineentry.c +++ b/unix/multilineentry.c @@ -1,8 +1,6 @@ // 6 december 2015 #include "uipriv_unix.h" -// TODO: ensure this can only be used to enter plain text - struct uiMultilineEntry { uiUnixControl c; GtkWidget *widget; From b957558ef45012be3ccb6b2fbe24a580188009cf Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 22 May 2016 19:40:56 -0400 Subject: [PATCH 0089/1329] Even more TODO resolution in uiDateTimePicker on GTK+. --- README.md | 1 + unix/datetimepicker.c | 37 ++++++++++++++++++++++++++++++------- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index e4534997..c1ccf600 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ This README is being written.
* Fixed uiMultilineEntry not properly having line breaks on Windows. * Added `uiNewNonWrappingMultilineEntry()`, which creates a uiMultilineEntry that scrolls horizontally instead of wrapping lines. (This is not documented as being changeable after the fact on Windows, hence it's a creation-time choice.) * uiAreas on Windows and some internal Direct2D areas now respond to `WM_PRINTCLIENT` properly, which should hopefully increase the quality of screenshots. + * uiDateTimePicker on GTK+ works properly on RTL layouts and no longer disappears off the bottom of the screen if not enough room is available. It will also no longer be marked for localization of the time format (what the separator should be and whether to use 24-hour time), as that information is not provided by the locale system. :( ## Runtime Requirements diff --git a/unix/datetimepicker.c b/unix/datetimepicker.c index c48d09d7..19689a22 100644 --- a/unix/datetimepicker.c +++ b/unix/datetimepicker.c @@ -2,6 +2,7 @@ #include "uipriv_unix.h" // LONGTERM imitate gnome-calendar's day/month/year entries above the calendar +// LONGTERM allow entering a 24-hour hour in the hour spinbutton and adjust accordingly #define dateTimePickerWidgetType (dateTimePickerWidget_get_type()) #define dateTimePickerWidget(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), dateTimePickerWidgetType, dateTimePickerWidget)) @@ -200,8 +201,13 @@ static void allocationToScreen(dateTimePickerWidget *d, gint *x, gint *y) { GdkWindow *window; GtkAllocation a; + GtkRequisition aWin; + GdkScreen *screen; + GdkRectangle workarea; + int otherY; gtk_widget_get_allocation(GTK_WIDGET(d), &a); + gtk_widget_get_preferred_size(d->window, &aWin, NULL); *x = 0; *y = 0; if (!gtk_widget_get_has_window(GTK_WIDGET(d))) { @@ -211,9 +217,24 @@ static void allocationToScreen(dateTimePickerWidget *d, gint *x, gint *y) window = gtk_widget_get_window(GTK_WIDGET(d)); gdk_window_get_root_coords(window, *x, *y, x, y); if (gtk_widget_get_direction(GTK_WIDGET(d)) == GTK_TEXT_DIR_RTL) - *x += a.width; // TODO subtract target width - // TODO monitor detection + *x += a.width - aWin.width; + + // now adjust to prevent the box from going offscreen + screen = gtk_widget_get_screen(GTK_WIDGET(d)); + gdk_screen_get_monitor_workarea(screen, + gdk_screen_get_monitor_at_window(screen, window), + &workarea); + if (*x < workarea.x) // too far to the left? + *x = workarea.x; + else if (*x + aWin.width > (workarea.x + workarea.width)) // too far to the right? + *x = (workarea.x + workarea.width) - aWin.width; + // this isn't the same algorithm used by GtkComboBox + // first, get our two choices; *y for down and otherY for up + otherY = *y - aWin.height; *y += a.height; + // and use otherY if we're too low + if (*y + aWin.height >= workarea.y + workarea.height) + *y = otherY; } static void showPopup(dateTimePickerWidget *d) @@ -333,7 +354,7 @@ static gint ampmSpinboxInput(GtkSpinButton *sb, gpointer ptr, gpointer data) char firstAM, firstPM; text = gtk_entry_get_text(GTK_ENTRY(sb)); - // TODO don't use ASCII here for case insensitivity + // LONGTERM don't use ASCII here for case insensitivity firstAM = g_ascii_tolower(nl_langinfo(AM_STR)[0]); firstPM = g_ascii_tolower(nl_langinfo(PM_STR)[0]); for (; *text != '\0'; text++) @@ -411,14 +432,11 @@ static void dateTimePickerWidget_init(dateTimePickerWidget *d) d->window = gtk_window_new(GTK_WINDOW_POPUP); gtk_window_set_resizable(GTK_WINDOW(d->window), FALSE); gtk_window_set_attached_to(GTK_WINDOW(d->window), GTK_WIDGET(d)); - // TODO set_keep_above()? gtk_window_set_decorated(GTK_WINDOW(d->window), FALSE); gtk_window_set_deletable(GTK_WINDOW(d->window), FALSE); gtk_window_set_type_hint(GTK_WINDOW(d->window), GDK_WINDOW_TYPE_HINT_COMBO); gtk_window_set_skip_taskbar_hint(GTK_WINDOW(d->window), TRUE); gtk_window_set_skip_pager_hint(GTK_WINDOW(d->window), TRUE); - // TODO accept_focus()? - // TODO focus_on_map()? gtk_window_set_has_resize_grip(GTK_WINDOW(d->window), FALSE); gtk_container_set_border_width(GTK_CONTAINER(d->window), 12); // and make it stand out a bit @@ -496,7 +514,12 @@ static void dateTimePickerWidget_init(dateTimePickerWidget *d) static void dateTimePickerWidget_dispose(GObject *obj) { - // TODO destroy window + dateTimePickerWidget *d = dateTimePickerWidget(obj); + + if (d->window != NULL) { + gtk_widget_destroy(d->window); + d->window = NULL; + } G_OBJECT_CLASS(dateTimePickerWidget_parent_class)->dispose(obj); } From ab0a9102b4e810f772208ad72c0cc69f61017f3c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 22 May 2016 20:02:47 -0400 Subject: [PATCH 0090/1329] Added a user bug for calling SetParent() on a uiWindow. --- README.md | 1 + common/GNUfiles.mk | 3 ++- common/userbugs.c | 8 ++++++++ darwin/window.m | 14 ++++++++++---- ui.h | 3 ++- unix/window.c | 14 ++++++++++---- windows/window.cpp | 14 ++++++++++---- 7 files changed, 43 insertions(+), 14 deletions(-) create mode 100644 common/userbugs.c diff --git a/README.md b/README.md index c1ccf600..5c7dc679 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ This README is being written.
* Added `uiNewNonWrappingMultilineEntry()`, which creates a uiMultilineEntry that scrolls horizontally instead of wrapping lines. (This is not documented as being changeable after the fact on Windows, hence it's a creation-time choice.) * uiAreas on Windows and some internal Direct2D areas now respond to `WM_PRINTCLIENT` properly, which should hopefully increase the quality of screenshots. * uiDateTimePicker on GTK+ works properly on RTL layouts and no longer disappears off the bottom of the screen if not enough room is available. It will also no longer be marked for localization of the time format (what the separator should be and whether to use 24-hour time), as that information is not provided by the locale system. :( + * Added `uiUserBugCannotSetParentOnToplevel()`, which should be used by implementations of toplevel controls in their `SetParent()` implementations. This will also be the beginning of consolidating common user bug messages into a single place, though this will be one of the only few exported user bug functions. ## Runtime Requirements diff --git a/common/GNUfiles.mk b/common/GNUfiles.mk index 58f901c6..465c76e8 100644 --- a/common/GNUfiles.mk +++ b/common/GNUfiles.mk @@ -5,7 +5,8 @@ CFILES += \ common/control.c \ common/debug.c \ common/matrix.c \ - common/shouldquit.c + common/shouldquit.c \ + common/userbugs.c HFILES += \ common/controlsigs.h \ diff --git a/common/userbugs.c b/common/userbugs.c new file mode 100644 index 00000000..0a85874c --- /dev/null +++ b/common/userbugs.c @@ -0,0 +1,8 @@ +// 22 may 2016 +#include "../ui.h" +#include "uipriv.h" + +void uiUserBugCannotSetParentOnToplevel(const char *type) +{ + userbug("You cannot make a %s a child of another uiControl,", type); +} diff --git a/darwin/window.m b/darwin/window.m index 1ece3f1e..58bd63ac 100644 --- a/darwin/window.m +++ b/darwin/window.m @@ -98,10 +98,16 @@ static void uiWindowDestroy(uiControl *c) } uiDarwinControlDefaultHandle(uiWindow, window) -// TODO? -uiDarwinControlDefaultParent(uiWindow, window) -uiDarwinControlDefaultSetParent(uiWindow, window) -// end TODO + +uiControl *uiWindowParent(uiControl *c) +{ + return NULL; +} + +void uiWindowSetParent(uiControl *c, uiControl *parent) +{ + uiUserBugCannotSetParentOnToplevel("uiWindow"); +} static int uiWindowToplevel(uiControl *c) { diff --git a/ui.h b/ui.h index 6febeaa4..ad847b57 100644 --- a/ui.h +++ b/ui.h @@ -38,7 +38,6 @@ _UI_EXTERN void uiFreeInitError(const char *err); _UI_EXTERN void uiMain(void); _UI_EXTERN void uiQuit(void); -// TODO write a test for this after adding multiline entries _UI_EXTERN void uiQueueMain(void (*f)(void *data), void *data); _UI_EXTERN void uiOnShouldQuit(int (*f)(void *data), void *data); @@ -84,6 +83,8 @@ _UI_EXTERN void uiFreeControl(uiControl *); _UI_EXTERN void uiControlVerifySetParent(uiControl *, uiControl *); _UI_EXTERN int uiControlEnabledToUser(uiControl *); +_UI_EXTERN void uiUserBugCannotSetParentOnToplevel(const char *type); + typedef struct uiWindow uiWindow; #define uiWindow(this) ((uiWindow *) (this)) _UI_EXTERN char *uiWindowTitle(uiWindow *w); diff --git a/unix/window.c b/unix/window.c index 1ebe1eb7..cca767e8 100644 --- a/unix/window.c +++ b/unix/window.c @@ -56,10 +56,16 @@ static void uiWindowDestroy(uiControl *c) } uiUnixControlDefaultHandle(uiWindow) -// TODO? -uiUnixControlDefaultParent(uiWindow) -uiUnixControlDefaultSetParent(uiWindow) -// end TODO + +uiControl *uiWindowParent(uiControl *c) +{ + return NULL; +} + +void uiWindowSetParent(uiControl *c, uiControl *parent) +{ + uiUserBugCannotSetParentOnToplevel("uiWindow"); +} static int uiWindowToplevel(uiControl *c) { diff --git a/windows/window.cpp b/windows/window.cpp index 42475b6d..0906619a 100644 --- a/windows/window.cpp +++ b/windows/window.cpp @@ -161,10 +161,16 @@ static void uiWindowDestroy(uiControl *c) } uiWindowsControlDefaultHandle(uiWindow) -// TODO? -uiWindowsControlDefaultParent(uiWindow) -uiWindowsControlDefaultSetParent(uiWindow) -// end TODO + +uiControl *uiWindowParent(uiControl *c) +{ + return NULL; +} + +void uiWindowSetParent(uiControl *c, uiControl *parent) +{ + uiUserBugCannotSetParentOnToplevel("uiWindow"); +} static int uiWindowToplevel(uiControl *c) { From 61185072f7f211fdbf1ebd7ca411d235be9bd30b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 22 May 2016 20:11:52 -0400 Subject: [PATCH 0091/1329] More TODO -> LONGTERM migration. Also made it so uiSpinbox and uiSlider merely swap min and max if min is larger. --- README.md | 1 + darwin/slider.m | 7 +++++++ darwin/spinbox.m | 9 ++++++--- doc/slider | 1 + doc/spinbox | 1 + unix/drawtext.c | 2 +- unix/slider.c | 7 +++++++ unix/spinbox.c | 11 +++++++---- windows/drawtext.cpp | 2 +- windows/slider.cpp | 7 +++++++ windows/spinbox.cpp | 9 ++++++--- 11 files changed, 45 insertions(+), 12 deletions(-) create mode 100644 doc/slider create mode 100644 doc/spinbox diff --git a/README.md b/README.md index 5c7dc679..3ba91ad6 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ This README is being written.
* uiAreas on Windows and some internal Direct2D areas now respond to `WM_PRINTCLIENT` properly, which should hopefully increase the quality of screenshots. * uiDateTimePicker on GTK+ works properly on RTL layouts and no longer disappears off the bottom of the screen if not enough room is available. It will also no longer be marked for localization of the time format (what the separator should be and whether to use 24-hour time), as that information is not provided by the locale system. :( * Added `uiUserBugCannotSetParentOnToplevel()`, which should be used by implementations of toplevel controls in their `SetParent()` implementations. This will also be the beginning of consolidating common user bug messages into a single place, though this will be one of the only few exported user bug functions. + * uiSpinbox and uiSlider now merely swap their min and max if min ≥ max. They will no longer panic and do nothing, respectively. ## Runtime Requirements diff --git a/darwin/slider.m b/darwin/slider.m index d280ba2e..4cee88c5 100644 --- a/darwin/slider.m +++ b/darwin/slider.m @@ -114,6 +114,13 @@ uiSlider *uiNewSlider(intmax_t min, intmax_t max) { uiSlider *s; NSSliderCell *cell; + intmax_t temp; + + if (min >= max) { + temp = min; + min = max; + max = temp; + } uiDarwinNewControl(uiSlider, s); diff --git a/darwin/spinbox.m b/darwin/spinbox.m index 5817efae..24ed9195 100644 --- a/darwin/spinbox.m +++ b/darwin/spinbox.m @@ -182,10 +182,13 @@ static void defaultOnChanged(uiSpinbox *s, void *data) uiSpinbox *uiNewSpinbox(intmax_t min, intmax_t max) { uiSpinbox *s; + intmax_t temp; - // TODO implicitly swap instead? - if (min >= max) - userbug("min >= max is invalid for a uiSpinbox."); + if (min >= max) { + temp = min; + min = max; + max = temp; + } uiDarwinNewControl(uiSpinbox, s); diff --git a/doc/slider b/doc/slider new file mode 100644 index 00000000..5a6ac040 --- /dev/null +++ b/doc/slider @@ -0,0 +1 @@ +if min >= max then they are swapped diff --git a/doc/spinbox b/doc/spinbox new file mode 100644 index 00000000..5a6ac040 --- /dev/null +++ b/doc/spinbox @@ -0,0 +1 @@ +if min >= max then they are swapped diff --git a/unix/drawtext.c b/unix/drawtext.c index 597f2643..bc12f22e 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -100,7 +100,7 @@ PangoFont *pangoDescToPangoFont(PangoFontDescription *pdesc) context = mkGenericPangoCairoContext(); f = pango_font_map_load_font(pango_cairo_font_map_get_default(), context, pdesc); if (f == NULL) { - // TODO + // LONGTERM g_error("[libui] no match in pangoDescToPangoFont(); report to andlabs"); } g_object_unref(context); diff --git a/unix/slider.c b/unix/slider.c index 81b261d8..b34fac1f 100644 --- a/unix/slider.c +++ b/unix/slider.c @@ -47,6 +47,13 @@ void uiSliderOnChanged(uiSlider *s, void (*f)(uiSlider *, void *), void *data) uiSlider *uiNewSlider(intmax_t min, intmax_t max) { uiSlider *s; + intmax_t temp; + + if (min >= max) { + temp = min; + min = max; + max = temp; + } uiUnixNewControl(uiSlider, s); diff --git a/unix/spinbox.c b/unix/spinbox.c index 433c3965..16be68f9 100644 --- a/unix/spinbox.c +++ b/unix/spinbox.c @@ -34,7 +34,7 @@ void uiSpinboxSetValue(uiSpinbox *s, intmax_t value) { // we need to inhibit sending of ::value-changed because this WILL send a ::value-changed otherwise g_signal_handler_block(s->spinButton, s->onChangedSignal); - // TODO does this clamp? + // this clamps for us gtk_spin_button_set_value(s->spinButton, (gdouble) value); g_signal_handler_unblock(s->spinButton, s->onChangedSignal); } @@ -48,10 +48,13 @@ void uiSpinboxOnChanged(uiSpinbox *s, void (*f)(uiSpinbox *, void *), void *data uiSpinbox *uiNewSpinbox(intmax_t min, intmax_t max) { uiSpinbox *s; + intmax_t temp; - // TODO just swap? - if (min >= max) - userbug("min >= max not allowed in uiNewSpinbox()."); + if (min >= max) { + temp = min; + min = max; + max = temp; + } uiUnixNewControl(uiSpinbox, s); diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 453b4c10..7a03f65d 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -244,7 +244,7 @@ uiDrawTextFont *uiDrawLoadClosestFont(const uiDrawTextFontDescriptor *desc) if (hr != S_OK) logHRESULT(L"error finding font family", hr); if (!exists) - implbug("TODO family not found in uiDrawLoadClosestFont()", hr); + implbug("LONGTERM family not found in uiDrawLoadClosestFont()", hr); hr = collection->GetFontFamily(index, &family); if (hr != S_OK) logHRESULT(L"error loading font family", hr); diff --git a/windows/slider.cpp b/windows/slider.cpp index 83017e3b..0783627c 100644 --- a/windows/slider.cpp +++ b/windows/slider.cpp @@ -71,6 +71,13 @@ void uiSliderOnChanged(uiSlider *s, void (*f)(uiSlider *, void *), void *data) uiSlider *uiNewSlider(intmax_t min, intmax_t max) { uiSlider *s; + intmax_t temp; + + if (min >= max) { + temp = min; + min = max; + max = temp; + } uiWindowsNewControl(uiSlider, s); diff --git a/windows/spinbox.cpp b/windows/spinbox.cpp index a9a9ed4b..f8c79d95 100644 --- a/windows/spinbox.cpp +++ b/windows/spinbox.cpp @@ -182,10 +182,13 @@ static void onResize(uiWindowsControl *c) uiSpinbox *uiNewSpinbox(intmax_t min, intmax_t max) { uiSpinbox *s; + intmax_t temp; - if (min >= max) - // TODO - implbug("error: min >= max in uiNewSpinbox()"); + if (min >= max) { + temp = min; + min = max; + max = temp; + } uiWindowsNewControl(uiSpinbox, s); From 9b4a13e9b3f897d2191748ae8ac8eed8dcf98d32 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 22 May 2016 20:35:40 -0400 Subject: [PATCH 0092/1329] Some more TODO resolution in the GTK+ backend. --- doc/drawtext | 1 + unix/drawtext.c | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) create mode 100644 doc/drawtext diff --git a/doc/drawtext b/doc/drawtext new file mode 100644 index 00000000..b0f1eafc --- /dev/null +++ b/doc/drawtext @@ -0,0 +1 @@ +on some unix systems, alpha blending fonts may not be available; this depends on your installed version of pango and is determined at runtime by libui diff --git a/unix/drawtext.c b/unix/drawtext.c index bc12f22e..e9f86c21 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -87,9 +87,9 @@ static const PangoStretch pangoStretches[] = { // we need a context for a few things // the documentation suggests creating cairo_t-specific, GdkScreen-specific, or even GtkWidget-specific contexts, but we can't really do that because we want our uiDrawTextFonts and uiDrawTextLayouts to be context-independent -// so this will have to do -// TODO really see if there's a better way instead; what do GDK and GTK+ do internally? gdk_pango_context_get()? -#define mkGenericPangoCairoContext() (pango_font_map_create_context(pango_cairo_font_map_get_default())) +// we could use pango_font_map_create_context(pango_cairo_font_map_get_default()) but that will ignore GDK-specific settings +// so let's use gdk_pango_context_get() instead; even though it's for the default screen only, it's good enough for us +#define mkGenericPangoCairoContext() (gdk_pango_context_get()) PangoFont *pangoDescToPangoFont(PangoFontDescription *pdesc) { @@ -251,9 +251,10 @@ void uiDrawTextLayoutExtents(uiDrawTextLayout *layout, double *width, double *he PangoRectangle logical; // in this case, the context is necessary to create the layout + // the layout takes a ref on the context so we can unref it afterward context = mkGenericPangoCairoContext(); pl = pango_layout_new(context); - // TODO g_object_unref() context? + g_object_unref(context); prepareLayout(layout, pl); pango_layout_get_extents(pl, NULL, &logical); @@ -287,7 +288,6 @@ static void addAttr(uiDrawTextLayout *layout, PangoAttribute *attr, intmax_t sta // these attributes are only supported on 1.38 and higher; we need to support 1.36 // use dynamic linking to make them work at least on newer systems -// TODO warn programmers static PangoAttribute *(*newFGAlphaAttr)(guint16 alpha) = NULL; static gboolean tried138 = FALSE; From b66be0bf2d227c7954c55b668f924dfa0b91a8b6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 22 May 2016 20:49:16 -0400 Subject: [PATCH 0093/1329] Stale TODO removal. Expanded page 9 to also show the positions of the second line. --- test/page9.c | 45 ++++++++++++++++++++++++++++++++++++++++----- unix/drawtext.c | 1 - 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/test/page9.c b/test/page9.c index 3c5e2073..5591c054 100644 --- a/test/page9.c +++ b/test/page9.c @@ -25,12 +25,17 @@ static double entryDouble(uiEntry *e) return d; } -// TODO this should be altered not to restore all state on exit so default text colors can be checked static void drawGuides(uiDrawContext *c, uiDrawTextFontMetrics *m) { uiDrawPath *p; uiDrawBrush b; uiDrawStrokeParams sp; + double leading; + double y; + + leading = 0; + if (uiCheckboxChecked(addLeading)) + leading = m->Leading; memset(&b, 0, sizeof (uiDrawBrush)); b.Type = uiDrawBrushTypeSolid; @@ -43,8 +48,10 @@ static void drawGuides(uiDrawContext *c, uiDrawTextFontMetrics *m) uiDrawSave(c); p = uiDrawNewPath(uiDrawFillModeWinding); - uiDrawPathNewFigure(p, 8, 10); - uiDrawPathLineTo(p, 8, 10 + m->Ascent); + y = 10; + uiDrawPathNewFigure(p, 8, y); + y += m->Ascent; + uiDrawPathLineTo(p, 8, y); uiDrawPathEnd(p); b.R = 0.94; b.G = 0.5; @@ -54,8 +61,9 @@ static void drawGuides(uiDrawContext *c, uiDrawTextFontMetrics *m) uiDrawFreePath(p); p = uiDrawNewPath(uiDrawFillModeWinding); - uiDrawPathNewFigure(p, 8, 10 + m->Ascent); - uiDrawPathLineTo(p, 8, 10 + m->Ascent + m->Descent); + uiDrawPathNewFigure(p, 8, y); + y += m->Descent; + uiDrawPathLineTo(p, 8, y); uiDrawPathEnd(p); b.R = 0.12; b.G = 0.56; @@ -64,6 +72,33 @@ static void drawGuides(uiDrawContext *c, uiDrawTextFontMetrics *m) uiDrawStroke(c, p, &b, &sp); uiDrawFreePath(p); + // and again for the second line + p = uiDrawNewPath(uiDrawFillModeWinding); + y += leading; + uiDrawPathNewFigure(p, 8, y); + y += m->Ascent; + uiDrawPathLineTo(p, 8, y); + uiDrawPathEnd(p); + b.R = 0.94; + b.G = 0.5; + b.B = 0.5; + b.A = 0.75; + uiDrawStroke(c, p, &b, &sp); + uiDrawFreePath(p); + + p = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathNewFigure(p, 8, y); + y += m->Descent; + uiDrawPathLineTo(p, 8, y); + uiDrawPathEnd(p); + b.R = 0.12; + b.G = 0.56; + b.B = 1.0; + b.A = 0.75; + uiDrawStroke(c, p, &b, &sp); + uiDrawFreePath(p); + + // and a box to text layout top-left corners p = uiDrawNewPath(uiDrawFillModeWinding); uiDrawPathAddRectangle(p, 0, 0, 10, 10); uiDrawPathEnd(p); diff --git a/unix/drawtext.c b/unix/drawtext.c index e9f86c21..86932191 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -156,7 +156,6 @@ void uiDrawTextFontDescribe(uiDrawTextFont *font, uiDrawTextFontDescriptor *desc #define pangoToCairo(pango) (((double) (pango)) / PANGO_SCALE) #define cairoToPango(cairo) ((gint) ((cairo) * PANGO_SCALE)) -// TODO this isn't enough; pango adds extra space to each layout void uiDrawTextFontGetMetrics(uiDrawTextFont *font, uiDrawTextFontMetrics *metrics) { PangoFontMetrics *pm; From a99a81f58479d06b51adcadd4dd962792bbd303a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 22 May 2016 22:52:29 -0400 Subject: [PATCH 0094/1329] Answered matrix scaling stuff. https://www.willamette.edu/~gorr/classes/GeneralGraphics/Transforms/transforms2d.htm#Combining --- README.md | 1 + darwin/draw.m | 3 +-- unix/drawmatrix.c | 4 +--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 3ba91ad6..68ff8f4b 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ This README is being written.
* uiDateTimePicker on GTK+ works properly on RTL layouts and no longer disappears off the bottom of the screen if not enough room is available. It will also no longer be marked for localization of the time format (what the separator should be and whether to use 24-hour time), as that information is not provided by the locale system. :( * Added `uiUserBugCannotSetParentOnToplevel()`, which should be used by implementations of toplevel controls in their `SetParent()` implementations. This will also be the beginning of consolidating common user bug messages into a single place, though this will be one of the only few exported user bug functions. * uiSpinbox and uiSlider now merely swap their min and max if min ≥ max. They will no longer panic and do nothing, respectively. + * Matrix scaling will no longer leave the matrix in an invalid state on OS X and GTK+. ## Runtime Requirements diff --git a/darwin/draw.m b/darwin/draw.m index 0552c07d..68d02bc0 100644 --- a/darwin/draw.m +++ b/darwin/draw.m @@ -332,13 +332,12 @@ void uiDrawMatrixScale(uiDrawMatrix *m, double xCenter, double yCenter, double x double xt, yt; m2c(m, &c); - // TODO explain why the translation must come first xt = x; yt = y; scaleCenter(xCenter, yCenter, &xt, &yt); c = CGAffineTransformTranslate(c, xt, yt); c = CGAffineTransformScale(c, x, y); - // TODO undo the translation? + c = CGAffineTransformTranslate(c, -xt, -yt); c2m(&c, m); } diff --git a/unix/drawmatrix.c b/unix/drawmatrix.c index 807b60d9..ac7ac579 100644 --- a/unix/drawmatrix.c +++ b/unix/drawmatrix.c @@ -37,13 +37,12 @@ void uiDrawMatrixScale(uiDrawMatrix *m, double xCenter, double yCenter, double x double xt, yt; m2c(m, &c); - // TODO explain why the translation must come first xt = x; yt = y; scaleCenter(xCenter, yCenter, &xt, &yt); cairo_matrix_translate(&c, xt, yt); cairo_matrix_scale(&c, x, y); - // TODO undo the translation? + cairo_matrix_translate(&c, -xt, -yt); c2m(&c, m); } @@ -54,7 +53,6 @@ void uiDrawMatrixRotate(uiDrawMatrix *m, double x, double y, double amount) m2c(m, &c); cairo_matrix_translate(&c, x, y); cairo_matrix_rotate(&c, amount); - // TODO undo the translation? also cocoa backend cairo_matrix_translate(&c, -x, -y); c2m(&c, m); } From 4ab0d9c1f64254769ba77ff57eb1e7f505879574 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 22 May 2016 22:57:18 -0400 Subject: [PATCH 0095/1329] More TODO resolution. --- doc/main | 1 + unix/main.c | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 doc/main diff --git a/doc/main b/doc/main new file mode 100644 index 00000000..9fa9c369 --- /dev/null +++ b/doc/main @@ -0,0 +1 @@ +after uiQuit or if uiShouldQuit returns nonzero, uiQueueMain's effect is undefined diff --git a/unix/main.c b/unix/main.c index 07010cd5..50f308bd 100644 --- a/unix/main.c +++ b/unix/main.c @@ -62,7 +62,6 @@ static gboolean doqueued(gpointer data) return FALSE; } -// TODO document that the effect of calling this function after uiQuit() is called (either directly or via a nonzero return to uiShouldQuit()) is undefined void uiQueueMain(void (*f)(void *data), void *data) { struct queued *q; From 60627e13a1e7f1ffdb6090e815a1aee44881f5fa Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 22 May 2016 23:05:37 -0400 Subject: [PATCH 0096/1329] Fleshed out page 12. --- test/page12.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/test/page12.c b/test/page12.c index dc06e279..5a8e963f 100644 --- a/test/page12.c +++ b/test/page12.c @@ -1,16 +1,60 @@ // 22 may 2016 #include "test.h" -// TODO add buttons for event testing and Append scroll/selection changing. +// TODO OS X: if the hboxes are empty, the text views don't show up + +static void meChanged(uiMultilineEntry *e, void *data) +{ + printf("%s changed\n", (char *) data); +} + +static void setClicked(uiButton *b, void *data) +{ + uiMultilineEntrySetText(uiMultilineEntry(data), "set"); +} + +static void appendClicked(uiButton *b, void *data) +{ + uiMultilineEntryAppend(uiMultilineEntry(data), "append\n"); +} + +static uiBox *half(uiMultilineEntry *(*mk)(void), const char *which) +{ + uiBox *vbox, *hbox; + uiMultilineEntry *me; + uiButton *button; + + vbox = newVerticalBox(); + + me = (*mk)(); + uiMultilineEntryOnChanged(me, meChanged, (void *) which); + uiBoxAppend(vbox, uiControl(me), 1); + + hbox = newHorizontalBox(); + uiBoxAppend(vbox, uiControl(hbox), 0); + + button = uiNewButton("Set"); + uiButtonOnClicked(button, setClicked, me); + uiBoxAppend(hbox, uiControl(button), 0); + + button = uiNewButton("Append"); + uiButtonOnClicked(button, appendClicked, me); + uiBoxAppend(hbox, uiControl(button), 0); + + return vbox; +} uiBox *makePage12(void) { uiBox *page12; + uiBox *b; page12 = newHorizontalBox(); - uiBoxAppend(page12, uiControl(uiNewMultilineEntry()), 1); - uiBoxAppend(page12, uiControl(uiNewNonWrappingMultilineEntry()), 1); + b = half(uiNewMultilineEntry, "wrap"); + uiBoxAppend(page12, uiControl(b), 1); + b = half(uiNewNonWrappingMultilineEntry, "no wrap"); + uiBoxAppend(page12, uiControl(b), 1); return page12; } From 0e5e37f98b1856a58b6c7d0856f7cfc754721fca Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 22 May 2016 23:14:33 -0400 Subject: [PATCH 0097/1329] Fixed multilne entry changed events on GTK+. --- README.md | 1 + unix/multilineentry.c | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 68ff8f4b..81a9b4a1 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ This README is being written.
* Added `uiUserBugCannotSetParentOnToplevel()`, which should be used by implementations of toplevel controls in their `SetParent()` implementations. This will also be the beginning of consolidating common user bug messages into a single place, though this will be one of the only few exported user bug functions. * uiSpinbox and uiSlider now merely swap their min and max if min ≥ max. They will no longer panic and do nothing, respectively. * Matrix scaling will no longer leave the matrix in an invalid state on OS X and GTK+. + * `uiMultilineEntrySetText()` and `uiMutlilineEntryAppend()` on GTK+ no longer fire `OnChanged()` events. ## Runtime Requirements diff --git a/unix/multilineentry.c b/unix/multilineentry.c index 92f0e27e..09ffd460 100644 --- a/unix/multilineentry.c +++ b/unix/multilineentry.c @@ -44,8 +44,10 @@ char *uiMultilineEntryText(uiMultilineEntry *e) void uiMultilineEntrySetText(uiMultilineEntry *e, const char *text) { - // TODO does this send a changed signal? + // we need to inhibit sending of ::changed because this WILL send a ::changed otherwise + g_signal_handler_block(e->textbuf, e->onChangedSignal); gtk_text_buffer_set_text(e->textbuf, text, -1); + g_signal_handler_unblock(e->textbuf, e->onChangedSignal); } // TODO scroll to end? @@ -54,8 +56,10 @@ void uiMultilineEntryAppend(uiMultilineEntry *e, const char *text) GtkTextIter end; gtk_text_buffer_get_end_iter(e->textbuf, &end); - // TODO does this send a changed signal? + // we need to inhibit sending of ::changed because this WILL send a ::changed otherwise + g_signal_handler_block(e->textbuf, e->onChangedSignal); gtk_text_buffer_insert(e->textbuf, &end, text, -1); + g_signal_handler_unblock(e->textbuf, e->onChangedSignal); } void uiMultilineEntryOnChanged(uiMultilineEntry *e, void (*f)(uiMultilineEntry *e, void *data), void *data) From 268bdbcb34e0574e0be3bdac891a228bf98bc1b3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 22 May 2016 23:28:17 -0400 Subject: [PATCH 0098/1329] More announcements. --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 81a9b4a1..26621c8b 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,10 @@ This README is being written.
## Announcements +* ** Date: Sun, 22 May 2016 23:30:22 -0400 Subject: [PATCH 0099/1329] Whoops, forgot the date --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 26621c8b..e42192d0 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ This README is being written.
## Announcements -* ** Date: Mon, 23 May 2016 00:41:56 -0400 Subject: [PATCH 0100/1329] More TODO resolution. --- unix/slider.c | 2 +- unix/spinbox.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/unix/slider.c b/unix/slider.c index b34fac1f..9ede700f 100644 --- a/unix/slider.c +++ b/unix/slider.c @@ -61,7 +61,7 @@ uiSlider *uiNewSlider(intmax_t min, intmax_t max) s->range = GTK_RANGE(s->widget); s->scale = GTK_SCALE(s->widget); - // TODO needed? + // ensure integers, just to be safe gtk_scale_set_digits(s->scale, 0); s->onChangedSignal = g_signal_connect(s->scale, "value-changed", G_CALLBACK(onChanged), s); diff --git a/unix/spinbox.c b/unix/spinbox.c index 16be68f9..d819c21f 100644 --- a/unix/spinbox.c +++ b/unix/spinbox.c @@ -62,7 +62,7 @@ uiSpinbox *uiNewSpinbox(intmax_t min, intmax_t max) s->entry = GTK_ENTRY(s->widget); s->spinButton = GTK_SPIN_BUTTON(s->widget); - // TODO needed? + // ensure integers, just to be safe gtk_spin_button_set_digits(s->spinButton, 0); s->onChangedSignal = g_signal_connect(s->spinButton, "value-changed", G_CALLBACK(onChanged), s); From 5d63fe4cecfa082b6131686d49ad03fbbec2c7d5 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 23 May 2016 01:11:43 -0400 Subject: [PATCH 0101/1329] Fixed surrogate pair drawing on OS X. --- README.md | 3 +++ darwin/drawtext.m | 44 ++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e42192d0..e888d59c 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,9 @@ This README is being written.
*Note that today's entry may be updated later today.* +* **23 May 2016** + * Fixed surrogate pair drawing on OS X. + * **22 May 2016** * Removed `uiControlVerifyDestroy()`; that is now part of `uiFreeControl()` itself. * Added `uiPi`, a constant for π. This is provided for C and C++ programmers, where there is no standard named constant for π; bindings authors shouldn't need to worry about this. diff --git a/darwin/drawtext.m b/darwin/drawtext.m index dbbe4db6..55e058fe 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -442,15 +442,17 @@ void uiDrawTextFontGetMetrics(uiDrawTextFont *font, uiDrawTextFontMetrics *metri struct uiDrawTextLayout { CFMutableAttributedStringRef mas; + CFRange *charsToRanges; double width; }; uiDrawTextLayout *uiDrawNewTextLayout(const char *str, uiDrawTextFont *defaultFont, double width) { uiDrawTextLayout *layout; -//TODO CFStringRef cfstr; CFAttributedStringRef immutable; CFMutableDictionaryRef attr; + CFStringRef backing; + CFIndex i, j, n; layout = uiNew(uiDrawTextLayout); @@ -458,11 +460,9 @@ uiDrawTextLayout *uiDrawNewTextLayout(const char *str, uiDrawTextFont *defaultFo // this will retain defaultFont->f; no need to worry CFDictionaryAddValue(attr, kCTFontAttributeName, defaultFont->f); - // TODO convert the NSString call to a CFString call immutable = CFAttributedStringCreate(NULL, (CFStringRef) [NSString stringWithUTF8String:str], attr); if (immutable == NULL) complain("error creating immutable attributed string in uiDrawNewTextLayout()"); -//TODO CFRelease(cfstr); CFRelease(attr); layout->mas = CFAttributedStringCreateMutableCopy(NULL, 0, immutable); @@ -472,11 +472,34 @@ uiDrawTextLayout *uiDrawNewTextLayout(const char *str, uiDrawTextFont *defaultFo uiDrawTextLayoutSetWidth(layout, width); + // unfortunately the CFRanges for attributes expect UTF-16 codepoints + // we want full characters + // fortunately CFStringGetRangeOfComposedCharactersAtIndex() is here for us + backing = CFAttributedStringGetString(layout->mas); + n = CFStringGetLength(backing); + // allocate one extra, just to be safe + layout->charsToRanges = (CFRange *) uiAlloc((n + 1) * sizeof (CFRange), "CFRange[]"); + i = 0; + j = 0; + while (i < n) { + CFRange range; + + range = CFStringGetRangeOfComposedCharactersAtIndex(backing, i); + i = range.location + range.length; + layout->charsToRanges[j] = range; + j++; + } + // and set the last one + layout->charsToRanges[j].location = i; + layout->charsToRanges[j].length = 0; + // TODO how will this affect drawing things that aren't surrogate pairs? + return layout; } void uiDrawFreeTextLayout(uiDrawTextLayout *layout) { + uiFree(layout->charsToRanges); CFRelease(layout->mas); uiFree(layout); } @@ -604,7 +627,20 @@ void doDrawText(CGContextRef c, CGFloat cheight, double x, double y, uiDrawTextL CGContextSetTextPosition(c, x, y); #endif -#define rangeToCFRange() CFRangeMake(startChar, endChar - startChar) +static CFRange charsToRange(uiDrawTextLayout *layout, intmax_t startChar, intmax_t endChar) +{ + CFRange start, end; + CFRange out; + + start = layout->charsToRanges[startChar]; + end = layout->charsToRanges[endChar]; + out.location = start.location; + // - 1 to avoid including the first code point after end + out.length = (end.location + end.length - 1) - start.location; + return out; +} + +#define rangeToCFRange() charsToRange(layout, startChar, endChar) void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, intmax_t startChar, intmax_t endChar, double r, double g, double b, double a) { From 156c3584f706753e892be77338d1b9c2cb669c5b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 23 May 2016 01:12:52 -0400 Subject: [PATCH 0102/1329] Documentation update. --- darwin/drawtext.m | 1 + 1 file changed, 1 insertion(+) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 55e058fe..9061603d 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -475,6 +475,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(const char *str, uiDrawTextFont *defaultFo // unfortunately the CFRanges for attributes expect UTF-16 codepoints // we want full characters // fortunately CFStringGetRangeOfComposedCharactersAtIndex() is here for us + // https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Strings/Articles/stringsClusters.html says that this does work on surrogate pairs (despite the name), and that this is the preferred function for this particular job anyway backing = CFAttributedStringGetString(layout->mas); n = CFStringGetLength(backing); // allocate one extra, just to be safe From a571bd477982e62d9508d16d6c71670ee9984e0e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 23 May 2016 08:11:22 -0400 Subject: [PATCH 0103/1329] Added a composed character test to page 10. --- test/page10.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/test/page10.c b/test/page10.c index ba8399da..bbbfe659 100644 --- a/test/page10.c +++ b/test/page10.c @@ -1,8 +1,6 @@ // 22 december 2015 #include "test.h" -// TODO test composed diacritic marks - static uiEntry *textString; static uiFontButton *textFontButton; static uiColorButton *textColorButton; @@ -29,6 +27,7 @@ static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *dp) uiDrawTextLayout *layout; double r, g, b, al; char surrogates[1 + 4 + 1 + 1]; + char composed[2 + 2 + 2 + 3 + 2 + 1]; double width, height; font = uiFontButtonFont(textFontButton); @@ -63,6 +62,29 @@ static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *dp) uiDrawText(dp->Context, 10, 10 + height, layout); uiDrawFreeTextLayout(layout); + composed[0] = 'z'; + composed[1] = 'z'; + composed[2] = 0xC3; // 2 + composed[3] = 0xA9; + composed[4] = 'z'; + composed[5] = 'z'; + composed[6] = 0x65; // 5 + composed[7] = 0xCC; + composed[8] = 0x81; + composed[9] = 'z'; + composed[10] = 'z'; + composed[11] = '\0'; + + layout = uiDrawNewTextLayout(composed, font, -1); + uiDrawTextLayoutSetColor(layout, + 2, 3, + 1, 0, 0.5, 0.5); + uiDrawTextLayoutSetColor(layout, + 5, 6, + 1, 0, 0.5, 0.5); + uiDrawText(dp->Context, 10, 10 + height + height, layout); + uiDrawFreeTextLayout(layout); + uiDrawFreeTextFont(font); } From 996ba99b0f45eb81954855b03aa9864918805c7c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 23 May 2016 17:41:15 -0400 Subject: [PATCH 0104/1329] Began uiEditableCombobox splitting. --- examples/controlgallery/main.c | 11 ++++++----- test/page10.c | 3 +++ test/page4.c | 23 +++++++++++++++++------ ui.h | 10 +++++++++- 4 files changed, 35 insertions(+), 12 deletions(-) diff --git a/examples/controlgallery/main.c b/examples/controlgallery/main.c index f29033e6..1e426041 100644 --- a/examples/controlgallery/main.c +++ b/examples/controlgallery/main.c @@ -82,6 +82,7 @@ int main(void) uiBox *inner2; uiEntry *entry; uiCombobox *cbox; + uiEditableCombobox *ecbox; uiRadioButtons *rb; uiTab *tab; @@ -206,11 +207,11 @@ int main(void) uiComboboxAppend(cbox, "Combobox Item 3"); uiBoxAppend(inner, uiControl(cbox), 0); - cbox = uiNewEditableCombobox(); - uiComboboxAppend(cbox, "Editable Item 1"); - uiComboboxAppend(cbox, "Editable Item 2"); - uiComboboxAppend(cbox, "Editable Item 3"); - uiBoxAppend(inner, uiControl(cbox), 0); + ecbox = uiNewEditableCombobox(); + uiEditableComboboxAppend(ecbox, "Editable Item 1"); + uiEditableComboboxAppend(ecbox, "Editable Item 2"); + uiEditableComboboxAppend(ecbox, "Editable Item 3"); + uiBoxAppend(inner, uiControl(ecbox), 0); rb = uiNewRadioButtons(); uiRadioButtonsAppend(rb, "Radio Button 1"); diff --git a/test/page10.c b/test/page10.c index bbbfe659..74de4fba 100644 --- a/test/page10.c +++ b/test/page10.c @@ -82,6 +82,9 @@ static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *dp) uiDrawTextLayoutSetColor(layout, 5, 6, 1, 0, 0.5, 0.5); + uiDrawTextLayoutSetColor(layout, + 6, 7, + 0.5, 0, 1, 0.5); uiDrawText(dp->Context, 10, 10 + height + height, layout); uiDrawFreeTextLayout(layout); diff --git a/test/page4.c b/test/page4.c index 27c68880..bb57ef07 100644 --- a/test/page4.c +++ b/test/page4.c @@ -29,13 +29,13 @@ SETTOO(Slider, Low, -80) SETTOO(Slider, High, 80) static uiCombobox *cbox; -static uiCombobox *editable; +static uiEditableCombobox *editable; static uiRadioButtons *rb; static void appendCBRB(uiButton *b, void *data) { uiComboboxAppend(cbox, "New Item"); - uiComboboxAppend(editable, "New Item"); + uiEditableComboboxAppend(editable, "New Item"); uiRadioButtonsAppend(rb, "New Item"); } @@ -46,6 +46,17 @@ static void onCBChanged(uiCombobox *c, void *data) (int) uiComboboxSelected(c)); } +static void onECBChanged(uiEditableCombobox *c, void *data) +{ + char *t; + + t = uiEditableComboboxText(c); + printf("%s combobox changed to %s\n", + (char *) data, + t); + uiFreeText(t); +} + uiBox *makePage4(void) { uiBox *page4; @@ -101,10 +112,10 @@ uiBox *makePage4(void) uiBoxAppend(page4, uiControl(cbox), 0); editable = uiNewEditableCombobox(); - uiComboboxAppend(editable, "Editable Item 1"); - uiComboboxAppend(editable, "Editable Item 2"); - uiComboboxAppend(editable, "Editable Item 3"); - uiComboboxOnSelected(editable, onCBChanged, "editable"); + uiEditableComboboxAppend(editable, "Editable Item 1"); + uiEditableComboboxAppend(editable, "Editable Item 2"); + uiEditableComboboxAppend(editable, "Editable Item 3"); + uiEditableComboboxOnChanged(editable, onECBChanged, "editable"); uiBoxAppend(page4, uiControl(editable), 0); rb = uiNewRadioButtons(); diff --git a/ui.h b/ui.h index ad847b57..66d1fb7e 100644 --- a/ui.h +++ b/ui.h @@ -190,7 +190,15 @@ _UI_EXTERN intmax_t uiComboboxSelected(uiCombobox *c); _UI_EXTERN void uiComboboxSetSelected(uiCombobox *c, intmax_t n); _UI_EXTERN void uiComboboxOnSelected(uiCombobox *c, void (*f)(uiCombobox *c, void *data), void *data); _UI_EXTERN uiCombobox *uiNewCombobox(void); -_UI_EXTERN uiCombobox *uiNewEditableCombobox(void); + +typedef struct uiEditableCombobox uiEditableCombobox; +#define uiEditableCombobox(this) ((uiEditableCombobox *) (this)) +_UI_EXTERN void uiEditableComboboxAppend(uiEditableCombobox *c, const char *text); +_UI_EXTERN char *uiEditableComboboxText(uiEditableCombobox *c); +_UI_EXTERN void uiEditableComboboxSetText(uiEditableCombobox *c, intmax_t n); +// TODO what do we call a function that sets the currently selected item and fills the text field with it? editable comboboxes have no consistent concept of selected item +_UI_EXTERN void uiEditableComboboxOnChanged(uiEditableCombobox *c, void (*f)(uiEditableCombobox *c, void *data), void *data); +_UI_EXTERN uiEditableCombobox *uiNewEditableCombobox(void); typedef struct uiRadioButtons uiRadioButtons; #define uiRadioButtons(this) ((uiRadioButtons *) (this)) From 363916855cfcd73c92502119a8dd9c499efb25eb Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 23 May 2016 18:50:02 -0400 Subject: [PATCH 0105/1329] Split uiCombobox on OS X. --- common/controlsigs.h | 1 + darwin/GNUfiles.mk | 1 + darwin/combobox.m | 155 +++++++++-------------------------- darwin/editablecombo.m | 179 +++++++++++++++++++++++++++++++++++++++++ test/page4.c | 1 + ui.h | 2 +- 6 files changed, 219 insertions(+), 120 deletions(-) create mode 100644 darwin/editablecombo.m diff --git a/common/controlsigs.h b/common/controlsigs.h index dc984329..38aa4de9 100644 --- a/common/controlsigs.h +++ b/common/controlsigs.h @@ -7,6 +7,7 @@ #define uiColorButtonSignature 0x436F6C42 #define uiComboboxSignature 0x436F6D62 #define uiDateTimePickerSignature 0x44545069 +#define uiEditableComboboxSignature 0x45644362 #define uiEntrySignature 0x456E7472 #define uiFontButtonSignature 0x466F6E42 #define uiGroupSignature 0x47727062 diff --git a/darwin/GNUfiles.mk b/darwin/GNUfiles.mk index 810ddd16..ae39d70e 100644 --- a/darwin/GNUfiles.mk +++ b/darwin/GNUfiles.mk @@ -15,6 +15,7 @@ MFILES += \ darwin/debug.m \ darwin/draw.m \ darwin/drawtext.m \ + darwin/editablecombo.m \ darwin/entry.m \ darwin/fontbutton.m \ darwin/group.m \ diff --git a/darwin/combobox.m b/darwin/combobox.m index 9f63157a..e956762e 100644 --- a/darwin/combobox.m +++ b/darwin/combobox.m @@ -5,37 +5,17 @@ // NSPopUpButton is fine. #define comboboxWidth 96 -@interface libui_intrinsicWidthNSComboBox : NSComboBox -@end - -@implementation libui_intrinsicWidthNSComboBox - -- (NSSize)intrinsicContentSize -{ - NSSize s; - - s = [super intrinsicContentSize]; - s.width = comboboxWidth; - return s; -} - -@end - struct uiCombobox { uiDarwinControl c; - BOOL editable; NSPopUpButton *pb; NSArrayController *pbac; - NSComboBox *cb; - NSView *handle; // for uiControlHandle() void (*onSelected)(uiCombobox *, void *); void *onSelectedData; }; -@interface comboboxDelegateClass : NSObject { +@interface comboboxDelegateClass : NSObject { struct mapTable *comboboxes; } -- (void)comboBoxSelectionDidChange:(NSNotification *)note; - (IBAction)onSelected:(id)sender; - (void)registerCombobox:(uiCombobox *)c; - (void)unregisterCombobox:(uiCombobox *)c; @@ -57,98 +37,57 @@ struct uiCombobox { [super dealloc]; } -// note: does not trigger when text changed -// TODO not perfect either: -// - triggered when keyboard navigating the open menu -// - does not trigger when the text is changed to a menu item (which normally selects that item; IDK how to inhibit that behavior - TODO) -- (void)comboBoxSelectionDidChange:(NSNotification *)note -{ - [self onSelected:[note object]]; -} - - (IBAction)onSelected:(id)sender { uiCombobox *c; - c = (uiCombobox *) mapGet(self->comboboxes, sender); + c = uiCombobox(mapGet(self->comboboxes, sender)); (*(c->onSelected))(c, c->onSelectedData); } - (void)registerCombobox:(uiCombobox *)c { - mapSet(self->comboboxes, c->handle, c); - if (c->editable) - [c->cb setDelegate:self]; - else { - [c->pb setTarget:self]; - [c->pb setAction:@selector(onSelected:)]; - } + mapSet(self->comboboxes, c->pb, c); + [c->pb setTarget:self]; + [c->pb setAction:@selector(onSelected:)]; } - (void)unregisterCombobox:(uiCombobox *)c { - if (c->editable) - [c->cb setDelegate:nil]; - else - [c->pb setTarget:nil]; - mapDelete(self->comboboxes, c->handle); + [c->pb setTarget:nil]; + mapDelete(self->comboboxes, c->pb); } @end static comboboxDelegateClass *comboboxDelegate = nil; -uiDarwinControlAllDefaultsExceptDestroy(uiCombobox, handle) +uiDarwinControlAllDefaultsExceptDestroy(uiCombobox, pb) static void uiComboboxDestroy(uiControl *cc) { uiCombobox *c = uiCombobox(cc); [comboboxDelegate unregisterCombobox:c]; - if (!c->editable) { - [c->pb unbind:@"contentObjects"]; - [c->pb unbind:@"selectedIndex"]; - [c->pbac release]; - } - [c->handle release]; + [c->pb unbind:@"contentObjects"]; + [c->pb unbind:@"selectedIndex"]; + [c->pbac release]; + [c->pb release]; uiFreeControl(uiControl(c)); } void uiComboboxAppend(uiCombobox *c, const char *text) { - if (c->editable) - [c->cb addItemWithObjectValue:toNSString(text)]; - else - [c->pbac addObject:toNSString(text)]; + [c->pbac addObject:toNSString(text)]; } intmax_t uiComboboxSelected(uiCombobox *c) { - if (c->editable) - return [c->cb indexOfSelectedItem]; return [c->pb indexOfSelectedItem]; } void uiComboboxSetSelected(uiCombobox *c, intmax_t n) { - if (c->editable) { - // see https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ComboBox/Tasks/SettingComboBoxValue.html#//apple_ref/doc/uid/20000256 - id delegate; - - // this triggers the delegate; turn it off for now - delegate = [c->cb delegate]; - [c->cb setDelegate:nil]; - - // this seems to work fine for -1 too - [c->cb selectItemAtIndex:n]; - if (n == -1) - [c->cb setObjectValue:@""]; - else - [c->cb setObjectValue:[c->cb objectValueOfSelectedItem]]; - - [c->cb setDelegate:delegate]; - return; - } [c->pb selectItemAtIndex:n]; } @@ -163,47 +102,35 @@ static void defaultOnSelected(uiCombobox *c, void *data) // do nothing } -static uiCombobox *finishNewCombobox(BOOL editable) +uiCombobox *uiNewCombobox(void) { uiCombobox *c; + NSPopUpButtonCell *pbcell; uiDarwinNewControl(uiCombobox, c); - c->editable = editable; - if (c->editable) { - c->cb = [[libui_intrinsicWidthNSComboBox alloc] initWithFrame:NSZeroRect]; - [c->cb setUsesDataSource:NO]; - [c->cb setButtonBordered:YES]; - [c->cb setCompletes:NO]; - uiDarwinSetControlFont(c->cb, NSRegularControlSize); - c->handle = c->cb; - } else { - NSPopUpButtonCell *pbcell; + c->pb = [[NSPopUpButton alloc] initWithFrame:NSZeroRect pullsDown:NO]; + [c->pb setPreferredEdge:NSMinYEdge]; + pbcell = (NSPopUpButtonCell *) [c->pb cell]; + [pbcell setArrowPosition:NSPopUpArrowAtBottom]; + // TODO font - c->pb = [[NSPopUpButton alloc] initWithFrame:NSZeroRect pullsDown:NO]; - [c->pb setPreferredEdge:NSMinYEdge]; - pbcell = (NSPopUpButtonCell *) [c->pb cell]; - [pbcell setArrowPosition:NSPopUpArrowAtBottom]; - // TODO font - c->handle = c->pb; - - // NSPopUpButton doesn't work like a combobox - // - it automatically selects the first item - // - it doesn't support duplicates - // but we can use a NSArrayController and Cocoa bindings to bypass these restrictions - c->pbac = [NSArrayController new]; - [c->pbac setAvoidsEmptySelection:NO]; - [c->pbac setSelectsInsertedObjects:NO]; - [c->pbac setAutomaticallyRearrangesObjects:NO]; - [c->pb bind:@"contentValues" - toObject:c->pbac - withKeyPath:@"arrangedObjects" - options:nil]; - [c->pb bind:@"selectedIndex" - toObject:c->pbac - withKeyPath:@"selectionIndex" - options:nil]; - } + // NSPopUpButton doesn't work like a combobox + // - it automatically selects the first item + // - it doesn't support duplicates + // but we can use a NSArrayController and Cocoa bindings to bypass these restrictions + c->pbac = [NSArrayController new]; + [c->pbac setAvoidsEmptySelection:NO]; + [c->pbac setSelectsInsertedObjects:NO]; + [c->pbac setAutomaticallyRearrangesObjects:NO]; + [c->pb bind:@"contentValues" + toObject:c->pbac + withKeyPath:@"arrangedObjects" + options:nil]; + [c->pb bind:@"selectedIndex" + toObject:c->pbac + withKeyPath:@"selectionIndex" + options:nil]; if (comboboxDelegate == nil) { comboboxDelegate = [comboboxDelegateClass new]; @@ -214,13 +141,3 @@ static uiCombobox *finishNewCombobox(BOOL editable) return c; } - -uiCombobox *uiNewCombobox(void) -{ - return finishNewCombobox(NO); -} - -uiCombobox *uiNewEditableCombobox(void) -{ - return finishNewCombobox(YES); -} diff --git a/darwin/editablecombo.m b/darwin/editablecombo.m new file mode 100644 index 00000000..5fa868fd --- /dev/null +++ b/darwin/editablecombo.m @@ -0,0 +1,179 @@ +// 14 august 2015 +#import "uipriv_darwin.h" + +// NSComboBoxes have no intrinsic width; we'll use the default Interface Builder width for them. +#define comboboxWidth 96 + +@interface libui_intrinsicWidthNSComboBox : NSComboBox +@end + +@implementation libui_intrinsicWidthNSComboBox + +- (NSSize)intrinsicContentSize +{ + NSSize s; + + s = [super intrinsicContentSize]; + s.width = comboboxWidth; + return s; +} + +@end + +struct uiEditableCombobox { + uiDarwinControl c; + NSComboBox *cb; + void (*onChanged)(uiEditableCombobox *, void *); + void *onChangedData; +}; + +@interface editableComboboxDelegateClass : NSObject { + struct mapTable *comboboxes; +} +- (void)controlTextDidChange:(NSNotification *)note; +- (void)comboBoxSelectionDidChange:(NSNotification *)note; +- (void)registerCombobox:(uiEditableCombobox *)c; +- (void)unregisterCombobox:(uiEditableCombobox *)c; +@end + +@implementation editableComboboxDelegateClass + +- (id)init +{ + self = [super init]; + if (self) + self->comboboxes = newMap(); + return self; +} + +- (void)dealloc +{ + mapDestroy(self->comboboxes); + [super dealloc]; +} + +- (void)controlTextDidChange:(NSNotification *)note +{ + uiEditableCombobox *c; + + c = uiEditableCombobox(mapGet(self->comboboxes, [note object])); + (*(c->onChanged))(c, c->onChangedData); +} + +// the above doesn't handle when an item is selected; this will +- (void)comboBoxSelectionDidChange:(NSNotification *)note +{ + // except this is sent BEFORE the entry is changed, and that doesn't send the above, so + // this is via http://stackoverflow.com/a/21059819/3408572 - it avoids the need to manage selected items + // this still isn't perfect — I get residual changes to the same value while navigating the list — but it's good enough + [self performSelector:@selector(controlTextDidChange:) + withObject:note + afterDelay:0]; +} + +- (void)registerCombobox:(uiEditableCombobox *)c +{ + mapSet(self->comboboxes, c->cb, c); + [c->cb setDelegate:self]; +} + +- (void)unregisterCombobox:(uiEditableCombobox *)c +{ + [c->cb setDelegate:nil]; + mapDelete(self->comboboxes, c->cb); +} + +@end + +static editableComboboxDelegateClass *comboboxDelegate = nil; + +uiDarwinControlAllDefaultsExceptDestroy(uiEditableCombobox, cb) + +static void uiEditableComboboxDestroy(uiControl *cc) +{ + uiEditableCombobox *c = uiEditableCombobox(cc); + + [comboboxDelegate unregisterCombobox:c]; + [c->cb release]; + uiFreeControl(uiControl(c)); +} + +void uiEditableComboboxAppend(uiEditableCombobox *c, const char *text) +{ + [c->cb addItemWithObjectValue:toNSString(text)]; +} + +char *uiEditableComboboxText(uiEditableCombobox *c) +{ + return uiDarwinNSStringToText([c->cb stringValue]); +} + +void uiEditableComboboxSetText(uiEditableCombobox *c, const char *text) +{ + NSString *t; + + t = toNSString(text); + [c->cb setStringValue:t]; + // do this just to keep synchronicity with the behavior of the real control + // for what it's worth, this automatic behavior when typing is the reason why this is separate from uiCombobox + [c->cb selectItemWithObjectValue:t]; +} + +#if 0 +// TODO +void uiEditableComboboxSetSelected(uiEditableCombobox *c, intmax_t n) +{ + if (c->editable) { + // see https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ComboBox/Tasks/SettingComboBoxValue.html#//apple_ref/doc/uid/20000256 + id delegate; + + // this triggers the delegate; turn it off for now + delegate = [c->cb delegate]; + [c->cb setDelegate:nil]; + + // this seems to work fine for -1 too + [c->cb selectItemAtIndex:n]; + if (n == -1) + [c->cb setObjectValue:@""]; + else + [c->cb setObjectValue:[c->cb objectValueOfSelectedItem]]; + + [c->cb setDelegate:delegate]; + return; + } + [c->pb selectItemAtIndex:n]; +} +#endif + +void uiEditableComboboxOnChanged(uiEditableCombobox *c, void (*f)(uiEditableCombobox *c, void *data), void *data) +{ + c->onChanged = f; + c->onChangedData = data; +} + +static void defaultOnChanged(uiEditableCombobox *c, void *data) +{ + // do nothing +} + +uiEditableCombobox *uiNewEditableCombobox(void) +{ + uiEditableCombobox *c; + + uiDarwinNewControl(uiEditableCombobox, c); + + c->cb = [[libui_intrinsicWidthNSComboBox alloc] initWithFrame:NSZeroRect]; + [c->cb setUsesDataSource:NO]; + [c->cb setButtonBordered:YES]; + [c->cb setCompletes:NO]; + uiDarwinSetControlFont(c->cb, NSRegularControlSize); + + if (comboboxDelegate == nil) { + comboboxDelegate = [editableComboboxDelegateClass new]; + [delegates addObject:comboboxDelegate]; + } + [comboboxDelegate registerCombobox:c]; + uiEditableComboboxOnChanged(c, defaultOnChanged, NULL); + + return c; +} diff --git a/test/page4.c b/test/page4.c index bb57ef07..00bc16ec 100644 --- a/test/page4.c +++ b/test/page4.c @@ -44,6 +44,7 @@ static void onCBChanged(uiCombobox *c, void *data) printf("%s combobox changed to %d\n", (char *) data, (int) uiComboboxSelected(c)); + uiEditableComboboxSetText(editable, "changed"); } static void onECBChanged(uiEditableCombobox *c, void *data) diff --git a/ui.h b/ui.h index 66d1fb7e..c1d7bb31 100644 --- a/ui.h +++ b/ui.h @@ -195,7 +195,7 @@ typedef struct uiEditableCombobox uiEditableCombobox; #define uiEditableCombobox(this) ((uiEditableCombobox *) (this)) _UI_EXTERN void uiEditableComboboxAppend(uiEditableCombobox *c, const char *text); _UI_EXTERN char *uiEditableComboboxText(uiEditableCombobox *c); -_UI_EXTERN void uiEditableComboboxSetText(uiEditableCombobox *c, intmax_t n); +_UI_EXTERN void uiEditableComboboxSetText(uiEditableCombobox *c, const char *text); // TODO what do we call a function that sets the currently selected item and fills the text field with it? editable comboboxes have no consistent concept of selected item _UI_EXTERN void uiEditableComboboxOnChanged(uiEditableCombobox *c, void (*f)(uiEditableCombobox *c, void *data), void *data); _UI_EXTERN uiEditableCombobox *uiNewEditableCombobox(void); From ab35ae254f3f3752b2aa218f33fbb9f6da5c55d1 Mon Sep 17 00:00:00 2001 From: Kevin Wojniak Date: Mon, 23 May 2016 20:18:41 -0700 Subject: [PATCH 0106/1329] Enable OS X Travis tests --- .travis.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 82070d64..ec542ff9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,16 +1,14 @@ os: - linux + - osx # This makes us use Ubuntu 14 instead of 12 dist: trusty language: c script: - - sudo apt-get update - - sudo apt-get install libgtk-3-dev -y || sudo apt-cache search libgtk3 + - if [ "$TRAVIS_OS_NAME" == "linux" ]; then sudo apt-get update; fi + - if [ "$TRAVIS_OS_NAME" == "linux" ]; then sudo apt-get install libgtk-3-dev -y || sudo apt-cache search libgtk3; fi - make -f GNUmakefile - make -f GNUmakefile test - make -f GNUmakefile examples - -# TODO osx -# need to figure out how to force language: objective-c and turn off the apt-get rules From 2ebb9052cc8eb4be99cad5df3ae15d586504bab0 Mon Sep 17 00:00:00 2001 From: Kevin Wojniak Date: Mon, 23 May 2016 21:09:46 -0700 Subject: [PATCH 0107/1329] Fix crash when closing program on OS X This fixes #14. Autorelease pools need to be used to make sure objects get properly released. Unfortunately this produces a new error when menuManager gets deallocated, which I am looking at fixing: map.m:25:mapDestroy() POSSIBLE IMPLEMENTATION BUG; CONTACT ANDLABS: attempt to destroy map with items inside --- darwin/main.m | 20 +++++++++++++++++--- darwin/menu.m | 30 +++++++++++++++++++----------- 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/darwin/main.m b/darwin/main.m index fa0e5782..7ea860c9 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -2,6 +2,7 @@ #import "uipriv_darwin.h" static BOOL canQuit = NO; +static NSAutoreleasePool *globalPool; @implementation applicationClass @@ -72,7 +73,8 @@ static BOOL canQuit = NO; - (void)dealloc { - [self.menuManager release]; + // Apple docs: "Don't Use Accessor Methods in Initializer Methods and dealloc" + [_menuManager release]; [super dealloc]; } @@ -99,6 +101,10 @@ uiInitOptions options; const char *uiInit(uiInitOptions *o) { + globalPool = [[NSAutoreleasePool alloc] init]; + + @autoreleasepool { + options = *o; [applicationClass sharedApplication]; // don't check for a NO return; something (launch services?) causes running from application bundles to always return NO when asking to change activation policy, even if the change is to the same activation policy! @@ -109,21 +115,29 @@ const char *uiInit(uiInitOptions *o) initAlloc(); // always do this so we always have an application menu - appDelegate().menuManager = [menuManager new]; + appDelegate().menuManager = [[menuManager new] autorelease]; [realNSApp() setMainMenu:[appDelegate().menuManager makeMenubar]]; setupFontPanel(); return NULL; + + } // @autoreleasepool } void uiUninit(void) { + @autoreleasepool { + uninitMenus(); + [appDelegate() release]; [realNSApp() setDelegate:nil]; - [appDelegate() release]; [realNSApp() release]; uninitAlloc(); + + } // @autoreleasepool + + [globalPool release]; } void uiFreeInitError(const char *err) diff --git a/darwin/menu.m b/darwin/menu.m index 0f33df1d..12ce5c26 100644 --- a/darwin/menu.m +++ b/darwin/menu.m @@ -116,14 +116,14 @@ enum { NSMenu *servicesMenu; appName = [[NSProcessInfo processInfo] processName]; - appMenuItem = [[NSMenuItem alloc] initWithTitle:appName action:NULL keyEquivalent:@""]; - appMenu = [[NSMenu alloc] initWithTitle:appName]; + appMenuItem = [[[NSMenuItem alloc] initWithTitle:appName action:NULL keyEquivalent:@""] autorelease]; + appMenu = [[[NSMenu alloc] initWithTitle:appName] autorelease]; [appMenuItem setSubmenu:appMenu]; [menubar addItem:appMenuItem]; // first is About title = [@"About " stringByAppendingString:appName]; - item = [[NSMenuItem alloc] initWithTitle:title action:@selector(onClicked:) keyEquivalent:@""]; + item = [[[NSMenuItem alloc] initWithTitle:title action:@selector(onClicked:) keyEquivalent:@""] autorelease]; [item setTarget:self]; [appMenu addItem:item]; self.aboutItem = item; @@ -131,7 +131,7 @@ enum { [appMenu addItem:[NSMenuItem separatorItem]]; // next is Preferences - item = [[NSMenuItem alloc] initWithTitle:@"Preferences…" action:@selector(onClicked:) keyEquivalent:@","]; + item = [[[NSMenuItem alloc] initWithTitle:@"Preferences…" action:@selector(onClicked:) keyEquivalent:@","] autorelease]; [item setTarget:self]; [appMenu addItem:item]; self.preferencesItem = item; @@ -139,8 +139,8 @@ enum { [appMenu addItem:[NSMenuItem separatorItem]]; // next is Services - item = [[NSMenuItem alloc] initWithTitle:@"Services" action:NULL keyEquivalent:@""]; - servicesMenu = [[NSMenu alloc] initWithTitle:@"Services"]; + item = [[[NSMenuItem alloc] initWithTitle:@"Services" action:NULL keyEquivalent:@""] autorelease]; + servicesMenu = [[[NSMenu alloc] initWithTitle:@"Services"] autorelease]; [item setSubmenu:servicesMenu]; [realNSApp() setServicesMenu:servicesMenu]; [appMenu addItem:item]; @@ -149,14 +149,14 @@ enum { // next are the three hiding options title = [@"Hide " stringByAppendingString:appName]; - item = [[NSMenuItem alloc] initWithTitle:title action:@selector(hide:) keyEquivalent:@"h"]; + item = [[[NSMenuItem alloc] initWithTitle:title action:@selector(hide:) keyEquivalent:@"h"] autorelease]; // the .xib file says they go to -1 ("First Responder", which sounds wrong...) // to do that, we simply leave the target as nil [appMenu addItem:item]; - item = [[NSMenuItem alloc] initWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; + item = [[[NSMenuItem alloc] initWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"] autorelease]; [item setKeyEquivalentModifierMask:(NSAlternateKeyMask | NSCommandKeyMask)]; [appMenu addItem:item]; - item = [[NSMenuItem alloc] initWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; + item = [[[NSMenuItem alloc] initWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""] autorelease]; [appMenu addItem:item]; [appMenu addItem:[NSMenuItem separatorItem]]; @@ -164,7 +164,7 @@ enum { // and finally Quit // DON'T use @selector(terminate:) as the action; we handle termination ourselves title = [@"Quit " stringByAppendingString:appName]; - item = [[NSMenuItem alloc] initWithTitle:title action:@selector(onQuitClicked:) keyEquivalent:@"q"]; + item = [[[NSMenuItem alloc] initWithTitle:title action:@selector(onQuitClicked:) keyEquivalent:@"q"] autorelease]; [item setTarget:self]; [appMenu addItem:item]; self.quitItem = item; @@ -174,7 +174,7 @@ enum { { NSMenu *menubar; - menubar = [[NSMenu alloc] initWithTitle:@""]; + menubar = [[[NSMenu alloc] initWithTitle:@""] autorelease]; [self buildApplicationMenu:menubar]; return menubar; } @@ -222,6 +222,8 @@ void uiMenuItemSetChecked(uiMenuItem *item, int checked) static uiMenuItem *newItem(uiMenu *m, int type, const char *name) { + @autoreleasepool { + uiMenuItem *item; if (menusFinalized) @@ -257,6 +259,8 @@ static uiMenuItem *newItem(uiMenu *m, int type, const char *name) [m->items addObject:[NSValue valueWithPointer:item]]; return item; + + } // @autoreleasepool } uiMenuItem *uiMenuAppendItem(uiMenu *m, const char *name) @@ -294,6 +298,8 @@ void uiMenuAppendSeparator(uiMenu *m) uiMenu *uiNewMenu(const char *name) { + @autoreleasepool { + uiMenu *m; if (menusFinalized) @@ -316,6 +322,8 @@ uiMenu *uiNewMenu(const char *name) [menus addObject:[NSValue valueWithPointer:m]]; return m; + + } // @autoreleasepool } void finalizeMenus(void) From abb3c39c7829c595b323fdf09353c50d8d0507db Mon Sep 17 00:00:00 2001 From: Kevin Wojniak Date: Mon, 23 May 2016 21:11:12 -0700 Subject: [PATCH 0108/1329] Spaces to tabs --- darwin/main.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/darwin/main.m b/darwin/main.m index 7ea860c9..15c91fea 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -130,7 +130,7 @@ void uiUninit(void) @autoreleasepool { uninitMenus(); - [appDelegate() release]; + [appDelegate() release]; [realNSApp() setDelegate:nil]; [realNSApp() release]; uninitAlloc(); From 49e17cbfd71f35281045dd68a87086fd280be310 Mon Sep 17 00:00:00 2001 From: Kevin Wojniak Date: Mon, 23 May 2016 21:41:52 -0700 Subject: [PATCH 0109/1329] Fix "attempt to destroy map with items inside" when menuManager is deallocated Fixes #58. The map needs to have its contents properly freed which requires releasing the properly retaining the NSMenuItem object. --- darwin/main.m | 1 - darwin/map.m | 16 ++++++++++++++++ darwin/menu.m | 20 +++++++++++++++----- darwin/uipriv_darwin.h | 2 ++ 4 files changed, 33 insertions(+), 6 deletions(-) diff --git a/darwin/main.m b/darwin/main.m index 15c91fea..e8985e99 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -129,7 +129,6 @@ void uiUninit(void) { @autoreleasepool { - uninitMenus(); [appDelegate() release]; [realNSApp() setDelegate:nil]; [realNSApp() release]; diff --git a/darwin/map.m b/darwin/map.m index 67b5edb6..46a7b8d2 100644 --- a/darwin/map.m +++ b/darwin/map.m @@ -41,3 +41,19 @@ void mapDelete(struct mapTable *m, void *key) { NSMapRemove(m->m, key); } + +void mapWalk(struct mapTable *m, void (*f)(void *key, void *value)) +{ + NSMapEnumerator e = NSEnumerateMapTable(m->m); + void *k = NULL; + void *v = NULL; + while (NSNextMapEnumeratorPair(&e, &k, &v)) { + f(k, v); + } + NSEndMapTableEnumeration(&e); +} + +void mapReset(struct mapTable *m) +{ + NSResetMapTable(m->m); +} diff --git a/darwin/menu.m b/darwin/menu.m index 12ce5c26..fcf819da 100644 --- a/darwin/menu.m +++ b/darwin/menu.m @@ -27,6 +27,14 @@ enum { typeSeparator, }; +static void mapItemReleaser(void *key, void *value) +{ + uiMenuItem *item; + + item = (uiMenuItem *)value; + [item->item release]; +} + @implementation menuManager - (id)init @@ -43,6 +51,9 @@ enum { - (void)dealloc { + uninitMenus(); + mapWalk(self->items, mapItemReleaser); + mapReset(self->items); mapDestroy(self->items); [super dealloc]; } @@ -234,16 +245,16 @@ static uiMenuItem *newItem(uiMenu *m, int type, const char *name) item->type = type; switch (item->type) { case typeQuit: - item->item = appDelegate().menuManager.quitItem; + item->item = [appDelegate().menuManager.quitItem retain]; break; case typePreferences: - item->item = appDelegate().menuManager.preferencesItem; + item->item = [appDelegate().menuManager.preferencesItem retain]; break; case typeAbout: - item->item = appDelegate().menuManager.aboutItem; + item->item = [appDelegate().menuManager.aboutItem retain]; break; case typeSeparator: - item->item = [NSMenuItem separatorItem]; + item->item = [[NSMenuItem separatorItem] retain]; [m->menu addItem:item->item]; break; default: @@ -335,7 +346,6 @@ void uninitMenus(void) { if (menus == NULL) return; - // don't worry about the actual NSMenus and NSMenuItems; they'll be freed when we clean up the NSApplication [menus enumerateObjectsUsingBlock:^(id obj, NSUInteger index, BOOL *stop) { NSValue *v; uiMenu *m; diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 4a8b1bdd..865d3a48 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -89,6 +89,8 @@ extern void mapDestroy(struct mapTable *m); extern void *mapGet(struct mapTable *m, void *key); extern void mapSet(struct mapTable *m, void *key, void *value); extern void mapDelete(struct mapTable *m, void *key); +extern void mapWalk(struct mapTable *m, void (*f)(void *key, void *value)); +extern void mapReset(struct mapTable *m); // area.m extern int sendAreaEvents(NSEvent *); From b9bc71a9bab992d458486403954353edbe098ea1 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 24 May 2016 01:19:57 -0400 Subject: [PATCH 0110/1329] Clarified some stuff in darwin/editablecombo.m. --- darwin/editablecombo.m | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/darwin/editablecombo.m b/darwin/editablecombo.m index 5fa868fd..2387f0fe 100644 --- a/darwin/editablecombo.m +++ b/darwin/editablecombo.m @@ -1,6 +1,12 @@ // 14 august 2015 #import "uipriv_darwin.h" +// So why did I split uiCombobox into uiCombobox and uiEditableCombobox? Here's (90% of the; the other 10% is GTK+ events) answer: +// When you type a value into a NSComboBox that just happens to be in the list, it will autoselect that item! +// I can't seem to find a workaround. +// Fortunately, there's other weird behaviors that made this split worth it. +// And besides, selected items make little sense with editable comboboxes... you either separate or combine them with the text entry :V + // NSComboBoxes have no intrinsic width; we'll use the default Interface Builder width for them. #define comboboxWidth 96 @@ -114,8 +120,8 @@ void uiEditableComboboxSetText(uiEditableCombobox *c, const char *text) t = toNSString(text); [c->cb setStringValue:t]; - // do this just to keep synchronicity with the behavior of the real control - // for what it's worth, this automatic behavior when typing is the reason why this is separate from uiCombobox + // yes, let's imitate the behavior that caused uiEditableCombobox to be separate in the first place! + // just to avoid confusion when users see an option in the list in the text field but not selected in the list [c->cb selectItemWithObjectValue:t]; } From e8cedf502f52bb69ba37e6a300d05a7d4a2be84c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 24 May 2016 01:35:05 -0400 Subject: [PATCH 0111/1329] Split uiCombobox on GTK+. --- unix/GNUfiles.mk | 1 + unix/combobox.c | 15 ++------- unix/editablecombo.c | 79 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 13 deletions(-) create mode 100644 unix/editablecombo.c diff --git a/unix/GNUfiles.mk b/unix/GNUfiles.mk index 7b35e890..2650ffa2 100644 --- a/unix/GNUfiles.mk +++ b/unix/GNUfiles.mk @@ -16,6 +16,7 @@ CFILES += \ unix/drawmatrix.c \ unix/drawpath.c \ unix/drawtext.c \ + unix/editablecombo.c \ unix/entry.c \ unix/fontbutton.c \ unix/group.c \ diff --git a/unix/combobox.c b/unix/combobox.c index c50b161e..65c0e59a 100644 --- a/unix/combobox.c +++ b/unix/combobox.c @@ -13,7 +13,6 @@ struct uiCombobox { uiUnixControlAllDefaults(uiCombobox) -// TODO this is triggered when editing an editable combobox's text static void onChanged(GtkComboBox *cbox, gpointer data) { uiCombobox *c = uiCombobox(data); @@ -50,13 +49,13 @@ void uiComboboxOnSelected(uiCombobox *c, void (*f)(uiCombobox *c, void *data), v c->onSelectedData = data; } -static uiCombobox *finishNewCombobox(GtkWidget *(*newfunc)(void)) +uiCombobox *uiNewCombobox(void) { uiCombobox *c; uiUnixNewControl(uiCombobox, c); - c->widget = (*newfunc)(); + c->widget = gtk_combo_box_text_new(); c->combobox = GTK_COMBO_BOX(c->widget); c->comboboxText = GTK_COMBO_BOX_TEXT(c->widget); @@ -65,13 +64,3 @@ static uiCombobox *finishNewCombobox(GtkWidget *(*newfunc)(void)) return c; } - -uiCombobox *uiNewCombobox(void) -{ - return finishNewCombobox(gtk_combo_box_text_new); -} - -uiCombobox *uiNewEditableCombobox(void) -{ - return finishNewCombobox(gtk_combo_box_text_new_with_entry); -} diff --git a/unix/editablecombo.c b/unix/editablecombo.c new file mode 100644 index 00000000..7ee3829e --- /dev/null +++ b/unix/editablecombo.c @@ -0,0 +1,79 @@ +// 11 june 2015 +#include "uipriv_unix.h" + +struct uiEditableCombobox { + uiUnixControl c; + GtkWidget *widget; + GtkBin *bin; + GtkComboBox *combobox; + GtkComboBoxText *comboboxText; + void (*onChanged)(uiEditableCombobox *, void *); + void *onChangedData; + gulong onChangedSignal; +}; + +uiUnixControlAllDefaults(uiEditableCombobox) + +static void onChanged(GtkComboBox *cbox, gpointer data) +{ + uiEditableCombobox *c = uiEditableCombobox(data); + + (*(c->onChanged))(c, c->onChangedData); +} + +static void defaultOnChanged(uiEditableCombobox *c, void *data) +{ + // do nothing +} + +void uiEditableComboboxAppend(uiEditableCombobox *c, const char *text) +{ + gtk_combo_box_text_append(c->comboboxText, NULL, text); +} + +char *uiEditableComboboxText(uiEditableCombobox *c) +{ + char *s; + char *out; + + s = gtk_combo_box_text_get_active_text(c->comboboxText); + // s will always be non-NULL in the case of a combobox with an entry (according to the source code) + out = uiUnixStrdupText(s); + g_free(s); + return out; +} + +void uiEditableComboboxSetText(uiEditableCombobox *c, const char *text) +{ + GtkEntry *e; + + // we need to inhibit sending of ::changed because this WILL send a ::changed otherwise + g_signal_handler_block(c->combobox, c->onChangedSignal); + // since there isn't a gtk_combo_box_text_set_active_text()... + e = GTK_ENTRY(gtk_bin_get_child(c->bin)); + gtk_entry_set_text(e, text); + g_signal_handler_unblock(c->combobox, c->onChangedSignal); +} + +void uiEditableComboboxOnChanged(uiEditableCombobox *c, void (*f)(uiEditableCombobox *c, void *data), void *data) +{ + c->onChanged = f; + c->onChangedData = data; +} + +uiEditableCombobox *uiNewEditableCombobox(void) +{ + uiEditableCombobox *c; + + uiUnixNewControl(uiEditableCombobox, c); + + c->widget = gtk_combo_box_text_new_with_entry(); + c->bin = GTK_BIN(c->widget); + c->combobox = GTK_COMBO_BOX(c->widget); + c->comboboxText = GTK_COMBO_BOX_TEXT(c->widget); + + c->onChangedSignal = g_signal_connect(c->widget, "changed", G_CALLBACK(onChanged), c); + uiEditableComboboxOnChanged(c, defaultOnChanged, NULL); + + return c; +} From ce338bcaaf16d70c368f5484a13d2484a35ab70f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 24 May 2016 01:41:54 -0400 Subject: [PATCH 0112/1329] Quick fix. --- darwin/menu.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/darwin/menu.m b/darwin/menu.m index fcf819da..f899236a 100644 --- a/darwin/menu.m +++ b/darwin/menu.m @@ -32,7 +32,8 @@ static void mapItemReleaser(void *key, void *value) uiMenuItem *item; item = (uiMenuItem *)value; - [item->item release]; + // TODO this crashes for me on OS X El Capitan +// [item->item release]; } @implementation menuManager From 095e08bc7907a101b2193fa3ea6a9957bb81d336 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 24 May 2016 10:06:24 -0400 Subject: [PATCH 0113/1329] Added uiEditableCombobox on Windows. --- README.md | 3 + windows/GNUfiles.mk | 1 + windows/combobox.cpp | 15 +---- windows/editablecombo.cpp | 119 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 125 insertions(+), 13 deletions(-) create mode 100644 windows/editablecombo.cpp diff --git a/README.md b/README.md index e888d59c..c9bfc225 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,9 @@ This README is being written.
*Note that today's entry may be updated later today.* +* **24 May 2016** + * As promised, `uiCombobox` is now split into `uiCombobox` for non-editable comboboxes and `uiEditableCombobox` for editable comboboxes. Mind the function changes as well :) + * **23 May 2016** * Fixed surrogate pair drawing on OS X. diff --git a/windows/GNUfiles.mk b/windows/GNUfiles.mk index 2791405b..25fa9451 100644 --- a/windows/GNUfiles.mk +++ b/windows/GNUfiles.mk @@ -23,6 +23,7 @@ CXXFILES += \ windows/drawpath.cpp \ windows/drawtext.cpp \ windows/dwrite.cpp \ + windows/editablecombo.cpp \ windows/entry.cpp \ windows/events.cpp \ windows/fontbutton.cpp \ diff --git a/windows/combobox.cpp b/windows/combobox.cpp index ff5be8a1..cb09e00d 100644 --- a/windows/combobox.cpp +++ b/windows/combobox.cpp @@ -13,7 +13,6 @@ struct uiCombobox { void *onSelectedData; }; -// TODO: NOT triggered on entering text static BOOL onWM_COMMAND(uiControl *cc, HWND hwnd, WORD code, LRESULT *lResult) { uiCombobox *c = uiCombobox(cc); @@ -95,7 +94,7 @@ void uiComboboxOnSelected(uiCombobox *c, void (*f)(uiCombobox *c, void *data), v c->onSelectedData = data; } -static uiCombobox *finishNewCombobox(DWORD style) +uiCombobox *uiNewCombobox(void) { uiCombobox *c; @@ -103,7 +102,7 @@ static uiCombobox *finishNewCombobox(DWORD style) c->hwnd = uiWindowsEnsureCreateControlHWND(WS_EX_CLIENTEDGE, L"combobox", L"", - style | WS_TABSTOP, + CBS_DROPDOWNLIST | WS_TABSTOP, hInstance, NULL, TRUE); @@ -112,13 +111,3 @@ static uiCombobox *finishNewCombobox(DWORD style) return c; } - -uiCombobox *uiNewCombobox(void) -{ - return finishNewCombobox(CBS_DROPDOWNLIST); -} - -uiCombobox *uiNewEditableCombobox(void) -{ - return finishNewCombobox(CBS_DROPDOWN); -} diff --git a/windows/editablecombo.cpp b/windows/editablecombo.cpp new file mode 100644 index 00000000..85b0e8eb --- /dev/null +++ b/windows/editablecombo.cpp @@ -0,0 +1,119 @@ +// 20 may 2015 +#include "uipriv_windows.hpp" + +// TODO +// - is there extra space on the bottom? + +// we as Common Controls 6 users don't need to worry about the height of comboboxes; see http://blogs.msdn.com/b/oldnewthing/archive/2006/03/10/548537.aspx + +struct uiEditableCombobox { + uiWindowsControl c; + HWND hwnd; + void (*onChanged)(uiEditableCombobox *, void *); + void *onChangedData; +}; + +static BOOL onWM_COMMAND(uiControl *cc, HWND hwnd, WORD code, LRESULT *lResult) +{ + uiEditableCombobox *c = uiEditableCombobox(cc); + + if (code == CBN_SELCHANGE) { + // like on OS X, this is sent before the edit has been updated :( + // TODO error check + // TODO proper function for GetAncestor() + PostMessage(GetAncestor(hwnd, GA_PARENT), + WM_COMMAND, + MAKEWPARAM(GetWindowLongPtrW(hwnd, GWLP_ID), CBN_EDITCHANGE), + (LPARAM) hwnd); + *lResult = 0; + return TRUE; + } + if (code != CBN_EDITCHANGE) + return FALSE; + (*(c->onChanged))(c, c->onChangedData); + *lResult = 0; + return TRUE; +} + +void uiEditableComboboxDestroy(uiControl *cc) +{ + uiEditableCombobox *c = uiEditableCombobox(cc); + + uiWindowsUnregisterWM_COMMANDHandler(c->hwnd); + uiWindowsEnsureDestroyWindow(c->hwnd); + uiFreeControl(uiControl(c)); +} + +uiWindowsControlAllDefaultsExceptDestroy(uiEditableCombobox) + +// from http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing +#define comboboxWidth 107 /* this is actually the shorter progress bar width, but Microsoft only indicates as wide as necessary; TODO */ +#define comboboxHeight 14 + +static void uiEditableComboboxMinimumSize(uiWindowsControl *cc, intmax_t *width, intmax_t *height) +{ + uiEditableCombobox *c = uiEditableCombobox(cc); + uiWindowsSizing sizing; + int x, y; + + x = comboboxWidth; + y = comboboxHeight; + uiWindowsGetSizing(c->hwnd, &sizing); + uiWindowsSizingDlgUnitsToPixels(&sizing, &x, &y); + *width = x; + *height = y; +} + +static void defaultOnChanged(uiEditableCombobox *c, void *data) +{ + // do nothing +} + +void uiEditableComboboxAppend(uiEditableCombobox *c, const char *text) +{ + WCHAR *wtext; + LRESULT res; + + wtext = toUTF16(text); + res = SendMessageW(c->hwnd, CB_ADDSTRING, 0, (LPARAM) wtext); + if (res == (LRESULT) CB_ERR) + logLastError(L"error appending item to uiEditableCombobox"); + else if (res == (LRESULT) CB_ERRSPACE) + logLastError(L"memory exhausted appending item to uiEditableCombobox"); + uiFree(wtext); +} + +char *uiEditableComboboxText(uiEditableCombobox *c) +{ + return uiWindowsWindowText(c->hwnd); +} + +void uiEditableComboboxSetText(uiEditableCombobox *c, const char *text) +{ + // does not trigger any notifications + uiWindowsSetWindowText(c->hwnd, text); +} + +void uiEditableComboboxOnChanged(uiEditableCombobox *c, void (*f)(uiEditableCombobox *c, void *data), void *data) +{ + c->onChanged = f; + c->onChangedData = data; +} + +uiEditableCombobox *uiNewEditableCombobox(void) +{ + uiEditableCombobox *c; + + uiWindowsNewControl(uiEditableCombobox, c); + + c->hwnd = uiWindowsEnsureCreateControlHWND(WS_EX_CLIENTEDGE, + L"combobox", L"", + CBS_DROPDOWN | WS_TABSTOP, + hInstance, NULL, + TRUE); + + uiWindowsRegisterWM_COMMANDHandler(c->hwnd, onWM_COMMAND, uiControl(c)); + uiEditableComboboxOnChanged(c, defaultOnChanged, NULL); + + return c; +} From 7fdcbb53658188c4e715075209b8ae1995105db2 Mon Sep 17 00:00:00 2001 From: Kevin Wojniak Date: Tue, 24 May 2016 12:24:54 -0700 Subject: [PATCH 0114/1329] Fix test crash on exit on OS X --- darwin/menu.m | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/darwin/menu.m b/darwin/menu.m index f899236a..b20214ad 100644 --- a/darwin/menu.m +++ b/darwin/menu.m @@ -32,8 +32,7 @@ static void mapItemReleaser(void *key, void *value) uiMenuItem *item; item = (uiMenuItem *)value; - // TODO this crashes for me on OS X El Capitan -// [item->item release]; + [item->item release]; } @implementation menuManager @@ -52,10 +51,10 @@ static void mapItemReleaser(void *key, void *value) - (void)dealloc { - uninitMenus(); mapWalk(self->items, mapItemReleaser); mapReset(self->items); mapDestroy(self->items); + uninitMenus(); [super dealloc]; } From 45edacf8cad4b1439f8cd99f3eb356b8c1140290 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 24 May 2016 21:18:29 -0400 Subject: [PATCH 0115/1329] Consolidated GetAncestor() calls again. --- windows/colorbutton.cpp | 2 +- windows/editablecombo.cpp | 3 +-- windows/fontbutton.cpp | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/windows/colorbutton.cpp b/windows/colorbutton.cpp index c4c91701..462c0ddb 100644 --- a/windows/colorbutton.cpp +++ b/windows/colorbutton.cpp @@ -31,7 +31,7 @@ static BOOL onWM_COMMAND(uiControl *c, HWND hwnd, WORD code, LRESULT *lResult) if (code != BN_CLICKED) return FALSE; - parent = GetAncestor(b->hwnd, GA_ROOT); // TODO didn't we have a function for this + parent = parentToplevel(b->hwnd); rgba.r = b->r; rgba.g = b->g; rgba.b = b->b; diff --git a/windows/editablecombo.cpp b/windows/editablecombo.cpp index 85b0e8eb..4c3820d1 100644 --- a/windows/editablecombo.cpp +++ b/windows/editablecombo.cpp @@ -20,8 +20,7 @@ static BOOL onWM_COMMAND(uiControl *cc, HWND hwnd, WORD code, LRESULT *lResult) if (code == CBN_SELCHANGE) { // like on OS X, this is sent before the edit has been updated :( // TODO error check - // TODO proper function for GetAncestor() - PostMessage(GetAncestor(hwnd, GA_PARENT), + PostMessage(parentOf(hwnd), WM_COMMAND, MAKEWPARAM(GetWindowLongPtrW(hwnd, GWLP_ID), CBN_EDITCHANGE), (LPARAM) hwnd); diff --git a/windows/fontbutton.cpp b/windows/fontbutton.cpp index 323504a3..22e11abb 100644 --- a/windows/fontbutton.cpp +++ b/windows/fontbutton.cpp @@ -41,7 +41,7 @@ static BOOL onWM_COMMAND(uiControl *c, HWND hwnd, WORD code, LRESULT *lResult) if (code != BN_CLICKED) return FALSE; - parent = GetAncestor(b->hwnd, GA_ROOT); // TODO didn't we have a function for this + parent = parentToplevel(b->hwnd); if (showFontDialog(parent, &(b->params))) { updateFontButtonLabel(b); (*(b->onChanged))(b, b->onChangedData); From a9e731ed5968f812fd7ecbaa37c7c89a1dcec744 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 24 May 2016 22:14:05 -0400 Subject: [PATCH 0116/1329] Added uiMainStep() and implemented it on Windows. --- ui.h | 1 + windows/main.cpp | 82 ++++++++++++++++++++++++++++++++++++------------ 2 files changed, 63 insertions(+), 20 deletions(-) diff --git a/ui.h b/ui.h index c1d7bb31..adc0b327 100644 --- a/ui.h +++ b/ui.h @@ -36,6 +36,7 @@ _UI_EXTERN void uiUninit(void); _UI_EXTERN void uiFreeInitError(const char *err); _UI_EXTERN void uiMain(void); +_UI_EXTERN int uiMainStep(int wait); _UI_EXTERN void uiQuit(void); _UI_EXTERN void uiQueueMain(void (*f)(void *data), void *data); diff --git a/windows/main.cpp b/windows/main.cpp index 2480cd46..3edd8635 100644 --- a/windows/main.cpp +++ b/windows/main.cpp @@ -41,31 +41,73 @@ void unregisterMessageFilter(void) logLastError(L"error unregistering libui message filter"); } -// TODO http://blogs.msdn.com/b/oldnewthing/archive/2005/04/08/406509.aspx when adding accelerators, TranslateAccelerators() before IsDialogMessage() +// LONGTERM http://blogs.msdn.com/b/oldnewthing/archive/2005/04/08/406509.aspx when adding accelerators, TranslateAccelerators() before IsDialogMessage() + +static void processMessage(MSG *msg) +{ + HWND active; + + // TODO really active? or parentToplevel(msg->hwnd)? + active = GetActiveWindow(); + if (active != NULL) + // TODO find documentation that says IsDialogMessage() calls CallMsgFilter() for us, because that's what's happening + if (IsDialogMessage(active, msg) != 0) + return; + TranslateMessage(msg); + DispatchMessageW(msg); +} + +static int waitMessage(MSG *msg) +{ + int res; + + res = GetMessageW(msg, NULL, 0, 0); + if (res < 0) { + logLastError(L"error calling GetMessage()"); + return 0; // bail out on error + } + return res != 0; // returns false on WM_QUIT +} void uiMain(void) { - MSG msg; - int res; - HWND active; + while (uiMainStep(1)) + ; +} - for (;;) { - res = GetMessageW(&msg, NULL, 0, 0); - if (res < 0) { - logLastError(L"error calling GetMessage()"); - break; // bail out on error - } - if (res == 0) // WM_QUIT - break; - // TODO really active? or parentToplevel(msg->hwnd)? - active = GetActiveWindow(); - if (active != NULL) - // TODO find documentation that says IsDialogMessage() calls CallMsgFilter() for us, because that's what's happening - if (IsDialogMessage(active, &msg) != 0) - continue; - TranslateMessage(&msg); - DispatchMessageW(&msg); +static int peekMessage(MSG *msg) +{ + BOOL res; + + res = PeekMessageW(msg, NULL, 0, 0, PM_REMOVE); + if (res == 0) + return 2; // no message available + if (msg->message != WM_QUIT) + return 1; // a message + return 0; // WM_QUIT +} + +int uiMainStep(int wait) +{ + MSG msg; + + if (wait) { + if (!waitMessage(&msg)) + return 0; + processMessage(&msg); + return 1; } + + // don't wait for a message + switch (peekMessage(&msg)) { + case 0: // quit + // TODO PostQuitMessage() again? + return 0; + case 1: // process a message + processMessage(&msg); + // fall out to the case for no message + } + return 1; // no message } void uiQuit(void) From 8bf24cb7913762b6e226b36176e6f883e470d37e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 24 May 2016 22:29:33 -0400 Subject: [PATCH 0117/1329] Implemented uiMainStep() on GTK+. --- unix/main.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/unix/main.c b/unix/main.c index 50f308bd..bfd05448 100644 --- a/unix/main.c +++ b/unix/main.c @@ -34,6 +34,16 @@ void uiMain(void) gtk_main(); } +int uiMainStep(int wait) +{ + gboolean block; + + block = FALSE; + if (wait) + block = TRUE; + return gtk_main_iteration_do(block) == FALSE; +} + // gtk_main_quit() may run immediately, or it may wait for other pending events; "it depends" (thanks mclasen in irc.gimp.net/#gtk+) // PostQuitMessage() on Windows always waits, so we must do so too // we'll do it by using an idle callback From 558e56c124391b5ee179b6782704c0186843f4b7 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 24 May 2016 22:44:40 -0400 Subject: [PATCH 0118/1329] Implemented uiMainStep() on OS X. --- darwin/main.m | 80 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 23 deletions(-) diff --git a/darwin/main.m b/darwin/main.m index e8985e99..c34ede14 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -104,38 +104,33 @@ const char *uiInit(uiInitOptions *o) globalPool = [[NSAutoreleasePool alloc] init]; @autoreleasepool { + options = *o; + [applicationClass sharedApplication]; + // don't check for a NO return; something (launch services?) causes running from application bundles to always return NO when asking to change activation policy, even if the change is to the same activation policy! + // see https://github.com/andlabs/ui/issues/6 + [realNSApp() setActivationPolicy:NSApplicationActivationPolicyRegular]; + [realNSApp() setDelegate:[appDelegate new]]; - options = *o; - [applicationClass sharedApplication]; - // don't check for a NO return; something (launch services?) causes running from application bundles to always return NO when asking to change activation policy, even if the change is to the same activation policy! - // see https://github.com/andlabs/ui/issues/6 - [realNSApp() setActivationPolicy:NSApplicationActivationPolicyRegular]; - [realNSApp() setDelegate:[appDelegate new]]; + initAlloc(); - initAlloc(); + // always do this so we always have an application menu + appDelegate().menuManager = [[menuManager new] autorelease]; + [realNSApp() setMainMenu:[appDelegate().menuManager makeMenubar]]; - // always do this so we always have an application menu - appDelegate().menuManager = [[menuManager new] autorelease]; - [realNSApp() setMainMenu:[appDelegate().menuManager makeMenubar]]; + setupFontPanel(); - setupFontPanel(); - - return NULL; - - } // @autoreleasepool + return NULL; + } } void uiUninit(void) { @autoreleasepool { - - [appDelegate() release]; - [realNSApp() setDelegate:nil]; - [realNSApp() release]; - uninitAlloc(); - - } // @autoreleasepool - + [appDelegate() release]; + [realNSApp() setDelegate:nil]; + [realNSApp() release]; + uninitAlloc(); + } [globalPool release]; } @@ -148,6 +143,45 @@ void uiMain(void) [realNSApp() run]; } +// see also: +// - http://www.cocoawithlove.com/2009/01/demystifying-nsapplication-by.html +// - https://github.com/gnustep/gui/blob/master/Source/NSApplication.m +int uiMainStep(int wait) +{ + NSDate *expire; + NSEvent *e; + NSEventType type; + + @autoreleasepool { + // ProPuke did this in his original PR requesting this + // I'm not sure if this will work, but I assume it will... + expire = [NSDate distantPast]; + if (wait) // but this is normal so it will work + expire = [NSDate distantFuture]; + + if (![realNSApp() isRunning]) + return 0; + + e = [realNSApp() nextEventMatchingMask:NSAnyEventMask + untilDate:expire + inMode:NSDefaultRunLoopMode + dequeue:YES]; + if (e == nil) + return 1; + + type = [e type]; + [realNSApp() sendEvent:e]; + [realNSApp() updateWindows]; + + // GNUstep does this + // it also updates the Services menu but there doesn't seem to be a public API for that so + if (type != NSPeriodic && type != NSMouseMoved) + [[realNSApp() mainMenu] update]; + + return 1; + } +} + // TODO make this delayed void uiQuit(void) { From 31d6939c48f7496cfa5c06125c0dc91d915c5f6d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 24 May 2016 22:46:53 -0400 Subject: [PATCH 0119/1329] README updates. --- README.md | 1 + darwin/menu.m | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c9bfc225..c1b7379c 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ This README is being written.
* **24 May 2016** * As promised, `uiCombobox` is now split into `uiCombobox` for non-editable comboboxes and `uiEditableCombobox` for editable comboboxes. Mind the function changes as well :) + * There is a new function `uiMainStep()`, which runs one iteration of the main loop. It takes a single boolean argument, indicating whether to wait for an event to occur or not. It returns true if an event was processed (or if no event is available if you don't want to wait) and false if the event loop was told to stop (for instance, `uiQuit()` was called). * **23 May 2016** * Fixed surrogate pair drawing on OS X. diff --git a/darwin/menu.m b/darwin/menu.m index b20214ad..4c6ec519 100644 --- a/darwin/menu.m +++ b/darwin/menu.m @@ -54,7 +54,7 @@ static void mapItemReleaser(void *key, void *value) mapWalk(self->items, mapItemReleaser); mapReset(self->items); mapDestroy(self->items); - uninitMenus(); + uninitMenus(); [super dealloc]; } From ff1971d8d62fb433cd814019b26b6dce257cdae2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 24 May 2016 23:13:44 -0400 Subject: [PATCH 0120/1329] More longterming. --- darwin/GNUfiles.mk | 2 +- darwin/GNUosspecific.mk | 2 +- darwin/util.m | 2 +- unix/GNUfiles.mk | 2 +- unix/GNUosspecific.mk | 2 +- windows/GNUfiles.mk | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/darwin/GNUfiles.mk b/darwin/GNUfiles.mk index ae39d70e..c420d02d 100644 --- a/darwin/GNUfiles.mk +++ b/darwin/GNUfiles.mk @@ -38,7 +38,7 @@ MFILES += \ HFILES += \ darwin/uipriv_darwin.h -# TODO split into a separate file or put in GNUmakefile.libui somehow? +# LONGTERM split into a separate file or put in GNUmakefile.libui somehow? # flags for Cocoa LDFLAGS += \ diff --git a/darwin/GNUosspecific.mk b/darwin/GNUosspecific.mk index 39acdaa4..cc13d1b1 100644 --- a/darwin/GNUosspecific.mk +++ b/darwin/GNUosspecific.mk @@ -9,5 +9,5 @@ USESSONAME = 1 SOVERSION = $(SOVERSIONA) SONAMEEXT = .$(SOVERSION)$(LIBSUFFIX) # note the explicit need for @rpath -# TODO -current_version, -compatibility_version +# LONGTERM -current_version, -compatibility_version SONAMEFLAG = -Wl,-install_name,@rpath/ diff --git a/darwin/util.m b/darwin/util.m index 7031e9f8..ab873906 100644 --- a/darwin/util.m +++ b/darwin/util.m @@ -1,7 +1,7 @@ // 7 april 2015 #import "uipriv_darwin.h" -// TODO do we really want to do this? make it an option? +// LONGTERM do we really want to do this? make it an option? void disableAutocorrect(NSTextView *tv) { [tv setEnabledTextCheckingTypes:0]; diff --git a/unix/GNUfiles.mk b/unix/GNUfiles.mk index 2650ffa2..f442a4e2 100644 --- a/unix/GNUfiles.mk +++ b/unix/GNUfiles.mk @@ -39,7 +39,7 @@ HFILES += \ unix/draw.h \ unix/uipriv_unix.h -# TODO split into a separate file or put in GNUmakefile.libui somehow? +# LONGTERM split into a separate file or put in GNUmakefile.libui somehow? # flags for GTK+ CFLAGS += \ diff --git a/unix/GNUosspecific.mk b/unix/GNUosspecific.mk index 7d8cb726..5f6fc938 100644 --- a/unix/GNUosspecific.mk +++ b/unix/GNUosspecific.mk @@ -5,7 +5,7 @@ LIBSUFFIX = .so OSHSUFFIX = .h TOOLCHAIN = gcc -# TODO clean up all the NAMEs and SUFFIXs and NOSOSUFFIXs or whatever it was +# LONGTERM clean up all the NAMEs and SUFFIXs and NOSOSUFFIXs or whatever it was USESSONAME = 1 SOVERSION = $(SOVERSION0) SONAMEEXT = $(LIBSUFFIX).$(SOVERSION) diff --git a/windows/GNUfiles.mk b/windows/GNUfiles.mk index 25fa9451..886aea58 100644 --- a/windows/GNUfiles.mk +++ b/windows/GNUfiles.mk @@ -63,7 +63,7 @@ HFILES += \ RCFILES += \ windows/resources.rc -# TODO split into a separate file or put in GNUmakefile.libui somehow? +# LONGTERM split into a separate file or put in GNUmakefile.libui somehow? # flags for the Windows API # TODO prune this list From bef8c4663fad37d08e1189754a98a43bcfbe5168 Mon Sep 17 00:00:00 2001 From: Kevin Wojniak Date: Tue, 24 May 2016 20:17:08 -0700 Subject: [PATCH 0121/1329] Fix additional OS X memory management issues Some of these were found via clang's analyzer --- darwin/alloc.m | 3 --- darwin/button.m | 2 +- darwin/checkbox.m | 2 +- darwin/combobox.m | 2 +- darwin/editablecombo.m | 2 +- darwin/entry.m | 2 +- darwin/main.m | 29 ++++++++++++++++++----------- darwin/slider.m | 2 +- darwin/tab.m | 15 +++++---------- darwin/window.m | 2 +- 10 files changed, 30 insertions(+), 31 deletions(-) diff --git a/darwin/alloc.m b/darwin/alloc.m index 0ce46404..887d658d 100644 --- a/darwin/alloc.m +++ b/darwin/alloc.m @@ -23,13 +23,10 @@ void initAlloc(void) void uninitAlloc(void) { NSMutableString *str; - NSUInteger i; NSValue *v; // delegates might have mapTables allocated // TODO verify they are empty - for (i = 0; i < [delegates count]; i++) - [[delegates objectAtIndex:i] release]; [delegates release]; if ([allocations count] == 0) { [allocations release]; diff --git a/darwin/button.m b/darwin/button.m index be45dba9..baccabbb 100644 --- a/darwin/button.m +++ b/darwin/button.m @@ -103,7 +103,7 @@ uiButton *uiNewButton(const char *text) uiDarwinSetControlFont(b->button, NSRegularControlSize); if (buttonDelegate == nil) { - buttonDelegate = [buttonDelegateClass new]; + buttonDelegate = [[buttonDelegateClass new] autorelease]; [delegates addObject:buttonDelegate]; } [buttonDelegate registerButton:b]; diff --git a/darwin/checkbox.m b/darwin/checkbox.m index 3914cfd4..cdacf419 100644 --- a/darwin/checkbox.m +++ b/darwin/checkbox.m @@ -118,7 +118,7 @@ uiCheckbox *uiNewCheckbox(const char *text) uiDarwinSetControlFont(c->button, NSRegularControlSize); if (checkboxDelegate == nil) { - checkboxDelegate = [checkboxDelegateClass new]; + checkboxDelegate = [[checkboxDelegateClass new] autorelease]; [delegates addObject:checkboxDelegate]; } [checkboxDelegate registerCheckbox:c]; diff --git a/darwin/combobox.m b/darwin/combobox.m index e956762e..dd67e503 100644 --- a/darwin/combobox.m +++ b/darwin/combobox.m @@ -133,7 +133,7 @@ uiCombobox *uiNewCombobox(void) options:nil]; if (comboboxDelegate == nil) { - comboboxDelegate = [comboboxDelegateClass new]; + comboboxDelegate = [[comboboxDelegateClass new] autorelease]; [delegates addObject:comboboxDelegate]; } [comboboxDelegate registerCombobox:c]; diff --git a/darwin/editablecombo.m b/darwin/editablecombo.m index 2387f0fe..67259521 100644 --- a/darwin/editablecombo.m +++ b/darwin/editablecombo.m @@ -175,7 +175,7 @@ uiEditableCombobox *uiNewEditableCombobox(void) uiDarwinSetControlFont(c->cb, NSRegularControlSize); if (comboboxDelegate == nil) { - comboboxDelegate = [editableComboboxDelegateClass new]; + comboboxDelegate = [[editableComboboxDelegateClass new] autorelease]; [delegates addObject:comboboxDelegate]; } [comboboxDelegate registerCombobox:c]; diff --git a/darwin/entry.m b/darwin/entry.m index 43f450d4..179c81e1 100644 --- a/darwin/entry.m +++ b/darwin/entry.m @@ -158,7 +158,7 @@ uiEntry *uiNewEntry(void) e->textfield = newEditableTextField(); if (entryDelegate == nil) { - entryDelegate = [entryDelegateClass new]; + entryDelegate = [[entryDelegateClass new] autorelease]; [delegates addObject:entryDelegate]; } [entryDelegate registerEntry:e]; diff --git a/darwin/main.m b/darwin/main.m index c34ede14..dd7f2d60 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -3,6 +3,8 @@ static BOOL canQuit = NO; static NSAutoreleasePool *globalPool; +static applicationClass* app; +static appDelegate* delegate; @implementation applicationClass @@ -101,15 +103,14 @@ uiInitOptions options; const char *uiInit(uiInitOptions *o) { - globalPool = [[NSAutoreleasePool alloc] init]; - @autoreleasepool { options = *o; - [applicationClass sharedApplication]; + app = [[applicationClass sharedApplication] retain]; // don't check for a NO return; something (launch services?) causes running from application bundles to always return NO when asking to change activation policy, even if the change is to the same activation policy! // see https://github.com/andlabs/ui/issues/6 [realNSApp() setActivationPolicy:NSApplicationActivationPolicyRegular]; - [realNSApp() setDelegate:[appDelegate new]]; + delegate = [appDelegate new]; + [realNSApp() setDelegate:delegate]; initAlloc(); @@ -118,20 +119,26 @@ const char *uiInit(uiInitOptions *o) [realNSApp() setMainMenu:[appDelegate().menuManager makeMenubar]]; setupFontPanel(); - - return NULL; } + + globalPool = [[NSAutoreleasePool alloc] init]; + + return NULL; } void uiUninit(void) { - @autoreleasepool { - [appDelegate() release]; - [realNSApp() setDelegate:nil]; - [realNSApp() release]; - uninitAlloc(); + if (!globalPool) { + userbug("You must call uiInit() first!"); } [globalPool release]; + + @autoreleasepool { + [delegate release]; + [realNSApp() setDelegate:nil]; + [app release]; + uninitAlloc(); + } } void uiFreeInitError(const char *err) diff --git a/darwin/slider.m b/darwin/slider.m index 4cee88c5..53274574 100644 --- a/darwin/slider.m +++ b/darwin/slider.m @@ -138,7 +138,7 @@ uiSlider *uiNewSlider(intmax_t min, intmax_t max) [cell setSliderType:NSLinearSlider]; if (sliderDelegate == nil) { - sliderDelegate = [sliderDelegateClass new]; + sliderDelegate = [[sliderDelegateClass new] autorelease]; [delegates addObject:sliderDelegate]; } [sliderDelegate registerSlider:s]; diff --git a/darwin/tab.m b/darwin/tab.m index 0e7a0d19..24060309 100644 --- a/darwin/tab.m +++ b/darwin/tab.m @@ -35,8 +35,8 @@ struct uiTab { { self = [super init]; if (self != nil) { - self->view = v; - self->pageID = o; + self->view = [v retain]; + self->pageID = [o retain]; } return self; } @@ -189,14 +189,14 @@ void uiTabInsertAt(uiTab *t, const char *name, uintmax_t n, uiControl *child) uiControlSetParent(child, uiControl(t)); - view = [[NSView alloc] initWithFrame:NSZeroRect]; + view = [[[NSView alloc] initWithFrame:NSZeroRect] autorelease]; // TODO if we turn off the autoresizing mask, nothing shows up; didn't this get documented somewhere? uiDarwinControlSetSuperview(uiDarwinControl(child), view); uiDarwinControlSyncEnableState(uiDarwinControl(child), uiControlEnabledToUser(uiControl(t))); // the documentation says these can be nil but the headers say these must not be; let's be safe and make them non-nil anyway pageID = [NSObject new]; - page = [[tabPage alloc] initWithView:view pageID:pageID]; + page = [[[tabPage alloc] initWithView:view pageID:pageID] autorelease]; page.c = child; // don't hug, just in case we're a stretchy tab @@ -206,16 +206,11 @@ void uiTabInsertAt(uiTab *t, const char *name, uintmax_t n, uiControl *child) uiDarwinControlSetHuggingPriority(uiDarwinControl(page.c), NSLayoutPriorityDefaultLow, NSLayoutConstraintOrientationVertical); [t->pages insertObject:page atIndex:n]; - [page release]; // no need for initial reference - i = [[NSTabViewItem alloc] initWithIdentifier:pageID]; + i = [[[NSTabViewItem alloc] initWithIdentifier:pageID] autorelease]; [i setLabel:toNSString(name)]; [i setView:view]; [t->tabview insertTabViewItem:i atIndex:n]; - // TODO release i? - - [pageID release]; // no need for initial reference - [view release]; tabRelayout(t); } diff --git a/darwin/window.m b/darwin/window.m index 58bd63ac..e5ebc93f 100644 --- a/darwin/window.m +++ b/darwin/window.m @@ -258,7 +258,7 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) [w->window setReleasedWhenClosed:NO]; if (windowDelegate == nil) { - windowDelegate = [windowDelegateClass new]; + windowDelegate = [[windowDelegateClass new] autorelease]; [delegates addObject:windowDelegate]; } [windowDelegate registerWindow:w]; From 278227a3d77eeac793c3e1bcb37fed61f544cd8a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 24 May 2016 23:27:56 -0400 Subject: [PATCH 0122/1329] More announcements. --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index c1b7379c..1c5cc166 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,10 @@ This README is being written.
## Announcements +* **24 May 2016** + * You can now help choose [a potential new build system for libui](https://github.com/andlabs/libui/issues/62). + * Tomorrow I will decide if OS X 10.7 will also be dropped alongside GTK+ 3.4-3.8 this Saturday. Stay tuned. + * **22 May 2016** * Two more open questions I'd like your feedback on are available [here](https://github.com/andlabs/libui/issues/48) and [here](https://github.com/andlabs/libui/issues/25). * Sometime in the next 48 hours (before 23:59 EDT on 24 May 2016) I will split `uiCombobox` into two separate controls, `uiCombobox` and `uiEditableCombobox`, each with slightly different events and "selected item" mechanics. Prepare your existing code. From 47632fe560103cdea42d64200d847ed1a18ab34f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 25 May 2016 01:08:55 -0400 Subject: [PATCH 0123/1329] Switched text layout attributes to grapheme-based indices on Windows, just like on OS X. --- windows/GNUfiles.mk | 4 +- windows/drawtext.cpp | 6 ++- windows/grapheme.cpp | 80 ++++++++++++++++++++++++++++++++++++++ windows/uipriv_windows.hpp | 4 ++ windows/winapi.hpp | 1 + 5 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 windows/grapheme.cpp diff --git a/windows/GNUfiles.mk b/windows/GNUfiles.mk index 886aea58..8ca7ad97 100644 --- a/windows/GNUfiles.mk +++ b/windows/GNUfiles.mk @@ -28,6 +28,7 @@ CXXFILES += \ windows/events.cpp \ windows/fontbutton.cpp \ windows/fontdialog.cpp \ + windows/grapheme.cpp \ windows/group.cpp \ windows/init.cpp \ windows/label.cpp \ @@ -66,9 +67,10 @@ RCFILES += \ # LONGTERM split into a separate file or put in GNUmakefile.libui somehow? # flags for the Windows API +# notice that usp10.lib comes before gdi32.lib # TODO prune this list LDFLAGS += \ - user32.lib kernel32.lib gdi32.lib comctl32.lib uxtheme.lib msimg32.lib comdlg32.lib d2d1.lib dwrite.lib ole32.lib oleaut32.lib oleacc.lib uuid.lib + user32.lib kernel32.lib usp10.lib gdi32.lib comctl32.lib uxtheme.lib msimg32.lib comdlg32.lib d2d1.lib dwrite.lib ole32.lib oleaut32.lib oleacc.lib uuid.lib # flags for building a shared library LDFLAGS += \ diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 7a03f65d..98bb745d 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -337,6 +337,7 @@ struct layoutAttr { struct uiDrawTextLayout { WCHAR *text; size_t textlen; + size_t *graphemes; double width; IDWriteTextFormat *format; std::vector *attrs; @@ -366,6 +367,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(const char *text, uiDrawTextFont *defaultF layout->text = toUTF16(text); layout->textlen = wcslen(layout->text); + layout->graphemes = graphemes(layout->text); uiDrawTextLayoutSetWidth(layout, width); @@ -425,8 +427,8 @@ IDWriteTextLayout *prepareLayout(uiDrawTextLayout *layout, ID2D1RenderTarget *rt logHRESULT(L"error creating IDWriteTextLayout", hr); for (const struct layoutAttr &attr : *(layout->attrs)) { - range.startPosition = attr.start; - range.length = attr.end - attr.start; + range.startPosition = layout->graphemes[attr.start]; + range.length = layout->graphemes[attr.end] - layout->graphemes[attr.start]; switch (attr.type) { case layoutAttrColor: if (rt == NULL) // determining extents, not drawing diff --git a/windows/grapheme.cpp b/windows/grapheme.cpp new file mode 100644 index 00000000..355e4037 --- /dev/null +++ b/windows/grapheme.cpp @@ -0,0 +1,80 @@ +// 25 may 2016 +#include "uipriv_windows.hpp" + +// We could use CharNext() to generate grapheme cluster boundaries, but it doesn't handle surrogate pairs properly (see http://archives.miloush.net/michkap/archive/2008/12/16/9223301.html). +// So let's use Uniscribe (see http://archives.miloush.net/michkap/archive/2005/01/14/352802.html) +// See also http://www.catch22.net/tuts/uniscribe-mysteries and http://www.catch22.net/tuts/keyboard-navigation for more details. + +static HRESULT itemize(WCHAR *msg, size_t len, SCRIPT_ITEM **out, int *outn) +{ + SCRIPT_CONTROL sc; + SCRIPT_STATE ss; + SCRIPT_ITEM *items; + size_t maxItems; + int n; + HRESULT hr; + + // make sure these are zero-initialized to avoid mangling the text + ZeroMemory(&sc, sizeof (SCRIPT_CONTROL)); + ZeroMemory(&ss, sizeof (SCRIPT_STATE)); + + maxItems = len + 2; + for (;;) { + items = new SCRIPT_ITEM[maxItems]; + hr = ScriptItemize(msg, len, + maxItems, + &sc, &ss, + items, &n); + if (hr == S_OK) + break; + // otherwise either an error or not enough room + delete[] items; + if (hr != E_OUTOFMEMORY) + return hr; + maxItems *= 2; // add some more and try again + } + + *out = items; + *outn = n; + return S_OK; +} + +size_t *graphemes(WCHAR *msg) +{ + size_t len; + SCRIPT_ITEM *items; + int i, n; + size_t *out; + size_t *op; + SCRIPT_LOGATTR *logattr; + int j, nn; + HRESULT hr; + + len = wcslen(msg); + hr = itemize(msg, len, &items, &n); + if (hr != S_OK) + logHRESULT(L"error itemizing string for finding grapheme cluster boundaries", hr); + + // should be enough; 2 more just to be safe + out = (size_t *) uiAlloc((len + 2) * sizeof (size_t), "size_t[]"); + op = out; + + // note that there are actually n + 1 elements in items + for (i = 0; i < n; i++) { + nn = items[i + 1].iCharPos - items[i].iCharPos; + logattr = new SCRIPT_LOGATTR[nn]; + hr = ScriptBreak(msg + items[i].iCharPos, nn, + &(items[i].a), logattr); + if (hr != S_OK) + logHRESULT(L"error breaking string for finding grapheme cluster boundaries", hr); + for (j = 0; j < nn; j++) + if (logattr[j].fCharStop != 0) + *op++ = items[i].iCharPos + j; + delete[] logattr; + } + // and handle the last item for the end of the string + *op++ = items[i].iCharPos; + + delete[] items; + return out; +} diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index f08ae042..abf72943 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -140,6 +140,10 @@ extern BOOL showColorDialog(HWND parent, struct colorDialogRGBA *c); // sizing.cpp extern void getSizing(HWND hwnd, uiWindowsSizing *sizing, HFONT font); +// grapheme.cpp +extern size_t *graphemes(WCHAR *msg); + + diff --git a/windows/winapi.hpp b/windows/winapi.hpp index ed30b782..246760da 100644 --- a/windows/winapi.hpp +++ b/windows/winapi.hpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include From 474d3fb1d5b6fb00a88b8d45f66d3f6847454240 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 25 May 2016 01:10:47 -0400 Subject: [PATCH 0124/1329] Oops, forgot to free something in the previous commit. --- windows/drawtext.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 98bb745d..f5e872b7 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -380,6 +380,7 @@ void uiDrawFreeTextLayout(uiDrawTextLayout *layout) { delete layout->attrs; layout->format->Release(); + uiFree(layout->graphemes); uiFree(layout->text); uiFree(layout); } From a8aa84217285a4cdbef8f45c7ace6454f199c93a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 25 May 2016 01:15:43 -0400 Subject: [PATCH 0125/1329] More TODOs. --- test/page10.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/page10.c b/test/page10.c index 74de4fba..6bc71085 100644 --- a/test/page10.c +++ b/test/page10.c @@ -1,6 +1,8 @@ // 22 december 2015 #include "test.h" +// TODO change addLeading into a checkbox for colorizing that one z + static uiEntry *textString; static uiFontButton *textFontButton; static uiColorButton *textColorButton; From ac652f0690ac9796fa0650204824af1a71cae18c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 25 May 2016 01:52:53 -0400 Subject: [PATCH 0126/1329] Did most of the work for grapheme cluster boundary based text layout characters on GTK+. --- unix/GNUfiles.mk | 1 + unix/drawtext.c | 28 +++++------------ unix/graphemes.c | 41 +++++++++++++++++++++++++ unix/uipriv_unix.h | 6 ++++ windows/GNUfiles.mk | 2 +- windows/{grapheme.cpp => graphemes.cpp} | 0 windows/uipriv_windows.hpp | 2 +- 7 files changed, 58 insertions(+), 22 deletions(-) create mode 100644 unix/graphemes.c rename windows/{grapheme.cpp => graphemes.cpp} (100%) diff --git a/unix/GNUfiles.mk b/unix/GNUfiles.mk index f442a4e2..b4a1784c 100644 --- a/unix/GNUfiles.mk +++ b/unix/GNUfiles.mk @@ -19,6 +19,7 @@ CFILES += \ unix/editablecombo.c \ unix/entry.c \ unix/fontbutton.c \ + unix/graphemes.c \ unix/group.c \ unix/label.c \ unix/main.c \ diff --git a/unix/drawtext.c b/unix/drawtext.c index 86932191..192e1327 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -173,34 +173,22 @@ void uiDrawTextFontGetMetrics(uiDrawTextFont *font, uiDrawTextFontMetrics *metri // note: PangoCairoLayouts are tied to a given cairo_t, so we can't store one in this device-independent structure struct uiDrawTextLayout { char *s; - ptrdiff_t *charsToBytes; + PangoGlyphString *glyphString; PangoFont *defaultFont; double width; PangoAttrList *attrs; }; -static ptrdiff_t *computeCharsToBytes(const char *s) -{ - ptrdiff_t *charsToBytes; - glong i, charlen; - - // we INCLUDE the null terminator as a character in the string - // g_utf8_offset_to_pointer() doesn't stop on null terminator so this should work - charlen = g_utf8_strlen(s, -1) + 1; - charsToBytes = (ptrdiff_t *) uiAlloc(charlen * sizeof (ptrdiff_t), "ptrdiff_t[]"); - // TODO speed this up by not needing to scan the whole string again - for (i = 0; i < charlen; i++) - charsToBytes[i] = g_utf8_offset_to_pointer(s, i) - s; - return charsToBytes; -} - uiDrawTextLayout *uiDrawNewTextLayout(const char *text, uiDrawTextFont *defaultFont, double width) { uiDrawTextLayout *layout; + PangoContext *context; layout = uiNew(uiDrawTextLayout); layout->s = g_strdup(text); - layout->charsToBytes = computeCharsToBytes(layout->s); + context = mkGenericPangoCairoContext(); + layout->glyphString = graphemes(layout->s, context); + g_object_unref(context); layout->defaultFont = defaultFont->f; g_object_ref(layout->defaultFont); // retain a copy uiDrawTextLayoutSetWidth(layout, width); @@ -212,7 +200,7 @@ void uiDrawFreeTextLayout(uiDrawTextLayout *layout) { pango_attr_list_unref(layout->attrs); g_object_unref(layout->defaultFont); - uiFree(layout->charsToBytes); + pango_glyph_string_free(layout->glyphString); g_free(layout->s); uiFree(layout); } @@ -279,8 +267,8 @@ void uiDrawText(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout) static void addAttr(uiDrawTextLayout *layout, PangoAttribute *attr, intmax_t startChar, intmax_t endChar) { - attr->start_index = layout->charsToBytes[startChar]; - attr->end_index = layout->charsToBytes[endChar]; + attr->start_index = layout->glyphString->log_clusters[startChar]; + attr->end_index = layout->glyphString->log_clusters[endChar]; pango_attr_list_insert(layout->attrs, attr); // pango_attr_list_insert() takes attr; we don't free it } diff --git a/unix/graphemes.c b/unix/graphemes.c new file mode 100644 index 00000000..a4cf0cc5 --- /dev/null +++ b/unix/graphemes.c @@ -0,0 +1,41 @@ +// 25 may 2016 +#include "uipriv_unix.H" + +// TODO this breaks for non-BMP characters? + +struct gparam { + const char *text; + PangoGlyphString *glyphString; +}; + +static void runItem(gpointer it, gpointer data) +{ + PangoItem *item = (PangoItem *) it; + struct gparam *gparam = (struct gparam *) data; + + pango_shape(gparam->text + item->offset, + item->length, + &(item->analysis), + gparam->glyphString); + pango_item_free(item); +} + +PangoGlyphString *graphemes(const char *text, PangoContext *context) +{ + size_t len; + GList *list; + struct gparam gparam; + + len = strlen(text); + list = pango_itemize(context, + text, 0, len, + NULL, NULL); + + gparam.text = text; + gparam.glyphString = pango_glyph_string_new(); + + g_list_foreach(list, runItem, &gparam); + + g_list_free(list); + return gparam.glyphString; +} diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index 67e48a85..f85874be 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -7,6 +7,7 @@ #include #include // see drawtext.c #include +#include #include "../ui.h" #include "../ui_unix.h" #include "../common/uipriv.h" @@ -42,5 +43,10 @@ extern void childSetMargined(struct child *c, int margined); // draw.c extern uiDrawContext *newContext(cairo_t *); extern void freeContext(uiDrawContext *); + +// drawtext.c extern uiDrawTextFont *mkTextFont(PangoFont *f, gboolean add); extern PangoFont *pangoDescToPangoFont(PangoFontDescription *pdesc); + +// graphemes.c +extern PangoGlyphString *graphemes(const char *text, PangoContext *context); diff --git a/windows/GNUfiles.mk b/windows/GNUfiles.mk index 8ca7ad97..905d6597 100644 --- a/windows/GNUfiles.mk +++ b/windows/GNUfiles.mk @@ -28,7 +28,7 @@ CXXFILES += \ windows/events.cpp \ windows/fontbutton.cpp \ windows/fontdialog.cpp \ - windows/grapheme.cpp \ + windows/graphemes.cpp \ windows/group.cpp \ windows/init.cpp \ windows/label.cpp \ diff --git a/windows/grapheme.cpp b/windows/graphemes.cpp similarity index 100% rename from windows/grapheme.cpp rename to windows/graphemes.cpp diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index abf72943..3e6a74c1 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -140,7 +140,7 @@ extern BOOL showColorDialog(HWND parent, struct colorDialogRGBA *c); // sizing.cpp extern void getSizing(HWND hwnd, uiWindowsSizing *sizing, HFONT font); -// grapheme.cpp +// graphemes.cpp extern size_t *graphemes(WCHAR *msg); From 5272e749c027c5b096fc2b5ed02a5ea6ef1fa38d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 25 May 2016 15:07:32 -0400 Subject: [PATCH 0127/1329] Fixed grapheme stuff on GTK+. --- README.md | 3 +++ unix/drawtext.c | 10 ++++----- unix/graphemes.c | 54 +++++++++++++++++++--------------------------- unix/uipriv_unix.h | 2 +- 4 files changed, 31 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 1c5cc166..1840a0b0 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,9 @@ This README is being written.
*Note that today's entry may be updated later today.* +* **25 May 2016** + * uiDrawTextLayout attributes are now specified in units of *graphemes* on all platforms. This means characters as seen from a user's perspective, not Unicode codepoints or UTF-8 bytes. So a long string of combining marker codepoints after one codepoint would still count as one grapheme. + * **24 May 2016** * As promised, `uiCombobox` is now split into `uiCombobox` for non-editable comboboxes and `uiEditableCombobox` for editable comboboxes. Mind the function changes as well :) * There is a new function `uiMainStep()`, which runs one iteration of the main loop. It takes a single boolean argument, indicating whether to wait for an event to occur or not. It returns true if an event was processed (or if no event is available if you don't want to wait) and false if the event loop was told to stop (for instance, `uiQuit()` was called). diff --git a/unix/drawtext.c b/unix/drawtext.c index 192e1327..a9b856fe 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -173,7 +173,7 @@ void uiDrawTextFontGetMetrics(uiDrawTextFont *font, uiDrawTextFontMetrics *metri // note: PangoCairoLayouts are tied to a given cairo_t, so we can't store one in this device-independent structure struct uiDrawTextLayout { char *s; - PangoGlyphString *glyphString; + ptrdiff_t *graphemes; PangoFont *defaultFont; double width; PangoAttrList *attrs; @@ -187,7 +187,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(const char *text, uiDrawTextFont *defaultF layout = uiNew(uiDrawTextLayout); layout->s = g_strdup(text); context = mkGenericPangoCairoContext(); - layout->glyphString = graphemes(layout->s, context); + layout->graphemes = graphemes(layout->s, context); g_object_unref(context); layout->defaultFont = defaultFont->f; g_object_ref(layout->defaultFont); // retain a copy @@ -200,7 +200,7 @@ void uiDrawFreeTextLayout(uiDrawTextLayout *layout) { pango_attr_list_unref(layout->attrs); g_object_unref(layout->defaultFont); - pango_glyph_string_free(layout->glyphString); + uiFree(layout->graphemes); g_free(layout->s); uiFree(layout); } @@ -267,8 +267,8 @@ void uiDrawText(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout) static void addAttr(uiDrawTextLayout *layout, PangoAttribute *attr, intmax_t startChar, intmax_t endChar) { - attr->start_index = layout->glyphString->log_clusters[startChar]; - attr->end_index = layout->glyphString->log_clusters[endChar]; + attr->start_index = layout->graphemes[startChar]; + attr->end_index = layout->graphemes[endChar]; pango_attr_list_insert(layout->attrs, attr); // pango_attr_list_insert() takes attr; we don't free it } diff --git a/unix/graphemes.c b/unix/graphemes.c index a4cf0cc5..6d101a92 100644 --- a/unix/graphemes.c +++ b/unix/graphemes.c @@ -1,41 +1,31 @@ // 25 may 2016 #include "uipriv_unix.H" -// TODO this breaks for non-BMP characters? - -struct gparam { - const char *text; - PangoGlyphString *glyphString; -}; - -static void runItem(gpointer it, gpointer data) +ptrdiff_t *graphemes(const char *text, PangoContext *context) { - PangoItem *item = (PangoItem *) it; - struct gparam *gparam = (struct gparam *) data; - - pango_shape(gparam->text + item->offset, - item->length, - &(item->analysis), - gparam->glyphString); - pango_item_free(item); -} - -PangoGlyphString *graphemes(const char *text, PangoContext *context) -{ - size_t len; - GList *list; - struct gparam gparam; + size_t len, lenchars; + PangoLogAttr *logattrs; + ptrdiff_t *out; + ptrdiff_t *op; + size_t i; len = strlen(text); - list = pango_itemize(context, - text, 0, len, - NULL, NULL); + lenchars = g_utf8_strlen(text, -1); + logattrs = (PangoLogAttr *) uiAlloc((lenchars + 1) * sizeof (PangoLogAttr), "PangoLogAttr[]"); + pango_get_log_attrs(text, len, + -1, NULL, + logattrs, lenchars + 1); - gparam.text = text; - gparam.glyphString = pango_glyph_string_new(); + // should be more than enough + out = (ptrdiff_t *) uiAlloc((lenchars + 2) * sizeof (ptrdiff_t), "ptrdiff_t[]"); + op = out; + for (i = 0; i < lenchars; i++) + if (logattrs[i].is_cursor_position != 0) + // TODO optimize this + *op++ = g_utf8_offset_to_pointer(text, i) - text; + // and do the last one + *op++ = len; - g_list_foreach(list, runItem, &gparam); - - g_list_free(list); - return gparam.glyphString; + uiFree(logattrs); + return out; } diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index f85874be..cd89e869 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -49,4 +49,4 @@ extern uiDrawTextFont *mkTextFont(PangoFont *f, gboolean add); extern PangoFont *pangoDescToPangoFont(PangoFontDescription *pdesc); // graphemes.c -extern PangoGlyphString *graphemes(const char *text, PangoContext *context); +extern ptrdiff_t *graphemes(const char *text, PangoContext *context); From 01e126f9cc0d42e5d4f707827d39efd6d4cb20f5 Mon Sep 17 00:00:00 2001 From: mogucpp Date: Thu, 26 May 2016 09:17:34 +0800 Subject: [PATCH 0128/1329] fix header name --- unix/graphemes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unix/graphemes.c b/unix/graphemes.c index 6d101a92..a2c47b72 100644 --- a/unix/graphemes.c +++ b/unix/graphemes.c @@ -1,5 +1,5 @@ // 25 may 2016 -#include "uipriv_unix.H" +#include "uipriv_unix.h" ptrdiff_t *graphemes(const char *text, PangoContext *context) { From d7b537e9edade5cd26f166f1577adc36c1636a66 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 25 May 2016 21:37:45 -0400 Subject: [PATCH 0129/1329] Came to a consensus about combobox fonts on OS X. --- darwin/combobox.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/darwin/combobox.m b/darwin/combobox.m index e956762e..7e047e9c 100644 --- a/darwin/combobox.m +++ b/darwin/combobox.m @@ -113,7 +113,9 @@ uiCombobox *uiNewCombobox(void) [c->pb setPreferredEdge:NSMinYEdge]; pbcell = (NSPopUpButtonCell *) [c->pb cell]; [pbcell setArrowPosition:NSPopUpArrowAtBottom]; - // TODO font + // the font defined by Interface Builder is Menu 13, which is lol + // just use the regular control size for consistency + uiDarwinSetControlFont(c->pb, NSRegularControlSize); // NSPopUpButton doesn't work like a combobox // - it automatically selects the first item From 965c2712e40cc9599fc0f77e249ef1fce956a54c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 25 May 2016 23:18:21 -0400 Subject: [PATCH 0130/1329] Updated bindings list based on wiki. --- README.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1840a0b0..06c1ff39 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,21 @@ Needs to be written. Consult ui.h and the examples for details for now. libui was originally written as part of my [package ui for Go](https://github.com/andlabs/ui). Now that libui is separate, package ui has become a binding to libui. As such, package ui is the only official binding. Other people have made bindings to other languages: -* TODO list them here + +Language | Bindings +--- | --- +**C#/.NET** | [LibUI.Binding](https://github.com/NattyNarwhal/LibUI.Binding) +**Crystal** | [libui.cr](https://github.com/Fusion/libui.cr) +**D** | [DerelictLibui](https://github.com/Extrawurst/DerelictLibui) +**Haskell** | [libui-haskell](https://github.com/ajnsit/libui-haskell) +**JavaScript** | [libui-node](https://github.com/parro-it/libui-node), [libui.js (merged into libui-node?)](https://github.com/mavenave/libui.js) +**Lua** | [libuilua](https://github.com/zevv/libuilua), [libui-lua](https://github.com/mdombroski/libui-lua) +**Node.js** | [libui-node](https://github.com/mavenave/libui.js) +**Python** | [pylibui](https://github.com/joaoventura/pylibui) +**Ruby** | [libui-ruby](https://github.com/jamescook/libui-ruby) +**Rust** | [libui-rs](https://github.com/pcwalton/libui-rs) + +TODO there was another C# binding somewhere ## Screenshots From 0b7b9a94e71e4623610ee9621ff16310c793e5bb Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 25 May 2016 23:35:09 -0400 Subject: [PATCH 0131/1329] More bindings stuff. --- README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 06c1ff39..fabcc379 100644 --- a/README.md +++ b/README.md @@ -75,19 +75,17 @@ Other people have made bindings to other languages: Language | Bindings --- | --- -**C#/.NET** | [LibUI.Binding](https://github.com/NattyNarwhal/LibUI.Binding) +**C#/.net** | [LibUI.Binding](https://github.com/NattyNarwhal/LibUI.Binding), [SharpUI](https://github.com/benpye/sharpui/) **Crystal** | [libui.cr](https://github.com/Fusion/libui.cr) **D** | [DerelictLibui](https://github.com/Extrawurst/DerelictLibui) **Haskell** | [libui-haskell](https://github.com/ajnsit/libui-haskell) -**JavaScript** | [libui-node](https://github.com/parro-it/libui-node), [libui.js (merged into libui-node?)](https://github.com/mavenave/libui.js) +**JavaScript** | [libui.js (merged into libui-node?)](https://github.com/mavenave/libui.js) **Lua** | [libuilua](https://github.com/zevv/libuilua), [libui-lua](https://github.com/mdombroski/libui-lua) -**Node.js** | [libui-node](https://github.com/mavenave/libui.js) +**Node.js** | [libui-node](https://github.com/parro-it/libui-node) **Python** | [pylibui](https://github.com/joaoventura/pylibui) **Ruby** | [libui-ruby](https://github.com/jamescook/libui-ruby) **Rust** | [libui-rs](https://github.com/pcwalton/libui-rs) -TODO there was another C# binding somewhere - ## Screenshots From examples/controlgallery: From 3896861f39f2a64dbc57f5134c87e09ebc2cbbc7 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 26 May 2016 17:44:35 -0400 Subject: [PATCH 0132/1329] Added another facet to page 10's tests: whether the letter after the combined one gets colored. --- test/page10.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/test/page10.c b/test/page10.c index 6bc71085..d7f26a73 100644 --- a/test/page10.c +++ b/test/page10.c @@ -1,14 +1,12 @@ // 22 december 2015 #include "test.h" -// TODO change addLeading into a checkbox for colorizing that one z - static uiEntry *textString; static uiFontButton *textFontButton; static uiColorButton *textColorButton; static uiEntry *textWidth; static uiButton *textApply; -static uiCheckbox *addLeading; +static uiCheckbox *noZ; static uiArea *textArea; static uiAreaHandler textAreaHandler; @@ -84,9 +82,10 @@ static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *dp) uiDrawTextLayoutSetColor(layout, 5, 6, 1, 0, 0.5, 0.5); - uiDrawTextLayoutSetColor(layout, - 6, 7, - 0.5, 0, 1, 0.5); + if (!uiCheckboxChecked(noZ)) + uiDrawTextLayoutSetColor(layout, + 6, 7, + 0.5, 0, 1, 0.5); uiDrawText(dp->Context, 10, 10 + height + height, layout); uiDrawFreeTextLayout(layout); @@ -124,7 +123,7 @@ static void onColorChanged(uiColorButton *b, void *data) uiAreaQueueRedrawAll(textArea); } -static void onTextApply(uiButton *b, void *data) +static void onNoZ(uiCheckbox *b, void *data) { uiAreaQueueRedrawAll(textArea); } @@ -158,16 +157,15 @@ uiBox *makePage10(void) uiBoxAppend(vbox, uiControl(hbox), 0); textApply = uiNewButton("Apply"); - uiButtonOnClicked(textApply, onTextApply, NULL); uiBoxAppend(hbox, uiControl(textApply), 1); textWidth = uiNewEntry(); uiEntrySetText(textWidth, "-1"); uiBoxAppend(hbox, uiControl(textWidth), 1); - addLeading = uiNewCheckbox("Add Leading"); - uiCheckboxSetChecked(addLeading, 1); - uiBoxAppend(hbox, uiControl(addLeading), 0); + noZ = uiNewCheckbox("No Z Color"); + uiCheckboxOnToggled(noZ, onNoZ, NULL); + uiBoxAppend(hbox, uiControl(noZ), 0); textAreaHandler.Draw = handlerDraw; textAreaHandler.MouseEvent = handlerMouseEvent; From 88e0a9c165dfd25c622be44a2f874a7dee17788f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 26 May 2016 20:48:49 -0400 Subject: [PATCH 0133/1329] Fixed the botched formula for attribute substring length on OS X. --- darwin/drawtext.m | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 9061603d..8ec17ba5 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -636,8 +636,7 @@ static CFRange charsToRange(uiDrawTextLayout *layout, intmax_t startChar, intmax start = layout->charsToRanges[startChar]; end = layout->charsToRanges[endChar]; out.location = start.location; - // - 1 to avoid including the first code point after end - out.length = (end.location + end.length - 1) - start.location; + out.length = end.location - start.location; return out; } From baf46c5434f743b0635ed507ae6927876e951ab7 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 26 May 2016 21:09:42 -0400 Subject: [PATCH 0134/1329] Removed an unnecessary TODO; mapTables cannot be freed unless empty anyway. --- darwin/alloc.m | 2 -- 1 file changed, 2 deletions(-) diff --git a/darwin/alloc.m b/darwin/alloc.m index 887d658d..e271b90e 100644 --- a/darwin/alloc.m +++ b/darwin/alloc.m @@ -25,8 +25,6 @@ void uninitAlloc(void) NSMutableString *str; NSValue *v; - // delegates might have mapTables allocated - // TODO verify they are empty [delegates release]; if ([allocations count] == 0) { [allocations release]; From 834cc1229993597b5a90710c796d0828d312b490 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 26 May 2016 21:25:32 -0400 Subject: [PATCH 0135/1329] Named the margins and padding. Made them functions that can take parameters in the future to allow more complex spacing options in the future. --- README.md | 3 +++ darwin/autolayout.m | 12 +++++++++++- darwin/box.m | 2 +- ui_darwin.h | 4 ++++ 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fabcc379..f7d98762 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,9 @@ This README is being written.
*Note that today's entry may be updated later today.* +* **26 May 2016** + * Two OS X-specific functions have been added: `uiDarwinMarginAmount()` and `uiDarwinPaddingAmount()`. These return the amount of margins and padding, respectively, to give to a control, and are intended for container implementations. These are suitable for the constant of a NSLayoutConstraint. They both take a pointer parameter that is reserved for future use and should be `NULL`. + * **25 May 2016** * uiDrawTextLayout attributes are now specified in units of *graphemes* on all platforms. This means characters as seen from a user's perspective, not Unicode codepoints or UTF-8 bytes. So a long string of combining marker codepoints after one codepoint would still count as one grapheme. diff --git a/darwin/autolayout.m b/darwin/autolayout.m index 81530e75..99f71130 100644 --- a/darwin/autolayout.m +++ b/darwin/autolayout.m @@ -18,6 +18,16 @@ NSLayoutConstraint *mkConstraint(id view1, NSLayoutAttribute attr1, NSLayoutRela return constraint; } +CGFloat uiDarwinMarginAmount(void *reserved) +{ + return 20.0; +} + +CGFloat uiDarwinPaddingAmount(void *reserved) +{ + return 8.0; +} + // this is needed for NSSplitView to work properly; see http://stackoverflow.com/questions/34574478/how-can-i-set-the-position-of-a-nssplitview-nowadays-setpositionofdivideratind (stal in irc.freenode.net/#macdev came up with the exact combination) // turns out it also works on NSTabView and NSBox too, possibly others! // and for bonus points, it even seems to fix unsatisfiable-constraint-autoresizing-mask issues with NSTabView and NSBox too!!! this is nuts @@ -31,7 +41,7 @@ static CGFloat margins(int margined) { if (!margined) return 0.0; - return 20.0; // TODO named constant + return uiDarwinMarginAmount(NULL); } void singleChildConstraintsEstablish(struct singleChildConstraints *c, NSView *contentView, NSView *childView, BOOL hugsTrailing, BOOL hugsBottom, int margined, NSString *desc) diff --git a/darwin/box.m b/darwin/box.m index a5cf41b0..7e480285 100644 --- a/darwin/box.m +++ b/darwin/box.m @@ -147,7 +147,7 @@ struct uiBox { { if (!self->padded) return 0.0; - return 8.0; // TODO named constant + return uiDarwinPaddingAmount(NULL); } // TODO something about spinbox hugging diff --git a/ui_darwin.h b/ui_darwin.h index 387e6829..a9b49cba 100644 --- a/ui_darwin.h +++ b/ui_darwin.h @@ -200,6 +200,10 @@ _UI_EXTERN BOOL uiDarwinShouldStopSyncEnableState(uiDarwinControl *, BOOL); // TODO document _UI_EXTERN void uiDarwinNotifyEdgeHuggingChanged(uiDarwinControl *); +// TODO document +_UI_EXTERN CGFloat uiDarwinMarginAmount(void *reserved); +_UI_EXTERN CGFloat uiDarwinPaddingAmount(void *reserved); + #ifdef __cplusplus } #endif From b082f69e3fa576c0f2696d760a68eb68fb6caffe Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 26 May 2016 21:27:13 -0400 Subject: [PATCH 0136/1329] More TODOs. --- ui_darwin.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ui_darwin.h b/ui_darwin.h index a9b49cba..81e71f2c 100644 --- a/ui_darwin.h +++ b/ui_darwin.h @@ -201,6 +201,7 @@ _UI_EXTERN BOOL uiDarwinShouldStopSyncEnableState(uiDarwinControl *, BOOL); _UI_EXTERN void uiDarwinNotifyEdgeHuggingChanged(uiDarwinControl *); // TODO document +// TODO document that values should not be cached _UI_EXTERN CGFloat uiDarwinMarginAmount(void *reserved); _UI_EXTERN CGFloat uiDarwinPaddingAmount(void *reserved); From f8311aa14db5787b9b3fb3aa8bc7f479783db96b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 26 May 2016 22:00:08 -0400 Subject: [PATCH 0137/1329] More TODO resolution. --- darwin/area.m | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/darwin/area.m b/darwin/area.m index 3caf1599..ea604863 100644 --- a/darwin/area.m +++ b/darwin/area.m @@ -52,7 +52,6 @@ struct uiArea { dp.AreaWidth = 0; dp.AreaHeight = 0; if (!a->scrolling) { - // TODO frame or bounds? dp.AreaWidth = [self frame].size.width; dp.AreaHeight = [self frame].size.height; } @@ -137,7 +136,6 @@ struct uiArea { me.AreaWidth = 0; me.AreaHeight = 0; if (!a->scrolling) { - // TODO frame or bounds? me.AreaWidth = [self frame].size.width; me.AreaHeight = [self frame].size.height; } @@ -182,11 +180,9 @@ struct uiArea { me.Held1To64 |= 2; if (buttonNumber != 3 && (pmb & 2) != 0) me.Held1To64 |= 4; - // buttons 4..64 + // buttons 4..32 + // https://developer.apple.com/library/mac/documentation/Carbon/Reference/QuartzEventServicesRef/index.html#//apple_ref/c/tdef/CGMouseButton says Quartz only supports up to 32 buttons max = 32; - // TODO are the upper 32 bits just mirrored? -// if (sizeof (NSUInteger) == 8) -// max = 64; for (i = 4; i <= max; i++) { uint64_t j; From 5040af30dd1f47fe7caba53bf2d10a0197168c87 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 26 May 2016 22:42:35 -0400 Subject: [PATCH 0138/1329] Really calculated info about scrollviews this time. --- sv/normal | 25 +++++++++++++++++++++++++ sv/outlineview | 25 +++++++++++++++++++++++++ sv/tableview | 25 +++++++++++++++++++++++++ sv/textview | 25 +++++++++++++++++++++++++ 4 files changed, 100 insertions(+) create mode 100644 sv/normal create mode 100644 sv/outlineview create mode 100644 sv/tableview create mode 100644 sv/textview diff --git a/sv/normal b/sv/normal new file mode 100644 index 00000000..8b6af87e --- /dev/null +++ b/sv/normal @@ -0,0 +1,25 @@ +2016-05-26 22:38:12.877 svtest[81790:544681] backgroundColor NSNamedColorSpace System controlColor +2016-05-26 22:38:12.877 svtest[81790:544681] drawsBackground 1 +2016-05-26 22:38:12.877 svtest[81790:544681] borderType 2 +2016-05-26 22:38:12.877 svtest[81790:544681] documentCursor (null) +2016-05-26 22:38:12.877 svtest[81790:544681] hasHorizontalScroller 1 +2016-05-26 22:38:12.877 svtest[81790:544681] hasVerticalScroller 1 +2016-05-26 22:38:12.877 svtest[81790:544681] autohidesScrollers 0 +2016-05-26 22:38:12.877 svtest[81790:544681] hasHorizontalRuler 0 +2016-05-26 22:38:12.878 svtest[81790:544681] hasVerticalRuler 0 +2016-05-26 22:38:12.878 svtest[81790:544681] rulersVisible 0 +2016-05-26 22:38:12.878 svtest[81790:544681] 10.10 autoAdjContentInsets 1 +2016-05-26 22:38:12.878 svtest[81790:544681] scrollerKnobStyle 0 +2016-05-26 22:38:12.878 svtest[81790:544681] scrollerStyle 1 +2016-05-26 22:38:12.878 svtest[81790:544681] horizontalLineScroll 10 +2016-05-26 22:38:12.878 svtest[81790:544681] verticalLineScroll 10 +2016-05-26 22:38:12.886 svtest[81790:544681] horizontalPageScroll 10 +2016-05-26 22:38:12.886 svtest[81790:544681] verticalPageScroll 10 +2016-05-26 22:38:12.886 svtest[81790:544681] scrollsDynamically 1 +2016-05-26 22:38:12.886 svtest[81790:544681] findBarPosition 1 +2016-05-26 22:38:12.886 svtest[81790:544681] usesPredomAxisScroll 0 +2016-05-26 22:38:12.886 svtest[81790:544681] horizontalElasticity 0 +2016-05-26 22:38:12.886 svtest[81790:544681] verticalElasticity 0 +2016-05-26 22:38:12.887 svtest[81790:544681] 10.8 allowsMagnification 0 +2016-05-26 22:38:12.887 svtest[81790:544681] 10.8 maxMagnification 4 +2016-05-26 22:38:12.887 svtest[81790:544681] 10.8 minMagnification 0.25 diff --git a/sv/outlineview b/sv/outlineview new file mode 100644 index 00000000..67a30877 --- /dev/null +++ b/sv/outlineview @@ -0,0 +1,25 @@ +2016-05-26 22:42:16.208 svtest[82103:547159] backgroundColor NSNamedColorSpace System controlBackgroundColor +2016-05-26 22:42:16.208 svtest[82103:547159] drawsBackground 1 +2016-05-26 22:42:16.208 svtest[82103:547159] borderType 2 +2016-05-26 22:42:16.208 svtest[82103:547159] documentCursor (null) +2016-05-26 22:42:16.209 svtest[82103:547159] hasHorizontalScroller 1 +2016-05-26 22:42:16.209 svtest[82103:547159] hasVerticalScroller 1 +2016-05-26 22:42:16.209 svtest[82103:547159] autohidesScrollers 1 +2016-05-26 22:42:16.209 svtest[82103:547159] hasHorizontalRuler 0 +2016-05-26 22:42:16.209 svtest[82103:547159] hasVerticalRuler 0 +2016-05-26 22:42:16.209 svtest[82103:547159] rulersVisible 0 +2016-05-26 22:42:16.209 svtest[82103:547159] 10.10 autoAdjContentInsets 1 +2016-05-26 22:42:16.209 svtest[82103:547159] scrollerKnobStyle 0 +2016-05-26 22:42:16.209 svtest[82103:547159] scrollerStyle 1 +2016-05-26 22:42:16.209 svtest[82103:547159] horizontalLineScroll 19 +2016-05-26 22:42:16.209 svtest[82103:547159] verticalLineScroll 19 +2016-05-26 22:42:16.217 svtest[82103:547159] horizontalPageScroll 10 +2016-05-26 22:42:16.218 svtest[82103:547159] verticalPageScroll 10 +2016-05-26 22:42:16.218 svtest[82103:547159] scrollsDynamically 1 +2016-05-26 22:42:16.218 svtest[82103:547159] findBarPosition 1 +2016-05-26 22:42:16.218 svtest[82103:547159] usesPredomAxisScroll 0 +2016-05-26 22:42:16.218 svtest[82103:547159] horizontalElasticity 0 +2016-05-26 22:42:16.218 svtest[82103:547159] verticalElasticity 0 +2016-05-26 22:42:16.218 svtest[82103:547159] 10.8 allowsMagnification 0 +2016-05-26 22:42:16.218 svtest[82103:547159] 10.8 maxMagnification 4 +2016-05-26 22:42:16.218 svtest[82103:547159] 10.8 minMagnification 0.25 diff --git a/sv/tableview b/sv/tableview new file mode 100644 index 00000000..558b6e19 --- /dev/null +++ b/sv/tableview @@ -0,0 +1,25 @@ +2016-05-26 22:41:26.514 svtest[82032:546554] backgroundColor NSNamedColorSpace System controlBackgroundColor +2016-05-26 22:41:26.514 svtest[82032:546554] drawsBackground 1 +2016-05-26 22:41:26.514 svtest[82032:546554] borderType 2 +2016-05-26 22:41:26.514 svtest[82032:546554] documentCursor (null) +2016-05-26 22:41:26.515 svtest[82032:546554] hasHorizontalScroller 1 +2016-05-26 22:41:26.515 svtest[82032:546554] hasVerticalScroller 1 +2016-05-26 22:41:26.515 svtest[82032:546554] autohidesScrollers 1 +2016-05-26 22:41:26.515 svtest[82032:546554] hasHorizontalRuler 0 +2016-05-26 22:41:26.516 svtest[82032:546554] hasVerticalRuler 0 +2016-05-26 22:41:26.516 svtest[82032:546554] rulersVisible 0 +2016-05-26 22:41:26.516 svtest[82032:546554] 10.10 autoAdjContentInsets 1 +2016-05-26 22:41:26.516 svtest[82032:546554] scrollerKnobStyle 0 +2016-05-26 22:41:26.516 svtest[82032:546554] scrollerStyle 1 +2016-05-26 22:41:26.516 svtest[82032:546554] horizontalLineScroll 19 +2016-05-26 22:41:26.516 svtest[82032:546554] verticalLineScroll 19 +2016-05-26 22:41:26.528 svtest[82032:546554] horizontalPageScroll 10 +2016-05-26 22:41:26.528 svtest[82032:546554] verticalPageScroll 10 +2016-05-26 22:41:26.528 svtest[82032:546554] scrollsDynamically 1 +2016-05-26 22:41:26.528 svtest[82032:546554] findBarPosition 1 +2016-05-26 22:41:26.528 svtest[82032:546554] usesPredomAxisScroll 0 +2016-05-26 22:41:26.528 svtest[82032:546554] horizontalElasticity 0 +2016-05-26 22:41:26.528 svtest[82032:546554] verticalElasticity 0 +2016-05-26 22:41:26.528 svtest[82032:546554] 10.8 allowsMagnification 0 +2016-05-26 22:41:26.528 svtest[82032:546554] 10.8 maxMagnification 4 +2016-05-26 22:41:26.528 svtest[82032:546554] 10.8 minMagnification 0.25 diff --git a/sv/textview b/sv/textview new file mode 100644 index 00000000..e6363625 --- /dev/null +++ b/sv/textview @@ -0,0 +1,25 @@ +2016-05-26 22:40:02.050 svtest[81927:545793] backgroundColor NSCalibratedWhiteColorSpace 1 1 +2016-05-26 22:40:02.050 svtest[81927:545793] drawsBackground 1 +2016-05-26 22:40:02.051 svtest[81927:545793] borderType 2 +2016-05-26 22:40:02.052 svtest[81927:545793] documentCursor +2016-05-26 22:40:02.052 svtest[81927:545793] hasHorizontalScroller 0 +2016-05-26 22:40:02.052 svtest[81927:545793] hasVerticalScroller 1 +2016-05-26 22:40:02.052 svtest[81927:545793] autohidesScrollers 0 +2016-05-26 22:40:02.052 svtest[81927:545793] hasHorizontalRuler 0 +2016-05-26 22:40:02.052 svtest[81927:545793] hasVerticalRuler 0 +2016-05-26 22:40:02.052 svtest[81927:545793] rulersVisible 0 +2016-05-26 22:40:02.052 svtest[81927:545793] 10.10 autoAdjContentInsets 1 +2016-05-26 22:40:02.052 svtest[81927:545793] scrollerKnobStyle 0 +2016-05-26 22:40:02.052 svtest[81927:545793] scrollerStyle 1 +2016-05-26 22:40:02.054 svtest[81927:545793] horizontalLineScroll 10 +2016-05-26 22:40:02.055 svtest[81927:545793] verticalLineScroll 10 +2016-05-26 22:40:02.062 svtest[81927:545793] horizontalPageScroll 10 +2016-05-26 22:40:02.062 svtest[81927:545793] verticalPageScroll 10 +2016-05-26 22:40:02.062 svtest[81927:545793] scrollsDynamically 1 +2016-05-26 22:40:02.062 svtest[81927:545793] findBarPosition 1 +2016-05-26 22:40:02.062 svtest[81927:545793] usesPredomAxisScroll 0 +2016-05-26 22:40:02.062 svtest[81927:545793] horizontalElasticity 0 +2016-05-26 22:40:02.062 svtest[81927:545793] verticalElasticity 0 +2016-05-26 22:40:02.062 svtest[81927:545793] 10.8 allowsMagnification 0 +2016-05-26 22:40:02.062 svtest[81927:545793] 10.8 maxMagnification 4 +2016-05-26 22:40:02.063 svtest[81927:545793] 10.8 minMagnification 0.25 From 325a288029692006c9f6a282363359639cbcf0ec Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 26 May 2016 22:44:55 -0400 Subject: [PATCH 0139/1329] More scroll view parameters. --- sv/sourcelist | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 sv/sourcelist diff --git a/sv/sourcelist b/sv/sourcelist new file mode 100644 index 00000000..010d6525 --- /dev/null +++ b/sv/sourcelist @@ -0,0 +1,25 @@ +2016-05-26 22:43:58.600 svtest[82237:548359] backgroundColor (null) +2016-05-26 22:43:58.600 svtest[82237:548359] drawsBackground 0 +2016-05-26 22:43:58.600 svtest[82237:548359] borderType 2 +2016-05-26 22:43:58.600 svtest[82237:548359] documentCursor (null) +2016-05-26 22:43:58.600 svtest[82237:548359] hasHorizontalScroller 1 +2016-05-26 22:43:58.600 svtest[82237:548359] hasVerticalScroller 1 +2016-05-26 22:43:58.600 svtest[82237:548359] autohidesScrollers 1 +2016-05-26 22:43:58.600 svtest[82237:548359] hasHorizontalRuler 0 +2016-05-26 22:43:58.600 svtest[82237:548359] hasVerticalRuler 0 +2016-05-26 22:43:58.600 svtest[82237:548359] rulersVisible 0 +2016-05-26 22:43:58.601 svtest[82237:548359] 10.10 autoAdjContentInsets 1 +2016-05-26 22:43:58.601 svtest[82237:548359] scrollerKnobStyle 0 +2016-05-26 22:43:58.601 svtest[82237:548359] scrollerStyle 1 +2016-05-26 22:43:58.601 svtest[82237:548359] horizontalLineScroll 19 +2016-05-26 22:43:58.601 svtest[82237:548359] verticalLineScroll 19 +2016-05-26 22:43:58.645 svtest[82237:548359] horizontalPageScroll 10 +2016-05-26 22:43:58.645 svtest[82237:548359] verticalPageScroll 10 +2016-05-26 22:43:58.645 svtest[82237:548359] scrollsDynamically 1 +2016-05-26 22:43:58.645 svtest[82237:548359] findBarPosition 1 +2016-05-26 22:43:58.645 svtest[82237:548359] usesPredomAxisScroll 0 +2016-05-26 22:43:58.645 svtest[82237:548359] horizontalElasticity 0 +2016-05-26 22:43:58.646 svtest[82237:548359] verticalElasticity 0 +2016-05-26 22:43:58.646 svtest[82237:548359] 10.8 allowsMagnification 0 +2016-05-26 22:43:58.646 svtest[82237:548359] 10.8 maxMagnification 4 +2016-05-26 22:43:58.646 svtest[82237:548359] 10.8 minMagnification 0.25 From 386c9b57e665e6fb82a6466dad1ef5bf417d3171 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 26 May 2016 22:52:07 -0400 Subject: [PATCH 0140/1329] Removed timestamps. --- sv/normal.nots | 25 +++++++++++++++++++++++++ sv/outlineview.nots | 25 +++++++++++++++++++++++++ sv/sourcelist.nots | 25 +++++++++++++++++++++++++ sv/tableview.nots | 25 +++++++++++++++++++++++++ sv/textview.nots | 25 +++++++++++++++++++++++++ 5 files changed, 125 insertions(+) create mode 100644 sv/normal.nots create mode 100644 sv/outlineview.nots create mode 100644 sv/sourcelist.nots create mode 100644 sv/tableview.nots create mode 100644 sv/textview.nots diff --git a/sv/normal.nots b/sv/normal.nots new file mode 100644 index 00000000..411d1d6d --- /dev/null +++ b/sv/normal.nots @@ -0,0 +1,25 @@ + backgroundColor NSNamedColorSpace System controlColor + drawsBackground 1 + borderType 2 + documentCursor (null) + hasHorizontalScroller 1 + hasVerticalScroller 1 + autohidesScrollers 0 + hasHorizontalRuler 0 + hasVerticalRuler 0 + rulersVisible 0 + 10.10 autoAdjContentInsets 1 + scrollerKnobStyle 0 + scrollerStyle 1 + horizontalLineScroll 10 + verticalLineScroll 10 + horizontalPageScroll 10 + verticalPageScroll 10 + scrollsDynamically 1 + findBarPosition 1 + usesPredomAxisScroll 0 + horizontalElasticity 0 + verticalElasticity 0 + 10.8 allowsMagnification 0 + 10.8 maxMagnification 4 + 10.8 minMagnification 0.25 diff --git a/sv/outlineview.nots b/sv/outlineview.nots new file mode 100644 index 00000000..fcf18493 --- /dev/null +++ b/sv/outlineview.nots @@ -0,0 +1,25 @@ + backgroundColor NSNamedColorSpace System controlBackgroundColor + drawsBackground 1 + borderType 2 + documentCursor (null) + hasHorizontalScroller 1 + hasVerticalScroller 1 + autohidesScrollers 1 + hasHorizontalRuler 0 + hasVerticalRuler 0 + rulersVisible 0 + 10.10 autoAdjContentInsets 1 + scrollerKnobStyle 0 + scrollerStyle 1 + horizontalLineScroll 19 + verticalLineScroll 19 + horizontalPageScroll 10 + verticalPageScroll 10 + scrollsDynamically 1 + findBarPosition 1 + usesPredomAxisScroll 0 + horizontalElasticity 0 + verticalElasticity 0 + 10.8 allowsMagnification 0 + 10.8 maxMagnification 4 + 10.8 minMagnification 0.25 diff --git a/sv/sourcelist.nots b/sv/sourcelist.nots new file mode 100644 index 00000000..742f41ea --- /dev/null +++ b/sv/sourcelist.nots @@ -0,0 +1,25 @@ + backgroundColor (null) + drawsBackground 0 + borderType 2 + documentCursor (null) + hasHorizontalScroller 1 + hasVerticalScroller 1 + autohidesScrollers 1 + hasHorizontalRuler 0 + hasVerticalRuler 0 + rulersVisible 0 + 10.10 autoAdjContentInsets 1 + scrollerKnobStyle 0 + scrollerStyle 1 + horizontalLineScroll 19 + verticalLineScroll 19 + horizontalPageScroll 10 + verticalPageScroll 10 + scrollsDynamically 1 + findBarPosition 1 + usesPredomAxisScroll 0 + horizontalElasticity 0 + verticalElasticity 0 + 10.8 allowsMagnification 0 + 10.8 maxMagnification 4 + 10.8 minMagnification 0.25 diff --git a/sv/tableview.nots b/sv/tableview.nots new file mode 100644 index 00000000..fcf18493 --- /dev/null +++ b/sv/tableview.nots @@ -0,0 +1,25 @@ + backgroundColor NSNamedColorSpace System controlBackgroundColor + drawsBackground 1 + borderType 2 + documentCursor (null) + hasHorizontalScroller 1 + hasVerticalScroller 1 + autohidesScrollers 1 + hasHorizontalRuler 0 + hasVerticalRuler 0 + rulersVisible 0 + 10.10 autoAdjContentInsets 1 + scrollerKnobStyle 0 + scrollerStyle 1 + horizontalLineScroll 19 + verticalLineScroll 19 + horizontalPageScroll 10 + verticalPageScroll 10 + scrollsDynamically 1 + findBarPosition 1 + usesPredomAxisScroll 0 + horizontalElasticity 0 + verticalElasticity 0 + 10.8 allowsMagnification 0 + 10.8 maxMagnification 4 + 10.8 minMagnification 0.25 diff --git a/sv/textview.nots b/sv/textview.nots new file mode 100644 index 00000000..7476b0ea --- /dev/null +++ b/sv/textview.nots @@ -0,0 +1,25 @@ + backgroundColor NSCalibratedWhiteColorSpace 1 1 + drawsBackground 1 + borderType 2 + documentCursor + hasHorizontalScroller 0 + hasVerticalScroller 1 + autohidesScrollers 0 + hasHorizontalRuler 0 + hasVerticalRuler 0 + rulersVisible 0 + 10.10 autoAdjContentInsets 1 + scrollerKnobStyle 0 + scrollerStyle 1 + horizontalLineScroll 10 + verticalLineScroll 10 + horizontalPageScroll 10 + verticalPageScroll 10 + scrollsDynamically 1 + findBarPosition 1 + usesPredomAxisScroll 0 + horizontalElasticity 0 + verticalElasticity 0 + 10.8 allowsMagnification 0 + 10.8 maxMagnification 4 + 10.8 minMagnification 0.25 From 221d57cac5a979390e8312e3b2de9734d09779ec Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 26 May 2016 23:43:51 -0400 Subject: [PATCH 0141/1329] More unnecessary TODO removal. Thanks to swillits in irc.freenode.net/#macdev. --- darwin/area.m | 2 -- 1 file changed, 2 deletions(-) diff --git a/darwin/area.m b/darwin/area.m index ea604863..5a786969 100644 --- a/darwin/area.m +++ b/darwin/area.m @@ -97,7 +97,6 @@ struct uiArea { - (void)setupNewTrackingArea { - // TODO NSTrackingAssumeInside? self->libui_ta = [[NSTrackingArea alloc] initWithRect:[self bounds] options:(NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | @@ -109,7 +108,6 @@ struct uiArea { [self addTrackingArea:self->libui_ta]; } -// TODO when do we call super here? - (void)updateTrackingAreas { [self removeTrackingArea:self->libui_ta]; From 59f4c8a460f42f403eb6e6e636792d600ec0a813 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 26 May 2016 23:49:19 -0400 Subject: [PATCH 0142/1329] Removed old NSTextView files. I'll rebuild this myself. --- _wip/custtvattr | 91 ---------------------------------------------- _wip/deftvattr | 91 ---------------------------------------------- _wip/manualtvattr | 93 ----------------------------------------------- _wip/xtvattr | 92 ---------------------------------------------- 4 files changed, 367 deletions(-) delete mode 100755 _wip/custtvattr delete mode 100755 _wip/deftvattr delete mode 100755 _wip/manualtvattr delete mode 100755 _wip/xtvattr diff --git a/_wip/custtvattr b/_wip/custtvattr deleted file mode 100755 index d782e12f..00000000 --- a/_wip/custtvattr +++ /dev/null @@ -1,91 +0,0 @@ -NSScrollView - backgroundColor NSCalibratedWhiteColorSpace 1 1 - drawsBackground 1 - borderType 2 - documentCursor - hasHorizontalScroller 0 - hasVerticalScroller 1 - autohidesScrollers 0 - hasHorizontalRuler 0 - hasVerticalRuler 0 - rulersVisible 0 - automaticallyAdjustsContentInsets 1 - contentInsets left:0 top:0 right:0 bottom:0 - scrollerInsets left:0 top:0 right:0 bottom:0 - scrollerKnobStyle 0 - scrollerStyle 1 - lineScroll 10 - horizontalLineScroll 10 - verticalLineScroll 10 - pageScroll 10 - horizontalPageScroll 10 - verticalPageScroll 10 - scrollsDynamically 1 - findBarPosition 1 - usesPredominantAxisScrolling 0 - horizontalScrollElasticity 0 - verticalScrollElasticity 0 - allowsMagnification 0 - magnification 1 - maxMagnification 4 - minMagnification 0.25 - -NSTextView - textContainerInset {0, 0} - textContainerOrigin {0, 0} - backgroundColor NSCalibratedWhiteColorSpace 1 1 - drawsBackground 1 - allowsDocumentBackgroundColorChange 0 - allowedInputSourceLocales (null) - allowsUndo 1 - isEditable 1 - isSelectable 1 - isFieldEditor 0 - isRichText 0 - importsGraphics 0 - defaultParagraphStyle (null) - allowsImageEditing 0 - isAutomaticQuoteSubstitutionEnabled 1 - isAutomaticLinkDetectionEnabled 0 - displaysLinkToolTips 1 - usesRuler 0 - isRulerVisible 0 - usesInspectorBar 0 - selectionAffinity 1 - selectionGranularity 0 - insertionPointColor NSNamedColorSpace System controlTextColor - selectedTextAttributes { - NSBackgroundColor = "NSNamedColorSpace System selectedTextBackgroundColor"; - NSColor = "NSNamedColorSpace System selectedTextColor"; -} - markedTextAttributes { - NSBackgroundColor = "NSCalibratedRGBColorSpace 1 0.866667 0.333333 1"; -} - linkTextAttributes { - NSColor = "NSCalibratedRGBColorSpace 0 0 1 1"; - NSCursor = ""; - NSUnderline = 1; -} - typingAttributes (null) - smartInsertDeleteEnabled 0 - isContinuousSpellCheckingEnabled 1 - isGrammarCheckingEnabled 0 - acceptsGlyphInfo 0 - usesFontPanel 0 - usesFindPanel 1 - enabledTextCheckingTypes 963 - isAutomaticDashSubstitutionEnabled 1 - isAutomaticDataDetectionEnabled 0 - isAutomaticSpellingCorrectionEnabled 1 - isAutomaticTextReplacementEnabled 1 - usesFindBar 0 - isIncrementalSearchingEnabled 0 - NSText: - font (null) - textColor (null) - baseWritingDirection -1 - maxSize {463, 10000000} - minSize {238, 133} - isVerticallyResizable 1 - isHorizontallyResizable 0 - diff --git a/_wip/deftvattr b/_wip/deftvattr deleted file mode 100755 index 2c79c899..00000000 --- a/_wip/deftvattr +++ /dev/null @@ -1,91 +0,0 @@ -NSScrollView - backgroundColor NSCalibratedWhiteColorSpace 1 1 - drawsBackground 1 - borderType 2 - documentCursor - hasHorizontalScroller 0 - hasVerticalScroller 1 - autohidesScrollers 0 - hasHorizontalRuler 0 - hasVerticalRuler 0 - rulersVisible 0 - automaticallyAdjustsContentInsets 1 - contentInsets left:0 top:0 right:0 bottom:0 - scrollerInsets left:0 top:0 right:0 bottom:0 - scrollerKnobStyle 0 - scrollerStyle 1 - lineScroll 10 - horizontalLineScroll 10 - verticalLineScroll 10 - pageScroll 10 - horizontalPageScroll 10 - verticalPageScroll 10 - scrollsDynamically 1 - findBarPosition 1 - usesPredominantAxisScrolling 0 - horizontalScrollElasticity 0 - verticalScrollElasticity 0 - allowsMagnification 0 - magnification 1 - maxMagnification 4 - minMagnification 0.25 - -NSTextView - textContainerInset {0, 0} - textContainerOrigin {0, 0} - backgroundColor NSCalibratedWhiteColorSpace 1 1 - drawsBackground 1 - allowsDocumentBackgroundColorChange 0 - allowedInputSourceLocales (null) - allowsUndo 1 - isEditable 1 - isSelectable 1 - isFieldEditor 0 - isRichText 1 - importsGraphics 0 - defaultParagraphStyle (null) - allowsImageEditing 0 - isAutomaticQuoteSubstitutionEnabled 1 - isAutomaticLinkDetectionEnabled 0 - displaysLinkToolTips 1 - usesRuler 1 - isRulerVisible 0 - usesInspectorBar 0 - selectionAffinity 0 - selectionGranularity 0 - insertionPointColor NSNamedColorSpace System controlTextColor - selectedTextAttributes { - NSBackgroundColor = "NSNamedColorSpace System selectedTextBackgroundColor"; - NSColor = "NSNamedColorSpace System selectedTextColor"; -} - markedTextAttributes { - NSBackgroundColor = "NSCalibratedRGBColorSpace 1 0.866667 0.333333 1"; -} - linkTextAttributes { - NSColor = "NSCalibratedRGBColorSpace 0 0 1 1"; - NSCursor = ""; - NSUnderline = 1; -} - typingAttributes (null) - smartInsertDeleteEnabled 1 - isContinuousSpellCheckingEnabled 1 - isGrammarCheckingEnabled 0 - acceptsGlyphInfo 1 - usesFontPanel 1 - usesFindPanel 1 - enabledTextCheckingTypes 963 - isAutomaticDashSubstitutionEnabled 1 - isAutomaticDataDetectionEnabled 0 - isAutomaticSpellingCorrectionEnabled 1 - isAutomaticTextReplacementEnabled 1 - usesFindBar 0 - isIncrementalSearchingEnabled 0 - NSText: - font (null) - textColor (null) - baseWritingDirection -1 - maxSize {463, 10000000} - minSize {238, 133} - isVerticallyResizable 1 - isHorizontallyResizable 0 - diff --git a/_wip/manualtvattr b/_wip/manualtvattr deleted file mode 100755 index 4d66634d..00000000 --- a/_wip/manualtvattr +++ /dev/null @@ -1,93 +0,0 @@ -NSScrollView - backgroundColor NSNamedColorSpace System controlBackgroundColor - drawsBackground 1 - borderType 0 - documentCursor - hasHorizontalScroller 0 - hasVerticalScroller 0 - autohidesScrollers 0 - hasHorizontalRuler 0 - hasVerticalRuler 0 - rulersVisible 0 - automaticallyAdjustsContentInsets 1 - contentInsets left:0 top:0 right:0 bottom:0 - scrollerInsets left:0 top:0 right:0 bottom:0 - scrollerKnobStyle 0 - scrollerStyle 1 - lineScroll 10 - horizontalLineScroll 10 - verticalLineScroll 10 - pageScroll 10 - horizontalPageScroll 10 - verticalPageScroll 10 - scrollsDynamically 1 - findBarPosition 1 - usesPredominantAxisScrolling 1 - horizontalScrollElasticity 0 - verticalScrollElasticity 0 - allowsMagnification 0 - magnification 1 - maxMagnification 4 - minMagnification 0.25 - -NSTextView - textContainerInset {0, 0} - textContainerOrigin {0, 0} - backgroundColor NSCalibratedWhiteColorSpace 1 1 - drawsBackground 1 - allowsDocumentBackgroundColorChange 0 - allowedInputSourceLocales (null) - allowsUndo 0 - isEditable 1 - isSelectable 1 - isFieldEditor 0 - isRichText 1 - importsGraphics 0 - defaultParagraphStyle (null) - allowsImageEditing 0 - isAutomaticQuoteSubstitutionEnabled 1 - isAutomaticLinkDetectionEnabled 0 - displaysLinkToolTips 1 - usesRuler 1 - isRulerVisible 0 - usesInspectorBar 0 - selectionAffinity 0 - selectionGranularity 0 - insertionPointColor NSNamedColorSpace System controlTextColor - selectedTextAttributes { - NSBackgroundColor = "NSNamedColorSpace System selectedTextBackgroundColor"; - NSColor = "NSNamedColorSpace System selectedTextColor"; -} - markedTextAttributes { - NSBackgroundColor = "NSCalibratedRGBColorSpace 1 0.866667 0.333333 1"; -} - linkTextAttributes { - NSColor = "NSCalibratedRGBColorSpace 0 0 1 1"; - NSCursor = ""; - NSUnderline = 1; -} - typingAttributes { - NSFont = "\"Helvetica 12.00 pt. P [] (0x60000005f2f0) fobj=0x6000001e4f00, spc=3.33\""; -} - smartInsertDeleteEnabled 1 - isContinuousSpellCheckingEnabled 0 - isGrammarCheckingEnabled 0 - acceptsGlyphInfo 1 - usesFontPanel 1 - usesFindPanel 0 - enabledTextCheckingTypes 961 - isAutomaticDashSubstitutionEnabled 1 - isAutomaticDataDetectionEnabled 0 - isAutomaticSpellingCorrectionEnabled 1 - isAutomaticTextReplacementEnabled 1 - usesFindBar 0 - isIncrementalSearchingEnabled 0 - NSText: - font "Helvetica 12.00 pt. P [] (0x60000005f2f0) fobj=0x6000001e4f00, spc=3.33" - textColor (null) - baseWritingDirection -1 - maxSize {0, 10000000} - minSize {0, 0} - isVerticallyResizable 1 - isHorizontallyResizable 0 - diff --git a/_wip/xtvattr b/_wip/xtvattr deleted file mode 100755 index dab92ac0..00000000 --- a/_wip/xtvattr +++ /dev/null @@ -1,92 +0,0 @@ -NSScrollView - backgroundColor NSNamedColorSpace System controlBackgroundColor - drawsBackground 1 - borderType 2 - documentCursor - hasHorizontalScroller 0 - hasVerticalScroller 1 - autohidesScrollers 1 - hasHorizontalRuler 0 - hasVerticalRuler 0 - rulersVisible 0 - automaticallyAdjustsContentInsets 1 - contentInsets left:0 top:0 right:0 bottom:0 - scrollerInsets left:0 top:0 right:0 bottom:0 - scrollerKnobStyle 0 - scrollerStyle 1 - lineScroll 10 - horizontalLineScroll 10 - verticalLineScroll 10 - pageScroll 10 - horizontalPageScroll 10 - verticalPageScroll 10 - scrollsDynamically 1 - findBarPosition 1 - usesPredominantAxisScrolling 1 - horizontalScrollElasticity 0 - verticalScrollElasticity 0 - allowsMagnification 0 - magnification 1 - maxMagnification 4 - minMagnification 0.25 - -NSTextView - textContainerInset {0, 0} - textContainerOrigin {0, 0} - backgroundColor NSNamedColorSpace System textBackgroundColor - drawsBackground 1 - allowsDocumentBackgroundColorChange 0 - allowedInputSourceLocales (null) - allowsUndo 1 - isEditable 1 - isSelectable 1 - isFieldEditor 0 - isRichText 0 - importsGraphics 0 - defaultParagraphStyle (null) - allowsImageEditing 0 - isAutomaticQuoteSubstitutionEnabled 0 - isAutomaticLinkDetectionEnabled 0 - displaysLinkToolTips 1 - usesRuler 0 - isRulerVisible 0 - usesInspectorBar 0 - selectionAffinity 0 - selectionGranularity 0 - insertionPointColor NSNamedColorSpace System controlTextColor - selectedTextAttributes { - NSBackgroundColor = "NSNamedColorSpace System selectedTextBackgroundColor"; - NSColor = "NSNamedColorSpace System selectedTextColor"; -} - markedTextAttributes { - NSBackgroundColor = "NSCalibratedRGBColorSpace 1 0.866667 0.333333 1"; -} - linkTextAttributes { - NSColor = "NSCalibratedRGBColorSpace 0 0 1 1"; - NSCursor = ""; - NSUnderline = 1; -} - typingAttributes { - NSFont = "\".HelveticaNeueDeskInterface-Regular 13.00 pt. P [] (0x7f886b4bc6a0) fobj=0x7f886b474d90, spc=3.94\""; -} - smartInsertDeleteEnabled 0 - isContinuousSpellCheckingEnabled 0 - isGrammarCheckingEnabled 0 - acceptsGlyphInfo 0 - usesFontPanel 0 - usesFindPanel 0 - enabledTextCheckingTypes 0 - isAutomaticDashSubstitutionEnabled 0 - isAutomaticDataDetectionEnabled 0 - isAutomaticSpellingCorrectionEnabled 0 - isAutomaticTextReplacementEnabled 0 - usesFindBar 0 - isIncrementalSearchingEnabled 0 - NSText: - font ".HelveticaNeueDeskInterface-Regular 13.00 pt. P [] (0x7f886b4bc6a0) fobj=0x7f886b474d90, spc=3.94" - textColor (null) - baseWritingDirection -1 - maxSize {1, 10000000} - minSize {1, 1} - isVerticallyResizable 1 - isHorizontallyResizable 0 From 3adb485c669c1508f2df122f9f1e14b199f2aeae Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 26 May 2016 23:50:54 -0400 Subject: [PATCH 0143/1329] More old WIP file removal. --- _wip/areatext/osxweights | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 _wip/areatext/osxweights diff --git a/_wip/areatext/osxweights b/_wip/areatext/osxweights deleted file mode 100644 index 34e1cc65..00000000 --- a/_wip/areatext/osxweights +++ /dev/null @@ -1,9 +0,0 @@ -NSFontWeightUltraLight -0.800000 -NSFontWeightThin -0.600000 -NSFontWeightLight -0.400000 -NSFontWeightRegular 0.000000 -NSFontWeightMedium 0.230000 -NSFontWeightSemibold 0.300000 -NSFontWeightBold 0.400000 -NSFontWeightHeavy 0.560000 -NSFontWeightBlack 0.620000 From b65175a19cc21298a2aa28f38a6c768895c90f5c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 27 May 2016 01:13:45 -0400 Subject: [PATCH 0144/1329] Refactored NSScrollView auto layout stuff. I should probably just split it into a separate file when I do implementt he shared scrollview source code. --- darwin/autolayout.m | 48 ++++++++++++++++++------------------------ darwin/uipriv_darwin.h | 4 ++-- 2 files changed, 22 insertions(+), 30 deletions(-) diff --git a/darwin/autolayout.m b/darwin/autolayout.m index 99f71130..fd9df5f0 100644 --- a/darwin/autolayout.m +++ b/darwin/autolayout.m @@ -185,25 +185,25 @@ void scrollViewConstraintsEstablish(struct scrollViewConstraints *c, NSScrollVie [sv addConstraint:c->documentTop]; [c->documentTop retain]; - if (!hscroll) { - c->documentTrailing = mkConstraint(dv, NSLayoutAttributeTrailing, - NSLayoutRelationEqual, - cv, NSLayoutAttributeTrailing, - 1, 0, - [desc stringByAppendingString:@"document trailing constraint"]); + c->hscroll = hscroll; + c->documentTrailing = mkConstraint(dv, NSLayoutAttributeTrailing, + NSLayoutRelationEqual, + cv, NSLayoutAttributeTrailing, + 1, 0, + [desc stringByAppendingString:@"document trailing constraint"]); + if (!c->hscroll) [sv addConstraint:c->documentTrailing]; - [c->documentTrailing retain]; - } + [c->documentTrailing retain]; - if (!vscroll) { - c->documentBottom = mkConstraint(dv, NSLayoutAttributeBottom, - NSLayoutRelationEqual, - sv, NSLayoutAttributeBottom, - 1, 0, - [desc stringByAppendingString:@"document bottom constraint"]); + c->vscroll = vscroll; + c->documentBottom = mkConstraint(dv, NSLayoutAttributeBottom, + NSLayoutRelationEqual, + sv, NSLayoutAttributeBottom, + 1, 0, + [desc stringByAppendingString:@"document bottom constraint"]); + if (!c->vscroll) [sv addConstraint:c->documentBottom]; - [c->documentBottom retain]; - } + [c->documentBottom retain]; } void scrollViewConstraintsRemove(struct scrollViewConstraints *c, NSScrollView *sv) @@ -219,23 +219,15 @@ void scrollViewConstraintsRemove(struct scrollViewConstraints *c, NSScrollView * c->documentTop = nil; } if (c->documentTrailing != nil) { - [sv removeConstraint:c->documentTrailing]; + if (!c->hscroll) + [sv removeConstraint:c->documentTrailing]; [c->documentTrailing release]; c->documentTrailing = nil; } if (c->documentBottom != nil) { - [sv removeConstraint:c->documentBottom]; + if (!c->vscroll) + [sv removeConstraint:c->documentBottom]; [c->documentBottom release]; c->documentBottom = nil; } - if (c->documentWidth != nil) { - [sv removeConstraint:c->documentWidth]; - [c->documentWidth release]; - c->documentWidth = nil; - } - if (c->documentHeight != nil) { - [sv removeConstraint:c->documentHeight]; - [c->documentHeight release]; - c->documentHeight = nil; - } } diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 865d3a48..36bf674a 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -75,10 +75,10 @@ extern void singleChildConstraintsSetMargined(struct singleChildConstraints *c, struct scrollViewConstraints { NSLayoutConstraint *documentLeading; NSLayoutConstraint *documentTop; + BOOL hscroll; NSLayoutConstraint *documentTrailing; + BOOL vscroll; NSLayoutConstraint *documentBottom; - NSLayoutConstraint *documentWidth; - NSLayoutConstraint *documentHeight; }; extern void scrollViewConstraintsEstablish(struct scrollViewConstraints *c, NSScrollView *sv, BOOL hscroll, BOOL vscroll, NSString *desc); extern void scrollViewConstraintsRemove(struct scrollViewConstraints *c, NSScrollView *sv); From e2266ab5777b75816dcbc38ab57dc7b5dab8c549 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 27 May 2016 12:18:35 -0400 Subject: [PATCH 0145/1329] More NSScrollView auto layout fixes. --- darwin/autolayout.m | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/darwin/autolayout.m b/darwin/autolayout.m index fd9df5f0..ef7556bc 100644 --- a/darwin/autolayout.m +++ b/darwin/autolayout.m @@ -164,6 +164,7 @@ void singleChildConstraintsSetMargined(struct singleChildConstraints *c, int mar void scrollViewConstraintsEstablish(struct scrollViewConstraints *c, NSScrollView *sv, BOOL hscroll, BOOL vscroll, NSString *desc) { NSView *cv, *dv; + NSLayoutRelation rel; scrollViewConstraintsRemove(c, sv); cv = [sv contentView]; @@ -186,23 +187,27 @@ void scrollViewConstraintsEstablish(struct scrollViewConstraints *c, NSScrollVie [c->documentTop retain]; c->hscroll = hscroll; + rel = NSLayoutRelationGreaterThanOrEqual; + if (!c->hscroll) + rel = NSLayoutRelationEqual; c->documentTrailing = mkConstraint(dv, NSLayoutAttributeTrailing, - NSLayoutRelationEqual, + rel, cv, NSLayoutAttributeTrailing, 1, 0, [desc stringByAppendingString:@"document trailing constraint"]); - if (!c->hscroll) - [sv addConstraint:c->documentTrailing]; + [sv addConstraint:c->documentTrailing]; [c->documentTrailing retain]; c->vscroll = vscroll; + rel = NSLayoutRelationGreaterThanOrEqual; + if (!c->vscroll) + rel = NSLayoutRelationEqual; c->documentBottom = mkConstraint(dv, NSLayoutAttributeBottom, - NSLayoutRelationEqual, + rel, sv, NSLayoutAttributeBottom, 1, 0, [desc stringByAppendingString:@"document bottom constraint"]); - if (!c->vscroll) - [sv addConstraint:c->documentBottom]; + [sv addConstraint:c->documentBottom]; [c->documentBottom retain]; } @@ -219,14 +224,12 @@ void scrollViewConstraintsRemove(struct scrollViewConstraints *c, NSScrollView * c->documentTop = nil; } if (c->documentTrailing != nil) { - if (!c->hscroll) - [sv removeConstraint:c->documentTrailing]; + [sv removeConstraint:c->documentTrailing]; [c->documentTrailing release]; c->documentTrailing = nil; } if (c->documentBottom != nil) { - if (!c->vscroll) - [sv removeConstraint:c->documentBottom]; + [sv removeConstraint:c->documentBottom]; [c->documentBottom release]; c->documentBottom = nil; } From d39cd76a8a84fca592caae6d871cc4b4ff5c467e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 27 May 2016 12:25:26 -0400 Subject: [PATCH 0146/1329] Quick fixup. --- darwin/autolayout.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/darwin/autolayout.m b/darwin/autolayout.m index ef7556bc..00651d27 100644 --- a/darwin/autolayout.m +++ b/darwin/autolayout.m @@ -204,7 +204,7 @@ void scrollViewConstraintsEstablish(struct scrollViewConstraints *c, NSScrollVie rel = NSLayoutRelationEqual; c->documentBottom = mkConstraint(dv, NSLayoutAttributeBottom, rel, - sv, NSLayoutAttributeBottom, + cv, NSLayoutAttributeBottom, 1, 0, [desc stringByAppendingString:@"document bottom constraint"]); [sv addConstraint:c->documentBottom]; From 46f6f2aac15a7d3094ddc4c35ff3f746d77ea62a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 27 May 2016 22:32:24 -0400 Subject: [PATCH 0147/1329] Updated bindings list. --- README.md | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index f7d98762..e24a9a1c 100644 --- a/README.md +++ b/README.md @@ -78,16 +78,17 @@ Other people have made bindings to other languages: Language | Bindings --- | --- -**C#/.net** | [LibUI.Binding](https://github.com/NattyNarwhal/LibUI.Binding), [SharpUI](https://github.com/benpye/sharpui/) -**Crystal** | [libui.cr](https://github.com/Fusion/libui.cr) -**D** | [DerelictLibui](https://github.com/Extrawurst/DerelictLibui) -**Haskell** | [libui-haskell](https://github.com/ajnsit/libui-haskell) -**JavaScript** | [libui.js (merged into libui-node?)](https://github.com/mavenave/libui.js) -**Lua** | [libuilua](https://github.com/zevv/libuilua), [libui-lua](https://github.com/mdombroski/libui-lua) -**Node.js** | [libui-node](https://github.com/parro-it/libui-node) -**Python** | [pylibui](https://github.com/joaoventura/pylibui) -**Ruby** | [libui-ruby](https://github.com/jamescook/libui-ruby) -**Rust** | [libui-rs](https://github.com/pcwalton/libui-rs) +C#/.net | [LibUI.Binding](https://github.com/NattyNarwhal/LibUI.Binding), [SharpUI](https://github.com/benpye/sharpui/) +Crystal | [libui.cr](https://github.com/Fusion/libui.cr) +D | [DerelictLibui](https://github.com/Extrawurst/DerelictLibui) +Haskell | [libui-haskell](https://github.com/ajnsit/libui-haskell) +JavaScript | [libui.js (merged into libui-node?)](https://github.com/mavenave/libui.js) +Julia | [Libui.jl](https://github.com/joa-quim/Libui.jl) +Lua | [libuilua](https://github.com/zevv/libuilua), [libui-lua](https://github.com/mdombroski/libui-lua) +Node.js | [libui-node](https://github.com/parro-it/libui-node) +Python | [pylibui](https://github.com/joaoventura/pylibui) +Ruby | [libui-ruby](https://github.com/jamescook/libui-ruby) +Rust | [libui-rs](https://github.com/pcwalton/libui-rs) ## Screenshots From 34d54f29b48a8aae3a4e37b2fc8d80889f8a72c8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 27 May 2016 23:42:05 -0400 Subject: [PATCH 0148/1329] Started cleaning up all the NSScrollView stuff into its own file so it can be reused. --- darwin/GNUfiles.mk | 1 + darwin/autolayout.m | 75 ----------------------- darwin/scrollview.m | 133 +++++++++++++++++++++++++++++++++++++++++ darwin/uipriv_darwin.h | 23 +++---- 4 files changed, 147 insertions(+), 85 deletions(-) create mode 100644 darwin/scrollview.m diff --git a/darwin/GNUfiles.mk b/darwin/GNUfiles.mk index c420d02d..aacf382f 100644 --- a/darwin/GNUfiles.mk +++ b/darwin/GNUfiles.mk @@ -26,6 +26,7 @@ MFILES += \ darwin/multilineentry.m \ darwin/progressbar.m \ darwin/radiobuttons.m \ + darwin/scrollview.m \ darwin/separator.m \ darwin/slider.m \ darwin/spinbox.m \ diff --git a/darwin/autolayout.m b/darwin/autolayout.m index 00651d27..9964155f 100644 --- a/darwin/autolayout.m +++ b/darwin/autolayout.m @@ -159,78 +159,3 @@ void singleChildConstraintsSetMargined(struct singleChildConstraints *c, int mar if (c->bottomConstraintEqual != nil) [c->bottomConstraintEqual setConstant:margin]; } - -// based on http://blog.bjhomer.com/2014/08/nsscrollview-and-autolayout.html because (as pointed out there) Apple's official guide is really only for iOS -void scrollViewConstraintsEstablish(struct scrollViewConstraints *c, NSScrollView *sv, BOOL hscroll, BOOL vscroll, NSString *desc) -{ - NSView *cv, *dv; - NSLayoutRelation rel; - - scrollViewConstraintsRemove(c, sv); - cv = [sv contentView]; - dv = [sv documentView]; - - c->documentLeading = mkConstraint(dv, NSLayoutAttributeLeading, - NSLayoutRelationEqual, - cv, NSLayoutAttributeLeading, - 1, 0, - [desc stringByAppendingString:@"document leading constraint"]); - [sv addConstraint:c->documentLeading]; - [c->documentLeading retain]; - - c->documentTop = mkConstraint(dv, NSLayoutAttributeTop, - NSLayoutRelationEqual, - cv, NSLayoutAttributeTop, - 1, 0, - [desc stringByAppendingString:@"document top constraint"]); - [sv addConstraint:c->documentTop]; - [c->documentTop retain]; - - c->hscroll = hscroll; - rel = NSLayoutRelationGreaterThanOrEqual; - if (!c->hscroll) - rel = NSLayoutRelationEqual; - c->documentTrailing = mkConstraint(dv, NSLayoutAttributeTrailing, - rel, - cv, NSLayoutAttributeTrailing, - 1, 0, - [desc stringByAppendingString:@"document trailing constraint"]); - [sv addConstraint:c->documentTrailing]; - [c->documentTrailing retain]; - - c->vscroll = vscroll; - rel = NSLayoutRelationGreaterThanOrEqual; - if (!c->vscroll) - rel = NSLayoutRelationEqual; - c->documentBottom = mkConstraint(dv, NSLayoutAttributeBottom, - rel, - cv, NSLayoutAttributeBottom, - 1, 0, - [desc stringByAppendingString:@"document bottom constraint"]); - [sv addConstraint:c->documentBottom]; - [c->documentBottom retain]; -} - -void scrollViewConstraintsRemove(struct scrollViewConstraints *c, NSScrollView *sv) -{ - if (c->documentLeading != nil) { - [sv removeConstraint:c->documentLeading]; - [c->documentLeading release]; - c->documentLeading = nil; - } - if (c->documentTop != nil) { - [sv removeConstraint:c->documentTop]; - [c->documentTop release]; - c->documentTop = nil; - } - if (c->documentTrailing != nil) { - [sv removeConstraint:c->documentTrailing]; - [c->documentTrailing release]; - c->documentTrailing = nil; - } - if (c->documentBottom != nil) { - [sv removeConstraint:c->documentBottom]; - [c->documentBottom release]; - c->documentBottom = nil; - } -} diff --git a/darwin/scrollview.m b/darwin/scrollview.m new file mode 100644 index 00000000..f02d5acf --- /dev/null +++ b/darwin/scrollview.m @@ -0,0 +1,133 @@ +// 27 may 2016 +#include "uipriv_darwin.h" + +struct scrollViewData { + NSLayoutConstraint *documentLeading; + NSLayoutConstraint *documentTop; + BOOL hscroll; + NSLayoutConstraint *documentTrailing; + BOOL vscroll; + NSLayoutConstraint *documentBottom; +}; + +NSScrollView *mkScrollView(struct scrollViewCreateParams *p, struct scrollViewData **dout) +{ + NSScrollView *sv; + NSBorderType border; + struct scrollViewData *d; + + sv = [[NSScrollView alloc] initWithFrame:NSZeroRect]; + // TODO verify background color for programmatically created NSTextView + if (p->BackgroundColor != nil) + [sv setBackgroundColor:p->BackgroundColor]; + [sv setDrawsBackground:p->DrawsBackground]; + border = NSNoBorder; + if (p->Bordered) + border = NSBezelBorder; + // TODO verify document cursor for programmatically created NSTextView + [sv setBorderType:border]; + [sv setAutohidesScrollers:YES]; + [sv setHasHorizontalRuler:NO]; + [sv setHasVerticalRuler:NO]; + [sv setRulersVisible:NO]; + [sv setScrollerKnobStyle:NSScrollerKnobStyleDefault]; + // the scroller style is documented as being set by default for us + // TODO verify line and page for programmatically created NSTextView + [sv scrollsDynamically:YES]; + [sv setFindBarPosition:NSScrollViewFindBarPositionAboveContent]; + [sv setUsesPredominantAxisScrolling:NO]; + [sv setHorizontalElasticity:NSScrollElasticityAutomatic]; + [sv setVerticalElasticity:NSScrollElasticityAutomatic]; + [sv setAllowsMagnification:NO]; + + [p->DocumentView setTranslatesAutoresizingMaskIntoConstraints:NO]; + [sv setDocumentView:p->DocumentView]; + d = uiNew(struct scrollViewData); + scrollViewSetScrolling(sv, d, p->HScroll, p->VScroll); + + *dout = d; + return sv; +} + +static void scrollViewConstraintsRemove(NSScrollView *sv, struct scrollViewData *d) +{ + if (d->documentLeading != nil) { + [sv removeConstraint:d->documentLeading]; + [d->documentLeading release]; + d->documentLeading = nil; + } + if (d->documentTop != nil) { + [sv removeConstraint:d->documentTop]; + [d->documentTop release]; + d->documentTop = nil; + } + if (d->documentTrailing != nil) { + [sv removeConstraint:d->documentTrailing]; + [d->documentTrailing release]; + d->documentTrailing = nil; + } + if (d->documentBottom != nil) { + [sv removeConstraint:d->documentBottom]; + [d->documentBottom release]; + d->documentBottom = nil; + } +} + +// based on http://blog.bjhomer.com/2014/08/nsscrollview-and-autolayout.html because (as pointed out there) Apple's official guide is really only for iOS +void scrollViewSetScrolling(NSScrollView *sv, struct scrollViewData *d, BOOL hscroll, BOOL vscroll) +{ + NSView *cv, *dv; + NSLayoutRelation rel; + + scrollViewConstraintsRemove(sv, d); + cv = [sv contentView]; + dv = [sv documentView]; + + d->documentLeading = mkConstraint(dv, NSLayoutAttributeLeading, + NSLayoutRelationEqual, + cv, NSLayoutAttributeLeading, + 1, 0, + @"NSScrollView document leading constraint"); + [sv addConstraint:d->documentLeading]; + [d->documentLeading retain]; + + d->documentTop = mkConstraint(dv, NSLayoutAttributeTop, + NSLayoutRelationEqual, + cv, NSLayoutAttributeTop, + 1, 0, + @"NSScrollView document top constraint"); + [sv addConstraint:d->documentTop]; + [d->documentTop retain]; + + d->hscroll = hscroll; + [sv setHasHorizontalScroller:d->hscroll]; + rel = NSLayoutRelationGreaterThanOrEqual; + if (!d->hscroll) + rel = NSLayoutRelationEqual; + d->documentTrailing = mkConstraint(dv, NSLayoutAttributeTrailing, + rel, + cv, NSLayoutAttributeTrailing, + 1, 0, + @"NSScrollView document trailing constraint"); + [sv addConstraint:d->documentTrailing]; + [d->documentTrailing retain]; + + d->vscroll = vscroll; + [sv setHasVerticalScroller:d->vscroll]; + rel = NSLayoutRelationGreaterThanOrEqual; + if (!d->vscroll) + rel = NSLayoutRelationEqual; + d->documentBottom = mkConstraint(dv, NSLayoutAttributeBottom, + rel, + cv, NSLayoutAttributeBottom, + 1, 0, + @"NSScrollView document bottom constraint"); + [sv addConstraint:d->documentBottom]; + [d->documentBottom retain]; +} + +void scrollViewFreeData(NSScrollView *sv, struct scrollViewData *d) +{ + scrollViewConstraintsRemove(sv, d); + uiFree(d); +} diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 36bf674a..e016bf3e 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -72,16 +72,6 @@ struct singleChildConstraints { extern void singleChildConstraintsEstablish(struct singleChildConstraints *c, NSView *contentView, NSView *childView, BOOL hugsTrailing, BOOL hugsBottom, int margined, NSString *desc); extern void singleChildConstraintsRemove(struct singleChildConstraints *c, NSView *cv); extern void singleChildConstraintsSetMargined(struct singleChildConstraints *c, int margined); -struct scrollViewConstraints { - NSLayoutConstraint *documentLeading; - NSLayoutConstraint *documentTop; - BOOL hscroll; - NSLayoutConstraint *documentTrailing; - BOOL vscroll; - NSLayoutConstraint *documentBottom; -}; -extern void scrollViewConstraintsEstablish(struct scrollViewConstraints *c, NSScrollView *sv, BOOL hscroll, BOOL vscroll, NSString *desc); -extern void scrollViewConstraintsRemove(struct scrollViewConstraints *c, NSScrollView *sv); // map.m extern struct mapTable *newMap(void); @@ -115,3 +105,16 @@ extern void setupFontPanel(void); // colorbutton.m extern BOOL colorButtonInhibitSendAction(SEL sel, id from, id to); + +// scrollview.m +struct scrollViewCreateParams { + NSView *DocumentView; + NSColor *BackgroundColor; + BOOL DrawsBackground; + BOOL Bordered; + BOOL HScroll; + BOOL VScroll; +}; +extern NSScrollView *mkScrollView(struct scrollViewCreateParams *p, struct scrollViewData **dout); +extern void scrollViewSetScrolling(NSScrollView *sv, struct scrollViewData *d, BOOL hscroll, BOOL vscroll); +extern void scrollViewFreeData(NSScrollView *sv, struct scrollViewData *d); From 923a678e87dd1eabc81a4c4ff336e250f529d50f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 27 May 2016 23:56:44 -0400 Subject: [PATCH 0149/1329] Integrated the new scrollview stuff with uiMultilineEntry. Nice and stable now :D --- darwin/multilineentry.m | 80 +++++++++++------------------------------ darwin/scrollview.m | 6 ++-- darwin/uipriv_darwin.h | 1 + 3 files changed, 25 insertions(+), 62 deletions(-) diff --git a/darwin/multilineentry.m b/darwin/multilineentry.m index 34611071..afcb1fec 100644 --- a/darwin/multilineentry.m +++ b/darwin/multilineentry.m @@ -44,15 +44,24 @@ struct uiMultilineEntry { uiDarwinControl c; NSScrollView *sv; intrinsicSizeTextView *tv; + struct scrollViewData *d; void (*onChanged)(uiMultilineEntry *, void *); void *onChangedData; - struct scrollViewConstraints constraints; }; // TODO events -// TODO destroy -uiDarwinControlAllDefaults(uiMultilineEntry, sv) +uiDarwinControlAllDefaultsExceptDestroy(uiMultilineEntry, sv) + +static void uiMultilineEntryDestroy(uiControl *c) +{ + uiMultilineEntry *e = uiMultilineEntry(c); + + scrollViewFreeData(e->sv, e->d); + [e->tv release]; + [e->sv release]; + uiFreeControl(uiControl(e)); +} static void defaultOnChanged(uiMultilineEntry *e, void *data) { @@ -107,16 +116,10 @@ static uiMultilineEntry *finishMultilineEntry(BOOL hscroll) { uiMultilineEntry *e; NSFont *font; + struct scrollViewCreateParams p; uiDarwinNewControl(uiMultilineEntry, e); - e->sv = [[NSScrollView alloc] initWithFrame:NSZeroRect]; - // TODO verify against Interface Builder - [e->sv setHasHorizontalScroller:hscroll]; - [e->sv setHasVerticalScroller:YES]; - [e->sv setAutohidesScrollers:YES]; - [e->sv setBorderType:NSBezelBorder]; - e->tv = [[intrinsicSizeTextView alloc] initWithFrame:NSZeroRect]; // verified against Interface Builder, except for rich text options [e->tv setAllowsDocumentBackgroundColorChange:NO]; @@ -166,15 +169,14 @@ static uiMultilineEntry *finishMultilineEntry(BOOL hscroll) // let's just set it to the standard control font anyway, just to be safe [e->tv setFont:font]; - [e->sv setDocumentView:e->tv]; - [e->tv setTranslatesAutoresizingMaskIntoConstraints:NO]; - scrollViewConstraintsEstablish(&(e->constraints), e->sv, hscroll, YES, @"uiMultilineEntry"); - // needed to allow horizontal shrinking - // TODO what about vertical text? - [e->tv setContentCompressionResistancePriority:NSLayoutPriorityDefaultLow forOrientation:NSLayoutConstraintOrientationHorizontal]; - -//TODO:void printinfo(NSScrollView *sv, NSTextView *tv); -//printinfo(e->sv, e->tv); + memset(&p, 0, sizeof (struct scrollViewCreateParams)); + p.DocumentView = e->tv; + p.BackgroundColor = nil; + p.DrawsBackground = YES; + p.Bordered = YES; + p.HScroll = hscroll; + p.VScroll = YES; + e->sv = mkScrollView(&p, &(e->d)); uiMultilineEntryOnChanged(e, defaultOnChanged, NULL); @@ -227,47 +229,7 @@ NSTextView *tv; } _s; _s.sv = sv; _s.tv = tv; - - add("NSScrollView"); - add(" backgroundColor %@", [self.sv backgroundColor]); - add(" drawsBackground %d", [self.sv drawsBackground]); - add(" borderType %d", [self.sv borderType]); - add(" documentCursor %@", [self.sv documentCursor]); - add(" hasHorizontalScroller %d", [self.sv hasHorizontalScroller]); - add(" hasVerticalScroller %d", [self.sv hasVerticalScroller]); - add(" autohidesScrollers %d", [self.sv autohidesScrollers]); - add(" hasHorizontalRuler %d", [self.sv hasHorizontalRuler]); - add(" hasVerticalRuler %d", [self.sv hasVerticalRuler]); - add(" rulersVisible %d", [self.sv rulersVisible]); - add(" automaticallyAdjustsContentInsets %d", - [self.sv automaticallyAdjustsContentInsets]); - add(" contentInsets %@", - edgeInsetsStr([self.sv contentInsets])); - add(" scrollerInsets %@", - edgeInsetsStr([self.sv scrollerInsets])); - add(" scrollerKnobStyle %d", [self.sv scrollerKnobStyle]); - add(" scrollerStyle %d", [self.sv scrollerStyle]); - add(" lineScroll %g", [self.sv lineScroll]); - add(" horizontalLineScroll %g", [self.sv horizontalLineScroll]); - add(" verticalLineScroll %g", [self.sv verticalLineScroll]); - add(" pageScroll %g", [self.sv pageScroll]); - add(" horizontalPageScroll %g", [self.sv horizontalPageScroll]); - add(" verticalPageScroll %g", [self.sv verticalPageScroll]); - add(" scrollsDynamically %d", [self.sv scrollsDynamically]); - add(" findBarPosition %d", [self.sv findBarPosition]); - add(" usesPredominantAxisScrolling %d", - [self.sv usesPredominantAxisScrolling]); - add(" horizontalScrollElasticity %d", - [self.sv horizontalScrollElasticity]); - add(" verticalScrollElasticity %d", - [self.sv verticalScrollElasticity]); - add(" allowsMagnification %d", [self.sv allowsMagnification]); - add(" magnification %g", [self.sv magnification]); - add(" maxMagnification %g", [self.sv maxMagnification]); - add(" minMagnification %g", [self.sv minMagnification]); - add(""); - add("NSTextView"); add(" textContainerInset %@", NSStringFromSize([self.tv textContainerInset])); diff --git a/darwin/scrollview.m b/darwin/scrollview.m index f02d5acf..90ef504f 100644 --- a/darwin/scrollview.m +++ b/darwin/scrollview.m @@ -33,11 +33,11 @@ NSScrollView *mkScrollView(struct scrollViewCreateParams *p, struct scrollViewDa [sv setScrollerKnobStyle:NSScrollerKnobStyleDefault]; // the scroller style is documented as being set by default for us // TODO verify line and page for programmatically created NSTextView - [sv scrollsDynamically:YES]; + [sv setScrollsDynamically:YES]; [sv setFindBarPosition:NSScrollViewFindBarPositionAboveContent]; [sv setUsesPredominantAxisScrolling:NO]; - [sv setHorizontalElasticity:NSScrollElasticityAutomatic]; - [sv setVerticalElasticity:NSScrollElasticityAutomatic]; + [sv setHorizontalScrollElasticity:NSScrollElasticityAutomatic]; + [sv setVerticalScrollElasticity:NSScrollElasticityAutomatic]; [sv setAllowsMagnification:NO]; [p->DocumentView setTranslatesAutoresizingMaskIntoConstraints:NO]; diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index e016bf3e..0bc79f37 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -115,6 +115,7 @@ struct scrollViewCreateParams { BOOL HScroll; BOOL VScroll; }; +struct scrollViewData; extern NSScrollView *mkScrollView(struct scrollViewCreateParams *p, struct scrollViewData **dout); extern void scrollViewSetScrolling(NSScrollView *sv, struct scrollViewData *d, BOOL hscroll, BOOL vscroll); extern void scrollViewFreeData(NSScrollView *sv, struct scrollViewData *d); From c74ac88598f1e0496411fc7197dd0adee44d39a2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 28 May 2016 00:21:30 -0400 Subject: [PATCH 0150/1329] Changed uiArea to use the new scroll view stuff on OS X. --- darwin/area.m | 50 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/darwin/area.m b/darwin/area.m index 5a786969..ff611a49 100644 --- a/darwin/area.m +++ b/darwin/area.m @@ -6,6 +6,7 @@ @interface areaView : NSView { uiArea *libui_a; NSTrackingArea *libui_ta; + NSSize libui_ss; } - (id)initWithFrame:(NSRect)r area:(uiArea *)a; - (uiModifiers)parseModifiers:(NSEvent *)e; @@ -16,6 +17,7 @@ - (int)doKeyUp:(NSEvent *)e; - (int)doFlagsChanged:(NSEvent *)e; - (void)setupNewTrackingArea; +- (void)setScrollingSize:(NSSize)s; @end struct uiArea { @@ -23,6 +25,7 @@ struct uiArea { NSView *view; // either sv or area depending on whether it is scrolling NSScrollView *sv; areaView *area; + struct scrollViewData *d; uiAreaHandler *ah; BOOL scrolling; }; @@ -35,6 +38,7 @@ struct uiArea { if (self) { self->libui_a = a; [self setupNewTrackingArea]; + self->libui_ss = r.size; } return self; } @@ -292,9 +296,34 @@ mouseEvent(otherMouseUp) [self setNeedsDisplay:YES]; } +- (void)setScrollingSize:(NSSize)s +{ + self->libui_ss = s; + [self invalidateIntrinsicContentSize]; +} + +- (NSSize)intrinsicContentSize +{ + if (!self->libui_a->scrolling) + return [super intrinsicContentSize]; + return self->libui_ss; +} + @end -uiDarwinControlAllDefaults(uiArea, view) +uiDarwinControlAllDefaultsExceptDestroy(uiArea, view) + +static void uiAreaDestroy(uiControl *c) +{ + uiArea *a = uiArea(c); + + if (a->scrolling) + scrollViewFreeData(a->sv, a->d); + [a->area release]; + if (a->scrolling) + [a->sv release]; + uiFreeControl(uiControl(a)); +} // called by subclasses of -[NSApplication sendEvent:] // by default, NSApplication eats some key events @@ -330,7 +359,7 @@ void uiAreaSetSize(uiArea *a, intmax_t width, intmax_t height) { if (!a->scrolling) userbug("You cannot call uiAreaSetSize() on a non-scrolling uiArea. (area: %p)", a); - [a->area setFrameSize:NSMakeSize(width, height)]; + [a->area setScrollingSize:NSMakeSize(width, height)]; } void uiAreaQueueRedrawAll(uiArea *a) @@ -365,23 +394,26 @@ uiArea *uiNewArea(uiAreaHandler *ah) uiArea *uiNewScrollingArea(uiAreaHandler *ah, intmax_t width, intmax_t height) { uiArea *a; + struct scrollViewCreateParams p; uiDarwinNewControl(uiArea, a); a->ah = ah; a->scrolling = YES; - a->sv = [[NSScrollView alloc] initWithFrame:NSZeroRect]; - // TODO configure a->sv for real - [a->sv setHasHorizontalScroller:YES]; - [a->sv setHasVerticalScroller:YES]; - a->area = [[areaView alloc] initWithFrame:NSMakeRect(0, 0, width, height) area:a]; - a->view = a->sv; + memset(&p, 0, sizeof (struct scrollViewCreateParams)); + p.DocumentView = a->area; + p.BackgroundColor = [NSColor controlColor]; + p.DrawsBackground = 1; + p.Bordered = NO; + p.HScroll = YES; + p.VScroll = YES; + a->sv = mkScrollView(&p, &(a->d)); - [a->sv setDocumentView:a->area]; + a->view = a->sv; return a; } From 119825f0de6787d8e96665510e7030394f4841c5 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 28 May 2016 00:35:56 -0400 Subject: [PATCH 0151/1329] More scroll view refinements. --- darwin/multilineentry.m | 3 ++- darwin/scrollview.m | 5 ++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/darwin/multilineentry.m b/darwin/multilineentry.m index afcb1fec..aa77891d 100644 --- a/darwin/multilineentry.m +++ b/darwin/multilineentry.m @@ -171,7 +171,8 @@ static uiMultilineEntry *finishMultilineEntry(BOOL hscroll) memset(&p, 0, sizeof (struct scrollViewCreateParams)); p.DocumentView = e->tv; - p.BackgroundColor = nil; + // this is what Interface Builder sets it to + p.BackgroundColor = [NSColor colorWithCalibratedWhite:1.0 alpha:1.0]; p.DrawsBackground = YES; p.Bordered = YES; p.HScroll = hscroll; diff --git a/darwin/scrollview.m b/darwin/scrollview.m index 90ef504f..6f441e04 100644 --- a/darwin/scrollview.m +++ b/darwin/scrollview.m @@ -17,14 +17,13 @@ NSScrollView *mkScrollView(struct scrollViewCreateParams *p, struct scrollViewDa struct scrollViewData *d; sv = [[NSScrollView alloc] initWithFrame:NSZeroRect]; - // TODO verify background color for programmatically created NSTextView if (p->BackgroundColor != nil) [sv setBackgroundColor:p->BackgroundColor]; [sv setDrawsBackground:p->DrawsBackground]; border = NSNoBorder; if (p->Bordered) border = NSBezelBorder; - // TODO verify document cursor for programmatically created NSTextView + // document view seems to set the cursor properly [sv setBorderType:border]; [sv setAutohidesScrollers:YES]; [sv setHasHorizontalRuler:NO]; @@ -32,7 +31,7 @@ NSScrollView *mkScrollView(struct scrollViewCreateParams *p, struct scrollViewDa [sv setRulersVisible:NO]; [sv setScrollerKnobStyle:NSScrollerKnobStyleDefault]; // the scroller style is documented as being set by default for us - // TODO verify line and page for programmatically created NSTextView + // LONGTERM verify line and page for programmatically created NSTableView [sv setScrollsDynamically:YES]; [sv setFindBarPosition:NSScrollViewFindBarPositionAboveContent]; [sv setUsesPredominantAxisScrolling:NO]; From 4c98fda7ff5c070684719200ce2f52d7985804d5 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 28 May 2016 11:36:16 -0400 Subject: [PATCH 0152/1329] Added Enable/Disable buttons to page 6. --- test/page6.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/test/page6.c b/test/page6.c index 332b2e66..896b3d50 100644 --- a/test/page6.c +++ b/test/page6.c @@ -72,10 +72,19 @@ static void redraw(uiCombobox *c, void *data) uiAreaQueueRedrawAll(area); } +static void enableArea(uiButton *b, void *data) +{ + if (data != NULL) + uiControlEnable(uiControl(area)); + else + uiControlDisable(uiControl(area)); +} + uiBox *makePage6(void) { uiBox *page6; uiBox *hbox; + uiButton *button; handler.ah.Draw = handlerDraw; handler.ah.MouseEvent = handlerMouseEvent; @@ -99,8 +108,19 @@ uiBox *makePage6(void) area = uiNewArea((uiAreaHandler *) (&handler)); uiBoxAppend(page6, uiControl(area), 1); + hbox = newHorizontalBox(); + uiBoxAppend(page6, uiControl(hbox), 0); + swallowKeys = uiNewCheckbox("Consider key events handled"); - uiBoxAppend(page6, uiControl(swallowKeys), 0); + uiBoxAppend(hbox, uiControl(swallowKeys), 1); + + button = uiNewButton("Enable"); + uiButtonOnClicked(button, enableArea, button); + uiBoxAppend(hbox, uiControl(button), 0); + + button = uiNewButton("Disable"); + uiButtonOnClicked(button, enableArea, NULL); + uiBoxAppend(hbox, uiControl(button), 0); return page6; } From 3d9d782db3d8f0eb1c110ec189b0efa3048afea1 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 28 May 2016 12:32:47 -0400 Subject: [PATCH 0153/1329] Implemented enabling/disabling on uiArea on OS X. --- darwin/area.m | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/darwin/area.m b/darwin/area.m index ff611a49..221c39fc 100644 --- a/darwin/area.m +++ b/darwin/area.m @@ -1,12 +1,11 @@ // 9 september 2015 #import "uipriv_darwin.h" -// TODO implement setEnabled: - @interface areaView : NSView { uiArea *libui_a; NSTrackingArea *libui_ta; NSSize libui_ss; + BOOL libui_enabled; } - (id)initWithFrame:(NSRect)r area:(uiArea *)a; - (uiModifiers)parseModifiers:(NSEvent *)e; @@ -18,6 +17,8 @@ - (int)doFlagsChanged:(NSEvent *)e; - (void)setupNewTrackingArea; - (void)setScrollingSize:(NSSize)s; +- (BOOL)isEnabled; +- (void)setEnabled:(BOOL)e; @end struct uiArea { @@ -195,7 +196,8 @@ struct uiArea { me.Held1To64 |= j; } - (*(a->ah->MouseEvent))(a->ah, a, &me); + if (self->libui_enabled) + (*(a->ah->MouseEvent))(a->ah, a, &me); } #define mouseEvent(name) \ @@ -218,14 +220,16 @@ mouseEvent(otherMouseUp) { uiArea *a = self->libui_a; - (*(a->ah->MouseCrossed))(a->ah, a, 0); + if (self->libui_enabled) + (*(a->ah->MouseCrossed))(a->ah, a, 0); } - (void)mouseExited:(NSEvent *)e { uiArea *a = self->libui_a; - (*(a->ah->MouseCrossed))(a->ah, a, 1); + if (self->libui_enabled) + (*(a->ah->MouseCrossed))(a->ah, a, 1); } // note: there is no equivalent to WM_CAPTURECHANGED on Mac OS X; there literally is no way to break a grab like that @@ -309,6 +313,24 @@ mouseEvent(otherMouseUp) return self->libui_ss; } +- (BOOL)becomeFirstResponder +{ + return [self isEnabled]; +} + +- (BOOL)isEnabled +{ + return self->libui_enabled; +} + +- (void)setEnabled:(BOOL)e +{ + self->libui_enabled = e; + if (!self->libui_enabled && [self window] != nil) + if ([[self window] firstResponder] == self) + [[self window] makeFirstResponder:nil]; +} + @end uiDarwinControlAllDefaultsExceptDestroy(uiArea, view) From ba97455ab349e01c367d1c27c363b72037e59b7f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 28 May 2016 12:40:30 -0400 Subject: [PATCH 0154/1329] Stale TODO removal. --- darwin/box.m | 3 --- 1 file changed, 3 deletions(-) diff --git a/darwin/box.m b/darwin/box.m index 7e480285..4dec15b4 100644 --- a/darwin/box.m +++ b/darwin/box.m @@ -150,7 +150,6 @@ struct uiBox { return uiDarwinPaddingAmount(NULL); } -// TODO something about spinbox hugging - (void)establishOurConstraints { boxChild *bc; @@ -294,7 +293,6 @@ struct uiBox { boxChild *bc; int stretchy; - // TODO separate into a method? bc = (boxChild *) [self->children objectAtIndex:n]; stretchy = bc.stretchy; @@ -328,7 +326,6 @@ struct uiBox { padding = [self paddingAmount]; for (c in self->inBetweens) [c setConstant:-padding]; - // TODO call anything? } - (BOOL)hugsTrailing From 0d6dac5c5adfa3d59ab0b8e430817793f3d12c0f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 28 May 2016 13:06:16 -0400 Subject: [PATCH 0155/1329] Migrated from GTK+ 3.4 to 3.10 and from OS X 10.7 to 10.8. --- README.md | 7 +++++-- TODO.md | 4 ++-- build/GNUmakefile.test | 2 +- darwin/GNUfiles.mk | 10 +++++----- darwin/radiobuttons.m | 1 - darwin/uipriv_darwin.h | 7 ++----- unix/stddialogs.c | 10 +++++----- unix/uipriv_unix.h | 8 ++++---- 8 files changed, 24 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index e24a9a1c..a0784b7d 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,9 @@ This README is being written.
*Note that today's entry may be updated later today.* +* **28 May 2016** + * As promised, **the minimum system requirements are now OS X 10.8 and GTK+ 3.10 for OS X and Unix, respectively**. + * **26 May 2016** * Two OS X-specific functions have been added: `uiDarwinMarginAmount()` and `uiDarwinPaddingAmount()`. These return the amount of margins and padding, respectively, to give to a control, and are intended for container implementations. These are suitable for the constant of a NSLayoutConstraint. They both take a pointer parameter that is reserved for future use and should be `NULL`. @@ -50,8 +53,8 @@ This README is being written.
## Runtime Requirements * Windows: Windows Vista SP2 with Platform Update or newer -* Unix: GTK+ 3.4 or newer -* Mac OS X: OS X 10.7 or newer +* Unix: GTK+ 3.10 or newer +* Mac OS X: OS X 10.8 or newer ## Build Requirements diff --git a/TODO.md b/TODO.md index 0df9f24a..e66885ec 100644 --- a/TODO.md +++ b/TODO.md @@ -6,9 +6,9 @@ - provisions for controls that cannot be grown? especiailly for windows -- LC_VERSION_MIN_MACOSX has the 10.11 SDK; see if we can knock it down to 10.7 too; OS version is fine +- LC_VERSION_MIN_MACOSX has the 10.11 SDK; see if we can knock it down to 10.8 too; OS version is fine - apply the OS version stuff to the test program and examples too - - what about micro versions (10.7.x)? force 10.7.0? + - what about micro versions (10.8.x)? force 10.8.0? - go through ALL the objective-c objects we create and make sure we are using the proper ownership (alloc/init and new are owned by us, all class method constructors are autoreleased - thanks mikeash) diff --git a/build/GNUmakefile.test b/build/GNUmakefile.test index 66e1921c..1179f8e8 100644 --- a/build/GNUmakefile.test +++ b/build/GNUmakefile.test @@ -18,7 +18,7 @@ ifeq ($(TOOLCHAIN),gcc) # tell the dynamic loader to search in the executable path for shared objects # note: OS X's linker complains if we say -rpath= instead of -rpath, # also note that OS X doesn't use $ORIGIN - see http://jorgen.tjer.no/post/2014/05/20/dt-rpath-ld-and-at-rpath-dyld/ - # the extra slash is needed on OS X 10.7 + # the extra slash is needed on OS X 10.7; have it around just to be correct ifeq ($(OS),darwin) LDFLAGS += -Wl,-rpath,@executable_path/ else diff --git a/darwin/GNUfiles.mk b/darwin/GNUfiles.mk index aacf382f..ed145a9d 100644 --- a/darwin/GNUfiles.mk +++ b/darwin/GNUfiles.mk @@ -49,13 +49,13 @@ LDFLAGS += \ # flags for OS X versioning CFLAGS += \ - -mmacosx-version-min=10.7 \ - -DMACOSX_DEPLOYMENT_TARGET=10.7 + -mmacosx-version-min=10.8 \ + -DMACOSX_DEPLOYMENT_TARGET=10.8 CXXFLAGS += \ - -mmacosx-version-min=10.7 \ - -DMACOSX_DEPLOYMENT_TARGET=10.7 + -mmacosx-version-min=10.8 \ + -DMACOSX_DEPLOYMENT_TARGET=10.8 LDFLAGS += \ - -mmacosx-version-min=10.7 + -mmacosx-version-min=10.8 # flags for building a shared library LDFLAGS += \ diff --git a/darwin/radiobuttons.m b/darwin/radiobuttons.m index 5af0bdea..a0228967 100644 --- a/darwin/radiobuttons.m +++ b/darwin/radiobuttons.m @@ -4,7 +4,6 @@ // In the old days you would use a NSMatrix for this; as of OS X 10.8 this was deprecated and now you need just a bunch of NSButtons with the same superview AND same action method. // This is documented on the NSMatrix page, but the rest of the OS X documentation says to still use NSMatrix. // NSMatrix has weird quirks anyway... -// TODO this does not work on 10.7 // TODO // - check that multiple radio buttons on the same parent container work right diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 0bc79f37..c1e842ca 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -1,6 +1,6 @@ // 6 january 2015 -#define MAC_OS_X_VERSION_MIN_REQUIRED MAC_OS_X_VERSION_10_7 -#define MAC_OS_X_VERSION_MAX_ALLOWED MAC_OS_X_VERSION_10_7 +#define MAC_OS_X_VERSION_MIN_REQUIRED MAC_OS_X_VERSION_10_8 +#define MAC_OS_X_VERSION_MAX_ALLOWED MAC_OS_X_VERSION_10_8 #import #import "../ui.h" #import "../ui_darwin.h" @@ -10,9 +10,6 @@ #error Sorry, libui cannot be compiled with ARC. #endif -// 10.7 fixups -#define NSEventModifierFlags NSUInteger - #define toNSString(str) [NSString stringWithUTF8String:(str)] #define fromNSString(str) [(str) UTF8String] diff --git a/unix/stddialogs.c b/unix/stddialogs.c index e4653442..53e3301a 100644 --- a/unix/stddialogs.c +++ b/unix/stddialogs.c @@ -6,7 +6,7 @@ #define windowWindow(w) (GTK_WINDOW(uiControlHandle(uiControl(w)))) -static char *filedialog(GtkWindow *parent, GtkFileChooserAction mode, const gchar *stock) +static char *filedialog(GtkWindow *parent, GtkFileChooserAction mode, const gchar *confirm) { GtkWidget *fcd; GtkFileChooser *fc; @@ -14,8 +14,8 @@ static char *filedialog(GtkWindow *parent, GtkFileChooserAction mode, const gcha char *filename; fcd = gtk_file_chooser_dialog_new(NULL, parent, mode, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - stock, GTK_RESPONSE_ACCEPT, + "_Cancel", GTK_RESPONSE_CANCEL, + confirm, GTK_RESPONSE_ACCEPT, NULL); fc = GTK_FILE_CHOOSER(fcd); gtk_file_chooser_set_local_only(fc, FALSE); @@ -35,12 +35,12 @@ static char *filedialog(GtkWindow *parent, GtkFileChooserAction mode, const gcha char *uiOpenFile(uiWindow *parent) { - return filedialog(windowWindow(parent), GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_OPEN); + return filedialog(windowWindow(parent), GTK_FILE_CHOOSER_ACTION_OPEN, "_Open"); } char *uiSaveFile(uiWindow *parent) { - return filedialog(windowWindow(parent), GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_SAVE); + return filedialog(windowWindow(parent), GTK_FILE_CHOOSER_ACTION_SAVE, "_Save"); } static void msgbox(GtkWindow *parent, const char *title, const char *description, GtkMessageType type, GtkButtonsType buttons) diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index cd89e869..785c5824 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -1,8 +1,8 @@ // 22 april 2015 -#define GLIB_VERSION_MIN_REQUIRED GLIB_VERSION_2_32 -#define GLIB_VERSION_MAX_ALLOWED GLIB_VERSION_2_32 -#define GDK_VERSION_MIN_REQUIRED GDK_VERSION_3_4 -#define GDK_VERSION_MAX_ALLOWED GDK_VERSION_3_4 +#define GLIB_VERSION_MIN_REQUIRED GLIB_VERSION_2_40 +#define GLIB_VERSION_MAX_ALLOWED GLIB_VERSION_2_40 +#define GDK_VERSION_MIN_REQUIRED GDK_VERSION_3_10 +#define GDK_VERSION_MAX_ALLOWED GDK_VERSION_3_10 #include #include #include // see drawtext.c From dbf8316e9ecda72c8164c1c82119cf1d21190058 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 28 May 2016 13:13:38 -0400 Subject: [PATCH 0156/1329] More notes. --- TODO.md | 1 + 1 file changed, 1 insertion(+) diff --git a/TODO.md b/TODO.md index e66885ec..ea0c2db6 100644 --- a/TODO.md +++ b/TODO.md @@ -64,6 +64,7 @@ notes to self - test RTL - automate RTL +- now that stock items are deprecated, I have to maintain translations of the Cancel, Open, and Save buttons on GTK+ myself (thanks baedert in irc.gimp.net/#gtk+) - http://blogs.msdn.com/b/oldnewthing/archive/2014/02/26/10503148.aspx From e45e0e89e97b97710b4dee0c0d473f6bfc8cabef Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 28 May 2016 13:19:10 -0400 Subject: [PATCH 0157/1329] More notes. --- TODO.md | 1 + 1 file changed, 1 insertion(+) diff --git a/TODO.md b/TODO.md index ea0c2db6..4c38b3f2 100644 --- a/TODO.md +++ b/TODO.md @@ -65,6 +65,7 @@ notes to self - test RTL - automate RTL - now that stock items are deprecated, I have to maintain translations of the Cancel, Open, and Save buttons on GTK+ myself (thanks baedert in irc.gimp.net/#gtk+) + - either that or keep using stock items - http://blogs.msdn.com/b/oldnewthing/archive/2014/02/26/10503148.aspx From a6c48e087bfc793a54baf973f8be75568395dfba Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 28 May 2016 15:57:01 -0400 Subject: [PATCH 0158/1329] 10.8 fixups. --- darwin/area.m | 3 +++ 1 file changed, 3 insertions(+) diff --git a/darwin/area.m b/darwin/area.m index 221c39fc..6d404077 100644 --- a/darwin/area.m +++ b/darwin/area.m @@ -1,6 +1,9 @@ // 9 september 2015 #import "uipriv_darwin.h" +// 10.8 fixups +#define NSEventModifierFlags NSUInteger + @interface areaView : NSView { uiArea *libui_a; NSTrackingArea *libui_ta; From 52c3c52b568711068e4856fbac9a7c56262b1ed9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 28 May 2016 16:03:36 -0400 Subject: [PATCH 0159/1329] Removed a TODO I can no longer fully confirm (only the focus rect of the NSComboBox seems to be clipped, which I'm sure is not my bug). --- darwin/box.m | 3 --- 1 file changed, 3 deletions(-) diff --git a/darwin/box.m b/darwin/box.m index 4dec15b4..bcc5071c 100644 --- a/darwin/box.m +++ b/darwin/box.m @@ -1,9 +1,6 @@ // 15 august 2015 #import "uipriv_darwin.h" -// TODOs to confirm -// - 10.8: if we switch to page 4, then switch back to page 1, check Spaced, and go back to page 4, some controls (progress bar, popup button) are clipped on the sides - @interface boxChild : NSObject @property uiControl *c; @property BOOL stretchy; From 1f9f317e7ab2889c3bd4c1549609360ae2e02575 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 28 May 2016 17:04:28 -0400 Subject: [PATCH 0160/1329] More TODOs. --- darwin/scrollview.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/darwin/scrollview.m b/darwin/scrollview.m index 6f441e04..fa3c7452 100644 --- a/darwin/scrollview.m +++ b/darwin/scrollview.m @@ -1,6 +1,8 @@ // 27 may 2016 #include "uipriv_darwin.h" +// TODO mouse test scroll view no longer works; scrolled drawing test may have a random size outside the area size + struct scrollViewData { NSLayoutConstraint *documentLeading; NSLayoutConstraint *documentTop; From ec4efe027b0264f91ab47a433fb7e1665e5a53c4 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 28 May 2016 17:36:31 -0400 Subject: [PATCH 0161/1329] Some TODO resolution. --- darwin/area.m | 1 + darwin/scrollview.m | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/darwin/area.m b/darwin/area.m index 6d404077..656a1488 100644 --- a/darwin/area.m +++ b/darwin/area.m @@ -43,6 +43,7 @@ struct uiArea { self->libui_a = a; [self setupNewTrackingArea]; self->libui_ss = r.size; + self->libui_enabled = YES; } return self; } diff --git a/darwin/scrollview.m b/darwin/scrollview.m index fa3c7452..5767e00f 100644 --- a/darwin/scrollview.m +++ b/darwin/scrollview.m @@ -1,7 +1,7 @@ // 27 may 2016 #include "uipriv_darwin.h" -// TODO mouse test scroll view no longer works; scrolled drawing test may have a random size outside the area size +// TODO scrolled drawing test may have a random size outside the area size struct scrollViewData { NSLayoutConstraint *documentLeading; From 18a84988fdd64895f7090a9be6004790ed205416 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 28 May 2016 17:41:50 -0400 Subject: [PATCH 0162/1329] Moved sv/ into _wip/. --- {sv => _wip/sv}/normal | 0 {sv => _wip/sv}/normal.nots | 0 {sv => _wip/sv}/outlineview | 0 {sv => _wip/sv}/outlineview.nots | 0 {sv => _wip/sv}/sourcelist | 0 {sv => _wip/sv}/sourcelist.nots | 0 {sv => _wip/sv}/tableview | 0 {sv => _wip/sv}/tableview.nots | 0 {sv => _wip/sv}/textview | 0 {sv => _wip/sv}/textview.nots | 0 10 files changed, 0 insertions(+), 0 deletions(-) rename {sv => _wip/sv}/normal (100%) rename {sv => _wip/sv}/normal.nots (100%) rename {sv => _wip/sv}/outlineview (100%) rename {sv => _wip/sv}/outlineview.nots (100%) rename {sv => _wip/sv}/sourcelist (100%) rename {sv => _wip/sv}/sourcelist.nots (100%) rename {sv => _wip/sv}/tableview (100%) rename {sv => _wip/sv}/tableview.nots (100%) rename {sv => _wip/sv}/textview (100%) rename {sv => _wip/sv}/textview.nots (100%) diff --git a/sv/normal b/_wip/sv/normal similarity index 100% rename from sv/normal rename to _wip/sv/normal diff --git a/sv/normal.nots b/_wip/sv/normal.nots similarity index 100% rename from sv/normal.nots rename to _wip/sv/normal.nots diff --git a/sv/outlineview b/_wip/sv/outlineview similarity index 100% rename from sv/outlineview rename to _wip/sv/outlineview diff --git a/sv/outlineview.nots b/_wip/sv/outlineview.nots similarity index 100% rename from sv/outlineview.nots rename to _wip/sv/outlineview.nots diff --git a/sv/sourcelist b/_wip/sv/sourcelist similarity index 100% rename from sv/sourcelist rename to _wip/sv/sourcelist diff --git a/sv/sourcelist.nots b/_wip/sv/sourcelist.nots similarity index 100% rename from sv/sourcelist.nots rename to _wip/sv/sourcelist.nots diff --git a/sv/tableview b/_wip/sv/tableview similarity index 100% rename from sv/tableview rename to _wip/sv/tableview diff --git a/sv/tableview.nots b/_wip/sv/tableview.nots similarity index 100% rename from sv/tableview.nots rename to _wip/sv/tableview.nots diff --git a/sv/textview b/_wip/sv/textview similarity index 100% rename from sv/textview rename to _wip/sv/textview diff --git a/sv/textview.nots b/_wip/sv/textview.nots similarity index 100% rename from sv/textview.nots rename to _wip/sv/textview.nots From 5f1e8fa6019ce36744cb59577779009cba3d4471 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Wed, 25 May 2016 15:18:48 -0700 Subject: [PATCH 0163/1329] Add support for static linking. Use `make STATIC=1` to build a static library. Some language ecosystems, like those of Rust, have a convention of static linking in order to make binaries easier to distribute. In those environments, this feature helps libui fit in more easily. In order to prevent internal symbols from linking, we first create an intermediate object file with `ld -r` and strip private symbols from it before using `ar` to create the library. --- GNUmakefile | 7 ++++++- build/GNUbasegcc.mk | 7 +------ build/GNUbasemsvc.mk | 4 ++++ build/GNUmakefile.example | 6 +++++- build/GNUmakefile.libui | 4 ++++ darwin/GNUfiles.mk | 7 +++---- darwin/GNUosspecific.mk | 7 +++++++ darwin/GNUosspecificlink.mk | 15 +++++++++++++++ unix/GNUfiles.mk | 11 +++++------ unix/GNUosspecific.mk | 9 +++++++++ unix/GNUosspecificlink.mk | 15 +++++++++++++++ windows/GNUfiles.mk | 2 ++ windows/GNUosspecific.mk | 1 + 13 files changed, 77 insertions(+), 18 deletions(-) create mode 100644 darwin/GNUosspecificlink.mk create mode 100644 unix/GNUosspecificlink.mk diff --git a/GNUmakefile b/GNUmakefile index 39b91cea..631495a4 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -46,9 +46,14 @@ export DESTDIR # other important variables export OBJDIR export OUTDIR +export STATIC + +ifneq (,$(STATIC)) +STATICLIB = STATICLIB=1 +endif libui: - @$(MAKE) -f build/GNUmakefile.libui inlibuibuild=1 + @$(MAKE) -f build/GNUmakefile.libui inlibuibuild=1 $(STATICLIB) clean: rm -rf $(OBJDIR) $(OUTDIR) diff --git a/build/GNUbasegcc.mk b/build/GNUbasegcc.mk index 757d8fd7..b6741d1d 100644 --- a/build/GNUbasegcc.mk +++ b/build/GNUbasegcc.mk @@ -50,12 +50,7 @@ else reallinker = $(CXX) endif -$(OUT): $(OFILES) | $(OUTDIR) - @$(reallinker) -o $(OUT) $(OFILES) $(LDFLAGS) -ifeq ($(USESSONAME),1) - @ln -sf $(NAME)$(SUFFIX) $(OUTNOSONAME) -endif - @echo ====== Linked $(OUT) +include $(OS)/GNUosspecificlink.mk .SECONDEXPANSION: diff --git a/build/GNUbasemsvc.mk b/build/GNUbasemsvc.mk index 7e1bb6bf..46703b87 100644 --- a/build/GNUbasemsvc.mk +++ b/build/GNUbasemsvc.mk @@ -69,7 +69,11 @@ OUT = $(OUTDIR)/$(NAME)$(SUFFIX) # TODO use $(CC), $(CXX), $(LD), and s$(RC) $(OUT): $(OFILES) | $(OUTDIR) +ifeq (,$(STATICLIB)) @link -out:$(OUT) $(OFILES) $(LDFLAGS) +else + @lib -out:$(OUT) $(OFILES) +endif @echo ====== Linked $(OUT) .SECONDEXPANSION: diff --git a/build/GNUmakefile.example b/build/GNUmakefile.example index 1219ac74..2b9d4cbd 100644 --- a/build/GNUmakefile.example +++ b/build/GNUmakefile.example @@ -30,7 +30,11 @@ SUFFIX = $(EXESUFFIX) # TODO merge with the one in build/GNUmakefile.test ifeq ($(TOOLCHAIN),gcc) - LDFLAGS += -L$(OUTDIR) -lui + ifeq (,$(STATIC)) + LDFLAGS += -L$(OUTDIR) -lui + else + LDFLAGS += -L$(OUTDIR) -lui $(NATIVE_UI_LDFLAGS) + endif # see build/GNUmakefile.test ifeq ($(OS),darwin) LDFLAGS += -Wl,-rpath,@executable_path/ diff --git a/build/GNUmakefile.libui b/build/GNUmakefile.libui index 41b106ae..96f11c28 100644 --- a/build/GNUmakefile.libui +++ b/build/GNUmakefile.libui @@ -17,10 +17,14 @@ HFILES += \ ui_$(OS)$(OSHSUFFIX) NAME = libui +ifeq (,$(STATIC)) SUFFIX = $(LIBSUFFIX) ifeq ($(USESSONAME),1) SUFFIX = $(SONAMEEXT) endif +else +SUFFIX = $(STATICLIBSUFFIX) +endif ifeq ($(TOOLCHAIN),gcc) # make every symbol hidden by default except _UI_EXTERN ones diff --git a/darwin/GNUfiles.mk b/darwin/GNUfiles.mk index ed145a9d..9b9ed693 100644 --- a/darwin/GNUfiles.mk +++ b/darwin/GNUfiles.mk @@ -42,10 +42,7 @@ HFILES += \ # LONGTERM split into a separate file or put in GNUmakefile.libui somehow? # flags for Cocoa -LDFLAGS += \ - -lobjc \ - -framework Foundation \ - -framework AppKit +LDFLAGS += $(NATIVE_UI_LDFLAGS) # flags for OS X versioning CFLAGS += \ @@ -57,9 +54,11 @@ CXXFLAGS += \ LDFLAGS += \ -mmacosx-version-min=10.8 +ifeq (,$(STATIC)) # flags for building a shared library LDFLAGS += \ -dynamiclib +endif # on warning about undefined symbols: # the gcc flags don't work with Apple's linker diff --git a/darwin/GNUosspecific.mk b/darwin/GNUosspecific.mk index cc13d1b1..9f948e73 100644 --- a/darwin/GNUosspecific.mk +++ b/darwin/GNUosspecific.mk @@ -3,6 +3,7 @@ EXESUFFIX = LIBSUFFIX = .dylib OSHSUFFIX = .h +STATICLIBSUFFIX = .a TOOLCHAIN = gcc USESSONAME = 1 @@ -11,3 +12,9 @@ SONAMEEXT = .$(SOVERSION)$(LIBSUFFIX) # note the explicit need for @rpath # LONGTERM -current_version, -compatibility_version SONAMEFLAG = -Wl,-install_name,@rpath/ + +NATIVE_UI_LDFLAGS += \ + -lobjc \ + -framework Foundation \ + -framework AppKit + diff --git a/darwin/GNUosspecificlink.mk b/darwin/GNUosspecificlink.mk new file mode 100644 index 00000000..0b9087e6 --- /dev/null +++ b/darwin/GNUosspecificlink.mk @@ -0,0 +1,15 @@ +# 28 may 2016 + +$(OUT): $(OFILES) | $(OUTDIR) +ifeq (,$(STATICLIB)) + @$(reallinker) -o $(OUT) $(OFILES) $(LDFLAGS) +ifeq ($(USESSONAME),1) + @ln -sf $(NAME)$(SUFFIX) $(OUTNOSONAME) +endif +else + nm -m $(OFILES) | sed -E -n 's/^[0-9a-f]* \([A-Z_]+,[a-z_]+\) external //p' > $(OBJDIR)/symbols + $(LD) -exported_symbols_list $(OBJDIR)/symbols -r $(OFILES) -o $(OUT:%.a=%.o) + $(AR) rcs $(OUT) $(OUT:%.a=%.o) +endif + @echo ====== Linked $(OUT) + diff --git a/unix/GNUfiles.mk b/unix/GNUfiles.mk index b4a1784c..adc345ec 100644 --- a/unix/GNUfiles.mk +++ b/unix/GNUfiles.mk @@ -43,17 +43,16 @@ HFILES += \ # LONGTERM split into a separate file or put in GNUmakefile.libui somehow? # flags for GTK+ -CFLAGS += \ - `pkg-config --cflags gtk+-3.0` -CXXFLAGS += \ - `pkg-config --cflags gtk+-3.0` -LDFLAGS += \ - `pkg-config --libs gtk+-3.0` -lm -ldl +CFLAGS += $(NATIVE_UI_CFLAGS) +CXXFLAGS += $(NATIVE_UI_CXXFLAGS) +LDFLAGS += $(NATIVE_UI_LDFLAGS) # flags for building a shared library # OS X does support -shared but it has a preferred name for this so let's use that there instead; hence this is not gcc-global +ifeq (,$(STATIC)) LDFLAGS += \ -shared +endif # flags for warning on undefined symbols # this is not gcc-global because OS X doesn't support these flags diff --git a/unix/GNUosspecific.mk b/unix/GNUosspecific.mk index 5f6fc938..77b91fbb 100644 --- a/unix/GNUosspecific.mk +++ b/unix/GNUosspecific.mk @@ -3,6 +3,7 @@ EXESUFFIX = LIBSUFFIX = .so OSHSUFFIX = .h +STATICLIBSUFFIX = .a TOOLCHAIN = gcc # LONGTERM clean up all the NAMEs and SUFFIXs and NOSOSUFFIXs or whatever it was @@ -11,3 +12,11 @@ SOVERSION = $(SOVERSION0) SONAMEEXT = $(LIBSUFFIX).$(SOVERSION) # this is not gcc-global because OS X uses a different filename format SONAMEFLAG = -Wl,-soname, + +NATIVE_UI_CFLAGS = \ + `pkg-config --cflags gtk+-3.0` +NATIVE_UI_CXXFLAGS = \ + `pkg-config --cflags gtk+-3.0` +NATIVE_UI_LDFLAGS = \ + `pkg-config --libs gtk+-3.0` -lm -ldl + diff --git a/unix/GNUosspecificlink.mk b/unix/GNUosspecificlink.mk new file mode 100644 index 00000000..573b289f --- /dev/null +++ b/unix/GNUosspecificlink.mk @@ -0,0 +1,15 @@ +# 28 may 2016 + +$(OUT): $(OFILES) | $(OUTDIR) +ifeq (,$(STATICLIB)) + @$(reallinker) -o $(OUT) $(OFILES) $(LDFLAGS) +ifeq ($(USESSONAME),1) + @ln -sf $(NAME)$(SUFFIX) $(OUTNOSONAME) +endif +else + $(LD) -r $(OFILES) -o $(OUT:%.a=%.o) + objcopy --localize-hidden $(OUT:%.a=%.o) + $(AR) rcs $(OUT) $(OUT:%.a=%.o) +endif + @echo ====== Linked $(OUT) + diff --git a/windows/GNUfiles.mk b/windows/GNUfiles.mk index 905d6597..d9e992d2 100644 --- a/windows/GNUfiles.mk +++ b/windows/GNUfiles.mk @@ -73,8 +73,10 @@ LDFLAGS += \ user32.lib kernel32.lib usp10.lib gdi32.lib comctl32.lib uxtheme.lib msimg32.lib comdlg32.lib d2d1.lib dwrite.lib ole32.lib oleaut32.lib oleacc.lib uuid.lib # flags for building a shared library +ifeq (,$(STATIC)) LDFLAGS += \ -dll +endif # TODO flags for warning on undefined symbols diff --git a/windows/GNUosspecific.mk b/windows/GNUosspecific.mk index a4da078f..c8335d66 100644 --- a/windows/GNUosspecific.mk +++ b/windows/GNUosspecific.mk @@ -3,6 +3,7 @@ EXESUFFIX = .exe LIBSUFFIX = .dll OSHSUFFIX = .h +STATICLIBSUFFIX = .lib TOOLCHAIN = msvc USESSONAME = 0 From eb11452dd371a831d40a407a7b38ad1d54f126cb Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 28 May 2016 18:01:25 -0400 Subject: [PATCH 0164/1329] More TODO resolution. Some TODOs were moved to the issue tracker. --- darwin/box.m | 5 +++-- darwin/checkbox.m | 3 ++- darwin/colorbutton.m | 7 +++++-- darwin/draw.m | 2 +- darwin/editablecombo.m | 2 +- darwin/group.m | 3 --- darwin/main.m | 3 +-- 7 files changed, 13 insertions(+), 12 deletions(-) diff --git a/darwin/box.m b/darwin/box.m index bcc5071c..8661d902 100644 --- a/darwin/box.m +++ b/darwin/box.m @@ -266,7 +266,7 @@ struct uiBox { if (bc.stretchy) priority = NSLayoutPriorityDefaultLow; else - // TODO will default high work? + // LONGTERM will default high work? priority = NSLayoutPriorityRequired; uiDarwinControlSetHuggingPriority(uiDarwinControl(bc.c), priority, self->primaryOrientation); // make sure controls don't hug their secondary direction so they fill the width of the view @@ -398,7 +398,8 @@ uiDarwinControlDefaultSetHuggingPriority(uiBox, view) void uiBoxAppend(uiBox *b, uiControl *c, int stretchy) { - // TODO on other platforms + // LONGTERM on other platforms + // or at leat allow this and implicitly turn it into a spacer if (c == NULL) userbug("You cannot add NULL to a uiBox."); [b->view append:c stretchy:stretchy]; diff --git a/darwin/checkbox.m b/darwin/checkbox.m index cdacf419..dd1ce093 100644 --- a/darwin/checkbox.m +++ b/darwin/checkbox.m @@ -110,11 +110,12 @@ uiCheckbox *uiNewCheckbox(const char *text) uiDarwinNewControl(uiCheckbox, c); - // TODO bezel style? transparent? c->button = [[NSButton alloc] initWithFrame:NSZeroRect]; [c->button setTitle:toNSString(text)]; [c->button setButtonType:NSSwitchButton]; + // doesn't seem to have an associated bezel style [c->button setBordered:NO]; + [c->button setTransparent:NO]; uiDarwinSetControlFont(c->button, NSRegularControlSize); if (checkboxDelegate == nil) { diff --git a/darwin/colorbutton.m b/darwin/colorbutton.m index 26100a0d..28a604af 100644 --- a/darwin/colorbutton.m +++ b/darwin/colorbutton.m @@ -6,6 +6,7 @@ @interface colorButton : NSColorWell { uiColorButton *libui_b; BOOL libui_changing; + BOOL libui_setting; } - (id)initWithFrame:(NSRect)frame libuiColorButton:(uiColorButton *)b; - (void)deactivateOnClose:(NSNotification *)note; @@ -77,7 +78,8 @@ struct uiColorButton { [super setColor:color]; // this is called by NSColorWell's init, so we have to guard - if (b != nil) + // also don't signal during a programmatic change + if (b != nil && !self->libui_setting) (*(b->onChanged))(b, b->onChangedData); } @@ -98,8 +100,9 @@ struct uiColorButton { - (void)libuiSetColor:(double)r g:(double)g b:(double)b a:(double)a { - // TODO does this set the panel color? does it send a signal? + self->libui_setting = YES; [self setColor:[NSColor colorWithSRGBRed:r green:g blue:b alpha:a]]; + self->libui_setting = NO; } // NSColorWell has no intrinsic size by default; give it at least the height of the equivalent button's diff --git a/darwin/draw.m b/darwin/draw.m index 68d02bc0..262ad3e2 100644 --- a/darwin/draw.m +++ b/darwin/draw.m @@ -197,7 +197,7 @@ void uiDrawStroke(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b, uiDrawStro // for a solid fill, we can merely have Core Graphics fill directly static void fillSolid(CGContextRef ctxt, uiDrawPath *p, uiDrawBrush *b) { - // TODO this uses DeviceRGB; switch to sRGB? + // TODO this uses DeviceRGB; switch to sRGB CGContextSetRGBFillColor(ctxt, b->R, b->G, b->B, b->A); switch (p->fillMode) { case uiDrawFillModeWinding: diff --git a/darwin/editablecombo.m b/darwin/editablecombo.m index 67259521..c676c562 100644 --- a/darwin/editablecombo.m +++ b/darwin/editablecombo.m @@ -126,7 +126,7 @@ void uiEditableComboboxSetText(uiEditableCombobox *c, const char *text) } #if 0 -// TODO +// LONGTERM void uiEditableComboboxSetSelected(uiEditableCombobox *c, intmax_t n) { if (c->editable) { diff --git a/darwin/group.m b/darwin/group.m index 4baca240..8dba62c3 100644 --- a/darwin/group.m +++ b/darwin/group.m @@ -1,8 +1,6 @@ // 14 august 2015 #import "uipriv_darwin.h" -// TODO just outright ban passing NULL to any parents? that still won't fix the initial case - struct uiGroup { uiDarwinControl c; NSBox *box; @@ -164,7 +162,6 @@ void uiGroupSetMargined(uiGroup *g, int margined) { g->margined = margined; singleChildConstraintsSetMargined(&(g->constraints), g->margined); - // TODO issue a relayout command? } uiGroup *uiNewGroup(const char *title) diff --git a/darwin/main.m b/darwin/main.m index dd7f2d60..97821201 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -189,7 +189,6 @@ int uiMainStep(int wait) } } -// TODO make this delayed void uiQuit(void) { canQuit = YES; @@ -197,7 +196,7 @@ void uiQuit(void) } // thanks to mikeash in irc.freenode.net/#macdev for suggesting the use of Grand Central Dispatch for this -// TODO will dispatch_get_main_queue() break after _CFRunLoopSetCurrent()? +// LONGTERM will dispatch_get_main_queue() break after _CFRunLoopSetCurrent()? void uiQueueMain(void (*f)(void *data), void *data) { // dispatch_get_main_queue() is a serial queue so it will not execute multiple uiQueueMain() functions concurrently From 42d837806d6a0e0317525f34162b28ca3c84c301 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 28 May 2016 18:16:13 -0400 Subject: [PATCH 0165/1329] More stale and duplicate TODO removal. --- darwin/tab.m | 5 ----- darwin/window.m | 1 - 2 files changed, 6 deletions(-) diff --git a/darwin/tab.m b/darwin/tab.m index 24060309..383e3dcb 100644 --- a/darwin/tab.m +++ b/darwin/tab.m @@ -1,9 +1,6 @@ // 15 august 2015 #import "uipriv_darwin.h" -// TODOs -// - page 2 tab doesn't lay out properly at first; need to jiggle on page change too :S - @interface tabPage : NSObject { struct singleChildConstraints constraints; int margined; @@ -81,7 +78,6 @@ struct uiTab { { self->margined = m; singleChildConstraintsSetMargined(&(self->constraints), self->margined); - // TODO issue a relayout command? } @end @@ -137,7 +133,6 @@ BOOL uiTabHugsTrailingEdge(uiDarwinControl *c) { uiTab *t = uiTab(c); - // TODO make a function? return t->horzHuggingPri < NSLayoutPriorityWindowSizeStayPut; } diff --git a/darwin/window.m b/darwin/window.m index e5ebc93f..2d8d2e45 100644 --- a/darwin/window.m +++ b/darwin/window.m @@ -231,7 +231,6 @@ void uiWindowSetMargined(uiWindow *w, int margined) { w->margined = margined; singleChildConstraintsSetMargined(&(w->constraints), w->margined); - // TODO issue a relayout command? } static int defaultOnClosing(uiWindow *w, void *data) From eb4bc4bfb54e49633f33548b7f25a553af1bfe8a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 28 May 2016 18:59:20 -0400 Subject: [PATCH 0166/1329] Added NSTextView attributes. Let's fix uiMultilineEntry! --- tvattr | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 tvattr diff --git a/tvattr b/tvattr new file mode 100644 index 00000000..07005947 --- /dev/null +++ b/tvattr @@ -0,0 +1,52 @@ +2016-05-28 18:58:55.717 svtest[76849:1446033] === NSText +2016-05-28 18:58:55.717 svtest[76849:1446033] backgroundColor NSCalibratedWhiteColorSpace 1 1 +2016-05-28 18:58:55.717 svtest[76849:1446033] drawsBackground 1 +2016-05-28 18:58:55.717 svtest[76849:1446033] editable 1 +2016-05-28 18:58:55.717 svtest[76849:1446033] selectable 1 +2016-05-28 18:58:55.717 svtest[76849:1446033] fieldEditor 0 +2016-05-28 18:58:55.717 svtest[76849:1446033] richText 0 +2016-05-28 18:58:55.718 svtest[76849:1446033] importsGraphics 0 +2016-05-28 18:58:55.718 svtest[76849:1446033] usesFontPanel 0 +2016-05-28 18:58:55.718 svtest[76849:1446033] rulerVisible 0 +2016-05-28 18:58:55.718 svtest[76849:1446033] font "Helvetica 12.00 pt. P [] (0x608000044200) fobj=0x1004095e0, spc=3.33" +2016-05-28 18:58:55.718 svtest[76849:1446033] alignment 4 +2016-05-28 18:58:55.718 svtest[76849:1446033] textColor (null) +2016-05-28 18:58:55.718 svtest[76849:1446033] baseWritingDir -1 +2016-05-28 18:58:55.718 svtest[76849:1446033] maxSize {463, 10000000} +2016-05-28 18:58:55.718 svtest[76849:1446033] minSize {238, 133} +2016-05-28 18:58:55.719 svtest[76849:1446033] vertResizable 1 +2016-05-28 18:58:55.719 svtest[76849:1446033] horzResizable 0 +2016-05-28 18:58:55.719 svtest[76849:1446033] === NSTextView +2016-05-28 18:58:55.719 svtest[76849:1446033] allowsBGCChange 0 +2016-05-28 18:58:55.719 svtest[76849:1446033] shouldDrawInsPt 1 +2016-05-28 18:58:55.739 svtest[76849:1446033] allowsUndo 1 +2016-05-28 18:58:55.739 svtest[76849:1446033] defaultParStyle (null) +2016-05-28 18:58:55.739 svtest[76849:1446033] allowsImageEdit 0 +2016-05-28 18:58:55.739 svtest[76849:1446033] autoQuoteSub 1 +2016-05-28 18:58:55.739 svtest[76849:1446033] autoLinkDetect 0 +2016-05-28 18:58:55.739 svtest[76849:1446033] displaysLinkTTs 1 +2016-05-28 18:58:55.739 svtest[76849:1446033] usesRuler 0 +2016-05-28 18:58:55.739 svtest[76849:1446033] usesInspector 0 +2016-05-28 18:58:55.739 svtest[76849:1446033] selectionAffin 0 +2016-05-28 18:58:55.739 svtest[76849:1446033] selectionGran 0 +2016-05-28 18:58:55.739 svtest[76849:1446033] insertionColor NSNamedColorSpace System controlTextColor +2016-05-28 18:58:55.739 svtest[76849:1446033] typingAttrib (null) +2016-05-28 18:58:55.739 svtest[76849:1446033] coalescingUndo 0 +2016-05-28 18:58:55.739 svtest[76849:1446033] smartInsertDel 0 +2016-05-28 18:58:55.739 svtest[76849:1446033] continSpellChk 0 +2016-05-28 18:58:55.739 svtest[76849:1446033] spellCheckTag 1 +2016-05-28 18:58:55.740 svtest[76849:1446033] grammarChecking 0 +2016-05-28 18:58:55.740 svtest[76849:1446033] usesFindPanel 1 +2016-05-28 18:58:55.740 svtest[76849:1446033] enabledTextChk 961 +2016-05-28 18:58:55.740 svtest[76849:1446033] autoDashSubst 1 +2016-05-28 18:58:55.740 svtest[76849:1446033] autoDataDetect 0 +2016-05-28 18:58:55.740 svtest[76849:1446033] autoSpellCorr 1 +2016-05-28 18:58:55.740 svtest[76849:1446033] autoTextReplace 1 +2016-05-28 18:58:55.741 svtest[76849:1446033] usesFindBar 0 +2016-05-28 18:58:55.741 svtest[76849:1446033] incrementSearch 0 +2016-05-28 18:58:55.741 svtest[76849:1446033] === NSTextContainer +2016-05-28 18:58:55.741 svtest[76849:1446033] 10.11?lineBreakMode 0 +2016-05-28 18:58:55.741 svtest[76849:1446033] widthTracks 1 +2016-05-28 18:58:55.742 svtest[76849:1446033] heightTracks 0 +2016-05-28 18:58:55.742 svtest[76849:1446033] === NSLayoutManager +2016-05-28 18:58:55.742 svtest[76849:1446033] noncontiguous 1 From a0084df23a700853b102d651439fa36c2a0ef4d0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 28 May 2016 19:29:25 -0400 Subject: [PATCH 0167/1329] Completely redid creating uiMultilineEntry on OS X. Much better now :D --- darwin/multilineentry.m | 170 ++++++++++------------------------------ tvattr | 52 ------------ 2 files changed, 40 insertions(+), 182 deletions(-) delete mode 100644 tvattr diff --git a/darwin/multilineentry.m b/darwin/multilineentry.m index aa77891d..712edf3f 100644 --- a/darwin/multilineentry.m +++ b/darwin/multilineentry.m @@ -121,45 +121,70 @@ static uiMultilineEntry *finishMultilineEntry(BOOL hscroll) uiDarwinNewControl(uiMultilineEntry, e); e->tv = [[intrinsicSizeTextView alloc] initWithFrame:NSZeroRect]; - // verified against Interface Builder, except for rich text options - [e->tv setAllowsDocumentBackgroundColorChange:NO]; - [e->tv setBackgroundColor:[NSColor textBackgroundColor]]; - [e->tv setTextColor:[NSColor textColor]]; - [e->tv setAllowsUndo:YES]; + + // verified against Interface Builder for a sufficiently customized text view + + // NSText properties: + // this is what Interface Builder sets the background color to + [e->tv setBackgroundColor:[NSColor colorWithCalibratedWhite:1.0 alpha:1.0]]; + [e->tv setDrawsBackground:YES]; [e->tv setEditable:YES]; [e->tv setSelectable:YES]; + [e->tv setFieldEditor:NO]; [e->tv setRichText:NO]; [e->tv setImportsGraphics:NO]; + [e->tv setUsesFontPanel:NO]; + [e->tv setRulerVisible:NO]; + // we'll handle font last + [e->tv setAlignment:NSTextAlignmentNatural]; + // textColor is set to nil, just keep the dfault [e->tv setBaseWritingDirection:NSWritingDirectionNatural]; - // TODO default paragraph format + [e->tv setHorizontallyResizable:NO]; + [e->tv setVerticallyResizable:YES]; + + // NSTextView properties: + [e->tv setAllowsDocumentBackgroundColorChange:NO]; + [e->tv setAllowsUndo:YES]; + // default paragraph style is nil; keep default [e->tv setAllowsImageEditing:NO]; [e->tv setAutomaticQuoteSubstitutionEnabled:NO]; [e->tv setAutomaticLinkDetectionEnabled:NO]; + [e->tv setDisplaysLinkToolTips:YES]; [e->tv setUsesRuler:NO]; - [e->tv setRulerVisible:NO]; [e->tv setUsesInspectorBar:NO]; [e->tv setSelectionGranularity:NSSelectByCharacter]; -//TODO [e->tv setInsertionPointColor:[NSColor insertionColor]]; + // there is a dedicated named insertion point color but oh well + [e->tv setInsertionPointColor:[NSColor controlTextColor]]; + // typing attributes is nil; keep default (we change it below for fonts though) + [e->tv setSmartInsertDeleteEnabled:NO]; [e->tv setContinuousSpellCheckingEnabled:NO]; [e->tv setGrammarCheckingEnabled:NO]; - [e->tv setUsesFontPanel:NO]; + [e->tv setUsesFindPanel:YES]; [e->tv setEnabledTextCheckingTypes:0]; [e->tv setAutomaticDashSubstitutionEnabled:NO]; + [e->tv setAutomaticDataDetectionEnabled:NO]; [e->tv setAutomaticSpellingCorrectionEnabled:NO]; [e->tv setAutomaticTextReplacementEnabled:NO]; - [e->tv setSmartInsertDeleteEnabled:NO]; - [e->tv setLayoutOrientation:NSTextLayoutOrientationHorizontal]; - // TODO default find panel behavior + [e->tv setUsesFindBar:NO]; + [e->tv setIncrementalSearchingEnabled:NO]; + + // NSTextContainer properties: + [[e->tv textContainer] setWidthTracksTextView:YES]; + [[e->tv textContainer] setHeightTracksTextView:NO]; + + // NSLayoutManager properties: + [[e->tv layoutManager] setAllowsNonContiguousLayout:YES]; + // now just to be safe; this will do some of the above but whatever disableAutocorrect(e->tv); - // this option is complex; just set it to the Interface Builder default - [[e->tv layoutManager] setAllowsNonContiguousLayout:YES]; + if (hscroll) { - // TODO this is a giant mess + // see https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/TextUILayer/Tasks/TextInScrollView.html [e->tv setHorizontallyResizable:YES]; [[e->tv textContainer] setWidthTracksTextView:NO]; [[e->tv textContainer] setContainerSize:NSMakeSize(CGFLOAT_MAX, CGFLOAT_MAX)]; } + // don't use uiDarwinSetControlFont() directly; we have to do a little extra work to set the font font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]]; [e->tv setTypingAttributes:[NSDictionary @@ -193,118 +218,3 @@ uiMultilineEntry *uiNewNonWrappingMultilineEntry(void) { return finishMultilineEntry(YES); } - -// TODO -#if 0 - -NSMutableString *s; -static void add(const char *fmt, ...) -{ - va_list ap; - NSString *fmts; - NSString *a; - - va_start(ap, fmt); - fmts = [NSString stringWithUTF8String:fmt]; - a = [[NSString alloc] initWithFormat:fmts arguments:ap]; - [s appendString:a]; - [s appendString:@"\n"]; - va_end(ap); -} - -static NSString *edgeInsetsStr(NSEdgeInsets i) -{ - return [NSString - stringWithFormat:@"left:%g top:%g right:%g bottom:%g", - i.left, i.top, i.right, i.bottom]; -} - -void printinfo(NSScrollView *sv, NSTextView *tv) -{ - s = [NSMutableString new]; - -#define self _s -struct { -NSScrollView *sv; -NSTextView *tv; -} _s; -_s.sv = sv; -_s.tv = tv; - - add("NSTextView"); - add(" textContainerInset %@", - NSStringFromSize([self.tv textContainerInset])); - add(" textContainerOrigin %@", - NSStringFromPoint([self.tv textContainerOrigin])); - add(" backgroundColor %@", [self.tv backgroundColor]); - add(" drawsBackground %d", [self.tv drawsBackground]); - add(" allowsDocumentBackgroundColorChange %d", - [self.tv allowsDocumentBackgroundColorChange]); - add(" allowedInputSourceLocales %@", - [self.tv allowedInputSourceLocales]); - add(" allowsUndo %d", [self.tv allowsUndo]); - add(" isEditable %d", [self.tv isEditable]); - add(" isSelectable %d", [self.tv isSelectable]); - add(" isFieldEditor %d", [self.tv isFieldEditor]); - add(" isRichText %d", [self.tv isRichText]); - add(" importsGraphics %d", [self.tv importsGraphics]); - add(" defaultParagraphStyle %@", - [self.tv defaultParagraphStyle]); - add(" allowsImageEditing %d", [self.tv allowsImageEditing]); - add(" isAutomaticQuoteSubstitutionEnabled %d", - [self.tv isAutomaticQuoteSubstitutionEnabled]); - add(" isAutomaticLinkDetectionEnabled %d", - [self.tv isAutomaticLinkDetectionEnabled]); - add(" displaysLinkToolTips %d", [self.tv displaysLinkToolTips]); - add(" usesRuler %d", [self.tv usesRuler]); - add(" isRulerVisible %d", [self.tv isRulerVisible]); - add(" usesInspectorBar %d", [self.tv usesInspectorBar]); - add(" selectionAffinity %d", [self.tv selectionAffinity]); - add(" selectionGranularity %d", [self.tv selectionGranularity]); - add(" insertionPointColor %@", [self.tv insertionPointColor]); - add(" selectedTextAttributes %@", - [self.tv selectedTextAttributes]); - add(" markedTextAttributes %@", [self.tv markedTextAttributes]); - add(" linkTextAttributes %@", [self.tv linkTextAttributes]); - add(" typingAttributes %@", [self.tv typingAttributes]); - add(" smartInsertDeleteEnabled %d", - [self.tv smartInsertDeleteEnabled]); - add(" isContinuousSpellCheckingEnabled %d", - [self.tv isContinuousSpellCheckingEnabled]); - add(" isGrammarCheckingEnabled %d", - [self.tv isGrammarCheckingEnabled]); - add(" acceptsGlyphInfo %d", [self.tv acceptsGlyphInfo]); - add(" usesFontPanel %d", [self.tv usesFontPanel]); - add(" usesFindPanel %d", [self.tv usesFindPanel]); - add(" enabledTextCheckingTypes %d", - [self.tv enabledTextCheckingTypes]); - add(" isAutomaticDashSubstitutionEnabled %d", - [self.tv isAutomaticDashSubstitutionEnabled]); - add(" isAutomaticDataDetectionEnabled %d", - [self.tv isAutomaticDataDetectionEnabled]); - add(" isAutomaticSpellingCorrectionEnabled %d", - [self.tv isAutomaticSpellingCorrectionEnabled]); - add(" isAutomaticTextReplacementEnabled %d", - [self.tv isAutomaticTextReplacementEnabled]); - add(" usesFindBar %d", [self.tv usesFindBar]); - add(" isIncrementalSearchingEnabled %d", - [self.tv isIncrementalSearchingEnabled]); - add(" NSText:"); - add(" font %@", [self.tv font]); - add(" textColor %@", [self.tv textColor]); - add(" baseWritingDirection %d", [self.tv baseWritingDirection]); - add(" maxSize %@", - NSStringFromSize([self.tv maxSize])); - add(" minSize %@", - NSStringFromSize([self.tv minSize])); - add(" isVerticallyResizable %d", - [self.tv isVerticallyResizable]); - add(" isHorizontallyResizable %d", - [self.tv isHorizontallyResizable]); - -#undef self - - fprintf(stdout, "%s", [s UTF8String]); -} - -#endif diff --git a/tvattr b/tvattr deleted file mode 100644 index 07005947..00000000 --- a/tvattr +++ /dev/null @@ -1,52 +0,0 @@ -2016-05-28 18:58:55.717 svtest[76849:1446033] === NSText -2016-05-28 18:58:55.717 svtest[76849:1446033] backgroundColor NSCalibratedWhiteColorSpace 1 1 -2016-05-28 18:58:55.717 svtest[76849:1446033] drawsBackground 1 -2016-05-28 18:58:55.717 svtest[76849:1446033] editable 1 -2016-05-28 18:58:55.717 svtest[76849:1446033] selectable 1 -2016-05-28 18:58:55.717 svtest[76849:1446033] fieldEditor 0 -2016-05-28 18:58:55.717 svtest[76849:1446033] richText 0 -2016-05-28 18:58:55.718 svtest[76849:1446033] importsGraphics 0 -2016-05-28 18:58:55.718 svtest[76849:1446033] usesFontPanel 0 -2016-05-28 18:58:55.718 svtest[76849:1446033] rulerVisible 0 -2016-05-28 18:58:55.718 svtest[76849:1446033] font "Helvetica 12.00 pt. P [] (0x608000044200) fobj=0x1004095e0, spc=3.33" -2016-05-28 18:58:55.718 svtest[76849:1446033] alignment 4 -2016-05-28 18:58:55.718 svtest[76849:1446033] textColor (null) -2016-05-28 18:58:55.718 svtest[76849:1446033] baseWritingDir -1 -2016-05-28 18:58:55.718 svtest[76849:1446033] maxSize {463, 10000000} -2016-05-28 18:58:55.718 svtest[76849:1446033] minSize {238, 133} -2016-05-28 18:58:55.719 svtest[76849:1446033] vertResizable 1 -2016-05-28 18:58:55.719 svtest[76849:1446033] horzResizable 0 -2016-05-28 18:58:55.719 svtest[76849:1446033] === NSTextView -2016-05-28 18:58:55.719 svtest[76849:1446033] allowsBGCChange 0 -2016-05-28 18:58:55.719 svtest[76849:1446033] shouldDrawInsPt 1 -2016-05-28 18:58:55.739 svtest[76849:1446033] allowsUndo 1 -2016-05-28 18:58:55.739 svtest[76849:1446033] defaultParStyle (null) -2016-05-28 18:58:55.739 svtest[76849:1446033] allowsImageEdit 0 -2016-05-28 18:58:55.739 svtest[76849:1446033] autoQuoteSub 1 -2016-05-28 18:58:55.739 svtest[76849:1446033] autoLinkDetect 0 -2016-05-28 18:58:55.739 svtest[76849:1446033] displaysLinkTTs 1 -2016-05-28 18:58:55.739 svtest[76849:1446033] usesRuler 0 -2016-05-28 18:58:55.739 svtest[76849:1446033] usesInspector 0 -2016-05-28 18:58:55.739 svtest[76849:1446033] selectionAffin 0 -2016-05-28 18:58:55.739 svtest[76849:1446033] selectionGran 0 -2016-05-28 18:58:55.739 svtest[76849:1446033] insertionColor NSNamedColorSpace System controlTextColor -2016-05-28 18:58:55.739 svtest[76849:1446033] typingAttrib (null) -2016-05-28 18:58:55.739 svtest[76849:1446033] coalescingUndo 0 -2016-05-28 18:58:55.739 svtest[76849:1446033] smartInsertDel 0 -2016-05-28 18:58:55.739 svtest[76849:1446033] continSpellChk 0 -2016-05-28 18:58:55.739 svtest[76849:1446033] spellCheckTag 1 -2016-05-28 18:58:55.740 svtest[76849:1446033] grammarChecking 0 -2016-05-28 18:58:55.740 svtest[76849:1446033] usesFindPanel 1 -2016-05-28 18:58:55.740 svtest[76849:1446033] enabledTextChk 961 -2016-05-28 18:58:55.740 svtest[76849:1446033] autoDashSubst 1 -2016-05-28 18:58:55.740 svtest[76849:1446033] autoDataDetect 0 -2016-05-28 18:58:55.740 svtest[76849:1446033] autoSpellCorr 1 -2016-05-28 18:58:55.740 svtest[76849:1446033] autoTextReplace 1 -2016-05-28 18:58:55.741 svtest[76849:1446033] usesFindBar 0 -2016-05-28 18:58:55.741 svtest[76849:1446033] incrementSearch 0 -2016-05-28 18:58:55.741 svtest[76849:1446033] === NSTextContainer -2016-05-28 18:58:55.741 svtest[76849:1446033] 10.11?lineBreakMode 0 -2016-05-28 18:58:55.741 svtest[76849:1446033] widthTracks 1 -2016-05-28 18:58:55.742 svtest[76849:1446033] heightTracks 0 -2016-05-28 18:58:55.742 svtest[76849:1446033] === NSLayoutManager -2016-05-28 18:58:55.742 svtest[76849:1446033] noncontiguous 1 From 6e94671782fba3e7208a7b55c22b1ae1bc07894a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 28 May 2016 21:17:54 -0400 Subject: [PATCH 0168/1329] More TODO resolution and LONGTERM relegation. --- darwin/radiobuttons.m | 9 +++++---- darwin/stddialogs.m | 6 +++--- darwin/tab.m | 14 +++++++++++--- unix/stddialogs.c | 4 ++-- 4 files changed, 21 insertions(+), 12 deletions(-) diff --git a/darwin/radiobuttons.m b/darwin/radiobuttons.m index a0228967..3b7a236b 100644 --- a/darwin/radiobuttons.m +++ b/darwin/radiobuttons.m @@ -5,9 +5,9 @@ // This is documented on the NSMatrix page, but the rest of the OS X documentation says to still use NSMatrix. // NSMatrix has weird quirks anyway... -// TODO -// - check that multiple radio buttons on the same parent container work right -// - 6 units of spacing between buttons, as suggested by Interface Builder? +// TODO check that multiple radio buttons on the same parent container work right + +// LONGTERM 6 units of spacing between buttons, as suggested by Interface Builder? struct uiRadioButtons { uiDarwinControl c; @@ -48,11 +48,12 @@ void uiRadioButtonsAppend(uiRadioButtons *r, const char *text) NSButton *b, *b2; NSLayoutConstraint *constraint; - // TODO bezel style? transparent? b = [[NSButton alloc] initWithFrame:NSZeroRect]; [b setTitle:toNSString(text)]; [b setButtonType:NSRadioButton]; + // doesn't seem to have an associated bezel style [b setBordered:NO]; + [b setTransparent:NO]; uiDarwinSetControlFont(b, NSRegularControlSize); [b setTranslatesAutoresizingMaskIntoConstraints:NO]; diff --git a/darwin/stddialogs.m b/darwin/stddialogs.m index f6fdc519..57ce9596 100644 --- a/darwin/stddialogs.m +++ b/darwin/stddialogs.m @@ -1,9 +1,9 @@ // 26 june 2015 #import "uipriv_darwin.h" -// TODO restructure this whole file -// TODO explicitly document this works as we want -// TODO note that font and color buttons also do this +// LONGTERM restructure this whole file +// LONGTERM explicitly document this works as we want +// LONGTERM note that font and color buttons also do this #define windowWindow(w) ((NSWindow *) uiControlHandle(uiControl(w))) diff --git a/darwin/tab.m b/darwin/tab.m index 383e3dcb..d5b5885d 100644 --- a/darwin/tab.m +++ b/darwin/tab.m @@ -114,8 +114,16 @@ uiDarwinControlDefaultEnabled(uiTab, tabview) uiDarwinControlDefaultEnable(uiTab, tabview) uiDarwinControlDefaultDisable(uiTab, tabview) -// TODO container update -uiDarwinControlDefaultSyncEnableState(uiTab, tabview) +static void uiTabSyncEnableState(uiDarwinControl *c, int enabled) +{ + uiTab *t = uiTab(c); + tabPage *page; + + if (uiDarwinShouldStopSyncEnableState(uiDarwinControl(t), enabled)) + return; + for (page in t->pages) + uiDarwinControlSyncEnableState(uiDarwinControl(page.c), enabled); +} uiDarwinControlDefaultSetSuperview(uiTab, tabview) @@ -185,7 +193,7 @@ void uiTabInsertAt(uiTab *t, const char *name, uintmax_t n, uiControl *child) uiControlSetParent(child, uiControl(t)); view = [[[NSView alloc] initWithFrame:NSZeroRect] autorelease]; - // TODO if we turn off the autoresizing mask, nothing shows up; didn't this get documented somewhere? + // note: if we turn off the autoresizing mask, nothing shows up uiDarwinControlSetSuperview(uiDarwinControl(child), view); uiDarwinControlSyncEnableState(uiDarwinControl(child), uiControlEnabledToUser(uiControl(t))); diff --git a/unix/stddialogs.c b/unix/stddialogs.c index 53e3301a..93302f75 100644 --- a/unix/stddialogs.c +++ b/unix/stddialogs.c @@ -1,8 +1,8 @@ // 26 june 2015 #include "uipriv_unix.h" -// TODO figure out why, and describe, that this is the desired behavior -// TODO also point out that font and color buttons also work like this +// LONGTERM figure out why, and describe, that this is the desired behavior +// LONGTERM also point out that font and color buttons also work like this #define windowWindow(w) (GTK_WINDOW(uiControlHandle(uiControl(w)))) From b130ddc04b249e2d5c54735b4bf748e683f3d30b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 28 May 2016 21:29:00 -0400 Subject: [PATCH 0169/1329] Added a test of having multiple uiRadioButtons in the same parent container and started a test of intrinsic sizes of all non-container controls. --- darwin/radiobuttons.m | 2 -- test/GNUfiles.mk | 1 + test/main.c | 5 ++++- test/page13.c | 30 ++++++++++++++++++++++++++++++ test/test.h | 3 +++ 5 files changed, 38 insertions(+), 3 deletions(-) create mode 100644 test/page13.c diff --git a/darwin/radiobuttons.m b/darwin/radiobuttons.m index 3b7a236b..b6c663e0 100644 --- a/darwin/radiobuttons.m +++ b/darwin/radiobuttons.m @@ -5,8 +5,6 @@ // This is documented on the NSMatrix page, but the rest of the OS X documentation says to still use NSMatrix. // NSMatrix has weird quirks anyway... -// TODO check that multiple radio buttons on the same parent container work right - // LONGTERM 6 units of spacing between buttons, as suggested by Interface Builder? struct uiRadioButtons { diff --git a/test/GNUfiles.mk b/test/GNUfiles.mk index 6cb4b0e3..94f1fa23 100644 --- a/test/GNUfiles.mk +++ b/test/GNUfiles.mk @@ -19,6 +19,7 @@ CFILES += \ test/page10.c \ test/page11.c \ test/page12.c \ + test/page13.c \ test/spaced.c HFILES += \ diff --git a/test/main.c b/test/main.c index 5b049120..dea51663 100644 --- a/test/main.c +++ b/test/main.c @@ -47,7 +47,7 @@ int main(int argc, char *argv[]) uiWindow *w; uiBox *page2, *page3, *page4, *page5; uiBox *page6, *page7, *page8, *page9, *page10; - uiBox *page11, *page12; + uiBox *page11, *page12, *page13; uiTab *outerTab; uiTab *innerTab; int nomenus = 0; @@ -141,6 +141,9 @@ int main(int argc, char *argv[]) page12 = makePage12(); uiTabAppend(innerTab, "Page 12", uiControl(page12)); + page13 = makePage13(); + uiTabAppend(innerTab, "Page 13", uiControl(page13)); + if (startspaced) setSpaced(1); diff --git a/test/page13.c b/test/page13.c new file mode 100644 index 00000000..264488ed --- /dev/null +++ b/test/page13.c @@ -0,0 +1,30 @@ +// 28 may 2016 +#include "test.h" + +uiBox *makePage13(void) +{ + uiBox *page13; + uiRadioButtons *rb; + uiButton *b; + + page13 = newVerticalBox(); + + rb = uiNewRadioButtons(); + uiRadioButtonsAppend(rb, "Item 1"); + uiRadioButtonsAppend(rb, "Item 2"); + uiRadioButtonsAppend(rb, "Item 3"); + uiBoxAppend(page13, uiControl(rb), 0); + + rb = uiNewRadioButtons(); + uiRadioButtonsAppend(rb, "Item A"); + uiRadioButtonsAppend(rb, "Item B"); + uiBoxAppend(page13, uiControl(rb), 0); + + b = uiNewButton("Horizontal"); + uiBoxAppend(page13, uiControl(b), 0); + + b = uiNewButton("Vertical"); + uiBoxAppend(page13, uiControl(b), 0); + + return page13; +} diff --git a/test/test.h b/test/test.h index b7257e5d..db687576 100644 --- a/test/test.h +++ b/test/test.h @@ -77,3 +77,6 @@ extern uiBox *makePage11(void); // page12.c extern uiBox *makePage12(void); + +// page13.c +extern uiBox *makePage13(void); From ae0dcada456535014f874227cd6be86030935cf4 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 28 May 2016 21:41:07 -0400 Subject: [PATCH 0170/1329] More TODO resolution work. --- darwin/spinbox.m | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/darwin/spinbox.m b/darwin/spinbox.m index 24ed9195..a4281a0e 100644 --- a/darwin/spinbox.m +++ b/darwin/spinbox.m @@ -29,6 +29,16 @@ struct uiSpinbox { void *onChangedData; }; +// yes folks, this varies by operating system! woo! +static CGFloat stepperYDelta(void) +{ + // 10.8 - xxx + // 10.9 - xxx + // 10.10 - xxx + // 10.11 - -1 + return -1; +} + @implementation libui_spinbox - (id)initWithFrame:(NSRect)r spinbox:(uiSpinbox *)sb @@ -82,17 +92,17 @@ struct uiSpinbox { [self addConstraint:mkConstraint(self->stepper, NSLayoutAttributeTop, NSLayoutRelationEqual, self, NSLayoutAttributeTop, - 1, -1, // TODO make sure this is right + 1, stepperYDelta(), @"uiSpinbox top edge stepper")]; [self addConstraint:mkConstraint(self->stepper, NSLayoutAttributeBottom, NSLayoutRelationEqual, self, NSLayoutAttributeBottom, - 1, -1, // TODO make sure this is right + 1, stepperYDelta(), @"uiSpinbox bottom edge stepper")]; [self addConstraint:mkConstraint(self->tf, NSLayoutAttributeTrailing, NSLayoutRelationEqual, self->stepper, NSLayoutAttributeLeading, - 1, -3, // TODO + 1, -3, // arbitrary amount; good enough visually (and it seems to match NSDatePicker too, at least on 10.11, which is even better) @"uiSpinbox space between text field and stepper")]; self->spinbox = sb; From c99ad0f0c5b318b823543c58583fea41720aa287 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 28 May 2016 21:50:24 -0400 Subject: [PATCH 0171/1329] 10.8 build fix. --- darwin/multilineentry.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/darwin/multilineentry.m b/darwin/multilineentry.m index 712edf3f..9a4f8095 100644 --- a/darwin/multilineentry.m +++ b/darwin/multilineentry.m @@ -136,7 +136,9 @@ static uiMultilineEntry *finishMultilineEntry(BOOL hscroll) [e->tv setUsesFontPanel:NO]; [e->tv setRulerVisible:NO]; // we'll handle font last - [e->tv setAlignment:NSTextAlignmentNatural]; + // while setAlignment: has been around since 10.0, the named constant "NSTextAlignmentNatural" seems to have only been introduced in 10.11 +#define ourNSTextAlignmentNatural 4 + [e->tv setAlignment:ourNSTextAlignmentNatural]; // textColor is set to nil, just keep the dfault [e->tv setBaseWritingDirection:NSWritingDirectionNatural]; [e->tv setHorizontallyResizable:NO]; From 1e8a90d9a2ac4743545c5546a6555f9c17344276 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 28 May 2016 22:35:40 -0400 Subject: [PATCH 0172/1329] Eschewed chronological order in ui.h for logical order. --- ui.h | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/ui.h b/ui.h index adc0b327..34527918 100644 --- a/ui.h +++ b/ui.h @@ -112,15 +112,6 @@ _UI_EXTERN void uiBoxSetPadded(uiBox *b, int padded); _UI_EXTERN uiBox *uiNewHorizontalBox(void); _UI_EXTERN uiBox *uiNewVerticalBox(void); -typedef struct uiEntry uiEntry; -#define uiEntry(this) ((uiEntry *) (this)) -_UI_EXTERN char *uiEntryText(uiEntry *e); -_UI_EXTERN void uiEntrySetText(uiEntry *e, const char *text); -_UI_EXTERN void uiEntryOnChanged(uiEntry *e, void (*f)(uiEntry *e, void *data), void *data); -_UI_EXTERN int uiEntryReadOnly(uiEntry *e); -_UI_EXTERN void uiEntrySetReadOnly(uiEntry *e, int readonly); -_UI_EXTERN uiEntry *uiNewEntry(void); - typedef struct uiCheckbox uiCheckbox; #define uiCheckbox(this) ((uiCheckbox *) (this)) _UI_EXTERN char *uiCheckboxText(uiCheckbox *c); @@ -130,6 +121,15 @@ _UI_EXTERN int uiCheckboxChecked(uiCheckbox *c); _UI_EXTERN void uiCheckboxSetChecked(uiCheckbox *c, int checked); _UI_EXTERN uiCheckbox *uiNewCheckbox(const char *text); +typedef struct uiEntry uiEntry; +#define uiEntry(this) ((uiEntry *) (this)) +_UI_EXTERN char *uiEntryText(uiEntry *e); +_UI_EXTERN void uiEntrySetText(uiEntry *e, const char *text); +_UI_EXTERN void uiEntryOnChanged(uiEntry *e, void (*f)(uiEntry *e, void *data), void *data); +_UI_EXTERN int uiEntryReadOnly(uiEntry *e); +_UI_EXTERN void uiEntrySetReadOnly(uiEntry *e, int readonly); +_UI_EXTERN uiEntry *uiNewEntry(void); + typedef struct uiLabel uiLabel; #define uiLabel(this) ((uiLabel *) (this)) _UI_EXTERN char *uiLabelText(uiLabel *l); @@ -167,12 +167,6 @@ _UI_EXTERN void uiSpinboxSetValue(uiSpinbox *s, intmax_t value); _UI_EXTERN void uiSpinboxOnChanged(uiSpinbox *s, void (*f)(uiSpinbox *s, void *data), void *data); _UI_EXTERN uiSpinbox *uiNewSpinbox(intmax_t min, intmax_t max); -typedef struct uiProgressBar uiProgressBar; -#define uiProgressBar(this) ((uiProgressBar *) (this)) -// TODO uiProgressBarValue() -_UI_EXTERN void uiProgressBarSetValue(uiProgressBar *p, int n); -_UI_EXTERN uiProgressBar *uiNewProgressBar(void); - typedef struct uiSlider uiSlider; #define uiSlider(this) ((uiSlider *) (this)) _UI_EXTERN intmax_t uiSliderValue(uiSlider *s); @@ -180,6 +174,12 @@ _UI_EXTERN void uiSliderSetValue(uiSlider *s, intmax_t value); _UI_EXTERN void uiSliderOnChanged(uiSlider *s, void (*f)(uiSlider *s, void *data), void *data); _UI_EXTERN uiSlider *uiNewSlider(intmax_t min, intmax_t max); +typedef struct uiProgressBar uiProgressBar; +#define uiProgressBar(this) ((uiProgressBar *) (this)) +// TODO uiProgressBarValue() +_UI_EXTERN void uiProgressBarSetValue(uiProgressBar *p, int n); +_UI_EXTERN uiProgressBar *uiNewProgressBar(void); + typedef struct uiSeparator uiSeparator; #define uiSeparator(this) ((uiSeparator *) (this)) _UI_EXTERN uiSeparator *uiNewHorizontalSeparator(void); From 0c85469e527b000758bdc5d2b9df59f423fa970c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 28 May 2016 22:49:27 -0400 Subject: [PATCH 0173/1329] More TODO and intrinsic size work. --- darwin/colorbutton.m | 15 ++---------- darwin/progressbar.m | 21 ++++++++++++++++- darwin/separator.m | 9 ++++---- darwin/spinbox.m | 2 +- test/page13.c | 55 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 82 insertions(+), 20 deletions(-) diff --git a/darwin/colorbutton.m b/darwin/colorbutton.m index 28a604af..83b61571 100644 --- a/darwin/colorbutton.m +++ b/darwin/colorbutton.m @@ -105,21 +105,10 @@ struct uiColorButton { self->libui_setting = NO; } -// NSColorWell has no intrinsic size by default; give it at least the height of the equivalent button's +// NSColorWell has no intrinsic size by default; give it the default Interface Builder size. - (NSSize)intrinsicContentSize { - NSSize ss; - NSButtonCell *bc; - - ss = [super intrinsicContentSize]; - bc = [NSButtonCell new]; - [bc setButtonType:NSPushOnPushOffButton]; - [bc setBordered:YES]; - [bc setBezelStyle:NSShadowlessSquareBezelStyle]; - [bc setTitle:@" "]; - ss.height = [bc cellSize].height; - [bc release]; - return ss; + return NSMakeSize(44, 23); } @end diff --git a/darwin/progressbar.m b/darwin/progressbar.m index d8c0b060..a26874f1 100644 --- a/darwin/progressbar.m +++ b/darwin/progressbar.m @@ -1,6 +1,25 @@ // 14 august 2015 #import "uipriv_darwin.h" +// NSProgressIndicator has no intrinsic width by default; use the default width in Interface Builder +#define progressIndicatorWidth 100 + +@interface intrinsicWidthNSProgressIndicator : NSProgressIndicator +@end + +@implementation intrinsicWidthNSProgressIndicator + +- (NSSize)intrinsicContentSize +{ + NSSize s; + + s = [super intrinsicContentSize]; + s.width = progressIndicatorWidth; + return s; +} + +@end + struct uiProgressBar { uiDarwinControl c; NSProgressIndicator *pi; @@ -30,7 +49,7 @@ uiProgressBar *uiNewProgressBar(void) uiDarwinNewControl(uiProgressBar, p); - p->pi = [[NSProgressIndicator alloc] initWithFrame:NSZeroRect]; + p->pi = [[intrinsicWidthNSProgressIndicator alloc] initWithFrame:NSZeroRect]; [p->pi setControlSize:NSRegularControlSize]; [p->pi setBezeled:YES]; [p->pi setStyle:NSProgressIndicatorBarStyle]; diff --git a/darwin/separator.m b/darwin/separator.m index 4a5b3431..fa82aa46 100644 --- a/darwin/separator.m +++ b/darwin/separator.m @@ -1,10 +1,8 @@ // 14 august 2015 #import "uipriv_darwin.h" -// A separator NSBox is horizontal if width >= height. -// Use Interface Builder's initial size as our initial size, to be safe. -#define separatorFrameWidth 96 /* alignment rect 96 */ -#define separatorFrameHeight 5 /* alignment rect 1 */ +// TODO make this intrinsic +#define separatorWidth 96 struct uiSeparator { uiDarwinControl c; @@ -19,7 +17,8 @@ uiSeparator *uiNewHorizontalSeparator(void) uiDarwinNewControl(uiSeparator, s); - s->box = [[NSBox alloc] initWithFrame:NSMakeRect(0, 0, separatorFrameWidth, separatorFrameHeight)]; + // make the initial width >= initial height to force horizontal + s->box = [[NSBox alloc] initWithFrame:NSMakeRect(0, 0, 100, 1)]; [s->box setBoxType:NSBoxSeparator]; [s->box setBorderType:NSGrooveBorder]; [s->box setTransparent:NO]; diff --git a/darwin/spinbox.m b/darwin/spinbox.m index a4281a0e..f9df1874 100644 --- a/darwin/spinbox.m +++ b/darwin/spinbox.m @@ -32,7 +32,7 @@ struct uiSpinbox { // yes folks, this varies by operating system! woo! static CGFloat stepperYDelta(void) { - // 10.8 - xxx + // 10.8 - 0 // 10.9 - xxx // 10.10 - xxx // 10.11 - -1 diff --git a/test/page13.c b/test/page13.c index 264488ed..1f986675 100644 --- a/test/page13.c +++ b/test/page13.c @@ -1,6 +1,59 @@ // 28 may 2016 #include "test.h" +static int winClose(uiWindow *w, void *data) +{ + return 1; +} + +static void openTestWindow(uiBox *(*mkf)(void)) +{ + uiWindow *w; + uiBox *b; + uiCombobox *c; + uiEditableCombobox *e; + uiRadioButtons *r; + + w = uiNewWindow("Test", 100, 100, 0); + uiWindowOnClosing(w, winClose, NULL); + uiWindowSetMargined(w, 1); + b = (*mkf)(); + uiWindowSetChild(w, uiControl(b)); + +#define BA(x) uiBoxAppend(b, uiControl(x), 0) + BA(uiNewButton("")); + BA(uiNewCheckbox("")); + BA(uiNewEntry()); + BA(uiNewLabel("")); + BA(uiNewSpinbox(0, 100)); + BA(uiNewProgressBar()); + BA(uiNewSlider(0, 100)); + BA(uiNewHorizontalSeparator()); + c = uiNewCombobox(); + uiComboboxAppend(c, ""); + BA(c); + e = uiNewEditableCombobox(); + uiEditableComboboxAppend(e, ""); + BA(e); + r = uiNewRadioButtons(); + uiRadioButtonsAppend(r, ""); + BA(r); + BA(uiNewDateTimePicker()); + BA(uiNewDatePicker()); + BA(uiNewTimePicker()); + BA(uiNewMultilineEntry()); + // TODO nonscrolling and scrolling areas? + BA(uiNewFontButton()); + BA(uiNewColorButton()); + + uiControlShow(uiControl(w)); +} + +static void buttonClicked(uiButton *b, void *data) +{ + openTestWindow((uiBox *(*)(void)) data); +} + uiBox *makePage13(void) { uiBox *page13; @@ -21,9 +74,11 @@ uiBox *makePage13(void) uiBoxAppend(page13, uiControl(rb), 0); b = uiNewButton("Horizontal"); + uiButtonOnClicked(b, buttonClicked, uiNewHorizontalBox); uiBoxAppend(page13, uiControl(b), 0); b = uiNewButton("Vertical"); + uiButtonOnClicked(b, buttonClicked, uiNewVerticalBox); uiBoxAppend(page13, uiControl(b), 0); return page13; From 58d59f437066b097a674f3db2bcc5ba5e6e02a29 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 28 May 2016 23:08:56 -0400 Subject: [PATCH 0174/1329] Cleaned up events and proper string manipulation on uiMultilineEntry on OS X. Woo! --- darwin/multilineentry.m | 68 ++++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/darwin/multilineentry.m b/darwin/multilineentry.m index 9a4f8095..3019c8b2 100644 --- a/darwin/multilineentry.m +++ b/darwin/multilineentry.m @@ -1,16 +1,35 @@ // 8 december 2015 #import "uipriv_darwin.h" -// TODO you actually have to click on parts with a line in them in order to start editing; clicking below the last line doesn't give focus - // NSTextView has no intrinsic content size by default, which wreaks havoc on a pure-Auto Layout system // we'll have to take over to get it to work // see also http://stackoverflow.com/questions/24210153/nstextview-not-properly-resizing-with-auto-layout and http://stackoverflow.com/questions/11237622/using-autolayout-with-expanding-nstextviews -@interface intrinsicSizeTextView : NSTextView +@interface intrinsicSizeTextView : NSTextView { + uiMultilineEntry *libui_e; +} +- (id)initWithFrame:(NSRect)r e:(uiMultilineEntry *)e; @end +struct uiMultilineEntry { + uiDarwinControl c; + NSScrollView *sv; + intrinsicSizeTextView *tv; + struct scrollViewData *d; + void (*onChanged)(uiMultilineEntry *, void *); + void *onChangedData; + BOOL changing; +}; + @implementation intrinsicSizeTextView +- (id)initWithFrame:(NSRect)r e:(uiMultilineEntry *)e +{ + self = [super initWithFrame:r]; + if (self) + self->libui_e = e; + return self; +} + - (NSSize)intrinsicContentSize { NSTextContainer *textContainer; @@ -28,29 +47,12 @@ { [super didChangeText]; [self invalidateIntrinsicContentSize]; -} - -// TODO this doesn't call the above? -// TODO this also isn't perfect; play around with cpp-multithread -- (void)setString:(NSString *)str -{ - [super setString:str]; - [self didChangeText]; + if (!self->libui_e->changing) + (*(self->libui_e->onChanged))(self->libui_e, self->libui_e->onChangedData); } @end -struct uiMultilineEntry { - uiDarwinControl c; - NSScrollView *sv; - intrinsicSizeTextView *tv; - struct scrollViewData *d; - void (*onChanged)(uiMultilineEntry *, void *); - void *onChangedData; -}; - -// TODO events - uiDarwinControlAllDefaultsExceptDestroy(uiMultilineEntry, sv) static void uiMultilineEntryDestroy(uiControl *c) @@ -75,20 +77,22 @@ char *uiMultilineEntryText(uiMultilineEntry *e) void uiMultilineEntrySetText(uiMultilineEntry *e, const char *text) { - // TODO does this send a changed signal? - [e->tv setString:toNSString(text)]; + [[e->tv textStorage] replaceCharactersInRange:NSMakeRange(0, [[e->tv string] length]) + withString:toNSString(text)]; + // must be called explicitly according to the documentation of shouldChangeTextInRange:replacementString: + e->changing = YES; + [e->tv didChangeText]; + e->changing = NO; } // TODO scroll to end? void uiMultilineEntryAppend(uiMultilineEntry *e, const char *text) { - // TODO better way? - NSString *str; - - // TODO does this send a changed signal? - str = [e->tv string]; - str = [str stringByAppendingString:toNSString(text)]; - [e->tv setString:str]; + [[e->tv textStorage] replaceCharactersInRange:NSMakeRange([[e->tv string] length], 0) + withString:toNSString(text)]; + e->changing = YES; + [e->tv didChangeText]; + e->changing = NO; } void uiMultilineEntryOnChanged(uiMultilineEntry *e, void (*f)(uiMultilineEntry *e, void *data), void *data) @@ -120,7 +124,7 @@ static uiMultilineEntry *finishMultilineEntry(BOOL hscroll) uiDarwinNewControl(uiMultilineEntry, e); - e->tv = [[intrinsicSizeTextView alloc] initWithFrame:NSZeroRect]; + e->tv = [[intrinsicSizeTextView alloc] initWithFrame:NSZeroRect e:e]; // verified against Interface Builder for a sufficiently customized text view From 84d3df031f74c963baefa9380d0ea26df9c7ef45 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 28 May 2016 23:37:07 -0400 Subject: [PATCH 0175/1329] Either solved, dropped, or relegated to LONGTERM many of drawtext.m's TODOs. --- darwin/drawtext.m | 48 ++++++++++++++++++----------------------------- doc/drawtext | 10 ++++++++++ 2 files changed, 28 insertions(+), 30 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 8ec17ba5..d252f0e5 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -4,8 +4,7 @@ // TODO #define complain(...) implbug(__VA_ARGS__) -// TODO for all relevant routines, make sure we are freeing memory correctly -// TODO make sure allocation failures throw exceptions? +// TODO double-check that we are properly handling allocation failures (or just toll free bridge from cocoa) struct uiDrawFontFamilies { CFArrayRef fonts; }; @@ -15,7 +14,6 @@ uiDrawFontFamilies *uiDrawListFontFamilies(void) uiDrawFontFamilies *ff; ff = uiNew(uiDrawFontFamilies); - // TODO is there a way to get an error reason? ff->fonts = CTFontManagerCopyAvailableFontFamilyNames(); if (ff->fonts == NULL) implbug("error getting available font names (no reason specified) (TODO)"); @@ -33,7 +31,7 @@ char *uiDrawFontFamiliesFamily(uiDrawFontFamilies *ff, uintmax_t n) char *family; familystr = (CFStringRef) CFArrayGetValueAtIndex(ff->fonts, n); - // TODO create a uiDarwinCFStringToText()? + // toll-free bridge family = uiDarwinNSStringToText((NSString *) familystr); // Get Rule means we do not free familystr return family; @@ -155,12 +153,11 @@ static void addFontSmallCapsAttr(CFMutableDictionaryRef attr) #define ourNSFontWeightBlack 0.620000 static const CGFloat ctWeights[] = { // yeah these two have their names swapped; blame Pango - // TODO note that these names do not necessarily line up with their OS names [uiDrawTextWeightThin] = ourNSFontWeightUltraLight, [uiDrawTextWeightUltraLight] = ourNSFontWeightThin, [uiDrawTextWeightLight] = ourNSFontWeightLight, // for this one let's go between Light and Regular - // TODO figure out if we can rely on the order for these (and the one below) + // we're doing nearest so if there happens to be an exact value hopefully it's close enough [uiDrawTextWeightBook] = ourNSFontWeightLight + ((ourNSFontWeightRegular - ourNSFontWeightLight) / 2), [uiDrawTextWeightNormal] = ourNSFontWeightRegular, [uiDrawTextWeightMedium] = ourNSFontWeightMedium, @@ -174,7 +171,7 @@ static const CGFloat ctWeights[] = { // Unfortunately there are still no named constants for these. // Let's just use normalized widths. -// As far as I can tell (OS X only has condensed fonts, not expanded fonts; TODO), regardless of condensed or expanded, negative means condensed and positive means expanded. +// As far as I can tell (OS X only ships with condensed fonts, not expanded fonts; TODO), regardless of condensed or expanded, negative means condensed and positive means expanded. // TODO verify this is correct static const CGFloat ctStretches[] = { [uiDrawTextStretchUltraCondensed] = -1.0, @@ -199,7 +196,6 @@ struct closeness { // Stupidity: CTFont requires an **exact match for the entire traits dictionary**, otherwise it will **drop ALL the traits**. // We have to implement the closest match ourselves. // Also we have to do this before adding the small caps flags, because the matching descriptors won't have those. -// TODO document that font matching is closest match but the search method is OS defined CTFontDescriptorRef matchTraits(CTFontDescriptorRef against, uiDrawTextWeight weight, uiDrawTextItalic italic, uiDrawTextStretch stretch) { CGFloat targetWeight; @@ -254,7 +250,7 @@ CTFontDescriptorRef matchTraits(CTFontDescriptorRef against, uiDrawTextWeight we traits = CTFontDescriptorCopyAttribute(current, kCTFontTraitsAttribute); if (traits == NULL) { // couldn't get traits; be safe by ranking it lowest - // TODO figure out what the longest possible distances are + // LONGTERM figure out what the longest possible distances are closeness[i].weight = 3; closeness[i].italic = 2; closeness[i].stretch = 3; @@ -277,13 +273,13 @@ CTFontDescriptorRef matchTraits(CTFontDescriptorRef against, uiDrawTextWeight we if (cfnum != NULL) { CGFloat val; - // TODO instead of complaining for this and width, should we just fall through to the default? + // LONGTERM instead of complaining for this and width and possibly also symbolic traits above, should we just fall through to the default? if (CFNumberGetValue(cfnum, kCFNumberCGFloatType, &val) == false) complain("error getting weight value in matchTraits()"); closeness[i].weight = val - targetWeight; } else // okay there's no weight key; let's try the literal meaning of the symbolic constant - // TODO is the weight key guaranteed? + // LONGTERM is the weight key guaranteed? if ((symbolic & kCTFontBoldTrait) != 0) closeness[i].weight = ourNSFontWeightBold - targetWeight; else @@ -316,7 +312,7 @@ CTFontDescriptorRef matchTraits(CTFontDescriptorRef against, uiDrawTextWeight we // now try width // TODO this does not seem to be enough for Skia's extended variants; the width trait is 0 but the Expanded flag is on - // TODO verify the rest of this matrix + // TODO verify the rest of this matrix (what matrix?) cfnum = CFDictionaryGetValue(traits, kCTFontWidthTrait); if (cfnum != NULL) { CGFloat val; @@ -326,7 +322,7 @@ CTFontDescriptorRef matchTraits(CTFontDescriptorRef against, uiDrawTextWeight we closeness[i].stretch = val - targetStretch; } else // okay there's no width key; let's try the literal meaning of the symbolic constant - // TODO is the width key guaranteed? + // LONGTERM is the width key guaranteed? if ((symbolic & kCTFontExpandedTrait) != 0) closeness[i].stretch = 1.0 - targetStretch; else if ((symbolic & kCTFontCondensedTrait) != 0) @@ -354,7 +350,7 @@ CTFontDescriptorRef matchTraits(CTFontDescriptorRef against, uiDrawTextWeight we const struct closeness *b = (const struct closeness *) bb; // via http://www.gnu.org/software/libc/manual/html_node/Comparison-Functions.html#Comparison-Functions - // TODO is this really the best way? isn't it the same as if (*a < *b) return -1; if (*a > *b) return 1; return 0; ? + // LONGTERM is this really the best way? isn't it the same as if (*a < *b) return -1; if (*a > *b) return 1; return 0; ? return (a->distance > b->distance) - (a->distance < b->distance); }); // and the first element of the sorted array is what we want @@ -397,14 +393,7 @@ uiDrawTextFont *uiDrawLoadClosestFont(const uiDrawTextFontDescriptor *desc) cfdesc = CTFontDescriptorCreateWithAttributes(attr); // TODO release attr? cfdesc = matchTraits(cfdesc, desc->Weight, desc->Italic, desc->Stretch); -/*TODO - attr = extractAttributes(cfdesc); - CFRelease(cfdesc); - // and NOW create the final descriptor - cfdesc = CTFontDescriptorCreateWithAttributes(attr); - // TODO release attr? -*/ // specify the initial size again just to be safe f = CTFontCreateWithFontDescriptor(cfdesc, desc->Size, NULL); // TODO release cfdesc? @@ -425,7 +414,7 @@ uintptr_t uiDrawTextFontHandle(uiDrawTextFont *font) void uiDrawTextFontDescribe(uiDrawTextFont *font, uiDrawTextFontDescriptor *desc) { - // TODO TODO TODO TODO + // TODO } // text sizes and user space points are identical: @@ -473,9 +462,9 @@ uiDrawTextLayout *uiDrawNewTextLayout(const char *str, uiDrawTextFont *defaultFo uiDrawTextLayoutSetWidth(layout, width); // unfortunately the CFRanges for attributes expect UTF-16 codepoints - // we want full characters + // we want graphemes // fortunately CFStringGetRangeOfComposedCharactersAtIndex() is here for us - // https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Strings/Articles/stringsClusters.html says that this does work on surrogate pairs (despite the name), and that this is the preferred function for this particular job anyway + // https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Strings/Articles/stringsClusters.html says that this does work on all multi-codepoint graphemes (despite the name), and that this is the preferred function for this particular job anyway backing = CFAttributedStringGetString(layout->mas); n = CFStringGetLength(backing); // allocate one extra, just to be safe @@ -493,7 +482,6 @@ uiDrawTextLayout *uiDrawNewTextLayout(const char *str, uiDrawTextFont *defaultFo // and set the last one layout->charsToRanges[j].location = i; layout->charsToRanges[j].length = 0; - // TODO how will this affect drawing things that aren't surrogate pairs? return layout; } @@ -548,8 +536,8 @@ static void freeFramesetter(struct framesetter *fs) CFRelease(fs->fs); } -// TODO document that the extent width can be greater than the requested width if the requested width is small enough that only one character can fit -// TODO figure out how line separation and leading plays into this +// LONGTERM allow line separation and leading to be factored into a wrapping text layout + // TODO reconcile differences in character wrapping on platforms void uiDrawTextLayoutExtents(uiDrawTextLayout *layout, double *width, double *height) { @@ -609,15 +597,15 @@ void doDrawText(CGContextRef c, CGFloat cheight, double x, double y, uiDrawTextL CGContextRestoreGState(c); } -// TODO provide an equivalent to CTLineGetTypographicBounds() on uiDrawTextLayout? +// LONGTERM provide an equivalent to CTLineGetTypographicBounds() on uiDrawTextLayout? -// TODO keep this for TODO and documentation purposes +// LONGTERM keep this for later features and documentation purposes #if 0 w = CTLineGetTypographicBounds(line, &ascent, &descent, NULL); // though CTLineGetTypographicBounds() returns 0 on error, it also returns 0 on an empty string, so we can't reasonably check for error CFRelease(line); - // TODO provide a way to get the image bounds as a separate function later + // LONGTERM provide a way to get the image bounds as a separate function later bounds = CTLineGetImageBounds(line, c); // though CTLineGetImageBounds() returns CGRectNull on error, it also returns CGRectNull on an empty string, so we can't reasonably check for error diff --git a/doc/drawtext b/doc/drawtext index b0f1eafc..e16b6c3e 100644 --- a/doc/drawtext +++ b/doc/drawtext @@ -1 +1,11 @@ on some unix systems, alpha blending fonts may not be available; this depends on your installed version of pango and is determined at runtime by libui + +uiDrawTextLayoutExtents: document that the extent width can be greater than the requested width if the requested width is small enough that only one character can fit + + +font matching is closest match but the search method is OS defined + + +weight names in libui do not necessarily line up with their OS names + + From d88233a0fb81524bff507b04fd710eb297b668ba Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 29 May 2016 04:53:49 -0400 Subject: [PATCH 0176/1329] Documentation updates. --- README.md | 3 +++ buildnotes | 2 ++ darwin/spinbox.m | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a0784b7d..f8bf6838 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,9 @@ This README is being written.
*Note that today's entry may be updated later today.* +* **29 May 2016** + * Thanks to @pcwalton, we can now statically link libui! Simply do `make STATIC=1` instead of just `make`. + * **28 May 2016** * As promised, **the minimum system requirements are now OS X 10.8 and GTK+ 3.10 for OS X and Unix, respectively**. diff --git a/buildnotes b/buildnotes index 8a8c811b..56c2801f 100644 --- a/buildnotes +++ b/buildnotes @@ -27,6 +27,8 @@ The build-time settings are TODO DESTDIR=xxx applied before PREFIX; used by Debian + STATIC=1 + statically link instead of shared To build the test program use diff --git a/darwin/spinbox.m b/darwin/spinbox.m index f9df1874..218d1530 100644 --- a/darwin/spinbox.m +++ b/darwin/spinbox.m @@ -33,7 +33,7 @@ struct uiSpinbox { static CGFloat stepperYDelta(void) { // 10.8 - 0 - // 10.9 - xxx + // 10.9 - 0 // 10.10 - xxx // 10.11 - -1 return -1; From 52917c7e7133af4543f4e32591f666c7ab0a4ae5 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 29 May 2016 04:57:06 -0400 Subject: [PATCH 0177/1329] Added static linking to the test program. --- build/GNUmakefile.test | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/build/GNUmakefile.test b/build/GNUmakefile.test index 1179f8e8..c0bc0001 100644 --- a/build/GNUmakefile.test +++ b/build/GNUmakefile.test @@ -14,7 +14,11 @@ NAME = test SUFFIX = $(EXESUFFIX) ifeq ($(TOOLCHAIN),gcc) - LDFLAGS += -L$(OUTDIR) -lui + ifeq (,$(STATIC)) + LDFLAGS += -L$(OUTDIR) -lui + else + LDFLAGS += -L$(OUTDIR) -lui $(NATIVE_UI_LDFLAGS) + endif # tell the dynamic loader to search in the executable path for shared objects # note: OS X's linker complains if we say -rpath= instead of -rpath, # also note that OS X doesn't use $ORIGIN - see http://jorgen.tjer.no/post/2014/05/20/dt-rpath-ld-and-at-rpath-dyld/ From e114502605f90b728a52e4788ea9f9cc06b4e2b1 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 29 May 2016 12:00:55 -0400 Subject: [PATCH 0178/1329] Tried to set up Windows static linking. --- README.md | 2 +- build/GNUbasemsvc.mk | 2 +- build/GNUmakefile.example | 8 ++++++-- windows/GNUfiles.mk | 11 +++++++---- windows/GNUosspecific.mk | 5 +++++ windows/resources.rc | 2 ++ windows/winapi.hpp | 2 ++ 7 files changed, 24 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index f8bf6838..aab21e47 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ This README is being written.
*Note that today's entry may be updated later today.* * **29 May 2016** - * Thanks to @pcwalton, we can now statically link libui! Simply do `make STATIC=1` instead of just `make`. + * Thanks to @pcwalton, we can now statically link libui! Simply do `make STATIC=1` instead of just `make`. Right now it only works on GTK+ and OS X. * **28 May 2016** * As promised, **the minimum system requirements are now OS X 10.8 and GTK+ 3.10 for OS X and Unix, respectively**. diff --git a/build/GNUbasemsvc.mk b/build/GNUbasemsvc.mk index 46703b87..0fc5daba 100644 --- a/build/GNUbasemsvc.mk +++ b/build/GNUbasemsvc.mk @@ -97,7 +97,7 @@ endif # note: don't run cvtres directly; the linker does that for us $(OBJDIR)/%.rc.o: $$(subst _,/,%).rc $(HFILES) | $(OBJDIR) - @rc -nologo -v -fo $@ $< + @rc -nologo -v -fo $@ $(RCFLAGS) $< @echo ====== Compiled $< $(OBJDIR) $(OUTDIR): diff --git a/build/GNUmakefile.example b/build/GNUmakefile.example index 2b9d4cbd..e4d14272 100644 --- a/build/GNUmakefile.example +++ b/build/GNUmakefile.example @@ -45,8 +45,12 @@ ifeq ($(TOOLCHAIN),gcc) LDFLAGS += -pthread endif else - # TODO is there an equivalent to -L? - LDFLAGS += $(OUTDIR)/libui.lib + ifeq (,$(STATIC)) + # TODO is there an equivalent to -L? + LDFLAGS += $(OUTDIR)/libui.lib + else + LDFLAGS += $(OUTDIR)/libui.lib $(NATIVE_UI_LDFLAGS) + endif endif # executables are not shared libraries diff --git a/windows/GNUfiles.mk b/windows/GNUfiles.mk index d9e992d2..0c3b50fa 100644 --- a/windows/GNUfiles.mk +++ b/windows/GNUfiles.mk @@ -67,10 +67,7 @@ RCFILES += \ # LONGTERM split into a separate file or put in GNUmakefile.libui somehow? # flags for the Windows API -# notice that usp10.lib comes before gdi32.lib -# TODO prune this list -LDFLAGS += \ - user32.lib kernel32.lib usp10.lib gdi32.lib comctl32.lib uxtheme.lib msimg32.lib comdlg32.lib d2d1.lib dwrite.lib ole32.lib oleaut32.lib oleacc.lib uuid.lib +LDFLAGS += $(NATIVE_UI_LDFLAGS) # flags for building a shared library ifeq (,$(STATIC)) @@ -83,3 +80,9 @@ endif # no need for a soname # TODO .def file + +ifneq (,$(STATIC)) +CFLAGS += -D_UI_STATIC +CXXFLAGS += -D_UI_STATIC +RCFLAGS += -D _UI_STATIC +endif diff --git a/windows/GNUosspecific.mk b/windows/GNUosspecific.mk index c8335d66..b074e06a 100644 --- a/windows/GNUosspecific.mk +++ b/windows/GNUosspecific.mk @@ -7,3 +7,8 @@ STATICLIBSUFFIX = .lib TOOLCHAIN = msvc USESSONAME = 0 + +# notice that usp10.lib comes before gdi32.lib +# TODO prune this list +NATIVE_UI_LDFLAGS = \ + user32.lib kernel32.lib usp10.lib gdi32.lib comctl32.lib uxtheme.lib msimg32.lib comdlg32.lib d2d1.lib dwrite.lib ole32.lib oleaut32.lib oleacc.lib uuid.lib diff --git a/windows/resources.rc b/windows/resources.rc index a0acfcbd..dc60b5e0 100644 --- a/windows/resources.rc +++ b/windows/resources.rc @@ -7,7 +7,9 @@ // this is the Common Controls 6 manifest // TODO set up the string values here +#ifndef _UI_STATIC ISOLATIONAWARE_MANIFEST_RESOURCE_ID RT_MANIFEST "libui.manifest" +#endif // this is the dialog template used by tab pages; see windows/tabpage.c for details rcTabPageDialog DIALOGEX 0, 0, 100, 100 diff --git a/windows/winapi.hpp b/windows/winapi.hpp index 246760da..86aba5d7 100644 --- a/windows/winapi.hpp +++ b/windows/winapi.hpp @@ -9,7 +9,9 @@ #define INITGUID // for the manifest +#ifndef _UI_STATIC #define ISOLATION_AWARE_ENABLED 1 +#endif // get Windows version right; right now Windows Vista // unless otherwise stated, all values from Microsoft's sdkddkver.h From 56cb25b230e69718f60734a5772b0db19c681ec7 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 29 May 2016 13:07:48 -0400 Subject: [PATCH 0179/1329] Started Windows TODO resolution. --- windows/alloc.cpp | 5 +++-- windows/area.cpp | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/windows/alloc.cpp b/windows/alloc.cpp index 9511c881..eeee3ad4 100644 --- a/windows/alloc.cpp +++ b/windows/alloc.cpp @@ -14,14 +14,15 @@ void initAlloc(void) void uninitAlloc(void) { std::ostringstream oss; + std::string ossstr; // keep alive, just to be safe if (heap.size() == 0) return; for (const auto &alloc : heap) // note the void * cast; otherwise it'll be treated as a string oss << (void *) (alloc.first) << " " << types[alloc.second] << "\n"; - // TODO keep oss.str() alive? - userbug("Some data was leaked; either you left a uiControl lying around or there's a bug in libui itself. Leaked data:\n%s", oss.str().c_str()); + ossstr = oss.str(); + userbug("Some data was leaked; either you left a uiControl lying around or there's a bug in libui itself. Leaked data:\n%s", ossstr.c_str()); } #define rawBytes(pa) (&((*pa)[0])) diff --git a/windows/area.cpp b/windows/area.cpp index bdad93f5..b4a87517 100644 --- a/windows/area.cpp +++ b/windows/area.cpp @@ -69,7 +69,8 @@ ATOM registerAreaClass(HICON hDefaultIcon, HCURSOR hDefaultCursor) wc.hIcon = hDefaultIcon; wc.hCursor = hDefaultCursor; wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1); - // TODO specify CS_HREDRAW/CS_VREDRAW in addition to or instead of calling InvalidateRect(NULL) in WM_WINDOWPOSCHANGED above, or not at all? + // this is just to be safe; see the InvalidateRect() call in the WM_WINDOWPOSCHANGED handler for more details + wc.style = CS_HREDRAW | CS_VREDRAW; return RegisterClassW(&wc); } From be8a957689df312556c97c102a793f4744314771 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 29 May 2016 18:35:06 -0400 Subject: [PATCH 0180/1329] Fixed static linking on Windows. --- README.md | 3 ++- build/GNUbasemsvc.mk | 17 +++++++++++++++-- build/GNUmakefile.example | 5 ++++- build/GNUmakefile.test | 14 ++++++++++++-- darwin/spinbox.m | 2 +- doc/static | 1 + examples/example.static.manifest | 32 ++++++++++++++++++++++++++++++++ examples/resources.rc | 4 ++++ test/resources.rc | 4 ++++ test/test.static.manifest | 32 ++++++++++++++++++++++++++++++++ 10 files changed, 107 insertions(+), 7 deletions(-) create mode 100644 doc/static create mode 100644 examples/example.static.manifest create mode 100644 test/test.static.manifest diff --git a/README.md b/README.md index aab21e47..fedc1cc1 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,8 @@ This README is being written.
*Note that today's entry may be updated later today.* * **29 May 2016** - * Thanks to @pcwalton, we can now statically link libui! Simply do `make STATIC=1` instead of just `make`. Right now it only works on GTK+ and OS X. + * Thanks to @pcwalton, we can now statically link libui! Simply do `make STATIC=1` instead of just `make`. + * On Windows you must provide a Common Controls 6 manifest for output static libraries to work properly. * **28 May 2016** * As promised, **the minimum system requirements are now OS X 10.8 and GTK+ 3.10 for OS X and Unix, respectively**. diff --git a/build/GNUbasemsvc.mk b/build/GNUbasemsvc.mk index 0fc5daba..5078ac7c 100644 --- a/build/GNUbasemsvc.mk +++ b/build/GNUbasemsvc.mk @@ -59,16 +59,26 @@ endif OFILES = \ $(subst /,_,$(CFILES)) \ $(subst /,_,$(CXXFILES)) \ - $(subst /,_,$(MFILES)) \ + $(subst /,_,$(MFILES)) +ifeq (,$(STATIC)) +OFILES += \ $(subst /,_,$(RCFILES)) +else +RESFILES = \ + $(subst /,_,$(RCFILES)) +endif OFILES := $(OFILES:%=$(OBJDIR)/%.o) OUT = $(OUTDIR)/$(NAME)$(SUFFIX) +ifneq (,$(STATIC)) +RESOUT = $(OUTDIR)/$(NAME).res +endif +# otherwise keep $(RESOUT) empty # TODO use $(CC), $(CXX), $(LD), and s$(RC) -$(OUT): $(OFILES) | $(OUTDIR) +$(OUT): $(OFILES) $(RESOUT) | $(OUTDIR) ifeq (,$(STATICLIB)) @link -out:$(OUT) $(OFILES) $(LDFLAGS) else @@ -99,6 +109,9 @@ endif $(OBJDIR)/%.rc.o: $$(subst _,/,%).rc $(HFILES) | $(OBJDIR) @rc -nologo -v -fo $@ $(RCFLAGS) $< @echo ====== Compiled $< +$(RESOUT): $$(RCFILES) $(HFILES) | $(OUTDIR) + @rc -nologo -v -fo $@ $(RCFLAGS) $< + @echo ====== Compiled $< $(OBJDIR) $(OUTDIR): @mkdir $@ diff --git a/build/GNUmakefile.example b/build/GNUmakefile.example index e4d14272..83932910 100644 --- a/build/GNUmakefile.example +++ b/build/GNUmakefile.example @@ -23,6 +23,9 @@ HFILES += \ ifeq ($(OS),windows) RCFILES += \ examples/resources.rc +ifneq (,$(STATIC)) +RCFLAGS += -D _UI_STATIC +endif endif NAME = $(EXAMPLE) @@ -49,7 +52,7 @@ else # TODO is there an equivalent to -L? LDFLAGS += $(OUTDIR)/libui.lib else - LDFLAGS += $(OUTDIR)/libui.lib $(NATIVE_UI_LDFLAGS) + LDFLAGS += $(OUTDIR)/libui.lib $(OUTDIR)/libui.res $(OUTDIR)/$(NAME).res $(NATIVE_UI_LDFLAGS) endif endif diff --git a/build/GNUmakefile.test b/build/GNUmakefile.test index c0bc0001..9e123977 100644 --- a/build/GNUmakefile.test +++ b/build/GNUmakefile.test @@ -13,6 +13,12 @@ HFILES += \ NAME = test SUFFIX = $(EXESUFFIX) +ifeq ($(OS),windows) +ifneq (,$(STATIC)) +RCFLAGS += -D _UI_STATIC +endif +endif + ifeq ($(TOOLCHAIN),gcc) ifeq (,$(STATIC)) LDFLAGS += -L$(OUTDIR) -lui @@ -29,8 +35,12 @@ ifeq ($(TOOLCHAIN),gcc) LDFLAGS += -Wl,-rpath,'$$ORIGIN' endif else - # TODO is there an equivalent to -L? - LDFLAGS += $(OUTDIR)/libui.lib + ifeq (,$(STATIC)) + # TODO is there an equivalent to -L? + LDFLAGS += $(OUTDIR)/libui.lib + else + LDFLAGS += $(OUTDIR)/libui.lib $(OUTDIR)/libui.res $(OUTDIR)/$(NAME).res $(NATIVE_UI_LDFLAGS) + endif endif # executables are not shared libraries diff --git a/darwin/spinbox.m b/darwin/spinbox.m index 218d1530..7347ef91 100644 --- a/darwin/spinbox.m +++ b/darwin/spinbox.m @@ -34,7 +34,7 @@ static CGFloat stepperYDelta(void) { // 10.8 - 0 // 10.9 - 0 - // 10.10 - xxx + // 10.10 - -1 // 10.11 - -1 return -1; } diff --git a/doc/static b/doc/static new file mode 100644 index 00000000..de039670 --- /dev/null +++ b/doc/static @@ -0,0 +1 @@ +comctl6 diff --git a/examples/example.static.manifest b/examples/example.static.manifest new file mode 100644 index 00000000..d8e83a83 --- /dev/null +++ b/examples/example.static.manifest @@ -0,0 +1,32 @@ + + + +Your application description here. + + + + + + + + + + + + + + + + diff --git a/examples/resources.rc b/examples/resources.rc index b55e24ec..49f486c1 100644 --- a/examples/resources.rc +++ b/examples/resources.rc @@ -6,4 +6,8 @@ // this is the Common Controls 6 manifest // TODO set up the string values here // 1 is the value of CREATEPROCESS_MANIFEST_RESOURCE_ID and 24 is the value of RT_MANIFEST; we use it directly to avoid needing to share winapi.h with the tests and examples +#ifndef _UI_STATIC 1 24 "example.manifest" +#else +1 24 "example.static.manifest" +#endif diff --git a/test/resources.rc b/test/resources.rc index 11c78a76..ebc5d6e6 100644 --- a/test/resources.rc +++ b/test/resources.rc @@ -6,4 +6,8 @@ // this is the Common Controls 6 manifest // TODO set up the string values here // 1 is the value of CREATEPROCESS_MANIFEST_RESOURCE_ID and 24 is the value of RT_MANIFEST; we use it directly to avoid needing to share winapi.h with the tests and examples +#ifndef _UI_STATIC 1 24 "test.manifest" +#else +1 24 "test.static.manifest" +#endif diff --git a/test/test.static.manifest b/test/test.static.manifest new file mode 100644 index 00000000..d8e83a83 --- /dev/null +++ b/test/test.static.manifest @@ -0,0 +1,32 @@ + + + +Your application description here. + + + + + + + + + + + + + + + + From 473e3b593bddf8ac6e3978287939679bc7622176 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 29 May 2016 18:36:11 -0400 Subject: [PATCH 0181/1329] Quick README fix. --- README.md | 2 +- doc/static | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fedc1cc1..186635d9 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ This README is being written.
* **29 May 2016** * Thanks to @pcwalton, we can now statically link libui! Simply do `make STATIC=1` instead of just `make`. - * On Windows you must provide a Common Controls 6 manifest for output static libraries to work properly. + * On Windows you must link both `libui.lib` and `libui.res` AND provide a Common Controls 6 manifest for output static binaries to work properly. * **28 May 2016** * As promised, **the minimum system requirements are now OS X 10.8 and GTK+ 3.10 for OS X and Unix, respectively**. diff --git a/doc/static b/doc/static index de039670..fe1a09ce 100644 --- a/doc/static +++ b/doc/static @@ -1 +1,2 @@ comctl6 +libui.res From efc1e55e5815bcfd8612a3d42ef4e988bd8103b1 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 29 May 2016 18:40:53 -0400 Subject: [PATCH 0182/1329] More OS X spinbox stuff. --- darwin/spinbox.m | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/darwin/spinbox.m b/darwin/spinbox.m index 7347ef91..0663e239 100644 --- a/darwin/spinbox.m +++ b/darwin/spinbox.m @@ -30,12 +30,12 @@ struct uiSpinbox { }; // yes folks, this varies by operating system! woo! +// 10.10 started drawing the NSStepper one point too low, so we have to fix it up conditionally +// TODO test this; we'll probably have to substitute 10_9 static CGFloat stepperYDelta(void) { - // 10.8 - 0 - // 10.9 - 0 - // 10.10 - -1 - // 10.11 - -1 + if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_9) + return 0; return -1; } From 0738eca6e45700d8b639f3294ed7da2915bf7943 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 29 May 2016 19:01:48 -0400 Subject: [PATCH 0183/1329] More work. --- darwin/spinbox.m | 1 + doc/drawtext | 2 ++ windows/fontbutton.cpp | 5 +---- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/darwin/spinbox.m b/darwin/spinbox.m index 0663e239..30fbc00b 100644 --- a/darwin/spinbox.m +++ b/darwin/spinbox.m @@ -34,6 +34,7 @@ struct uiSpinbox { // TODO test this; we'll probably have to substitute 10_9 static CGFloat stepperYDelta(void) { + // via https://developer.apple.com/library/mac/releasenotes/AppKit/RN-AppKit/ if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_9) return 0; return -1; diff --git a/doc/drawtext b/doc/drawtext index e16b6c3e..9d377135 100644 --- a/doc/drawtext +++ b/doc/drawtext @@ -9,3 +9,5 @@ font matching is closest match but the search method is OS defined weight names in libui do not necessarily line up with their OS names +uiDrawFontHandle() may not return a unique handle per instance + diff --git a/windows/fontbutton.cpp b/windows/fontbutton.cpp index 22e11abb..c4f841db 100644 --- a/windows/fontbutton.cpp +++ b/windows/fontbutton.cpp @@ -25,8 +25,7 @@ static void updateFontButtonLabel(uiFontButton *b) WCHAR *text; text = fontDialogParamsToString(&(b->params)); - // TODO error check - SendMessageW(b->hwnd, WM_SETTEXT, 0, (LPARAM) text); + setWindowText(text); uiFree(text); // changing the text might necessitate a change in the button's size @@ -94,8 +93,6 @@ uiDrawTextFont *uiFontButtonFont(uiFontButton *b) return mkTextFont(b->params.font, TRUE, b->params.familyName, TRUE, b->params.size); } -// TODO document that the Handle of a Font may not be unique - void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data) { b->onChanged = f; From a226c80993be2a59f9214d1ac570406bc9d35964 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 29 May 2016 19:55:53 -0400 Subject: [PATCH 0184/1329] More TODO resolution and elimination and delegation and explanation. --- windows/combobox.cpp | 7 ++----- windows/editablecombo.cpp | 13 +++++-------- windows/fontbutton.cpp | 2 +- windows/group.cpp | 22 ++++++++++++++++------ windows/init.cpp | 15 ++++++++------- windows/main.cpp | 16 +++++++++------- windows/menu.cpp | 14 ++++++++------ windows/utilwin.cpp | 5 +++-- 8 files changed, 52 insertions(+), 42 deletions(-) diff --git a/windows/combobox.cpp b/windows/combobox.cpp index cb09e00d..ac10f211 100644 --- a/windows/combobox.cpp +++ b/windows/combobox.cpp @@ -1,9 +1,6 @@ // 20 may 2015 #include "uipriv_windows.hpp" -// TODO -// - is there extra space on the bottom? - // we as Common Controls 6 users don't need to worry about the height of comboboxes; see http://blogs.msdn.com/b/oldnewthing/archive/2006/03/10/548537.aspx struct uiCombobox { @@ -36,8 +33,8 @@ void uiComboboxDestroy(uiControl *cc) uiWindowsControlAllDefaultsExceptDestroy(uiCombobox) // from http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing -#define comboboxWidth 107 /* this is actually the shorter progress bar width, but Microsoft only indicates as wide as necessary; TODO */ -#define comboboxHeight 14 +#define comboboxWidth 107 /* this is actually the shorter progress bar width, but Microsoft only indicates as wide as necessary; LONGTERM */ +#define comboboxHeight 14 /* LONGTERM: is this too high? */ static void uiComboboxMinimumSize(uiWindowsControl *cc, intmax_t *width, intmax_t *height) { diff --git a/windows/editablecombo.cpp b/windows/editablecombo.cpp index 4c3820d1..38d38aab 100644 --- a/windows/editablecombo.cpp +++ b/windows/editablecombo.cpp @@ -1,9 +1,6 @@ // 20 may 2015 #include "uipriv_windows.hpp" -// TODO -// - is there extra space on the bottom? - // we as Common Controls 6 users don't need to worry about the height of comboboxes; see http://blogs.msdn.com/b/oldnewthing/archive/2006/03/10/548537.aspx struct uiEditableCombobox { @@ -19,11 +16,11 @@ static BOOL onWM_COMMAND(uiControl *cc, HWND hwnd, WORD code, LRESULT *lResult) if (code == CBN_SELCHANGE) { // like on OS X, this is sent before the edit has been updated :( - // TODO error check - PostMessage(parentOf(hwnd), + if (PostMessage(parentOf(hwnd), WM_COMMAND, MAKEWPARAM(GetWindowLongPtrW(hwnd, GWLP_ID), CBN_EDITCHANGE), - (LPARAM) hwnd); + (LPARAM) hwnd) == 0) + logLastError(L"error posting CBN_EDITCHANGE after CBN_SELCHANGE"); *lResult = 0; return TRUE; } @@ -46,8 +43,8 @@ void uiEditableComboboxDestroy(uiControl *cc) uiWindowsControlAllDefaultsExceptDestroy(uiEditableCombobox) // from http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing -#define comboboxWidth 107 /* this is actually the shorter progress bar width, but Microsoft only indicates as wide as necessary; TODO */ -#define comboboxHeight 14 +#define comboboxWidth 107 /* this is actually the shorter progress bar width, but Microsoft only indicates as wide as necessary; LONGTERM */ +#define comboboxHeight 14 /* LONGTERM: is this too high? */ static void uiEditableComboboxMinimumSize(uiWindowsControl *cc, intmax_t *width, intmax_t *height) { diff --git a/windows/fontbutton.cpp b/windows/fontbutton.cpp index c4f841db..b0f2c047 100644 --- a/windows/fontbutton.cpp +++ b/windows/fontbutton.cpp @@ -25,7 +25,7 @@ static void updateFontButtonLabel(uiFontButton *b) WCHAR *text; text = fontDialogParamsToString(&(b->params)); - setWindowText(text); + setWindowText(b->hwnd, text); uiFree(text); // changing the text might necessitate a change in the button's size diff --git a/windows/group.cpp b/windows/group.cpp index 3af67acd..b1e4c653 100644 --- a/windows/group.cpp +++ b/windows/group.cpp @@ -93,15 +93,18 @@ static void uiGroupMinimumSize(uiWindowsControl *c, intmax_t *width, intmax_t *h { uiGroup *g = uiGroup(c); int mx, mtop, mbottom; + intmax_t labelWidth; *width = 0; *height = 0; if (g->child != NULL) uiWindowsControlMinimumSize(uiWindowsControl(g->child), width, height); + labelWidth = uiWindowsWindowTextWidth(g->hwnd); + if (*width < labelWidth) // don't clip the label; it doesn't ellipsize + *width = labelWidth; groupMargins(g, &mx, &mtop, &mbottom); *width += 2 * mx; *height += mtop + mbottom; - // TODO label width? and when? } static void uiGroupMinimumSizeChanged(uiWindowsControl *c) @@ -159,18 +162,25 @@ void uiGroupSetMargined(uiGroup *g, int margined) static LRESULT CALLBACK groupSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) { uiGroup *g = uiGroup(dwRefData); + WINDOWPOS *wp = (WINDOWPOS *) lParam; + MINMAXINFO *mmi = (MINMAXINFO *) lParam; + intmax_t minwid, minht; LRESULT lResult; if (handleParentMessages(hwnd, uMsg, wParam, lParam, &lResult) != FALSE) return lResult; switch (uMsg) { case WM_WINDOWPOSCHANGED: - // TODO check - // TODO add check in container.c + if ((wp->flags & SWP_NOSIZE) != 0) + break; groupRelayout(g); - // TODO is this right? - break; - // TODO WM_GETMINMAXINFO + return 0; + case WM_GETMINMAXINFO: + lResult = DefWindowProcW(hwnd, uMsg, wParam, lParam); + uiWindowsControlMinimumSize(uiWindowsControl(g), &minwid, &minht); + mmi->ptMinTrackSize.x = minwid; + mmi->ptMinTrackSize.y = minht; + return lResult; case WM_NCDESTROY: if (RemoveWindowSubclass(hwnd, groupSubProc, uIdSubclass) == FALSE) logLastError(L"error removing groupbox subclass"); diff --git a/windows/init.cpp b/windows/init.cpp index 2de75c4f..c91929f9 100644 --- a/windows/init.cpp +++ b/windows/init.cpp @@ -6,7 +6,7 @@ int nCmdShow; HFONT hMessageFont; -// TODO needed? +// LONGTERM needed? HBRUSH hollowBrush; // the returned pointer is actually to the second character @@ -38,7 +38,7 @@ static const char *initerr(const char *message, const WCHAR *label, DWORD value) #define ieLastErr(msg) initerr("=" msg, L"GetLastError() ==", GetLastError()) #define ieHRESULT(msg, hr) initerr("=" msg, L"HRESULT", (DWORD) hr) -// TODO make common +// LONGTERM make common uiInitOptions options; #define wantedICCClasses ( \ @@ -70,6 +70,8 @@ const char *uiInit(uiInitOptions *o) if ((si.dwFlags & STARTF_USESHOWWINDOW) != 0) nCmdShow = si.wShowWindow; + // LONGTERM set DPI awareness + hDefaultIcon = LoadIconW(NULL, IDI_APPLICATION); if (hDefaultIcon == NULL) return ieLastErr("loading default icon for window classes"); @@ -79,7 +81,7 @@ const char *uiInit(uiInitOptions *o) ce = initUtilWindow(hDefaultIcon, hDefaultCursor); if (ce != NULL) - return ieLastErr("TODO use ce here"); + return initerr(ce, L"GetLastError() ==", GetLastError()); if (registerWindowClass(hDefaultIcon, hDefaultCursor) == 0) return ieLastErr("registering uiWindow window class"); @@ -92,9 +94,8 @@ const char *uiInit(uiInitOptions *o) if (hMessageFont == NULL) return ieLastErr("loading default messagebox font; this is the default UI font"); - // TODO rewrite this error message if (initContainer(hDefaultIcon, hDefaultCursor) == 0) - return ieLastErr("initializing uiMakeContainer() window class"); + return ieLastErr("initializing uiWindowsMakeContainer() window class"); hollowBrush = (HBRUSH) GetStockObject(HOLLOW_BRUSH); if (hollowBrush == NULL) @@ -109,8 +110,8 @@ const char *uiInit(uiInitOptions *o) hr = CoInitialize(NULL); if (hr != S_OK && hr != S_FALSE) return ieHRESULT("initializing COM", hr); - // TODO initialize COM security - // TODO (windows vista) turn off COM exception handling + // LONGTERM initialize COM security + // LONGTERM (windows vista) turn off COM exception handling hr = initDraw(); if (hr != S_OK) diff --git a/windows/main.cpp b/windows/main.cpp index 3edd8635..1db790cb 100644 --- a/windows/main.cpp +++ b/windows/main.cpp @@ -45,13 +45,15 @@ void unregisterMessageFilter(void) static void processMessage(MSG *msg) { - HWND active; + HWND correctParent; - // TODO really active? or parentToplevel(msg->hwnd)? - active = GetActiveWindow(); - if (active != NULL) - // TODO find documentation that says IsDialogMessage() calls CallMsgFilter() for us, because that's what's happening - if (IsDialogMessage(active, msg) != 0) + if (msg->hwnd != NULL) + correctParent = parentToplevel(msg->hwnd); + else // just to be safe + correctParent = GetActiveWindow(); + if (correctParent != NULL) + // this calls our mesage filter above for us + if (IsDialogMessage(correctParent, msg) != 0) return; TranslateMessage(msg); DispatchMessageW(msg); @@ -118,6 +120,6 @@ void uiQuit(void) void uiQueueMain(void (*f)(void *data), void *data) { if (PostMessageW(utilWindow, msgQueued, (WPARAM) f, (LPARAM) data) == 0) - // TODO this is likely not safe to call across threads (allocates memory) + // LONGTERM this is likely not safe to call across threads (allocates memory) logLastError(L"error queueing function to run on main thread"); } diff --git a/windows/menu.cpp b/windows/menu.cpp index 484bf756..bfac0129 100644 --- a/windows/menu.cpp +++ b/windows/menu.cpp @@ -1,7 +1,7 @@ // 24 april 2015 #include "uipriv_windows.hpp" -// TODO migrate to std::vector +// LONGTERM migrate to std::vector static uiMenu **menus = NULL; static uintmax_t len = 0; @@ -146,10 +146,12 @@ static uiMenuItem *newItem(uiMenu *m, int type, const char *name) curID++; } - // TODO copy this from the unix one - item->onClicked = defaultOnClicked; - if (item->type == typeQuit) + if (item->type == typeQuit) { + // can't call uiMenuItemOnClicked() here item->onClicked = onQuitClicked; + item->onClickedData = NULL; + } else + uiMenuItemOnClicked(item, defaultOnClicked, NULL); return item; } @@ -185,7 +187,7 @@ uiMenuItem *uiMenuAppendPreferencesItem(uiMenu *m) uiMenuItem *uiMenuAppendAboutItem(uiMenu *m) { if (hasAbout) - // TODO place these userbug strings in a header? + // TODO place these userbug strings in a header userbug("You can not have multiple About menu items in a program."); hasAbout = TRUE; newItem(m, typeSeparator, NULL); @@ -350,7 +352,7 @@ void uninitMenus(void) for (j = 0; j < m->len; j++) { item = m->items[j]; if (item->len != 0) - // TODO userbug()? + // LONGTERM userbug()? implbug("menu item %p (%ws) still has uiWindows attached; did you forget to destroy some windows?", item, item->name); if (item->name != NULL) uiFree(item->name); diff --git a/windows/utilwin.cpp b/windows/utilwin.cpp index 992eea68..414ae83a 100644 --- a/windows/utilwin.cpp +++ b/windows/utilwin.cpp @@ -51,7 +51,8 @@ const char *initUtilWindow(HICON hDefaultIcon, HCURSOR hDefaultCursor) wc.hCursor = hDefaultCursor; wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1); if (RegisterClass(&wc) == 0) - return "registering utility window class"; + // see init.cpp for an explanation of the =s + return "=registering utility window class"; utilWindow = CreateWindowExW(0, utilWindowClass, L"libui utility window", @@ -59,7 +60,7 @@ const char *initUtilWindow(HICON hDefaultIcon, HCURSOR hDefaultCursor) 0, 0, 100, 100, NULL, NULL, hInstance, NULL); if (utilWindow == NULL) - return "creating utility window"; + return "=creating utility window"; // and just to be safe EnableWindow(utilWindow, FALSE); From 2ed29a7fa0e36c2efd68db24a6e08448144eb6d1 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 29 May 2016 21:13:03 -0400 Subject: [PATCH 0185/1329] More stuff. Screw this; releasing as is. --- windows/multilineentry.cpp | 13 +++++-------- windows/resources.rc | 6 +++--- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/windows/multilineentry.cpp b/windows/multilineentry.cpp index bac05752..7151cdd0 100644 --- a/windows/multilineentry.cpp +++ b/windows/multilineentry.cpp @@ -1,8 +1,7 @@ // 8 april 2015 #include "uipriv_windows.hpp" -// TODO there's alpha darkening of text going on; something is up in our parent logic -// TODO resizing collapses newlines +// TODO there's alpha darkening of text going on in read-only ones; something is up in our parent logic struct uiMultilineEntry { uiWindowsControl c; @@ -38,7 +37,7 @@ uiWindowsControlAllDefaultsExceptDestroy(uiMultilineEntry) // from http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing #define entryWidth 107 /* this is actually the shorter progress bar width, but Microsoft only indicates as wide as necessary */ -// TODO change this for multiline text boxes +// LONGTERM change this for multiline text boxes (longterm because how?) #define entryHeight 14 static void uiMultilineEntryMinimumSize(uiWindowsControl *c, intmax_t *width, intmax_t *height) @@ -60,7 +59,6 @@ static void defaultOnChanged(uiMultilineEntry *e, void *data) // do nothing } -// TODO apply crlf conversion char *uiMultilineEntryText(uiMultilineEntry *e) { char *out; @@ -70,7 +68,6 @@ char *uiMultilineEntryText(uiMultilineEntry *e) return out; } -// TODO apply crlf conversion void uiMultilineEntrySetText(uiMultilineEntry *e, const char *text) { char *crlf; @@ -84,17 +81,16 @@ void uiMultilineEntrySetText(uiMultilineEntry *e, const char *text) // don't queue the control for resize; entry sizes are independent of their contents } -// TOOD crlf stuff void uiMultilineEntryAppend(uiMultilineEntry *e, const char *text) { LRESULT n; char *crlf; WCHAR *wtext; - // TODO does doing this raise EN_CHANGED? + // doing this raises an EN_CHANGED + e->inhibitChanged = TRUE; // TODO preserve selection? caret? what if caret used to be at end? // TODO scroll to bottom? - // TODO overdraw issues n = SendMessageW(e->hwnd, WM_GETTEXTLENGTH, 0, 0); SendMessageW(e->hwnd, EM_SETSEL, n, n); crlf = LFtoCRLF(text); @@ -102,6 +98,7 @@ void uiMultilineEntryAppend(uiMultilineEntry *e, const char *text) uiFree(crlf); SendMessageW(e->hwnd, EM_REPLACESEL, FALSE, (LPARAM) wtext); uiFree(wtext); + e->inhibitChanged = FALSE; } void uiMultilineEntryOnChanged(uiMultilineEntry *e, void (*f)(uiMultilineEntry *, void *), void *data) diff --git a/windows/resources.rc b/windows/resources.rc index dc60b5e0..74402dfb 100644 --- a/windows/resources.rc +++ b/windows/resources.rc @@ -6,7 +6,7 @@ #pragma code_page(65001) // this is the Common Controls 6 manifest -// TODO set up the string values here +// LONGTERM set up the string values here #ifndef _UI_STATIC ISOLATIONAWARE_MANIFEST_RESOURCE_ID RT_MANIFEST "libui.manifest" #endif @@ -21,8 +21,8 @@ END // this is for our custom DirectWrite-based font dialog (see fontdialog.cpp) // this is based on the "New Font Dialog with Syslink" in Microsoft's font.dlg -// TODO look at localization -// TODO make it look tighter and nicer like the real one, including the actual heights of the font family and style comboboxes +// LONGTERM look at localization +// LONGTERM make it look tighter and nicer like the real one, including the actual heights of the font family and style comboboxes rcFontDialog DIALOGEX 13, 54, 243, 200 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU | DS_3DLOOK CAPTION "Font" From 708d8f38e9892a9510f22acaf893dc1ef27e71ae Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 29 May 2016 22:02:49 -0400 Subject: [PATCH 0186/1329] Don't use -fPIC on static builds. --- build/GNUbasegcc.mk | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/build/GNUbasegcc.mk b/build/GNUbasegcc.mk index b6741d1d..9e3e8636 100644 --- a/build/GNUbasegcc.mk +++ b/build/GNUbasegcc.mk @@ -1,11 +1,8 @@ # 16 october 2015 -# TODO the loader looks for the soname, not the base name, which is frustrating - # Global flags. CFLAGS += \ - -fPIC \ -Wall -Wextra -pedantic \ -Wno-unused-parameter \ -Wno-switch \ @@ -14,14 +11,17 @@ CFLAGS += \ # C++11 is needed due to stupid rules involving commas at the end of enum lists that C++03 stupidly didn't follow # This means sorry, no GCC 2 for Haiku builds :( CXXFLAGS += \ - -fPIC \ -Wall -Wextra -pedantic \ -Wno-unused-parameter \ -Wno-switch \ --std=c++11 -LDFLAGS += \ - -fPIC +# -fPIC shouldn't be used with static builds (see https://github.com/andlabs/libui/issues/72#issuecomment-222395547) +ifeq (,$(STATIC)) +CFLAGS += -fPIC +CXXFLAGS += -fPIC +LDFLAGS += -fPIC +endif ifneq ($(RELEASE),1) CFLAGS += -g From 8247bc2b2b318fba7db2e1740b863a0df6ad6d54 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 29 May 2016 22:38:29 -0400 Subject: [PATCH 0187/1329] Alpha 3 release. --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 186635d9..a06333c2 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,14 @@ This README is being written.
## Announcements +* **29 May 2016** + * **Alpha 3 is here!** Get it [here](https://github.com/andlabs/libui/releases/tag/alpha3). + * The next packaged release will introduce: + * uiGrid, another way to lay out controls, a la GtkGrid + * uiOpenGLArea, a way to render OpenGL content in a libui uiArea + * uiTable, a data grid control that may or may not have tree facilities (if it does, it will be called uiTree instead) + * a complete, possibly rewritten, drawing and text rendering infrastructure + * **24 May 2016** * You can now help choose [a potential new build system for libui](https://github.com/andlabs/libui/issues/62). * Tomorrow I will decide if OS X 10.7 will also be dropped alongside GTK+ 3.4-3.8 this Saturday. Stay tuned. From f779978a424f2a217ace4262a05a62decd431d9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Ventura?= Date: Mon, 30 May 2016 13:06:25 +0100 Subject: [PATCH 0188/1329] Declare 10.9 version in Darwin Fixes compilation issues in Mavericks --- darwin/uipriv_darwin.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index c1e842ca..7805fac8 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -13,6 +13,10 @@ #define toNSString(str) [NSString stringWithUTF8String:(str)] #define fromNSString(str) [(str) UTF8String] +#ifndef NSAppKitVersionNumber10_9 +#define NSAppKitVersionNumber10_9 1265 +#endif + // menu.m @interface menuManager : NSObject { struct mapTable *items; From 0fc4ff588fabb24c465cd71ddc4b651004aa75b4 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 30 May 2016 12:00:08 -0400 Subject: [PATCH 0189/1329] Added a preliminary CMakeLists.txt. This might be the one I go with. --- CMakeLists.txt | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..bae91db7 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,42 @@ +# 30 may 2016 +cmake_minimum_required(VERSION 2.8.12) +project(libui) + +option(examples "Build the example projects" OFF) +option(test "Build the test project" OFF) + +set(_OSDIR unix) +set(_OSSRCEXT c) +set(_OUTNAME libui.so.0) +if(APPLE) + set(_OSDIR darwin) + set(_OSSRCEXT m) + set(_OUTNAME libui.A.dylib) + + # always use our rpath + set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) + # the / is required by some older versions of OS X + set(CMAKE_INSTALL_RPATH "@executable_path/") + set(CMAKE_MACOSX_RPATH TRUE) +elseif(WINDOWS) + set(_OSDIR windows) + set(_OSSRCEXT cpp) + set(_OUTNAME libui.dll) +endif(APPLE) + +include_directories(. common ${_OSDIR} test) + +file(GLOB SOURCES + common/*.c + ${_OSDIR}/*.${_OSSRCEXT}) + +add_library(libui SHARED ${SOURCES}) +set_target_properties(libui PROPERTIES OUTPUT_NAME ${_OUTNAME}) +target_link_libraries(libui "-framework Foundation -framework AppKit") + +if(test) + file(GLOB TESTSOURCES test/*.c) + add_executable(tester ${TESTSOURCES}) + set_target_properties(tester PROPERTIES OUTPUT_NAME test) + target_link_libraries(tester libui) +endif(test) From ee8b4f71c09cdd33b578ac52ddcc8328379b8fcf Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 30 May 2016 13:28:27 -0400 Subject: [PATCH 0190/1329] More cmake work. --- CMakeLists.txt | 60 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bae91db7..b1de814c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,16 +2,11 @@ cmake_minimum_required(VERSION 2.8.12) project(libui) -option(examples "Build the example projects" OFF) -option(test "Build the test project" OFF) - -set(_OSDIR unix) -set(_OSSRCEXT c) -set(_OUTNAME libui.so.0) if(APPLE) set(_OSDIR darwin) set(_OSSRCEXT m) - set(_OUTNAME libui.A.dylib) + set(_SETVERSION TRUE) + set(_VERSION "A") # always use our rpath set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) @@ -21,22 +16,51 @@ if(APPLE) elseif(WINDOWS) set(_OSDIR windows) set(_OSSRCEXT cpp) - set(_OUTNAME libui.dll) + set(_SETVERSION FALSE) +else(APPLE) + set(_OSDIR unix) + set(_OSSRCEXT c) + set(_SETVERSION TRUE) + set(_VERSION "0") + + # always use our rpath + set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) endif(APPLE) include_directories(. common ${_OSDIR} test) - file(GLOB SOURCES common/*.c ${_OSDIR}/*.${_OSSRCEXT}) -add_library(libui SHARED ${SOURCES}) -set_target_properties(libui PROPERTIES OUTPUT_NAME ${_OUTNAME}) -target_link_libraries(libui "-framework Foundation -framework AppKit") +macro(libui _name _mode _setver _exclude) + add_library(${_name} ${_mode} ${SOURCES}) + set_target_properties(${_name} PROPERTIES + OUTPUT_NAME ui) + if(${_setver}) + set_target_properties(${_name} PROPERTIES + SOVERSION ${_VERSION}) + endif(${_setver}) + # omit libui-shared from default builds + if(${_exclude}) + set_target_properties(${_name} PROPERTIES + EXCLUDE_FROM_ALL 1) + endif(${_exclude}) + target_link_libraries(${_name} "-framework Foundation -framework AppKit") +endmacro(libui) -if(test) - file(GLOB TESTSOURCES test/*.c) - add_executable(tester ${TESTSOURCES}) - set_target_properties(tester PROPERTIES OUTPUT_NAME test) - target_link_libraries(tester libui) -endif(test) +libui(libui SHARED ${_SETVERSION} FALSE) +libui(libui-static STATIC FALSE TRUE) + +include_directories(test) +file(GLOB TESTSOURCES test/*.c) + +macro(test _name _libui) + add_executable(${_name} ${TESTSOURCES}) + set_target_properties(${_name} PROPERTIES + OUTPUT_NAME test + EXCLUDE_FROM_ALL 1) + target_link_libraries(${_name} ${_libui}) +endmacro(test) + +test(tester libui) +test(tester-static libui-static) From 308d0eca889e40c476d8e4fe121dee6e27b9f5de Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 30 May 2016 14:32:05 -0400 Subject: [PATCH 0191/1329] Even more cmake work. --- CMakeLists.txt | 80 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 64 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b1de814c..afb3d6d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,22 +2,35 @@ cmake_minimum_required(VERSION 2.8.12) project(libui) +set(_NOSHARED FALSE) +if(WIN32) + if(NOT MSVC) + set(_NOSHARED TRUE) + endif() +endif() +macro(nosharedmingw _target) + add_custom_target(${_target} + COMMAND exit 1 + COMMENT "Sorry, libui for Windows cannot be built as a DLL with MinGW. You will need to either build as a static library or build with MSVC.") +endmacro(nosharedmingw) + if(APPLE) set(_OSDIR darwin) set(_OSSRCEXT m) set(_SETVERSION TRUE) set(_VERSION "A") + set(_LIBUI_LDFLAGS "-framework Foundation -framework AppKit") # always use our rpath set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) # the / is required by some older versions of OS X set(CMAKE_INSTALL_RPATH "@executable_path/") set(CMAKE_MACOSX_RPATH TRUE) -elseif(WINDOWS) +elseif(WIN32) set(_OSDIR windows) set(_OSSRCEXT cpp) set(_SETVERSION FALSE) -else(APPLE) +else() set(_OSDIR unix) set(_OSSRCEXT c) set(_SETVERSION TRUE) @@ -25,9 +38,9 @@ else(APPLE) # always use our rpath set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) -endif(APPLE) +endif() -include_directories(. common ${_OSDIR} test) +include_directories(. common ${_OSDIR}) file(GLOB SOURCES common/*.c ${_OSDIR}/*.${_OSSRCEXT}) @@ -39,28 +52,63 @@ macro(libui _name _mode _setver _exclude) if(${_setver}) set_target_properties(${_name} PROPERTIES SOVERSION ${_VERSION}) - endif(${_setver}) - # omit libui-shared from default builds + endif() + # omit libui-static from default builds if(${_exclude}) set_target_properties(${_name} PROPERTIES EXCLUDE_FROM_ALL 1) - endif(${_exclude}) - target_link_libraries(${_name} "-framework Foundation -framework AppKit") -endmacro(libui) + endif() + set_target_properties(${_name} PROPERTIES + LINK_FLAGS "${_LIBUI_LDFLAGS}") +endmacro() libui(libui SHARED ${_SETVERSION} FALSE) -libui(libui-static STATIC FALSE TRUE) +if(${_NOSHARED}) + nosharedmingw(libui-static) +else() + libui(libui-static STATIC FALSE TRUE) +endif() include_directories(test) file(GLOB TESTSOURCES test/*.c) -macro(test _name _libui) - add_executable(${_name} ${TESTSOURCES}) +macro(executable_base _name _outname _libui _static) + add_executable(${_name} ${XSRC}) set_target_properties(${_name} PROPERTIES - OUTPUT_NAME test + OUTPUT_NAME ${_outname} EXCLUDE_FROM_ALL 1) target_link_libraries(${_name} ${_libui}) -endmacro(test) + # be sure to include libui libraries in the output + if(${_static}) + set_target_properties(${_name} PROPERTIES + LINK_FLAGS "${_LIBUI_LDFLAGS}") + endif() +endmacro() -test(tester libui) -test(tester-static libui-static) +macro(executable _name _outname _dir) + include_directories(${_dir}) + file(GLOB XSRC ${_dir}/*.c ${_dir}/*.cpp) + executable_base(${_name} ${_outname} libui FALSE) + if(${_NOSHARED}) + nosharedmingw(${_name}-static) + else() + executable_base(${_name}-static ${_outname} libui-static TRUE) + endif() + set(XSRC) +endmacro() + +executable(tester test test) +executable(controlgallery controlgallery examples/controlgallery) +executable(histogram histogram examples/histogram) +executable(cpp-multithread cpp-multithread examples/cpp-multithread) + +add_custom_target(examples + DEPENDS + controlgallery + histogram + cpp-multithread) +add_custom_target(examples-static + DEPENDS + controlgallery-static + histogram-static + cpp-multithread-static) From 3f3c6fc09b3f91d9043fca0d2ecada54f6bc8dd9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 30 May 2016 15:40:44 -0400 Subject: [PATCH 0192/1329] More cmake work. Enough for now; let's try another one to see if it's any better. --- CMakeLists.txt | 56 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index afb3d6d6..a5faf063 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,14 @@ if(APPLE) set(_OSSRCEXT m) set(_SETVERSION TRUE) set(_VERSION "A") - set(_LIBUI_LDFLAGS "-framework Foundation -framework AppKit") + + set(_COMMON_CFLAGS + "${_COMMON_CFLAGS} -mmacosx-version-min=10.8 -DMACOSX_DEPLOYMENT_TARGET=10.8") + set(_COMMON_LDFLAGS + "${_COMMON_LDFLAGS} -mmacosx-version-min=10.8") + + set(_LIBUI_LDFLAGS + "${_LIBUI_LDFLAGS} -lobjc -framework Foundation -framework AppKit") # always use our rpath set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) @@ -40,26 +47,58 @@ else() set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) endif() +if(MSVC) + # TODO +else() + # don't use C_VERSION or CXX_VERSION because they use GNU standards + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --std=c99") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11") + + set(_COMMON_CFLAGS + "${_COMMON_CFLAGS} -Wall -Wextra -pedantic") + set(_COMMON_CFLAGS + "${_COMMON_CFLAGS} -Wno-unused-parameter") + set(_COMMON_CFLAGS + "${_COMMON_CFLAGS} -Wno-switch") + set(_COMMON_LDFLAGS + "${_COMMON_LDFLAGS}") + if(NOT WIN32) + set(_PICFLAG "-fPIC") + endif() + + set(_LIBUI_CFLAGS + "${_LIBUI_CFLAGS} -D_UI_EXTERN='__attribute__((visibility(\"default\"))) extern' -fvisibility=hidden") + set(_LIBUI_LDFLAGS + "${_LIBUI_LDFLAGS} -fvisibility=hidden") +endif() +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${_COMMON_CFLAGS}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${_COMMON_CFLAGS}") +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${_COMMON_LDFLAGS}") +set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${_COMMON_LDFLAGS} ${_PICFLAG}") +# don't set ${CMAKE_STATIC_LINKER_FLAGS}; that's just ar + include_directories(. common ${_OSDIR}) file(GLOB SOURCES common/*.c ${_OSDIR}/*.${_OSSRCEXT}) -macro(libui _name _mode _setver _exclude) +macro(libui _name _mode) add_library(${_name} ${_mode} ${SOURCES}) set_target_properties(${_name} PROPERTIES OUTPUT_NAME ui) - if(${_setver}) + if("${_mode}" STREQUAL "SHARED") + # only put version number on shared build set_target_properties(${_name} PROPERTIES SOVERSION ${_VERSION}) - endif() - # omit libui-static from default builds - if(${_exclude}) + else() + # don't build libui-static by default set_target_properties(${_name} PROPERTIES EXCLUDE_FROM_ALL 1) endif() set_target_properties(${_name} PROPERTIES - LINK_FLAGS "${_LIBUI_LDFLAGS}") + COMPILE_FLAGS "${_LIBUI_CFLAGS}") + set_target_properties(${_name} PROPERTIES + LINK_FLAGS "${_PICFLAG} ${_LIBUI_LDFLAGS}") endmacro() libui(libui SHARED ${_SETVERSION} FALSE) @@ -82,6 +121,9 @@ macro(executable_base _name _outname _libui _static) if(${_static}) set_target_properties(${_name} PROPERTIES LINK_FLAGS "${_LIBUI_LDFLAGS}") + else() + set_target_properties(${_name} PROPERTIES + LINK_FLAGS "${_PICFLAG}") endif() endmacro() From cec5df44c1ea264b28a7873bc96c7e19d5e93bf0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 30 May 2016 18:51:34 -0400 Subject: [PATCH 0193/1329] Added wscript. --- wscript | 200 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100644 wscript diff --git a/wscript b/wscript new file mode 100644 index 00000000..e37ac141 --- /dev/null +++ b/wscript @@ -0,0 +1,200 @@ +# 30 may 2016 + +from waflib import Utils + +top = '.' +out = '_build' + +def _platform(ctx): + plat = Utils.unversioned_sys_platform() + if plat == 'win32': + return 'windows' + if plat == 'darwin': + return 'darwin' + return 'unix' + +def _platsrcsuffix(plat): + if plat == 'windows': + return 'cpp' + if plat == 'darwin': + return 'm' + return 'c' + +def _plathsuffix(plat): + if plat == 'windows': + return 'hpp' + return 'h' + +def _toolchain(ctx): + return ctx.env['CC_NAME'] + +def options(ctx): + ctx.add_option('--toolchain', action='store', default=None, help='Toolchain to use, in waf terms; otheriwse compiler_c will determine') + ctx.load('compiler_c') + ctx.load('compiler_cxx') + +def configure(ctx): + if ctx.options.toolchain is not None: + conf.options.check_c_compiler = [ctx.options.toolchain] + conf.options.check_cxx_compiler = [ctx.options.toolchain] + ctx.load('compiler_c') + ctx.load('compiler_cxx') + + ctx.setenv('shared', env = ctx.env.derive()) + ctx.write_config_header('ignore/shared.h', remove = False) + ctx.setenv('static', env = ctx.env.derive()) + ctx.write_config_header('ignore/static.h', remove = False) + +def _common_flags(platform, toolchain, buildmode, gcclang, msvclang): + if toolchain == "msvc": + return list([ + msvclang, + ]) + l = list([ + "-Wall", "-Wextra", "-pedantic", + "-Wno-unused-parameter", + "-Wno-switch", + gcclang, + ]) + if buildmode == "shared" and platform != "windows": + l += ["-fPIC"] + if platform == "darwin": + l += [ + "-mmacosx-version-min=10.8", + "-DMACOSX_DEPLOYMENT_TARGET=10.8", + ] + return l + +def _common_cflags(platform, toolchain, buildmode): + return _common_flags(platform, toolchain, buildmode, + '--std=c99', '-TC') + +def _common_cxxflags(platform, toolchain, buildmode): + return _common_flags(platform, toolchain, buildmode, + '--std=c99', '-TP') + +def _common_ldflags(platform, toolchain, buildmode): + if toolchain == "msvc": + return list([]) + l = list([]) + if buildmode == "shared" and platform != "windows": + l += ["-fPIC"] + if platform == "darwin": + l += [ + "-mmacosx-version-min=10.8", + ] + return l + +def _libui_cflags(toolchain): + if toolchain == "msvc": + return list([ + ]) + return list([ + """-D_UI_EXTERN=__attribute__((visibility("default"))) extern""", + "-fvisibility=hidden", + ]) + +def _libui_ldflags(toolchain): + if toolchain == "msvc": # no additional options + return list([]) + return list([ + "-fvisibility=hidden", + ]) + +def _platform_libs(platform, toolchain): + if toolchain == "msvc": + return list([]) + if platform == "darwin": + return list([ + "-lobjc", + "-framework", "Foundation", + "-framework", "AppKit", + ]) + if platform == "windows": + return list([]) + return list([]) + +def _libui_sharedldflags(platform, toolchain): + if toolchain == "msvc": + return list([]) + if platform == "darwin": + return list([ + "-install_name", "@rpath/", + ]) + if platform == "windows": + return list([]) + return list([]) + +def _exe_sharedldflags(platform, toolchain): + if toolchain == "msvc": + return list([]) + if platform == "darwin": + return list([ + # the / is needed by older OS X versions + "-rpath", "@executable_path/", + ]) + if platform == "windows": + return list([]) + return list([]) + +def _libui(ctx, platform, toolchain, buildmode): + headers = ctx.path.ant_glob( + "ui.h ui_%s.h %s/*.%s" % + (platform, platform, _plathsuffix(platform))) + sources = ctx.path.ant_glob( + "common/*.c %s/*.%s" % + (platform, _platsrcsuffix(platform))) + cflags = _common_cflags(platform, toolchain, buildmode) + cxxflags = _common_cxxflags(platform, toolchain, buildmode) + ldflags = _common_ldflags(platform, toolchain, buildmode) + cflags += _libui_cflags(toolchain) + cxxflags += _libui_cflags(toolchain) + ldflags += _libui_ldflags(toolchain) + ldflags += _platform_libs(platform, toolchain) + ldflags += _libui_sharedldflags(platform, toolchain) + target = "ui" + if platform == "windows": + target = "libui" + func = ctx.shlib + if buildmode == "static": + func = ctx.stlib + func( + target = target, + source = sources, + includes = headers, + cflags = cflags, + cxxflags = cxxflags, + ldflags = ldflags, + ) + +def build(ctx): + platform = _platform(ctx) + toolchain = _toolchain(ctx) + buildmode = ctx.variant + if not buildmode: + ctx.fatal("Use either 'waf build_shared` or `waf build_static`") + + if platform == "windows" and toolchain != "msvc" and buildmode == "shared": + ctx.fatal("Sorry, building a DLL on Windows with MinGW is not yet supported. Either build a static library or use MSVC.") + + _libui(ctx, platform, toolchain, buildmode) + +# from https://github.com/waf-project/waf/blob/master/demos/variants/wscript +def init(ctx): + from waflib.Build import BuildContext, CleanContext, InstallContext, UninstallContext + for x in 'shared static'.split(): + for y in (BuildContext, CleanContext, InstallContext, UninstallContext): + name = y.__name__.replace('Context','').lower() + class tmp(y): + cmd = name + '_' + x + variant = x + def buildall(ctx): + import waflib.Options + for x in ('build_shared', 'build_static'): + waflib.Options.commands.insert(0, x) + +# via https://github.com/waf-project/waf/blob/master/demos/mac_app/wscript +from waflib import TaskGen +@TaskGen.extension('.m') +def m_hook(self, node): + return self.create_compiled_task('c', node) From 517661f935e35f7862c7dcec0f68e1a748230958 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 30 May 2016 21:32:38 -0400 Subject: [PATCH 0194/1329] More cmake work. --- CMakeLists.txt | 90 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 65 insertions(+), 25 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a5faf063..59a0ea9a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,6 @@ # 30 may 2016 cmake_minimum_required(VERSION 2.8.12) +set(CMAKE_OSX_DEPLOYMENT_TARGET, "10.8") project(libui) set(_NOSHARED FALSE) @@ -20,13 +21,10 @@ if(APPLE) set(_SETVERSION TRUE) set(_VERSION "A") - set(_COMMON_CFLAGS - "${_COMMON_CFLAGS} -mmacosx-version-min=10.8 -DMACOSX_DEPLOYMENT_TARGET=10.8") - set(_COMMON_LDFLAGS - "${_COMMON_LDFLAGS} -mmacosx-version-min=10.8") + # TODO -DMACOSX_DEPLOYMENT_TARGET=10.8") ? - set(_LIBUI_LDFLAGS - "${_LIBUI_LDFLAGS} -lobjc -framework Foundation -framework AppKit") + set(_PLATFORM_LIBS + "-lobjc -framework Foundation -framework AppKit") # always use our rpath set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) @@ -37,12 +35,28 @@ elseif(WIN32) set(_OSDIR windows) set(_OSSRCEXT cpp) set(_SETVERSION FALSE) + + # note that usp10 comes before gdi32 + # TODO prune this list + set(_PLATFORM_LIBS_BASE + user32 kernel32 usp10 gdi32 comctl32 uxtheme msimg32 comdlg32 d2d1 dwrite ole32 oleaut32 oleacc uuid) + if(MSVC) + string(REPLACE ";" ".lib " _PLATFORM_LIBS "${_PLATFORM_LIBS_BASE}") + set(_PLATFORM_LIBS "${_PLATFORM_LIBS}.lib") + else() + string(REPLACE ";" " -l" _PLATFORM_LIBS "${_PLATFORM_LIBS_BASE}") + set(_PLATFORM_LIBS "-l${_PLATFORM_LIBS}") + endif() else() set(_OSDIR unix) set(_OSSRCEXT c) set(_SETVERSION TRUE) set(_VERSION "0") + pkg_check_modules(GTK REQUIRED gtk+-3.0) + set(_PLATFORM_CFLAGS "${GTK_CFLAGS}") + set(_PLATFORM_LIBS "${GTK_LDFLAGS} -lm -ldl") + # always use our rpath set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) endif() @@ -50,38 +64,64 @@ endif() if(MSVC) # TODO else() + set(_COMMON_FLAGS_BASE + "-Wall -Wextra -pedantic -Wno-unused-parameter -Wno-switch") # don't use C_VERSION or CXX_VERSION because they use GNU standards - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --std=c99") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11") + set(_COMMON_CFLAGS "${_COMMON_FLAGS_BASE} --std=c99") + set(_COMMON_CXX_FLAGS "${_COMMON_FLAGS_BASE} --std=c++11") - set(_COMMON_CFLAGS - "${_COMMON_CFLAGS} -Wall -Wextra -pedantic") - set(_COMMON_CFLAGS - "${_COMMON_CFLAGS} -Wno-unused-parameter") - set(_COMMON_CFLAGS - "${_COMMON_CFLAGS} -Wno-switch") - set(_COMMON_LDFLAGS - "${_COMMON_LDFLAGS}") if(NOT WIN32) set(_PICFLAG "-fPIC") endif() set(_LIBUI_CFLAGS - "${_LIBUI_CFLAGS} -D_UI_EXTERN='__attribute__((visibility(\"default\"))) extern' -fvisibility=hidden") + "-D_UI_EXTERN='__attribute__((visibility(\"default\"))) extern' -fvisibility=hidden ${_PLATFORM_CFLAGS}") set(_LIBUI_LDFLAGS - "${_LIBUI_LDFLAGS} -fvisibility=hidden") + "-fvisibility=hidden") endif() -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${_COMMON_CFLAGS}") -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${_COMMON_CFLAGS}") -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${_COMMON_LDFLAGS}") -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${_COMMON_LDFLAGS} ${_PICFLAG}") -# don't set ${CMAKE_STATIC_LINKER_FLAGS}; that's just ar -include_directories(. common ${_OSDIR}) -file(GLOB SOURCES +set(_COMMON_C_FLAGS + "${CMAKE_C_FLAGS} ${_COMMON_C_FLAGS}") +set(_COMMON_CXX_FLAGS + "${CMAKE_CXX_FLAGS} ${_COMMON_C_FLAGS}") + +set(_LIBUI_SHARED_CFLAGS + "${_COMMON_CFLAGS} ${_LIBUI_CFLAGS} ${_PICFLAG}") +set(_LIBUI_STATIC_CFLAGS + "${_COMMON_CFLAGS} ${_LIBUI_CFLAGS}") +set(_LIBUI_SHARED_CXXFLAGS + "${_COMMON_CXXFLAGS} ${_LIBUI_CFLAGS} ${_PICFLAG}") +set(_LIBUI_STATIC_CXXFLAGS + "${_COMMON_CXXFLAGS} ${_LIBUI_CFLAGS}") +set(_LIBUI_SHARED_LDFLAGS + "${CMAKE_SHARED_LINKER_FLAGS} ${_COMMON_LDFLAGS} ${_LIBUI_LDFLAGS} ${_PLATFORM_LDFLAGS} ${_PICFLAG}") +set(_LIBUI_STATIC_LDFLAGS + "${_COMMON_LDFLAGS} ${_LIBUI_LDFLAGS}") + +set(_EXE_SHARED_CFLAGS + "${_COMMON_CFLAGS} ${_PICFLAG}") +set(_EXE_STATIC_CFLAGS + "${_COMMON_CFLAGS}") +set(_EXE_SHARED_CXXFLAGS + "${_COMMON_CXXFLAGS} ${_PICFLAG}") +set(_EXE_STATIC_CXXFLAGS + "${_COMMON_CXXFLAGS}") +set(_EXE_SHARED_LDFLAGS + "${CMAKE_SHARED_LINKER_FLAGS} ${_COMMON_LDFLAGS} ${_PICFLAG}") +set(_EXE_STATIC_LDFLAGS + "${_COMMON_LDFLAGS} ${_PLATFORM_LDFLAGS}") + +set(_LIBUI_HEADERS . common ${_OSDIR}) +file(GLOB _LIBUI_SOURCES common/*.c ${_OSDIR}/*.${_OSSRCEXT}) +macro(_begin_shared _cflags _cxxflags _ldflags) + set(CMAKE_C_FLAGS "${_cflags}") + set(CMAKE_CXX_FLAGS "${_cxxflags}") + set(CMAKE_SHARED_LINKER_FLAGS "${_ldflags}") +endmacro() + macro(libui _name _mode) add_library(${_name} ${_mode} ${SOURCES}) set_target_properties(${_name} PROPERTIES From aa28904408fb75ae8042c616982c13cbe2a5a784 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 30 May 2016 22:26:08 -0400 Subject: [PATCH 0195/1329] Removed the -DMACOSX_DEPLOYMENT_TARGET; this is an env var, not a macro. Thanks to Psy in irc.freenode.net/#macdev. --- CMakeLists.txt | 2 -- darwin/GNUfiles.mk | 6 ++---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 59a0ea9a..412d9973 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,8 +21,6 @@ if(APPLE) set(_SETVERSION TRUE) set(_VERSION "A") - # TODO -DMACOSX_DEPLOYMENT_TARGET=10.8") ? - set(_PLATFORM_LIBS "-lobjc -framework Foundation -framework AppKit") diff --git a/darwin/GNUfiles.mk b/darwin/GNUfiles.mk index 9b9ed693..12615f72 100644 --- a/darwin/GNUfiles.mk +++ b/darwin/GNUfiles.mk @@ -46,11 +46,9 @@ LDFLAGS += $(NATIVE_UI_LDFLAGS) # flags for OS X versioning CFLAGS += \ - -mmacosx-version-min=10.8 \ - -DMACOSX_DEPLOYMENT_TARGET=10.8 + -mmacosx-version-min=10.8 CXXFLAGS += \ - -mmacosx-version-min=10.8 \ - -DMACOSX_DEPLOYMENT_TARGET=10.8 + -mmacosx-version-min=10.8 LDFLAGS += \ -mmacosx-version-min=10.8 From fcec2b87e541847d2890e4ad7fcc9921c7ca84ce Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 31 May 2016 09:12:45 -0400 Subject: [PATCH 0196/1329] Started refactoring the cmake file again. --- CMakeLists.txt | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 412d9973..d80a22f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,26 @@ # 30 may 2016 cmake_minimum_required(VERSION 2.8.12) -set(CMAKE_OSX_DEPLOYMENT_TARGET, "10.8") + +# set up our configurations +set(CMAKE_CONFIGURATION_TYPES Debug Static Release ReleaseStatic) +# we load the variables after calling project() + +# and we need to set this up prior to project() too +set(CMAKE_OSX_DEPLOYMENT_TARGET "10.8") + project(libui) +# now that we called project(), load our config variables +macro(cfgcopy _prefix) + set(${_prefix}_STATIC "${${_prefix}_DEBUG}") + set(${_prefix}_RELEASESTATIC "${${_prefix}_RELEASE}") +endmacro() +cfgcopy(CMAKE_C_FLAGS) +cfgcopy(CMAKE_CXX_FLAGS) +cfgcopy(CMAKE_SHARED_LINKER_FLAGS) +cfgcopy(CMAKE_STATIC_LINKER_FLAGS) +cfgcopy(CMAKE_EXE_LINKER_FLAGS) + set(_NOSHARED FALSE) if(WIN32) if(NOT MSVC) @@ -15,6 +33,10 @@ macro(nosharedmingw _target) COMMENT "Sorry, libui for Windows cannot be built as a DLL with MinGW. You will need to either build as a static library or build with MSVC.") endmacro(nosharedmingw) +macro(append _var _val) + set(${_var} "${${_var}} ${_val}") +endmacro() + if(APPLE) set(_OSDIR darwin) set(_OSSRCEXT m) @@ -65,11 +87,18 @@ else() set(_COMMON_FLAGS_BASE "-Wall -Wextra -pedantic -Wno-unused-parameter -Wno-switch") # don't use C_VERSION or CXX_VERSION because they use GNU standards - set(_COMMON_CFLAGS "${_COMMON_FLAGS_BASE} --std=c99") - set(_COMMON_CXX_FLAGS "${_COMMON_FLAGS_BASE} --std=c++11") + append(CMAKE_C_FLAGS " --std=c99") + append(CMAKE_CXX_FLAGS " --std=c++11") if(NOT WIN32) - set(_PICFLAG "-fPIC") + append(CMAKE_C_FLAGS_DEBUG " -fPIC") + append(CMAKE_CXX_FLAGS_DEBUG " -fPIC") + append(CMAKE_SHARED_LINKER_FLAGS_DEBUG " -fPIC") + append(CMAKE_EXE_LINKER_FLAGS_DEBUG " -fPIC") + append(CMAKE_C_FLAGS_RELEASE " -fPIC") + append(CMAKE_CXX_FLAGS_RELEASE " -fPIC") + append(CMAKE_SHARED_LINKER_FLAGS_RELEASE " -fPIC") + append(CMAKE_EXE_LINKER_FLAGS_RELEASE " -fPIC") endif() set(_LIBUI_CFLAGS From ec730962aa903fff7cd71bfe95543321527ca28e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 31 May 2016 11:10:35 -0400 Subject: [PATCH 0197/1329] More work with cmake. --- CMakeLists.txt | 141 +++++++----------------------------------- common/CMakeLists.txt | 16 +++++ rest_CMake | 85 +++++++++++++++++++++++++ 3 files changed, 122 insertions(+), 120 deletions(-) create mode 100644 common/CMakeLists.txt create mode 100644 rest_CMake diff --git a/CMakeLists.txt b/CMakeLists.txt index d80a22f6..b7a8f918 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,10 @@ cmake_minimum_required(VERSION 2.8.12) # set up our configurations set(CMAKE_CONFIGURATION_TYPES Debug Static Release ReleaseStatic) # we load the variables after calling project() +# default to Debug if no configuration specified +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Debug) +endif() # and we need to set this up prior to project() too set(CMAKE_OSX_DEPLOYMENT_TARGET "10.8") @@ -36,6 +40,10 @@ endmacro(nosharedmingw) macro(append _var _val) set(${_var} "${${_var}} ${_val}") endmacro() +macro(append2 _var1 _var2 _val) + append(${_var1} "${_val}") + append(${_var2} "${_val}") +endmacro() if(APPLE) set(_OSDIR darwin) @@ -74,7 +82,7 @@ else() set(_VERSION "0") pkg_check_modules(GTK REQUIRED gtk+-3.0) - set(_PLATFORM_CFLAGS "${GTK_CFLAGS}") + set(_LIBUI_CFLAGS "${GTK_CFLAGS}") set(_PLATFORM_LIBS "${GTK_LDFLAGS} -lm -ldl") # always use our rpath @@ -84,8 +92,8 @@ endif() if(MSVC) # TODO else() - set(_COMMON_FLAGS_BASE - "-Wall -Wextra -pedantic -Wno-unused-parameter -Wno-switch") + append2(CMAKE_C_FLAGS CMAKE_CXX_FLAGS + " -Wall -Wextra -pedantic -Wno-unused-parameter -Wno-switch") # don't use C_VERSION or CXX_VERSION because they use GNU standards append(CMAKE_C_FLAGS " --std=c99") append(CMAKE_CXX_FLAGS " --std=c++11") @@ -101,123 +109,16 @@ else() append(CMAKE_EXE_LINKER_FLAGS_RELEASE " -fPIC") endif() - set(_LIBUI_CFLAGS - "-D_UI_EXTERN='__attribute__((visibility(\"default\"))) extern' -fvisibility=hidden ${_PLATFORM_CFLAGS}") - set(_LIBUI_LDFLAGS - "-fvisibility=hidden") + append(_LIBUI_CFLAGS + " -D_UI_EXTERN='__attribute__((visibility(\"default\"))) extern' -fvisibility=hidden ${_PLATFORM_CFLAGS}") + + append(CMAKE_SHARED_LINKER_FLAGS " -fvisibility=hidden") + # don't amend CMAKE_STATIC_LINKER_FLAGS; that's for ar endif() -set(_COMMON_C_FLAGS - "${CMAKE_C_FLAGS} ${_COMMON_C_FLAGS}") -set(_COMMON_CXX_FLAGS - "${CMAKE_CXX_FLAGS} ${_COMMON_C_FLAGS}") +# and add the platform libraries to the three places that need it: shared library links and the two static executable links +append(CMAKE_SHARED_LINKER_FLAGS " ${_PLATFORM_LIBS}") +append2(CMAKE_EXE_LINKER_FLAGS_STATIC CMAKE_EXE_LINKER_FLAGS_RUNTIMESTATIC + " ${_PLATFORM_LIBS}") -set(_LIBUI_SHARED_CFLAGS - "${_COMMON_CFLAGS} ${_LIBUI_CFLAGS} ${_PICFLAG}") -set(_LIBUI_STATIC_CFLAGS - "${_COMMON_CFLAGS} ${_LIBUI_CFLAGS}") -set(_LIBUI_SHARED_CXXFLAGS - "${_COMMON_CXXFLAGS} ${_LIBUI_CFLAGS} ${_PICFLAG}") -set(_LIBUI_STATIC_CXXFLAGS - "${_COMMON_CXXFLAGS} ${_LIBUI_CFLAGS}") -set(_LIBUI_SHARED_LDFLAGS - "${CMAKE_SHARED_LINKER_FLAGS} ${_COMMON_LDFLAGS} ${_LIBUI_LDFLAGS} ${_PLATFORM_LDFLAGS} ${_PICFLAG}") -set(_LIBUI_STATIC_LDFLAGS - "${_COMMON_LDFLAGS} ${_LIBUI_LDFLAGS}") - -set(_EXE_SHARED_CFLAGS - "${_COMMON_CFLAGS} ${_PICFLAG}") -set(_EXE_STATIC_CFLAGS - "${_COMMON_CFLAGS}") -set(_EXE_SHARED_CXXFLAGS - "${_COMMON_CXXFLAGS} ${_PICFLAG}") -set(_EXE_STATIC_CXXFLAGS - "${_COMMON_CXXFLAGS}") -set(_EXE_SHARED_LDFLAGS - "${CMAKE_SHARED_LINKER_FLAGS} ${_COMMON_LDFLAGS} ${_PICFLAG}") -set(_EXE_STATIC_LDFLAGS - "${_COMMON_LDFLAGS} ${_PLATFORM_LDFLAGS}") - -set(_LIBUI_HEADERS . common ${_OSDIR}) -file(GLOB _LIBUI_SOURCES - common/*.c - ${_OSDIR}/*.${_OSSRCEXT}) - -macro(_begin_shared _cflags _cxxflags _ldflags) - set(CMAKE_C_FLAGS "${_cflags}") - set(CMAKE_CXX_FLAGS "${_cxxflags}") - set(CMAKE_SHARED_LINKER_FLAGS "${_ldflags}") -endmacro() - -macro(libui _name _mode) - add_library(${_name} ${_mode} ${SOURCES}) - set_target_properties(${_name} PROPERTIES - OUTPUT_NAME ui) - if("${_mode}" STREQUAL "SHARED") - # only put version number on shared build - set_target_properties(${_name} PROPERTIES - SOVERSION ${_VERSION}) - else() - # don't build libui-static by default - set_target_properties(${_name} PROPERTIES - EXCLUDE_FROM_ALL 1) - endif() - set_target_properties(${_name} PROPERTIES - COMPILE_FLAGS "${_LIBUI_CFLAGS}") - set_target_properties(${_name} PROPERTIES - LINK_FLAGS "${_PICFLAG} ${_LIBUI_LDFLAGS}") -endmacro() - -libui(libui SHARED ${_SETVERSION} FALSE) -if(${_NOSHARED}) - nosharedmingw(libui-static) -else() - libui(libui-static STATIC FALSE TRUE) -endif() - -include_directories(test) -file(GLOB TESTSOURCES test/*.c) - -macro(executable_base _name _outname _libui _static) - add_executable(${_name} ${XSRC}) - set_target_properties(${_name} PROPERTIES - OUTPUT_NAME ${_outname} - EXCLUDE_FROM_ALL 1) - target_link_libraries(${_name} ${_libui}) - # be sure to include libui libraries in the output - if(${_static}) - set_target_properties(${_name} PROPERTIES - LINK_FLAGS "${_LIBUI_LDFLAGS}") - else() - set_target_properties(${_name} PROPERTIES - LINK_FLAGS "${_PICFLAG}") - endif() -endmacro() - -macro(executable _name _outname _dir) - include_directories(${_dir}) - file(GLOB XSRC ${_dir}/*.c ${_dir}/*.cpp) - executable_base(${_name} ${_outname} libui FALSE) - if(${_NOSHARED}) - nosharedmingw(${_name}-static) - else() - executable_base(${_name}-static ${_outname} libui-static TRUE) - endif() - set(XSRC) -endmacro() - -executable(tester test test) -executable(controlgallery controlgallery examples/controlgallery) -executable(histogram histogram examples/histogram) -executable(cpp-multithread cpp-multithread examples/cpp-multithread) - -add_custom_target(examples - DEPENDS - controlgallery - histogram - cpp-multithread) -add_custom_target(examples-static - DEPENDS - controlgallery-static - histogram-static - cpp-multithread-static) +add_subdirectory("common") diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt new file mode 100644 index 00000000..ab960223 --- /dev/null +++ b/common/CMakeLists.txt @@ -0,0 +1,16 @@ +# 31 may 2016 + +include_directories(.. .) + +add_library(libui-common OBJECT + areaevents.c + control.c + debug.c + matrix.c + shouldquit.c + userbugs.c +) +set_target_properties(libui-common PROPERTIES + COMPILE_FLAGS "${_LIBUI_CFLAGS}" + LINK_FLAGS "${_LIBUI_LDFLAGS}" +) diff --git a/rest_CMake b/rest_CMake new file mode 100644 index 00000000..694b0fcd --- /dev/null +++ b/rest_CMake @@ -0,0 +1,85 @@ + +set(_LIBUI_HEADERS . common ${_OSDIR}) +file(GLOB _LIBUI_SOURCES + common/*.c + ${_OSDIR}/*.${_OSSRCEXT}) + +macro(_begin_shared _cflags _cxxflags _ldflags) + set(CMAKE_C_FLAGS "${_cflags}") + set(CMAKE_CXX_FLAGS "${_cxxflags}") + set(CMAKE_SHARED_LINKER_FLAGS "${_ldflags}") +endmacro() + +macro(libui _name _mode) + add_library(${_name} ${_mode} ${SOURCES}) + set_target_properties(${_name} PROPERTIES + OUTPUT_NAME ui) + if("${_mode}" STREQUAL "SHARED") + # only put version number on shared build + set_target_properties(${_name} PROPERTIES + SOVERSION ${_VERSION}) + else() + # don't build libui-static by default + set_target_properties(${_name} PROPERTIES + EXCLUDE_FROM_ALL 1) + endif() + set_target_properties(${_name} PROPERTIES + COMPILE_FLAGS "${_LIBUI_CFLAGS}") + set_target_properties(${_name} PROPERTIES + LINK_FLAGS "${_PICFLAG} ${_LIBUI_LDFLAGS}") +endmacro() + +libui(libui SHARED ${_SETVERSION} FALSE) +if(${_NOSHARED}) + nosharedmingw(libui-static) +else() + libui(libui-static STATIC FALSE TRUE) +endif() + +include_directories(test) +file(GLOB TESTSOURCES test/*.c) + +macro(executable_base _name _outname _libui _static) + add_executable(${_name} ${XSRC}) + set_target_properties(${_name} PROPERTIES + OUTPUT_NAME ${_outname} + EXCLUDE_FROM_ALL 1) + target_link_libraries(${_name} ${_libui}) + # be sure to include libui libraries in the output + if(${_static}) + set_target_properties(${_name} PROPERTIES + LINK_FLAGS "${_LIBUI_LDFLAGS}") + else() + set_target_properties(${_name} PROPERTIES + LINK_FLAGS "${_PICFLAG}") + endif() +endmacro() + +macro(executable _name _outname _dir) + include_directories(${_dir}) + file(GLOB XSRC ${_dir}/*.c ${_dir}/*.cpp) + executable_base(${_name} ${_outname} libui FALSE) + if(${_NOSHARED}) + nosharedmingw(${_name}-static) + else() + executable_base(${_name}-static ${_outname} libui-static TRUE) + endif() + set(XSRC) +endmacro() + +executable(tester test test) +executable(controlgallery controlgallery examples/controlgallery) +executable(histogram histogram examples/histogram) +executable(cpp-multithread cpp-multithread examples/cpp-multithread) + +add_custom_target(examples + DEPENDS + controlgallery + histogram + cpp-multithread) +add_custom_target(examples-static + DEPENDS + controlgallery-static + histogram-static + cpp-multithread-static) + From 0fff9c6fd6c2c4cb3ebc55c868f27ad39165f18e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 1 Jun 2016 12:48:26 -0400 Subject: [PATCH 0198/1329] More CMake work. Thanks to Mr-Hide in irc.freenode.net/#cmake. --- CMakeLists.txt | 48 ++++++++++++++++++++++++--------- common/CMakeLists.txt | 1 - darwin/CMakeLists.txt | 62 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 97 insertions(+), 14 deletions(-) create mode 100644 darwin/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index b7a8f918..6c6c3bb5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,8 +6,24 @@ set(CMAKE_CONFIGURATION_TYPES Debug Static Release ReleaseStatic) # we load the variables after calling project() # default to Debug if no configuration specified if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE Debug) + # the CACHE FORCE is necessary for this to work properly + set(CMAKE_BUILD_TYPE Debug CACHE STRING "Build type; one of: Debug Release Static ReleaseStatic" FORCE) endif() +# and save whether this is shared in a variable +if(${CMAKE_BUILD_TYPE} STREQUAL "Debug") + set(_SHARED TRUE) +elseif(${CMAKE_BUILD_TYPE} STREQUAL "Release") + set(_SHARED TRUE) +endif() +if(WIN32) + if(NOT MSVC) + if(_SHARED) + message(FATAL_ERROR + "Sorry, libui for Windows cannot be built as a DLL with MinGW. You will need to either build as a static library or build with MSVC.") + endif() + endif() +endif() + # and we need to set this up prior to project() too set(CMAKE_OSX_DEPLOYMENT_TARGET "10.8") @@ -25,18 +41,6 @@ cfgcopy(CMAKE_SHARED_LINKER_FLAGS) cfgcopy(CMAKE_STATIC_LINKER_FLAGS) cfgcopy(CMAKE_EXE_LINKER_FLAGS) -set(_NOSHARED FALSE) -if(WIN32) - if(NOT MSVC) - set(_NOSHARED TRUE) - endif() -endif() -macro(nosharedmingw _target) - add_custom_target(${_target} - COMMAND exit 1 - COMMENT "Sorry, libui for Windows cannot be built as a DLL with MinGW. You will need to either build as a static library or build with MSVC.") -endmacro(nosharedmingw) - macro(append _var _val) set(${_var} "${${_var}} ${_val}") endmacro() @@ -122,3 +126,21 @@ append2(CMAKE_EXE_LINKER_FLAGS_STATIC CMAKE_EXE_LINKER_FLAGS_RUNTIMESTATIC " ${_PLATFORM_LIBS}") add_subdirectory("common") +add_subdirectory("${_OSDIR}") +if(_SHARED) + add_library(libui SHARED + $ + $ + ) + if(_SETVERSION) + set_target_properties(libui PROPERTIES + SOVERSION "${_VERSION}") + endif() +else() + _add_static(libui + $ + $ + ) +endif() +set_target_properties(libui PROPERTIES + OUTPUT_NAME ui) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index ab960223..cc93822d 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -12,5 +12,4 @@ add_library(libui-common OBJECT ) set_target_properties(libui-common PROPERTIES COMPILE_FLAGS "${_LIBUI_CFLAGS}" - LINK_FLAGS "${_LIBUI_LDFLAGS}" ) diff --git a/darwin/CMakeLists.txt b/darwin/CMakeLists.txt new file mode 100644 index 00000000..651a175e --- /dev/null +++ b/darwin/CMakeLists.txt @@ -0,0 +1,62 @@ +# 31 may 2016 + +include_directories(.. . ../common) + +add_library(libui-darwin OBJECT + alloc.m + area.m + areaevents.m + autolayout.m + box.m + button.m + checkbox.m + colorbutton.m + combobox.m + control.m + datetimepicker.m + debug.m + draw.m + drawtext.m + editablecombo.m + entry.m + fontbutton.m + group.m + label.m + main.m + map.m + menu.m + multilineentry.m + progressbar.m + radiobuttons.m + scrollview.m + separator.m + slider.m + spinbox.m + stddialogs.m + tab.m + text.m + util.m + window.m +) +set_target_properties(libui-darwin PROPERTIES + COMPILE_FLAGS "${_LIBUI_CFLAGS}" +) + +macro(_add_static _name) + add_library(${_name} STATIC "${ARGN}") + file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/sharedhidden) + add_custom_command( + TARGET ${_name} POST_BUILD + COMMAND + ${CMAKE_AR} x $ + COMMAND + nm -m *.o | sed -E -n "'s/^[0-9a-f]* \\([A-Z_]+,[a-z_]+\\) external //p'" > ${_name}.lst + COMMAND + ld -exported_symbols_list ${_name}.lst -r *.o -o ../_combined_${_name}.o + COMMAND + rm $ + COMMAND + ${CMAKE_AR} rcs $ ../_combined_${_name}.o + WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/sharedhidden + COMMENT "Removing hidden symbols") +endmacro() From 08c06f24c031550e36171fd7bad07fce5e7dd063 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 1 Jun 2016 14:07:43 -0400 Subject: [PATCH 0199/1329] Added the test program to the CMake setup. --- CMakeLists.txt | 15 ++++++++++++--- test/CMakeLists.txt | 26 ++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 3 deletions(-) create mode 100644 test/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 6c6c3bb5..adb78166 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,7 +51,6 @@ endmacro() if(APPLE) set(_OSDIR darwin) - set(_OSSRCEXT m) set(_SETVERSION TRUE) set(_VERSION "A") @@ -65,7 +64,6 @@ if(APPLE) set(CMAKE_MACOSX_RPATH TRUE) elseif(WIN32) set(_OSDIR windows) - set(_OSSRCEXT cpp) set(_SETVERSION FALSE) # note that usp10 comes before gdi32 @@ -81,7 +79,6 @@ elseif(WIN32) endif() else() set(_OSDIR unix) - set(_OSSRCEXT c) set(_SETVERSION TRUE) set(_VERSION "0") @@ -144,3 +141,15 @@ else() endif() set_target_properties(libui PROPERTIES OUTPUT_NAME ui) + +macro(_add_exec _name) + add_executable(${_name} + WIN32 EXCLUDE_FROM_ALL + ${ARGN}) + target_link_libraries(${_name} libui) +endmacro() + +add_subdirectory("test") +set_target_properties(tester PROPERTIES + OUTPUT_NAME test + WIN32_EXECUTABLE FALSE) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 00000000..6c2b2daf --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,26 @@ +# 1 june 2016 + +include_directories(.. .) + +_add_exec(tester + drawtests.c + main.c + menus.c + page1.c + page10.c + page11.c + page12.c + page13.c + page2.c + page3.c + page4.c + page5.c + page6.c + page7.c + page7a.c + page7b.c + page7c.c + page8.c + page9.c + spaced.c +) From 81c555ea3b4f15243bc06f5147679350013d21c0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 1 Jun 2016 14:42:24 -0400 Subject: [PATCH 0200/1329] Added the examples CMakeLists.txt. --- CMakeLists.txt | 2 ++ examples/CMakeLists.txt | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 examples/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index adb78166..4162b8e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -153,3 +153,5 @@ add_subdirectory("test") set_target_properties(tester PROPERTIES OUTPUT_NAME test WIN32_EXECUTABLE FALSE) + +add_subdirectory("examples") diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 00000000..64f35869 --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,19 @@ +# 1 june 2016 + +include_directories(..) + +_add_exec(controlgallery + controlgallery/main.c +) +_add_exec(histogram + histogram/main.c +) +_add_exec(cpp-multithread + cpp-multithread/main.cpp +) + +add_custom_target(examples + DEPENDS + controlgallery + histogram + cpp-multithread) From 7bdeefedeb0a11fbbc41b8bd20f4c372143aa859 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 1 Jun 2016 14:50:35 -0400 Subject: [PATCH 0201/1329] More cmake setup. Now we can start the Unix tests, which we should have ready to go as-is... --- CMakeLists.txt | 3 + rest_CMake | 85 --------------------- wscript | 200 ------------------------------------------------- 3 files changed, 3 insertions(+), 285 deletions(-) delete mode 100644 rest_CMake delete mode 100644 wscript diff --git a/CMakeLists.txt b/CMakeLists.txt index 4162b8e3..7ee46176 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,9 @@ set(CMAKE_OSX_DEPLOYMENT_TARGET "10.8") project(libui) +set(EXECUTABLE_OUTPUT_PATH "${PROJECT_BINARY_DIR}/out") +set(LIBRARY_OUTPUT_PATH "${PROJECT_BINARY_DIR}/out") + # now that we called project(), load our config variables macro(cfgcopy _prefix) set(${_prefix}_STATIC "${${_prefix}_DEBUG}") diff --git a/rest_CMake b/rest_CMake deleted file mode 100644 index 694b0fcd..00000000 --- a/rest_CMake +++ /dev/null @@ -1,85 +0,0 @@ - -set(_LIBUI_HEADERS . common ${_OSDIR}) -file(GLOB _LIBUI_SOURCES - common/*.c - ${_OSDIR}/*.${_OSSRCEXT}) - -macro(_begin_shared _cflags _cxxflags _ldflags) - set(CMAKE_C_FLAGS "${_cflags}") - set(CMAKE_CXX_FLAGS "${_cxxflags}") - set(CMAKE_SHARED_LINKER_FLAGS "${_ldflags}") -endmacro() - -macro(libui _name _mode) - add_library(${_name} ${_mode} ${SOURCES}) - set_target_properties(${_name} PROPERTIES - OUTPUT_NAME ui) - if("${_mode}" STREQUAL "SHARED") - # only put version number on shared build - set_target_properties(${_name} PROPERTIES - SOVERSION ${_VERSION}) - else() - # don't build libui-static by default - set_target_properties(${_name} PROPERTIES - EXCLUDE_FROM_ALL 1) - endif() - set_target_properties(${_name} PROPERTIES - COMPILE_FLAGS "${_LIBUI_CFLAGS}") - set_target_properties(${_name} PROPERTIES - LINK_FLAGS "${_PICFLAG} ${_LIBUI_LDFLAGS}") -endmacro() - -libui(libui SHARED ${_SETVERSION} FALSE) -if(${_NOSHARED}) - nosharedmingw(libui-static) -else() - libui(libui-static STATIC FALSE TRUE) -endif() - -include_directories(test) -file(GLOB TESTSOURCES test/*.c) - -macro(executable_base _name _outname _libui _static) - add_executable(${_name} ${XSRC}) - set_target_properties(${_name} PROPERTIES - OUTPUT_NAME ${_outname} - EXCLUDE_FROM_ALL 1) - target_link_libraries(${_name} ${_libui}) - # be sure to include libui libraries in the output - if(${_static}) - set_target_properties(${_name} PROPERTIES - LINK_FLAGS "${_LIBUI_LDFLAGS}") - else() - set_target_properties(${_name} PROPERTIES - LINK_FLAGS "${_PICFLAG}") - endif() -endmacro() - -macro(executable _name _outname _dir) - include_directories(${_dir}) - file(GLOB XSRC ${_dir}/*.c ${_dir}/*.cpp) - executable_base(${_name} ${_outname} libui FALSE) - if(${_NOSHARED}) - nosharedmingw(${_name}-static) - else() - executable_base(${_name}-static ${_outname} libui-static TRUE) - endif() - set(XSRC) -endmacro() - -executable(tester test test) -executable(controlgallery controlgallery examples/controlgallery) -executable(histogram histogram examples/histogram) -executable(cpp-multithread cpp-multithread examples/cpp-multithread) - -add_custom_target(examples - DEPENDS - controlgallery - histogram - cpp-multithread) -add_custom_target(examples-static - DEPENDS - controlgallery-static - histogram-static - cpp-multithread-static) - diff --git a/wscript b/wscript deleted file mode 100644 index e37ac141..00000000 --- a/wscript +++ /dev/null @@ -1,200 +0,0 @@ -# 30 may 2016 - -from waflib import Utils - -top = '.' -out = '_build' - -def _platform(ctx): - plat = Utils.unversioned_sys_platform() - if plat == 'win32': - return 'windows' - if plat == 'darwin': - return 'darwin' - return 'unix' - -def _platsrcsuffix(plat): - if plat == 'windows': - return 'cpp' - if plat == 'darwin': - return 'm' - return 'c' - -def _plathsuffix(plat): - if plat == 'windows': - return 'hpp' - return 'h' - -def _toolchain(ctx): - return ctx.env['CC_NAME'] - -def options(ctx): - ctx.add_option('--toolchain', action='store', default=None, help='Toolchain to use, in waf terms; otheriwse compiler_c will determine') - ctx.load('compiler_c') - ctx.load('compiler_cxx') - -def configure(ctx): - if ctx.options.toolchain is not None: - conf.options.check_c_compiler = [ctx.options.toolchain] - conf.options.check_cxx_compiler = [ctx.options.toolchain] - ctx.load('compiler_c') - ctx.load('compiler_cxx') - - ctx.setenv('shared', env = ctx.env.derive()) - ctx.write_config_header('ignore/shared.h', remove = False) - ctx.setenv('static', env = ctx.env.derive()) - ctx.write_config_header('ignore/static.h', remove = False) - -def _common_flags(platform, toolchain, buildmode, gcclang, msvclang): - if toolchain == "msvc": - return list([ - msvclang, - ]) - l = list([ - "-Wall", "-Wextra", "-pedantic", - "-Wno-unused-parameter", - "-Wno-switch", - gcclang, - ]) - if buildmode == "shared" and platform != "windows": - l += ["-fPIC"] - if platform == "darwin": - l += [ - "-mmacosx-version-min=10.8", - "-DMACOSX_DEPLOYMENT_TARGET=10.8", - ] - return l - -def _common_cflags(platform, toolchain, buildmode): - return _common_flags(platform, toolchain, buildmode, - '--std=c99', '-TC') - -def _common_cxxflags(platform, toolchain, buildmode): - return _common_flags(platform, toolchain, buildmode, - '--std=c99', '-TP') - -def _common_ldflags(platform, toolchain, buildmode): - if toolchain == "msvc": - return list([]) - l = list([]) - if buildmode == "shared" and platform != "windows": - l += ["-fPIC"] - if platform == "darwin": - l += [ - "-mmacosx-version-min=10.8", - ] - return l - -def _libui_cflags(toolchain): - if toolchain == "msvc": - return list([ - ]) - return list([ - """-D_UI_EXTERN=__attribute__((visibility("default"))) extern""", - "-fvisibility=hidden", - ]) - -def _libui_ldflags(toolchain): - if toolchain == "msvc": # no additional options - return list([]) - return list([ - "-fvisibility=hidden", - ]) - -def _platform_libs(platform, toolchain): - if toolchain == "msvc": - return list([]) - if platform == "darwin": - return list([ - "-lobjc", - "-framework", "Foundation", - "-framework", "AppKit", - ]) - if platform == "windows": - return list([]) - return list([]) - -def _libui_sharedldflags(platform, toolchain): - if toolchain == "msvc": - return list([]) - if platform == "darwin": - return list([ - "-install_name", "@rpath/", - ]) - if platform == "windows": - return list([]) - return list([]) - -def _exe_sharedldflags(platform, toolchain): - if toolchain == "msvc": - return list([]) - if platform == "darwin": - return list([ - # the / is needed by older OS X versions - "-rpath", "@executable_path/", - ]) - if platform == "windows": - return list([]) - return list([]) - -def _libui(ctx, platform, toolchain, buildmode): - headers = ctx.path.ant_glob( - "ui.h ui_%s.h %s/*.%s" % - (platform, platform, _plathsuffix(platform))) - sources = ctx.path.ant_glob( - "common/*.c %s/*.%s" % - (platform, _platsrcsuffix(platform))) - cflags = _common_cflags(platform, toolchain, buildmode) - cxxflags = _common_cxxflags(platform, toolchain, buildmode) - ldflags = _common_ldflags(platform, toolchain, buildmode) - cflags += _libui_cflags(toolchain) - cxxflags += _libui_cflags(toolchain) - ldflags += _libui_ldflags(toolchain) - ldflags += _platform_libs(platform, toolchain) - ldflags += _libui_sharedldflags(platform, toolchain) - target = "ui" - if platform == "windows": - target = "libui" - func = ctx.shlib - if buildmode == "static": - func = ctx.stlib - func( - target = target, - source = sources, - includes = headers, - cflags = cflags, - cxxflags = cxxflags, - ldflags = ldflags, - ) - -def build(ctx): - platform = _platform(ctx) - toolchain = _toolchain(ctx) - buildmode = ctx.variant - if not buildmode: - ctx.fatal("Use either 'waf build_shared` or `waf build_static`") - - if platform == "windows" and toolchain != "msvc" and buildmode == "shared": - ctx.fatal("Sorry, building a DLL on Windows with MinGW is not yet supported. Either build a static library or use MSVC.") - - _libui(ctx, platform, toolchain, buildmode) - -# from https://github.com/waf-project/waf/blob/master/demos/variants/wscript -def init(ctx): - from waflib.Build import BuildContext, CleanContext, InstallContext, UninstallContext - for x in 'shared static'.split(): - for y in (BuildContext, CleanContext, InstallContext, UninstallContext): - name = y.__name__.replace('Context','').lower() - class tmp(y): - cmd = name + '_' + x - variant = x - def buildall(ctx): - import waflib.Options - for x in ('build_shared', 'build_static'): - waflib.Options.commands.insert(0, x) - -# via https://github.com/waf-project/waf/blob/master/demos/mac_app/wscript -from waflib import TaskGen -@TaskGen.extension('.m') -def m_hook(self, node): - return self.create_compiled_task('c', node) From e4a66b786e5268e001f76b6a7ebbb85d53564100 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 1 Jun 2016 20:21:10 -0400 Subject: [PATCH 0202/1329] Fixed static builds, added GTK+ builds, and cleaned out things in general for cmake. --- CMakeLists.txt | 34 ++++++++++----------- darwin/CMakeLists.txt | 29 ++++++++++-------- examples/CMakeLists.txt | 3 ++ unix/CMakeLists.txt | 65 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 101 insertions(+), 30 deletions(-) create mode 100644 unix/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ee46176..fa29f1d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,8 +30,10 @@ set(CMAKE_OSX_DEPLOYMENT_TARGET "10.8") project(libui) -set(EXECUTABLE_OUTPUT_PATH "${PROJECT_BINARY_DIR}/out") -set(LIBRARY_OUTPUT_PATH "${PROJECT_BINARY_DIR}/out") +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/out") +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/out") +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/out") +set(CMAKE_PDB_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/out") # now that we called project(), load our config variables macro(cfgcopy _prefix) @@ -58,7 +60,8 @@ if(APPLE) set(_VERSION "A") set(_PLATFORM_LIBS - "-lobjc -framework Foundation -framework AppKit") + -lobjc "-framework Foundation" "-framework AppKit" + ) # always use our rpath set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) @@ -71,26 +74,22 @@ elseif(WIN32) # note that usp10 comes before gdi32 # TODO prune this list - set(_PLATFORM_LIBS_BASE - user32 kernel32 usp10 gdi32 comctl32 uxtheme msimg32 comdlg32 d2d1 dwrite ole32 oleaut32 oleacc uuid) - if(MSVC) - string(REPLACE ";" ".lib " _PLATFORM_LIBS "${_PLATFORM_LIBS_BASE}") - set(_PLATFORM_LIBS "${_PLATFORM_LIBS}.lib") - else() - string(REPLACE ";" " -l" _PLATFORM_LIBS "${_PLATFORM_LIBS_BASE}") - set(_PLATFORM_LIBS "-l${_PLATFORM_LIBS}") - endif() + set(_PLATFORM_LIBS + user32 kernel32 usp10 gdi32 comctl32 uxtheme msimg32 comdlg32 d2d1 dwrite ole32 oleaut32 oleacc uuid + ) else() set(_OSDIR unix) set(_SETVERSION TRUE) set(_VERSION "0") + find_package(PkgConfig REQUIRED) pkg_check_modules(GTK REQUIRED gtk+-3.0) - set(_LIBUI_CFLAGS "${GTK_CFLAGS}") + string(REPLACE ";" " " _LIBUI_CFLAGS "${GTK_CFLAGS}") set(_PLATFORM_LIBS "${GTK_LDFLAGS} -lm -ldl") # always use our rpath set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) + set(CMAKE_INSTALL_RPATH "\$ORIGIN") endif() if(MSVC) @@ -120,11 +119,6 @@ else() # don't amend CMAKE_STATIC_LINKER_FLAGS; that's for ar endif() -# and add the platform libraries to the three places that need it: shared library links and the two static executable links -append(CMAKE_SHARED_LINKER_FLAGS " ${_PLATFORM_LIBS}") -append2(CMAKE_EXE_LINKER_FLAGS_STATIC CMAKE_EXE_LINKER_FLAGS_RUNTIMESTATIC - " ${_PLATFORM_LIBS}") - add_subdirectory("common") add_subdirectory("${_OSDIR}") if(_SHARED) @@ -136,6 +130,7 @@ if(_SHARED) set_target_properties(libui PROPERTIES SOVERSION "${_VERSION}") endif() + target_link_libraries(libui PRIVATE ${_PLATFORM_LIBS}) else() _add_static(libui $ @@ -150,6 +145,9 @@ macro(_add_exec _name) WIN32 EXCLUDE_FROM_ALL ${ARGN}) target_link_libraries(${_name} libui) + if(NOT _SHARED) + target_link_libraries(${_name} ${_PLATFORM_LIBS}) + endif() endmacro() add_subdirectory("test") diff --git a/darwin/CMakeLists.txt b/darwin/CMakeLists.txt index 651a175e..82450544 100644 --- a/darwin/CMakeLists.txt +++ b/darwin/CMakeLists.txt @@ -42,21 +42,26 @@ set_target_properties(libui-darwin PROPERTIES COMPILE_FLAGS "${_LIBUI_CFLAGS}" ) +# thanks to Mr-Hide in irc.freenode.net/#cmake macro(_add_static _name) - add_library(${_name} STATIC "${ARGN}") - file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/sharedhidden) + add_library(${_name}-temporary STATIC "${ARGN}") + set_target_properties(${_name}-temporary PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}") + set(_aname $) + set(_lname ${_name}-combined.list) + set(_oname ${_name}-combined.o) add_custom_command( - TARGET ${_name} POST_BUILD + OUTPUT ${_oname} COMMAND - ${CMAKE_AR} x $ + nm -m ${_aname} | sed -E -n "'s/^[0-9a-f]* \\([A-Z_]+,[a-z_]+\\) external //p'" > ${_lname} COMMAND - nm -m *.o | sed -E -n "'s/^[0-9a-f]* \\([A-Z_]+,[a-z_]+\\) external //p'" > ${_name}.lst - COMMAND - ld -exported_symbols_list ${_name}.lst -r *.o -o ../_combined_${_name}.o - COMMAND - rm $ - COMMAND - ${CMAKE_AR} rcs $ ../_combined_${_name}.o - WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/sharedhidden + ld -exported_symbols_list ${_lname} -r -all_load ${_aname} -o ${_oname} COMMENT "Removing hidden symbols") + add_library(${_name} STATIC ${_oname}) + # otherwise cmake won't know which linker to use + set_target_properties(${_name} PROPERTIES + LINKER_LANGUAGE C) + set(_aname) + set(_lname) + set(_oname) endmacro() diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 64f35869..af34dfa0 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -5,12 +5,15 @@ include_directories(..) _add_exec(controlgallery controlgallery/main.c ) + _add_exec(histogram histogram/main.c ) + _add_exec(cpp-multithread cpp-multithread/main.cpp ) +target_link_libraries(cpp-multithread pthread) add_custom_target(examples DEPENDS diff --git a/unix/CMakeLists.txt b/unix/CMakeLists.txt new file mode 100644 index 00000000..b98091ef --- /dev/null +++ b/unix/CMakeLists.txt @@ -0,0 +1,65 @@ +# 1 june 2016 + +include_directories(.. . ../common) + +add_library(libui-unix OBJECT + alloc.c + area.c + box.c + button.c + checkbox.c + child.c + colorbutton.c + combobox.c + control.c + datetimepicker.c + debug.c + draw.c + drawmatrix.c + drawpath.c + drawtext.c + editablecombo.c + entry.c + fontbutton.c + graphemes.c + group.c + label.c + main.c + menu.c + multilineentry.c + progressbar.c + radiobuttons.c + separator.c + slider.c + spinbox.c + stddialogs.c + tab.c + text.c + util.c + window.c +) +set_target_properties(libui-unix PROPERTIES + COMPILE_FLAGS "${_LIBUI_CFLAGS}" +) + +# thanks to Mr-Hide in irc.freenode.net/#cmake +macro(_add_static _name) + add_library(${_name}-temporary STATIC "${ARGN}") + set_target_properties(${_name}-temporary PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}") + set(_aname $) + set(_oname ${_name}-combined.o) + add_custom_command( + OUTPUT ${_oname} + COMMAND + ld -r --whole-archive ${_aname} -o ${_oname} + COMMAND + objcopy --localize-hidden ${_oname} + COMMENT "Removing hidden symbols") + add_library(${_name} STATIC ${_oname}) + # otherwise cmake won't know which linker to use + set_target_properties(${_name} PROPERTIES + LINKER_LANGUAGE C) + set(_aname) + set(_oname) +endmacro() From ee373a94d6e8d600bf285a396d6a4ebb65fc978a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 1 Jun 2016 21:45:39 -0400 Subject: [PATCH 0203/1329] Started the Windows cmake stuff and tried to get it to work on MinGW again. --- CMakeLists.txt | 31 ++++++++++++------ windows/CMakeLists.txt | 64 ++++++++++++++++++++++++++++++++++++++ windows/compilerver.hpp | 5 +-- windows/drawtext.cpp | 2 ++ windows/events.cpp | 3 +- windows/uipriv_windows.hpp | 8 +++++ 6 files changed, 99 insertions(+), 14 deletions(-) create mode 100644 windows/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index fa29f1d1..e29cd74a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,13 @@ if(${CMAKE_BUILD_TYPE} STREQUAL "Debug") elseif(${CMAKE_BUILD_TYPE} STREQUAL "Release") set(_SHARED TRUE) endif() + +# and we need to set this up prior to project() too +set(CMAKE_OSX_DEPLOYMENT_TARGET "10.8") + +project(libui) + +# TODO can this be above the project()? if(WIN32) if(NOT MSVC) if(_SHARED) @@ -24,12 +31,6 @@ if(WIN32) endif() endif() - -# and we need to set this up prior to project() too -set(CMAKE_OSX_DEPLOYMENT_TARGET "10.8") - -project(libui) - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/out") set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/out") set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/out") @@ -77,6 +78,8 @@ elseif(WIN32) set(_PLATFORM_LIBS user32 kernel32 usp10 gdi32 comctl32 uxtheme msimg32 comdlg32 d2d1 dwrite ole32 oleaut32 oleacc uuid ) + + set(_RESOURCES_RC resources.rc) else() set(_OSDIR unix) set(_SETVERSION TRUE) @@ -112,8 +115,13 @@ else() append(CMAKE_EXE_LINKER_FLAGS_RELEASE " -fPIC") endif() - append(_LIBUI_CFLAGS - " -D_UI_EXTERN='__attribute__((visibility(\"default\"))) extern' -fvisibility=hidden ${_PLATFORM_CFLAGS}") + if(WIN32) + append(_LIBUI_CFLAGS + " -D _UI_EXTERN=\"__declspec(dllexport) extern\" ${_PLATFORM_CFLAGS}") + else() + append(_LIBUI_CFLAGS + " -D_UI_EXTERN='__attribute__((visibility(\"default\"))) extern' -fvisibility=hidden ${_PLATFORM_CFLAGS}") + endif() append(CMAKE_SHARED_LINKER_FLAGS " -fvisibility=hidden") # don't amend CMAKE_STATIC_LINKER_FLAGS; that's for ar @@ -143,7 +151,8 @@ set_target_properties(libui PROPERTIES macro(_add_exec _name) add_executable(${_name} WIN32 EXCLUDE_FROM_ALL - ${ARGN}) + ${ARGN} + ${_RESOURCES_RC}) target_link_libraries(${_name} libui) if(NOT _SHARED) target_link_libraries(${_name} ${_PLATFORM_LIBS}) @@ -155,4 +164,8 @@ set_target_properties(tester PROPERTIES OUTPUT_NAME test WIN32_EXECUTABLE FALSE) +# the same resources.rc is shared by all the examples +if(_RESOURCES_RC) + set(_RESOURCES_RC "../${_RESOURCES_RC}") +endif() add_subdirectory("examples") diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt new file mode 100644 index 00000000..3ddae42a --- /dev/null +++ b/windows/CMakeLists.txt @@ -0,0 +1,64 @@ +# 1 june 2016 + +include_directories(.. . ../common) + +add_library(libui-windows OBJECT + alloc.cpp + area.cpp + areadraw.cpp + areaevents.cpp + areascroll.cpp + areautil.cpp + box.cpp + button.cpp + checkbox.cpp + colorbutton.cpp + colordialog.cpp + combobox.cpp + container.cpp + control.cpp + d2dscratch.cpp + datetimepicker.cpp + debug.cpp + draw.cpp + drawmatrix.cpp + drawpath.cpp + drawtext.cpp + dwrite.cpp + editablecombo.cpp + entry.cpp + events.cpp + fontbutton.cpp + fontdialog.cpp + graphemes.cpp + group.cpp + init.cpp + label.cpp + main.cpp + menu.cpp + multilineentry.cpp + parent.cpp + progressbar.cpp + radiobuttons.cpp + separator.cpp + sizing.cpp + slider.cpp + spinbox.cpp + stddialogs.cpp + tab.cpp + tabpage.cpp + text.cpp + utf16.cpp + utilwin.cpp + window.cpp + winpublic.cpp + winutil.cpp + resources.rc +) +set_target_properties(libui-windows PROPERTIES + COMPILE_FLAGS "${_LIBUI_CFLAGS}" +) + +macro(_add_static _name) + add_library(${_name} STATIC "${ARGN}") +endmacro() diff --git a/windows/compilerver.hpp b/windows/compilerver.hpp index 535b5138..6c9e6b81 100644 --- a/windows/compilerver.hpp +++ b/windows/compilerver.hpp @@ -8,9 +8,6 @@ #endif #endif -// MinGW -#ifdef __MINGW32__ -#error At present, MinGW is not supported; see README.md for details. -#endif +// LONGTERM MinGW // other compilers can be added here as necessary diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index f5e872b7..08855e4e 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -79,6 +79,8 @@ uiDrawTextFont *mkTextFont(IDWriteFont *df, BOOL addRef, WCHAR *family, BOOL cop // TODO consider moving these all to dwrite.cpp +// TODO MinGW-w64 is missing this one +#define DWRITE_FONT_WEIGHT_SEMI_LIGHT (DWRITE_FONT_WEIGHT(350)) static const struct { bool lastOne; uiDrawTextWeight uival; diff --git a/windows/events.cpp b/windows/events.cpp index a6da31a8..45e8d43d 100644 --- a/windows/events.cpp +++ b/windows/events.cpp @@ -8,7 +8,8 @@ struct handler { uiControl *c; // just to ensure handlers[new HWND] initializes properly - struct handler() + // TODO gcc can't handle a struct keyword here? or is that a MSVC extension? + handler() { this->commandHandler = NULL; this->notifyHandler = NULL; diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index 3e6a74c1..1a00bada 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -50,9 +50,17 @@ extern WCHAR *itoutf16(intmax_t i); #define _wsn(m) _ws2n(m) #define debugargs const WCHAR *file, const WCHAR *line, const WCHAR *func extern HRESULT _logLastError(debugargs, const WCHAR *s); +#ifdef _MSC_VER #define logLastError(s) _logLastError(_ws(__FILE__), _wsn(__LINE__), _ws(__FUNCTION__), s) +#else +#define logLastError(s) _logLastError(_ws(__FILE__), _wsn(__LINE__), L"TODO none of the function name macros are macros in MinGW", s) +#endif extern HRESULT _logHRESULT(debugargs, const WCHAR *s, HRESULT hr); +#ifdef _MSC_VER #define logHRESULT(s, hr) _logHRESULT(_ws(__FILE__), _wsn(__LINE__), _ws(__FUNCTION__), s, hr) +#else +#define logHRESULT(s, hr) _logHRESULT(_ws(__FILE__), _wsn(__LINE__), L"TODO none of the function name macros are macros in MinGW", s, hr) +#endif // winutil.cpp extern int windowClassOf(HWND hwnd, ...); From 1dbbab50a502831f7763b0268cbf76b34826efce Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 1 Jun 2016 22:18:40 -0400 Subject: [PATCH 0204/1329] More Windows work. --- CMakeLists.txt | 20 +++++++++++++++----- build/GNUbasemsvc.mk | 15 --------------- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e29cd74a..a35db793 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -96,7 +96,17 @@ else() endif() if(MSVC) - # TODO + append2(CMAKE_C_FLAGS CMAKE_CXX_FLAGS + "-W4 -wd4100 -bigobj -RTC1 -RTCs -RTCu") + + # shut the compiler up in some cases + # LONGTERM still needed? + append(CMAKE_CXX_FLAGS " -EHsc") + + append2(CMAKE_SHARED_LINKER_FLAGS CMAKE_STATIC_LINKER_FLAGS + " -largeaddressaware -incremental:no") + append(CMAKE_EXE_LINKER_FLAGS + " -largeaddressaware -incremental:no") else() append2(CMAKE_C_FLAGS CMAKE_CXX_FLAGS " -Wall -Wextra -pedantic -Wno-unused-parameter -Wno-switch") @@ -127,6 +137,10 @@ else() # don't amend CMAKE_STATIC_LINKER_FLAGS; that's for ar endif() +if(NOT _SHARED) + append(_LIBUI_CFLAGS " -D_UI_STATIC") +endif() + add_subdirectory("common") add_subdirectory("${_OSDIR}") if(_SHARED) @@ -164,8 +178,4 @@ set_target_properties(tester PROPERTIES OUTPUT_NAME test WIN32_EXECUTABLE FALSE) -# the same resources.rc is shared by all the examples -if(_RESOURCES_RC) - set(_RESOURCES_RC "../${_RESOURCES_RC}") -endif() add_subdirectory("examples") diff --git a/build/GNUbasemsvc.mk b/build/GNUbasemsvc.mk index 5078ac7c..42072af3 100644 --- a/build/GNUbasemsvc.mk +++ b/build/GNUbasemsvc.mk @@ -27,22 +27,7 @@ # TODO /analyze requires us to write annotations everywhere # TODO undecided flags from qo? # -RTCc is not supplied because it's discouraged as of VS2015; see https://www.reddit.com/r/cpp/comments/46mhne/rtcc_rejects_conformant_code_with_visual_c_2015/d06auq5 -CFLAGS += \ - -W4 \ - -wd4100 \ - -TC \ - -bigobj -nologo \ - -RTC1 -RTCs -RTCu -# TODO prune these -# -EHsc is to shut the compiler up in some cases -CXXFLAGS += \ - -W4 \ - -wd4100 \ - -TP \ - -bigobj -nologo \ - -RTC1 -RTCs -RTCu \ - -EHsc # TODO warnings on undefined symbols LDFLAGS += \ From 2ef3dafbc6c88595217a4b691a6b685e5b72fa8d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 2 Jun 2016 00:23:42 -0400 Subject: [PATCH 0205/1329] Fixed more Windows bits. --- CMakeLists.txt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a35db793..ee4535ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,10 +103,11 @@ if(MSVC) # LONGTERM still needed? append(CMAKE_CXX_FLAGS " -EHsc") + # note the /MANIFEST:NO (which must be / and uppercase); thanks FraGag (https://github.com/andlabs/libui/issues/93#issuecomment-223183436) append2(CMAKE_SHARED_LINKER_FLAGS CMAKE_STATIC_LINKER_FLAGS - " -largeaddressaware -incremental:no") + " /LARGEADDRESSAWARE /INCREMENTAL:NO /MANIFEST:NO") append(CMAKE_EXE_LINKER_FLAGS - " -largeaddressaware -incremental:no") + " /LARGEADDRESSAWARE /INCREMENTAL:NO /MANIFEST:NO") else() append2(CMAKE_C_FLAGS CMAKE_CXX_FLAGS " -Wall -Wextra -pedantic -Wno-unused-parameter -Wno-switch") @@ -159,8 +160,11 @@ else() $ ) endif() -set_target_properties(libui PROPERTIES - OUTPUT_NAME ui) +# non-Windows platforms add an extra lib- at the beginning +if(NOT WIN32) + set_target_properties(libui PROPERTIES + OUTPUT_NAME ui) +endif() macro(_add_exec _name) add_executable(${_name} From 2d1b6093f0c6e8379264335ce8fc2fdf9bd5c87d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 2 Jun 2016 00:52:29 -0400 Subject: [PATCH 0206/1329] More CMake Windows fixes. --- CMakeLists.txt | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ee4535ea..3892ac85 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -97,16 +97,15 @@ endif() if(MSVC) append2(CMAKE_C_FLAGS CMAKE_CXX_FLAGS - "-W4 -wd4100 -bigobj -RTC1 -RTCs -RTCu") + "/W4 /wd4100 /bigobj /RTC1 /RTCs /RTCu") # shut the compiler up in some cases # LONGTERM still needed? - append(CMAKE_CXX_FLAGS " -EHsc") + append(CMAKE_CXX_FLAGS " /EHsc") # note the /MANIFEST:NO (which must be / and uppercase); thanks FraGag (https://github.com/andlabs/libui/issues/93#issuecomment-223183436) - append2(CMAKE_SHARED_LINKER_FLAGS CMAKE_STATIC_LINKER_FLAGS - " /LARGEADDRESSAWARE /INCREMENTAL:NO /MANIFEST:NO") - append(CMAKE_EXE_LINKER_FLAGS + # also don't apply to CMAKE_STATIC_LINKER_FLAGS; those are passed to a different tool that doesn't support them + append2(CMAKE_SHARED_LINKER_FLAGS CMAKE_EXE_LINKER_FLAGS " /LARGEADDRESSAWARE /INCREMENTAL:NO /MANIFEST:NO") else() append2(CMAKE_C_FLAGS CMAKE_CXX_FLAGS From da8649312ed543ea9be6242bfe57aac27db905f3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 2 Jun 2016 12:52:32 -0400 Subject: [PATCH 0207/1329] More cmake work. Better, but not all there. --- CMakeLists.txt | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3892ac85..88355f42 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,6 +78,10 @@ elseif(WIN32) set(_PLATFORM_LIBS user32 kernel32 usp10 gdi32 comctl32 uxtheme msimg32 comdlg32 d2d1 dwrite ole32 oleaut32 oleacc uuid ) + # and don't include the default libraries + # note the CACHE FORCE stuff is required here + set(CMAKE_C_STANDARD_LIBRARIES CACHE STRING "" FORCE) + set(CMAKE_CXX_STANDARD_LIBRARIES CACHE STRING "" FORCE) set(_RESOURCES_RC resources.rc) else() @@ -103,6 +107,8 @@ if(MSVC) # LONGTERM still needed? append(CMAKE_CXX_FLAGS " /EHsc") + append(_LIBUI_CFLAGS " ${_PLATFORM_CFLAGS}") + # note the /MANIFEST:NO (which must be / and uppercase); thanks FraGag (https://github.com/andlabs/libui/issues/93#issuecomment-223183436) # also don't apply to CMAKE_STATIC_LINKER_FLAGS; those are passed to a different tool that doesn't support them append2(CMAKE_SHARED_LINKER_FLAGS CMAKE_EXE_LINKER_FLAGS @@ -126,11 +132,9 @@ else() endif() if(WIN32) - append(_LIBUI_CFLAGS - " -D _UI_EXTERN=\"__declspec(dllexport) extern\" ${_PLATFORM_CFLAGS}") + append(_LIBUI_CFLAGS " ${_PLATFORM_CFLAGS}") else() - append(_LIBUI_CFLAGS - " -D_UI_EXTERN='__attribute__((visibility(\"default\"))) extern' -fvisibility=hidden ${_PLATFORM_CFLAGS}") + append(_LIBUI_CFLAGS " -fvisibility=hidden ${_PLATFORM_CFLAGS}") endif() append(CMAKE_SHARED_LINKER_FLAGS " -fvisibility=hidden") @@ -164,6 +168,14 @@ if(NOT WIN32) set_target_properties(libui PROPERTIES OUTPUT_NAME ui) endif() +# let cmake handle quoting and escaping for us +if(WIN32) + set_target_properties(libui PROPERTIES + COMPILE_DEFINITIONS "_UI_EXTERN=__declspec(dllexport) extern") +else() + set_target_properties(libui PROPERTIES + COMPILE_DEFINITIONS "_UI_EXTERN=__attribute__((visibility(\"default\"))) extern") +endif() macro(_add_exec _name) add_executable(${_name} From 14f5d5388a7774c3c77165a48d3f0d8bf832da30 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 2 Jun 2016 17:28:51 -0400 Subject: [PATCH 0208/1329] Tried harder with the CMake lists. Still no go. They should probably be rewritten again... --- CMakeLists.txt | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 88355f42..8d6aa603 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,9 @@ # 30 may 2016 cmake_minimum_required(VERSION 2.8.12) +# TODOs +# - ensure all set_target_properties() calls are approrpiately set, APPEND, and APPEND_STRING + # set up our configurations set(CMAKE_CONFIGURATION_TYPES Debug Static Release ReleaseStatic) # we load the variables after calling project() @@ -170,11 +173,11 @@ if(NOT WIN32) endif() # let cmake handle quoting and escaping for us if(WIN32) - set_target_properties(libui PROPERTIES - COMPILE_DEFINITIONS "_UI_EXTERN=__declspec(dllexport) extern") + target_compile_definitions(libui + PRIVATE "_UI_EXTERN=__declspec(dllexport) extern") else() - set_target_properties(libui PROPERTIES - COMPILE_DEFINITIONS "_UI_EXTERN=__attribute__((visibility(\"default\"))) extern") + target_compile_definitions(libui + PRIVATE "_UI_EXTERN=__attribute__((visibility(\"default\"))) extern") endif() macro(_add_exec _name) From 1903115bbd255372cda6218752d655af42e26f2a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 3 Jun 2016 00:29:31 -0400 Subject: [PATCH 0209/1329] Pushed all the build files elsewhere. I'm rewriting the CMakeLists files from scratch. --- buildnotes | 45 ------------------- .../CMakeLists.txt | 0 GNUmakefile => migrate_build/GNUmakefile | 0 {build => migrate_build/build}/GNUbasegcc.mk | 0 {build => migrate_build/build}/GNUbasemsvc.mk | 0 .../build}/GNUmakefile.example | 0 .../build}/GNUmakefile.libui | 0 .../build}/GNUmakefile.test | 0 {common => migrate_build/common}/GNUfiles.mk | 0 .../common_CMakeLists.txt | 0 {darwin => migrate_build/darwin}/GNUfiles.mk | 0 .../darwin}/GNUinstall.mk | 0 .../darwin}/GNUosspecific.mk | 0 .../darwin}/GNUosspecificlink.mk | 0 .../darwin_CMakeLists.txt | 0 .../examples_CMakeLists.txt | 0 {test => migrate_build/test}/GNUfiles.mk | 0 .../test_CMakeLists.txt | 0 {unix => migrate_build/unix}/GNUfiles.mk | 0 {unix => migrate_build/unix}/GNUinstall.mk | 0 {unix => migrate_build/unix}/GNUosspecific.mk | 0 .../unix}/GNUosspecificlink.mk | 0 .../unix_CMakeLists.txt | 0 .../windows}/GNUfiles.mk | 0 .../windows}/GNUinstall.mk | 0 .../windows}/GNUosspecific.mk | 0 .../windows_CMakeLists.txt | 0 27 files changed, 45 deletions(-) delete mode 100644 buildnotes rename CMakeLists.txt => migrate_build/CMakeLists.txt (100%) rename GNUmakefile => migrate_build/GNUmakefile (100%) rename {build => migrate_build/build}/GNUbasegcc.mk (100%) rename {build => migrate_build/build}/GNUbasemsvc.mk (100%) rename {build => migrate_build/build}/GNUmakefile.example (100%) rename {build => migrate_build/build}/GNUmakefile.libui (100%) rename {build => migrate_build/build}/GNUmakefile.test (100%) rename {common => migrate_build/common}/GNUfiles.mk (100%) rename common/CMakeLists.txt => migrate_build/common_CMakeLists.txt (100%) rename {darwin => migrate_build/darwin}/GNUfiles.mk (100%) rename {darwin => migrate_build/darwin}/GNUinstall.mk (100%) rename {darwin => migrate_build/darwin}/GNUosspecific.mk (100%) rename {darwin => migrate_build/darwin}/GNUosspecificlink.mk (100%) rename darwin/CMakeLists.txt => migrate_build/darwin_CMakeLists.txt (100%) rename examples/CMakeLists.txt => migrate_build/examples_CMakeLists.txt (100%) rename {test => migrate_build/test}/GNUfiles.mk (100%) rename test/CMakeLists.txt => migrate_build/test_CMakeLists.txt (100%) rename {unix => migrate_build/unix}/GNUfiles.mk (100%) rename {unix => migrate_build/unix}/GNUinstall.mk (100%) rename {unix => migrate_build/unix}/GNUosspecific.mk (100%) rename {unix => migrate_build/unix}/GNUosspecificlink.mk (100%) rename unix/CMakeLists.txt => migrate_build/unix_CMakeLists.txt (100%) rename {windows => migrate_build/windows}/GNUfiles.mk (100%) rename {windows => migrate_build/windows}/GNUinstall.mk (100%) rename {windows => migrate_build/windows}/GNUosspecific.mk (100%) rename windows/CMakeLists.txt => migrate_build/windows_CMakeLists.txt (100%) diff --git a/buildnotes b/buildnotes deleted file mode 100644 index 56c2801f..00000000 --- a/buildnotes +++ /dev/null @@ -1,45 +0,0 @@ -HOW TO BUILD -Simply type - - make [variables...] - -The build-time settings are - - OS=xxx - default operating system - supported OSs are - windows - unix - darwin - haiku - this is autodetected by default - CFLAGS=xxx - CXXFLAGS=xxx - LDFLAGS=xxx - compiler flags - this is where you can specify -m32 or -m64, for instance - Objective-C uses $(CFLAGS) - RELEASE=xxx - set to 1 to disable debug symbols - must be 1, any other value means "include debug symbols" - TODO separate debug symbols from debug runtime - PREFIX=xxx - TODO - DESTDIR=xxx - applied before PREFIX; used by Debian - STATIC=1 - statically link instead of shared - -To build the test program use - - make [variables....] test - -The variables are the same as above. - -To build an example, use - - make EXAMPLE=name [variables...] example - -You must specify the example name. - -TODO discuss the internals diff --git a/CMakeLists.txt b/migrate_build/CMakeLists.txt similarity index 100% rename from CMakeLists.txt rename to migrate_build/CMakeLists.txt diff --git a/GNUmakefile b/migrate_build/GNUmakefile similarity index 100% rename from GNUmakefile rename to migrate_build/GNUmakefile diff --git a/build/GNUbasegcc.mk b/migrate_build/build/GNUbasegcc.mk similarity index 100% rename from build/GNUbasegcc.mk rename to migrate_build/build/GNUbasegcc.mk diff --git a/build/GNUbasemsvc.mk b/migrate_build/build/GNUbasemsvc.mk similarity index 100% rename from build/GNUbasemsvc.mk rename to migrate_build/build/GNUbasemsvc.mk diff --git a/build/GNUmakefile.example b/migrate_build/build/GNUmakefile.example similarity index 100% rename from build/GNUmakefile.example rename to migrate_build/build/GNUmakefile.example diff --git a/build/GNUmakefile.libui b/migrate_build/build/GNUmakefile.libui similarity index 100% rename from build/GNUmakefile.libui rename to migrate_build/build/GNUmakefile.libui diff --git a/build/GNUmakefile.test b/migrate_build/build/GNUmakefile.test similarity index 100% rename from build/GNUmakefile.test rename to migrate_build/build/GNUmakefile.test diff --git a/common/GNUfiles.mk b/migrate_build/common/GNUfiles.mk similarity index 100% rename from common/GNUfiles.mk rename to migrate_build/common/GNUfiles.mk diff --git a/common/CMakeLists.txt b/migrate_build/common_CMakeLists.txt similarity index 100% rename from common/CMakeLists.txt rename to migrate_build/common_CMakeLists.txt diff --git a/darwin/GNUfiles.mk b/migrate_build/darwin/GNUfiles.mk similarity index 100% rename from darwin/GNUfiles.mk rename to migrate_build/darwin/GNUfiles.mk diff --git a/darwin/GNUinstall.mk b/migrate_build/darwin/GNUinstall.mk similarity index 100% rename from darwin/GNUinstall.mk rename to migrate_build/darwin/GNUinstall.mk diff --git a/darwin/GNUosspecific.mk b/migrate_build/darwin/GNUosspecific.mk similarity index 100% rename from darwin/GNUosspecific.mk rename to migrate_build/darwin/GNUosspecific.mk diff --git a/darwin/GNUosspecificlink.mk b/migrate_build/darwin/GNUosspecificlink.mk similarity index 100% rename from darwin/GNUosspecificlink.mk rename to migrate_build/darwin/GNUosspecificlink.mk diff --git a/darwin/CMakeLists.txt b/migrate_build/darwin_CMakeLists.txt similarity index 100% rename from darwin/CMakeLists.txt rename to migrate_build/darwin_CMakeLists.txt diff --git a/examples/CMakeLists.txt b/migrate_build/examples_CMakeLists.txt similarity index 100% rename from examples/CMakeLists.txt rename to migrate_build/examples_CMakeLists.txt diff --git a/test/GNUfiles.mk b/migrate_build/test/GNUfiles.mk similarity index 100% rename from test/GNUfiles.mk rename to migrate_build/test/GNUfiles.mk diff --git a/test/CMakeLists.txt b/migrate_build/test_CMakeLists.txt similarity index 100% rename from test/CMakeLists.txt rename to migrate_build/test_CMakeLists.txt diff --git a/unix/GNUfiles.mk b/migrate_build/unix/GNUfiles.mk similarity index 100% rename from unix/GNUfiles.mk rename to migrate_build/unix/GNUfiles.mk diff --git a/unix/GNUinstall.mk b/migrate_build/unix/GNUinstall.mk similarity index 100% rename from unix/GNUinstall.mk rename to migrate_build/unix/GNUinstall.mk diff --git a/unix/GNUosspecific.mk b/migrate_build/unix/GNUosspecific.mk similarity index 100% rename from unix/GNUosspecific.mk rename to migrate_build/unix/GNUosspecific.mk diff --git a/unix/GNUosspecificlink.mk b/migrate_build/unix/GNUosspecificlink.mk similarity index 100% rename from unix/GNUosspecificlink.mk rename to migrate_build/unix/GNUosspecificlink.mk diff --git a/unix/CMakeLists.txt b/migrate_build/unix_CMakeLists.txt similarity index 100% rename from unix/CMakeLists.txt rename to migrate_build/unix_CMakeLists.txt diff --git a/windows/GNUfiles.mk b/migrate_build/windows/GNUfiles.mk similarity index 100% rename from windows/GNUfiles.mk rename to migrate_build/windows/GNUfiles.mk diff --git a/windows/GNUinstall.mk b/migrate_build/windows/GNUinstall.mk similarity index 100% rename from windows/GNUinstall.mk rename to migrate_build/windows/GNUinstall.mk diff --git a/windows/GNUosspecific.mk b/migrate_build/windows/GNUosspecific.mk similarity index 100% rename from windows/GNUosspecific.mk rename to migrate_build/windows/GNUosspecific.mk diff --git a/windows/CMakeLists.txt b/migrate_build/windows_CMakeLists.txt similarity index 100% rename from windows/CMakeLists.txt rename to migrate_build/windows_CMakeLists.txt From 089793e25ddd4375fd77e0a57c2b4192ad912d23 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 3 Jun 2016 10:29:54 -0400 Subject: [PATCH 0210/1329] Restored unmodified GNU make files. --- migrate_build/build/GNUbasemsvc.mk | 15 +++++++++++++++ migrate_build/darwin/GNUfiles.mk | 6 ++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/migrate_build/build/GNUbasemsvc.mk b/migrate_build/build/GNUbasemsvc.mk index 42072af3..5078ac7c 100644 --- a/migrate_build/build/GNUbasemsvc.mk +++ b/migrate_build/build/GNUbasemsvc.mk @@ -27,7 +27,22 @@ # TODO /analyze requires us to write annotations everywhere # TODO undecided flags from qo? # -RTCc is not supplied because it's discouraged as of VS2015; see https://www.reddit.com/r/cpp/comments/46mhne/rtcc_rejects_conformant_code_with_visual_c_2015/d06auq5 +CFLAGS += \ + -W4 \ + -wd4100 \ + -TC \ + -bigobj -nologo \ + -RTC1 -RTCs -RTCu +# TODO prune these +# -EHsc is to shut the compiler up in some cases +CXXFLAGS += \ + -W4 \ + -wd4100 \ + -TP \ + -bigobj -nologo \ + -RTC1 -RTCs -RTCu \ + -EHsc # TODO warnings on undefined symbols LDFLAGS += \ diff --git a/migrate_build/darwin/GNUfiles.mk b/migrate_build/darwin/GNUfiles.mk index 12615f72..9b9ed693 100644 --- a/migrate_build/darwin/GNUfiles.mk +++ b/migrate_build/darwin/GNUfiles.mk @@ -46,9 +46,11 @@ LDFLAGS += $(NATIVE_UI_LDFLAGS) # flags for OS X versioning CFLAGS += \ - -mmacosx-version-min=10.8 + -mmacosx-version-min=10.8 \ + -DMACOSX_DEPLOYMENT_TARGET=10.8 CXXFLAGS += \ - -mmacosx-version-min=10.8 + -mmacosx-version-min=10.8 \ + -DMACOSX_DEPLOYMENT_TARGET=10.8 LDFLAGS += \ -mmacosx-version-min=10.8 From 44b4d248814b22df2b5cc74954ad6baee9472b1e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 3 Jun 2016 14:56:21 -0400 Subject: [PATCH 0211/1329] Started a new cmake file. Seems to work so far... --- CMakeLists.txt | 99 +++++++++++++++++++++ common/CMakeLists.txt | 16 ++++ migrate_build/CMakeLists.txt | 16 ---- migrate_build/build/GNUbasemsvc.mk | 117 ------------------------- migrate_build/build/GNUmakefile.libui | 12 --- migrate_build/windows/GNUfiles.mk | 88 ------------------- migrate_build/windows/GNUosspecific.mk | 14 --- windows/CMakeLists.txt | 80 +++++++++++++++++ 8 files changed, 195 insertions(+), 247 deletions(-) create mode 100644 CMakeLists.txt create mode 100644 common/CMakeLists.txt create mode 100644 windows/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..5cf2e587 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,99 @@ +# 3 june 2016 +cmake_minimum_required(VERSION 2.8.11) + +project(libui LANGUAGES C CXX) +option(BUILD_SHARED_LIBS "Whether to build libui as a shared library or a static library" ON) + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/out") +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/out") +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/out") +set(CMAKE_PDB_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/out") + +if(APPLE) + set(_OSNAME darwin) +elseif(WIN32) + set(_OSNAME windows) +else() + set(_OSNAME unix) +endif() + +if(BUILD_SHARED_LIBS) + # shared libraries link against system libs; executables don't + set(_LIBUI_LINKMODE PRIVATE) +else() + # static libraries don't link against system libs; executables do + set(_LIBUI_LINKMODE INTERFACE) +endif() + +# common flags +if(MSVC) + # TODO subsystem version + + # TODO /Wall does too much + # TODO -Wno-switch equivalent + # TODO /sdl turns C4996 into an ERROR + # don't use /analyze; that requires us to write annotations everywhere + # TODO undecided flags from qo? + # /RTCc is not supplied because it's discouraged as of VS2015; see https://www.reddit.com/r/cpp/comments/46mhne/rtcc_rejects_conformant_code_with_visual_c_2015/d06auq5 + # /EHsc is to shut the compiler up in some cases + # TODO make /EHsc C++-only + set(_COMMON_CFLAGS + /W4 /wd4100 + /bigobj /nologo + /RTC1 /RTCs /RTCu + /EHsc + ) + + # note the /MANIFEST:NO (which must be / and uppercase); thanks FraGag (https://github.com/andlabs/libui/issues/93#issuecomment-223183436) + # TODO warnings on undefined symbols + set(_COMMON_LDFLAGS + /LARGEADDRESSAWARE + /NOLOGO + /INCREMENTAL:NO + /MANIFEST:NO + ) + + # TODO autogenerate a .def file? +else() +endif() + +add_subdirectory("common") +add_subdirectory("${_OSNAME}") +add_library(${_LIBUINAME} ${_LIBUI_SOURCES}) +target_include_directories(${_LIBUINAME} + PUBLIC . + PRIVATE ${_LIBUI_INCLUEDIRS}) +target_compile_definitions(${_LIBUINAME} + PRIVATE ${_LIBUI_DEFS}) +target_compile_options(${_LIBUINAME} + PUBLIC ${_COMMON_CFLAGS} + PRIVATE ${_LIBUI_CFLAGS}) +# TODO link directories? +target_link_libraries(${_LIBUINAME} + ${_LIBUI_LINKMODE} ${_LIBUI_LIBS}) +# on Windows the linker for static libraries is different; don't give it the flags +# TODO are these inherited? +if(BUILD_SHARED_LIBS) + set_property(TARGET ${_LIBUINAME} APPEND PROPERTY + LINK_FLAGS ${_LIBUI_LDFLAGS}) +endif() +if(NOT BUILD_SHARED_LIBS) + _handle_static() + target_compile_definitions(${_LIBUINAME} + PRIVATE _UI_STATIC) +endif() +# don't put this in the OS CMakeLists.txt to be safe about quoting +if(WIN32) + target_compile_definitions(${_LIBUINAME} + PRIVATE "_UI_EXTERN=__declspec(dllexport) extern") +else() + target_compile_definitions(${_LIBUINAME} + PRIVATE "_UI_EXTERN=__attribute__((visibility(\"default\"))) extern") +endif() + +macro(_add_exec _name) + add_executable(${_name} + WIN32 EXCLUDE_FROM_ALL + ${ARGN}) + target_link_libraries(${_name} libui) +endmacro() diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt new file mode 100644 index 00000000..91d79493 --- /dev/null +++ b/common/CMakeLists.txt @@ -0,0 +1,16 @@ +# 3 june 2016 + +list(APPEND _LIBUI_SOURCES + common/areaevents.c + common/control.c + common/debug.c + common/matrix.c + common/shouldquit.c + common/userbugs.c +) +set(_LIBUI_SOURCES ${_LIBUI_SOURCES} PARENT_SCOPE) + +list(APPEND _LIBUI_INCLUDEDIRS + common +) +set(_LIBUI_INCLUDEDIRS ${_LIBUI_INCLUDEDIRS} PARENT_SCOPE) diff --git a/migrate_build/CMakeLists.txt b/migrate_build/CMakeLists.txt index 8d6aa603..79ebc0a8 100644 --- a/migrate_build/CMakeLists.txt +++ b/migrate_build/CMakeLists.txt @@ -34,11 +34,6 @@ if(WIN32) endif() endif() -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/out") -set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/out") -set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/out") -set(CMAKE_PDB_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/out") - # now that we called project(), load our config variables macro(cfgcopy _prefix) set(${_prefix}_STATIC "${${_prefix}_DEBUG}") @@ -180,17 +175,6 @@ else() PRIVATE "_UI_EXTERN=__attribute__((visibility(\"default\"))) extern") endif() -macro(_add_exec _name) - add_executable(${_name} - WIN32 EXCLUDE_FROM_ALL - ${ARGN} - ${_RESOURCES_RC}) - target_link_libraries(${_name} libui) - if(NOT _SHARED) - target_link_libraries(${_name} ${_PLATFORM_LIBS}) - endif() -endmacro() - add_subdirectory("test") set_target_properties(tester PROPERTIES OUTPUT_NAME test diff --git a/migrate_build/build/GNUbasemsvc.mk b/migrate_build/build/GNUbasemsvc.mk index 5078ac7c..e69de29b 100644 --- a/migrate_build/build/GNUbasemsvc.mk +++ b/migrate_build/build/GNUbasemsvc.mk @@ -1,117 +0,0 @@ -# 16 october 2015 - -# IMPORTANT -# Do NOT use / for command-line options here! -# This breaks on GNU makes that come with some versions of -# MinGW because they mangle things that start with /, thinking that -# those arguments are Unix paths that need to be converted to -# Windows paths. This cannot be turned off. -_-' -# MSDN says cl, rc, and link all accept - instead of /, so we're good. -# See also: -# - https://github.com/andlabs/libui/issues/16 -# - http://www.mingw.org/wiki/Posix_path_conversion -# - http://www.mingw.org/wiki/FAQ -# - http://stackoverflow.com/questions/7250130/how-to-stop-mingw-and-msys-from-mangling-path-names-given-at-the-command-line -# - http://stackoverflow.com/questions/28533664/how-to-prevent-msys-to-convert-the-file-path-for-an-external-program - -# TODO subsystem version - -# TODO silence compiler non-diagnostics (/nologo is not enough) - -# Global flags. - -# TODO /Wall does too much -# TODO -Wno-switch equivalent -# TODO /sdl turns C4996 into an ERROR -# TODO loads of warnings in the system header files -# TODO /analyze requires us to write annotations everywhere -# TODO undecided flags from qo? -# -RTCc is not supplied because it's discouraged as of VS2015; see https://www.reddit.com/r/cpp/comments/46mhne/rtcc_rejects_conformant_code_with_visual_c_2015/d06auq5 -CFLAGS += \ - -W4 \ - -wd4100 \ - -TC \ - -bigobj -nologo \ - -RTC1 -RTCs -RTCu - -# TODO prune these -# -EHsc is to shut the compiler up in some cases -CXXFLAGS += \ - -W4 \ - -wd4100 \ - -TP \ - -bigobj -nologo \ - -RTC1 -RTCs -RTCu \ - -EHsc - -# TODO warnings on undefined symbols -LDFLAGS += \ - -largeaddressaware -nologo -incremental:no - -ifneq ($(RELEASE),1) - CFLAGS += -Zi - CXXFLAGS += -Zi - LDFLAGS += -debug -endif - -# Build rules. - -OFILES = \ - $(subst /,_,$(CFILES)) \ - $(subst /,_,$(CXXFILES)) \ - $(subst /,_,$(MFILES)) -ifeq (,$(STATIC)) -OFILES += \ - $(subst /,_,$(RCFILES)) -else -RESFILES = \ - $(subst /,_,$(RCFILES)) -endif - -OFILES := $(OFILES:%=$(OBJDIR)/%.o) - -OUT = $(OUTDIR)/$(NAME)$(SUFFIX) -ifneq (,$(STATIC)) -RESOUT = $(OUTDIR)/$(NAME).res -endif -# otherwise keep $(RESOUT) empty - -# TODO use $(CC), $(CXX), $(LD), and s$(RC) - -$(OUT): $(OFILES) $(RESOUT) | $(OUTDIR) -ifeq (,$(STATICLIB)) - @link -out:$(OUT) $(OFILES) $(LDFLAGS) -else - @lib -out:$(OUT) $(OFILES) -endif - @echo ====== Linked $(OUT) - -.SECONDEXPANSION: - -# TODO can we put /Fd$@.pdb in a variable? -$(OBJDIR)/%.c.o: $$(subst _,/,%).c $(HFILES) | $(OBJDIR) -ifeq ($(RELEASE),1) - @cl -Fo:$@ -c $< $(CFLAGS) -else - @cl -Fo:$@ -c $< $(CFLAGS) -Fd$@.pdb -endif - @echo ====== Compiled $< - -$(OBJDIR)/%.cpp.o: $$(subst _,/,%).cpp $(HFILES) | $(OBJDIR) -ifeq ($(RELEASE),1) - @cl -Fo:$@ -c $< $(CXXFLAGS) -else - @cl -Fo:$@ -c $< $(CXXFLAGS) -Fd$@.pdb -endif - @echo ====== Compiled $< - -# note: don't run cvtres directly; the linker does that for us -$(OBJDIR)/%.rc.o: $$(subst _,/,%).rc $(HFILES) | $(OBJDIR) - @rc -nologo -v -fo $@ $(RCFLAGS) $< - @echo ====== Compiled $< -$(RESOUT): $$(RCFILES) $(HFILES) | $(OUTDIR) - @rc -nologo -v -fo $@ $(RCFLAGS) $< - @echo ====== Compiled $< - -$(OBJDIR) $(OUTDIR): - @mkdir $@ diff --git a/migrate_build/build/GNUmakefile.libui b/migrate_build/build/GNUmakefile.libui index 96f11c28..34f2af12 100644 --- a/migrate_build/build/GNUmakefile.libui +++ b/migrate_build/build/GNUmakefile.libui @@ -27,23 +27,11 @@ SUFFIX = $(STATICLIBSUFFIX) endif ifeq ($(TOOLCHAIN),gcc) - # make every symbol hidden by default except _UI_EXTERN ones - # thanks ebassi in irc.gimp.net/#gtk+ - CFLAGS += \ - -D_UI_EXTERN='__attribute__((visibility("default"))) extern' \ - -fvisibility=hidden - CXXFLAGS += \ - -D_UI_EXTERN='__attribute__((visibility("default"))) extern' \ -fvisibility=hidden LDFLAGS += \ -fvisibility=hidden else - # make every symbol hidden by default except _UI_EXTERN ones # TODO autogenerate a .def file? - CFLAGS += \ - -D "_UI_EXTERN=__declspec(dllexport) extern" - CXXFLAGS += \ - -D "_UI_EXTERN=__declspec(dllexport) extern" endif ifeq ($(RELEASE),1) diff --git a/migrate_build/windows/GNUfiles.mk b/migrate_build/windows/GNUfiles.mk index 0c3b50fa..e69de29b 100644 --- a/migrate_build/windows/GNUfiles.mk +++ b/migrate_build/windows/GNUfiles.mk @@ -1,88 +0,0 @@ -# 22 april 2015 - -CXXFILES += \ - windows/alloc.cpp \ - windows/area.cpp \ - windows/areadraw.cpp \ - windows/areaevents.cpp \ - windows/areascroll.cpp \ - windows/areautil.cpp \ - windows/box.cpp \ - windows/button.cpp \ - windows/checkbox.cpp \ - windows/colorbutton.cpp \ - windows/colordialog.cpp \ - windows/combobox.cpp \ - windows/container.cpp \ - windows/control.cpp \ - windows/d2dscratch.cpp \ - windows/datetimepicker.cpp \ - windows/debug.cpp \ - windows/draw.cpp \ - windows/drawmatrix.cpp \ - windows/drawpath.cpp \ - windows/drawtext.cpp \ - windows/dwrite.cpp \ - windows/editablecombo.cpp \ - windows/entry.cpp \ - windows/events.cpp \ - windows/fontbutton.cpp \ - windows/fontdialog.cpp \ - windows/graphemes.cpp \ - windows/group.cpp \ - windows/init.cpp \ - windows/label.cpp \ - windows/main.cpp \ - windows/menu.cpp \ - windows/multilineentry.cpp \ - windows/parent.cpp \ - windows/progressbar.cpp \ - windows/radiobuttons.cpp \ - windows/separator.cpp \ - windows/sizing.cpp \ - windows/slider.cpp \ - windows/spinbox.cpp \ - windows/stddialogs.cpp \ - windows/tab.cpp \ - windows/tabpage.cpp \ - windows/text.cpp \ - windows/utf16.cpp \ - windows/utilwin.cpp \ - windows/window.cpp \ - windows/winpublic.cpp \ - windows/winutil.cpp - -HFILES += \ - windows/_uipriv_migrate.hpp \ - windows/area.hpp \ - windows/compilerver.hpp \ - windows/draw.hpp \ - windows/resources.hpp \ - windows/uipriv_windows.hpp \ - windows/winapi.hpp - -RCFILES += \ - windows/resources.rc - -# LONGTERM split into a separate file or put in GNUmakefile.libui somehow? - -# flags for the Windows API -LDFLAGS += $(NATIVE_UI_LDFLAGS) - -# flags for building a shared library -ifeq (,$(STATIC)) -LDFLAGS += \ - -dll -endif - -# TODO flags for warning on undefined symbols - -# no need for a soname - -# TODO .def file - -ifneq (,$(STATIC)) -CFLAGS += -D_UI_STATIC -CXXFLAGS += -D_UI_STATIC -RCFLAGS += -D _UI_STATIC -endif diff --git a/migrate_build/windows/GNUosspecific.mk b/migrate_build/windows/GNUosspecific.mk index b074e06a..e69de29b 100644 --- a/migrate_build/windows/GNUosspecific.mk +++ b/migrate_build/windows/GNUosspecific.mk @@ -1,14 +0,0 @@ -# 16 october 2015 - -EXESUFFIX = .exe -LIBSUFFIX = .dll -OSHSUFFIX = .h -STATICLIBSUFFIX = .lib -TOOLCHAIN = msvc - -USESSONAME = 0 - -# notice that usp10.lib comes before gdi32.lib -# TODO prune this list -NATIVE_UI_LDFLAGS = \ - user32.lib kernel32.lib usp10.lib gdi32.lib comctl32.lib uxtheme.lib msimg32.lib comdlg32.lib d2d1.lib dwrite.lib ole32.lib oleaut32.lib oleacc.lib uuid.lib diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt new file mode 100644 index 00000000..84d1f2aa --- /dev/null +++ b/windows/CMakeLists.txt @@ -0,0 +1,80 @@ +# 3 june 2016 + +list(APPEND _LIBUI_SOURCES + windows/alloc.cpp + windows/area.cpp + windows/areadraw.cpp + windows/areaevents.cpp + windows/areascroll.cpp + windows/areautil.cpp + windows/box.cpp + windows/button.cpp + windows/checkbox.cpp + windows/colorbutton.cpp + windows/colordialog.cpp + windows/combobox.cpp + windows/container.cpp + windows/control.cpp + windows/d2dscratch.cpp + windows/datetimepicker.cpp + windows/debug.cpp + windows/draw.cpp + windows/drawmatrix.cpp + windows/drawpath.cpp + windows/drawtext.cpp + windows/dwrite.cpp + windows/editablecombo.cpp + windows/entry.cpp + windows/events.cpp + windows/fontbutton.cpp + windows/fontdialog.cpp + windows/graphemes.cpp + windows/group.cpp + windows/init.cpp + windows/label.cpp + windows/main.cpp + windows/menu.cpp + windows/multilineentry.cpp + windows/parent.cpp + windows/progressbar.cpp + windows/radiobuttons.cpp + windows/separator.cpp + windows/sizing.cpp + windows/slider.cpp + windows/spinbox.cpp + windows/stddialogs.cpp + windows/tab.cpp + windows/tabpage.cpp + windows/text.cpp + windows/utf16.cpp + windows/utilwin.cpp + windows/window.cpp + windows/winpublic.cpp + windows/winutil.cpp + windows/resources.rc +) +set(_LIBUI_SOURCES ${_LIBUI_SOURCES} PARENT_SCOPE) + +list(APPEND _LIBUI_INCLUDEDIRS + windows +) +set(_LIBUI_INCLUDEDIRS _LIBUI_INCLUDEDIRS PARENT_SCOPE) + +# no special handling of static libraries needed +set(_LIBUINAME libui PARENT_SCOPE) +macro(_handle_static) +endmacro() + +# notice that usp10 comes before gdi32 +# TODO prune this list +set(_LIBUI_LIBS + user32 kernel32 usp10 gdi32 comctl32 uxtheme msimg32 comdlg32 d2d1 dwrite ole32 oleaut32 oleacc uuid +PARENT_SCOPE) + +if(NOT MSVC) + if(BUILD_SHARED_LIBS) + message(FATAL_ERROR + "Sorry, but libui for Windows can currently only be built as a static library with MinGW. You will need to either build as a static library or switch to MSVC." + ) + endif() +endif() From 05b542182ac15c97449ca1be1a291ebf0ed9def5 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 3 Jun 2016 17:30:00 -0400 Subject: [PATCH 0212/1329] More cmake stuff. MSVC shared builds work as we want them to so far (mostly). --- CMakeLists.txt | 22 +++++++++++++++++++--- migrate_build/CMakeLists.txt | 3 --- test/CMakeLists.txt | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 6 deletions(-) create mode 100644 test/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 5cf2e587..e8507cc8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,6 +13,11 @@ if(APPLE) set(_OSNAME darwin) elseif(WIN32) set(_OSNAME windows) + + # and don't include the default libraries with ANY of the builds + # note the CACHE FORCE stuff is required here + set(CMAKE_C_STANDARD_LIBRARIES CACHE STRING "" FORCE) + set(CMAKE_CXX_STANDARD_LIBRARIES CACHE STRING "" FORCE) else() set(_OSNAME unix) endif() @@ -57,6 +62,14 @@ if(MSVC) else() endif() +# TODO why do I need to do this? why doesn't target_link_libraries() work? +macro(_target_link_options_private _target) + foreach(_opt IN LISTS ${ARGN}) + set_property(TARGET ${_target} APPEND_STRING PROPERTY + LINK_FLAGS " ${_opt}") + endforeach() +endmacro() + add_subdirectory("common") add_subdirectory("${_OSNAME}") add_library(${_LIBUINAME} ${_LIBUI_SOURCES}) @@ -72,10 +85,10 @@ target_compile_options(${_LIBUINAME} target_link_libraries(${_LIBUINAME} ${_LIBUI_LINKMODE} ${_LIBUI_LIBS}) # on Windows the linker for static libraries is different; don't give it the flags -# TODO are these inherited? if(BUILD_SHARED_LIBS) - set_property(TARGET ${_LIBUINAME} APPEND PROPERTY - LINK_FLAGS ${_LIBUI_LDFLAGS}) + _target_link_options_private(${_LIBUINAME} + _COMMON_LDFLAGS + _LIBUI_LDFLAGS) endif() if(NOT BUILD_SHARED_LIBS) _handle_static() @@ -96,4 +109,7 @@ macro(_add_exec _name) WIN32 EXCLUDE_FROM_ALL ${ARGN}) target_link_libraries(${_name} libui) + _target_link_options_private(${_name} + _COMMON_LDFLAGS) endmacro() +add_subdirectory("test") diff --git a/migrate_build/CMakeLists.txt b/migrate_build/CMakeLists.txt index 79ebc0a8..130eaeea 100644 --- a/migrate_build/CMakeLists.txt +++ b/migrate_build/CMakeLists.txt @@ -176,8 +176,5 @@ else() endif() add_subdirectory("test") -set_target_properties(tester PROPERTIES - OUTPUT_NAME test - WIN32_EXECUTABLE FALSE) add_subdirectory("examples") diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 00000000..8d6ba03a --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,36 @@ +# 3 june 2016 + +if(WIN32) + set(_TEST_RESOURCES_RC resources.rc) +endif() + +_add_exec(tester + drawtests.c + main.c + menus.c + page1.c + page10.c + page11.c + page12.c + page13.c + page2.c + page3.c + page4.c + page5.c + page6.c + page7.c + page7a.c + page7b.c + page7c.c + page8.c + page9.c + spaced.c + ${_TEST_RESOURCES_RC} +) +target_include_directories(tester + PRIVATE test +) +set_target_properties(tester PROPERTIES + OUTPUT_NAME test + WIN32_EXECUTABLE FALSE +) From a3fe7edf3bcb9273e5aa4bc2aee7f7436c017e6f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 3 Jun 2016 18:28:14 -0400 Subject: [PATCH 0213/1329] More cmake work. --- CMakeLists.txt | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e8507cc8..d07946b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,9 @@ # 3 june 2016 cmake_minimum_required(VERSION 2.8.11) +# TODOs: +# - MSVC static linking does not include the .res file in out\, so executables lack the necessary + project(libui LANGUAGES C CXX) option(BUILD_SHARED_LIBS "Whether to build libui as a shared library or a static library" ON) @@ -22,14 +25,6 @@ else() set(_OSNAME unix) endif() -if(BUILD_SHARED_LIBS) - # shared libraries link against system libs; executables don't - set(_LIBUI_LINKMODE PRIVATE) -else() - # static libraries don't link against system libs; executables do - set(_LIBUI_LINKMODE INTERFACE) -endif() - # common flags if(MSVC) # TODO subsystem version @@ -82,8 +77,11 @@ target_compile_options(${_LIBUINAME} PUBLIC ${_COMMON_CFLAGS} PRIVATE ${_LIBUI_CFLAGS}) # TODO link directories? -target_link_libraries(${_LIBUINAME} - ${_LIBUI_LINKMODE} ${_LIBUI_LIBS}) +# because we need 2.8.11 for CentOS, we can't use target_link_libraries(INTERFACE) for static executables :( +if(BUILD_SHARED_LIBS) + target_link_libraries(${_LIBUINAME} + PRIVATE ${_LIBUI_LIBS}) +endif() # on Windows the linker for static libraries is different; don't give it the flags if(BUILD_SHARED_LIBS) _target_link_options_private(${_LIBUINAME} @@ -111,5 +109,14 @@ macro(_add_exec _name) target_link_libraries(${_name} libui) _target_link_options_private(${_name} _COMMON_LDFLAGS) + # make shared-linked executables PIC too + if(BUILD_SHARED_LIBS) + set_property(TARGET ${_name} PROPERTY + POSITION_INDEPENDENT_CODE True) + endif() + # because we need 2.8.11 for CentOS, we can't use target_link_libraries(PUBLIC) for static executables :( + if(NOT BUILD_SHARED_LIBS) + target_link_libraries(${_name} ${_LIBUI_LIBS}) + endif() endmacro() add_subdirectory("test") From 0d88e5eb8b093a4f0682a12fe6549fac15a3e7f5 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 3 Jun 2016 19:25:43 -0400 Subject: [PATCH 0214/1329] More cmake work. That's enough of Visual Studio for now. --- CMakeLists.txt | 5 ++++- examples/CMakeLists.txt | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 examples/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index d07946b0..15367662 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,7 +57,9 @@ if(MSVC) else() endif() -# TODO why do I need to do this? why doesn't target_link_libraries() work? +# problem: +# - target_link_libraries() only supports - for flags +# - but cmake only doesn't generate the manifest if the flag has a / macro(_target_link_options_private _target) foreach(_opt IN LISTS ${ARGN}) set_property(TARGET ${_target} APPEND_STRING PROPERTY @@ -120,3 +122,4 @@ macro(_add_exec _name) endif() endmacro() add_subdirectory("test") +add_subdirectory("examples") diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 00000000..3a9ec4c9 --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,38 @@ +# 3 june 2016 + +if(WIN32) + set(_EXAMPLE_RESOURCES_RC resources.rc) +endif() + +macro(_add_example _name) + _add_exec(${_name} ${ARGN}) + # because Microsoft's toolchain is dumb + if(MSVC) + set_property(TARGET ${_name} APPEND_STRING PROPERTY + LINK_FLAGS " /ENTRY:mainCRTStartup") + endif() +endmacro() + +_add_example(controlgallery + controlgallery/main.c + ${_EXAMPLE_RESOURCES_RC} +) + +_add_example(histogram + histogram/main.c + ${_EXAMPLE_RESOURCES_RC} +) + +_add_example(cpp-multithread + cpp-multithread/main.cpp + ${_EXAMPLE_RESOURCES_RC} +) +if(NOT WIN32) + target_link_libraries(cpp-multithread pthread) +endif() + +add_custom_target(examples + DEPENDS + controlgallery + histogram + cpp-multithread) From 838851f55a651fc8091f8f721b8cd70aacd57609 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 3 Jun 2016 20:26:58 -0400 Subject: [PATCH 0215/1329] Started the gcc stuff. Doesn't work on Windows; too much to fix. --- CMakeLists.txt | 14 +++++++++++++- migrate_build/CMakeLists.txt | 3 --- migrate_build/build/GNUbasegcc.mk | 21 --------------------- migrate_build/build/GNUmakefile.libui | 2 +- 4 files changed, 14 insertions(+), 26 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 15367662..e039fd9d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,9 @@ cmake_minimum_required(VERSION 2.8.11) # TODOs: -# - MSVC static linking does not include the .res file in out\, so executables lack the necessary +# - MSVC static linking does not include the .res file in out\, so executables lack the necessary resources +# - same thing with MinGW? +# - MinGW doesn't work in general; windres doesn't like _UI_EXTERN (we could mitigate this by moving the stuff into ui.h and taking advantage of the libui_EXPORTS macro cmake makes for us) but project(libui LANGUAGES C CXX) option(BUILD_SHARED_LIBS "Whether to build libui as a shared library or a static library" ON) @@ -55,6 +57,16 @@ if(MSVC) # TODO autogenerate a .def file? else() + set(_COMMON_CFLAGS + -Wall -Wextra -pedantic + -Wno-unused-parameter + -Wno-switch + -fvisibility=hidden + ) + # don't use C_VERSION or CXX_VERSION because they use GNU standards + string(APPEND CMAKE_C_FLAGS " --std=c99") + string(APPEND CMAKE_CXX_FLAGS " --std=c++11") + endif() # problem: diff --git a/migrate_build/CMakeLists.txt b/migrate_build/CMakeLists.txt index 130eaeea..7ae649d2 100644 --- a/migrate_build/CMakeLists.txt +++ b/migrate_build/CMakeLists.txt @@ -114,9 +114,6 @@ if(MSVC) else() append2(CMAKE_C_FLAGS CMAKE_CXX_FLAGS " -Wall -Wextra -pedantic -Wno-unused-parameter -Wno-switch") - # don't use C_VERSION or CXX_VERSION because they use GNU standards - append(CMAKE_C_FLAGS " --std=c99") - append(CMAKE_CXX_FLAGS " --std=c++11") if(NOT WIN32) append(CMAKE_C_FLAGS_DEBUG " -fPIC") diff --git a/migrate_build/build/GNUbasegcc.mk b/migrate_build/build/GNUbasegcc.mk index 9e3e8636..6e834d15 100644 --- a/migrate_build/build/GNUbasegcc.mk +++ b/migrate_build/build/GNUbasegcc.mk @@ -3,32 +3,11 @@ # Global flags. CFLAGS += \ - -Wall -Wextra -pedantic \ - -Wno-unused-parameter \ - -Wno-switch \ --std=c99 -# C++11 is needed due to stupid rules involving commas at the end of enum lists that C++03 stupidly didn't follow -# This means sorry, no GCC 2 for Haiku builds :( CXXFLAGS += \ - -Wall -Wextra -pedantic \ - -Wno-unused-parameter \ - -Wno-switch \ --std=c++11 -# -fPIC shouldn't be used with static builds (see https://github.com/andlabs/libui/issues/72#issuecomment-222395547) -ifeq (,$(STATIC)) -CFLAGS += -fPIC -CXXFLAGS += -fPIC -LDFLAGS += -fPIC -endif - -ifneq ($(RELEASE),1) - CFLAGS += -g - CXXFLAGS += -g - LDFLAGS += -g -endif - # Build rules. OFILES = \ diff --git a/migrate_build/build/GNUmakefile.libui b/migrate_build/build/GNUmakefile.libui index 34f2af12..c4ac54d3 100644 --- a/migrate_build/build/GNUmakefile.libui +++ b/migrate_build/build/GNUmakefile.libui @@ -27,7 +27,7 @@ SUFFIX = $(STATICLIBSUFFIX) endif ifeq ($(TOOLCHAIN),gcc) - -fvisibility=hidden + LDFLAGS += \ -fvisibility=hidden else From 5c01a8dec336ba078b50128dcea839ecd0902bf8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 3 Jun 2016 21:14:55 -0400 Subject: [PATCH 0216/1329] Re-added the Unix stuff. --- CMakeLists.txt | 18 +++++++ migrate_build/CMakeLists.txt | 8 +--- migrate_build/unix_CMakeLists.txt | 19 +------- unix/CMakeLists.txt | 80 +++++++++++++++++++++++++++++++ 4 files changed, 100 insertions(+), 25 deletions(-) create mode 100644 unix/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index e039fd9d..ed751af3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,6 +25,12 @@ elseif(WIN32) set(CMAKE_CXX_STANDARD_LIBRARIES CACHE STRING "" FORCE) else() set(_OSNAME unix) + set(_HASVERSION TRUE) + set(_VERSION "0") + + # always use our rpath + set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) + set(CMAKE_INSTALL_RPATH "\$ORIGIN") endif() # common flags @@ -115,6 +121,18 @@ else() target_compile_definitions(${_LIBUINAME} PRIVATE "_UI_EXTERN=__attribute__((visibility(\"default\"))) extern") endif() +if(NOT WIN32) + # on non-Windows platforms cmake adds an extra lib- + # note that we apply this to libui, not to any intermediates + set_target_properties(libui PROPERTIES + OUTPUT_NAME ui) +endif() +if(BUILD_SHARED_LIBS) + if(_HASVERSION) + set_target_properties(${_LIBUINAME} PROPERTIES + SOVERSION "${_VERSION}") + endif() +endif() macro(_add_exec _name) add_executable(${_name} diff --git a/migrate_build/CMakeLists.txt b/migrate_build/CMakeLists.txt index 7ae649d2..c4917780 100644 --- a/migrate_build/CMakeLists.txt +++ b/migrate_build/CMakeLists.txt @@ -84,17 +84,11 @@ elseif(WIN32) set(_RESOURCES_RC resources.rc) else() set(_OSDIR unix) - set(_SETVERSION TRUE) - set(_VERSION "0") - find_package(PkgConfig REQUIRED) - pkg_check_modules(GTK REQUIRED gtk+-3.0) string(REPLACE ";" " " _LIBUI_CFLAGS "${GTK_CFLAGS}") set(_PLATFORM_LIBS "${GTK_LDFLAGS} -lm -ldl") - # always use our rpath - set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) - set(CMAKE_INSTALL_RPATH "\$ORIGIN") + endif() if(MSVC) diff --git a/migrate_build/unix_CMakeLists.txt b/migrate_build/unix_CMakeLists.txt index b98091ef..e36a66b6 100644 --- a/migrate_build/unix_CMakeLists.txt +++ b/migrate_build/unix_CMakeLists.txt @@ -44,22 +44,5 @@ set_target_properties(libui-unix PROPERTIES # thanks to Mr-Hide in irc.freenode.net/#cmake macro(_add_static _name) - add_library(${_name}-temporary STATIC "${ARGN}") - set_target_properties(${_name}-temporary PROPERTIES - ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}") - set(_aname $) - set(_oname ${_name}-combined.o) - add_custom_command( - OUTPUT ${_oname} - COMMAND - ld -r --whole-archive ${_aname} -o ${_oname} - COMMAND - objcopy --localize-hidden ${_oname} - COMMENT "Removing hidden symbols") - add_library(${_name} STATIC ${_oname}) - # otherwise cmake won't know which linker to use - set_target_properties(${_name} PROPERTIES - LINKER_LANGUAGE C) - set(_aname) - set(_oname) + endmacro() diff --git a/unix/CMakeLists.txt b/unix/CMakeLists.txt new file mode 100644 index 00000000..d8626062 --- /dev/null +++ b/unix/CMakeLists.txt @@ -0,0 +1,80 @@ +# 3 june 2016 + +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED gtk+-3.0) + +list(APPEND _LIBUI_SOURCES + unix/alloc.c + unix/area.c + unix/box.c + unix/button.c + unix/checkbox.c + unix/child.c + unix/colorbutton.c + unix/combobox.c + unix/control.c + unix/datetimepicker.c + unix/debug.c + unix/draw.c + unix/drawmatrix.c + unix/drawpath.c + unix/drawtext.c + unix/editablecombo.c + unix/entry.c + unix/fontbutton.c + unix/graphemes.c + unix/group.c + unix/label.c + unix/main.c + unix/menu.c + unix/multilineentry.c + unix/progressbar.c + unix/radiobuttons.c + unix/separator.c + unix/slider.c + unix/spinbox.c + unix/stddialogs.c + unix/tab.c + unix/text.c + unix/util.c + unix/window.c +) +set(_LIBUI_SOURCES ${_LIBUI_SOURCES} PARENT_SCOPE) + +list(APPEND _LIBUI_INCLUDEDIRS + unix +) +set(_LIBUI_INCLUDEDIRS _LIBUI_INCLUDEDIRS PARENT_SCOPE) + +set(_LIBUINAME libui PARENT_SCOPE) +if(NOT BUILD_SHARED_LIBS) + set(_LIBUINAME libui-temporary PARENT_SCOPE) +endif() +macro(_handle_static) + set_target_properties(${_LIBUINAME} PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}") + set(_aname $) + set(_oname libui-combined.o) + add_custom_command( + OUTPUT ${_oname} + COMMAND + ld -r --whole-archive ${_aname} -o ${_oname} + COMMAND + objcopy --localize-hidden ${_oname} + COMMENT "Removing hidden symbols") + add_library(libui STATIC ${_oname}) + # otherwise cmake won't know which linker to use + set_target_properties(libui PROPERTIES + LINKER_LANGUAGE C) + set(_aname) + set(_oname) +endmacro() + +# TODO the other variables don't work? +set(_LIBUI_CFLAGS + ${GTK_CFLAGS} +PARENT_SCOPE) + +set(_LIBUI_LIBS + ${GTK_LDFLAGS} m dl +PARENT_SCOPE) From bbb8791a479ed217b81b87293aeec550b73233b0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 3 Jun 2016 21:28:39 -0400 Subject: [PATCH 0217/1329] More Unix fixes. --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index ed751af3..a01959d3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,7 @@ cmake_minimum_required(VERSION 2.8.11) # - MSVC static linking does not include the .res file in out\, so executables lack the necessary resources # - same thing with MinGW? # - MinGW doesn't work in general; windres doesn't like _UI_EXTERN (we could mitigate this by moving the stuff into ui.h and taking advantage of the libui_EXPORTS macro cmake makes for us) but +# - Unix: gcc static linking extra step makes the above a moot point; PUBLIC properties don't propagate project(libui LANGUAGES C CXX) option(BUILD_SHARED_LIBS "Whether to build libui as a shared library or a static library" ON) From abcf1edf43ada4cac18f97eeffecf719dfda9847 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 3 Jun 2016 21:48:10 -0400 Subject: [PATCH 0218/1329] Re-added OS X file. Now to just prune everything again. --- CMakeLists.txt | 14 +++++- darwin/CMakeLists.txt | 75 +++++++++++++++++++++++++++++ migrate_build/CMakeLists.txt | 38 --------------- migrate_build/darwin_CMakeLists.txt | 23 --------- 4 files changed, 88 insertions(+), 62 deletions(-) create mode 100644 darwin/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index a01959d3..8d965e60 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,11 @@ cmake_minimum_required(VERSION 2.8.11) # - MSVC static linking does not include the .res file in out\, so executables lack the necessary resources # - same thing with MinGW? # - MinGW doesn't work in general; windres doesn't like _UI_EXTERN (we could mitigate this by moving the stuff into ui.h and taking advantage of the libui_EXPORTS macro cmake makes for us) but -# - Unix: gcc static linking extra step makes the above a moot point; PUBLIC properties don't propagate +# - Unix, Darwin: static linking makes the above a moot point; PUBLIC properties don't propagate +# - what is it, the static linking or the extra build step? + +# the docs say we need to set this up prior to project() +set(CMAKE_OSX_DEPLOYMENT_TARGET "10.8") project(libui LANGUAGES C CXX) option(BUILD_SHARED_LIBS "Whether to build libui as a shared library or a static library" ON) @@ -17,6 +21,14 @@ set(CMAKE_PDB_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/out") if(APPLE) set(_OSNAME darwin) + set(_HASVERSION TRUE) + set(_VERSION "A") + + # always use our rpath + set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) + # the / is required by some older versions of OS X + set(CMAKE_INSTALL_RPATH "@executable_path/") + set(CMAKE_MACOSX_RPATH TRUE) elseif(WIN32) set(_OSNAME windows) diff --git a/darwin/CMakeLists.txt b/darwin/CMakeLists.txt new file mode 100644 index 00000000..49f878a1 --- /dev/null +++ b/darwin/CMakeLists.txt @@ -0,0 +1,75 @@ +# 3 june 2016 + +list(APPEND _LIBUI_SOURCES + darwin/alloc.m + darwin/area.m + darwin/areaevents.m + darwin/autolayout.m + darwin/box.m + darwin/button.m + darwin/checkbox.m + darwin/colorbutton.m + darwin/combobox.m + darwin/control.m + darwin/datetimepicker.m + darwin/debug.m + darwin/draw.m + darwin/drawtext.m + darwin/editablecombo.m + darwin/entry.m + darwin/fontbutton.m + darwin/group.m + darwin/label.m + darwin/main.m + darwin/map.m + darwin/menu.m + darwin/multilineentry.m + darwin/progressbar.m + darwin/radiobuttons.m + darwin/scrollview.m + darwin/separator.m + darwin/slider.m + darwin/spinbox.m + darwin/stddialogs.m + darwin/tab.m + darwin/text.m + darwin/util.m + darwin/window.m +) +set(_LIBUI_SOURCES ${_LIBUI_SOURCES} PARENT_SCOPE) + +list(APPEND _LIBUI_INCLUDEDIRS + darwin +) +set(_LIBUI_INCLUDEDIRS _LIBUI_INCLUDEDIRS PARENT_SCOPE) + +set(_LIBUINAME libui PARENT_SCOPE) +if(NOT BUILD_SHARED_LIBS) + set(_LIBUINAME libui-temporary PARENT_SCOPE) +endif() +# thanks to Mr-Hide in irc.freenode.net/#cmake +macro(_handle_static) + set_target_properties(${_LIBUINAME} PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}") + set(_aname $) + set(_lname libui-combined.list) + set(_oname libui-combined.o) + add_custom_command( + OUTPUT ${_oname} + COMMAND + nm -m ${_aname} | sed -E -n "'s/^[0-9a-f]* \\([A-Z_]+,[a-z_]+\\) external //p'" > ${_lname} + COMMAND + ld -exported_symbols_list ${_lname} -r -all_load ${_aname} -o ${_oname} + COMMENT "Removing hidden symbols") + add_library(libui STATIC ${_oname}) + # otherwise cmake won't know which linker to use + set_target_properties(libui PROPERTIES + LINKER_LANGUAGE C) + set(_aname) + set(_lname) + set(_oname) +endmacro() + +set(_LIBUI_LIBS + objc "-framework Foundation" "-framework AppKit" +PARENT_SCOPE) diff --git a/migrate_build/CMakeLists.txt b/migrate_build/CMakeLists.txt index c4917780..02f7e8e2 100644 --- a/migrate_build/CMakeLists.txt +++ b/migrate_build/CMakeLists.txt @@ -53,44 +53,6 @@ macro(append2 _var1 _var2 _val) append(${_var2} "${_val}") endmacro() -if(APPLE) - set(_OSDIR darwin) - set(_SETVERSION TRUE) - set(_VERSION "A") - - set(_PLATFORM_LIBS - -lobjc "-framework Foundation" "-framework AppKit" - ) - - # always use our rpath - set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) - # the / is required by some older versions of OS X - set(CMAKE_INSTALL_RPATH "@executable_path/") - set(CMAKE_MACOSX_RPATH TRUE) -elseif(WIN32) - set(_OSDIR windows) - set(_SETVERSION FALSE) - - # note that usp10 comes before gdi32 - # TODO prune this list - set(_PLATFORM_LIBS - user32 kernel32 usp10 gdi32 comctl32 uxtheme msimg32 comdlg32 d2d1 dwrite ole32 oleaut32 oleacc uuid - ) - # and don't include the default libraries - # note the CACHE FORCE stuff is required here - set(CMAKE_C_STANDARD_LIBRARIES CACHE STRING "" FORCE) - set(CMAKE_CXX_STANDARD_LIBRARIES CACHE STRING "" FORCE) - - set(_RESOURCES_RC resources.rc) -else() - set(_OSDIR unix) - - string(REPLACE ";" " " _LIBUI_CFLAGS "${GTK_CFLAGS}") - set(_PLATFORM_LIBS "${GTK_LDFLAGS} -lm -ldl") - - -endif() - if(MSVC) append2(CMAKE_C_FLAGS CMAKE_CXX_FLAGS "/W4 /wd4100 /bigobj /RTC1 /RTCs /RTCu") diff --git a/migrate_build/darwin_CMakeLists.txt b/migrate_build/darwin_CMakeLists.txt index 82450544..d303bf5e 100644 --- a/migrate_build/darwin_CMakeLists.txt +++ b/migrate_build/darwin_CMakeLists.txt @@ -42,26 +42,3 @@ set_target_properties(libui-darwin PROPERTIES COMPILE_FLAGS "${_LIBUI_CFLAGS}" ) -# thanks to Mr-Hide in irc.freenode.net/#cmake -macro(_add_static _name) - add_library(${_name}-temporary STATIC "${ARGN}") - set_target_properties(${_name}-temporary PROPERTIES - ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}") - set(_aname $) - set(_lname ${_name}-combined.list) - set(_oname ${_name}-combined.o) - add_custom_command( - OUTPUT ${_oname} - COMMAND - nm -m ${_aname} | sed -E -n "'s/^[0-9a-f]* \\([A-Z_]+,[a-z_]+\\) external //p'" > ${_lname} - COMMAND - ld -exported_symbols_list ${_lname} -r -all_load ${_aname} -o ${_oname} - COMMENT "Removing hidden symbols") - add_library(${_name} STATIC ${_oname}) - # otherwise cmake won't know which linker to use - set_target_properties(${_name} PROPERTIES - LINKER_LANGUAGE C) - set(_aname) - set(_lname) - set(_oname) -endmacro() From 7dcfb8c6c3dbf44a370a4cfad6b485c09460d715 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 3 Jun 2016 22:19:33 -0400 Subject: [PATCH 0219/1329] Reworked how _UI_EXTERN works to allow MinGW static linking on Windows. --- CMakeLists.txt | 18 +++++------------- ui.h | 11 +++++++++-- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8d965e60..0c07c743 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,11 +2,8 @@ cmake_minimum_required(VERSION 2.8.11) # TODOs: -# - MSVC static linking does not include the .res file in out\, so executables lack the necessary resources -# - same thing with MinGW? -# - MinGW doesn't work in general; windres doesn't like _UI_EXTERN (we could mitigate this by moving the stuff into ui.h and taking advantage of the libui_EXPORTS macro cmake makes for us) but -# - Unix, Darwin: static linking makes the above a moot point; PUBLIC properties don't propagate -# - what is it, the static linking or the extra build step? +# - Windows: static linking does not include the .res file in out\, so executables lack the necessary resources +# - Darwin, Unix: static linking temporary target makes PUBLIC properties not propagate # the docs say we need to set this up prior to project() set(CMAKE_OSX_DEPLOYMENT_TARGET "10.8") @@ -106,6 +103,9 @@ target_include_directories(${_LIBUINAME} PRIVATE ${_LIBUI_INCLUEDIRS}) target_compile_definitions(${_LIBUINAME} PRIVATE ${_LIBUI_DEFS}) +# cmake produces this for us by default but only for shared libraries +target_compile_definitions(${_LIBUINAME} + PRIVATE libui_EXPORTS) target_compile_options(${_LIBUINAME} PUBLIC ${_COMMON_CFLAGS} PRIVATE ${_LIBUI_CFLAGS}) @@ -126,14 +126,6 @@ if(NOT BUILD_SHARED_LIBS) target_compile_definitions(${_LIBUINAME} PRIVATE _UI_STATIC) endif() -# don't put this in the OS CMakeLists.txt to be safe about quoting -if(WIN32) - target_compile_definitions(${_LIBUINAME} - PRIVATE "_UI_EXTERN=__declspec(dllexport) extern") -else() - target_compile_definitions(${_LIBUINAME} - PRIVATE "_UI_EXTERN=__attribute__((visibility(\"default\"))) extern") -endif() if(NOT WIN32) # on non-Windows platforms cmake adds an extra lib- # note that we apply this to libui, not to any intermediates diff --git a/ui.h b/ui.h index 34527918..c7a8858b 100644 --- a/ui.h +++ b/ui.h @@ -12,8 +12,15 @@ extern "C" { #endif -// TODO add __declspec(dllimport) on windows -#ifndef _UI_EXTERN +// this macro is generated by cmake +#ifdef libui_EXPORTS +#ifdef _WIN32 +#define _UI_EXTERN __declspec(dllexport) extern +#else +#define _UI_EXTERN __attribute__((visibility("default"))) extern +#endif +#else +// TODO add __declspec(dllimport) on windows, but only if not static #define _UI_EXTERN extern #endif From b65fb6509b809a3b6a2e08edda17bd1bcff95bf2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 4 Jun 2016 00:16:37 -0400 Subject: [PATCH 0220/1329] More notes. --- darwin/menu.m | 1 + 1 file changed, 1 insertion(+) diff --git a/darwin/menu.m b/darwin/menu.m index 4c6ec519..735cac50 100644 --- a/darwin/menu.m +++ b/darwin/menu.m @@ -126,6 +126,7 @@ static void mapItemReleaser(void *key, void *value) NSString *title; NSMenu *servicesMenu; + // note: no need to call setAppleMenu: on this anymore; see https://developer.apple.com/library/mac/releasenotes/AppKit/RN-AppKitOlderNotes/#X10_6Notes appName = [[NSProcessInfo processInfo] processName]; appMenuItem = [[[NSMenuItem alloc] initWithTitle:appName action:NULL keyEquivalent:@""] autorelease]; appMenu = [[[NSMenu alloc] initWithTitle:appName] autorelease]; From edbbe6eb59cdd0b5a9ab1e1772aa45ddd879525c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 4 Jun 2016 00:17:19 -0400 Subject: [PATCH 0221/1329] Removed TODO for the previous commit. --- TODO.md | 1 - 1 file changed, 1 deletion(-) diff --git a/TODO.md b/TODO.md index 4c38b3f2..6e31ef33 100644 --- a/TODO.md +++ b/TODO.md @@ -32,7 +32,6 @@ - DPI awareness on windows -- consider calling setAppleMenu: for the application menu; it doesn't seem to make much of a difference but - http://stackoverflow.com/questions/4543087/applicationwillterminate-and-the-dock-but-wanting-to-cancel-this-action ultimately: From 7d2f364256be87e2398d3aa57bbc21cb76eb1451 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 4 Jun 2016 13:57:10 -0400 Subject: [PATCH 0222/1329] Fixed Windows static linking issues. --- CMakeLists.txt | 6 +++--- windows/CMakeLists.txt | 16 +++++++++++++++- windows/resources.rc | 1 + 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c07c743..f811a658 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,6 @@ cmake_minimum_required(VERSION 2.8.11) # TODOs: -# - Windows: static linking does not include the .res file in out\, so executables lack the necessary resources # - Darwin, Unix: static linking temporary target makes PUBLIC properties not propagate # the docs say we need to set this up prior to project() @@ -123,8 +122,9 @@ if(BUILD_SHARED_LIBS) endif() if(NOT BUILD_SHARED_LIBS) _handle_static() + # TODO this really should be PRIVATE but I haven't fully figured this out target_compile_definitions(${_LIBUINAME} - PRIVATE _UI_STATIC) + PUBLIC _UI_STATIC) endif() if(NOT WIN32) # on non-Windows platforms cmake adds an extra lib- @@ -143,7 +143,7 @@ macro(_add_exec _name) add_executable(${_name} WIN32 EXCLUDE_FROM_ALL ${ARGN}) - target_link_libraries(${_name} libui) + target_link_libraries(${_name} libui ${_LIBUI_STATIC_RES}) _target_link_options_private(${_name} _COMMON_LDFLAGS) # make shared-linked executables PIC too diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 84d1f2aa..7119b0a3 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -60,9 +60,23 @@ list(APPEND _LIBUI_INCLUDEDIRS ) set(_LIBUI_INCLUDEDIRS _LIBUI_INCLUDEDIRS PARENT_SCOPE) -# no special handling of static libraries needed set(_LIBUINAME libui PARENT_SCOPE) +if(NOT BUILD_SHARED_LIBS) + set(_LIBUI_STATIC_RES ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/libui.res PARENT_SCOPE) +endif() macro(_handle_static) + if(MSVC) + set(_res_suffix res) + else() + set(_res_suffix obj) + endif() + # TODO this full path feels hacky + add_custom_command( + TARGET libui PRE_LINK + COMMAND + ${CMAKE_COMMAND} -E copy $/CMakeFiles/libui.dir/windows/resources.rc.${_res_suffix} ${_LIBUI_STATIC_RES} + COMMENT "Copying libui.res") + set(_res_suffix) endmacro() # notice that usp10 comes before gdi32 diff --git a/windows/resources.rc b/windows/resources.rc index 74402dfb..989dfc91 100644 --- a/windows/resources.rc +++ b/windows/resources.rc @@ -6,6 +6,7 @@ #pragma code_page(65001) // this is the Common Controls 6 manifest +// we only define it in a shared build; static builds have to include the appropriate parts of the manifest in the output executable // LONGTERM set up the string values here #ifndef _UI_STATIC ISOLATIONAWARE_MANIFEST_RESOURCE_ID RT_MANIFEST "libui.manifest" From 66fde9ee0cad3427cf8a21a48f9651f99277d2ec Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 4 Jun 2016 14:30:43 -0400 Subject: [PATCH 0223/1329] More fixups to the previous commit. --- CMakeLists.txt | 4 ++-- windows/CMakeLists.txt | 3 ++- windows/resources.rc | 2 ++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f811a658..266ef0fc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -126,8 +126,8 @@ if(NOT BUILD_SHARED_LIBS) target_compile_definitions(${_LIBUINAME} PUBLIC _UI_STATIC) endif() -if(NOT WIN32) - # on non-Windows platforms cmake adds an extra lib- +if(NOT MSVC) + # on non-MSVC compilers cmake adds an extra lib- # note that we apply this to libui, not to any intermediates set_target_properties(libui PROPERTIES OUTPUT_NAME ui) diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 7119b0a3..a2175786 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -60,6 +60,7 @@ list(APPEND _LIBUI_INCLUDEDIRS ) set(_LIBUI_INCLUDEDIRS _LIBUI_INCLUDEDIRS PARENT_SCOPE) +# Windows won't link resources in static libraries; we need to provide the libui.res file in this case. set(_LIBUINAME libui PARENT_SCOPE) if(NOT BUILD_SHARED_LIBS) set(_LIBUI_STATIC_RES ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/libui.res PARENT_SCOPE) @@ -72,7 +73,7 @@ macro(_handle_static) endif() # TODO this full path feels hacky add_custom_command( - TARGET libui PRE_LINK + TARGET libui POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy $/CMakeFiles/libui.dir/windows/resources.rc.${_res_suffix} ${_LIBUI_STATIC_RES} COMMENT "Copying libui.res") diff --git a/windows/resources.rc b/windows/resources.rc index 989dfc91..c465ee63 100644 --- a/windows/resources.rc +++ b/windows/resources.rc @@ -2,6 +2,8 @@ #include "winapi.hpp" #include "resources.hpp" +// TODO change and pin down IDs for static linking; document them + // this is a UTF-8 file #pragma code_page(65001) From c31699e4cdfeede258ae233c556c4b0f3e994184 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 4 Jun 2016 14:36:55 -0400 Subject: [PATCH 0224/1329] Pinned down resource numbers now. --- doc/winstatic | 1 + windows/resources.hpp | 8 +++++--- windows/resources.rc | 2 -- 3 files changed, 6 insertions(+), 5 deletions(-) create mode 100644 doc/winstatic diff --git a/doc/winstatic b/doc/winstatic new file mode 100644 index 00000000..5f163cfd --- /dev/null +++ b/doc/winstatic @@ -0,0 +1 @@ +libui uses resources starting at 29000 diff --git a/windows/resources.hpp b/windows/resources.hpp index 96bd5cf8..4ae54725 100644 --- a/windows/resources.hpp +++ b/windows/resources.hpp @@ -1,8 +1,10 @@ // 30 may 2015 -#define rcTabPageDialog 100 -#define rcFontDialog 101 -#define rcColorDialog 102 +#define rcTabPageDialog 29000 +#define rcFontDialog 29001 +#define rcColorDialog 29002 + +// TODO normalize these #define rcFontFamilyCombobox 1000 #define rcFontStyleCombobox 1001 diff --git a/windows/resources.rc b/windows/resources.rc index c465ee63..989dfc91 100644 --- a/windows/resources.rc +++ b/windows/resources.rc @@ -2,8 +2,6 @@ #include "winapi.hpp" #include "resources.hpp" -// TODO change and pin down IDs for static linking; document them - // this is a UTF-8 file #pragma code_page(65001) From cfc92c64acee88e56e41bf55b890378e62e5bf0e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 4 Jun 2016 14:59:26 -0400 Subject: [PATCH 0225/1329] Worked around the OS X issue for now. Let's go! --- CMakeLists.txt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 266ef0fc..69b810d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,6 @@ # 3 june 2016 cmake_minimum_required(VERSION 2.8.11) -# TODOs: -# - Darwin, Unix: static linking temporary target makes PUBLIC properties not propagate - # the docs say we need to set this up prior to project() set(CMAKE_OSX_DEPLOYMENT_TARGET "10.8") @@ -155,6 +152,14 @@ macro(_add_exec _name) if(NOT BUILD_SHARED_LIBS) target_link_libraries(${_name} ${_LIBUI_LIBS}) endif() + + # TODOfor some reason these don't propagate + if(NOT WIN32) + target_include_directories(${_name} + PUBLIC .) + target_compile_options(${_name} + PUBLIC ${_COMMON_CFLAGS}) + endif() endmacro() add_subdirectory("test") add_subdirectory("examples") From fac03b050bc79bf3b69e8d96c67bd612744fb0ef Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 4 Jun 2016 15:31:29 -0400 Subject: [PATCH 0226/1329] More TODOs. --- CMakeLists.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 69b810d6..a9004197 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,13 @@ cmake_minimum_required(VERSION 2.8.11) # the docs say we need to set this up prior to project() set(CMAKE_OSX_DEPLOYMENT_TARGET "10.8") +# we want to disable incremental linking +# see also: +# - https://github.com/bulletphysics/bullet3/blob/master/CMakeLists.txt#L43 +# - https://cmake.org/pipermail/cmake/2010-February/035174.html +# this must also go before project() +set(MSVC_INCREMENTAL_DEFAULT ON) + project(libui LANGUAGES C CXX) option(BUILD_SHARED_LIBS "Whether to build libui as a shared library or a static library" ON) @@ -68,6 +75,9 @@ if(MSVC) ) # TODO autogenerate a .def file? + + # more incremental linking fixes + # TODO actually get rid of incremental linking here else() set(_COMMON_CFLAGS -Wall -Wextra -pedantic From cb5fa98e756a6af7d3e5add19c6ad9dedfcbf777 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 4 Jun 2016 15:40:59 -0400 Subject: [PATCH 0227/1329] Let's start removing the old files, putting in any missed bells and whistles. --- CMakeLists.txt | 3 + migrate_build/CMakeLists.txt | 133 -------------------------- migrate_build/common_CMakeLists.txt | 15 --- migrate_build/darwin_CMakeLists.txt | 44 --------- migrate_build/examples_CMakeLists.txt | 22 ----- migrate_build/test_CMakeLists.txt | 26 ----- migrate_build/unix_CMakeLists.txt | 48 ---------- migrate_build/windows_CMakeLists.txt | 64 ------------- 8 files changed, 3 insertions(+), 352 deletions(-) delete mode 100644 migrate_build/CMakeLists.txt delete mode 100644 migrate_build/common_CMakeLists.txt delete mode 100644 migrate_build/darwin_CMakeLists.txt delete mode 100644 migrate_build/examples_CMakeLists.txt delete mode 100644 migrate_build/test_CMakeLists.txt delete mode 100644 migrate_build/unix_CMakeLists.txt delete mode 100644 migrate_build/windows_CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index a9004197..f3e4771b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,6 +89,9 @@ else() string(APPEND CMAKE_C_FLAGS " --std=c99") string(APPEND CMAKE_CXX_FLAGS " --std=c++11") + set(_COMMON_LDFLAGS + -fvisibility=hidden + ) endif() # problem: diff --git a/migrate_build/CMakeLists.txt b/migrate_build/CMakeLists.txt deleted file mode 100644 index 02f7e8e2..00000000 --- a/migrate_build/CMakeLists.txt +++ /dev/null @@ -1,133 +0,0 @@ -# 30 may 2016 -cmake_minimum_required(VERSION 2.8.12) - -# TODOs -# - ensure all set_target_properties() calls are approrpiately set, APPEND, and APPEND_STRING - -# set up our configurations -set(CMAKE_CONFIGURATION_TYPES Debug Static Release ReleaseStatic) -# we load the variables after calling project() -# default to Debug if no configuration specified -if(NOT CMAKE_BUILD_TYPE) - # the CACHE FORCE is necessary for this to work properly - set(CMAKE_BUILD_TYPE Debug CACHE STRING "Build type; one of: Debug Release Static ReleaseStatic" FORCE) -endif() -# and save whether this is shared in a variable -if(${CMAKE_BUILD_TYPE} STREQUAL "Debug") - set(_SHARED TRUE) -elseif(${CMAKE_BUILD_TYPE} STREQUAL "Release") - set(_SHARED TRUE) -endif() - -# and we need to set this up prior to project() too -set(CMAKE_OSX_DEPLOYMENT_TARGET "10.8") - -project(libui) - -# TODO can this be above the project()? -if(WIN32) - if(NOT MSVC) - if(_SHARED) - message(FATAL_ERROR - "Sorry, libui for Windows cannot be built as a DLL with MinGW. You will need to either build as a static library or build with MSVC.") - endif() - endif() -endif() - -# now that we called project(), load our config variables -macro(cfgcopy _prefix) - set(${_prefix}_STATIC "${${_prefix}_DEBUG}") - set(${_prefix}_RELEASESTATIC "${${_prefix}_RELEASE}") -endmacro() -cfgcopy(CMAKE_C_FLAGS) -cfgcopy(CMAKE_CXX_FLAGS) -cfgcopy(CMAKE_SHARED_LINKER_FLAGS) -cfgcopy(CMAKE_STATIC_LINKER_FLAGS) -cfgcopy(CMAKE_EXE_LINKER_FLAGS) - -macro(append _var _val) - set(${_var} "${${_var}} ${_val}") -endmacro() -macro(append2 _var1 _var2 _val) - append(${_var1} "${_val}") - append(${_var2} "${_val}") -endmacro() - -if(MSVC) - append2(CMAKE_C_FLAGS CMAKE_CXX_FLAGS - "/W4 /wd4100 /bigobj /RTC1 /RTCs /RTCu") - - # shut the compiler up in some cases - # LONGTERM still needed? - append(CMAKE_CXX_FLAGS " /EHsc") - - append(_LIBUI_CFLAGS " ${_PLATFORM_CFLAGS}") - - # note the /MANIFEST:NO (which must be / and uppercase); thanks FraGag (https://github.com/andlabs/libui/issues/93#issuecomment-223183436) - # also don't apply to CMAKE_STATIC_LINKER_FLAGS; those are passed to a different tool that doesn't support them - append2(CMAKE_SHARED_LINKER_FLAGS CMAKE_EXE_LINKER_FLAGS - " /LARGEADDRESSAWARE /INCREMENTAL:NO /MANIFEST:NO") -else() - append2(CMAKE_C_FLAGS CMAKE_CXX_FLAGS - " -Wall -Wextra -pedantic -Wno-unused-parameter -Wno-switch") - - if(NOT WIN32) - append(CMAKE_C_FLAGS_DEBUG " -fPIC") - append(CMAKE_CXX_FLAGS_DEBUG " -fPIC") - append(CMAKE_SHARED_LINKER_FLAGS_DEBUG " -fPIC") - append(CMAKE_EXE_LINKER_FLAGS_DEBUG " -fPIC") - append(CMAKE_C_FLAGS_RELEASE " -fPIC") - append(CMAKE_CXX_FLAGS_RELEASE " -fPIC") - append(CMAKE_SHARED_LINKER_FLAGS_RELEASE " -fPIC") - append(CMAKE_EXE_LINKER_FLAGS_RELEASE " -fPIC") - endif() - - if(WIN32) - append(_LIBUI_CFLAGS " ${_PLATFORM_CFLAGS}") - else() - append(_LIBUI_CFLAGS " -fvisibility=hidden ${_PLATFORM_CFLAGS}") - endif() - - append(CMAKE_SHARED_LINKER_FLAGS " -fvisibility=hidden") - # don't amend CMAKE_STATIC_LINKER_FLAGS; that's for ar -endif() - -if(NOT _SHARED) - append(_LIBUI_CFLAGS " -D_UI_STATIC") -endif() - -add_subdirectory("common") -add_subdirectory("${_OSDIR}") -if(_SHARED) - add_library(libui SHARED - $ - $ - ) - if(_SETVERSION) - set_target_properties(libui PROPERTIES - SOVERSION "${_VERSION}") - endif() - target_link_libraries(libui PRIVATE ${_PLATFORM_LIBS}) -else() - _add_static(libui - $ - $ - ) -endif() -# non-Windows platforms add an extra lib- at the beginning -if(NOT WIN32) - set_target_properties(libui PROPERTIES - OUTPUT_NAME ui) -endif() -# let cmake handle quoting and escaping for us -if(WIN32) - target_compile_definitions(libui - PRIVATE "_UI_EXTERN=__declspec(dllexport) extern") -else() - target_compile_definitions(libui - PRIVATE "_UI_EXTERN=__attribute__((visibility(\"default\"))) extern") -endif() - -add_subdirectory("test") - -add_subdirectory("examples") diff --git a/migrate_build/common_CMakeLists.txt b/migrate_build/common_CMakeLists.txt deleted file mode 100644 index cc93822d..00000000 --- a/migrate_build/common_CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -# 31 may 2016 - -include_directories(.. .) - -add_library(libui-common OBJECT - areaevents.c - control.c - debug.c - matrix.c - shouldquit.c - userbugs.c -) -set_target_properties(libui-common PROPERTIES - COMPILE_FLAGS "${_LIBUI_CFLAGS}" -) diff --git a/migrate_build/darwin_CMakeLists.txt b/migrate_build/darwin_CMakeLists.txt deleted file mode 100644 index d303bf5e..00000000 --- a/migrate_build/darwin_CMakeLists.txt +++ /dev/null @@ -1,44 +0,0 @@ -# 31 may 2016 - -include_directories(.. . ../common) - -add_library(libui-darwin OBJECT - alloc.m - area.m - areaevents.m - autolayout.m - box.m - button.m - checkbox.m - colorbutton.m - combobox.m - control.m - datetimepicker.m - debug.m - draw.m - drawtext.m - editablecombo.m - entry.m - fontbutton.m - group.m - label.m - main.m - map.m - menu.m - multilineentry.m - progressbar.m - radiobuttons.m - scrollview.m - separator.m - slider.m - spinbox.m - stddialogs.m - tab.m - text.m - util.m - window.m -) -set_target_properties(libui-darwin PROPERTIES - COMPILE_FLAGS "${_LIBUI_CFLAGS}" -) - diff --git a/migrate_build/examples_CMakeLists.txt b/migrate_build/examples_CMakeLists.txt deleted file mode 100644 index af34dfa0..00000000 --- a/migrate_build/examples_CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -# 1 june 2016 - -include_directories(..) - -_add_exec(controlgallery - controlgallery/main.c -) - -_add_exec(histogram - histogram/main.c -) - -_add_exec(cpp-multithread - cpp-multithread/main.cpp -) -target_link_libraries(cpp-multithread pthread) - -add_custom_target(examples - DEPENDS - controlgallery - histogram - cpp-multithread) diff --git a/migrate_build/test_CMakeLists.txt b/migrate_build/test_CMakeLists.txt deleted file mode 100644 index 6c2b2daf..00000000 --- a/migrate_build/test_CMakeLists.txt +++ /dev/null @@ -1,26 +0,0 @@ -# 1 june 2016 - -include_directories(.. .) - -_add_exec(tester - drawtests.c - main.c - menus.c - page1.c - page10.c - page11.c - page12.c - page13.c - page2.c - page3.c - page4.c - page5.c - page6.c - page7.c - page7a.c - page7b.c - page7c.c - page8.c - page9.c - spaced.c -) diff --git a/migrate_build/unix_CMakeLists.txt b/migrate_build/unix_CMakeLists.txt deleted file mode 100644 index e36a66b6..00000000 --- a/migrate_build/unix_CMakeLists.txt +++ /dev/null @@ -1,48 +0,0 @@ -# 1 june 2016 - -include_directories(.. . ../common) - -add_library(libui-unix OBJECT - alloc.c - area.c - box.c - button.c - checkbox.c - child.c - colorbutton.c - combobox.c - control.c - datetimepicker.c - debug.c - draw.c - drawmatrix.c - drawpath.c - drawtext.c - editablecombo.c - entry.c - fontbutton.c - graphemes.c - group.c - label.c - main.c - menu.c - multilineentry.c - progressbar.c - radiobuttons.c - separator.c - slider.c - spinbox.c - stddialogs.c - tab.c - text.c - util.c - window.c -) -set_target_properties(libui-unix PROPERTIES - COMPILE_FLAGS "${_LIBUI_CFLAGS}" -) - -# thanks to Mr-Hide in irc.freenode.net/#cmake -macro(_add_static _name) - -endmacro() diff --git a/migrate_build/windows_CMakeLists.txt b/migrate_build/windows_CMakeLists.txt deleted file mode 100644 index 3ddae42a..00000000 --- a/migrate_build/windows_CMakeLists.txt +++ /dev/null @@ -1,64 +0,0 @@ -# 1 june 2016 - -include_directories(.. . ../common) - -add_library(libui-windows OBJECT - alloc.cpp - area.cpp - areadraw.cpp - areaevents.cpp - areascroll.cpp - areautil.cpp - box.cpp - button.cpp - checkbox.cpp - colorbutton.cpp - colordialog.cpp - combobox.cpp - container.cpp - control.cpp - d2dscratch.cpp - datetimepicker.cpp - debug.cpp - draw.cpp - drawmatrix.cpp - drawpath.cpp - drawtext.cpp - dwrite.cpp - editablecombo.cpp - entry.cpp - events.cpp - fontbutton.cpp - fontdialog.cpp - graphemes.cpp - group.cpp - init.cpp - label.cpp - main.cpp - menu.cpp - multilineentry.cpp - parent.cpp - progressbar.cpp - radiobuttons.cpp - separator.cpp - sizing.cpp - slider.cpp - spinbox.cpp - stddialogs.cpp - tab.cpp - tabpage.cpp - text.cpp - utf16.cpp - utilwin.cpp - window.cpp - winpublic.cpp - winutil.cpp - resources.rc -) -set_target_properties(libui-windows PROPERTIES - COMPILE_FLAGS "${_LIBUI_CFLAGS}" -) - -macro(_add_static _name) - add_library(${_name} STATIC "${ARGN}") -endmacro() From d57035e59d7f12e8b003ae0e3d457992b7a440cf Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 4 Jun 2016 15:49:43 -0400 Subject: [PATCH 0228/1329] More deleting old build systems. --- CMakeLists.txt | 6 ++ migrate_build/GNUmakefile | 77 ----------------------- migrate_build/build/GNUbasemsvc.mk | 0 migrate_build/common/GNUfiles.mk | 13 ---- migrate_build/darwin/GNUosspecificlink.mk | 15 ----- migrate_build/test/GNUfiles.mk | 31 --------- migrate_build/unix/GNUosspecificlink.mk | 15 ----- migrate_build/windows/GNUfiles.mk | 0 migrate_build/windows/GNUinstall.mk | 3 - migrate_build/windows/GNUosspecific.mk | 0 10 files changed, 6 insertions(+), 154 deletions(-) delete mode 100644 migrate_build/GNUmakefile delete mode 100644 migrate_build/build/GNUbasemsvc.mk delete mode 100644 migrate_build/common/GNUfiles.mk delete mode 100644 migrate_build/darwin/GNUosspecificlink.mk delete mode 100644 migrate_build/test/GNUfiles.mk delete mode 100644 migrate_build/unix/GNUosspecificlink.mk delete mode 100644 migrate_build/windows/GNUfiles.mk delete mode 100644 migrate_build/windows/GNUinstall.mk delete mode 100644 migrate_build/windows/GNUosspecific.mk diff --git a/CMakeLists.txt b/CMakeLists.txt index f3e4771b..e1d4171e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,12 @@ # 3 june 2016 cmake_minimum_required(VERSION 2.8.11) +# TODOs +# - silence entering/leaving messages? +# - uname -s for more refined OS control +# - Haiku for haiku +# - debian DESTDIR? https://github.com/andlabs/libui/pull/10 + # the docs say we need to set this up prior to project() set(CMAKE_OSX_DEPLOYMENT_TARGET "10.8") diff --git a/migrate_build/GNUmakefile b/migrate_build/GNUmakefile deleted file mode 100644 index 631495a4..00000000 --- a/migrate_build/GNUmakefile +++ /dev/null @@ -1,77 +0,0 @@ -# 16 october 2015 - -# TODO http://stackoverflow.com/questions/4122831/disable-make-builtin-rules-and-variables-from-inside-the-make-file - -# silence entering/leaving messages -MAKEFLAGS += --no-print-directory - -OUTDIR = out -OBJDIR = .obj - -# MAME does this so :/ -ifeq ($(OS),Windows_NT) - OS = windows -endif - -ifndef OS - UNAME = $(shell uname -s) - ifeq ($(UNAME),Darwin) - OS = darwin - else ifeq ($(UNAME),Haiku) - OS = haiku - else - OS = unix - endif -endif - -# default is to build with debug symbols -ifndef RELEASE - RELEASE = 0 -endif - -# parameters -export OS -# TODO CC, CXX, RC, LD -export CFLAGS -export CXXFLAGS -# TODO RCFLAGS -export LDFLAGS -export RELEASE -export EXAMPLE -export PREFIX - -# for Debian - see https://github.com/andlabs/libui/pull/10 -export DESTDIR - -# other important variables -export OBJDIR -export OUTDIR -export STATIC - -ifneq (,$(STATIC)) -STATICLIB = STATICLIB=1 -endif - -libui: - @$(MAKE) -f build/GNUmakefile.libui inlibuibuild=1 $(STATICLIB) - -clean: - rm -rf $(OBJDIR) $(OUTDIR) - -test: libui - @$(MAKE) -f build/GNUmakefile.test inlibuibuild=1 - -# TODO provide a build option for the queuemaintest - -example: libui - @$(MAKE) -f build/GNUmakefile.example inlibuibuild=1 - -examples: - @$(MAKE) -f GNUmakefile example EXAMPLE=controlgallery - @$(MAKE) -f GNUmakefile example EXAMPLE=histogram - @$(MAKE) -f GNUmakefile example EXAMPLE=cpp-multithread - -.PHONY: examples - -install: - @$(MAKE) -f build/GNUmakefile.libui install inlibuibuild=1 diff --git a/migrate_build/build/GNUbasemsvc.mk b/migrate_build/build/GNUbasemsvc.mk deleted file mode 100644 index e69de29b..00000000 diff --git a/migrate_build/common/GNUfiles.mk b/migrate_build/common/GNUfiles.mk deleted file mode 100644 index 465c76e8..00000000 --- a/migrate_build/common/GNUfiles.mk +++ /dev/null @@ -1,13 +0,0 @@ -# 16 october 2015 - -CFILES += \ - common/areaevents.c \ - common/control.c \ - common/debug.c \ - common/matrix.c \ - common/shouldquit.c \ - common/userbugs.c - -HFILES += \ - common/controlsigs.h \ - common/uipriv.h diff --git a/migrate_build/darwin/GNUosspecificlink.mk b/migrate_build/darwin/GNUosspecificlink.mk deleted file mode 100644 index 0b9087e6..00000000 --- a/migrate_build/darwin/GNUosspecificlink.mk +++ /dev/null @@ -1,15 +0,0 @@ -# 28 may 2016 - -$(OUT): $(OFILES) | $(OUTDIR) -ifeq (,$(STATICLIB)) - @$(reallinker) -o $(OUT) $(OFILES) $(LDFLAGS) -ifeq ($(USESSONAME),1) - @ln -sf $(NAME)$(SUFFIX) $(OUTNOSONAME) -endif -else - nm -m $(OFILES) | sed -E -n 's/^[0-9a-f]* \([A-Z_]+,[a-z_]+\) external //p' > $(OBJDIR)/symbols - $(LD) -exported_symbols_list $(OBJDIR)/symbols -r $(OFILES) -o $(OUT:%.a=%.o) - $(AR) rcs $(OUT) $(OUT:%.a=%.o) -endif - @echo ====== Linked $(OUT) - diff --git a/migrate_build/test/GNUfiles.mk b/migrate_build/test/GNUfiles.mk deleted file mode 100644 index 94f1fa23..00000000 --- a/migrate_build/test/GNUfiles.mk +++ /dev/null @@ -1,31 +0,0 @@ -# 22 april 2015 - -CFILES += \ - test/drawtests.c \ - test/main.c \ - test/menus.c \ - test/page1.c \ - test/page2.c \ - test/page3.c \ - test/page4.c \ - test/page5.c \ - test/page6.c \ - test/page7.c \ - test/page7a.c \ - test/page7b.c \ - test/page7c.c \ - test/page8.c \ - test/page9.c \ - test/page10.c \ - test/page11.c \ - test/page12.c \ - test/page13.c \ - test/spaced.c - -HFILES += \ - test/test.h - -ifeq ($(OS),windows) -RCFILES += \ - test/resources.rc -endif diff --git a/migrate_build/unix/GNUosspecificlink.mk b/migrate_build/unix/GNUosspecificlink.mk deleted file mode 100644 index 573b289f..00000000 --- a/migrate_build/unix/GNUosspecificlink.mk +++ /dev/null @@ -1,15 +0,0 @@ -# 28 may 2016 - -$(OUT): $(OFILES) | $(OUTDIR) -ifeq (,$(STATICLIB)) - @$(reallinker) -o $(OUT) $(OFILES) $(LDFLAGS) -ifeq ($(USESSONAME),1) - @ln -sf $(NAME)$(SUFFIX) $(OUTNOSONAME) -endif -else - $(LD) -r $(OFILES) -o $(OUT:%.a=%.o) - objcopy --localize-hidden $(OUT:%.a=%.o) - $(AR) rcs $(OUT) $(OUT:%.a=%.o) -endif - @echo ====== Linked $(OUT) - diff --git a/migrate_build/windows/GNUfiles.mk b/migrate_build/windows/GNUfiles.mk deleted file mode 100644 index e69de29b..00000000 diff --git a/migrate_build/windows/GNUinstall.mk b/migrate_build/windows/GNUinstall.mk deleted file mode 100644 index 1d783c01..00000000 --- a/migrate_build/windows/GNUinstall.mk +++ /dev/null @@ -1,3 +0,0 @@ -install: - @echo "No install for windows !" - @exit 1 diff --git a/migrate_build/windows/GNUosspecific.mk b/migrate_build/windows/GNUosspecific.mk deleted file mode 100644 index e69de29b..00000000 From 0bca3e21da57c7e7a13153c849eb92d8a342b369 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 4 Jun 2016 15:52:55 -0400 Subject: [PATCH 0229/1329] Even more old build tools removal. Next will be a one-fell-swoop thing. --- migrate_build/darwin/GNUfiles.mk | 61 --------------------------- migrate_build/darwin/GNUinstall.mk | 9 ---- migrate_build/darwin/GNUosspecific.mk | 20 --------- migrate_build/unix/GNUinstall.mk | 8 ---- migrate_build/unix/GNUosspecific.mk | 22 ---------- 5 files changed, 120 deletions(-) delete mode 100644 migrate_build/darwin/GNUinstall.mk delete mode 100644 migrate_build/darwin/GNUosspecific.mk delete mode 100644 migrate_build/unix/GNUinstall.mk delete mode 100644 migrate_build/unix/GNUosspecific.mk diff --git a/migrate_build/darwin/GNUfiles.mk b/migrate_build/darwin/GNUfiles.mk index 9b9ed693..48c19c7e 100644 --- a/migrate_build/darwin/GNUfiles.mk +++ b/migrate_build/darwin/GNUfiles.mk @@ -1,64 +1,3 @@ -# 28 april 2015 - -MFILES += \ - darwin/alloc.m \ - darwin/area.m \ - darwin/areaevents.m \ - darwin/autolayout.m \ - darwin/box.m \ - darwin/button.m \ - darwin/checkbox.m \ - darwin/colorbutton.m \ - darwin/combobox.m \ - darwin/control.m \ - darwin/datetimepicker.m \ - darwin/debug.m \ - darwin/draw.m \ - darwin/drawtext.m \ - darwin/editablecombo.m \ - darwin/entry.m \ - darwin/fontbutton.m \ - darwin/group.m \ - darwin/label.m \ - darwin/main.m \ - darwin/map.m \ - darwin/menu.m \ - darwin/multilineentry.m \ - darwin/progressbar.m \ - darwin/radiobuttons.m \ - darwin/scrollview.m \ - darwin/separator.m \ - darwin/slider.m \ - darwin/spinbox.m \ - darwin/stddialogs.m \ - darwin/tab.m \ - darwin/text.m \ - darwin/util.m \ - darwin/window.m - -HFILES += \ - darwin/uipriv_darwin.h - -# LONGTERM split into a separate file or put in GNUmakefile.libui somehow? - -# flags for Cocoa -LDFLAGS += $(NATIVE_UI_LDFLAGS) - -# flags for OS X versioning -CFLAGS += \ - -mmacosx-version-min=10.8 \ - -DMACOSX_DEPLOYMENT_TARGET=10.8 -CXXFLAGS += \ - -mmacosx-version-min=10.8 \ - -DMACOSX_DEPLOYMENT_TARGET=10.8 -LDFLAGS += \ - -mmacosx-version-min=10.8 - -ifeq (,$(STATIC)) -# flags for building a shared library -LDFLAGS += \ - -dynamiclib -endif # on warning about undefined symbols: # the gcc flags don't work with Apple's linker diff --git a/migrate_build/darwin/GNUinstall.mk b/migrate_build/darwin/GNUinstall.mk deleted file mode 100644 index 41df8837..00000000 --- a/migrate_build/darwin/GNUinstall.mk +++ /dev/null @@ -1,9 +0,0 @@ -ifndef PREFIX - PREFIX=/usr -endif - -# Incorrect for Mac Os X, this should be easy to fix -install: $(OUT) - cp $(OUT) $(DESTDIR)$(PREFIX)/lib/libui.0.dylib - ln -s libui.0.dylib $(DESTDIR)$(PREFIX)/lib/libui.dylib - cp ui.h ui_$(OS).h $(DESTDIR)$(PREFIX)/include/ diff --git a/migrate_build/darwin/GNUosspecific.mk b/migrate_build/darwin/GNUosspecific.mk deleted file mode 100644 index 9f948e73..00000000 --- a/migrate_build/darwin/GNUosspecific.mk +++ /dev/null @@ -1,20 +0,0 @@ -# 16 october 2015 - -EXESUFFIX = -LIBSUFFIX = .dylib -OSHSUFFIX = .h -STATICLIBSUFFIX = .a -TOOLCHAIN = gcc - -USESSONAME = 1 -SOVERSION = $(SOVERSIONA) -SONAMEEXT = .$(SOVERSION)$(LIBSUFFIX) -# note the explicit need for @rpath -# LONGTERM -current_version, -compatibility_version -SONAMEFLAG = -Wl,-install_name,@rpath/ - -NATIVE_UI_LDFLAGS += \ - -lobjc \ - -framework Foundation \ - -framework AppKit - diff --git a/migrate_build/unix/GNUinstall.mk b/migrate_build/unix/GNUinstall.mk deleted file mode 100644 index 634dbc06..00000000 --- a/migrate_build/unix/GNUinstall.mk +++ /dev/null @@ -1,8 +0,0 @@ -ifndef PREFIX - PREFIX=/usr -endif - -install: $(OUT) - cp $(OUT) $(DESTDIR)$(PREFIX)/lib/libui.so.0 - ln -fs libui.so.0 $(DESTDIR)$(PREFIX)/lib/libui.so - cp ui.h ui_$(OS).h $(DESTDIR)$(PREFIX)/include/ diff --git a/migrate_build/unix/GNUosspecific.mk b/migrate_build/unix/GNUosspecific.mk deleted file mode 100644 index 77b91fbb..00000000 --- a/migrate_build/unix/GNUosspecific.mk +++ /dev/null @@ -1,22 +0,0 @@ -# 16 october 2015 - -EXESUFFIX = -LIBSUFFIX = .so -OSHSUFFIX = .h -STATICLIBSUFFIX = .a -TOOLCHAIN = gcc - -# LONGTERM clean up all the NAMEs and SUFFIXs and NOSOSUFFIXs or whatever it was -USESSONAME = 1 -SOVERSION = $(SOVERSION0) -SONAMEEXT = $(LIBSUFFIX).$(SOVERSION) -# this is not gcc-global because OS X uses a different filename format -SONAMEFLAG = -Wl,-soname, - -NATIVE_UI_CFLAGS = \ - `pkg-config --cflags gtk+-3.0` -NATIVE_UI_CXXFLAGS = \ - `pkg-config --cflags gtk+-3.0` -NATIVE_UI_LDFLAGS = \ - `pkg-config --libs gtk+-3.0` -lm -ldl - From acbeb86c7ff70a69f159273a63b607802b9599e2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 4 Jun 2016 18:52:40 -0400 Subject: [PATCH 0230/1329] Pruned out and merged in th erest of the migrate_build files. --- CMakeLists.txt | 18 ++++++- migrate_build/build/GNUbasegcc.mk | 53 --------------------- migrate_build/build/GNUmakefile.example | 62 ------------------------ migrate_build/build/GNUmakefile.libui | 50 -------------------- migrate_build/build/GNUmakefile.test | 49 ------------------- migrate_build/darwin/GNUfiles.mk | 4 -- migrate_build/unix/GNUfiles.mk | 63 ------------------------- 7 files changed, 16 insertions(+), 283 deletions(-) delete mode 100644 migrate_build/build/GNUbasegcc.mk delete mode 100644 migrate_build/build/GNUmakefile.example delete mode 100644 migrate_build/build/GNUmakefile.libui delete mode 100644 migrate_build/build/GNUmakefile.test delete mode 100644 migrate_build/darwin/GNUfiles.mk delete mode 100644 migrate_build/unix/GNUfiles.mk diff --git a/CMakeLists.txt b/CMakeLists.txt index e1d4171e..4ad81045 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -138,15 +138,29 @@ if(BUILD_SHARED_LIBS) endif() if(NOT BUILD_SHARED_LIBS) _handle_static() - # TODO this really should be PRIVATE but I haven't fully figured this out + # TODO figure out a way to tell libui that it's static target_compile_definitions(${_LIBUINAME} - PUBLIC _UI_STATIC) + PUBLIC _UI_STATIC) endif() if(NOT MSVC) # on non-MSVC compilers cmake adds an extra lib- # note that we apply this to libui, not to any intermediates set_target_properties(libui PROPERTIES OUTPUT_NAME ui) + + # flags for warning on undefined symbols + # TODO figure out why FreeBSD follows linked libraries here + # TODO figure out MSVC equivalents + if(BUILD_SHARED_LIBS) + if(NOT (${CMAKE_SYSTEM_NAME} STREQUAL FreeBSD)) + # on OS X we don't need to do this; Apple's linker warns about undefined symbols in -shared builds! + if(NOT APPLE) + target_link_libraries(libui + PRIVATE -Wl,--no-undefined -Wl,--no-allow-shlib-undefined + ) + endif() + endif() + endif() endif() if(BUILD_SHARED_LIBS) if(_HASVERSION) diff --git a/migrate_build/build/GNUbasegcc.mk b/migrate_build/build/GNUbasegcc.mk deleted file mode 100644 index 6e834d15..00000000 --- a/migrate_build/build/GNUbasegcc.mk +++ /dev/null @@ -1,53 +0,0 @@ -# 16 october 2015 - -# Global flags. - -CFLAGS += \ - --std=c99 - -CXXFLAGS += \ - --std=c++11 - -# Build rules. - -OFILES = \ - $(subst /,_,$(CFILES)) \ - $(subst /,_,$(CXXFILES)) \ - $(subst /,_,$(MFILES)) \ - $(subst /,_,$(RCFILES)) - -OFILES := $(OFILES:%=$(OBJDIR)/%.o) - -OUT = $(OUTDIR)/$(NAME)$(SUFFIX) -OUTNOSONAME = $(OUTDIR)/$(NAME)$(LIBSUFFIX) - -# TODO allow using LD -# LD is defined by default so we need a way to override the default define without blocking a user define -ifeq ($(CXXFILES),) - reallinker = $(CC) -else - reallinker = $(CXX) -endif - -include $(OS)/GNUosspecificlink.mk - -.SECONDEXPANSION: - -$(OBJDIR)/%.c.o: $$(subst _,/,%).c $(HFILES) | $(OBJDIR) - @$(CC) -o $@ -c $< $(CFLAGS) - @echo ====== Compiled $< - -$(OBJDIR)/%.cpp.o: $$(subst _,/,%).cpp $(HFILES) | $(OBJDIR) - @$(CXX) -o $@ -c $< $(CXXFLAGS) - @echo ====== Compiled $< - -$(OBJDIR)/%.m.o: $$(subst _,/,%).m $(HFILES) | $(OBJDIR) - @$(CC) -o $@ -c $< $(CFLAGS) - @echo ====== Compiled $< - -$(OBJDIR)/%.rc.o: $$(subst _,/,%).rc $(HFILES) | $(OBJDIR) - @$(RC) $(RCFLAGS) $< $@ - @echo ====== Compiled $< - -$(OBJDIR) $(OUTDIR): - @mkdir -p $@ diff --git a/migrate_build/build/GNUmakefile.example b/migrate_build/build/GNUmakefile.example deleted file mode 100644 index 83932910..00000000 --- a/migrate_build/build/GNUmakefile.example +++ /dev/null @@ -1,62 +0,0 @@ -# 16 october 2015 - -ifndef inlibuibuild -$(error Do not run these makefiles directly.) -endif -ifndef EXAMPLE -$(error You must specify an example to build by adding EXAMPLE=name to the command line.) -endif - -include $(OS)/GNUosspecific.mk - -ifneq ($(findstring cpp-,$(EXAMPLE)),) -CXXFILES += \ - examples/$(EXAMPLE)/main.cpp -else -CFILES += \ - examples/$(EXAMPLE)/main.c -endif - -HFILES += \ - ui.h - -ifeq ($(OS),windows) -RCFILES += \ - examples/resources.rc -ifneq (,$(STATIC)) -RCFLAGS += -D _UI_STATIC -endif -endif - -NAME = $(EXAMPLE) -SUFFIX = $(EXESUFFIX) - -# TODO merge with the one in build/GNUmakefile.test -ifeq ($(TOOLCHAIN),gcc) - ifeq (,$(STATIC)) - LDFLAGS += -L$(OUTDIR) -lui - else - LDFLAGS += -L$(OUTDIR) -lui $(NATIVE_UI_LDFLAGS) - endif - # see build/GNUmakefile.test - ifeq ($(OS),darwin) - LDFLAGS += -Wl,-rpath,@executable_path/ - else - LDFLAGS += -Wl,-rpath,'$$ORIGIN' - endif - ifneq ($(findstring cpp-,$(EXAMPLE)),) - LDFLAGS += -pthread - endif -else - ifeq (,$(STATIC)) - # TODO is there an equivalent to -L? - LDFLAGS += $(OUTDIR)/libui.lib - else - LDFLAGS += $(OUTDIR)/libui.lib $(OUTDIR)/libui.res $(OUTDIR)/$(NAME).res $(NATIVE_UI_LDFLAGS) - endif -endif - -# executables are not shared libraries -USESSONAME = 0 - -include build/GNUbase$(TOOLCHAIN).mk diff --git a/migrate_build/build/GNUmakefile.libui b/migrate_build/build/GNUmakefile.libui deleted file mode 100644 index c4ac54d3..00000000 --- a/migrate_build/build/GNUmakefile.libui +++ /dev/null @@ -1,50 +0,0 @@ -# 16 october 2015 - -ifndef inlibuibuild -$(error Do not run these makefiles directly.) -endif - -# for GCC -SOVERSION0 = 0 -SOVERSIONA = A - -include $(OS)/GNUosspecific.mk -include common/GNUfiles.mk -include $(OS)/GNUfiles.mk - -HFILES += \ - ui.h \ - ui_$(OS)$(OSHSUFFIX) - -NAME = libui -ifeq (,$(STATIC)) -SUFFIX = $(LIBSUFFIX) -ifeq ($(USESSONAME),1) - SUFFIX = $(SONAMEEXT) -endif -else -SUFFIX = $(STATICLIBSUFFIX) -endif - -ifeq ($(TOOLCHAIN),gcc) - - LDFLAGS += \ - -fvisibility=hidden -else - # TODO autogenerate a .def file? -endif - -ifeq ($(RELEASE),1) - CFLAGS += -D_UI_RELEASE - CXXFLAGS += -D_UI_RELEASE -endif - -ifeq ($(USESSONAME),1) - LDFLAGS += $(SONAMEFLAG)$(NAME)$(SUFFIX) -endif - -include build/GNUbase$(TOOLCHAIN).mk - -# install rule is OS specific -# TODO probably better off making it a toolchain-wide rule -include $(OS)/GNUinstall.mk diff --git a/migrate_build/build/GNUmakefile.test b/migrate_build/build/GNUmakefile.test deleted file mode 100644 index 9e123977..00000000 --- a/migrate_build/build/GNUmakefile.test +++ /dev/null @@ -1,49 +0,0 @@ -# 16 october 2015 - -ifndef inlibuibuild -$(error Do not run these makefiles directly.) -endif - -include $(OS)/GNUosspecific.mk -include test/GNUfiles.mk - -HFILES += \ - ui.h - -NAME = test -SUFFIX = $(EXESUFFIX) - -ifeq ($(OS),windows) -ifneq (,$(STATIC)) -RCFLAGS += -D _UI_STATIC -endif -endif - -ifeq ($(TOOLCHAIN),gcc) - ifeq (,$(STATIC)) - LDFLAGS += -L$(OUTDIR) -lui - else - LDFLAGS += -L$(OUTDIR) -lui $(NATIVE_UI_LDFLAGS) - endif - # tell the dynamic loader to search in the executable path for shared objects - # note: OS X's linker complains if we say -rpath= instead of -rpath, - # also note that OS X doesn't use $ORIGIN - see http://jorgen.tjer.no/post/2014/05/20/dt-rpath-ld-and-at-rpath-dyld/ - # the extra slash is needed on OS X 10.7; have it around just to be correct - ifeq ($(OS),darwin) - LDFLAGS += -Wl,-rpath,@executable_path/ - else - LDFLAGS += -Wl,-rpath,'$$ORIGIN' - endif -else - ifeq (,$(STATIC)) - # TODO is there an equivalent to -L? - LDFLAGS += $(OUTDIR)/libui.lib - else - LDFLAGS += $(OUTDIR)/libui.lib $(OUTDIR)/libui.res $(OUTDIR)/$(NAME).res $(NATIVE_UI_LDFLAGS) - endif -endif - -# executables are not shared libraries -USESSONAME = 0 - -include build/GNUbase$(TOOLCHAIN).mk diff --git a/migrate_build/darwin/GNUfiles.mk b/migrate_build/darwin/GNUfiles.mk deleted file mode 100644 index 48c19c7e..00000000 --- a/migrate_build/darwin/GNUfiles.mk +++ /dev/null @@ -1,4 +0,0 @@ - -# on warning about undefined symbols: -# the gcc flags don't work with Apple's linker -# fortunately, we don't need any; Apple's linker warns about undefined symbols in -shared builds! diff --git a/migrate_build/unix/GNUfiles.mk b/migrate_build/unix/GNUfiles.mk deleted file mode 100644 index adc345ec..00000000 --- a/migrate_build/unix/GNUfiles.mk +++ /dev/null @@ -1,63 +0,0 @@ -# 22 april 2015 - -CFILES += \ - unix/alloc.c \ - unix/area.c \ - unix/box.c \ - unix/button.c \ - unix/checkbox.c \ - unix/child.c \ - unix/colorbutton.c \ - unix/combobox.c \ - unix/control.c \ - unix/datetimepicker.c \ - unix/debug.c \ - unix/draw.c \ - unix/drawmatrix.c \ - unix/drawpath.c \ - unix/drawtext.c \ - unix/editablecombo.c \ - unix/entry.c \ - unix/fontbutton.c \ - unix/graphemes.c \ - unix/group.c \ - unix/label.c \ - unix/main.c \ - unix/menu.c \ - unix/multilineentry.c \ - unix/progressbar.c \ - unix/radiobuttons.c \ - unix/separator.c \ - unix/slider.c \ - unix/spinbox.c \ - unix/stddialogs.c \ - unix/tab.c \ - unix/text.c \ - unix/util.c \ - unix/window.c - -HFILES += \ - unix/draw.h \ - unix/uipriv_unix.h - -# LONGTERM split into a separate file or put in GNUmakefile.libui somehow? - -# flags for GTK+ -CFLAGS += $(NATIVE_UI_CFLAGS) -CXXFLAGS += $(NATIVE_UI_CXXFLAGS) -LDFLAGS += $(NATIVE_UI_LDFLAGS) - -# flags for building a shared library -# OS X does support -shared but it has a preferred name for this so let's use that there instead; hence this is not gcc-global -ifeq (,$(STATIC)) -LDFLAGS += \ - -shared -endif - -# flags for warning on undefined symbols -# this is not gcc-global because OS X doesn't support these flags -# TODO figure out why FreeBSD follows linked libraries here -ifneq ($(shell uname -s),FreeBSD) -LDFLAGS += \ - -Wl,--no-undefined -Wl,--no-allow-shlib-undefined -endif From dac8aea49a0597613b671b24c39a9b0f007c8263 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 5 Jun 2016 00:20:22 -0400 Subject: [PATCH 0231/1329] Removed now-irrelevant stuff. --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index a06333c2..6fe224a0 100644 --- a/README.md +++ b/README.md @@ -74,8 +74,7 @@ This README is being written.
* GNU make 3.81 or newer (Xcode comes with this; on Windows you will need to get it yourself) * Windows: Microsoft Visual Studio 2013 or newer (2013 is needed for `va_copy()`) * MinGW is currently unsupported. MinGW-w64 support will be re-added once the following features come in: - * [Isolation awareness](https://msdn.microsoft.com/en-us/library/aa375197%28v=vs.85%29.aspx) - * Linker symbols for some functions such as `TaskDialog()` (which I thought I submitted...) + * [Isolation awareness](https://msdn.microsoft.com/en-us/library/aa375197%28v=vs.85%29.aspx), which is how you get themed controls from a DLL without needing a manifest * Unix: nothing specific * Mac OS X: nothing specific, so long as you can build Cocoa programs From b21ef44a055a0ffe432a38ecc0e405ace605df9c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 5 Jun 2016 10:43:04 -0400 Subject: [PATCH 0232/1329] Defaulted to debug mode. --- CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ad81045..4c3a53f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,12 @@ set(CMAKE_OSX_DEPLOYMENT_TARGET "10.8") # this must also go before project() set(MSVC_INCREMENTAL_DEFAULT ON) +# default to debug builds +# do this before project() just to be safe +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE DEBUG CACHE STRING "" FORCE) +endif() + project(libui LANGUAGES C CXX) option(BUILD_SHARED_LIBS "Whether to build libui as a shared library or a static library" ON) From b8d9218b7ff8d835f39a7072882560f23c9e9cbf Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 5 Jun 2016 11:50:42 -0400 Subject: [PATCH 0233/1329] More TODOs. --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c3a53f9..9c64e2af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,7 @@ cmake_minimum_required(VERSION 2.8.11) # - uname -s for more refined OS control # - Haiku for haiku # - debian DESTDIR? https://github.com/andlabs/libui/pull/10 +# - static libgcc in MinGW builds # the docs say we need to set this up prior to project() set(CMAKE_OSX_DEPLOYMENT_TARGET "10.8") From 14c8630d1c19f264e1873aeb11dfda33bd47ada2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 5 Jun 2016 13:40:32 -0400 Subject: [PATCH 0234/1329] Adjusted travis.yml. --- .travis.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index ec542ff9..a1b52520 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,10 @@ language: c script: - if [ "$TRAVIS_OS_NAME" == "linux" ]; then sudo apt-get update; fi - if [ "$TRAVIS_OS_NAME" == "linux" ]; then sudo apt-get install libgtk-3-dev -y || sudo apt-cache search libgtk3; fi - - make -f GNUmakefile - - make -f GNUmakefile test - - make -f GNUmakefile examples + - mkdir build + - cd build + - cmake .. -G "Unix Makefiles" + - make test examples + - rm -rf * + - cmake .. -G "Unix Makefiles" -DBUILD_SHARED_LIBS=OFF + - make test examples From 80a0ba1448d013cde6167df7c71fecffe7220e49 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 5 Jun 2016 13:43:18 -0400 Subject: [PATCH 0235/1329] tester not test. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index a1b52520..5c287abd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,7 @@ script: - mkdir build - cd build - cmake .. -G "Unix Makefiles" - - make test examples + - make tester examples - rm -rf * - cmake .. -G "Unix Makefiles" -DBUILD_SHARED_LIBS=OFF - - make test examples + - make tester examples From 044e10165d17892b143553e8f0e57aa371bc81b2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 5 Jun 2016 13:45:27 -0400 Subject: [PATCH 0236/1329] Added cmake --version for debugging OS X builds. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 5c287abd..e71145bf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,7 @@ script: - if [ "$TRAVIS_OS_NAME" == "linux" ]; then sudo apt-get install libgtk-3-dev -y || sudo apt-cache search libgtk3; fi - mkdir build - cd build + - cmake --version - cmake .. -G "Unix Makefiles" - make tester examples - rm -rf * From 10aa198ac32970c67c9fb7f196c6c8f85d024098 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 5 Jun 2016 13:48:51 -0400 Subject: [PATCH 0237/1329] string(APPEND) is too recent for travis's cmake (or 2.8.11, for that matter). --- .travis.yml | 1 - CMakeLists.txt | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index e71145bf..5c287abd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,6 @@ script: - if [ "$TRAVIS_OS_NAME" == "linux" ]; then sudo apt-get install libgtk-3-dev -y || sudo apt-cache search libgtk3; fi - mkdir build - cd build - - cmake --version - cmake .. -G "Unix Makefiles" - make tester examples - rm -rf * diff --git a/CMakeLists.txt b/CMakeLists.txt index 9c64e2af..3c081290 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -99,8 +99,8 @@ else() -fvisibility=hidden ) # don't use C_VERSION or CXX_VERSION because they use GNU standards - string(APPEND CMAKE_C_FLAGS " --std=c99") - string(APPEND CMAKE_CXX_FLAGS " --std=c++11") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --std=c99") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11") set(_COMMON_LDFLAGS -fvisibility=hidden From 538965b4ef3758b95e649462f8b129927983d964 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 5 Jun 2016 16:07:40 -0400 Subject: [PATCH 0238/1329] Fixed the MinGW ABI stuff again... --- windows/areautil.cpp | 2 +- windows/colordialog.cpp | 19 +++++++++++++++---- windows/d2dscratch.cpp | 2 +- windows/fontdialog.cpp | 4 ++-- windows/uipriv_windows.hpp | 2 ++ windows/winutil.cpp | 16 ++++++++++++++++ 6 files changed, 37 insertions(+), 8 deletions(-) diff --git a/windows/areautil.cpp b/windows/areautil.cpp index 8d2d7fc8..9dc72fbb 100644 --- a/windows/areautil.cpp +++ b/windows/areautil.cpp @@ -11,7 +11,7 @@ void loadAreaSize(uiArea *a, ID2D1RenderTarget *rt, double *width, double *heigh if (!a->scrolling) { if (rt == NULL) rt = a->rt; - size = rt->GetSize(); + size = realGetSize(rt); *width = size.width; *height = size.height; } diff --git a/windows/colordialog.cpp b/windows/colordialog.cpp index a8beca30..2efe72c8 100644 --- a/windows/colordialog.cpp +++ b/windows/colordialog.cpp @@ -309,7 +309,18 @@ static void drawGrid(ID2D1RenderTarget *rt, D2D1_RECT_F *fillRect) // mind the divisions; they represent the fact the original uses a viewport size.width = 100 / 10; size.height = 100 / 10; + // yay more ABI bugs +#ifdef _MSC_VER pformat = rt->GetPixelFormat(); +#else + { + typedef D2D1_PIXEL_FORMAT *(__stdcall ID2D1RenderTarget::* GetPixelFormatF)(D2D1_PIXEL_FORMAT *); + GetPixelFormatF gpf; + + gpf = (GetPixelFormatF) (&(rt->GetPixelFormat)); + (rt->*gpf)(&pformat); + } +#endif hr = rt->CreateCompatibleRenderTarget(&size, NULL, &pformat, D2D1_COMPATIBLE_RENDER_TARGET_OPTIONS_NONE, &brt); @@ -383,7 +394,7 @@ static void drawSVChooser(struct colorDialog *c, ID2D1RenderTarget *rt) ID2D1SolidColorBrush *markerBrush; HRESULT hr; - size = rt->GetSize(); + size = realGetSize(rt); rect.left = 0; rect.top = 0; rect.right = size.width; @@ -625,7 +636,7 @@ static void drawHSlider(struct colorDialog *c, ID2D1RenderTarget *rt) D2D1_POINT_2F center; HRESULT hr; - size = rt->GetSize(); + size = realGetSize(rt); rect.left = size.width / 6; // leftmost sixth for arrow rect.top = 0; rect.right = size.width; @@ -712,7 +723,7 @@ static void drawPreview(struct colorDialog *c, ID2D1RenderTarget *rt) ID2D1SolidColorBrush *brush; HRESULT hr; - size = rt->GetSize(); + size = realGetSize(rt); rect.left = 0; rect.top = 0; rect.right = size.width; @@ -769,7 +780,7 @@ static void drawOpacitySlider(struct colorDialog *c, ID2D1RenderTarget *rt) D2D1_POINT_2F center; HRESULT hr; - size = rt->GetSize(); + size = realGetSize(rt); rect.left = 0; rect.top = 0; rect.right = size.width; diff --git a/windows/d2dscratch.cpp b/windows/d2dscratch.cpp index 63218adc..6dc2ba5f 100644 --- a/windows/d2dscratch.cpp +++ b/windows/d2dscratch.cpp @@ -56,7 +56,7 @@ static void d2dScratchDoLButtonDown(HWND hwnd, ID2D1RenderTarget *rt, LPARAM lPa pos.x = (xpix * 96) / dpix; pos.y = (ypix * 96) / dpiy; - size = rt->GetSize(); + size = realGetSize(rt); SendMessageW(hwnd, msgD2DScratchLButtonDown, (WPARAM) (&pos), (LPARAM) (&size)); } diff --git a/windows/fontdialog.cpp b/windows/fontdialog.cpp index c30230f0..603a17db 100644 --- a/windows/fontdialog.cpp +++ b/windows/fontdialog.cpp @@ -386,8 +386,8 @@ static void fontDialogDrawSampleText(struct fontDialog *f, ID2D1RenderTarget *rt rect.left = 0; rect.top = 0; - rect.right = rt->GetSize().width; - rect.bottom = rt->GetSize().height; + rect.right = realGetSize(rt).width; + rect.bottom = realGetSize(rt).height; rt->DrawText(sample, wcslen(sample), format, &rect, diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index 1a00bada..d33203f3 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -151,6 +151,8 @@ extern void getSizing(HWND hwnd, uiWindowsSizing *sizing, HFONT font); // graphemes.cpp extern size_t *graphemes(WCHAR *msg); +// TODO move into a dedicated file abibugs.cpp when we rewrite the drawing code +extern D2D1_SIZE_F realGetSize(ID2D1RenderTarget *rt); diff --git a/windows/winutil.cpp b/windows/winutil.cpp index 2faf8192..7f7716ac 100644 --- a/windows/winutil.cpp +++ b/windows/winutil.cpp @@ -136,3 +136,19 @@ void invalidateRect(HWND hwnd, RECT *r, BOOL erase) if (InvalidateRect(hwnd, r, erase) == 0) logLastError(L"error invalidating window rect"); } + +// that damn ABI bug is never going to escape me is it +D2D1_SIZE_F realGetSize(ID2D1RenderTarget *rt) +{ +#ifdef _MSC_VER + return rt->GetSize(); +#else + D2D1_SIZE_F size; + typedef D2D1_SIZE_F *(__stdcall ID2D1RenderTarget::* GetSizeF)(D2D1_SIZE_F *); + GetSizeF gs; + + gs = (GetSizeF) (&(rt->GetSize)); + (rt->*gs)(&size); + return size; +#endif +} From ea6db7d8159c53575ff4b37f42d47d11e81f46b0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 5 Jun 2016 16:23:01 -0400 Subject: [PATCH 0239/1329] Statically linked MinGW-w64 libraries in those executable builds. --- CMakeLists.txt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3c081290..f303be4e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,6 @@ cmake_minimum_required(VERSION 2.8.11) # - uname -s for more refined OS control # - Haiku for haiku # - debian DESTDIR? https://github.com/andlabs/libui/pull/10 -# - static libgcc in MinGW builds # the docs say we need to set this up prior to project() set(CMAKE_OSX_DEPLOYMENT_TARGET "10.8") @@ -105,6 +104,15 @@ else() set(_COMMON_LDFLAGS -fvisibility=hidden ) + + # don't require shipping the MinGW-w64 DLLs + if(WIN32) + list(APPEND _COMMON_LDFLAGS + -static + -static-libgcc + -static-libstdc++ + ) + endif() endif() # problem: From df5e0a3e37f973cc70a7d2631e94cc5b16e6fedb Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 5 Jun 2016 16:34:39 -0400 Subject: [PATCH 0240/1329] Expanded the README. Let's package alpha3.1! --- README.md | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 6fe224a0..48311583 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,11 @@ This README is being written.
## Announcements +* **5 June 2016** + * **The build system is now cmake.** cmake 2.8.11 or higher is needed. + * Static linking is now possible. + * MinGW linking is back, but static only. + * **29 May 2016** * **Alpha 3 is here!** Get it [here](https://github.com/andlabs/libui/releases/tag/alpha3). * The next packaged release will introduce: @@ -71,14 +76,39 @@ This README is being written.
## Build Requirements * All platforms: - * GNU make 3.81 or newer (Xcode comes with this; on Windows you will need to get it yourself) -* Windows: Microsoft Visual Studio 2013 or newer (2013 is needed for `va_copy()`) - * MinGW is currently unsupported. MinGW-w64 support will be re-added once the following features come in: + * CMake 3.81 or newer +* Windows: either + * Microsoft Visual Studio 2013 or newer (2013 is needed for `va_copy()`) — you can build either a static or a shared library + * MinGW-w64 (other flavors of MinGW may not work) — **you can only build a static library**; shared library support will be re-added once the following features come in: * [Isolation awareness](https://msdn.microsoft.com/en-us/library/aa375197%28v=vs.85%29.aspx), which is how you get themed controls from a DLL without needing a manifest -* Unix: nothing specific -* Mac OS X: nothing specific, so long as you can build Cocoa programs +* Unix: nothing else specific +* Mac OS X: nothing else specific, so long as you can build Cocoa programs -(TODO write some notes on make variables and cross-compiling) +## Building + +Out-of-tree builds typical of cmake are preferred: + +``` +$ mkdir build +$ cd build +$ cmake .. +``` + +Pass `-DBUILD_SHARED_LIBS=OFF` to `cmake` to build a static library. The standard cmake build configurations are provided; if none is specified, `Debug` is used. + +If you use a makefile generator with cmake, then + +``` +$ make +$ make tester # for the test program +$ make examples # for examples +``` + +and pass `VERBOSE=1` to see build commands. Build targets will be in the `build/out` folder. + +Project file generators should work, but are untested by me. + +On Windows, I use the `Unix Makefiles` generator and GNU make (built using the `build_w32.bat` script included in the source and run in the Visual Studio command line). In this state, if MinGW-w64 (either 32-bit or 64-bit) is not in your `%PATH%`, cmake will use MSVC by default; otherwise, cmake will use with whatever MinGW-w64 is in your path. `set PATH=%PATH%;c:\msys2\mingw(32/64)\bin` should be enough to temporarily change to a MinGW-w64 build for the current command line session only if you installed MinGW-w64 through [MSYS2](https://msys2.github.io/); no need to change global environment variables constantly. ## Documentation From 0ffd8badb87e58a6acc13ddf609c2905c240d32b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 5 Jun 2016 16:39:31 -0400 Subject: [PATCH 0241/1329] Er whoops. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 48311583..27ede1f7 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ This README is being written.
## Build Requirements * All platforms: - * CMake 3.81 or newer + * CMake 2.8.11 or newer * Windows: either * Microsoft Visual Studio 2013 or newer (2013 is needed for `va_copy()`) — you can build either a static or a shared library * MinGW-w64 (other flavors of MinGW may not work) — **you can only build a static library**; shared library support will be re-added once the following features come in: From b779cddef4a5a32a2f3d993a3322ec9177f09dc3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 5 Jun 2016 16:45:18 -0400 Subject: [PATCH 0242/1329] One last try to fix the OS X builder on Travis. --- CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index f303be4e..14a091e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,6 +41,10 @@ if(APPLE) # the / is required by some older versions of OS X set(CMAKE_INSTALL_RPATH "@executable_path/") set(CMAKE_MACOSX_RPATH TRUE) + + # older versions of cmake incorrectly treat Objective-C as C++ + # see also https://cmake.org/pipermail/cmake-developers/2014-March/010072.html + list(APPEND CMAKE_C_SOURCE_FILE_EXTENSIONS m) elseif(WIN32) set(_OSNAME windows) From 02d09bdf95ef520ac5e0972528767297cfe8f284 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 5 Jun 2016 16:55:37 -0400 Subject: [PATCH 0243/1329] Revert "One last try to fix the OS X builder on Travis." It didn't work :( This reverts commit b779cddef4a5a32a2f3d993a3322ec9177f09dc3. --- CMakeLists.txt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 14a091e1..f303be4e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,10 +41,6 @@ if(APPLE) # the / is required by some older versions of OS X set(CMAKE_INSTALL_RPATH "@executable_path/") set(CMAKE_MACOSX_RPATH TRUE) - - # older versions of cmake incorrectly treat Objective-C as C++ - # see also https://cmake.org/pipermail/cmake-developers/2014-March/010072.html - list(APPEND CMAKE_C_SOURCE_FILE_EXTENSIONS m) elseif(WIN32) set(_OSNAME windows) From eeb20c316b7b1a369296278eab819c2bf16c2e67 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 5 Jun 2016 17:12:10 -0400 Subject: [PATCH 0244/1329] Fixed uiSpinbox events not working on Windows. NOW let's alpha 3.1. --- windows/spinbox.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/windows/spinbox.cpp b/windows/spinbox.cpp index f8c79d95..1857975d 100644 --- a/windows/spinbox.cpp +++ b/windows/spinbox.cpp @@ -61,7 +61,7 @@ static void uiSpinboxDestroy(uiControl *c) { uiSpinbox *s = uiSpinbox(c); - uiWindowsUnregisterWM_COMMANDHandler(s->hwnd); + uiWindowsUnregisterWM_COMMANDHandler(s->edit); uiWindowsEnsureDestroyWindow(s->updown); uiWindowsEnsureDestroyWindow(s->edit); uiWindowsEnsureDestroyWindow(s->hwnd); @@ -202,7 +202,7 @@ uiSpinbox *uiNewSpinbox(intmax_t min, intmax_t max) TRUE); uiWindowsEnsureSetParentHWND(s->edit, s->hwnd); - uiWindowsRegisterWM_COMMANDHandler(s->hwnd, onWM_COMMAND, uiControl(s)); + uiWindowsRegisterWM_COMMANDHandler(s->edit, onWM_COMMAND, uiControl(s)); uiSpinboxOnChanged(s, defaultOnChanged, NULL); recreateUpDown(s); From 6ebdc96b93273c3cedf81159e7843025caa83058 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 5 Jun 2016 18:00:18 -0400 Subject: [PATCH 0245/1329] Updated the README and example screenshots. Here we go for real! --- README.md | 8 +++++--- examples/controlgallery/darwin.png | Bin 137948 -> 97260 bytes examples/controlgallery/unix.png | Bin 41710 -> 41757 bytes examples/controlgallery/windows.png | Bin 49245 -> 48217 bytes 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 27ede1f7..3d029c7a 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,15 @@ This README is being written.
[![Build Status](https://travis-ci.org/andlabs/libui.png)](https://travis-ci.org/andlabs/libui) +*(currently failing because the version of cmake that Travis uses treats Objective-C files as C++; if you know the fix please file a PR)* ## Announcements * **5 June 2016** - * **The build system is now cmake.** cmake 2.8.11 or higher is needed. - * Static linking is now possible. - * MinGW linking is back, but static only. + * **Alpha 3.1 is here.** This was a much-needed update to Alpha 3 that changes a few things: + * **The build system is now cmake.** cmake 2.8.11 or higher is needed. + * Static linking is now fully possible. + * MinGW linking is back, but static only. * **29 May 2016** * **Alpha 3 is here!** Get it [here](https://github.com/andlabs/libui/releases/tag/alpha3). diff --git a/examples/controlgallery/darwin.png b/examples/controlgallery/darwin.png index 6514507f595e25dc5c539f5b8d91c085c7f34575..f61b54ba2d92f69006f789a8f8a1c56993b8db41 100644 GIT binary patch delta 91379 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 kRQqbk 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 Eg17u?ZvX%Q diff --git a/examples/controlgallery/unix.png b/examples/controlgallery/unix.png index 2aa5ea0a0da2169c0d271b12753402e1a831407b..0c58d0909678372cbb3db2906d72683814b68626 100644 GIT binary patch delta 25911 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 Ix#`>g0M-@CE&u=k delta 25842 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 PgaE9ftz7-gJoJA7r=Goz diff --git a/examples/controlgallery/windows.png b/examples/controlgallery/windows.png index 5917ca0f659f5280eb49aec6f2b16da69643bd15..4be832f7f96535d435c96991f6a6b50207eb6ba0 100755 GIT binary patch literal 48217 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 JkN$P-{{Wsx0)qem From d0be2979e4e80b334e6dabd552a4d55db9bf5de8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 5 Jun 2016 20:26:44 -0400 Subject: [PATCH 0246/1329] Added Password and Search Entries. Now for the implementation. --- test/page13.c | 20 ++++++++++++++++++++ ui.h | 2 ++ 2 files changed, 22 insertions(+) diff --git a/test/page13.c b/test/page13.c index 1f986675..26617d79 100644 --- a/test/page13.c +++ b/test/page13.c @@ -45,6 +45,8 @@ static void openTestWindow(uiBox *(*mkf)(void)) // TODO nonscrolling and scrolling areas? BA(uiNewFontButton()); BA(uiNewColorButton()); + BA(uiNewPasswordEntry()); + BA(uiNewSearchEntry()); uiControlShow(uiControl(w)); } @@ -54,11 +56,21 @@ static void buttonClicked(uiButton *b, void *data) openTestWindow((uiBox *(*)(void)) data); } +static void entryChanged(uiEntry *e, void *data) +{ + char *text; + + text = uiEntryText(e); + printf("%s entry changed: %s\n", (const char *) data, text); + uiFreeText(text); +} + uiBox *makePage13(void) { uiBox *page13; uiRadioButtons *rb; uiButton *b; + uiEntry *e; page13 = newVerticalBox(); @@ -81,5 +93,13 @@ uiBox *makePage13(void) uiButtonOnClicked(b, buttonClicked, uiNewVerticalBox); uiBoxAppend(page13, uiControl(b), 0); + e = uiNewPasswordEntry(); + uiEntryOnChanged(e, entryChanged, "password"); + uiBoxAppend(page13, uiControl(e), 0); + + e = uiNewSearchEntry(); + uiEntryOnChanged(e, entryChanged, "search"); + uiBoxAppend(page13, uiControl(e), 0); + return page13; } diff --git a/ui.h b/ui.h index c7a8858b..aede021b 100644 --- a/ui.h +++ b/ui.h @@ -136,6 +136,8 @@ _UI_EXTERN void uiEntryOnChanged(uiEntry *e, void (*f)(uiEntry *e, void *data), _UI_EXTERN int uiEntryReadOnly(uiEntry *e); _UI_EXTERN void uiEntrySetReadOnly(uiEntry *e, int readonly); _UI_EXTERN uiEntry *uiNewEntry(void); +_UI_EXTERN uiEntry *uiNewPasswordEntry(void); +_UI_EXTERN uiEntry *uiNewSearchEntry(void); typedef struct uiLabel uiLabel; #define uiLabel(this) ((uiLabel *) (this)) From 4e7d2812bd3f66228c3705ad4169ff8393ab3040 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 5 Jun 2016 20:39:18 -0400 Subject: [PATCH 0247/1329] Implemented uiPasswordEntry() and uiSearchEntry() on OS X. --- darwin/entry.m | 93 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 86 insertions(+), 7 deletions(-) diff --git a/darwin/entry.m b/darwin/entry.m index 179c81e1..760fa83a 100644 --- a/darwin/entry.m +++ b/darwin/entry.m @@ -20,6 +20,40 @@ @end +// TODO does this have one on its own? +@interface libui_intrinsicWidthNSSecureTextField : NSSecureTextField +@end + +@implementation libui_intrinsicWidthNSSecureTextField + +- (NSSize)intrinsicContentSize +{ + NSSize s; + + s = [super intrinsicContentSize]; + s.width = textfieldWidth; + return s; +} + +@end + +// TODO does this have one on its own? +@interface libui_intrinsicWidthNSSearchField : NSSearchField +@end + +@implementation libui_intrinsicWidthNSSearchField + +- (NSSize)intrinsicContentSize +{ + NSSize s; + + s = [super intrinsicContentSize]; + s.width = textfieldWidth; + return s; +} + +@end + struct uiEntry { uiDarwinControl c; NSTextField *textfield; @@ -27,10 +61,16 @@ struct uiEntry { void *onChangedData; }; +static BOOL isSearchField(NSTextField *tf) +{ + return [tf isKindOfClass:[NSSearchField class]]; +} + @interface entryDelegateClass : NSObject { struct mapTable *entries; } - (void)controlTextDidChange:(NSNotification *)note; +- (IBAction)onSearch:(id)sender; - (void)registerEntry:(uiEntry *)e; - (void)unregisterEntry:(uiEntry *)e; @end @@ -52,22 +92,34 @@ struct uiEntry { } - (void)controlTextDidChange:(NSNotification *)note +{ + [self onSearch:[note object]]; +} + +- (IBAction)onSearch:(id)sender { uiEntry *e; - e = (uiEntry *) mapGet(self->entries, [note object]); + e = (uiEntry *) mapGet(self->entries, sender); (*(e->onChanged))(e, e->onChangedData); } - (void)registerEntry:(uiEntry *)e { mapSet(self->entries, e->textfield, e); - [e->textfield setDelegate:self]; + if (isSearchField(e->textfield)) { + [e->textfield setTarget:self]; + [e->textfield setAction:@selector(onSearch:)]; + } else + [e->textfield setDelegate:self]; } - (void)unregisterEntry:(uiEntry *)e { - [e->textfield setDelegate:nil]; + if (isSearchField(e->textfield)) + [e->textfield setTarget:nil]; + else + [e->textfield setDelegate:nil]; mapDelete(self->entries, e->textfield); } @@ -139,23 +191,28 @@ void finishNewTextField(NSTextField *t, BOOL isEntry) [[t cell] setScrollable:YES]; } -NSTextField *newEditableTextField(void) +static NSTextField *realNewEditableTextField(Class class) { NSTextField *tf; - tf = [[libui_intrinsicWidthNSTextField alloc] initWithFrame:NSZeroRect]; + tf = [[class alloc] initWithFrame:NSZeroRect]; [tf setSelectable:YES]; // otherwise the setting is masked by the editable default of YES finishNewTextField(tf, YES); return tf; } -uiEntry *uiNewEntry(void) +NSTextField *newEditableTextField(void) +{ + return realNewEditableTextField([libui_intrinsicWidthNSTextField class]); +} + +static uiEntry *finishNewEntry(Class class) { uiEntry *e; uiDarwinNewControl(uiEntry, e); - e->textfield = newEditableTextField(); + e->textfield = realNewEditableTextField(class); if (entryDelegate == nil) { entryDelegate = [[entryDelegateClass new] autorelease]; @@ -166,3 +223,25 @@ uiEntry *uiNewEntry(void) return e; } + +uiEntry *uiNewEntry(void) +{ + return finishNewEntry([libui_intrinsicWidthNSTextField class]); +} + +uiEntry *uiNewPasswordEntry(void) +{ + return finishNewEntry([libui_intrinsicWidthNSSecureTextField class]); +} + +uiEntry *uiNewSearchEntry(void) +{ + uiEntry *e; + NSSearchField *s; + + e = finishNewEntry([libui_intrinsicWidthNSSearchField class]); + s = (NSSearchField *) (e->textfield); + [s setSendsSearchStringImmediately:NO]; + [s setSendsWholeSearchString:NO]; + return e; +} From 3d5d1408c71ef49cb69e445123639277d862e23d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 5 Jun 2016 20:54:16 -0400 Subject: [PATCH 0248/1329] Implemented uiPasswordEntry and uiSearchEntry on GTK+. --- unix/entry.c | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/unix/entry.c b/unix/entry.c index a71a47b5..4a9a1d04 100644 --- a/unix/entry.c +++ b/unix/entry.c @@ -60,18 +60,38 @@ void uiEntrySetReadOnly(uiEntry *e, int readonly) gtk_editable_set_editable(e->editable, editable); } -uiEntry *uiNewEntry(void) +static uiEntry *finishNewEntry(GtkWidget *w, const gchar *signal) { uiEntry *e; uiUnixNewControl(uiEntry, e); - e->widget = gtk_entry_new(); + e->widget = w; e->entry = GTK_ENTRY(e->widget); e->editable = GTK_EDITABLE(e->widget); - e->onChangedSignal = g_signal_connect(e->widget, "changed", G_CALLBACK(onChanged), e); + e->onChangedSignal = g_signal_connect(e->widget, signal, G_CALLBACK(onChanged), e); uiEntryOnChanged(e, defaultOnChanged, NULL); return e; } + +uiEntry *uiNewEntry(void) +{ + return finishNewEntry(gtk_entry_new(), "changed"); +} + +uiEntry *uiNewPasswordEntry(void) +{ + GtkWidget *e; + + e = gtk_entry_new(); + gtk_entry_set_visibility(GTK_ENTRY(e), FALSE); + return finishNewEntry(e, "changed"); +} + +// TODO make it use a separate function to be type-safe +uiEntry *uiNewSearchEntry(void) +{ + return finishNewEntry(gtk_search_entry_new(), "search-changed"); +} From 15456c8b41778eb201a0cd59834d8823eddbcda6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 5 Jun 2016 21:02:59 -0400 Subject: [PATCH 0249/1329] Implemented uiPasswordEntry and uiSearchEntry on Windows. --- README.md | 4 ++++ windows/entry.cpp | 20 ++++++++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3d029c7a..b64ebb65 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,10 @@ This README is being written.
*Note that today's entry may be updated later today.* +* **5 June 2016** + * Added `uiNewPasswordEntry()`, which creates a new `uiEntry` suitable for entering passwords. + * Added `uiNewSearchEntry()`, which creates a new `uiEntry` suitable for searching. On some systems, the `OnChanged()` event will be slightly delayed and/or combined, to produce a more natural feel when searching. + * **29 May 2016** * Thanks to @pcwalton, we can now statically link libui! Simply do `make STATIC=1` instead of just `make`. * On Windows you must link both `libui.lib` and `libui.res` AND provide a Common Controls 6 manifest for output static binaries to work properly. diff --git a/windows/entry.cpp b/windows/entry.cpp index 878f359d..6cc865a3 100644 --- a/windows/entry.cpp +++ b/windows/entry.cpp @@ -92,7 +92,7 @@ void uiEntrySetReadOnly(uiEntry *e, int readonly) logLastError(L"error making uiEntry read-only"); } -uiEntry *uiNewEntry(void) +static uiEntry *finishNewEntry(DWORD style) { uiEntry *e; @@ -100,7 +100,7 @@ uiEntry *uiNewEntry(void) e->hwnd = uiWindowsEnsureCreateControlHWND(WS_EX_CLIENTEDGE, L"edit", L"", - ES_AUTOHSCROLL | ES_LEFT | ES_NOHIDESEL | WS_TABSTOP, + style | ES_AUTOHSCROLL | ES_LEFT | ES_NOHIDESEL | WS_TABSTOP, hInstance, NULL, TRUE); @@ -109,3 +109,19 @@ uiEntry *uiNewEntry(void) return e; } + +uiEntry *uiNewEntry(void) +{ + return finishNewEntry(0); +} + +uiEntry *uiNewPasswordEntry(void) +{ + return finishNewEntry(ES_PASSWORD); +} + +uiEntry *uiNewSearchEntry(void) +{ + // TODO + return finishNewEntry(0); +} From 812086be1b92a6bc71a60a09438970d986d1ace6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 5 Jun 2016 21:27:44 -0400 Subject: [PATCH 0250/1329] Improved the appearance of uiSearchEntry on Windows somewhat. --- windows/entry.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/windows/entry.cpp b/windows/entry.cpp index 6cc865a3..13bae178 100644 --- a/windows/entry.cpp +++ b/windows/entry.cpp @@ -122,6 +122,13 @@ uiEntry *uiNewPasswordEntry(void) uiEntry *uiNewSearchEntry(void) { - // TODO - return finishNewEntry(0); + uiEntry *e; + HRESULT hr; + + e = finishNewEntry(0); + // TODO this is from ThemeExplorer; is it documented anywhere? + // TODO SearchBoxEditComposited has no border + hr = SetWindowTheme(e->hwnd, L"SearchBoxEdit", NULL); + // TODO will hr be S_OK if themes are disabled? + return e; } From 6cb3cd4ed765dad15c9712434560408104bf7b82 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 6 Jun 2016 09:27:07 -0400 Subject: [PATCH 0251/1329] More robust accelerator tests. --- test/menus.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/menus.c b/test/menus.c index 8c7e0d02..3e75e27b 100644 --- a/test/menus.c +++ b/test/menus.c @@ -94,10 +94,10 @@ void initMenus(void) multiMenu = uiNewMenu("Multi"); uiMenuAppendSeparator(multiMenu); uiMenuAppendSeparator(multiMenu); - uiMenuAppendItem(multiMenu, "Item"); + uiMenuAppendItem(multiMenu, "Item && Item && Item"); uiMenuAppendSeparator(multiMenu); uiMenuAppendSeparator(multiMenu); - uiMenuAppendItem(multiMenu, "Item"); + uiMenuAppendItem(multiMenu, "Item __ Item __ Item"); uiMenuAppendSeparator(multiMenu); uiMenuAppendSeparator(multiMenu); From 852d2ee1432be4294a66a6548c7d8dde154b987b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 6 Jun 2016 10:29:55 -0400 Subject: [PATCH 0252/1329] Clarified the README a bit. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 09f7a680..b58937f4 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,7 @@ This README is being written.
Out-of-tree builds typical of cmake are preferred: ``` +$ # you must be in the top-level libui directory, otherwise this won't work $ mkdir build $ cd build $ cmake .. From 8d660431d79022fad3a68636b8ca9fe457f3245b Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Mon, 6 Jun 2016 23:49:04 +0100 Subject: [PATCH 0253/1329] Add Nim binding to readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b58937f4..751ec05c 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,7 @@ Haskell | [libui-haskell](https://github.com/ajnsit/libui-haskell) JavaScript | [libui.js (merged into libui-node?)](https://github.com/mavenave/libui.js) Julia | [Libui.jl](https://github.com/joa-quim/Libui.jl) Lua | [libuilua](https://github.com/zevv/libuilua), [libui-lua](https://github.com/mdombroski/libui-lua) +Nim | [ui](https://github.com/nim-lang/ui) Node.js | [libui-node](https://github.com/parro-it/libui-node) Python | [pylibui](https://github.com/joaoventura/pylibui) Ruby | [libui-ruby](https://github.com/jamescook/libui-ruby) From 5accda32e53930ad6c12e09953d5f81923f6eabb Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 6 Jun 2016 18:56:58 -0400 Subject: [PATCH 0254/1329] Added new uiRadioButtons functions and implemented them on GTK+. More TODOs. --- CMakeLists.txt | 1 + test/page4.c | 24 ++++++++++++++++++ ui.h | 3 +++ unix/radiobuttons.c | 62 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 90 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index f303be4e..262af94f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,7 @@ cmake_minimum_required(VERSION 2.8.11) # - uname -s for more refined OS control # - Haiku for haiku # - debian DESTDIR? https://github.com/andlabs/libui/pull/10 +# - libui-combined* needs to be deleted so that custom command can run every time # the docs say we need to set this up prior to project() set(CMAKE_OSX_DEPLOYMENT_TARGET "10.8") diff --git a/test/page4.c b/test/page4.c index 00bc16ec..5a448c44 100644 --- a/test/page4.c +++ b/test/page4.c @@ -58,6 +58,23 @@ static void onECBChanged(uiEditableCombobox *c, void *data) uiFreeText(t); } +static void onRBSelected(uiRadioButtons *r, void *data) +{ + printf("radio buttons %d\n", uiRadioButtonsSelected(r)); +} + +static void selectSecond(uiButton *b, void *data) +{ + // TODO combobox, editable + uiRadioButtonsSetSelected(rb, 1); +} + +static void selectNone(uiButton *b, void *data) +{ + // TODO combobox, editable + uiRadioButtonsSetSelected(rb, -1); +} + uiBox *makePage4(void) { uiBox *page4; @@ -123,12 +140,19 @@ uiBox *makePage4(void) uiRadioButtonsAppend(rb, "Item 1"); uiRadioButtonsAppend(rb, "Item 2"); uiRadioButtonsAppend(rb, "Item 3"); + uiRadioButtonsOnSelected(rb, onRBSelected, NULL); uiBoxAppend(page4, uiControl(rb), 0); hbox = newHorizontalBox(); b = uiNewButton("Append"); uiButtonOnClicked(b, appendCBRB, NULL); uiBoxAppend(hbox, uiControl(b), 0); + b = uiNewButton("Second"); + uiButtonOnClicked(b, selectSecond, NULL); + uiBoxAppend(hbox, uiControl(b), 0); + b = uiNewButton("None"); + uiButtonOnClicked(b, selectNone, NULL); + uiBoxAppend(hbox, uiControl(b), 0); uiBoxAppend(page4, uiControl(hbox), 0); uiBoxAppend(page4, uiControl(uiNewHorizontalSeparator()), 0); diff --git a/ui.h b/ui.h index aede021b..d3996cb7 100644 --- a/ui.h +++ b/ui.h @@ -213,6 +213,9 @@ _UI_EXTERN uiEditableCombobox *uiNewEditableCombobox(void); typedef struct uiRadioButtons uiRadioButtons; #define uiRadioButtons(this) ((uiRadioButtons *) (this)) _UI_EXTERN void uiRadioButtonsAppend(uiRadioButtons *r, const char *text); +_UI_EXTERN intmax_t uiRadioButtonsSelected(uiRadioButtons *r); +_UI_EXTERN void uiRadioButtonsSetSelected(uiRadioButtons *r, intmax_t n); +_UI_EXTERN void uiRadioButtonsOnSelected(uiRadioButtons *r, void (*f)(uiRadioButtons *, void *), void *data); _UI_EXTERN uiRadioButtons *uiNewRadioButtons(void); typedef struct uiDateTimePicker uiDateTimePicker; diff --git a/unix/radiobuttons.c b/unix/radiobuttons.c index 7956f5f6..24bc80b1 100644 --- a/unix/radiobuttons.c +++ b/unix/radiobuttons.c @@ -9,10 +9,31 @@ struct uiRadioButtons { GtkContainer *container; GtkBox *box; GPtrArray *buttons; + void (*onSelected)(uiRadioButtons *, void *); + void *onSelectedData; + gboolean changing; }; uiUnixControlAllDefaultsExceptDestroy(uiRadioButtons) +static void defaultOnSelected(uiRadioButtons *r, void *data) +{ + // do nothing +} + +static void onToggled(GtkToggleButton *tb, gpointer data) +{ + uiRadioButtons *r = uiRadioButtons(data); + + // only care if a button is selected + if (!gtk_toggle_button_get_active(tb)) + return; + // ignore programmatic changes + if (r->changing) + return; + (*(r->onSelected))(r, r->onSelectedData); +} + static void uiRadioButtonsDestroy(uiControl *c) { uiRadioButtons *r = uiRadioButtons(c); @@ -37,11 +58,50 @@ void uiRadioButtonsAppend(uiRadioButtons *r, const char *text) if (r->buttons->len > 0) previous = GTK_RADIO_BUTTON(g_ptr_array_index(r->buttons, 0)); rb = gtk_radio_button_new_with_label_from_widget(previous, text); + g_signal_connect(rb, "toggled", G_CALLBACK(onToggled), r); gtk_container_add(r->container, rb); g_ptr_array_add(r->buttons, rb); gtk_widget_show(rb); } +intmax_t uiRadioButtonsSelected(uiRadioButtons *r) +{ + GtkToggleButton *tb; + guint i; + + for (i = 0; i < r->buttons->len; i++) { + tb = GTK_TOGGLE_BUTTON(g_ptr_array_index(r->buttons, i)); + if (gtk_toggle_button_get_active(tb)) + return i; + } + return -1; +} + +void uiRadioButtonsSetSelected(uiRadioButtons *r, intmax_t n) +{ + GtkToggleButton *tb; + gboolean active = TRUE; + + // TODO this doesn't work + if (n == -1) { + n = uiRadioButtonsSelected(r); + if (n == -1) // no selection; keep it that way + return; + active = FALSE; + } + tb = GTK_TOGGLE_BUTTON(g_ptr_array_index(r->buttons, n)); + // this is easier than remembering all the signals + r->changing = TRUE; + gtk_toggle_button_set_active(tb, active); + r->changing = FALSE; +} + +void uiRadioButtonsOnSelected(uiRadioButtons *r, void (*f)(uiRadioButtons *, void *), void *data) +{ + r->onSelected = f; + r->onSelectedData = data; +} + uiRadioButtons *uiNewRadioButtons(void) { uiRadioButtons *r; @@ -54,5 +114,7 @@ uiRadioButtons *uiNewRadioButtons(void) r->buttons = g_ptr_array_new(); + uiRadioButtonsOnSelected(r, defaultOnSelected, NULL); + return r; } From 9a5bc738c507f3fc55f0ff65e5c1ccc23e6988a3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 6 Jun 2016 19:51:46 -0400 Subject: [PATCH 0255/1329] Implemented likewise on Windows. --- windows/radiobuttons.cpp | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/windows/radiobuttons.cpp b/windows/radiobuttons.cpp index d2b80b53..3063d3cd 100644 --- a/windows/radiobuttons.cpp +++ b/windows/radiobuttons.cpp @@ -12,6 +12,8 @@ struct uiRadioButtons { uiWindowsControl c; HWND hwnd; // of the container std::vector *hwnds; // of the buttons + void (*onSelected)(uiRadioButtons *, void *); + void *onSelectedData; }; static BOOL onWM_COMMAND(uiControl *c, HWND clicked, WORD code, LRESULT *lResult) @@ -28,10 +30,16 @@ static BOOL onWM_COMMAND(uiControl *c, HWND clicked, WORD code, LRESULT *lResult check = BST_CHECKED; SendMessage(hwnd, BM_SETCHECK, check, 0); } + (*(r->onSelected))(r, r->onSelectedData); *lResult = 0; return TRUE; } +static void defaultOnSelected(uiRadioButtons *r, void *data) +{ + // do nothing +} + static void uiRadioButtonsDestroy(uiControl *c) { uiRadioButtons *r = uiRadioButtons(c); @@ -141,6 +149,33 @@ void uiRadioButtonsAppend(uiRadioButtons *r, const char *text) uiWindowsControlMinimumSizeChanged(uiWindowsControl(r)); } +intmax_t uiRadioButtonsSelected(uiRadioButtons *r) +{ + size_t i; + + for (i = 0; i < r->hwnds->size(); i++) + if (SendMessage((*(r->hwnds))[i], BM_GETCHECK, 0, 0) == BST_CHECKED) + return i; + return -1; +} + +void uiRadioButtonsSetSelected(uiRadioButtons *r, intmax_t n) +{ + intmax_t m; + + m = uiRadioButtonsSelected(r); + if (m != -1) + SendMessage((*(r->hwnds))[m], BM_SETCHECK, BST_UNCHECKED, 0); + if (n != -1) + SendMessage((*(r->hwnds))[n], BM_SETCHECK, BST_CHECKED, 0); +} + +void uiRadioButtonsOnSelected(uiRadioButtons *r, void (*f)(uiRadioButtons *, void *), void *data) +{ + r->onSelected = f; + r->onSelectedData = data; +} + static void onResize(uiWindowsControl *c) { radiobuttonsRelayout(uiRadioButtons(c)); @@ -156,5 +191,7 @@ uiRadioButtons *uiNewRadioButtons(void) r->hwnds = new std::vector; + uiRadioButtonsOnSelected(r, defaultOnSelected, NULL); + return r; } From 3e1258cc62ee39aab7145df192d2a9eb8b2157a3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 6 Jun 2016 20:12:17 -0400 Subject: [PATCH 0256/1329] Implemented the new radio button stuff on OS X. --- README.md | 3 ++ darwin/radiobuttons.m | 81 +++++++++++++++++++++++++++++++++++++++++-- unix/radiobuttons.c | 3 +- 3 files changed, 84 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 751ec05c..a2c0b1ef 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,9 @@ This README is being written.
*Note that today's entry may be updated later today.* +* **6 June 2016** + * Added `uiRadioButtonsSelected()`, `uiRadioButtonsSetSelected()`, and `uiRadioButtonsOnSelected()` to control selection of a radio button and catch an event when such a thing happens. + * **5 June 2016** * Added `uiNewPasswordEntry()`, which creates a new `uiEntry` suitable for entering passwords. * Added `uiNewSearchEntry()`, which creates a new `uiEntry` suitable for searching. On some systems, the `OnChanged()` event will be slightly delayed and/or combined, to produce a more natural feel when searching. diff --git a/darwin/radiobuttons.m b/darwin/radiobuttons.m index b6c663e0..c9bcfe9f 100644 --- a/darwin/radiobuttons.m +++ b/darwin/radiobuttons.m @@ -7,16 +7,50 @@ // LONGTERM 6 units of spacing between buttons, as suggested by Interface Builder? +@interface radioButtonsDelegate : NSObject { + uiRadioButtons *libui_r; +} +- (id)initWithR:(uiRadioButtons *)r; +- (IBAction)onClicked:(id)sender; +@end + struct uiRadioButtons { uiDarwinControl c; NSView *view; NSMutableArray *buttons; NSMutableArray *constraints; NSLayoutConstraint *lastv; + radioButtonsDelegate *delegate; + void (*onSelected)(uiRadioButtons *, void *); + void *onSelectedData; }; +@implementation radioButtonsDelegate + +- (id)initWithR:(uiRadioButtons *)r +{ + self = [super init]; + if (self) + self->libui_r = r; + return self; +} + +- (IBAction)onClicked:(id)sender +{ + uiRadioButtons *r = self->libui_r; + + (*(r->onSelected))(r, r->onSelectedData); +} + +@end + uiDarwinControlAllDefaultsExceptDestroy(uiRadioButtons, view) +static void defaultOnSelected(uiRadioButtons *r, void *data) +{ + // do nothing +} + static void uiRadioButtonsDestroy(uiControl *c) { uiRadioButtons *r = uiRadioButtons(c); @@ -28,9 +62,13 @@ static void uiRadioButtonsDestroy(uiControl *c) if (r->lastv != nil) [r->lastv release]; // destroy the buttons - for (b in r->buttons) + for (b in r->buttons) { + [b setTarget:nil]; [b removeFromSuperview]; + } [r->buttons release]; + // destroy the delegate + [r->delegate release]; // and destroy ourselves [r->view release]; uiFreeControl(uiControl(r)); @@ -55,7 +93,7 @@ void uiRadioButtonsAppend(uiRadioButtons *r, const char *text) uiDarwinSetControlFont(b, NSRegularControlSize); [b setTranslatesAutoresizingMaskIntoConstraints:NO]; - // TODO set target + [b setTarget:r->delegate]; [b setAction:@selector(onClicked:)]; [r->buttons addObject:b]; @@ -114,6 +152,41 @@ void uiRadioButtonsAppend(uiRadioButtons *r, const char *text) [r->lastv retain]; } +intmax_t uiRadioButtonsSelected(uiRadioButtons *r) +{ + NSButton *b; + NSUInteger i; + + for (i = 0; i < [r->buttons count]; i++) { + b = (NSButton *) [r->buttons objectAtIndex:i]; + if ([b state] == NSOnState) + return i; + } + return -1; +} + +void uiRadioButtonsSetSelected(uiRadioButtons *r, intmax_t n) +{ + NSButton *b; + NSInteger state; + + state = NSOnState; + if (n == -1) { + n = uiRadioButtonsSelected(r); + if (n == -1) // from nothing to nothing; do nothing + return; + state = NSOffState; + } + b = (NSButton *) [r->buttons objectAtIndex:n]; + [b setState:state]; +} + +void uiRadioButtonsOnSelected(uiRadioButtons *r, void (*f)(uiRadioButtons *, void *), void *data) +{ + r->onSelected = f; + r->onSelectedData = data; +} + uiRadioButtons *uiNewRadioButtons(void) { uiRadioButtons *r; @@ -124,5 +197,9 @@ uiRadioButtons *uiNewRadioButtons(void) r->buttons = [NSMutableArray new]; r->constraints = [NSMutableArray new]; + r->delegate = [[radioButtonsDelegate alloc] initWithR:r]; + + uiRadioButtonsOnSelected(r, defaultOnSelected, NULL); + return r; } diff --git a/unix/radiobuttons.c b/unix/radiobuttons.c index 24bc80b1..4f6e24e5 100644 --- a/unix/radiobuttons.c +++ b/unix/radiobuttons.c @@ -80,8 +80,9 @@ intmax_t uiRadioButtonsSelected(uiRadioButtons *r) void uiRadioButtonsSetSelected(uiRadioButtons *r, intmax_t n) { GtkToggleButton *tb; - gboolean active = TRUE; + gboolean active; + active = TRUE; // TODO this doesn't work if (n == -1) { n = uiRadioButtonsSelected(r); From 1ad18ddc8ea861a8dc5de77cfa8dfc01c809bdeb Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 7 Jun 2016 09:56:53 -0400 Subject: [PATCH 0257/1329] We are FINALLY adding uiForm. About time :V --- common/controlsigs.h | 1 + test/page13.c | 10 ++++++++-- test/spaced.c | 14 ++++++++++++++ test/test.h | 1 + ui.h | 7 +++++++ 5 files changed, 31 insertions(+), 2 deletions(-) diff --git a/common/controlsigs.h b/common/controlsigs.h index 38aa4de9..b788135d 100644 --- a/common/controlsigs.h +++ b/common/controlsigs.h @@ -10,6 +10,7 @@ #define uiEditableComboboxSignature 0x45644362 #define uiEntrySignature 0x456E7472 #define uiFontButtonSignature 0x466F6E42 +#define uiFormSignature 0x466F726D #define uiGroupSignature 0x47727062 #define uiLabelSignature 0x4C61626C #define uiMultilineEntrySignature 0x4D6C6E45 diff --git a/test/page13.c b/test/page13.c index 26617d79..514fe94f 100644 --- a/test/page13.c +++ b/test/page13.c @@ -70,6 +70,7 @@ uiBox *makePage13(void) uiBox *page13; uiRadioButtons *rb; uiButton *b; + uiForm *f; uiEntry *e; page13 = newVerticalBox(); @@ -93,13 +94,18 @@ uiBox *makePage13(void) uiButtonOnClicked(b, buttonClicked, uiNewVerticalBox); uiBoxAppend(page13, uiControl(b), 0); + f = newForm(); + uiBoxAppend(page13, uiControl(f), 1); + e = uiNewPasswordEntry(); uiEntryOnChanged(e, entryChanged, "password"); - uiBoxAppend(page13, uiControl(e), 0); + uiFormAppend(f, "Password Entry", uiControl(e), 0); e = uiNewSearchEntry(); uiEntryOnChanged(e, entryChanged, "search"); - uiBoxAppend(page13, uiControl(e), 0); + uiFormAppend(f, "Search Box", uiControl(e), 0); + + uiFormAppend(f, "MLE", uiControl(uiNewMultilineEntry()), 1); return page13; } diff --git a/test/spaced.c b/test/spaced.c index 8bd4f465..7eb34fab 100644 --- a/test/spaced.c +++ b/test/spaced.c @@ -31,6 +31,7 @@ enum types { box, tab, group, + form, }; void setSpaced(int spaced) @@ -56,6 +57,9 @@ void setSpaced(int spaced) case group: uiGroupSetMargined(uiGroup(p), spaced); break; + case form: + uiFormSetPadded(uiForm(p), spaced); + break; } } } @@ -88,6 +92,7 @@ void querySpaced(char out[12]) // more than enough if (uiGroupMargined(uiGroup(pp))) m++; break; + // TODO form } } @@ -147,3 +152,12 @@ uiGroup *newGroup(const char *text) append(g, group); return g; } + +uiForm *newForm(void) +{ + uiForm *f; + + f = uiNewForm(); + append(f, form); + return f; +} diff --git a/test/test.h b/test/test.h index db687576..b59879b9 100644 --- a/test/test.h +++ b/test/test.h @@ -22,6 +22,7 @@ extern uiBox *newHorizontalBox(void); extern uiBox *newVerticalBox(void); extern uiTab *newTab(void); extern uiGroup *newGroup(const char *); +extern uiForm *newForm(void); // menus.c extern uiMenuItem *shouldQuitItem; diff --git a/ui.h b/ui.h index d3996cb7..93488518 100644 --- a/ui.h +++ b/ui.h @@ -620,6 +620,13 @@ _UI_EXTERN void uiColorButtonSetColor(uiColorButton *b, double r, double g, doub _UI_EXTERN void uiColorButtonOnChanged(uiColorButton *b, void (*f)(uiColorButton *, void *), void *data); _UI_EXTERN uiColorButton *uiNewColorButton(void); +typedef struct uiForm uiForm; +#define uiForm(this) ((uiForm *) (this)) +_UI_EXTERN void uiFormAppend(uiForm *f, const char *label, uiControl *control, int stretchy); +_UI_EXTERN int uiFormPadded(uiForm *f); +_UI_EXTERN void uiFormSetPadded(uiForm *f, int padded); +_UI_EXTERN uiForm *uiNewForm(void); + #ifdef __cplusplus } #endif From c6e8537269812c7ca589aeb6b6f918442b2dd587 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 7 Jun 2016 11:29:49 -0400 Subject: [PATCH 0258/1329] Started the implementation of uiForm on OS X. Mostly works, but there are Auto Layout glitches (of course there are Auto Layout glitches) --- darwin/CMakeLists.txt | 1 + darwin/form.m | 414 +++++++++++++++++++++++++++++++++++++++++ darwin/label.m | 20 +- darwin/uipriv_darwin.h | 3 + ui.h | 2 +- 5 files changed, 433 insertions(+), 7 deletions(-) create mode 100644 darwin/form.m diff --git a/darwin/CMakeLists.txt b/darwin/CMakeLists.txt index 49f878a1..25671ae0 100644 --- a/darwin/CMakeLists.txt +++ b/darwin/CMakeLists.txt @@ -18,6 +18,7 @@ list(APPEND _LIBUI_SOURCES darwin/editablecombo.m darwin/entry.m darwin/fontbutton.m + darwin/form.m darwin/group.m darwin/label.m darwin/main.m diff --git a/darwin/form.m b/darwin/form.m new file mode 100644 index 00000000..fdd8e79d --- /dev/null +++ b/darwin/form.m @@ -0,0 +1,414 @@ +// 7 june 2016 +#import "uipriv_darwin.h" + +@interface formChild : NSObject +@property uiControl *c; +@property (strong) NSTextField *label; +@property BOOL stretchy; +@property NSLayoutPriority oldHorzHuggingPri; +@property NSLayoutPriority oldVertHuggingPri; +@property (strong) NSLayoutConstraint *baseline; +- (NSView *)view; +@end + +@interface formView : NSView { + uiForm *f; + NSMutableArray *children; + int padded; + uintmax_t nStretchy; + + NSLayoutConstraint *first; + NSMutableArray *inBetweens; + NSLayoutConstraint *last; + NSMutableArray *widths; + NSMutableArray *leadings; + NSMutableArray *middles; + NSMutableArray *trailings; +} +- (id)initWithF:(uiForm *)ff; +- (void)onDestroy; +- (void)removeOurConstraints; +- (void)syncEnableStates:(int)enabled; +- (CGFloat)paddingAmount; +- (void)establishOurConstraints; +- (void)append:(NSString *)label c:(uiControl *)c stretchy:(int)stretchy; +//TODO- (void)delete:(uintmax_t)n; +- (int)isPadded; +- (void)setPadded:(int)p; +- (BOOL)hugsTrailing; +- (BOOL)hugsBottom; +@end + +struct uiForm { + uiDarwinControl c; + formView *view; +}; + +@implementation formChild + +- (NSView *)view +{ + return (NSView *) uiControlHandle(self.c); +} + +@end + +@implementation formView + +- (id)initWithF:(uiForm *)ff +{ + self = [super initWithFrame:NSZeroRect]; + if (self != nil) { + self->f = ff; + self->padded = 0; + self->children = [NSMutableArray new]; + self->nStretchy = 0; + + self->inBetweens = [NSMutableArray new]; + self->widths = [NSMutableArray new]; + self->leadings = [NSMutableArray new]; + self->middles = [NSMutableArray new]; + self->trailings = [NSMutableArray new]; + } + return self; +} + +- (void)onDestroy +{ + formChild *fc; + + [self removeOurConstraints]; + [self->inBetweens release]; + [self->widths release]; + [self->leadings release]; + [self->middles release]; + [self->trailings release]; + + for (fc in self->children) { + [self removeConstraint:fc.baseline]; + fc.baseline = nil; + uiControlSetParent(fc.c, NULL); + uiDarwinControlSetSuperview(uiDarwinControl(fc.c), nil); + uiControlDestroy(fc.c); + [fc.label removeFromSuperview]; + fc.label = nil; + } + [self->children release]; +} + +- (void)removeOurConstraints +{ + if (self->first != nil) { + [self removeConstraint:self->first]; + [self->first release]; + self->first = nil; + } + if ([self->inBetweens count] != 0) { + [self removeConstraints:self->inBetweens]; + [self->inBetweens removeAllObjects]; + } + if (self->last != nil) { + [self removeConstraint:self->last]; + [self->last release]; + self->last = nil; + } + if ([self->widths count] != 0) { + [self removeConstraints:self->widths]; + [self->widths removeAllObjects]; + } + if ([self->leadings count] != 0) { + [self removeConstraints:self->leadings]; + [self->leadings removeAllObjects]; + } + if ([self->middles count] != 0) { + [self removeConstraints:self->middles]; + [self->middles removeAllObjects]; + } + if ([self->trailings count] != 0) { + [self removeConstraints:self->trailings]; + [self->trailings removeAllObjects]; + } +} + +- (void)syncEnableStates:(int)enabled +{ + formChild *fc; + + for (fc in self->children) + uiDarwinControlSyncEnableState(uiDarwinControl(fc.c), enabled); +} + +- (CGFloat)paddingAmount +{ + if (!self->padded) + return 0.0; + return uiDarwinPaddingAmount(NULL); +} + +- (void)establishOurConstraints +{ + formChild *fc; + CGFloat padding; + NSView *prev, *prevlabel;; + NSLayoutConstraint *c; + NSLayoutRelation relation; + + [self removeOurConstraints]; + if ([self->children count] == 0) + return; + padding = [self paddingAmount]; + + // first arrange the children vertically and make them the same width + prev = nil; + for (fc in self->children) { + if (prev == nil) { // first view + self->first = mkConstraint(self, NSLayoutAttributeTop, + NSLayoutRelationEqual, + [fc view], NSLayoutAttributeTop, + 1, 0, + @"uiForm first vertical constraint"); + [self addConstraint:self->first]; + [self->first retain]; + prev = [fc view]; + prevlabel = fc.label; + continue; + } + // not the first; link it + c = mkConstraint(prev, NSLayoutAttributeBottom, + NSLayoutRelationEqual, + [fc view], NSLayoutAttributeTop, + 1, -padding, + @"uiForm in-between vertical constraint"); + [self addConstraint:c]; + [self->inBetweens addObject:c]; + // and make the same width + c = mkConstraint(prev, NSLayoutAttributeWidth, + NSLayoutRelationEqual, + [fc view], NSLayoutAttributeWidth, + 1, 0, + @"uiForm control width constraint"); + [self addConstraint:c]; + [self->widths addObject:c]; + c = mkConstraint(prevlabel, NSLayoutAttributeWidth, + NSLayoutRelationEqual, + fc.label, NSLayoutAttributeWidth, + 1, 0, + @"uiForm label lwidth constraint"); + [self addConstraint:c]; + [self->widths addObject:c]; + prev = [fc view]; + prevlabel = fc.label; + } + relation = NSLayoutRelationEqual; + if (self->nStretchy != 0) + relation = NSLayoutRelationLessThanOrEqual; + self->last = mkConstraint(prev, NSLayoutAttributeBottom, + NSLayoutRelationEqual, + self, NSLayoutAttributeBottom, + 1, 0, + @"uiForm last vertical constraint"); + [self addConstraint:self->last]; + [self->last retain]; + + // now arrange the controls horizontally + for (fc in self->children) { + c = mkConstraint(self, NSLayoutAttributeLeading, + NSLayoutRelationEqual, + fc.label, NSLayoutAttributeLeading, + 1, 0, + @"uiForm leading constraint"); + [self addConstraint:c]; + [self->leadings addObject:c]; + c = mkConstraint(fc.label, NSLayoutAttributeTrailing, + NSLayoutRelationEqual, + [fc view], NSLayoutAttributeLeading, + 1, -padding, + @"uiForm middle constraint"); + [self addConstraint:c]; + [self->middles addObject:c]; + c = mkConstraint([fc view], NSLayoutAttributeTrailing, + NSLayoutRelationEqual, + self, NSLayoutAttributeTrailing, + 1, 0, + @"uiForm trailing constraint"); + [self addConstraint:c]; + [self->trailings addObject:c]; + } + + // we don't arrange the labels vertically; that's done when we add the control since those constraints don't need to change (they just need to be at their baseline) +} + +- (void)append:(NSString *)label c:(uiControl *)c stretchy:(int)stretchy +{ + formChild *fc; + NSLayoutPriority priority; + NSLayoutAttribute attribute; + uintmax_t oldnStretchy; + + fc = [formChild new]; + fc.c = c; + fc.label = newLabel(label); + [fc.label setTranslatesAutoresizingMaskIntoConstraints:NO]; + [self addSubview:fc.label]; + fc.stretchy = stretchy; + fc.oldHorzHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(fc.c), NSLayoutConstraintOrientationHorizontal); + fc.oldVertHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(fc.c), NSLayoutConstraintOrientationVertical); + + uiControlSetParent(fc.c, uiControl(self->f)); + uiDarwinControlSetSuperview(uiDarwinControl(fc.c), self); + uiDarwinControlSyncEnableState(uiDarwinControl(fc.c), uiControlEnabledToUser(uiControl(self->f))); + + // if a control is stretchy, it should not hug vertically + // otherwise, it should *forcibly* hug + if (fc.stretchy) + priority = NSLayoutPriorityDefaultLow; + else + // LONGTERM will default high work? + priority = NSLayoutPriorityRequired; + uiDarwinControlSetHuggingPriority(uiDarwinControl(fc.c), priority, NSLayoutConstraintOrientationVertical); + // make sure controls don't hug their horizontal direction so they fill the width of the view + uiDarwinControlSetHuggingPriority(uiDarwinControl(fc.c), NSLayoutPriorityDefaultLow, NSLayoutConstraintOrientationHorizontal); + + // and constrain the baselines to position the label vertically + // if the view is a scroll view, align tops, not baselines + // this is what Interface Builder does + attribute = NSLayoutAttributeBaseline; + if ([[fc view] isKindOfClass:[NSScrollView class]]) + attribute = NSLayoutAttributeTop; + fc.baseline = mkConstraint(fc.label, attribute, + NSLayoutRelationEqual, + [fc view], attribute, + 1, 0, + @"uiForm baseline constraint"); + [self addConstraint:fc.baseline]; + + [self->children addObject:fc]; + + [self establishOurConstraints]; + if (fc.stretchy) { + oldnStretchy = self->nStretchy; + self->nStretchy++; + if (oldnStretchy == 0) + uiDarwinNotifyEdgeHuggingChanged(uiDarwinControl(self->f)); + } + + [fc release]; // we don't need the initial reference now +} + +//TODO- (void)delete:(uintmax_t)n + +- (int)isPadded +{ + return self->padded; +} + +- (void)setPadded:(int)p +{ + CGFloat padding; + NSLayoutConstraint *c; + + self->padded = p; + padding = [self paddingAmount]; + for (c in self->inBetweens) + [c setConstant:-padding]; + for (c in self->middles) + [c setConstant:-padding]; +} + +- (BOOL)hugsTrailing +{ + return YES; // always hug trailing +} + +- (BOOL)hugsBottom +{ + // only hug if we have stretchy + return self->nStretchy != 0; +} + +@end + +static void uiFormDestroy(uiControl *c) +{ + uiForm *f = uiForm(c); + + [f->view onDestroy]; + [f->view release]; + uiFreeControl(uiControl(f)); +} + +uiDarwinControlDefaultHandle(uiForm, view) +uiDarwinControlDefaultParent(uiForm, view) +uiDarwinControlDefaultSetParent(uiForm, view) +uiDarwinControlDefaultToplevel(uiForm, view) +uiDarwinControlDefaultVisible(uiForm, view) +uiDarwinControlDefaultShow(uiForm, view) +uiDarwinControlDefaultHide(uiForm, view) +uiDarwinControlDefaultEnabled(uiForm, view) +uiDarwinControlDefaultEnable(uiForm, view) +uiDarwinControlDefaultDisable(uiForm, view) + +static void uiFormSyncEnableState(uiDarwinControl *c, int enabled) +{ + uiForm *f = uiForm(c); + + if (uiDarwinShouldStopSyncEnableState(uiDarwinControl(f), enabled)) + return; + [f->view syncEnableStates:enabled]; +} + +uiDarwinControlDefaultSetSuperview(uiForm, view) + +static BOOL uiFormHugsTrailingEdge(uiDarwinControl *c) +{ + uiForm *f = uiForm(c); + + return [f->view hugsTrailing]; +} + +static BOOL uiFormHugsBottom(uiDarwinControl *c) +{ + uiForm *f = uiForm(c); + + return [f->view hugsBottom]; +} + +static void uiFormChildEdgeHuggingChanged(uiDarwinControl *c) +{ + uiForm *f = uiForm(c); + + [f->view establishOurConstraints]; +} + +uiDarwinControlDefaultHuggingPriority(uiForm, view) +uiDarwinControlDefaultSetHuggingPriority(uiForm, view) + +void uiFormAppend(uiForm *f, const char *label, uiControl *c, int stretchy) +{ + // LONGTERM on other platforms + // or at leat allow this and implicitly turn it into a spacer + if (c == NULL) + userbug("You cannot add NULL to a uiForm."); + [f->view append:toNSString(label) c:c stretchy:stretchy]; +} + +int uiFormPadded(uiForm *f) +{ + return [f->view isPadded]; +} + +void uiFormSetPadded(uiForm *f, int padded) +{ + [f->view setPadded:padded]; +} + +uiForm *uiNewForm(void) +{ + uiForm *f; + + uiDarwinNewControl(uiForm, f); + + f->view = [[formView alloc] initWithF:f]; + + return f; +} diff --git a/darwin/label.m b/darwin/label.m index e2b9d540..897bc3ff 100644 --- a/darwin/label.m +++ b/darwin/label.m @@ -18,18 +18,26 @@ void uiLabelSetText(uiLabel *l, const char *text) [l->textfield setStringValue:toNSString(text)]; } +NSTextField *newLabel(NSString *str) +{ + NSTextField *tf; + + tf = [[NSTextField alloc] initWithFrame:NSZeroRect]; + [tf setStringValue:str]; + [tf setEditable:NO]; + [tf setSelectable:NO]; + [tf setDrawsBackground:NO]; + finishNewTextField(tf, NO); + return tf; +} + uiLabel *uiNewLabel(const char *text) { uiLabel *l; uiDarwinNewControl(uiLabel, l); - l->textfield = [[NSTextField alloc] initWithFrame:NSZeroRect]; - [l->textfield setStringValue:toNSString(text)]; - [l->textfield setEditable:NO]; - [l->textfield setSelectable:NO]; - [l->textfield setDrawsBackground:NO]; - finishNewTextField(l->textfield, NO); + l->textfield = newLabel(toNSString(text)); return l; } diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 7805fac8..79f9a07a 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -120,3 +120,6 @@ struct scrollViewData; extern NSScrollView *mkScrollView(struct scrollViewCreateParams *p, struct scrollViewData **dout); extern void scrollViewSetScrolling(NSScrollView *sv, struct scrollViewData *d, BOOL hscroll, BOOL vscroll); extern void scrollViewFreeData(NSScrollView *sv, struct scrollViewData *d); + +// label.cpp +extern NSTextField *newLabel(NSString *str); diff --git a/ui.h b/ui.h index 93488518..00c5481b 100644 --- a/ui.h +++ b/ui.h @@ -622,7 +622,7 @@ _UI_EXTERN uiColorButton *uiNewColorButton(void); typedef struct uiForm uiForm; #define uiForm(this) ((uiForm *) (this)) -_UI_EXTERN void uiFormAppend(uiForm *f, const char *label, uiControl *control, int stretchy); +_UI_EXTERN void uiFormAppend(uiForm *f, const char *label, uiControl *c, int stretchy); _UI_EXTERN int uiFormPadded(uiForm *f); _UI_EXTERN void uiFormSetPadded(uiForm *f, int padded); _UI_EXTERN uiForm *uiNewForm(void); From ff3c9f0c670461702d27a7b3af42d723f656f557 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 7 Jun 2016 14:35:43 -0400 Subject: [PATCH 0259/1329] Made uiSearchEntry appropriately rounded on OS X. --- darwin/entry.m | 3 +++ 1 file changed, 3 insertions(+) diff --git a/darwin/entry.m b/darwin/entry.m index 760fa83a..382e66a1 100644 --- a/darwin/entry.m +++ b/darwin/entry.m @@ -243,5 +243,8 @@ uiEntry *uiNewSearchEntry(void) s = (NSSearchField *) (e->textfield); [s setSendsSearchStringImmediately:NO]; [s setSendsWholeSearchString:NO]; + [s setBordered:NO]; + [s setBezelStyle:NSTextFieldRoundedBezel]; + [s setBezeled:YES]; return e; } From 7f05b796aa0d686ee631ab098c2c66df75f098ee Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 7 Jun 2016 14:38:03 -0400 Subject: [PATCH 0260/1329] Started rewriting the OS X uiForm auto layout. --- darwin/form.m | 80 ++------------------------------------------------- 1 file changed, 3 insertions(+), 77 deletions(-) diff --git a/darwin/form.m b/darwin/form.m index fdd8e79d..56b1485b 100644 --- a/darwin/form.m +++ b/darwin/form.m @@ -158,83 +158,6 @@ struct uiForm { return; padding = [self paddingAmount]; - // first arrange the children vertically and make them the same width - prev = nil; - for (fc in self->children) { - if (prev == nil) { // first view - self->first = mkConstraint(self, NSLayoutAttributeTop, - NSLayoutRelationEqual, - [fc view], NSLayoutAttributeTop, - 1, 0, - @"uiForm first vertical constraint"); - [self addConstraint:self->first]; - [self->first retain]; - prev = [fc view]; - prevlabel = fc.label; - continue; - } - // not the first; link it - c = mkConstraint(prev, NSLayoutAttributeBottom, - NSLayoutRelationEqual, - [fc view], NSLayoutAttributeTop, - 1, -padding, - @"uiForm in-between vertical constraint"); - [self addConstraint:c]; - [self->inBetweens addObject:c]; - // and make the same width - c = mkConstraint(prev, NSLayoutAttributeWidth, - NSLayoutRelationEqual, - [fc view], NSLayoutAttributeWidth, - 1, 0, - @"uiForm control width constraint"); - [self addConstraint:c]; - [self->widths addObject:c]; - c = mkConstraint(prevlabel, NSLayoutAttributeWidth, - NSLayoutRelationEqual, - fc.label, NSLayoutAttributeWidth, - 1, 0, - @"uiForm label lwidth constraint"); - [self addConstraint:c]; - [self->widths addObject:c]; - prev = [fc view]; - prevlabel = fc.label; - } - relation = NSLayoutRelationEqual; - if (self->nStretchy != 0) - relation = NSLayoutRelationLessThanOrEqual; - self->last = mkConstraint(prev, NSLayoutAttributeBottom, - NSLayoutRelationEqual, - self, NSLayoutAttributeBottom, - 1, 0, - @"uiForm last vertical constraint"); - [self addConstraint:self->last]; - [self->last retain]; - - // now arrange the controls horizontally - for (fc in self->children) { - c = mkConstraint(self, NSLayoutAttributeLeading, - NSLayoutRelationEqual, - fc.label, NSLayoutAttributeLeading, - 1, 0, - @"uiForm leading constraint"); - [self addConstraint:c]; - [self->leadings addObject:c]; - c = mkConstraint(fc.label, NSLayoutAttributeTrailing, - NSLayoutRelationEqual, - [fc view], NSLayoutAttributeLeading, - 1, -padding, - @"uiForm middle constraint"); - [self addConstraint:c]; - [self->middles addObject:c]; - c = mkConstraint([fc view], NSLayoutAttributeTrailing, - NSLayoutRelationEqual, - self, NSLayoutAttributeTrailing, - 1, 0, - @"uiForm trailing constraint"); - [self addConstraint:c]; - [self->trailings addObject:c]; - } - // we don't arrange the labels vertically; that's done when we add the control since those constraints don't need to change (they just need to be at their baseline) } @@ -249,6 +172,9 @@ struct uiForm { fc.c = c; fc.label = newLabel(label); [fc.label setTranslatesAutoresizingMaskIntoConstraints:NO]; + // and make the label no larger than it needs to be + [fc.label setContentHuggingPriority:NSLayoutPriorityRequired forOrientation:NSLayoutConstraintOrientationHorizontal]; + [fc.label setContentHuggingPriority:NSLayoutPriorityRequired forOrientation:NSLayoutConstraintOrientationVertical]; [self addSubview:fc.label]; fc.stretchy = stretchy; fc.oldHorzHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(fc.c), NSLayoutConstraintOrientationHorizontal); From 95fcceef2fc76bc79a82e8eaae6e2fe259ae7ab2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 7 Jun 2016 14:59:12 -0400 Subject: [PATCH 0261/1329] Re-establishing uiForm constraints: vertical constraints. --- darwin/form.m | 63 +++++++++++++++++++++++++++------------------------ 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/darwin/form.m b/darwin/form.m index 56b1485b..eb3bb2e7 100644 --- a/darwin/form.m +++ b/darwin/form.m @@ -20,10 +20,6 @@ NSLayoutConstraint *first; NSMutableArray *inBetweens; NSLayoutConstraint *last; - NSMutableArray *widths; - NSMutableArray *leadings; - NSMutableArray *middles; - NSMutableArray *trailings; } - (id)initWithF:(uiForm *)ff; - (void)onDestroy; @@ -65,10 +61,6 @@ struct uiForm { self->nStretchy = 0; self->inBetweens = [NSMutableArray new]; - self->widths = [NSMutableArray new]; - self->leadings = [NSMutableArray new]; - self->middles = [NSMutableArray new]; - self->trailings = [NSMutableArray new]; } return self; } @@ -79,10 +71,6 @@ struct uiForm { [self removeOurConstraints]; [self->inBetweens release]; - [self->widths release]; - [self->leadings release]; - [self->middles release]; - [self->trailings release]; for (fc in self->children) { [self removeConstraint:fc.baseline]; @@ -112,22 +100,6 @@ struct uiForm { [self->last release]; self->last = nil; } - if ([self->widths count] != 0) { - [self removeConstraints:self->widths]; - [self->widths removeAllObjects]; - } - if ([self->leadings count] != 0) { - [self removeConstraints:self->leadings]; - [self->leadings removeAllObjects]; - } - if ([self->middles count] != 0) { - [self removeConstraints:self->middles]; - [self->middles removeAllObjects]; - } - if ([self->trailings count] != 0) { - [self removeConstraints:self->trailings]; - [self->trailings removeAllObjects]; - } } - (void)syncEnableStates:(int)enabled @@ -158,6 +130,39 @@ struct uiForm { return; padding = [self paddingAmount]; + // first arrange the main controls vertically + prev = nil; + for (fc in self->children) { + if (prev == nil) { // first control; tie to top + self->first = mkConstraint(self, NSLayoutAttributeTop, + NSLayoutRelationEqual, + [fc view], NSLayoutAttributeTop, + 1, 0, + @"uiForm first child top constraint"); + [self addConstraint:self->first]; + [self->first retain]; + prev = [fc view]; + continue; + } + // not first; tie to previous + c = mkConstraint(prev, NSLayoutAttributeBottom, + NSLayoutRelationEqual, + [fc view], NSLayoutAttributeTop, + 1, -padding, + @"uiForm middle vertical constraint"); + [self addConstraint:c]; + [self->inBetweens addObject:c]; + prev = [fc view]; + } + // and the last one + self->last = mkConstraint(prev, NSLayoutAttributeBottom, + NSLayoutRelationEqual, + self, NSLayoutAttributeBottom, + 1, 0, + @"uiForm last child bottom constraint"); + [self addConstraint:self->last]; + [self->last retain]; + // we don't arrange the labels vertically; that's done when we add the control since those constraints don't need to change (they just need to be at their baseline) } @@ -237,8 +242,6 @@ struct uiForm { padding = [self paddingAmount]; for (c in self->inBetweens) [c setConstant:-padding]; - for (c in self->middles) - [c setConstant:-padding]; } - (BOOL)hugsTrailing From 39f76bdb256dac96b25fb815744b017ada65efb4 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 7 Jun 2016 15:04:14 -0400 Subject: [PATCH 0262/1329] Part 2: leading and trailing edges. --- darwin/form.m | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/darwin/form.m b/darwin/form.m index eb3bb2e7..0e3c4b86 100644 --- a/darwin/form.m +++ b/darwin/form.m @@ -20,6 +20,7 @@ NSLayoutConstraint *first; NSMutableArray *inBetweens; NSLayoutConstraint *last; + NSMutableArray *hEdges; } - (id)initWithF:(uiForm *)ff; - (void)onDestroy; @@ -61,6 +62,7 @@ struct uiForm { self->nStretchy = 0; self->inBetweens = [NSMutableArray new]; + self->hEdges = [NSMutableArray new]; } return self; } @@ -71,6 +73,7 @@ struct uiForm { [self removeOurConstraints]; [self->inBetweens release]; + [self->hEdges release]; for (fc in self->children) { [self removeConstraint:fc.baseline]; @@ -100,6 +103,10 @@ struct uiForm { [self->last release]; self->last = nil; } + if ([self->hEdges count] != 0) { + [self removeConstraints:self->hEdges]; + [self->hEdges removeAllObjects]; + } } - (void)syncEnableStates:(int)enabled @@ -163,6 +170,24 @@ struct uiForm { [self addConstraint:self->last]; [self->last retain]; + // now tiethe labels to the left (weakly, for right-alignment) and tie the controls to the right (strongly) + for (fc in self->children) { + c = mkConstraint(self, NSLayoutAttributeLeading, + NSLayoutRelationLessThanOrEqual, + fc.label, NSLayoutAttributeLeading, + 1, 0, + @"uiForm label leading edge constraint"); + [self addConstraint:c]; + [self->hEdges addObject:c]; + c = mkConstraint([fc view], NSLayoutAttributeTrailing, + NSLayoutRelationEqual, + self, NSLayoutAttributeTrailing, + 1, 0, + @"uiForm child trailing edge constraint"); + [self addConstraint:c]; + [self->hEdges addObject:c]; + } + // we don't arrange the labels vertically; that's done when we add the control since those constraints don't need to change (they just need to be at their baseline) } From 4fde8121568a9955ee265fd3347088c03e06ac76 Mon Sep 17 00:00:00 2001 From: Hugo Schmitt Date: Tue, 7 Jun 2016 16:09:03 -0300 Subject: [PATCH 0263/1329] Cmake 2.8.12 doesn't understand LANGUAGES keyword Fortunately we can just use project(libui) since C and CPP are the languages enabled by default. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 262af94f..72bf3fed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,7 +24,7 @@ if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE DEBUG CACHE STRING "" FORCE) endif() -project(libui LANGUAGES C CXX) +project(libui) option(BUILD_SHARED_LIBS "Whether to build libui as a shared library or a static library" ON) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/out") From 958893a73d48411f79952701cc18e1b656b8e8bd Mon Sep 17 00:00:00 2001 From: Gregory Haberek Date: Tue, 7 Jun 2016 17:45:57 -0400 Subject: [PATCH 0264/1329] Added Euphoria binding to README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a2c0b1ef..9e124a07 100644 --- a/README.md +++ b/README.md @@ -141,6 +141,7 @@ Language | Bindings C#/.net | [LibUI.Binding](https://github.com/NattyNarwhal/LibUI.Binding), [SharpUI](https://github.com/benpye/sharpui/) Crystal | [libui.cr](https://github.com/Fusion/libui.cr) D | [DerelictLibui](https://github.com/Extrawurst/DerelictLibui) +Euphoria | [libui-euphoria](https://github.com/ghaberek/libui-euphoria) Haskell | [libui-haskell](https://github.com/ajnsit/libui-haskell) JavaScript | [libui.js (merged into libui-node?)](https://github.com/mavenave/libui.js) Julia | [Libui.jl](https://github.com/joa-quim/Libui.jl) From 0fa706b8d4773498a5d3ab46f04284f2d1ecb8a1 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 7 Jun 2016 18:49:59 -0400 Subject: [PATCH 0265/1329] Undid all of that. There's an easier way... --- darwin/form.m | 90 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 68 insertions(+), 22 deletions(-) diff --git a/darwin/form.m b/darwin/form.m index 0e3c4b86..fdd8e79d 100644 --- a/darwin/form.m +++ b/darwin/form.m @@ -20,7 +20,10 @@ NSLayoutConstraint *first; NSMutableArray *inBetweens; NSLayoutConstraint *last; - NSMutableArray *hEdges; + NSMutableArray *widths; + NSMutableArray *leadings; + NSMutableArray *middles; + NSMutableArray *trailings; } - (id)initWithF:(uiForm *)ff; - (void)onDestroy; @@ -62,7 +65,10 @@ struct uiForm { self->nStretchy = 0; self->inBetweens = [NSMutableArray new]; - self->hEdges = [NSMutableArray new]; + self->widths = [NSMutableArray new]; + self->leadings = [NSMutableArray new]; + self->middles = [NSMutableArray new]; + self->trailings = [NSMutableArray new]; } return self; } @@ -73,7 +79,10 @@ struct uiForm { [self removeOurConstraints]; [self->inBetweens release]; - [self->hEdges release]; + [self->widths release]; + [self->leadings release]; + [self->middles release]; + [self->trailings release]; for (fc in self->children) { [self removeConstraint:fc.baseline]; @@ -103,9 +112,21 @@ struct uiForm { [self->last release]; self->last = nil; } - if ([self->hEdges count] != 0) { - [self removeConstraints:self->hEdges]; - [self->hEdges removeAllObjects]; + if ([self->widths count] != 0) { + [self removeConstraints:self->widths]; + [self->widths removeAllObjects]; + } + if ([self->leadings count] != 0) { + [self removeConstraints:self->leadings]; + [self->leadings removeAllObjects]; + } + if ([self->middles count] != 0) { + [self removeConstraints:self->middles]; + [self->middles removeAllObjects]; + } + if ([self->trailings count] != 0) { + [self removeConstraints:self->trailings]; + [self->trailings removeAllObjects]; } } @@ -137,55 +158,81 @@ struct uiForm { return; padding = [self paddingAmount]; - // first arrange the main controls vertically + // first arrange the children vertically and make them the same width prev = nil; for (fc in self->children) { - if (prev == nil) { // first control; tie to top + if (prev == nil) { // first view self->first = mkConstraint(self, NSLayoutAttributeTop, NSLayoutRelationEqual, [fc view], NSLayoutAttributeTop, 1, 0, - @"uiForm first child top constraint"); + @"uiForm first vertical constraint"); [self addConstraint:self->first]; [self->first retain]; prev = [fc view]; + prevlabel = fc.label; continue; } - // not first; tie to previous + // not the first; link it c = mkConstraint(prev, NSLayoutAttributeBottom, NSLayoutRelationEqual, [fc view], NSLayoutAttributeTop, 1, -padding, - @"uiForm middle vertical constraint"); + @"uiForm in-between vertical constraint"); [self addConstraint:c]; [self->inBetweens addObject:c]; + // and make the same width + c = mkConstraint(prev, NSLayoutAttributeWidth, + NSLayoutRelationEqual, + [fc view], NSLayoutAttributeWidth, + 1, 0, + @"uiForm control width constraint"); + [self addConstraint:c]; + [self->widths addObject:c]; + c = mkConstraint(prevlabel, NSLayoutAttributeWidth, + NSLayoutRelationEqual, + fc.label, NSLayoutAttributeWidth, + 1, 0, + @"uiForm label lwidth constraint"); + [self addConstraint:c]; + [self->widths addObject:c]; prev = [fc view]; + prevlabel = fc.label; } - // and the last one + relation = NSLayoutRelationEqual; + if (self->nStretchy != 0) + relation = NSLayoutRelationLessThanOrEqual; self->last = mkConstraint(prev, NSLayoutAttributeBottom, NSLayoutRelationEqual, self, NSLayoutAttributeBottom, 1, 0, - @"uiForm last child bottom constraint"); + @"uiForm last vertical constraint"); [self addConstraint:self->last]; [self->last retain]; - // now tiethe labels to the left (weakly, for right-alignment) and tie the controls to the right (strongly) + // now arrange the controls horizontally for (fc in self->children) { c = mkConstraint(self, NSLayoutAttributeLeading, - NSLayoutRelationLessThanOrEqual, + NSLayoutRelationEqual, fc.label, NSLayoutAttributeLeading, 1, 0, - @"uiForm label leading edge constraint"); + @"uiForm leading constraint"); [self addConstraint:c]; - [self->hEdges addObject:c]; + [self->leadings addObject:c]; + c = mkConstraint(fc.label, NSLayoutAttributeTrailing, + NSLayoutRelationEqual, + [fc view], NSLayoutAttributeLeading, + 1, -padding, + @"uiForm middle constraint"); + [self addConstraint:c]; + [self->middles addObject:c]; c = mkConstraint([fc view], NSLayoutAttributeTrailing, NSLayoutRelationEqual, self, NSLayoutAttributeTrailing, 1, 0, - @"uiForm child trailing edge constraint"); + @"uiForm trailing constraint"); [self addConstraint:c]; - [self->hEdges addObject:c]; + [self->trailings addObject:c]; } // we don't arrange the labels vertically; that's done when we add the control since those constraints don't need to change (they just need to be at their baseline) @@ -202,9 +249,6 @@ struct uiForm { fc.c = c; fc.label = newLabel(label); [fc.label setTranslatesAutoresizingMaskIntoConstraints:NO]; - // and make the label no larger than it needs to be - [fc.label setContentHuggingPriority:NSLayoutPriorityRequired forOrientation:NSLayoutConstraintOrientationHorizontal]; - [fc.label setContentHuggingPriority:NSLayoutPriorityRequired forOrientation:NSLayoutConstraintOrientationVertical]; [self addSubview:fc.label]; fc.stretchy = stretchy; fc.oldHorzHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(fc.c), NSLayoutConstraintOrientationHorizontal); @@ -267,6 +311,8 @@ struct uiForm { padding = [self paddingAmount]; for (c in self->inBetweens) [c setConstant:-padding]; + for (c in self->middles) + [c setConstant:-padding]; } - (BOOL)hugsTrailing From 0fe5214c09ead8b21dd1a29ce375f7f6041b3f6a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 7 Jun 2016 18:57:02 -0400 Subject: [PATCH 0266/1329] Revert "Undid all of that. There's an easier way..." ...that only works for nibs. :( This reverts commit 0fa706b8d4773498a5d3ab46f04284f2d1ecb8a1. --- darwin/form.m | 90 +++++++++++++-------------------------------------- 1 file changed, 22 insertions(+), 68 deletions(-) diff --git a/darwin/form.m b/darwin/form.m index fdd8e79d..0e3c4b86 100644 --- a/darwin/form.m +++ b/darwin/form.m @@ -20,10 +20,7 @@ NSLayoutConstraint *first; NSMutableArray *inBetweens; NSLayoutConstraint *last; - NSMutableArray *widths; - NSMutableArray *leadings; - NSMutableArray *middles; - NSMutableArray *trailings; + NSMutableArray *hEdges; } - (id)initWithF:(uiForm *)ff; - (void)onDestroy; @@ -65,10 +62,7 @@ struct uiForm { self->nStretchy = 0; self->inBetweens = [NSMutableArray new]; - self->widths = [NSMutableArray new]; - self->leadings = [NSMutableArray new]; - self->middles = [NSMutableArray new]; - self->trailings = [NSMutableArray new]; + self->hEdges = [NSMutableArray new]; } return self; } @@ -79,10 +73,7 @@ struct uiForm { [self removeOurConstraints]; [self->inBetweens release]; - [self->widths release]; - [self->leadings release]; - [self->middles release]; - [self->trailings release]; + [self->hEdges release]; for (fc in self->children) { [self removeConstraint:fc.baseline]; @@ -112,21 +103,9 @@ struct uiForm { [self->last release]; self->last = nil; } - if ([self->widths count] != 0) { - [self removeConstraints:self->widths]; - [self->widths removeAllObjects]; - } - if ([self->leadings count] != 0) { - [self removeConstraints:self->leadings]; - [self->leadings removeAllObjects]; - } - if ([self->middles count] != 0) { - [self removeConstraints:self->middles]; - [self->middles removeAllObjects]; - } - if ([self->trailings count] != 0) { - [self removeConstraints:self->trailings]; - [self->trailings removeAllObjects]; + if ([self->hEdges count] != 0) { + [self removeConstraints:self->hEdges]; + [self->hEdges removeAllObjects]; } } @@ -158,81 +137,55 @@ struct uiForm { return; padding = [self paddingAmount]; - // first arrange the children vertically and make them the same width + // first arrange the main controls vertically prev = nil; for (fc in self->children) { - if (prev == nil) { // first view + if (prev == nil) { // first control; tie to top self->first = mkConstraint(self, NSLayoutAttributeTop, NSLayoutRelationEqual, [fc view], NSLayoutAttributeTop, 1, 0, - @"uiForm first vertical constraint"); + @"uiForm first child top constraint"); [self addConstraint:self->first]; [self->first retain]; prev = [fc view]; - prevlabel = fc.label; continue; } - // not the first; link it + // not first; tie to previous c = mkConstraint(prev, NSLayoutAttributeBottom, NSLayoutRelationEqual, [fc view], NSLayoutAttributeTop, 1, -padding, - @"uiForm in-between vertical constraint"); + @"uiForm middle vertical constraint"); [self addConstraint:c]; [self->inBetweens addObject:c]; - // and make the same width - c = mkConstraint(prev, NSLayoutAttributeWidth, - NSLayoutRelationEqual, - [fc view], NSLayoutAttributeWidth, - 1, 0, - @"uiForm control width constraint"); - [self addConstraint:c]; - [self->widths addObject:c]; - c = mkConstraint(prevlabel, NSLayoutAttributeWidth, - NSLayoutRelationEqual, - fc.label, NSLayoutAttributeWidth, - 1, 0, - @"uiForm label lwidth constraint"); - [self addConstraint:c]; - [self->widths addObject:c]; prev = [fc view]; - prevlabel = fc.label; } - relation = NSLayoutRelationEqual; - if (self->nStretchy != 0) - relation = NSLayoutRelationLessThanOrEqual; + // and the last one self->last = mkConstraint(prev, NSLayoutAttributeBottom, NSLayoutRelationEqual, self, NSLayoutAttributeBottom, 1, 0, - @"uiForm last vertical constraint"); + @"uiForm last child bottom constraint"); [self addConstraint:self->last]; [self->last retain]; - // now arrange the controls horizontally + // now tiethe labels to the left (weakly, for right-alignment) and tie the controls to the right (strongly) for (fc in self->children) { c = mkConstraint(self, NSLayoutAttributeLeading, - NSLayoutRelationEqual, + NSLayoutRelationLessThanOrEqual, fc.label, NSLayoutAttributeLeading, 1, 0, - @"uiForm leading constraint"); + @"uiForm label leading edge constraint"); [self addConstraint:c]; - [self->leadings addObject:c]; - c = mkConstraint(fc.label, NSLayoutAttributeTrailing, - NSLayoutRelationEqual, - [fc view], NSLayoutAttributeLeading, - 1, -padding, - @"uiForm middle constraint"); - [self addConstraint:c]; - [self->middles addObject:c]; + [self->hEdges addObject:c]; c = mkConstraint([fc view], NSLayoutAttributeTrailing, NSLayoutRelationEqual, self, NSLayoutAttributeTrailing, 1, 0, - @"uiForm trailing constraint"); + @"uiForm child trailing edge constraint"); [self addConstraint:c]; - [self->trailings addObject:c]; + [self->hEdges addObject:c]; } // we don't arrange the labels vertically; that's done when we add the control since those constraints don't need to change (they just need to be at their baseline) @@ -249,6 +202,9 @@ struct uiForm { fc.c = c; fc.label = newLabel(label); [fc.label setTranslatesAutoresizingMaskIntoConstraints:NO]; + // and make the label no larger than it needs to be + [fc.label setContentHuggingPriority:NSLayoutPriorityRequired forOrientation:NSLayoutConstraintOrientationHorizontal]; + [fc.label setContentHuggingPriority:NSLayoutPriorityRequired forOrientation:NSLayoutConstraintOrientationVertical]; [self addSubview:fc.label]; fc.stretchy = stretchy; fc.oldHorzHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(fc.c), NSLayoutConstraintOrientationHorizontal); @@ -311,8 +267,6 @@ struct uiForm { padding = [self paddingAmount]; for (c in self->inBetweens) [c setConstant:-padding]; - for (c in self->middles) - [c setConstant:-padding]; } - (BOOL)hugsTrailing From 2f3c0449bac45eca943c95f22bf8206b6d97b68f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 7 Jun 2016 23:24:29 -0400 Subject: [PATCH 0267/1329] Tried to debug all this ambiguity. Argh. --- darwin/form.m | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/darwin/form.m b/darwin/form.m index 0e3c4b86..0317ce21 100644 --- a/darwin/form.m +++ b/darwin/form.m @@ -186,6 +186,14 @@ struct uiForm { @"uiForm child trailing edge constraint"); [self addConstraint:c]; [self->hEdges addObject:c]; + + c = mkConstraint(fc.label, NSLayoutAttributeTrailing, + NSLayoutRelationEqual, + [fc view], NSLayoutAttributeLeading, + 1, 0, + @"TODO"); + [self addConstraint:c]; + [self->inBetweens addObject:c]; } // we don't arrange the labels vertically; that's done when we add the control since those constraints don't need to change (they just need to be at their baseline) @@ -263,6 +271,11 @@ struct uiForm { CGFloat padding; NSLayoutConstraint *c; +dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3*NSEC_PER_SEC), +dispatch_get_main_queue(), +^{ +[[self window]visualizeConstraints:[self constraints]]; +}); self->padded = p; padding = [self paddingAmount]; for (c in self->inBetweens) From 9295af59abf6beb41fcc9b4a39739708b31c61f2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 7 Jun 2016 23:25:01 -0400 Subject: [PATCH 0268/1329] More TODOs. --- darwin/tab.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/darwin/tab.m b/darwin/tab.m index d5b5885d..c3fac1d1 100644 --- a/darwin/tab.m +++ b/darwin/tab.m @@ -1,6 +1,8 @@ // 15 august 2015 #import "uipriv_darwin.h" +// TODO need to jiggle on tab change too (second page disabled tab label initially ambiguous) + @interface tabPage : NSObject { struct singleChildConstraints constraints; int margined; From 45d394fc814a5212b4a8009a14718e69b318cbd0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 8 Jun 2016 07:54:43 -0400 Subject: [PATCH 0269/1329] Okay new plan. --- darwin/form.m | 103 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 68 insertions(+), 35 deletions(-) diff --git a/darwin/form.m b/darwin/form.m index 0317ce21..fdd8e79d 100644 --- a/darwin/form.m +++ b/darwin/form.m @@ -20,7 +20,10 @@ NSLayoutConstraint *first; NSMutableArray *inBetweens; NSLayoutConstraint *last; - NSMutableArray *hEdges; + NSMutableArray *widths; + NSMutableArray *leadings; + NSMutableArray *middles; + NSMutableArray *trailings; } - (id)initWithF:(uiForm *)ff; - (void)onDestroy; @@ -62,7 +65,10 @@ struct uiForm { self->nStretchy = 0; self->inBetweens = [NSMutableArray new]; - self->hEdges = [NSMutableArray new]; + self->widths = [NSMutableArray new]; + self->leadings = [NSMutableArray new]; + self->middles = [NSMutableArray new]; + self->trailings = [NSMutableArray new]; } return self; } @@ -73,7 +79,10 @@ struct uiForm { [self removeOurConstraints]; [self->inBetweens release]; - [self->hEdges release]; + [self->widths release]; + [self->leadings release]; + [self->middles release]; + [self->trailings release]; for (fc in self->children) { [self removeConstraint:fc.baseline]; @@ -103,9 +112,21 @@ struct uiForm { [self->last release]; self->last = nil; } - if ([self->hEdges count] != 0) { - [self removeConstraints:self->hEdges]; - [self->hEdges removeAllObjects]; + if ([self->widths count] != 0) { + [self removeConstraints:self->widths]; + [self->widths removeAllObjects]; + } + if ([self->leadings count] != 0) { + [self removeConstraints:self->leadings]; + [self->leadings removeAllObjects]; + } + if ([self->middles count] != 0) { + [self removeConstraints:self->middles]; + [self->middles removeAllObjects]; + } + if ([self->trailings count] != 0) { + [self removeConstraints:self->trailings]; + [self->trailings removeAllObjects]; } } @@ -137,63 +158,81 @@ struct uiForm { return; padding = [self paddingAmount]; - // first arrange the main controls vertically + // first arrange the children vertically and make them the same width prev = nil; for (fc in self->children) { - if (prev == nil) { // first control; tie to top + if (prev == nil) { // first view self->first = mkConstraint(self, NSLayoutAttributeTop, NSLayoutRelationEqual, [fc view], NSLayoutAttributeTop, 1, 0, - @"uiForm first child top constraint"); + @"uiForm first vertical constraint"); [self addConstraint:self->first]; [self->first retain]; prev = [fc view]; + prevlabel = fc.label; continue; } - // not first; tie to previous + // not the first; link it c = mkConstraint(prev, NSLayoutAttributeBottom, NSLayoutRelationEqual, [fc view], NSLayoutAttributeTop, 1, -padding, - @"uiForm middle vertical constraint"); + @"uiForm in-between vertical constraint"); [self addConstraint:c]; [self->inBetweens addObject:c]; + // and make the same width + c = mkConstraint(prev, NSLayoutAttributeWidth, + NSLayoutRelationEqual, + [fc view], NSLayoutAttributeWidth, + 1, 0, + @"uiForm control width constraint"); + [self addConstraint:c]; + [self->widths addObject:c]; + c = mkConstraint(prevlabel, NSLayoutAttributeWidth, + NSLayoutRelationEqual, + fc.label, NSLayoutAttributeWidth, + 1, 0, + @"uiForm label lwidth constraint"); + [self addConstraint:c]; + [self->widths addObject:c]; prev = [fc view]; + prevlabel = fc.label; } - // and the last one + relation = NSLayoutRelationEqual; + if (self->nStretchy != 0) + relation = NSLayoutRelationLessThanOrEqual; self->last = mkConstraint(prev, NSLayoutAttributeBottom, NSLayoutRelationEqual, self, NSLayoutAttributeBottom, 1, 0, - @"uiForm last child bottom constraint"); + @"uiForm last vertical constraint"); [self addConstraint:self->last]; [self->last retain]; - // now tiethe labels to the left (weakly, for right-alignment) and tie the controls to the right (strongly) + // now arrange the controls horizontally for (fc in self->children) { c = mkConstraint(self, NSLayoutAttributeLeading, - NSLayoutRelationLessThanOrEqual, + NSLayoutRelationEqual, fc.label, NSLayoutAttributeLeading, 1, 0, - @"uiForm label leading edge constraint"); + @"uiForm leading constraint"); [self addConstraint:c]; - [self->hEdges addObject:c]; + [self->leadings addObject:c]; + c = mkConstraint(fc.label, NSLayoutAttributeTrailing, + NSLayoutRelationEqual, + [fc view], NSLayoutAttributeLeading, + 1, -padding, + @"uiForm middle constraint"); + [self addConstraint:c]; + [self->middles addObject:c]; c = mkConstraint([fc view], NSLayoutAttributeTrailing, NSLayoutRelationEqual, self, NSLayoutAttributeTrailing, 1, 0, - @"uiForm child trailing edge constraint"); + @"uiForm trailing constraint"); [self addConstraint:c]; - [self->hEdges addObject:c]; - - c = mkConstraint(fc.label, NSLayoutAttributeTrailing, - NSLayoutRelationEqual, - [fc view], NSLayoutAttributeLeading, - 1, 0, - @"TODO"); - [self addConstraint:c]; - [self->inBetweens addObject:c]; + [self->trailings addObject:c]; } // we don't arrange the labels vertically; that's done when we add the control since those constraints don't need to change (they just need to be at their baseline) @@ -210,9 +249,6 @@ struct uiForm { fc.c = c; fc.label = newLabel(label); [fc.label setTranslatesAutoresizingMaskIntoConstraints:NO]; - // and make the label no larger than it needs to be - [fc.label setContentHuggingPriority:NSLayoutPriorityRequired forOrientation:NSLayoutConstraintOrientationHorizontal]; - [fc.label setContentHuggingPriority:NSLayoutPriorityRequired forOrientation:NSLayoutConstraintOrientationVertical]; [self addSubview:fc.label]; fc.stretchy = stretchy; fc.oldHorzHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(fc.c), NSLayoutConstraintOrientationHorizontal); @@ -271,15 +307,12 @@ struct uiForm { CGFloat padding; NSLayoutConstraint *c; -dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3*NSEC_PER_SEC), -dispatch_get_main_queue(), -^{ -[[self window]visualizeConstraints:[self constraints]]; -}); self->padded = p; padding = [self paddingAmount]; for (c in self->inBetweens) [c setConstant:-padding]; + for (c in self->middles) + [c setConstant:-padding]; } - (BOOL)hugsTrailing From d2323427793814c7f9393edce5381039ce940eff Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 8 Jun 2016 08:55:57 -0400 Subject: [PATCH 0270/1329] I give up. MCVE time. --- darwin/form.m | 75 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 62 insertions(+), 13 deletions(-) diff --git a/darwin/form.m b/darwin/form.m index fdd8e79d..7064c66c 100644 --- a/darwin/form.m +++ b/darwin/form.m @@ -1,13 +1,18 @@ // 7 june 2016 #import "uipriv_darwin.h" -@interface formChild : NSObject +@interface formChild : NSView @property uiControl *c; @property (strong) NSTextField *label; @property BOOL stretchy; @property NSLayoutPriority oldHorzHuggingPri; @property NSLayoutPriority oldVertHuggingPri; @property (strong) NSLayoutConstraint *baseline; +@property (strong) NSLayoutConstraint *trailing; +@property (strong) NSLayoutConstraint *top; +@property (strong) NSLayoutConstraint *bottom; +- (id)initWithLabel:(NSTextField *)l; +- (void)onDestroy; - (NSView *)view; @end @@ -46,6 +51,51 @@ struct uiForm { @implementation formChild +- (id)initWithLabel:(NSTextField *)l +{ + self = [super initWithFrame:NSZeroRect]; + if (self) { + self.label = l; + [self.label setTranslatesAutoresizingMaskIntoConstraints:NO]; + [self.label setContentHuggingPriority:NSLayoutPriorityRequired forOrientation:NSLayoutConstraintOrientationHorizontal]; + [self.label setContentHuggingPriority:NSLayoutPriorityRequired forOrientation:NSLayoutConstraintOrientationVertical]; + [self addSubview:self.label]; + + self.trailing = mkConstraint(self.label, NSLayoutAttributeTrailing, + NSLayoutRelationEqual, + self, NSLayoutAttributeTrailing, + 1, 0, + @"uiForm label trailing"); + [self addConstraint:self.trailing]; + self.top = mkConstraint(self.label, NSLayoutAttributeTop, + NSLayoutRelationEqual, + self, NSLayoutAttributeTop, + 1, 0, + @"uiForm label top"); + [self addConstraint:self.top]; + self.bottom = mkConstraint(self.label, NSLayoutAttributeBottom, + NSLayoutRelationEqual, + self, NSLayoutAttributeBottom, + 1, 0, + @"uiForm label bottom"); + [self addConstraint:self.bottom]; + } + return self; +} + +- (void)onDestroy +{ + [self removeConstraint:self.trailing]; + self.trailing = nil; + [self removeConstraint:self.top]; + self.top = nil; + [self removeConstraint:self.bottom]; + self.bottom = nil; + + [self.label removeFromSuperview]; + self.label = nil; +} + - (NSView *)view { return (NSView *) uiControlHandle(self.c); @@ -90,8 +140,8 @@ struct uiForm { uiControlSetParent(fc.c, NULL); uiDarwinControlSetSuperview(uiDarwinControl(fc.c), nil); uiControlDestroy(fc.c); - [fc.label removeFromSuperview]; - fc.label = nil; + [fc onDestroy]; + [fc removeFromSuperview]; } [self->children release]; } @@ -149,7 +199,7 @@ struct uiForm { { formChild *fc; CGFloat padding; - NSView *prev, *prevlabel;; + NSView *prev, *prevlabel; NSLayoutConstraint *c; NSLayoutRelation relation; @@ -170,7 +220,7 @@ struct uiForm { [self addConstraint:self->first]; [self->first retain]; prev = [fc view]; - prevlabel = fc.label; + prevlabel = fc; continue; } // not the first; link it @@ -191,13 +241,13 @@ struct uiForm { [self->widths addObject:c]; c = mkConstraint(prevlabel, NSLayoutAttributeWidth, NSLayoutRelationEqual, - fc.label, NSLayoutAttributeWidth, + fc, NSLayoutAttributeWidth, 1, 0, @"uiForm label lwidth constraint"); [self addConstraint:c]; [self->widths addObject:c]; prev = [fc view]; - prevlabel = fc.label; + prevlabel = fc; } relation = NSLayoutRelationEqual; if (self->nStretchy != 0) @@ -214,12 +264,12 @@ struct uiForm { for (fc in self->children) { c = mkConstraint(self, NSLayoutAttributeLeading, NSLayoutRelationEqual, - fc.label, NSLayoutAttributeLeading, + fc, NSLayoutAttributeLeading, 1, 0, @"uiForm leading constraint"); [self addConstraint:c]; [self->leadings addObject:c]; - c = mkConstraint(fc.label, NSLayoutAttributeTrailing, + c = mkConstraint(fc, NSLayoutAttributeTrailing, NSLayoutRelationEqual, [fc view], NSLayoutAttributeLeading, 1, -padding, @@ -245,14 +295,13 @@ struct uiForm { NSLayoutAttribute attribute; uintmax_t oldnStretchy; - fc = [formChild new]; + fc = [[formChild alloc] initWithLabel:newLabel(label)]; fc.c = c; - fc.label = newLabel(label); - [fc.label setTranslatesAutoresizingMaskIntoConstraints:NO]; - [self addSubview:fc.label]; fc.stretchy = stretchy; fc.oldHorzHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(fc.c), NSLayoutConstraintOrientationHorizontal); fc.oldVertHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(fc.c), NSLayoutConstraintOrientationVertical); + [fc setTranslatesAutoresizingMaskIntoConstraints:NO]; + [self addSubview:fc]; uiControlSetParent(fc.c, uiControl(self->f)); uiDarwinControlSetSuperview(uiDarwinControl(fc.c), self); From 69cad5e6081b36b918b313670c19eaf97a57cd25 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 8 Jun 2016 16:26:23 -0400 Subject: [PATCH 0271/1329] Fixed uiForm on OS X. --- darwin/form.m | 43 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/darwin/form.m b/darwin/form.m index 7064c66c..67e3843b 100644 --- a/darwin/form.m +++ b/darwin/form.m @@ -1,6 +1,8 @@ // 7 june 2016 #import "uipriv_darwin.h" +// TODO in the test program, sometimes one of the radio buttons can disappear (try when spaced) + @interface formChild : NSView @property uiControl *c; @property (strong) NSTextField *label; @@ -8,8 +10,9 @@ @property NSLayoutPriority oldHorzHuggingPri; @property NSLayoutPriority oldVertHuggingPri; @property (strong) NSLayoutConstraint *baseline; -@property (strong) NSLayoutConstraint *trailing; +@property (strong) NSLayoutConstraint *leading; @property (strong) NSLayoutConstraint *top; +@property (strong) NSLayoutConstraint *trailing; @property (strong) NSLayoutConstraint *bottom; - (id)initWithLabel:(NSTextField *)l; - (void)onDestroy; @@ -59,20 +62,28 @@ struct uiForm { [self.label setTranslatesAutoresizingMaskIntoConstraints:NO]; [self.label setContentHuggingPriority:NSLayoutPriorityRequired forOrientation:NSLayoutConstraintOrientationHorizontal]; [self.label setContentHuggingPriority:NSLayoutPriorityRequired forOrientation:NSLayoutConstraintOrientationVertical]; + [self.label setContentCompressionResistancePriority:NSLayoutPriorityRequired forOrientation:NSLayoutConstraintOrientationHorizontal]; + [self.label setContentCompressionResistancePriority:NSLayoutPriorityRequired forOrientation:NSLayoutConstraintOrientationVertical]; [self addSubview:self.label]; - self.trailing = mkConstraint(self.label, NSLayoutAttributeTrailing, - NSLayoutRelationEqual, - self, NSLayoutAttributeTrailing, + self.leading = mkConstraint(self.label, NSLayoutAttributeLeading, + NSLayoutRelationGreaterThanOrEqual, + self, NSLayoutAttributeLeading, 1, 0, - @"uiForm label trailing"); - [self addConstraint:self.trailing]; + @"uiForm label leading"); + [self addConstraint:self.leading]; self.top = mkConstraint(self.label, NSLayoutAttributeTop, NSLayoutRelationEqual, self, NSLayoutAttributeTop, 1, 0, @"uiForm label top"); [self addConstraint:self.top]; + self.trailing = mkConstraint(self.label, NSLayoutAttributeTrailing, + NSLayoutRelationEqual, + self, NSLayoutAttributeTrailing, + 1, 0, + @"uiForm label trailing"); + [self addConstraint:self.trailing]; self.bottom = mkConstraint(self.label, NSLayoutAttributeBottom, NSLayoutRelationEqual, self, NSLayoutAttributeBottom, @@ -253,7 +264,7 @@ struct uiForm { if (self->nStretchy != 0) relation = NSLayoutRelationLessThanOrEqual; self->last = mkConstraint(prev, NSLayoutAttributeBottom, - NSLayoutRelationEqual, + relation, self, NSLayoutAttributeBottom, 1, 0, @"uiForm last vertical constraint"); @@ -269,6 +280,16 @@ struct uiForm { @"uiForm leading constraint"); [self addConstraint:c]; [self->leadings addObject:c]; + // coerce the control to be as wide as possible + // see http://stackoverflow.com/questions/37710892/in-auto-layout-i-set-up-labels-that-shouldnt-grow-horizontally-and-controls-th + c = mkConstraint(self, NSLayoutAttributeLeading, + NSLayoutRelationEqual, + [fc view], NSLayoutAttributeLeading, + 1, 0, + @"uiForm leading constraint"); + [c setPriority:NSLayoutPriorityDefaultHigh]; + [self addConstraint:c]; + [self->leadings addObject:c]; c = mkConstraint(fc, NSLayoutAttributeTrailing, NSLayoutRelationEqual, [fc view], NSLayoutAttributeLeading, @@ -283,6 +304,14 @@ struct uiForm { @"uiForm trailing constraint"); [self addConstraint:c]; [self->trailings addObject:c]; + // TODO + c = mkConstraint(fc, NSLayoutAttributeBottom, + NSLayoutRelationLessThanOrEqual, + self, NSLayoutAttributeBottom, + 1, 0, + @"TODO"); + [self addConstraint:c]; + [self->trailings addObject:c]; } // we don't arrange the labels vertically; that's done when we add the control since those constraints don't need to change (they just need to be at their baseline) From 4b2858b53a32461b630fddbf3fd77884a2b4a5d9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 8 Jun 2016 18:19:41 -0400 Subject: [PATCH 0272/1329] Implemented uiForm on GTK+. --- unix/CMakeLists.txt | 1 + unix/form.c | 132 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 unix/form.c diff --git a/unix/CMakeLists.txt b/unix/CMakeLists.txt index d8626062..42903125 100644 --- a/unix/CMakeLists.txt +++ b/unix/CMakeLists.txt @@ -22,6 +22,7 @@ list(APPEND _LIBUI_SOURCES unix/editablecombo.c unix/entry.c unix/fontbutton.c + unix/form.c unix/graphemes.c unix/group.c unix/label.c diff --git a/unix/form.c b/unix/form.c new file mode 100644 index 00000000..03acd23a --- /dev/null +++ b/unix/form.c @@ -0,0 +1,132 @@ +// 8 june 2016 +#include "uipriv_unix.h" + +struct formChild { + uiControl *c; + GtkWidget *label; + gboolean oldhexpand; + GtkAlign oldhalign; + gboolean oldvexpand; + GtkAlign oldvalign; +}; + +struct uiForm { + uiUnixControl c; + GtkWidget *widget; + GtkContainer *container; + GtkGrid *grid; + GArray *children; + int padded; + // TODO OS X is missing this + GtkSizeGroup *stretchygroup; // ensures all stretchy controls have the same size +}; + +uiUnixControlAllDefaultsExceptDestroy(uiForm) + +#define ctrl(f, i) &g_array_index(f->children, struct formChild, i) + +static void uiFormDestroy(uiControl *c) +{ + uiForm *f = uiForm(c); + struct formChild *fc; + guint i; + + // kill the size group + g_object_unref(f->stretchygroup); + // free all controls + for (i = 0; i < f->children->len; i++) { + fc = ctrl(f, i); + uiControlSetParent(fc->c, NULL); + // and make sure the widget itself stays alive + uiUnixControlSetContainer(uiUnixControl(fc->c), f->container, TRUE); + uiControlDestroy(fc->c); + gtk_widget_destroy(fc->label); + } + g_array_free(f->children, TRUE); + // and then ourselves + g_object_unref(f->widget); + uiFreeControl(uiControl(f)); +} + +void uiFormAppend(uiForm *f, const char *label, uiControl *c, int stretchy) +{ + struct formChild fc; + GtkWidget *widget; + guint row; + + fc.c = c; + widget = GTK_WIDGET(uiControlHandle(fc.c)); + fc.oldhexpand = gtk_widget_get_hexpand(widget); + fc.oldhalign = gtk_widget_get_halign(widget); + fc.oldvexpand = gtk_widget_get_vexpand(widget); + fc.oldvalign = gtk_widget_get_valign(widget); + + if (stretchy) { + gtk_widget_set_vexpand(widget, TRUE); + gtk_widget_set_valign(widget, GTK_ALIGN_FILL); + gtk_size_group_add_widget(f->stretchygroup, widget); + } else + gtk_widget_set_vexpand(widget, FALSE); + // and make them fill horizontally + gtk_widget_set_hexpand(widget, TRUE); + gtk_widget_set_halign(widget, GTK_ALIGN_FILL); + + fc.label = gtk_label_new(label); + gtk_widget_set_hexpand(fc.label, FALSE); + gtk_widget_set_halign(fc.label, GTK_ALIGN_END); + gtk_widget_set_vexpand(fc.label, FALSE); + if (GTK_IS_SCROLLED_WINDOW(widget)) + gtk_widget_set_valign(fc.label, GTK_ALIGN_START); + else + gtk_widget_set_valign(fc.label, GTK_ALIGN_CENTER); + gtk_style_context_add_class(gtk_widget_get_style_context(fc.label), "dim-label"); + row = f->children->len; + gtk_grid_attach(f->grid, fc.label, + 0, row, + 1, 1); + gtk_widget_show_all(fc.label); + + uiControlSetParent(fc.c, uiControl(f)); + uiUnixControlSetContainer(uiUnixControl(fc.c), f->container, FALSE); + g_array_append_val(f->children, fc); + + // move the widget to the correct place + gtk_container_child_set(f->container, widget, + "left-attach", 1, + "top-attach", row, + NULL); +} + +int uiFormPadded(uiForm *f) +{ + return f->padded; +} + +void uiFormSetPadded(uiForm *f, int padded) +{ + f->padded = padded; + if (f->padded) { + gtk_grid_set_row_spacing(f->grid, gtkYPadding); + gtk_grid_set_column_spacing(f->grid, gtkXPadding); + } else { + gtk_grid_set_row_spacing(f->grid, 0); + gtk_grid_set_column_spacing(f->grid, 0); + } +} + +uiForm *uiNewForm(void) +{ + uiForm *f; + + uiUnixNewControl(uiForm, f); + + f->widget = gtk_grid_new(); + f->container = GTK_CONTAINER(f->widget); + f->grid = GTK_GRID(f->widget); + + f->stretchygroup = gtk_size_group_new(GTK_SIZE_GROUP_VERTICAL); + + f->children = g_array_new(FALSE, TRUE, sizeof (struct formChild)); + + return f; +} From afe27177d5a27ad9127719a8522c03db171bf9e0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 8 Jun 2016 23:33:32 -0400 Subject: [PATCH 0273/1329] Implemented uiForm on Windows. --- README.md | 3 + windows/CMakeLists.txt | 1 + windows/form.cpp | 287 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 291 insertions(+) create mode 100644 windows/form.cpp diff --git a/README.md b/README.md index a2c0b1ef..85549e7a 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,9 @@ This README is being written.
*Note that today's entry may be updated later today.* +* **8 June 2016** + * Added `uiForm`, a new container control that arranges controls vertically, with properly aligned labels on each. Have fun! + * **6 June 2016** * Added `uiRadioButtonsSelected()`, `uiRadioButtonsSetSelected()`, and `uiRadioButtonsOnSelected()` to control selection of a radio button and catch an event when such a thing happens. diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index a2175786..0caedb39 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -28,6 +28,7 @@ list(APPEND _LIBUI_SOURCES windows/events.cpp windows/fontbutton.cpp windows/fontdialog.cpp + windows/form.cpp windows/graphemes.cpp windows/group.cpp windows/init.cpp diff --git a/windows/form.cpp b/windows/form.cpp new file mode 100644 index 00000000..a22f0f89 --- /dev/null +++ b/windows/form.cpp @@ -0,0 +1,287 @@ +// 8 june 2016 +#include "uipriv_windows.hpp" + +struct formChild { + uiControl *c; + HWND label; + int stretchy; + intmax_t height; +}; + +struct uiForm { + uiWindowsControl c; + HWND hwnd; + std::vector *controls; + int padded; +}; + +static void formPadding(uiForm *f, int *xpadding, int *ypadding) +{ + uiWindowsSizing sizing; + + *xpadding = 0; + *ypadding = 0; + if (f->padded) { + uiWindowsGetSizing(f->hwnd, &sizing); + uiWindowsSizingStandardPadding(&sizing, xpadding, ypadding); + } +} + +// via http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing +#define labelHeight 8 +#define labelYOffset 3 + +static void formRelayout(uiForm *f) +{ + RECT r; + intmax_t x, y, width, height; + int xpadding, ypadding; + uintmax_t nStretchy; + intmax_t labelwid, stretchyht; + intmax_t thiswid; + uintmax_t i; + intmax_t minimumWidth, minimumHeight; + uiWindowsSizing sizing; + int labelht, labelyoff; + + if (f->controls->size() == 0) + return; + + uiWindowsEnsureGetClientRect(f->hwnd, &r); + x = r.left; + y = r.top; + width = r.right - r.left; + height = r.bottom - r.top; + + // -1) get this Form's padding + formPadding(f, &xpadding, &ypadding); + + // 0) inset the available rect by the needed padding + // TODO this is incorrect if any controls are hidden + width -= xpadding; + height -= (f->controls->size() - 1) * ypadding; + + // 1) get width of labels and height of non-stretchy controls + // this will tell us how much space will be left for controls + labelwid = 0; + stretchyht = height; + nStretchy = 0; + for (struct formChild &fc : *(f->controls)) { + if (!uiControlVisible(fc.c)) + continue; + thiswid = uiWindowsWindowTextWidth(fc.label); + if (labelwid < thiswid) + labelwid = thiswid; + if (fc.stretchy) { + nStretchy++; + continue; + } + uiWindowsControlMinimumSize(uiWindowsControl(fc.c), &minimumWidth, &minimumHeight); + fc.height = minimumHeight; + stretchyht -= minimumHeight; + } + + // 2) now get the width of controls and the height of stretchy controls + width -= labelwid; + if (nStretchy != 0) { + stretchyht /= nStretchy; + for (struct formChild &fc : *(f->controls)) { + if (!uiControlVisible(fc.c)) + continue; + if (fc.stretchy) + fc.height = stretchyht; + } + } + + // 3) get the y offset + labelyoff = labelYOffset; + uiWindowsGetSizing(f->hwnd, &sizing); + uiWindowsSizingDlgUnitsToPixels(&sizing, NULL, &labelyoff); + + // 4) now we can position controls + // first, make relative to the top-left corner of the container + // also prefer left alignment on Windows + x = labelwid + xpadding; + y = 0; + for (const struct formChild &fc : *(f->controls)) { + if (!uiControlVisible(fc.c)) + continue; + labelht = labelHeight; + uiWindowsGetSizing(f->hwnd, &sizing); + uiWindowsSizingDlgUnitsToPixels(&sizing, NULL, &labelht); + uiWindowsEnsureMoveWindowDuringResize(fc.label, 0, y + labelyoff - sizing.InternalLeading, labelwid, labelht); + uiWindowsEnsureMoveWindowDuringResize((HWND) uiControlHandle(fc.c), x, y, width, fc.height); + y += fc.height + ypadding; + } +} + +static void uiFormDestroy(uiControl *c) +{ + uiForm *f = uiForm(c); + + for (const struct formChild &fc : *(f->controls)) { + uiControlSetParent(fc.c, NULL); + uiControlDestroy(fc.c); + uiWindowsEnsureDestroyWindow(fc.label); + } + delete f->controls; + uiWindowsEnsureDestroyWindow(f->hwnd); + uiFreeControl(uiControl(f)); +} + +uiWindowsControlDefaultHandle(uiForm) +uiWindowsControlDefaultParent(uiForm) +uiWindowsControlDefaultSetParent(uiForm) +uiWindowsControlDefaultToplevel(uiForm) +uiWindowsControlDefaultVisible(uiForm) +uiWindowsControlDefaultShow(uiForm) +uiWindowsControlDefaultHide(uiForm) +uiWindowsControlDefaultEnabled(uiForm) +uiWindowsControlDefaultEnable(uiForm) +uiWindowsControlDefaultDisable(uiForm) + +static void uiFormSyncEnableState(uiWindowsControl *c, int enabled) +{ + uiForm *f = uiForm(c); + + if (uiWindowsShouldStopSyncEnableState(uiWindowsControl(f), enabled)) + return; + for (const struct formChild &fc : *(f->controls)) + uiWindowsControlSyncEnableState(uiWindowsControl(fc.c), enabled); +} + +uiWindowsControlDefaultSetParentHWND(uiForm) + +static void uiFormMinimumSize(uiWindowsControl *c, intmax_t *width, intmax_t *height) +{ + uiForm *f = uiForm(c); + int xpadding, ypadding; + uintmax_t nStretchy; + // these two contain the largest minimum width and height of all stretchy controls in the form + // all stretchy controls will use this value to determine the final minimum size + intmax_t maxLabelWidth, maxControlWidth; + intmax_t maxStretchyHeight; + intmax_t labelwid; + uintmax_t i; + intmax_t minimumWidth, minimumHeight; + uiWindowsSizing sizing; + + *width = 0; + *height = 0; + if (f->controls->size() == 0) + return; + + // 0) get this Form's padding + formPadding(f, &xpadding, &ypadding); + + // 1) initialize the desired rect with the needed padding + // TODO this is wrong if any controls are hidden + *width = xpadding; + *height = (f->controls->size() - 1) * ypadding; + + // 2) determine the longest width of all controls and labels; add in the height of non-stretchy controls and get (but not add in) the largest heights of stretchy controls + // we still add in like direction of stretchy controls + nStretchy = 0; + maxLabelWidth = 0; + maxControlWidth = 0; + maxStretchyHeight = 0; + for (const struct formChild &fc : *(f->controls)) { + if (!uiControlVisible(fc.c)) + continue; + labelwid = uiWindowsWindowTextWidth(fc.label); + if (maxLabelWidth < labelwid) + maxLabelWidth = labelwid; + uiWindowsControlMinimumSize(uiWindowsControl(fc.c), &minimumWidth, &minimumHeight); + if (fc.stretchy) { + nStretchy++; + if (maxStretchyHeight < minimumHeight) + maxStretchyHeight = minimumHeight; + } + if (maxControlWidth < minimumWidth) + maxControlWidth = minimumWidth; + if (!fc.stretchy) + *height += minimumHeight; + } + *width += maxLabelWidth + maxControlWidth; + + // 3) and now we can add in stretchy controls + *height += nStretchy * maxStretchyHeight; +} + +static void uiFormMinimumSizeChanged(uiWindowsControl *c) +{ + uiForm *f = uiForm(c); + + if (uiWindowsControlTooSmall(uiWindowsControl(f))) { + uiWindowsControlContinueMinimumSizeChanged(uiWindowsControl(f)); + return; + } + formRelayout(f); +} + +uiWindowsControlDefaultLayoutRect(uiForm) +uiWindowsControlDefaultAssignControlIDZOrder(uiForm) + +static void formArrangeChildren(uiForm *f) +{ + LONG_PTR controlID; + HWND insertAfter; + uintmax_t i; + + controlID = 100; + insertAfter = NULL; + for (const struct formChild &fc : *(f->controls)) { + // TODO assign label ID and z-order + uiWindowsControlAssignControlIDZOrder(uiWindowsControl(fc.c), &controlID, &insertAfter); + } +} + +void uiFormAppend(uiForm *f, const char *label, uiControl *c, int stretchy) +{ + struct formChild fc; + WCHAR *wlabel; + + fc.c = c; + wlabel = toUTF16(label); + fc.label = uiWindowsEnsureCreateControlHWND(0, + L"STATIC", wlabel, + SS_LEFT | SS_NOPREFIX, + hInstance, NULL, + TRUE); + uiWindowsEnsureSetParentHWND(fc.label, f->hwnd); + fc.stretchy = stretchy; + uiControlSetParent(fc.c, uiControl(f)); + uiWindowsControlSetParentHWND(uiWindowsControl(fc.c), f->hwnd); + f->controls->push_back(fc); + formArrangeChildren(f); + uiWindowsControlMinimumSizeChanged(uiWindowsControl(f)); +} + +int uiFormPadded(uiForm *f) +{ + return f->padded; +} + +void uiFormSetPadded(uiForm *f, int padded) +{ + f->padded = padded; + uiWindowsControlMinimumSizeChanged(uiWindowsControl(f)); +} + +static void onResize(uiWindowsControl *c) +{ + formRelayout(uiForm(c)); +} + +uiForm *uiNewForm(void) +{ + uiForm *f; + + uiWindowsNewControl(uiForm, f); + + f->hwnd = uiWindowsMakeContainer(uiWindowsControl(f), onResize); + + f->controls = new std::vector; + + return f; +} From c96068316ad8d7892c16c26e47c08ff73e09f6f7 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 8 Jun 2016 23:52:53 -0400 Subject: [PATCH 0274/1329] More TODOs. --- TODO.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/TODO.md b/TODO.md index 6e31ef33..5ba8193d 100644 --- a/TODO.md +++ b/TODO.md @@ -1,3 +1,7 @@ +- more robust layout handling + - uiFormTie() for ensuring multiple uiForms have the same label area widths + - uiSizeGroup for size groups (GtkSizeGroup on GTK+, auto layout constraints on OS X; consider adding after 10.8 is gone) + - windows: should the initial hwndInsertAfter be HWND_BOTTOM for what we want? - windows: document the rules for controls and containers From 3fad148200823e2e3be38181a162b5f9891a4202 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 9 Jun 2016 08:20:39 -0400 Subject: [PATCH 0275/1329] Extracted the old grid.go for migration. --- _wip/grid.go | 477 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 477 insertions(+) create mode 100644 _wip/grid.go diff --git a/_wip/grid.go b/_wip/grid.go new file mode 100644 index 00000000..d778c5f9 --- /dev/null +++ b/_wip/grid.go @@ -0,0 +1,477 @@ +// 31 august 2014 + +package ui + +import ( + "fmt" +) + +// Grid is a Control that arranges other Controls in a grid. +// Grid is a very powerful container: it can position and size each Control in several ways and can (and must) have Controls added to it at any time, in any direction. +// it can also have Controls spanning multiple rows and columns. +// +// Each Control in a Grid has associated "expansion" and "alignment" values in both the X and Y direction. +// Expansion determines whether all cells in the same row/column are given whatever space is left over after figuring out how big the rest of the Grid should be. +// Alignment determines the position of a Control relative to its cell after computing the above. +// The special alignment Fill can be used to grow a Control to fit its cell. +// Note that expansion and alignment are independent variables. +// For more information on expansion and alignment, read https://developer.gnome.org/gtk3/unstable/ch28s02.html. +type Grid interface { + Control + + // Add adds a Control to the Grid. + // If this is the first Control in the Grid, it is merely added; nextTo should be nil. + // Otherwise, it is placed relative to nextTo. + // If nextTo is nil, it is placed next to the previously added Control. + // The effect of adding the same Control multiple times is undefined, as is the effect of adding a Control next to one not present in the Grid. + // The effect of overlapping spanning Controls is also undefined. + // Add panics if either xspan or yspan are zero or negative. + Add(control Control, nextTo Control, side Side, xexpand bool, xalign Align, yexpand bool, yalign Align, xspan int, yspan int) + + // Padded and SetPadded get and set whether the controls of the Grid have padding between them. + // The size of the padding is platform-dependent. + Padded() bool + SetPadded(padded bool) +} + +// Align represents the alignment of a Control in its cell of a Grid. +type Align uint + +const ( + LeftTop Align = iota + Center + RightBottom + Fill +) + +// Side represents a side of a Control to add other Controls to a Grid to. +type Side uint + +const ( + West Side = iota + East + North + South + nSides +) + +type grid struct { + controls []gridCell + indexof map[Control]int + prev int + parent *controlParent + padded bool + + xmax int + ymax int +} + +type gridCell struct { + control Control + xexpand bool + xalign Align + yexpand bool + yalign Align + xspan int + yspan int + + x int + y int + + finalx int + finaly int + finalwidth int + finalheight int + prefwidth int + prefheight int +} + +// NewGrid creates a new Grid with no Controls. +func NewGrid() Grid { + return &grid{ + indexof: map[Control]int{}, + } +} + +// ensures that all (x, y) pairs are 0-based +// also computes g.xmax/g.ymax +func (g *grid) reorigin() { + xmin := 0 + ymin := 0 + for i := range g.controls { + if g.controls[i].x < xmin { + xmin = g.controls[i].x + } + if g.controls[i].y < ymin { + ymin = g.controls[i].y + } + } + xmin = -xmin + ymin = -ymin + g.xmax = 0 + g.ymax = 0 + for i := range g.controls { + g.controls[i].x += xmin + g.controls[i].y += ymin + if g.xmax < g.controls[i].x+g.controls[i].xspan { + g.xmax = g.controls[i].x + g.controls[i].xspan + } + if g.ymax < g.controls[i].y+g.controls[i].yspan { + g.ymax = g.controls[i].y + g.controls[i].yspan + } + } +} + +func (g *grid) Add(control Control, nextTo Control, side Side, xexpand bool, xalign Align, yexpand bool, yalign Align, xspan int, yspan int) { + if xspan <= 0 || yspan <= 0 { + panic(fmt.Errorf("invalid span %dx%d given to Grid.Add()", xspan, yspan)) + } + cell := gridCell{ + control: control, + xexpand: xexpand, + xalign: xalign, + yexpand: yexpand, + yalign: yalign, + xspan: xspan, + yspan: yspan, + } + if g.parent != nil { + control.setParent(g.parent) + } + // if this is the first control, just add it in directly + if len(g.controls) != 0 { + next := g.prev + if nextTo != nil { + next = g.indexof[nextTo] + } + switch side { + case West: + cell.x = g.controls[next].x - cell.xspan + cell.y = g.controls[next].y + case North: + cell.x = g.controls[next].x + cell.y = g.controls[next].y - cell.yspan + case East: + cell.x = g.controls[next].x + g.controls[next].xspan + cell.y = g.controls[next].y + case South: + cell.x = g.controls[next].x + cell.y = g.controls[next].y + g.controls[next].yspan + default: + panic(fmt.Errorf("invalid side %d in Grid.Add()", side)) + } + } + g.controls = append(g.controls, cell) + g.prev = len(g.controls) - 1 + g.indexof[control] = g.prev + g.reorigin() +} + +func (g *grid) Padded() bool { + return g.padded +} + +func (g *grid) SetPadded(padded bool) { + g.padded = padded +} + +func (g *grid) setParent(p *controlParent) { + g.parent = p + for _, c := range g.controls { + c.control.setParent(g.parent) + } +} + +func (g *grid) containerShow() { + for _, c := range g.controls { + c.control.containerShow() + } +} + +func (g *grid) containerHide() { + for _, c := range g.controls { + c.control.containerHide() + } +} + +// builds the topological cell grid; also makes colwidths and rowheights +func (g *grid) mkgrid() (gg [][]int, colwidths []int, rowheights []int) { + gg = make([][]int, g.ymax) + for y := 0; y < g.ymax; y++ { + gg[y] = make([]int, g.xmax) + for x := 0; x < g.xmax; x++ { + gg[y][x] = -1 + } + } + for i := range g.controls { + for y := g.controls[i].y; y < g.controls[i].y+g.controls[i].yspan; y++ { + for x := g.controls[i].x; x < g.controls[i].x+g.controls[i].xspan; x++ { + gg[y][x] = i + } + } + } + return gg, make([]int, g.xmax), make([]int, g.ymax) +} + +func (g *grid) resize(x int, y int, width int, height int, d *sizing) { + if len(g.controls) == 0 { + // nothing to do + return + } + + // -2) get this Grid's padding + xpadding := d.xpadding + ypadding := d.ypadding + if !g.padded { + xpadding = 0 + ypadding = 0 + } + + // -1) discount padding from width/height + width -= (g.xmax - 1) * xpadding + height -= (g.ymax - 1) * ypadding + + // 0) build necessary data structures + gg, colwidths, rowheights := g.mkgrid() + xexpand := make([]bool, g.xmax) + yexpand := make([]bool, g.ymax) + + // 1) compute colwidths and rowheights before handling expansion + // we only count non-spanning controls to avoid weirdness + for y := 0; y < len(gg); y++ { + for x := 0; x < len(gg[y]); x++ { + i := gg[y][x] + if i == -1 { + continue + } + w, h := g.controls[i].control.preferredSize(d) + if g.controls[i].xspan == 1 { + if colwidths[x] < w { + colwidths[x] = w + } + } + if g.controls[i].yspan == 1 { + if rowheights[y] < h { + rowheights[y] = h + } + } + // save these for step 6 + g.controls[i].prefwidth = w + g.controls[i].prefheight = h + } + } + + // 2) figure out which rows/columns expand but not span + // we need to know which expanding rows/columns don't span before we can handle the ones that do + for i := range g.controls { + if g.controls[i].xexpand && g.controls[i].xspan == 1 { + xexpand[g.controls[i].x] = true + } + if g.controls[i].yexpand && g.controls[i].yspan == 1 { + yexpand[g.controls[i].y] = true + } + } + + // 2) figure out which rows/columns expand that do span + // the way we handle this is simple: if none of the spanned rows/columns expand, make all rows/columns expand + for i := range g.controls { + if g.controls[i].xexpand && g.controls[i].xspan != 1 { + do := true + for x := g.controls[i].x; x < g.controls[i].x+g.controls[i].xspan; x++ { + if xexpand[x] { + do = false + break + } + } + if do { + for x := g.controls[i].x; x < g.controls[i].x+g.controls[i].xspan; x++ { + xexpand[x] = true + } + } + } + if g.controls[i].yexpand && g.controls[i].yspan != 1 { + do := true + for y := g.controls[i].y; y < g.controls[i].y+g.controls[i].yspan; y++ { + if yexpand[y] { + do = false + break + } + } + if do { + for y := g.controls[i].y; y < g.controls[i].y+g.controls[i].yspan; y++ { + yexpand[y] = true + } + } + } + } + + // 4) compute and assign expanded widths/heights + nxexpand := 0 + nyexpand := 0 + for x, expand := range xexpand { + if expand { + nxexpand++ + } else { + width -= colwidths[x] + } + } + for y, expand := range yexpand { + if expand { + nyexpand++ + } else { + height -= rowheights[y] + } + } + for x, expand := range xexpand { + if expand { + colwidths[x] = width / nxexpand + } + } + for y, expand := range yexpand { + if expand { + rowheights[y] = height / nyexpand + } + } + + // 5) reset the final coordinates for the next step + for i := range g.controls { + g.controls[i].finalx = 0 + g.controls[i].finaly = 0 + g.controls[i].finalwidth = 0 + g.controls[i].finalheight = 0 + } + + // 6) compute cell positions and sizes + for y := 0; y < g.ymax; y++ { + curx := 0 + prev := -1 + for x := 0; x < g.xmax; x++ { + i := gg[y][x] + if i != -1 && y == g.controls[i].y { // don't repeat this step if the control spans vertically + if i != prev { + g.controls[i].finalx = curx + } else { + g.controls[i].finalwidth += xpadding + } + g.controls[i].finalwidth += colwidths[x] + } + curx += colwidths[x] + xpadding + prev = i + } + } + for x := 0; x < g.xmax; x++ { + cury := 0 + prev := -1 + for y := 0; y < g.ymax; y++ { + i := gg[y][x] + if i != -1 && x == g.controls[i].x { // don't repeat this step if the control spans horizontally + if i != prev { + g.controls[i].finaly = cury + } else { + g.controls[i].finalheight += ypadding + } + g.controls[i].finalheight += rowheights[y] + } + cury += rowheights[y] + ypadding + prev = i + } + } + + // 7) everything as it stands now is set for xalign == Fill yalign == Fill; set the correct alignments + // this is why we saved prefwidth/prefheight above + for i := range g.controls { + if g.controls[i].xalign != Fill { + switch g.controls[i].xalign { + case RightBottom: + g.controls[i].finalx += g.controls[i].finalwidth - g.controls[i].prefwidth + case Center: + g.controls[i].finalx += (g.controls[i].finalwidth - g.controls[i].prefwidth) / 2 + } + g.controls[i].finalwidth = g.controls[i].prefwidth // for all three + } + if g.controls[i].yalign != Fill { + switch g.controls[i].yalign { + case RightBottom: + g.controls[i].finaly += g.controls[i].finalheight - g.controls[i].prefheight + case Center: + g.controls[i].finaly += (g.controls[i].finalheight - g.controls[i].prefheight) / 2 + } + g.controls[i].finalheight = g.controls[i].prefheight // for all three + } + } + + // 8) and FINALLY we draw + for _, ycol := range gg { + for _, i := range ycol { + if i != -1 { // treat empty cells like spaces + g.controls[i].control.resize( + g.controls[i].finalx+x, g.controls[i].finaly+y, + g.controls[i].finalwidth, g.controls[i].finalheight, d) + } + } + } + + return +} + +func (g *grid) preferredSize(d *sizing) (width, height int) { + if len(g.controls) == 0 { + // nothing to do + return 0, 0 + } + + // -1) get this Grid's padding + xpadding := d.xpadding + ypadding := d.ypadding + if !g.padded { + xpadding = 0 + ypadding = 0 + } + + // 0) build necessary data structures + gg, colwidths, rowheights := g.mkgrid() + + // 1) compute colwidths and rowheights before handling expansion + // TODO put this in its own function (but careful about the spanning calculation in allocate()) + for y := 0; y < len(gg); y++ { + for x := 0; x < len(gg[y]); x++ { + i := gg[y][x] + if i == -1 { + continue + } + w, h := g.controls[i].control.preferredSize(d) + // allot equal space in the presence of spanning to keep things sane + if colwidths[x] < w/g.controls[i].xspan { + colwidths[x] = w / g.controls[i].xspan + } + if rowheights[y] < h/g.controls[i].yspan { + rowheights[y] = h / g.controls[i].yspan + } + // save these for step 6 + g.controls[i].prefwidth = w + g.controls[i].prefheight = h + } + } + + // 2) compute total column width/row height + colwidth := 0 + rowheight := 0 + for _, w := range colwidths { + colwidth += w + } + for _, h := range rowheights { + rowheight += h + } + + // and that's it; just account for padding + return colwidth + (g.xmax-1) * xpadding, + rowheight + (g.ymax-1) * ypadding +} + +func (g *grid) nTabStops() int { + n := 0 + for _, c := range g.controls { + n += c.control.nTabStops() + } + return n +} From 06ae417cb3f596fc2121ac841c17394c4f8c17ed Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 9 Jun 2016 11:22:02 -0400 Subject: [PATCH 0276/1329] Split old announcements and updates out. --- ANNOUNCE.md | 22 ++++++++++++++++++++++ Changelog.md | 33 ++++++++++++++++++++++++++++++++ README.md | 53 ++-------------------------------------------------- 3 files changed, 57 insertions(+), 51 deletions(-) create mode 100644 ANNOUNCE.md create mode 100644 Changelog.md diff --git a/ANNOUNCE.md b/ANNOUNCE.md new file mode 100644 index 00000000..4db42eb4 --- /dev/null +++ b/ANNOUNCE.md @@ -0,0 +1,22 @@ +# Old Announcements + +* **29 May 2016** + * **Alpha 3 is here!** Get it [here](https://github.com/andlabs/libui/releases/tag/alpha3). + * The next packaged release will introduce: + * uiGrid, another way to lay out controls, a la GtkGrid + * uiOpenGLArea, a way to render OpenGL content in a libui uiArea + * uiTable, a data grid control that may or may not have tree facilities (if it does, it will be called uiTree instead) + * a complete, possibly rewritten, drawing and text rendering infrastructure + +* **24 May 2016** + * You can now help choose [a potential new build system for libui](https://github.com/andlabs/libui/issues/62). + * Tomorrow I will decide if OS X 10.7 will also be dropped alongside GTK+ 3.4-3.8 this Saturday. Stay tuned. + +* **22 May 2016** + * Two more open questions I'd like your feedback on are available [here](https://github.com/andlabs/libui/issues/48) and [here](https://github.com/andlabs/libui/issues/25). + * Sometime in the next 48 hours (before 23:59 EDT on 24 May 2016) I will split `uiCombobox` into two separate controls, `uiCombobox` and `uiEditableCombobox`, each with slightly different events and "selected item" mechanics. Prepare your existing code. + +* **21 May 2016** + * I will now post announcements and updates here. + * Now that Ubuntu 16.04 LTS is here, no earlier than next Saturday, 28 May 2016 at noon EDT, **I will bump the minimum GTK+ version from 3.4 to 3.10**. This will add a lot of new features that I can now add to libui, such as search-oriented uiEntries, lists of arbitrary control layouts, and more. If you are still running a Linux distribution that doesn't come with 3.10, you will either need to upgrade or use jhbuild to set up a newer version of GTK+ in a private environment. + * You can decide if I should also drop OS X 10.7 [here](https://github.com/andlabs/libui/issues/46). diff --git a/Changelog.md b/Changelog.md new file mode 100644 index 00000000..29fac274 --- /dev/null +++ b/Changelog.md @@ -0,0 +1,33 @@ +# Old Updates + +* **29 May 2016** + * Thanks to @pcwalton, we can now statically link libui! Simply do `make STATIC=1` instead of just `make`. + * On Windows you must link both `libui.lib` and `libui.res` AND provide a Common Controls 6 manifest for output static binaries to work properly. + +* **28 May 2016** + * As promised, **the minimum system requirements are now OS X 10.8 and GTK+ 3.10 for OS X and Unix, respectively**. + +* **26 May 2016** + * Two OS X-specific functions have been added: `uiDarwinMarginAmount()` and `uiDarwinPaddingAmount()`. These return the amount of margins and padding, respectively, to give to a control, and are intended for container implementations. These are suitable for the constant of a NSLayoutConstraint. They both take a pointer parameter that is reserved for future use and should be `NULL`. + +* **25 May 2016** + * uiDrawTextLayout attributes are now specified in units of *graphemes* on all platforms. This means characters as seen from a user's perspective, not Unicode codepoints or UTF-8 bytes. So a long string of combining marker codepoints after one codepoint would still count as one grapheme. + +* **24 May 2016** + * As promised, `uiCombobox` is now split into `uiCombobox` for non-editable comboboxes and `uiEditableCombobox` for editable comboboxes. Mind the function changes as well :) + * There is a new function `uiMainStep()`, which runs one iteration of the main loop. It takes a single boolean argument, indicating whether to wait for an event to occur or not. It returns true if an event was processed (or if no event is available if you don't want to wait) and false if the event loop was told to stop (for instance, `uiQuit()` was called). + +* **23 May 2016** + * Fixed surrogate pair drawing on OS X. + +* **22 May 2016** + * Removed `uiControlVerifyDestroy()`; that is now part of `uiFreeControl()` itself. + * Added `uiPi`, a constant for π. This is provided for C and C++ programmers, where there is no standard named constant for π; bindings authors shouldn't need to worry about this. + * Fixed uiMultilineEntry not properly having line breaks on Windows. + * Added `uiNewNonWrappingMultilineEntry()`, which creates a uiMultilineEntry that scrolls horizontally instead of wrapping lines. (This is not documented as being changeable after the fact on Windows, hence it's a creation-time choice.) + * uiAreas on Windows and some internal Direct2D areas now respond to `WM_PRINTCLIENT` properly, which should hopefully increase the quality of screenshots. + * uiDateTimePicker on GTK+ works properly on RTL layouts and no longer disappears off the bottom of the screen if not enough room is available. It will also no longer be marked for localization of the time format (what the separator should be and whether to use 24-hour time), as that information is not provided by the locale system. :( + * Added `uiUserBugCannotSetParentOnToplevel()`, which should be used by implementations of toplevel controls in their `SetParent()` implementations. This will also be the beginning of consolidating common user bug messages into a single place, though this will be one of the only few exported user bug functions. + * uiSpinbox and uiSlider now merely swap their min and max if min ≥ max. They will no longer panic and do nothing, respectively. + * Matrix scaling will no longer leave the matrix in an invalid state on OS X and GTK+. + * `uiMultilineEntrySetText()` and `uiMutlilineEntryAppend()` on GTK+ no longer fire `OnChanged()` events. diff --git a/README.md b/README.md index 85549e7a..497a302a 100644 --- a/README.md +++ b/README.md @@ -12,26 +12,7 @@ This README is being written.
* Static linking is now fully possible. * MinGW linking is back, but static only. -* **29 May 2016** - * **Alpha 3 is here!** Get it [here](https://github.com/andlabs/libui/releases/tag/alpha3). - * The next packaged release will introduce: - * uiGrid, another way to lay out controls, a la GtkGrid - * uiOpenGLArea, a way to render OpenGL content in a libui uiArea - * uiTable, a data grid control that may or may not have tree facilities (if it does, it will be called uiTree instead) - * a complete, possibly rewritten, drawing and text rendering infrastructure - -* **24 May 2016** - * You can now help choose [a potential new build system for libui](https://github.com/andlabs/libui/issues/62). - * Tomorrow I will decide if OS X 10.7 will also be dropped alongside GTK+ 3.4-3.8 this Saturday. Stay tuned. - -* **22 May 2016** - * Two more open questions I'd like your feedback on are available [here](https://github.com/andlabs/libui/issues/48) and [here](https://github.com/andlabs/libui/issues/25). - * Sometime in the next 48 hours (before 23:59 EDT on 24 May 2016) I will split `uiCombobox` into two separate controls, `uiCombobox` and `uiEditableCombobox`, each with slightly different events and "selected item" mechanics. Prepare your existing code. - -* **21 May 2016** - * I will now post announcements and updates here. - * Now that Ubuntu 16.04 LTS is here, no earlier than next Saturday, 28 May 2016 at noon EDT, **I will bump the minimum GTK+ version from 3.4 to 3.10**. This will add a lot of new features that I can now add to libui, such as search-oriented uiEntries, lists of arbitrary control layouts, and more. If you are still running a Linux distribution that doesn't come with 3.10, you will either need to upgrade or use jhbuild to set up a newer version of GTK+ in a private environment. - * You can decide if I should also drop OS X 10.7 [here](https://github.com/andlabs/libui/issues/46). +*Old announcements can be found in the ANNOUNCE.md file.* ## Updates @@ -47,37 +28,7 @@ This README is being written.
* Added `uiNewPasswordEntry()`, which creates a new `uiEntry` suitable for entering passwords. * Added `uiNewSearchEntry()`, which creates a new `uiEntry` suitable for searching. On some systems, the `OnChanged()` event will be slightly delayed and/or combined, to produce a more natural feel when searching. -* **29 May 2016** - * Thanks to @pcwalton, we can now statically link libui! Simply do `make STATIC=1` instead of just `make`. - * On Windows you must link both `libui.lib` and `libui.res` AND provide a Common Controls 6 manifest for output static binaries to work properly. - -* **28 May 2016** - * As promised, **the minimum system requirements are now OS X 10.8 and GTK+ 3.10 for OS X and Unix, respectively**. - -* **26 May 2016** - * Two OS X-specific functions have been added: `uiDarwinMarginAmount()` and `uiDarwinPaddingAmount()`. These return the amount of margins and padding, respectively, to give to a control, and are intended for container implementations. These are suitable for the constant of a NSLayoutConstraint. They both take a pointer parameter that is reserved for future use and should be `NULL`. - -* **25 May 2016** - * uiDrawTextLayout attributes are now specified in units of *graphemes* on all platforms. This means characters as seen from a user's perspective, not Unicode codepoints or UTF-8 bytes. So a long string of combining marker codepoints after one codepoint would still count as one grapheme. - -* **24 May 2016** - * As promised, `uiCombobox` is now split into `uiCombobox` for non-editable comboboxes and `uiEditableCombobox` for editable comboboxes. Mind the function changes as well :) - * There is a new function `uiMainStep()`, which runs one iteration of the main loop. It takes a single boolean argument, indicating whether to wait for an event to occur or not. It returns true if an event was processed (or if no event is available if you don't want to wait) and false if the event loop was told to stop (for instance, `uiQuit()` was called). - -* **23 May 2016** - * Fixed surrogate pair drawing on OS X. - -* **22 May 2016** - * Removed `uiControlVerifyDestroy()`; that is now part of `uiFreeControl()` itself. - * Added `uiPi`, a constant for π. This is provided for C and C++ programmers, where there is no standard named constant for π; bindings authors shouldn't need to worry about this. - * Fixed uiMultilineEntry not properly having line breaks on Windows. - * Added `uiNewNonWrappingMultilineEntry()`, which creates a uiMultilineEntry that scrolls horizontally instead of wrapping lines. (This is not documented as being changeable after the fact on Windows, hence it's a creation-time choice.) - * uiAreas on Windows and some internal Direct2D areas now respond to `WM_PRINTCLIENT` properly, which should hopefully increase the quality of screenshots. - * uiDateTimePicker on GTK+ works properly on RTL layouts and no longer disappears off the bottom of the screen if not enough room is available. It will also no longer be marked for localization of the time format (what the separator should be and whether to use 24-hour time), as that information is not provided by the locale system. :( - * Added `uiUserBugCannotSetParentOnToplevel()`, which should be used by implementations of toplevel controls in their `SetParent()` implementations. This will also be the beginning of consolidating common user bug messages into a single place, though this will be one of the only few exported user bug functions. - * uiSpinbox and uiSlider now merely swap their min and max if min ≥ max. They will no longer panic and do nothing, respectively. - * Matrix scaling will no longer leave the matrix in an invalid state on OS X and GTK+. - * `uiMultilineEntrySetText()` and `uiMutlilineEntryAppend()` on GTK+ no longer fire `OnChanged()` events. +*Old updates can be found in the Changelog.md file.* ## Runtime Requirements From 075eae15e5bef0f1e37c6e4d95da5e67cc269878 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 9 Jun 2016 17:15:59 -0400 Subject: [PATCH 0277/1329] Started uiGrid. --- test/CMakeLists.txt | 9 ++--- test/main.c | 4 +++ test/page14.c | 82 +++++++++++++++++++++++++++++++++++++++++++++ test/spaced.c | 14 ++++++++ test/test.h | 4 +++ ui.h | 22 ++++++++++++ 6 files changed, 131 insertions(+), 4 deletions(-) create mode 100644 test/page14.c diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8d6ba03a..c6511e80 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -9,10 +9,6 @@ _add_exec(tester main.c menus.c page1.c - page10.c - page11.c - page12.c - page13.c page2.c page3.c page4.c @@ -24,6 +20,11 @@ _add_exec(tester page7c.c page8.c page9.c + page10.c + page11.c + page12.c + page13.c + page14.c spaced.c ${_TEST_RESOURCES_RC} ) diff --git a/test/main.c b/test/main.c index dea51663..63f21b86 100644 --- a/test/main.c +++ b/test/main.c @@ -48,6 +48,7 @@ int main(int argc, char *argv[]) uiBox *page2, *page3, *page4, *page5; uiBox *page6, *page7, *page8, *page9, *page10; uiBox *page11, *page12, *page13; + uiTab *page14; uiTab *outerTab; uiTab *innerTab; int nomenus = 0; @@ -144,6 +145,9 @@ int main(int argc, char *argv[]) page13 = makePage13(); uiTabAppend(innerTab, "Page 13", uiControl(page13)); + page14 = makePage14(); + uiTabAppend(innerTab, "Page 14", uiControl(page14)); + if (startspaced) setSpaced(1); diff --git a/test/page14.c b/test/page14.c new file mode 100644 index 00000000..babb9467 --- /dev/null +++ b/test/page14.c @@ -0,0 +1,82 @@ +// 9 june 2016 +#include "test.h" + +enum { + red, + green, + blue, + yellow, +}; + +static const struct { + double r; + double g; + double b; +} colors[] = { + { 1, 0, 0 }, + { 0, 0.5, 0 }, + { 0, 0, 1 }, + { 1, 1, 0 }, +}; + +static uiControl *testControl(const char *label, int color) +{ + uiColorButton *b; + + b = uiNewColorButton(); + uiColorButtonSetColor(b, colors[color].r, colors[color].g, colors[color].b, 1.0); + return uiControl(b); +} + +static uiControl *simpleGrid(void) +{ + uiGrid *g; + uiControl *t4; + + g = newGrid(); + + uiGridAppend(g, testControl("1", red), + 0, 0, 1, 1, + 0, uiAlignFill, 0, uiAlignFill); + uiGridAppend(g, testControl("2", green), + 1, 0, 1, 1, + 0, uiAlignFill, 0, uiAlignFill); + uiGridAppend(g, testControl("3", blue), + 2, 0, 1, 1, + 0, uiAlignFill, 0, uiAlignFill); + t4 = testControl("4", green); + uiGridAppend(g, t4, + 0, 1, 1, 1, + 0, uiAreaFill, 1, uiAreaFill); + uiGridInsertAt(g, testControl("5", blue), + t4, uiAtTrailing, 2, 1, + 0, uiAreaFill, 0, uiAreaFill); + uiGridAppend(g, testControl("6", yellow), + -1, 0, 1, 2, + 1, uiAreaFill, 0, uiAreaFill); + + return uiControl(g); +} + +static const struct { + const char *name; + uiControl *(*f)(void); +} pages[] = { + { "Simple Grid", simpleGrid }, // from GTK+ test/testgrid.c + { NULL, NULL }, +} + +uiTab *makePage14(void) +{ + uiTab *page14; + int i; + + page14 = newTab(); + + for (i = 0; pages[i].name != NULL; i++) + uiTabAppend(page14, + pages[i].name, + (*(pages[i].f))()); + + return page14; +} diff --git a/test/spaced.c b/test/spaced.c index 7eb34fab..a54a0089 100644 --- a/test/spaced.c +++ b/test/spaced.c @@ -32,6 +32,7 @@ enum types { tab, group, form, + grid, }; void setSpaced(int spaced) @@ -60,6 +61,9 @@ void setSpaced(int spaced) case form: uiFormSetPadded(uiForm(p), spaced); break; + case grid: + uiGridSetPadded(uiGrid(p), spaced); + break; } } } @@ -93,6 +97,7 @@ void querySpaced(char out[12]) // more than enough m++; break; // TODO form + // TODO grid } } @@ -161,3 +166,12 @@ uiForm *newForm(void) append(f, form); return f; } + +uiGrid *newGrid(void) +{ + uiGrid *g; + + g = uiNewGrid(); + append(g, grid); + return g; +} diff --git a/test/test.h b/test/test.h index b59879b9..2afe3e39 100644 --- a/test/test.h +++ b/test/test.h @@ -23,6 +23,7 @@ extern uiBox *newVerticalBox(void); extern uiTab *newTab(void); extern uiGroup *newGroup(const char *); extern uiForm *newForm(void); +extern uiGrid *newGrid(void); // menus.c extern uiMenuItem *shouldQuitItem; @@ -81,3 +82,6 @@ extern uiBox *makePage12(void); // page13.c extern uiBox *makePage13(void); + +// page14.c +extern uiTab *makePage14(void); diff --git a/ui.h b/ui.h index 00c5481b..20d23eee 100644 --- a/ui.h +++ b/ui.h @@ -627,6 +627,28 @@ _UI_EXTERN int uiFormPadded(uiForm *f); _UI_EXTERN void uiFormSetPadded(uiForm *f, int padded); _UI_EXTERN uiForm *uiNewForm(void); +_UI_ENUM(uiAlign) { + uiAlignFill, + uiAlignStart, + uiAlignCenter, + uiAlignEnd, +}; + +_UI_ENUM(uiAt) { + uiAtLeading, + uiAtTop, + uiAtTrailing, + uiAtBottom, +}; + +typedef struct uiGrid uiGrid; +#define uiGrid(this) ((uiGrid *) (this)) +_UI_EXTERN void uiGridAppend(uiGrid *g, uiControl *c, intmax_t left, intmax_t top, intmax_t xspan, intmax_t yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign); +_UI_EXTERN void uiGridInsertAt(uiGrid *g, uiControl *c, uiControl *existing, uiAt at, intmax_t xspan, intmax_t yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign); +_UI_EXTERN int uiGridPadded(uiGrid *g); +_UI_EXTERN void uiGridSetPadded(uiGrid *g, int padded); +_UI_EXTERN uiGrid *uiNewGrid(void); + #ifdef __cplusplus } #endif From 4b149ddfefa29a2d4ae5f123ee7357779025a940 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 9 Jun 2016 18:57:58 -0400 Subject: [PATCH 0278/1329] Implemented uiGrid on GTK+. --- common/controlsigs.h | 1 + test/page14.c | 8 +-- ui_unix.h | 1 + unix/CMakeLists.txt | 1 + unix/form.c | 1 - unix/grid.c | 141 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 148 insertions(+), 5 deletions(-) create mode 100644 unix/grid.c diff --git a/common/controlsigs.h b/common/controlsigs.h index b788135d..03e675cc 100644 --- a/common/controlsigs.h +++ b/common/controlsigs.h @@ -11,6 +11,7 @@ #define uiEntrySignature 0x456E7472 #define uiFontButtonSignature 0x466F6E42 #define uiFormSignature 0x466F726D +#define uiGridSignature 0x47726964 #define uiGroupSignature 0x47727062 #define uiLabelSignature 0x4C61626C #define uiMultilineEntrySignature 0x4D6C6E45 diff --git a/test/page14.c b/test/page14.c index babb9467..af181ab6 100644 --- a/test/page14.c +++ b/test/page14.c @@ -47,13 +47,13 @@ static uiControl *simpleGrid(void) t4 = testControl("4", green); uiGridAppend(g, t4, 0, 1, 1, 1, - 0, uiAreaFill, 1, uiAreaFill); + 0, uiAlignFill, 1, uiAlignFill); uiGridInsertAt(g, testControl("5", blue), t4, uiAtTrailing, 2, 1, - 0, uiAreaFill, 0, uiAreaFill); + 0, uiAlignFill, 0, uiAlignFill); uiGridAppend(g, testControl("6", yellow), -1, 0, 1, 2, - 1, uiAreaFill, 0, uiAreaFill); + 1, uiAlignFill, 0, uiAlignFill); return uiControl(g); } @@ -64,7 +64,7 @@ static const struct { } pages[] = { { "Simple Grid", simpleGrid }, // from GTK+ test/testgrid.c { NULL, NULL }, -} +}; uiTab *makePage14(void) { diff --git a/ui_unix.h b/ui_unix.h index f8cdf3dd..5a91257b 100644 --- a/ui_unix.h +++ b/ui_unix.h @@ -80,6 +80,7 @@ _UI_EXTERN void uiUnixControlSetContainer(uiUnixControl *, GtkContainer *, gbool { \ gtk_widget_set_sensitive(type(c)->widget, FALSE); \ } +// TODO this whole addedBefore stuff is a MASSIVE HACK. #define uiUnixControlDefaultSetContainer(type) \ static void type ## SetContainer(uiUnixControl *c, GtkContainer *container, gboolean remove) \ { \ diff --git a/unix/CMakeLists.txt b/unix/CMakeLists.txt index 42903125..33eedfac 100644 --- a/unix/CMakeLists.txt +++ b/unix/CMakeLists.txt @@ -24,6 +24,7 @@ list(APPEND _LIBUI_SOURCES unix/fontbutton.c unix/form.c unix/graphemes.c + unix/grid.c unix/group.c unix/label.c unix/main.c diff --git a/unix/form.c b/unix/form.c index 03acd23a..bf0c9b52 100644 --- a/unix/form.c +++ b/unix/form.c @@ -37,7 +37,6 @@ static void uiFormDestroy(uiControl *c) for (i = 0; i < f->children->len; i++) { fc = ctrl(f, i); uiControlSetParent(fc->c, NULL); - // and make sure the widget itself stays alive uiUnixControlSetContainer(uiUnixControl(fc->c), f->container, TRUE); uiControlDestroy(fc->c); gtk_widget_destroy(fc->label); diff --git a/unix/grid.c b/unix/grid.c new file mode 100644 index 00000000..3ed60ec5 --- /dev/null +++ b/unix/grid.c @@ -0,0 +1,141 @@ +// 9 june 2016 +#include "uipriv_unix.h" + +struct gridChild { + uiControl *c; + GtkWidget *label; + gboolean oldhexpand; + GtkAlign oldhalign; + gboolean oldvexpand; + GtkAlign oldvalign; +}; + +struct uiGrid { + uiUnixControl c; + GtkWidget *widget; + GtkContainer *container; + GtkGrid *grid; + GArray *children; + int padded; +}; + +uiUnixControlAllDefaultsExceptDestroy(uiGrid) + +#define ctrl(g, i) &g_array_index(g->children, struct gridChild, i) + +static void uiGridDestroy(uiControl *c) +{ + uiGrid *g = uiGrid(c); + struct gridChild *gc; + guint i; + + // free all controls + for (i = 0; i < g->children->len; i++) { + gc = ctrl(g, i); + uiControlSetParent(gc->c, NULL); + uiUnixControlSetContainer(uiUnixControl(gc->c), g->container, TRUE); + uiControlDestroy(gc->c); + } + g_array_free(g->children, TRUE); + // and then ourselves + g_object_unref(g->widget); + uiFreeControl(uiControl(g)); +} + +#define TODO_MASSIVE_HACK(c) \ + if (!uiUnixControl(c)->addedBefore) { \ + g_object_ref_sink(GTK_WIDGET(uiControlHandle(uiControl(c)))); \ + gtk_widget_show(GTK_WIDGET(uiControlHandle(uiControl(c)))); \ + uiUnixControl(c)->addedBefore = TRUE; \ + } + +static const GtkAlign gtkAligns[] = { + [uiAlignFill] = GTK_ALIGN_FILL, + [uiAlignStart] = GTK_ALIGN_START, + [uiAlignCenter] = GTK_ALIGN_CENTER, + [uiAlignEnd] = GTK_ALIGN_END, +}; + +static const GtkPositionType gtkPositions[] = { + [uiAtLeading] = GTK_POS_LEFT, + [uiAtTop] = GTK_POS_TOP, + [uiAtTrailing] = GTK_POS_RIGHT, + [uiAtBottom] = GTK_POS_BOTTOM, +}; + +static GtkWidget *prepare(struct gridChild *gc, uiControl *c, int hexpand, uiAlign halign, int vexpand, uiAlign valign) +{ + GtkWidget *widget; + + gc->c = c; + widget = GTK_WIDGET(uiControlHandle(gc->c)); + gc->oldhexpand = gtk_widget_get_hexpand(widget); + gc->oldhalign = gtk_widget_get_halign(widget); + gc->oldvexpand = gtk_widget_get_vexpand(widget); + gc->oldvalign = gtk_widget_get_valign(widget); + gtk_widget_set_hexpand(widget, hexpand != 0); + gtk_widget_set_halign(widget, gtkAligns[halign]); + gtk_widget_set_vexpand(widget, vexpand != 0); + gtk_widget_set_valign(widget, gtkAligns[valign]); + return widget; +} + +void uiGridAppend(uiGrid *g, uiControl *c, intmax_t left, intmax_t top, intmax_t xspan, intmax_t yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign) +{ + struct gridChild gc; + GtkWidget *widget; + + widget = prepare(&gc, c, hexpand, halign, vexpand, valign); + uiControlSetParent(gc.c, uiControl(g)); + TODO_MASSIVE_HACK(uiUnixControl(gc.c)); + gtk_grid_attach(g->grid, widget, + left, top, + xspan, yspan); + g_array_append_val(g->children, gc); +} + +void uiGridInsertAt(uiGrid *g, uiControl *c, uiControl *existing, uiAt at, intmax_t xspan, intmax_t yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign) +{ + struct gridChild gc; + GtkWidget *widget; + + widget = prepare(&gc, c, hexpand, halign, vexpand, valign); + uiControlSetParent(gc.c, uiControl(g)); + TODO_MASSIVE_HACK(uiUnixControl(gc.c)); + gtk_grid_attach_next_to(g->grid, widget, + GTK_WIDGET(uiControlHandle(existing)), gtkPositions[at], + xspan, yspan); + g_array_append_val(g->children, gc); +} + +int uiGridPadded(uiGrid *g) +{ + return g->padded; +} + +void uiGridSetPadded(uiGrid *g, int padded) +{ + g->padded = padded; + if (g->padded) { + gtk_grid_set_row_spacing(g->grid, gtkYPadding); + gtk_grid_set_column_spacing(g->grid, gtkXPadding); + } else { + gtk_grid_set_row_spacing(g->grid, 0); + gtk_grid_set_column_spacing(g->grid, 0); + } +} + +uiGrid *uiNewGrid(void) +{ + uiGrid *g; + + uiUnixNewControl(uiGrid, g); + + g->widget = gtk_grid_new(); + g->container = GTK_CONTAINER(g->widget); + g->grid = GTK_GRID(g->widget); + + g->children = g_array_new(FALSE, TRUE, sizeof (struct gridChild)); + + return g; +} From 3a3b96a38e78d21318b07c761441694ebe5c39fa Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 9 Jun 2016 21:30:33 -0400 Subject: [PATCH 0279/1329] Implemented more of GTK+'s grid tests. --- test/page14.c | 131 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 129 insertions(+), 2 deletions(-) diff --git a/test/page14.c b/test/page14.c index af181ab6..1564f735 100644 --- a/test/page14.c +++ b/test/page14.c @@ -1,11 +1,19 @@ // 9 june 2016 #include "test.h" +// TODOs: +// - GTK+ - make all expanding controls the same size, to match the other OSs? will they match the other OSs? + enum { red, green, blue, yellow, + white, + magenta, + orange, + purple, + cyan, }; static const struct { @@ -17,6 +25,11 @@ static const struct { { 0, 0.5, 0 }, { 0, 0, 1 }, { 1, 1, 0 }, + { 1, 1, 1 }, + { 1, 0, 1 }, + { 1, 0.65, 0 }, + { 0.5, 0, 0.5 }, + { 0, 1, 1 }, }; static uiControl *testControl(const char *label, int color) @@ -34,7 +47,6 @@ static uiControl *simpleGrid(void) uiControl *t4; g = newGrid(); - uiGridAppend(g, testControl("1", red), 0, 0, 1, 1, 0, uiAlignFill, 0, uiAlignFill); @@ -54,7 +66,117 @@ static uiControl *simpleGrid(void) uiGridAppend(g, testControl("6", yellow), -1, 0, 1, 2, 1, uiAlignFill, 0, uiAlignFill); + return uiControl(g); +} +static uiControl *boxComparison(void) +{ + uiBox *vbox; + uiGrid *g; + uiBox *hbox; + + vbox = newVerticalBox(); + uiBoxAppend(vbox, uiControl(uiNewLabel("Above")), 0); + uiBoxAppend(vbox, uiControl(uiNewHorizontalSeparator()), 0); + + hbox = newHorizontalBox(); + uiBoxAppend(vbox, uiControl(hbox), 0); + uiBoxAppend(hbox, testControl("1", white), 0); + uiBoxAppend(hbox, uiControl(uiNewLabel("A label")), 1); + uiBoxAppend(hbox, testControl("2", green), 0); + uiBoxAppend(hbox, uiControl(uiNewLabel("Another label")), 1); + uiBoxAppend(hbox, testControl("3", red), 0); + + uiBoxAppend(vbox, uiControl(uiNewHorizontalSeparator()), 0); + + g = newGrid(); + uiBoxAppend(vbox, uiControl(g), 0); + uiGridAppend(g, testControl("1", white), + 0, 0, 1, 1, + 0, uiAlignFill, 0, uiAlignFill); + uiGridAppend(g, uiControl(uiNewLabel("A label")), + 1, 0, 1, 1, + 1, uiAlignFill, 0, uiAlignFill); + uiGridAppend(g, testControl("2", green), + 2, 0, 1, 1, + 0, uiAlignFill, 0, uiAlignFill); + uiGridAppend(g, uiControl(uiNewLabel("Another label")), + 3, 0, 1, 1, + 1, uiAlignFill, 0, uiAlignFill); + uiGridAppend(g, testControl("3", red), + 4, 0, 1, 1, + 0, uiAlignFill, 0, uiAlignFill); + + uiBoxAppend(vbox, uiControl(uiNewHorizontalSeparator()), 0); + uiBoxAppend(vbox, uiControl(uiNewLabel("Below")), 0); + return uiControl(vbox); +} + +static uiControl *emptyLine(void) +{ + uiGrid *g; + + g = newGrid(); + uiGridAppend(g, testControl("(0, 0)", red), + 0, 0, 1, 1, + 1, uiAlignFill, 1, uiAlignFill); + uiGridAppend(g, testControl("(0, 1)", blue), + 0, 1, 1, 1, + 0, uiAlignFill, 0, uiAlignFill); + uiGridAppend(g, testControl("(10, 0)", green), + 10, 0, 1, 1, + 0, uiAlignFill, 0, uiAlignFill); + uiGridAppend(g, testControl("(10, 1)", magenta), + 10, 1, 1, 1, + 0, uiAlignFill, 0, uiAlignFill); + return uiControl(g); +} + +static uiControl *emptyGrid(void) +{ + uiGrid *g; + uiControl *t; + + g = newGrid(); + t = testControl("(0, 0)", red); + uiGridAppend(g, t, + 0, 0, 1, 1, + 1, uiAlignFill, 1, uiAlignFill); + uiControlHide(t); + return uiControl(g); +} + +// TODO insert (need specialized insert/delete) + +static uiControl *spanningGrid(void) +{ + uiGrid *g; + + g = uiNewGrid(); + uiGridAppend(g, testControl("0", blue), + 0, 4, 4, 1, + 1, uiAlignFill, 0, uiAlignFill); + uiGridAppend(g, testControl("1", green), + 4, 0, 1, 4, + 0, uiAlignFill, 1, uiAlignFill); + uiGridAppend(g, testControl("2", red), + 3, 3, 1, 1, + 1, uiAlignFill, 1, uiAlignFill); + uiGridAppend(g, testControl("3", yellow), + 0, 3, 2, 1, + 0, uiAlignFill, 0, uiAlignFill); + uiGridAppend(g, testControl("4", orange), + 3, 0, 1, 2, + 0, uiAlignFill, 0, uiAlignFill); + uiGridAppend(g, testControl("5", purple), + 1, 1, 1, 1, + 0, uiAlignFill, 0, uiAlignFill); + uiGridAppend(g, testControl("6", white), + 0, 1, 1, 1, + 0, uiAlignFill, 0, uiAlignFill); + uiGridAppend(g, testControl("7", cyan), + 1, 0, 1, 1, + 0, uiAlignFill, 0, uiAlignFill); return uiControl(g); } @@ -62,7 +184,12 @@ static const struct { const char *name; uiControl *(*f)(void); } pages[] = { - { "Simple Grid", simpleGrid }, // from GTK+ test/testgrid.c + // based on GTK+ test/testgrid.c + { "Simple Grid", simpleGrid }, + { "Box Comparison", boxComparison }, + { "Empty Line", emptyLine }, + { "Empty Grid", emptyGrid }, + { "Spanning Grid", spanningGrid }, { NULL, NULL }, }; From 554221fd66375fa4c8993a78cd7f2c8b403e197a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Jun 2016 19:34:48 -0400 Subject: [PATCH 0280/1329] Added Windows uiGrid. Doesn't quite work yet. --- CMakeLists.txt | 1 + _wip/grid.go | 470 +-------------------------------- windows/CMakeLists.txt | 1 + windows/grid.cpp | 580 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 583 insertions(+), 469 deletions(-) create mode 100644 windows/grid.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 72bf3fed..4b647c6e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,7 @@ cmake_minimum_required(VERSION 2.8.11) # - Haiku for haiku # - debian DESTDIR? https://github.com/andlabs/libui/pull/10 # - libui-combined* needs to be deleted so that custom command can run every time +# - add notelemetry.obj to *ALL TARGETS* on VS2015 and up - https://www.infoq.com/news/2016/06/visual-cpp-telemetry # the docs say we need to set this up prior to project() set(CMAKE_OSX_DEPLOYMENT_TARGET "10.8") diff --git a/_wip/grid.go b/_wip/grid.go index d778c5f9..e6b253ed 100644 --- a/_wip/grid.go +++ b/_wip/grid.go @@ -1,472 +1,4 @@ -// 31 august 2014 - -package ui - -import ( - "fmt" -) - -// Grid is a Control that arranges other Controls in a grid. -// Grid is a very powerful container: it can position and size each Control in several ways and can (and must) have Controls added to it at any time, in any direction. -// it can also have Controls spanning multiple rows and columns. -// -// Each Control in a Grid has associated "expansion" and "alignment" values in both the X and Y direction. -// Expansion determines whether all cells in the same row/column are given whatever space is left over after figuring out how big the rest of the Grid should be. -// Alignment determines the position of a Control relative to its cell after computing the above. -// The special alignment Fill can be used to grow a Control to fit its cell. -// Note that expansion and alignment are independent variables. -// For more information on expansion and alignment, read https://developer.gnome.org/gtk3/unstable/ch28s02.html. -type Grid interface { - Control - - // Add adds a Control to the Grid. - // If this is the first Control in the Grid, it is merely added; nextTo should be nil. - // Otherwise, it is placed relative to nextTo. - // If nextTo is nil, it is placed next to the previously added Control. - // The effect of adding the same Control multiple times is undefined, as is the effect of adding a Control next to one not present in the Grid. - // The effect of overlapping spanning Controls is also undefined. - // Add panics if either xspan or yspan are zero or negative. - Add(control Control, nextTo Control, side Side, xexpand bool, xalign Align, yexpand bool, yalign Align, xspan int, yspan int) - - // Padded and SetPadded get and set whether the controls of the Grid have padding between them. - // The size of the padding is platform-dependent. - Padded() bool - SetPadded(padded bool) -} - -// Align represents the alignment of a Control in its cell of a Grid. -type Align uint - -const ( - LeftTop Align = iota - Center - RightBottom - Fill -) - -// Side represents a side of a Control to add other Controls to a Grid to. -type Side uint - -const ( - West Side = iota - East - North - South - nSides -) - -type grid struct { - controls []gridCell - indexof map[Control]int - prev int - parent *controlParent - padded bool - - xmax int - ymax int -} - -type gridCell struct { - control Control - xexpand bool - xalign Align - yexpand bool - yalign Align - xspan int - yspan int - - x int - y int - - finalx int - finaly int - finalwidth int - finalheight int - prefwidth int - prefheight int -} - -// NewGrid creates a new Grid with no Controls. -func NewGrid() Grid { - return &grid{ - indexof: map[Control]int{}, - } -} - -// ensures that all (x, y) pairs are 0-based -// also computes g.xmax/g.ymax -func (g *grid) reorigin() { - xmin := 0 - ymin := 0 - for i := range g.controls { - if g.controls[i].x < xmin { - xmin = g.controls[i].x - } - if g.controls[i].y < ymin { - ymin = g.controls[i].y - } - } - xmin = -xmin - ymin = -ymin - g.xmax = 0 - g.ymax = 0 - for i := range g.controls { - g.controls[i].x += xmin - g.controls[i].y += ymin - if g.xmax < g.controls[i].x+g.controls[i].xspan { - g.xmax = g.controls[i].x + g.controls[i].xspan - } - if g.ymax < g.controls[i].y+g.controls[i].yspan { - g.ymax = g.controls[i].y + g.controls[i].yspan - } - } -} - -func (g *grid) Add(control Control, nextTo Control, side Side, xexpand bool, xalign Align, yexpand bool, yalign Align, xspan int, yspan int) { - if xspan <= 0 || yspan <= 0 { - panic(fmt.Errorf("invalid span %dx%d given to Grid.Add()", xspan, yspan)) - } - cell := gridCell{ - control: control, - xexpand: xexpand, - xalign: xalign, - yexpand: yexpand, - yalign: yalign, - xspan: xspan, - yspan: yspan, - } - if g.parent != nil { - control.setParent(g.parent) - } - // if this is the first control, just add it in directly - if len(g.controls) != 0 { - next := g.prev - if nextTo != nil { - next = g.indexof[nextTo] - } - switch side { - case West: - cell.x = g.controls[next].x - cell.xspan - cell.y = g.controls[next].y - case North: - cell.x = g.controls[next].x - cell.y = g.controls[next].y - cell.yspan - case East: - cell.x = g.controls[next].x + g.controls[next].xspan - cell.y = g.controls[next].y - case South: - cell.x = g.controls[next].x - cell.y = g.controls[next].y + g.controls[next].yspan - default: - panic(fmt.Errorf("invalid side %d in Grid.Add()", side)) - } - } - g.controls = append(g.controls, cell) - g.prev = len(g.controls) - 1 - g.indexof[control] = g.prev - g.reorigin() -} - -func (g *grid) Padded() bool { - return g.padded -} - -func (g *grid) SetPadded(padded bool) { - g.padded = padded -} - -func (g *grid) setParent(p *controlParent) { - g.parent = p - for _, c := range g.controls { - c.control.setParent(g.parent) - } -} - -func (g *grid) containerShow() { - for _, c := range g.controls { - c.control.containerShow() - } -} - -func (g *grid) containerHide() { - for _, c := range g.controls { - c.control.containerHide() - } -} - -// builds the topological cell grid; also makes colwidths and rowheights -func (g *grid) mkgrid() (gg [][]int, colwidths []int, rowheights []int) { - gg = make([][]int, g.ymax) - for y := 0; y < g.ymax; y++ { - gg[y] = make([]int, g.xmax) - for x := 0; x < g.xmax; x++ { - gg[y][x] = -1 - } - } - for i := range g.controls { - for y := g.controls[i].y; y < g.controls[i].y+g.controls[i].yspan; y++ { - for x := g.controls[i].x; x < g.controls[i].x+g.controls[i].xspan; x++ { - gg[y][x] = i - } - } - } - return gg, make([]int, g.xmax), make([]int, g.ymax) -} - -func (g *grid) resize(x int, y int, width int, height int, d *sizing) { - if len(g.controls) == 0 { - // nothing to do - return - } - - // -2) get this Grid's padding - xpadding := d.xpadding - ypadding := d.ypadding - if !g.padded { - xpadding = 0 - ypadding = 0 - } - - // -1) discount padding from width/height - width -= (g.xmax - 1) * xpadding - height -= (g.ymax - 1) * ypadding - - // 0) build necessary data structures - gg, colwidths, rowheights := g.mkgrid() - xexpand := make([]bool, g.xmax) - yexpand := make([]bool, g.ymax) - - // 1) compute colwidths and rowheights before handling expansion - // we only count non-spanning controls to avoid weirdness - for y := 0; y < len(gg); y++ { - for x := 0; x < len(gg[y]); x++ { - i := gg[y][x] - if i == -1 { - continue - } - w, h := g.controls[i].control.preferredSize(d) - if g.controls[i].xspan == 1 { - if colwidths[x] < w { - colwidths[x] = w - } - } - if g.controls[i].yspan == 1 { - if rowheights[y] < h { - rowheights[y] = h - } - } - // save these for step 6 - g.controls[i].prefwidth = w - g.controls[i].prefheight = h - } - } - - // 2) figure out which rows/columns expand but not span - // we need to know which expanding rows/columns don't span before we can handle the ones that do - for i := range g.controls { - if g.controls[i].xexpand && g.controls[i].xspan == 1 { - xexpand[g.controls[i].x] = true - } - if g.controls[i].yexpand && g.controls[i].yspan == 1 { - yexpand[g.controls[i].y] = true - } - } - - // 2) figure out which rows/columns expand that do span - // the way we handle this is simple: if none of the spanned rows/columns expand, make all rows/columns expand - for i := range g.controls { - if g.controls[i].xexpand && g.controls[i].xspan != 1 { - do := true - for x := g.controls[i].x; x < g.controls[i].x+g.controls[i].xspan; x++ { - if xexpand[x] { - do = false - break - } - } - if do { - for x := g.controls[i].x; x < g.controls[i].x+g.controls[i].xspan; x++ { - xexpand[x] = true - } - } - } - if g.controls[i].yexpand && g.controls[i].yspan != 1 { - do := true - for y := g.controls[i].y; y < g.controls[i].y+g.controls[i].yspan; y++ { - if yexpand[y] { - do = false - break - } - } - if do { - for y := g.controls[i].y; y < g.controls[i].y+g.controls[i].yspan; y++ { - yexpand[y] = true - } - } - } - } - - // 4) compute and assign expanded widths/heights - nxexpand := 0 - nyexpand := 0 - for x, expand := range xexpand { - if expand { - nxexpand++ - } else { - width -= colwidths[x] - } - } - for y, expand := range yexpand { - if expand { - nyexpand++ - } else { - height -= rowheights[y] - } - } - for x, expand := range xexpand { - if expand { - colwidths[x] = width / nxexpand - } - } - for y, expand := range yexpand { - if expand { - rowheights[y] = height / nyexpand - } - } - - // 5) reset the final coordinates for the next step - for i := range g.controls { - g.controls[i].finalx = 0 - g.controls[i].finaly = 0 - g.controls[i].finalwidth = 0 - g.controls[i].finalheight = 0 - } - - // 6) compute cell positions and sizes - for y := 0; y < g.ymax; y++ { - curx := 0 - prev := -1 - for x := 0; x < g.xmax; x++ { - i := gg[y][x] - if i != -1 && y == g.controls[i].y { // don't repeat this step if the control spans vertically - if i != prev { - g.controls[i].finalx = curx - } else { - g.controls[i].finalwidth += xpadding - } - g.controls[i].finalwidth += colwidths[x] - } - curx += colwidths[x] + xpadding - prev = i - } - } - for x := 0; x < g.xmax; x++ { - cury := 0 - prev := -1 - for y := 0; y < g.ymax; y++ { - i := gg[y][x] - if i != -1 && x == g.controls[i].x { // don't repeat this step if the control spans horizontally - if i != prev { - g.controls[i].finaly = cury - } else { - g.controls[i].finalheight += ypadding - } - g.controls[i].finalheight += rowheights[y] - } - cury += rowheights[y] + ypadding - prev = i - } - } - - // 7) everything as it stands now is set for xalign == Fill yalign == Fill; set the correct alignments - // this is why we saved prefwidth/prefheight above - for i := range g.controls { - if g.controls[i].xalign != Fill { - switch g.controls[i].xalign { - case RightBottom: - g.controls[i].finalx += g.controls[i].finalwidth - g.controls[i].prefwidth - case Center: - g.controls[i].finalx += (g.controls[i].finalwidth - g.controls[i].prefwidth) / 2 - } - g.controls[i].finalwidth = g.controls[i].prefwidth // for all three - } - if g.controls[i].yalign != Fill { - switch g.controls[i].yalign { - case RightBottom: - g.controls[i].finaly += g.controls[i].finalheight - g.controls[i].prefheight - case Center: - g.controls[i].finaly += (g.controls[i].finalheight - g.controls[i].prefheight) / 2 - } - g.controls[i].finalheight = g.controls[i].prefheight // for all three - } - } - - // 8) and FINALLY we draw - for _, ycol := range gg { - for _, i := range ycol { - if i != -1 { // treat empty cells like spaces - g.controls[i].control.resize( - g.controls[i].finalx+x, g.controls[i].finaly+y, - g.controls[i].finalwidth, g.controls[i].finalheight, d) - } - } - } - - return -} - -func (g *grid) preferredSize(d *sizing) (width, height int) { - if len(g.controls) == 0 { - // nothing to do - return 0, 0 - } - - // -1) get this Grid's padding - xpadding := d.xpadding - ypadding := d.ypadding - if !g.padded { - xpadding = 0 - ypadding = 0 - } - - // 0) build necessary data structures - gg, colwidths, rowheights := g.mkgrid() - - // 1) compute colwidths and rowheights before handling expansion - // TODO put this in its own function (but careful about the spanning calculation in allocate()) - for y := 0; y < len(gg); y++ { - for x := 0; x < len(gg[y]); x++ { - i := gg[y][x] - if i == -1 { - continue - } - w, h := g.controls[i].control.preferredSize(d) - // allot equal space in the presence of spanning to keep things sane - if colwidths[x] < w/g.controls[i].xspan { - colwidths[x] = w / g.controls[i].xspan - } - if rowheights[y] < h/g.controls[i].yspan { - rowheights[y] = h / g.controls[i].yspan - } - // save these for step 6 - g.controls[i].prefwidth = w - g.controls[i].prefheight = h - } - } - - // 2) compute total column width/row height - colwidth := 0 - rowheight := 0 - for _, w := range colwidths { - colwidth += w - } - for _, h := range rowheights { - rowheight += h - } - - // and that's it; just account for padding - return colwidth + (g.xmax-1) * xpadding, - rowheight + (g.ymax-1) * ypadding -} +func (g *grid) preferredSize(d *sizing) (width, height int) {} func (g *grid) nTabStops() int { n := 0 diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 0caedb39..a2506705 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -30,6 +30,7 @@ list(APPEND _LIBUI_SOURCES windows/fontdialog.cpp windows/form.cpp windows/graphemes.cpp + windows/grid.cpp windows/group.cpp windows/init.cpp windows/label.cpp diff --git a/windows/grid.cpp b/windows/grid.cpp new file mode 100644 index 00000000..1117a2b7 --- /dev/null +++ b/windows/grid.cpp @@ -0,0 +1,580 @@ +// 10 june 2016 +#include "uipriv_windows.hpp" + +// TODO compare with GTK+: +// - what happens if you call InsertAt() twice? +// - what happens if you call Append() twice? + +// TODOs +// - make ALL the controls handle hidden children right + +struct gridChild { + uiControl *c; + intmax_t left; + intmax_t top; + intmax_t xspan; + intmax_t yspan; + int hexpand; + uiAlign halign; + int vexpand; + uiAlign valign; + + // have these here so they don't need to be reallocated each relayout + intmax_t finalx, finaly; + intmax_t finalwidth, finalheight; + intmax_t minwidth, minheight; +}; + +struct uiGrid { + uiWindowsControl c; + HWND hwnd; + std::vector *children; + std::map *indexof; + int padded; + + intmax_t xmin, ymin; + intmax_t xmax, ymax; +}; + +#define xcount(g) ((g)->xmax - (g)->xmin) +#define ycount(g) ((g)->ymax - (g)->ymin) +#define toxindex(g, x) ((x) - (g)->xmin) +#define toyindex(g, y) ((y) - (g)->ymin) + +class gridLayoutData { + size_t ycount; +public: + intmax_t **gg; // topological map gg[y][x] = control index + intmax_t *colwidths; + intmax_t *rowheights; + bool *hexpand; + bool *vexpand; + + gridLayoutData(uiGrid *g) + { + size_t i; + intmax_t x, y; + + this->gg = new intmax_t *[ycount(g)]; + for (y = 0; y < ycount(g); y++) { + this->gg[y] = new intmax_t[xcount(g)]; + for (x = 0; x < xcount(g); x++) + this->gg[y][x] = -1; + } + + for (i = 0; i < g->children->size(); i++) { + struct gridChild *gc; + + gc = (*(g->children))[i]; + for (y = gc->top; y < gc->top + gc->yspan; y++) + for (x = gc->left; x < gc->left + gc->xspan; x++) + this->gg[toyindex(g, y)][toxindex(g, x)] = i; + } + + this->colwidths = new intmax_t[xcount(g)]; + ZeroMemory(this->colwidths, xcount(g) * sizeof (intmax_t)); + this->rowheights = new intmax_t[ycount(g)]; + ZeroMemory(this->rowheights, ycount(g) * sizeof (intmax_t)); + this->hexpand = new bool[xcount(g)]; + ZeroMemory(this->hexpand, xcount(g) * sizeof (bool)); + this->vexpand = new bool[ycount(g)]; + ZeroMemory(this->vexpand, ycount(g) * sizeof (bool)); + + this->ycount = ycount(g); + } + + ~gridLayoutData() + { + size_t y; + + delete[] this->hexpand; + delete[] this->vexpand; + delete[] this->colwidths; + delete[] this->rowheights; + for (y = 0; y < this->ycount; y++) + delete[] this->gg[y]; + delete[] this->gg; + } +}; + +static void gridPadding(uiGrid *g, int *xpadding, int *ypadding) +{ + uiWindowsSizing sizing; + + *xpadding = 0; + *ypadding = 0; + if (g->padded) { + uiWindowsGetSizing(g->hwnd, &sizing); + uiWindowsSizingStandardPadding(&sizing, xpadding, ypadding); + } +} + +static void gridRelayout(uiGrid *g) +{ + RECT r; + intmax_t x, y, width, height; + gridLayoutData *ld; + int xpadding, ypadding; + intmax_t ix, iy; + intmax_t iwidth, iheight; + int i; + struct gridChild *gc; + intmax_t nhexpand, nvexpand; + + if (g->children->size() == 0) + return; // nothing to do + + uiWindowsEnsureGetClientRect(g->hwnd, &r); + x = r.left; + y = r.top; + width = r.right - r.left; + height = r.bottom - r.top; + + gridPadding(g, &xpadding, &ypadding); + ld = new gridLayoutData(g); + + // 0) discount padding from width/height + width -= (xcount(g) - 1) * xpadding; + height -= (ycount(g) - 1) * ypadding; + + // 1) compute colwidths and rowheights before handling expansion + // we only count non-spanning controls to avoid weirdness + for (iy = 0; iy < ycount(g); iy++) + for (ix = 0; ix < xcount(g); ix++) { + i = ld->gg[iy][ix]; + if (i == -1) + continue; + gc = (*(g->children))[i]; + uiWindowsControlMinimumSize(uiWindowsControl(gc->c), &iwidth, &iheight); + if (gc->xspan == 1) + if (ld->colwidths[ix] < iwidth) + ld->colwidths[ix] = iwidth; + if (gc->yspan == 1) + if (ld->rowheights[iy] < iheight) + ld->rowheights[iy] = iheight; + // save these for step 6 + gc->minwidth = iwidth; + gc->minheight = iheight; + } + + // 2) figure out which rows/columns expand but not span + // we need to know which expanding rows/columns don't span before we can handle the ones that do + for (i = 0; i < g->children->size(); i++) { + gc = (*(g->children))[i]; + if (gc->hexpand && gc->xspan == 1) + ld->hexpand[gc->left] = true; + if (gc->vexpand && gc->yspan == 1) + ld->vexpand[gc->top] = true; + } + + // 3) figure out which rows/columns expand that do span + // the way we handle this is simple: if none of the spanned rows/columns expand, make all rows/columns expand + for (i = 0; i < g->children->size(); i++) { + gc = (*(g->children))[i]; + if (gc->hexpand && gc->xspan != 1) { + bool doit = true; + + for (ix = gc->left; ix < gc->left + gc->xspan; ix++) + if (ld->hexpand[ix]) { + doit = false; + break; + } + if (doit) + for (ix = gc->left; ix < gc->left + gc->xspan; ix++) + ld->hexpand[ix] = true; + } + if (gc->vexpand && gc->yspan != 1) { + bool doit = true; + + for (iy = gc->top; iy < gc->top + gc->yspan; iy++) + if (ld->vexpand[iy]) { + doit = false; + break; + } + if (doit) + for (iy = gc->top; iy < gc->top + gc->yspan; iy++) + ld->vexpand[iy] = true; + } + } + + + // 4) compute and assign expanded widths/heights + nhexpand = 0; + nvexpand = 0; + for (i = 0; i < xcount(g); i++) + if (ld->hexpand[i]) + nhexpand++; + else + width -= ld->colwidths[i]; + for (i = 0; i < ycount(g); i++) + if (ld->vexpand[i]) + nvexpand++; + else + height -= ld->rowheights[i]; + for (i = 0; i < xcount(g); i++) + if (ld->hexpand[i]) + ld->colwidths[i] = width / nhexpand; + for (i = 0; i < ycount(g); i++) + if (ld->vexpand[i]) + ld->rowheights[i] = height / nvexpand; + + // 5) reset the final coordinates for the next step + for (i = 0; i < g->children->size(); i++) { + gc = (*(g->children))[i]; + gc->finalx = 0; + gc->finaly = 0; + gc->finalwidth = 0; + gc->finalheight = 0; + } + + // 6) compute cell positions and sizes + for (iy = 0; iy < ycount(g); y++) { + intmax_t curx; + int prev; + + curx = 0; + prev = -1; + for (ix = 0; ix < xcount(g); ix++) { + i = ld->gg[iy][ix]; + if (i != -1) { + gc = (*(g->children))[i]; + if (iy == gc->top) { // don't repeat this step if the control spans vertically + if (i != prev) + gc->finalx = curx; + else + gc->finalwidth += xpadding; + gc->finalwidth += ld->colwidths[ix]; + } + } + curx += ld->colwidths[ix] + xpadding; + prev = i; + } + } + for (ix = 0; ix < xcount(g); ix++) { + intmax_t cury; + int prev; + + cury = 0; + prev = -1; + for (iy = 0; iy < ycount(g); iy++) { + i = ld->gg[iy][ix]; + if (i != -1) { + gc = (*(g->children))[i]; + if (x == gc->top) { // don't repeat this step if the control spans horizontally + if (i != prev) + gc->finaly = cury; + else + gc->finalheight += ypadding; + gc->finalheight += ld->rowheights[iy]; + } + } + cury += ld->rowheights[iy] + ypadding; + prev = i; + } + } + + // 7) everything as it stands now is set for xalign == Fill yalign == Fill; set the correct alignments + // this is why we saved minwidth/minheight above + for (i = 0; i < g->children->size(); i++) { + gc = (*(g->children))[i]; + if (gc->halign != uiAlignFill) { + switch (gc->halign) { + case uiAlignEnd: + gc->finalx += gc->finalwidth - gc->minwidth; + break; + case uiAlignCenter: + gc->finalx += (gc->finalwidth - gc->minwidth) / 2; + break; + } + gc->finalwidth = gc->minwidth; // for all three + } + if (gc->valign != uiAlignFill) { + switch (gc->valign) { + case uiAlignEnd: + gc->finaly += gc->finalheight - gc->minheight; + break; + case uiAlignCenter: + gc->finaly += (gc->finalheight - gc->minheight) / 2; + break; + } + gc->finalheight = gc->minheight; // for all three + } + } + + // 8) and FINALLY we resize + for (iy = 0; iy < ycount(g); iy++) + for (ix = 0; ix < xcount(g); ix++) { + i = ld->gg[iy][ix]; + if (i != -1) { // treat empty cells like spaces + gc = (*(g->children))[i]; + uiWindowsEnsureMoveWindowDuringResize( + (HWND) uiControlHandle(gc->c), + gc->finalx,//TODO + x, + gc->finaly,//TODO + y, + gc->finalwidth, + gc->finalheight); + } + } + + delete ld; +} + +static void uiGridDestroy(uiControl *c) +{ + uiGrid *g = uiGrid(c); + + for (struct gridChild *gc : *(g->children)) { + uiControlSetParent(gc->c, NULL); + uiControlDestroy(gc->c); + uiFree(gc); + } + delete g->indexof; + delete g->children; + uiWindowsEnsureDestroyWindow(g->hwnd); + uiFreeControl(uiControl(g)); +} + +uiWindowsControlDefaultHandle(uiGrid) +uiWindowsControlDefaultParent(uiGrid) +uiWindowsControlDefaultSetParent(uiGrid) +uiWindowsControlDefaultToplevel(uiGrid) +uiWindowsControlDefaultVisible(uiGrid) +uiWindowsControlDefaultShow(uiGrid) +uiWindowsControlDefaultHide(uiGrid) +uiWindowsControlDefaultEnabled(uiGrid) +uiWindowsControlDefaultEnable(uiGrid) +uiWindowsControlDefaultDisable(uiGrid) + +static void uiGridSyncEnableState(uiWindowsControl *c, int enabled) +{ + uiGrid *g = uiGrid(c); + + if (uiWindowsShouldStopSyncEnableState(uiWindowsControl(g), enabled)) + return; + for (const struct gridChild *gc : *(g->children)) + uiWindowsControlSyncEnableState(uiWindowsControl(gc->c), enabled); +} + +uiWindowsControlDefaultSetParentHWND(uiGrid) + +static void uiGridMinimumSize(uiWindowsControl *c, intmax_t *width, intmax_t *height) +{ + uiGrid *g = uiGrid(c); + int xpadding, ypadding; + gridLayoutData *ld; + intmax_t x, y; + int i; + struct gridChild *gc; + intmax_t minwid, minht; + intmax_t colwidth, rowheight; + + *width = 0; + *height = 0; + if (g->children->size() == 0) + return; // nothing to do + + gridPadding(g, &xpadding, &ypadding); + ld = new gridLayoutData(g); + + // 1) compute colwidths and rowheights before handling expansion + // TODO put this in its own function (but careful about the spanning calculation in gridRelayout()) + for (y = 0; y < ycount(g); y++) + for (x = 0; x < xcount(g); x++) { + i = ld->gg[y][x]; + if (i == -1) + continue; + gc = (*(g->children))[i]; + uiWindowsControlMinimumSize(uiWindowsControl(gc->c), &minwid, &minht); + // allot equal space in the presence of spanning to keep things sane + if (ld->colwidths[x] < minwid / gc->xspan) + ld->colwidths[x] = minwid / gc->xspan; + if (ld->rowheights[y] < minht / gc->yspan) + ld->rowheights[y] = minht / gc->yspan; + // save these for step 6 + gc->minwidth = minwid; + gc->minheight = minht; + } + + // 2) compute total column width/row height + colwidth = 0; + rowheight = 0; + for (x = 0; x < xcount(g); x++) + colwidth += ld->colwidths[x]; + for (y = 0; y < ycount(g); y++) + rowheight += ld->rowheights[y]; + + // and that's it; just account for padding + *width = colwidth + (g->xmax-1) * xpadding; + *height = rowheight + (g->ymax-1) * ypadding; +} + +static void uiGridMinimumSizeChanged(uiWindowsControl *c) +{ + uiGrid *g = uiGrid(c); + + if (uiWindowsControlTooSmall(uiWindowsControl(g))) { + uiWindowsControlContinueMinimumSizeChanged(uiWindowsControl(g)); + return; + } + gridRelayout(g); +} + +uiWindowsControlDefaultLayoutRect(uiGrid) +uiWindowsControlDefaultAssignControlIDZOrder(uiGrid) + +// must have called gridRecomputeMinMax() first +static void gridArrangeChildren(uiGrid *g) +{ + LONG_PTR controlID; + HWND insertAfter; + gridLayoutData *ld; + bool *visited; + intmax_t x, y; + int i; + struct gridChild *gc; + + if (g->children->size() == 0) + return; // nothing to do + ld = new gridLayoutData(g); + controlID = 100; + insertAfter = NULL; + visited = new bool[g->children->size()]; + ZeroMemory(visited, g->children->size() * sizeof (bool)); + for (y = 0; y < ycount(g); y++) + for (x = 0; x < xcount(g); x++) { + i = ld->gg[y][x]; + if (i == -1) + continue; + if (visited[i]) + continue; + visited[i] = true; + gc = (*(g->children))[i]; + uiWindowsControlAssignControlIDZOrder(uiWindowsControl(gc->c), &controlID, &insertAfter); + } + delete[] visited; + delete ld; +} + +static void gridRecomputeMinMax(uiGrid *g) +{ + bool first = true; + + for (struct gridChild *gc : *(g->children)) { + if (first) { + g->xmin = gc->left; + g->ymin = gc->top; + g->xmax = gc->left + gc->xspan; + g->ymax = gc->top + gc->yspan; + first = false; + continue; + } + if (g->xmin > gc->left) + g->xmin = gc->left; + if (g->ymin > gc->top) + g->ymin = gc->top; + if (g->xmax < (gc->left + gc->xspan)) + g->xmax = gc->left + gc->xspan; + if (g->ymax < (gc->top + gc->yspan)) + g->ymax = gc->top + gc->yspan; + } +} + +static struct gridChild *toChild(uiControl *c, intmax_t xspan, intmax_t yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign) +{ + struct gridChild *gc; + + if (xspan < 0) + userbug("You cannot have a negative xspan in a uiGrid cell."); + if (yspan < 0) + userbug("You cannot have a negative yspan in a uiGrid cell."); + gc = uiNew(struct gridChild); + gc->c = c; + gc->xspan = xspan; + gc->yspan = yspan; + gc->hexpand = hexpand; + gc->halign = halign; + gc->vexpand = vexpand; + gc->valign = valign; + return gc; +} + +static void add(uiGrid *g, struct gridChild *gc) +{ + uiControlSetParent(gc->c, uiControl(g)); + uiWindowsControlSetParentHWND(uiWindowsControl(gc->c), g->hwnd); + g->children->push_back(gc); + (*(g->indexof))[gc->c] = g->children->size() - 1; + gridRecomputeMinMax(g); + gridArrangeChildren(g); + uiWindowsControlMinimumSizeChanged(uiWindowsControl(g)); +} + +void uiGridAppend(uiGrid *g, uiControl *c, intmax_t left, intmax_t top, intmax_t xspan, intmax_t yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign) +{ + struct gridChild *gc; + + gc = toChild(c, xspan, yspan, hexpand, halign, vexpand, valign); + gc->left = left; + gc->top = top; + add(g, gc); +} + +// TODO decide what happens if existing is NULL +void uiGridInsertAt(uiGrid *g, uiControl *c, uiControl *existing, uiAt at, intmax_t xspan, intmax_t yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign) +{ + struct gridChild *gc; + struct gridChild *other; + + gc = toChild(c, xspan, yspan, hexpand, halign, vexpand, valign); + other = (*(g->children))[(*(g->indexof))[existing]]; + switch (at) { + case uiAtLeading: + gc->left = other->left - gc->xspan; + gc->top = other->top; + break; + case uiAtTop: + gc->left = other->left; + gc->top = other->top - gc->yspan; + break; + case uiAtTrailing: + gc->left = other->left + other->xspan; + gc->top = other->top; + break; + case uiAtBottom: + gc->left = other->left; + gc->top = other->top + other->yspan; + break; + // TODO add error checks to ALL enums + } + add(g, gc); +} + +int uiGridPadded(uiGrid *g) +{ + return g->padded; +} + +void uiGridSetPadded(uiGrid *g, int padded) +{ + g->padded = padded; + uiWindowsControlMinimumSizeChanged(uiWindowsControl(g)); +} + +static void onResize(uiWindowsControl *c) +{ + gridRelayout(uiGrid(c)); +} + +uiGrid *uiNewGrid(void) +{ + uiGrid *g; + + uiWindowsNewControl(uiGrid, g); + + g->hwnd = uiWindowsMakeContainer(uiWindowsControl(g), onResize); + + g->children = new std::vector; + g->indexof = new std::map; + + return g; +} From 662c9de179889f68a56207a1f45a4d07ffa74184 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Jun 2016 21:24:30 -0400 Subject: [PATCH 0281/1329] Fixed the Windows grid code. Now it's time for the OS X code (giant bottle of alcohol here). --- windows/grid.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/windows/grid.cpp b/windows/grid.cpp index 1117a2b7..a262045b 100644 --- a/windows/grid.cpp +++ b/windows/grid.cpp @@ -162,9 +162,9 @@ static void gridRelayout(uiGrid *g) for (i = 0; i < g->children->size(); i++) { gc = (*(g->children))[i]; if (gc->hexpand && gc->xspan == 1) - ld->hexpand[gc->left] = true; + ld->hexpand[toxindex(g, gc->left)] = true; if (gc->vexpand && gc->yspan == 1) - ld->vexpand[gc->top] = true; + ld->vexpand[toyindex(g, gc->top)] = true; } // 3) figure out which rows/columns expand that do span @@ -175,25 +175,25 @@ static void gridRelayout(uiGrid *g) bool doit = true; for (ix = gc->left; ix < gc->left + gc->xspan; ix++) - if (ld->hexpand[ix]) { + if (ld->hexpand[toxindex(g, ix)]) { doit = false; break; } if (doit) for (ix = gc->left; ix < gc->left + gc->xspan; ix++) - ld->hexpand[ix] = true; + ld->hexpand[toxindex(g, ix)] = true; } if (gc->vexpand && gc->yspan != 1) { bool doit = true; for (iy = gc->top; iy < gc->top + gc->yspan; iy++) - if (ld->vexpand[iy]) { + if (ld->vexpand[toyindex(g, iy)]) { doit = false; break; } if (doit) for (iy = gc->top; iy < gc->top + gc->yspan; iy++) - ld->vexpand[iy] = true; + ld->vexpand[toyindex(g, iy)] = true; } } @@ -228,7 +228,7 @@ static void gridRelayout(uiGrid *g) } // 6) compute cell positions and sizes - for (iy = 0; iy < ycount(g); y++) { + for (iy = 0; iy < ycount(g); iy++) { intmax_t curx; int prev; @@ -238,7 +238,7 @@ static void gridRelayout(uiGrid *g) i = ld->gg[iy][ix]; if (i != -1) { gc = (*(g->children))[i]; - if (iy == gc->top) { // don't repeat this step if the control spans vertically + if (iy == toyindex(g, gc->top)) { // don't repeat this step if the control spans vertically if (i != prev) gc->finalx = curx; else @@ -260,7 +260,7 @@ static void gridRelayout(uiGrid *g) i = ld->gg[iy][ix]; if (i != -1) { gc = (*(g->children))[i]; - if (x == gc->top) { // don't repeat this step if the control spans horizontally + if (ix == toxindex(g, gc->left)) { // don't repeat this step if the control spans horizontally if (i != prev) gc->finaly = cury; else From 61a94fde57b8e650e18e04235754a0e0ec7f671b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Jun 2016 22:29:20 -0400 Subject: [PATCH 0282/1329] Fixed a memory leak in Windows uiForm. NOW OS X. --- windows/form.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/windows/form.cpp b/windows/form.cpp index a22f0f89..64b55a31 100644 --- a/windows/form.cpp +++ b/windows/form.cpp @@ -248,6 +248,7 @@ void uiFormAppend(uiForm *f, const char *label, uiControl *c, int stretchy) SS_LEFT | SS_NOPREFIX, hInstance, NULL, TRUE); + uiFree(wlabel); uiWindowsEnsureSetParentHWND(fc.label, f->hwnd); fc.stretchy = stretchy; uiControlSetParent(fc.c, uiControl(f)); From a18e3c71b97a1b6cd88d61abe7fb1c0474cdf99c Mon Sep 17 00:00:00 2001 From: Kevin Wojniak Date: Fri, 10 Jun 2016 21:23:13 -0700 Subject: [PATCH 0283/1329] Fix void* warning "Format specifies type 'void *' but the argument has type 'uiWindow *' (aka 'struct uiWindow *')" --- test/main.c | 2 +- test/menus.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/main.c b/test/main.c index dea51663..c05741eb 100644 --- a/test/main.c +++ b/test/main.c @@ -82,7 +82,7 @@ int main(int argc, char *argv[]) w = newWindow("Main Window", 320, 240, 1); uiWindowOnClosing(w, onClosing, NULL); - printf("main window %p\n", w); + printf("main window %p\n", (void*)w); uiOnShouldQuit(onShouldQuit, w); diff --git a/test/menus.c b/test/menus.c index 3e75e27b..d8fc44fa 100644 --- a/test/menus.c +++ b/test/menus.c @@ -47,7 +47,7 @@ static void forceOff(uiMenuItem *item, uiWindow *w, void *data) static void whatWindow(uiMenuItem *item, uiWindow *w, void *data) { - printf("menu item clicked on window %p\n", w); + printf("menu item clicked on window %p\n", (void*)w); } void initMenus(void) From d528fae1f40376940407181a3073c17885b668fb Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 11 Jun 2016 23:09:53 -0400 Subject: [PATCH 0284/1329] Added uiGrid on OS X. Now to fix build errors. --- _wip/grid.go | 9 - darwin/CMakeLists.txt | 1 + darwin/form.m | 1 + darwin/grid.m | 558 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 560 insertions(+), 9 deletions(-) delete mode 100644 _wip/grid.go create mode 100644 darwin/grid.m diff --git a/_wip/grid.go b/_wip/grid.go deleted file mode 100644 index e6b253ed..00000000 --- a/_wip/grid.go +++ /dev/null @@ -1,9 +0,0 @@ -func (g *grid) preferredSize(d *sizing) (width, height int) {} - -func (g *grid) nTabStops() int { - n := 0 - for _, c := range g.controls { - n += c.control.nTabStops() - } - return n -} diff --git a/darwin/CMakeLists.txt b/darwin/CMakeLists.txt index 25671ae0..e7fe4507 100644 --- a/darwin/CMakeLists.txt +++ b/darwin/CMakeLists.txt @@ -19,6 +19,7 @@ list(APPEND _LIBUI_SOURCES darwin/entry.m darwin/fontbutton.m darwin/form.m + darwin/grid.m darwin/group.m darwin/label.m darwin/main.m diff --git a/darwin/form.m b/darwin/form.m index 67e3843b..3a61336b 100644 --- a/darwin/form.m +++ b/darwin/form.m @@ -333,6 +333,7 @@ struct uiForm { [self addSubview:fc]; uiControlSetParent(fc.c, uiControl(self->f)); + // TODO fix this it's wrong uiDarwinControlSetSuperview(uiDarwinControl(fc.c), self); uiDarwinControlSyncEnableState(uiDarwinControl(fc.c), uiControlEnabledToUser(uiControl(self->f))); diff --git a/darwin/grid.m b/darwin/grid.m new file mode 100644 index 00000000..d71a0378 --- /dev/null +++ b/darwin/grid.m @@ -0,0 +1,558 @@ +// 11 june 2016 +#import "uipriv_darwin.h" + +// TODO wrap the child in a view if its align isn't fill +// maybe it's easier to do it regardless of align +@interface gridChild +@property uiControl *c; +@property intmax_t left; +@property intmax_t top; +@property intmax_t hspan; +@property intmax_t vspan; +@property int hexpand; +@property uiAlign halign; +@property int vexpand; +@property uiAlign valign; + +@property NSLayoutPriority oldHorzHuggingPri; +@property NSLayoutPriority oldVertHuggingPri; +- (NSView *)view; +@end + +@interface gridView : NSView { + uiGrid *g; + NSMutableArray *children; + int padded; + uintmax_t nhexpand; + uintmax_t nvexpand; + + NSMutableArray *edges; + NSMutableArray *inBetweens; +} +- (id)initWithG:(uiGrid *)gg; +- (void)onDestroy; +- (void)removeOurConstraints; +- (void)syncEnableStates:(int)enabled; +- (CGFloat)paddingAmount; +- (void)establishOurConstraints; +- (void)append:(gridChild *)gc; +- (void)insert:(gridChild *)gc after:(uiControl *)c at:(uiAt)at; +- (int)isPadded; +- (void)setPadded:(int)p; +- (BOOL)hugsTrailing; +- (BOOL)hugsBottom; +@end + +struct uiGrid { + uiDarwinControl c; + gridView *view; +}; + +@implementation formChild + +- (NSView *)view +{ + return (NSView *) uiControlHandle(self.c); +} + +@end + +@implementation gridView + +- (id)initWithG:(uiGrid *)gg +{ + self = [super initWithFrame:NSZeroRect]; + if (self != nil) { + self->g = gg; + self->padded = 0; + self->children = [NSMutableArray new]; + self->nStretchy = 0; + + self->edges = [NSMutableArray new]; + self->inBetweens = [NSMutableArray new]; + } + return self; +} + +- (void)onDestroy +{ + gridChild *gc; + + [self removeOurConstraints]; + [self->edges release]; + [self->inBetweens release]; + + for (gc in self->children) { + uiControlSetParent(gc.c, NULL); + uiDarwinControlSetSuperview(uiDarwinControl(gc.c), nil); + uiControlDestroy(gc.c); + } + [self->children release]; +} + +- (void)removeOurConstraints +{ + if ([self->edges count] != 0) { + [self removeConstraints:self->edges]; + [self->edges removeAllObjects]; + } + if ([self->inBetweens count] != 0) { + [self removeConstraints:self->inBetweens]; + [self->inBetweens removeAllObjects]; + } +} + +- (void)syncEnableStates:(int)enabled +{ + gridChild *gc; + + for (gc in self->children) + uiDarwinControlSyncEnableState(uiDarwinControl(gc.c), enabled); +} + +- (CGFloat)paddingAmount +{ + if (!self->padded) + return 0.0; + return uiDarwinPaddingAmount(NULL); +} + +- (void)establishOurConstraints +{ + gridChild *gc; + CGFloat padding; + intmax_t xmin, ymin; + intmax_t xmax, ymax; + intmax_t xcount, ycount; + BOOL first; + int **gg; + intmax_t x, y; + int i; + NSMutableSet *set; + NSNumber *number; + NSLayoutConstraint *c; + NSView **colviews, **rowviews; + + [self removeOurConstraints]; + if ([self->children count] == 0) + return; + padding = [self paddingAmount]; + + // first, figure out the minimum and maximum row and column numbers + first = YES; + for (gc in self->children) { + if (first) { + xmin = gc.left; + ymin = gc.top; + xmax = gc.left + gc.xspan; + ymax = gc.top + gc.yspan; + first = NO; + continue; + } + if (xmin > gc.left) + xmin = gc.left; + if (ymin > gc.top) + ymin = gc.top; + if (xmax < (gc.left + gc.xspan)) + xmax = gc.left + gc.xspan; + if (ymax < (gc.top + gc.yspan)) + ymax = gc.top + gc.yspan; + } + xcount = xmax - xmin; + ycount = ymax - ymin; + + // now build a topological map of the grid gg[y][x] + gg = (int **) uiAlloc(ycount * sizeof (int *), "int[][]"); + for (y = 0; y < ycount; y++) { + gg[y] = (int *) uiAlloc(xcount * sizeof (int), "int[]"); + for (x = 0; x < xcount; x++) + gg[y][x] = -1; // empty + } + for (i = 0; i < [self->children count]; i++) { + gc = (gridChild *) [self->children objectAtIndex:i]; + for (y = gc.top; y < gc.top + gc.yspan; y++) + for (x = gc.left; x < gc.left + gc.xspan; x++) + gg[y - ymin][x - xmin] = i; + } + + // now establish all the edge constraints + // leading edge + set = [NSMutableSet new]; + for (y = 0; y < ycount; y++) + [set addObject:[NSNumber numberWithInt:gg[y][0]]]; + for (number in set) + if ([number intValue] != -1) { + gc = (gridChild *) [self->children objectAtIndex:[number intValue]]; + c = mkConstraint(self, NSLayoutAttributeLeading, + NSLayoutRelationEqual, + [gc view], NSLayoutAttributeLeading, + 1, 0, + @"uiGrid leading edge constraint"); + [self addConstraint:c]; + [self->edges addObject:c]; + } + // top + [set removeAllObjects]; + for (x = 0; x < xcount; x++) + [set addObject:[NSNumber numberWithInt:gg[0][x]]]; + for (number in set) + if ([number intValue] != -1) { + gc = (gridChild *) [self->children objectAtIndex:[number intValue]]; + c = mkConstraint(self, NSLayoutAttributeTop, + NSLayoutRelationEqual, + [gc view], NSLayoutAttributeTop, + 1, 0, + @"uiGrid top edge constraint"); + [self addConstraint:c]; + [self->edges addObject:c]; + } + // trailing edge + [set removeAllObjects]; + for (y = 0; y < ycount; y++) + [set addObject:[NSNumber numberWithInt:gg[y][xcount - 1]]]; + for (number in set) + if ([number intValue] != -1) { + gc = (gridChild *) [self->children objectAtIndex:[number intValue]]; + c = mkConstraint(self, NSLayoutAttributeTrailing, + NSLayoutRelationEqual, + [gc view], NSLayoutAttributeTrailing, + 1, 0, + @"uiGrid trailing edge constraint"); + [self addConstraint:c]; + [self->edges addObject:c]; + } + // bottom + [set removeAllObjects]; + for (x = 0; x < xcount; x++) + [set addObject:[NSNumber numberWithInt:gg[ycount - 1][x]]]; + for (number in set) + if ([number intValue] != -1) { + gc = (gridChild *) [self->children objectAtIndex:[number intValue]]; + c = mkConstraint(self, NSLayoutAttributeBottom, + NSLayoutRelationEqual, + [gc view], NSLayoutAttributeBottom, + 1, 0, + @"uiGrid bottom edge constraint"); + [self addConstraint:c]; + [self->edges addObject:c]; + } + [set release]; + + // now go through every row and column and extract SOME view from that row and column for the inner constraints + // if it turns out that a row or column is totally empty, duplicate the one to the left (this has the effect of collapsing empty rows) + // note that the edges cannot be empty because we built a smallest fitting rectangle way back in step 1 + colviews = (NSView **) uiAlloc(xcount * sizeof (NSView *), "NSView *[]"); + for (x = 0; x < xcount; x++) { + for (y = 0; y < ycount; y++) + if (gg[y][x] != -1) { + gc = (gridChild *) [self->children objectAtIndex:gg[y][x]]; + colviews[x] = [gc view]; + break; + } + if (colviews[x] == nil) + colviews[x] = colviews[x - 1]; + } + rowviews = (NSView **) uiAlloc(ycount * sizeof (NSView *), "NSView *[]"); + for (y = 0; y < ycount; y++) { + for (x = 0; x < xcount; x++) + if (gg[y][x] != -1) { + gc = (gridChild *) [self->children objectAtIndex:gg[y][x]]; + rowviews[y] = [gc view]; + break; + } + if (rowviews[y] == nil) + rowviews[y] = colviews[y - 1]; + } + + // now string all the views together + for (gc in self->children) { + if (gc.left != xmin) { + c = mkConstraint([gc view], NSLayoutAttributeLeading, + NSLayoutRelationEqual, + colviews[(gc.left - 1) - xmin], NSLayoutAttributeTrailing, + 1, padding, + @"uiGrid leading constraint"); + [self addConstraint:c]; + [self->inBetweens addObject:c]; + } + if (gc.top != ymin) { + c = mkConstraint([gc view], NSLayoutAttributeTop, + NSLayoutRelationEqual, + rowviews[(gc.top - 1) - ymin], NSLayoutAttributeBottom, + 1, padding, + @"uiGrid top constraint"); + [self addConstraint:c]; + [self->inBetweens addObject:c]; + } + if ((gc.left + gc.xspan) != xmax) { + c = mkConstraint([gc view], NSLayoutAttributeTrailing, + NSLayoutRelationEqual, + colviews[(gc.left + gc.xspan) - xmin], NSLayoutAttributeLeading, + 1, -padding, + @"uiGrid trailing constraint"); + [self addConstraint:c]; + [self->inBetweens addObject:c]; + } + if ((gc.top + gc.yspan) != ymax) { + c = mkConstraint([gc view], NSLayoutAttributeBottom, + NSLayoutRelationEqual, + rowviews[(gc.top + gc.yspan) - ymin], NSLayoutAttributeTop, + 1, -padding, + @"uiGrid bottom constraint"); + [self addConstraint:c]; + [self->inBetweens addObject:c]; + } + } + + // TODO make all expanding rows/columns the same height/width + + // and finally clean up + uiFree(colviews); + uiFree(rowviews); + for (y = 0; y < ycount; y++) + uiFree(gg[y]); + uiFree(gg); +} + +- (void)append:(gridChild *)gc +{ + NSLayoutPriority priority; + BOOL update; + intmax_t oldn; + + uiControlSetParent(gc.c, uiControl(self->g)); + uiDarwinControlSetSuperview(uiDarwinControl(gc.c), self); + uiDarwinControlSyncEnableState(uiDarwinControl(gc.c), uiControlEnabledToUser(uiControl(self->g))); + + // if a control expands horizontally, it should not hug horizontally + // otherwise, it should *forcibly* hug + if (gc.hexpand) + priority = NSLayoutPriorityDefaultLow; + else + // LONGTERM will default high work? + priority = NSLayoutPriorityRequired; + uiDarwinControlSetHuggingPriority(uiDarwinControl(gc.c), priority, NSLayoutConstraintOrientationHorizontal); + // same for vertical direction + if (gc.vexpand) + priority = NSLayoutPriorityDefaultLow; + else + // LONGTERM will default high work? + priority = NSLayoutPriorityRequired; + uiDarwinControlSetHuggingPriority(uiDarwinControl(gc.c), priority, NSLayoutConstraintOrientationVertical); + + [self->children addObject:gc]; + + [self establishOurConstraints]; + update = NO; + if (fc.hexpand) { + oldn = self->nhexpand; + self->nhexpand++; + if (oldn == 0) + update = YES; + } + if (fc.vexpand) { + oldn = self->nvexpand; + self->nvexpand++; + if (oldn == 0) + update = YES; + } + if (update) + uiDarwinNotifyEdgeHuggingChanged(uiDarwinControl(self->g)); + + [gc release]; // we don't need the initial reference now +} + +- (void)insert:(gridChild *)gc after:(uiControl *)c at:(uiAt)at +{ + gridChild *other; + BOOL found; + + found = NO; + for (other in self->children) + if (other.c == c) { + found = YES; + break; + } + if (!found) + userbug("Existing control %p is not in grid %p; you cannot add other controls next to it", c, self->g); + + switch (at) { + case uiAtLeading: + gc.left = other.left - gc.xspan; + gc.top = other.top; + break; + case uiAtTop: + gc.left = other.left; + gc.top = other.top - gc.yspan; + break; + case uiAtTrailing: + gc.left = other.left + other.xspan; + gc.top = other.top; + break; + case uiAtBottom: + gc.left = other.left; + gc.top = other.top + other.yspan; + break; + // TODO add error checks to ALL enums + } + + [self append:gc]; +} + +- (int)isPadded +{ + return self->padded; +} + +- (void)setPadded:(int)p +{ + CGFloat padding; + NSLayoutConstraint *c; + + self->padded = p; + padding = [self paddingAmount]; + for (c in self->inBetweens) + switch ([c firstAttribute]) { + case NSLayoutAttributeLeading: + case NSLayoutAttributeTop: + [c setConstant:padding]; + break; + case NSLayoutAttributeTrailing: + case NSLayoutAttributeBottom: + [c setConstant:-padding]; + break; + } +} + +- (BOOL)hugsTrailing +{ + // only hug if we have horizontally expanding + return self->nhexpand != 0; +} + +- (BOOL)hugsBottom +{ + // only hug if we have vertically expanding + return self->nvexpand != 0; +} + +@end + +static void uiGridDestroy(uiControl *c) +{ + uiGrid *g = uiGrid(c); + + [g->view onDestroy]; + [g->view release]; + uiFreeControl(uiControl(g)); +} + +uiDarwinControlDefaultHandle(uiGrid, view) +uiDarwinControlDefaultParent(uiGrid, view) +uiDarwinControlDefaultSetParent(uiGrid, view) +uiDarwinControlDefaultToplevel(uiGrid, view) +uiDarwinControlDefaultVisible(uiGrid, view) +uiDarwinControlDefaultShow(uiGrid, view) +uiDarwinControlDefaultHide(uiGrid, view) +uiDarwinControlDefaultEnabled(uiGrid, view) +uiDarwinControlDefaultEnable(uiGrid, view) +uiDarwinControlDefaultDisable(uiGrid, view) + +static void uiGridSyncEnableState(uiDarwinControl *c, int enabled) +{ + uiGrid *g = uiGrid(c); + + if (uiDarwinShouldStopSyncEnableState(uiDarwinControl(g), enabled)) + return; + [g->view syncEnableStates:enabled]; +} + +uiDarwinControlDefaultSetSuperview(uiGrid, view) + +static BOOL uiGridHugsTrailingEdge(uiDarwinControl *c) +{ + uiGrid *g = uiGrid(c); + + return [g->view hugsTrailing]; +} + +static BOOL uiGridHugsBottom(uiDarwinControl *c) +{ + uiGrid *g = uiGrid(c); + + return [g->view hugsBottom]; +} + +static void uiGridChildEdgeHuggingChanged(uiDarwinControl *c) +{ + uiGrid *g = uiGrid(c); + + [g->view establishOurConstraints]; +} + +uiDarwinControlDefaultHuggingPriority(uiGrid, view) +uiDarwinControlDefaultSetHuggingPriority(uiGrid, view) + +static gridChild *toChild(uiControl *c, intmax_t xspan, intmax_t yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign) +{ + gridChild *gc; + + if (xspan < 0) + userbug("You cannot have a negative xspan in a uiGrid cell."); + if (yspan < 0) + userbug("You cannot have a negative yspan in a uiGrid cell."); + gc = [gridChild new]; + gc.c = c; + gc.xspan = xspan; + gc.yspan = yspan; + gc.hexpand = hexpand; + gc.halign = halign; + gc.vexpand = vexpand; + gc.valign = valign; + gc.oldHorzHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(gc.c), NSLayoutConstraintOrientationHorizontal); + gc.oldVertHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(gc.c), NSLayoutConstraintOrientationVertical); + return gc; +} + +void uiGridAppend(uiGrid *g, uiControl *c, intmax_t left, intmax_t top, intmax_t xspan, intmax_t yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign) +{ + gridChild *gc; + + // LONGTERM on other platforms + // or at leat allow this and implicitly turn it into a spacer + if (c == NULL) + userbug("You cannot add NULL to a uiGrid."); + gc = toChild(c, xspan, yspan, hexpand, halign, vexpand, valign); + gc.left = left; + gc.top = top; + [g->view append:gc]; +} + +void uiGridInsertAt(uiGrid *g, uiControl *c, uiControl *existing, uiAt at, intmax_t xspan, intmax_t yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign) +{ + gridChild *gc; + + gc = toChild(c, xspan, yspan, hexpand, halign, vexpand, valign); + [g->view insert:gc after:existing at:at]; +} + +int uiGridPadded(uiGrid *g) +{ + return [g->view isPadded]; +} + +void uiGridSetPadded(uiGrid *g, int padded) +{ + [g->view setPadded:padded]; +} + +uiGrid *uiNewForm(void) +{ + uiGrid *g; + + uiDarwinNewControl(uiGrid, g); + + g->view = [[gridView alloc] initWithG:g]; + + return g; +} From faa989aefdd08dddb42b4b9e91232386e67a9efa Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 11 Jun 2016 23:28:51 -0400 Subject: [PATCH 0285/1329] Fixed compile errors. Runtime errors, on the other hand... --- darwin/grid.m | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/darwin/grid.m b/darwin/grid.m index d71a0378..a9249551 100644 --- a/darwin/grid.m +++ b/darwin/grid.m @@ -3,12 +3,12 @@ // TODO wrap the child in a view if its align isn't fill // maybe it's easier to do it regardless of align -@interface gridChild +@interface gridChild : NSObject @property uiControl *c; @property intmax_t left; @property intmax_t top; -@property intmax_t hspan; -@property intmax_t vspan; +@property intmax_t xspan; +@property intmax_t yspan; @property int hexpand; @property uiAlign halign; @property int vexpand; @@ -48,7 +48,7 @@ struct uiGrid { gridView *view; }; -@implementation formChild +@implementation gridChild - (NSView *)view { @@ -66,7 +66,8 @@ struct uiGrid { self->g = gg; self->padded = 0; self->children = [NSMutableArray new]; - self->nStretchy = 0; + self->nhexpand = 0; + self->nvexpand = 0; self->edges = [NSMutableArray new]; self->inBetweens = [NSMutableArray new]; @@ -344,13 +345,13 @@ struct uiGrid { [self establishOurConstraints]; update = NO; - if (fc.hexpand) { + if (gc.hexpand) { oldn = self->nhexpand; self->nhexpand++; if (oldn == 0) update = YES; } - if (fc.vexpand) { + if (gc.vexpand) { oldn = self->nvexpand; self->nvexpand++; if (oldn == 0) @@ -546,7 +547,7 @@ void uiGridSetPadded(uiGrid *g, int padded) [g->view setPadded:padded]; } -uiGrid *uiNewForm(void) +uiGrid *uiNewGrid(void) { uiGrid *g; From 965077055439f8965726bd9d18618a35896f5338 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 11 Jun 2016 23:42:58 -0400 Subject: [PATCH 0286/1329] Fixed a typo. Doesn't fix runtime bugs yet. --- darwin/grid.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/darwin/grid.m b/darwin/grid.m index a9249551..4528319d 100644 --- a/darwin/grid.m +++ b/darwin/grid.m @@ -262,7 +262,7 @@ struct uiGrid { break; } if (rowviews[y] == nil) - rowviews[y] = colviews[y - 1]; + rowviews[y] = rowviews[y - 1]; } // now string all the views together From fc2218b51c4da519147c268f68f8aaf3efce2005 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 11 Jun 2016 23:52:35 -0400 Subject: [PATCH 0287/1329] Added some rather fickle debugging. --- darwin/grid.m | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/darwin/grid.m b/darwin/grid.m index 4528319d..d019d9df 100644 --- a/darwin/grid.m +++ b/darwin/grid.m @@ -410,6 +410,11 @@ struct uiGrid { CGFloat padding; NSLayoutConstraint *c; +dispatch_after( +dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC), +dispatch_get_main_queue(), +^{ [[self window] visualizeConstraints:[self constraints]]; } +); self->padded = p; padding = [self paddingAmount]; for (c in self->inBetweens) From bf1595795cc3d6ee4109f20c9795203f3403692a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 11 Jun 2016 23:53:49 -0400 Subject: [PATCH 0288/1329] Fixed a tester bug. --- test/page14.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/page14.c b/test/page14.c index 1564f735..0e9f1f41 100644 --- a/test/page14.c +++ b/test/page14.c @@ -152,7 +152,7 @@ static uiControl *spanningGrid(void) { uiGrid *g; - g = uiNewGrid(); + g = newGrid(); uiGridAppend(g, testControl("0", blue), 0, 4, 4, 1, 1, uiAlignFill, 0, uiAlignFill); From 2c08f52a5bc6d36d0a0b04eec3956ce544d29ede Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 12 Jun 2016 11:55:02 -0400 Subject: [PATCH 0289/1329] Moved Compatibility.md from ui to libui. --- Compatibility.md | 140 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 Compatibility.md diff --git a/Compatibility.md b/Compatibility.md new file mode 100644 index 00000000..eb136b7c --- /dev/null +++ b/Compatibility.md @@ -0,0 +1,140 @@ +# Useful things in newer versions + +## Windows +### Windows 7 +http://channel9.msdn.com/blogs/pdc2008/pc43 + +TODO look up PDC 2008 talk "new shell user interface" + +- new animation and text engine +- ribbon control (didn't this have some additional license?) +- LVITEM.piColFmt + +### Windows 8 + +### Windows 8.1 + +### Windows 10 + +## GTK+ +TODO what ships with Ubuntu Quantal (12.10)? + +### GTK+ 3.6 +ships with: Ubuntu Raring (13.04) + +- GtkEntry and GtkTextView have input purposes and input hints for external input methods but do not change input themselves + - according to Company, we connect to insert-text for that +- GtkLevelBar +- GtkMenuButton +- **GtkSearchEntry** + +### GTK+ 3.8 +ships with: Ubuntu Saucy (13.10) + +Not many interesting new things to us here, unless you count widget-internal tickers and single-click instead of double-click to select list items (a la KDE)... and oh yeah, also widget opacity. + +### GTK+ 3.10 +ships with: **Ubuntu Trusty (14.04 LTS)** +
GLib version: 2.40 + +- tab character stops in GtkEntry +- GtkHeaderBar + - intended for titlebar overrides; GtkInfoBar is what I keep thinking GtkHeaderBar is +- **GtkListBox** +- GtkRevealer for smooth animations of disclosure triangles +- GtkSearchBar for custom search popups +- **GtkStack and GtkStackSwitcher** +- titlebar overrides (seems to be the hot new thing) + +### GTK+ 3.12 +ships with: Ubuntu Utopic (14.10) +
GLib version: 2.42 + +- GtkActionBar (basically like the bottom-of-the-window toolbars in Mac programs) +- gtk_get_locale_direction(), for internationalization +- more control over GtkHeaderBar +- **GtkPopover** + - GtkPopovers on GtkMenuButtons +- GtkStack signaling +- **gtk_tree_path_new_from_indicesv()** (for when we add Table if we have trees too) + +### GTK+ 3.14 +ships with: **Debian Jessie**, Ubuntu Vivid (15.04) +
GLib version: Debian: 2.42, Ubuntu: 2.44 + +- gestures +- better GtkListbox selection handling +- more style classes (TODO also prior?) +- delayed switch changes on GtkSwitch + +### GTK+ 3.16 +ships with: Ubuntu Wily (15.10) +
GLib version: 2.46 + +- gtk_clipboard_get_default() (???) +- **GtkGLArea** +- proper xalign and yalign for GtkLabel; should get rid of runtime deprecation warnings +- better control of GtkListBox model-based creation (probably not relevant but) +- GtkModelButton (for GActions; probably not relevant?) +- wide handles on GtkPaned +- GtkPopoverMenu +- IPP paper names in GtkPaperSize (TODO will this be important for printing?) +- multiple matches in GtkSearchEntry (TODO evaluate priority) +- **GtkStackSidebar** +- GTK_STYLE_CLASS_LABEL, GTK_STYLE_CLASS_MONOSPACE, GTK_STYLE_CLASS_STATUSBAR, GTK_STYLE_CLASS_TOUCH_SELECTION, GTK_STYLE_CLASS_WIDE (TODO figure out which of these are useful) +- GtkTextView: extend-selection +- GtkTextView: font fallbacks + +### GTK+ 3.18 + +### GTK+ 3.20 + +## Cocoa +### Mac OS X 10.8 + +- Foundation ([full details](https://developer.apple.com/library/mac/releasenotes/Foundation/RN-FoundationOlderNotes/#//apple_ref/doc/uid/TP40008080-TRANSLATED_CHAPTER_965-TRANSLATED_DEST_999B)) + - NSDateComponents supports leap months + - NSNumberFormatter and NSDateFormatter default to 10.4 behavior by default (need to explicitly do this on 10.7) + - **NSUserNotification and NSUserNotificationCenter for Growl-style notifications** + - better linguistic triggers for Spanish and Italian + - NSByteCountFormatter +- AppKit ([full details](https://developer.apple.com/library/mac/releasenotes/AppKit/RN-AppKitOlderNotes/#X10_8Notes)) + - view-based NSTableView/NSOutlineView have expansion tooltips + - NSScrollView magnification + - Quick Look events; TODO see if they conflict with keyboard handling in Area + - NSPageController (maybe useful?) + - not useful for package UI, but may be useful for a new library (probably not by me): NSSharingService + - NSOpenPanel and NSSavePanel are now longer NSPanels or NSWindows in sandboxed applications; this may be an issue should anyone dare to enable sandboxing on a program that uses package ui + - NSTextAlternatives + - -[NSOpenGLContext setFullScreen] now ineffective + - +[NSColor underPageBackgroundColor] + +### Mac OS X 10.9 + +- Foundation ([full details](https://developer.apple.com/library/mac/releasenotes/Foundation/RN-Foundation/)) + - system-provided progress reporting/cancellation support + - NSURLComponents + - **NSCalendar, NSDateFormatter, and NSNumberFormatter are now thread-safe** + - various NSCalendar and NSDateComponents improvements +- AppKit ([full details](https://developer.apple.com/library/mac/releasenotes/AppKit/RN-AppKit/)) + - sheet handling is now block-based, queued, and in NSWindow; the delegate-based NSApplication API will still exist, except without the queue + - similar changes to NSAlert + - **return value changes to NSAlert** + - window visibility APIs (occlusion) + - NSApplicationActivationPolicyAccessory + - fullscreen toolbar behavior changes + - status items for multiple menu bars + - better NSSharingService support + - a special accelerated scrolling mode, Responsive Scrolling; won't matter for us since I plan to support the scroll wheel and it won't + - NSScrollView live scrolling notifications + - NSScrollView floating (anchored/non-scrolling) subviews + - better multimonitor support + - better key-value observing for NSOpenPanel/NSSavePanel (might want to look this up to see if we can override some other juicy details... TODO) + - better accessory view key-view handling in NSOpenPanel/NSSavePanel + - NSAppearance + - **-[NSTableView moveRowAtIndex:toIndex:] bug regarding first responders fixed** + - view-specific RTL overrides + +### Mac OS X 10.10 + +### Mac OS X 10.11 From 31f2b6d059472ccbe1df2c19b6ae57406036ce3f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 12 Jun 2016 11:55:26 -0400 Subject: [PATCH 0290/1329] More future plans. --- Compatibility.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Compatibility.md b/Compatibility.md index eb136b7c..bc16f1bf 100644 --- a/Compatibility.md +++ b/Compatibility.md @@ -138,3 +138,4 @@ ships with: Ubuntu Wily (15.10) ### Mac OS X 10.10 ### Mac OS X 10.11 +* **NSLayoutGuide** From 8f0bac54a3bffbd3e796f88e3885b4cb1fd8b822 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 12 Jun 2016 12:31:44 -0400 Subject: [PATCH 0291/1329] Took an alternate route through the constraints in the grid. This should be a bit easier... --- darwin/grid.m | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/darwin/grid.m b/darwin/grid.m index d019d9df..c0cb4d3a 100644 --- a/darwin/grid.m +++ b/darwin/grid.m @@ -133,6 +133,7 @@ struct uiGrid { NSNumber *number; NSLayoutConstraint *c; NSView **colviews, **rowviews; + NSView *firstView; [self removeOurConstraints]; if ([self->children count] == 0) @@ -237,7 +238,57 @@ struct uiGrid { [self addConstraint:c]; [self->edges addObject:c]; } - [set release]; + + // now put all the views in the same row and column together + for (x = 0; x < xcount; x++) { + [set removeAllObjects]; + for (y = 0; y < ycount; y++) + [set addObject:[NSNumber numberWithInt:gg[y][x]]]; + first = YES; + for (number in set) { + if ([number intValue] == -1) + continue; + gc = (gridChild *) [self->children objectAtIndex:[number intValue]]; + if (first) { + firstView = [gc view]; + first = NO; + continue; + } + c = mkConstraint([gc view], NSLayoutAttributeLeading, + NSLayoutRelationEqual, + firstView, NSLayoutAttributeLeading, + 1, 0, + @"uiGrid column left edge constraint"); + [self addConstraint:c]; + [self->edges addObject:c]; + } + } + for (y = 0; y < ycount; y++) { + [set removeAllObjects]; + for (x = 0; x < xcount; x++) + [set addObject:[NSNumber numberWithInt:gg[y][x]]]; + first = YES; + for (number in set) { + if ([number intValue] == -1) + continue; + gc = (gridChild *) [self->children objectAtIndex:[number intValue]]; + if (first) { + firstView = [gc view]; + first = NO; + continue; + } + c = mkConstraint([gc view], NSLayoutAttributeTop, + NSLayoutRelationEqual, + firstView, NSLayoutAttributeTop, + 1, 0, + @"uiGrid row top edge constraint"); + [self addConstraint:c]; + [self->edges addObject:c]; + } + } + +// TODO +return; // now go through every row and column and extract SOME view from that row and column for the inner constraints // if it turns out that a row or column is totally empty, duplicate the one to the left (this has the effect of collapsing empty rows) @@ -310,6 +361,7 @@ struct uiGrid { // and finally clean up uiFree(colviews); uiFree(rowviews); + [set release]; for (y = 0; y < ycount; y++) uiFree(gg[y]); uiFree(gg); From 6496de0fb2171e2001eb0a3c36b2a1b5647df5ba Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 12 Jun 2016 12:35:13 -0400 Subject: [PATCH 0292/1329] More TODOs. --- darwin/grid.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/darwin/grid.m b/darwin/grid.m index c0cb4d3a..ab13a62e 100644 --- a/darwin/grid.m +++ b/darwin/grid.m @@ -1,6 +1,8 @@ // 11 june 2016 #import "uipriv_darwin.h" +// TODO adjust all containers to handle hidden cells properly + // TODO wrap the child in a view if its align isn't fill // maybe it's easier to do it regardless of align @interface gridChild : NSObject From a2e5dbc94ca896430649edfca816d5c499540f7a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 12 Jun 2016 13:12:36 -0400 Subject: [PATCH 0293/1329] Saved some experiments. --- adjacent.m | 40 ++++++++++++++++++++++++++++++++++++++++ reduce.m | 30 ++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 adjacent.m create mode 100644 reduce.m diff --git a/adjacent.m b/adjacent.m new file mode 100644 index 00000000..5bf80468 --- /dev/null +++ b/adjacent.m @@ -0,0 +1,40 @@ + // now find all horizontally adjacent views and string them together + for (y = 0; y < ycount; y++) + for (x = 0; x < xcount - 1; x++) { + if (gg[y][x] == -1) + continue; + if (gg[y][x + 1] == -1) + continue; + if (gg[y][x] == gg[y][x + 1]) // spanning + continue; + gc = (gridChild *) [self->children objectAtIndex:gg[y][x]]; + firstView = [gc view]; + gc = (gridChild *) [self->children objectAtIndex:gg[y][x + 1]]; + c = mkConstraint(firstView, NSLayoutAttributeTrailing, + NSLayoutRelationEqual, + [gc view], NSLayoutAttributeLeading, + 1, -padding, + @"uiGrid inside trailing attribute"); + [self addConstraint:c]; + [self->inBetweens addObject:c]; + } + // and same for vertically adjacent + for (x = 0; x < xcount; x++) + for (y = 0; y < ycount - 1; y++) { + if (gg[y][x] == -1) + continue; + if (gg[y + 1][x] == -1) + continue; + if (gg[y][x] == gg[y + 1][x]) // spanning + continue; + gc = (gridChild *) [self->children objectAtIndex:gg[y][x]]; + firstView = [gc view]; + gc = (gridChild *) [self->children objectAtIndex:gg[y + 1][x]]; + c = mkConstraint(firstView, NSLayoutAttributeBottom, + NSLayoutRelationEqual, + [gc view], NSLayoutAttributeTop, + 1, -padding, + @"uiGrid inside bottom attribute"); + [self addConstraint:c]; + [self->inBetweens addObject:c]; + } diff --git a/reduce.m b/reduce.m new file mode 100644 index 00000000..e3c9b676 --- /dev/null +++ b/reduce.m @@ -0,0 +1,30 @@ + // if a row or column only contains emptys and spanning cells of a opposite-direction spannings, remove it by duplicating the previous row or column + BOOL onlyEmptyAndSpanning; + for (y = 0; y < ycount; y++) { + onlyEmptyAndSpanning = YES; + for (x = 0; x < xcount; x++) + if (gg[y][x] != -1) { + gc = (gridChild *) [self->children objectAtIndex:gg[y][x]]; + if (gc.yspan == 1 || gc.top - ymin == y) { + onlyEmptyAndSpanning = NO; + break; + } + } + if (onlyEmptyAndSpanning) + for (x = 0; x < xcount; x++) + gg[y][x] = gg[y - 1][x]; + } + for (x = 0; x < xcount; x++) { + onlyEmptyAndSpanning = YES; + for (y = 0; y < ycount; y++) + if (gg[y][x] != -1) { + gc = (gridChild *) [self->children objectAtIndex:gg[y][x]]; + if (gc.xspan == 1 || gc.left - xmin == x) { + onlyEmptyAndSpanning = NO; + break; + } + } + if (onlyEmptyAndSpanning) + for (y = 0; y < ycount; y++) + gg[y][x] = gg[y][x - 1]; + } From ebf4bce2454f041b2d557ac00f9f6c07d1a00bb0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 12 Jun 2016 13:28:44 -0400 Subject: [PATCH 0294/1329] Moved the .m files out of the way. --- adjacent.m => _grid/adjacent.m | 0 reduce.m => _grid/reduce.m | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename adjacent.m => _grid/adjacent.m (100%) rename reduce.m => _grid/reduce.m (100%) diff --git a/adjacent.m b/_grid/adjacent.m similarity index 100% rename from adjacent.m rename to _grid/adjacent.m diff --git a/reduce.m b/_grid/reduce.m similarity index 100% rename from reduce.m rename to _grid/reduce.m From 77f4c2f3095520258ebb95a648f3915145ae0854 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 12 Jun 2016 13:47:33 -0400 Subject: [PATCH 0295/1329] Split out more of the uiGrid logic. Rewrote the edge constraints to use a topological map of views. Added dummy views for empty cells. --- _grid/samerowcol.m | 47 +++++++++ _grid/tieany.m | 66 ++++++++++++ darwin/grid.m | 244 ++++++++++++--------------------------------- 3 files changed, 176 insertions(+), 181 deletions(-) create mode 100644 _grid/samerowcol.m create mode 100644 _grid/tieany.m diff --git a/_grid/samerowcol.m b/_grid/samerowcol.m new file mode 100644 index 00000000..cf20f8ce --- /dev/null +++ b/_grid/samerowcol.m @@ -0,0 +1,47 @@ + // now put all the views in the same row and column together + for (x = 0; x < xcount; x++) { + [set removeAllObjects]; + for (y = 0; y < ycount; y++) + [set addObject:[NSNumber numberWithInt:gg[y][x]]]; + first = YES; + for (number in set) { + if ([number intValue] == -1) + continue; + gc = (gridChild *) [self->children objectAtIndex:[number intValue]]; + if (first) { + firstView = [gc view]; + first = NO; + continue; + } + c = mkConstraint([gc view], NSLayoutAttributeLeading, + NSLayoutRelationEqual, + firstView, NSLayoutAttributeLeading, + 1, 0, + @"uiGrid column left edge constraint"); + [self addConstraint:c]; + [self->edges addObject:c]; + } + } + for (y = 0; y < ycount; y++) { + [set removeAllObjects]; + for (x = 0; x < xcount; x++) + [set addObject:[NSNumber numberWithInt:gg[y][x]]]; + first = YES; + for (number in set) { + if ([number intValue] == -1) + continue; + gc = (gridChild *) [self->children objectAtIndex:[number intValue]]; + if (first) { + firstView = [gc view]; + first = NO; + continue; + } + c = mkConstraint([gc view], NSLayoutAttributeTop, + NSLayoutRelationEqual, + firstView, NSLayoutAttributeTop, + 1, 0, + @"uiGrid row top edge constraint"); + [self addConstraint:c]; + [self->edges addObject:c]; + } + } diff --git a/_grid/tieany.m b/_grid/tieany.m new file mode 100644 index 00000000..e8da979e --- /dev/null +++ b/_grid/tieany.m @@ -0,0 +1,66 @@ + NSView **colviews, **rowviews; + // now go through every row and column and extract SOME view from that row and column for the inner constraints + // if it turns out that a row or column is totally empty, duplicate the one to the left (this has the effect of collapsing empty rows) + // note that the edges cannot be empty because we built a smallest fitting rectangle way back in step 1 + colviews = (NSView **) uiAlloc(xcount * sizeof (NSView *), "NSView *[]"); + for (x = 0; x < xcount; x++) { + for (y = 0; y < ycount; y++) + if (gg[y][x] != -1) { + gc = (gridChild *) [self->children objectAtIndex:gg[y][x]]; + colviews[x] = [gc view]; + break; + } + if (colviews[x] == nil) + colviews[x] = colviews[x - 1]; + } + rowviews = (NSView **) uiAlloc(ycount * sizeof (NSView *), "NSView *[]"); + for (y = 0; y < ycount; y++) { + for (x = 0; x < xcount; x++) + if (gg[y][x] != -1) { + gc = (gridChild *) [self->children objectAtIndex:gg[y][x]]; + rowviews[y] = [gc view]; + break; + } + if (rowviews[y] == nil) + rowviews[y] = rowviews[y - 1]; + } + + // now string all the views together + for (gc in self->children) { + if (gc.left != xmin) { + c = mkConstraint([gc view], NSLayoutAttributeLeading, + NSLayoutRelationEqual, + colviews[(gc.left - 1) - xmin], NSLayoutAttributeTrailing, + 1, padding, + @"uiGrid leading constraint"); + [self addConstraint:c]; + [self->inBetweens addObject:c]; + } + if (gc.top != ymin) { + c = mkConstraint([gc view], NSLayoutAttributeTop, + NSLayoutRelationEqual, + rowviews[(gc.top - 1) - ymin], NSLayoutAttributeBottom, + 1, padding, + @"uiGrid top constraint"); + [self addConstraint:c]; + [self->inBetweens addObject:c]; + } + if ((gc.left + gc.xspan) != xmax) { + c = mkConstraint([gc view], NSLayoutAttributeTrailing, + NSLayoutRelationEqual, + colviews[(gc.left + gc.xspan) - xmin], NSLayoutAttributeLeading, + 1, -padding, + @"uiGrid trailing constraint"); + [self addConstraint:c]; + [self->inBetweens addObject:c]; + } + if ((gc.top + gc.yspan) != ymax) { + c = mkConstraint([gc view], NSLayoutAttributeBottom, + NSLayoutRelationEqual, + rowviews[(gc.top + gc.yspan) - ymin], NSLayoutAttributeTop, + 1, -padding, + @"uiGrid bottom constraint"); + [self addConstraint:c]; + [self->inBetweens addObject:c]; + } + } diff --git a/darwin/grid.m b/darwin/grid.m index ab13a62e..6c22e67e 100644 --- a/darwin/grid.m +++ b/darwin/grid.m @@ -30,6 +30,8 @@ NSMutableArray *edges; NSMutableArray *inBetweens; + + NSMutableArray *emptyCellViews; } - (id)initWithG:(uiGrid *)gg; - (void)onDestroy; @@ -73,6 +75,8 @@ struct uiGrid { self->edges = [NSMutableArray new]; self->inBetweens = [NSMutableArray new]; + + self->emptyCellViews = [NSMutableArray new]; } return self; } @@ -85,6 +89,8 @@ struct uiGrid { [self->edges release]; [self->inBetweens release]; + [self->emptyCellViews release]; + for (gc in self->children) { uiControlSetParent(gc.c, NULL); uiDarwinControlSetSuperview(uiDarwinControl(gc.c), nil); @@ -95,6 +101,8 @@ struct uiGrid { - (void)removeOurConstraints { + NSView *v; + if ([self->edges count] != 0) { [self removeConstraints:self->edges]; [self->edges removeAllObjects]; @@ -103,6 +111,10 @@ struct uiGrid { [self removeConstraints:self->inBetweens]; [self->inBetweens removeAllObjects]; } + + for (v in self->emptyCellViews) + [v removeFromSuperview]; + [self->emptyCellViews removeAllObjects]; } - (void)syncEnableStates:(int)enabled @@ -129,13 +141,10 @@ struct uiGrid { intmax_t xcount, ycount; BOOL first; int **gg; + NSView ***gv; intmax_t x, y; int i; - NSMutableSet *set; - NSNumber *number; NSLayoutConstraint *c; - NSView **colviews, **rowviews; - NSView *firstView; [self removeOurConstraints]; if ([self->children count] == 0) @@ -179,194 +188,67 @@ struct uiGrid { gg[y - ymin][x - xmin] = i; } + // now build a topological map of the grid's views gv[y][x] + // for any empty cell, create a dummy view + gv = (NSView ***) uiAlloc(ycount * sizeof (NSView **), "NSView *[][]"); + for (y = 0; y < ycount; y++) { + gv[y] = (NSView **) uiAlloc(xcount * sizeof (NSView *), "NSView *[]"); + for (x = 0; x < xcount; x++) + if (gg[y][x] == -1) { + gv[y][x] = [[NSView alloc] initWithFrame:NSZeroRect]; + [self addSubview:gv[y][x]]; + [self->emptyCellViews addObject:gv[y][x]]; + } else { + gc = (gridChild *) [self->children objectAtIndex:gg[y][x]]; + gv[y][x] = [gc view]; + } + } + // now establish all the edge constraints - // leading edge - set = [NSMutableSet new]; - for (y = 0; y < ycount; y++) - [set addObject:[NSNumber numberWithInt:gg[y][0]]]; - for (number in set) - if ([number intValue] != -1) { - gc = (gridChild *) [self->children objectAtIndex:[number intValue]]; - c = mkConstraint(self, NSLayoutAttributeLeading, - NSLayoutRelationEqual, - [gc view], NSLayoutAttributeLeading, - 1, 0, - @"uiGrid leading edge constraint"); - [self addConstraint:c]; - [self->edges addObject:c]; - } - // top - [set removeAllObjects]; - for (x = 0; x < xcount; x++) - [set addObject:[NSNumber numberWithInt:gg[0][x]]]; - for (number in set) - if ([number intValue] != -1) { - gc = (gridChild *) [self->children objectAtIndex:[number intValue]]; - c = mkConstraint(self, NSLayoutAttributeTop, - NSLayoutRelationEqual, - [gc view], NSLayoutAttributeTop, - 1, 0, - @"uiGrid top edge constraint"); - [self addConstraint:c]; - [self->edges addObject:c]; - } - // trailing edge - [set removeAllObjects]; - for (y = 0; y < ycount; y++) - [set addObject:[NSNumber numberWithInt:gg[y][xcount - 1]]]; - for (number in set) - if ([number intValue] != -1) { - gc = (gridChild *) [self->children objectAtIndex:[number intValue]]; - c = mkConstraint(self, NSLayoutAttributeTrailing, - NSLayoutRelationEqual, - [gc view], NSLayoutAttributeTrailing, - 1, 0, - @"uiGrid trailing edge constraint"); - [self addConstraint:c]; - [self->edges addObject:c]; - } - // bottom - [set removeAllObjects]; - for (x = 0; x < xcount; x++) - [set addObject:[NSNumber numberWithInt:gg[ycount - 1][x]]]; - for (number in set) - if ([number intValue] != -1) { - gc = (gridChild *) [self->children objectAtIndex:[number intValue]]; - c = mkConstraint(self, NSLayoutAttributeBottom, - NSLayoutRelationEqual, - [gc view], NSLayoutAttributeBottom, - 1, 0, - @"uiGrid bottom edge constraint"); - [self addConstraint:c]; - [self->edges addObject:c]; - } - - // now put all the views in the same row and column together - for (x = 0; x < xcount; x++) { - [set removeAllObjects]; - for (y = 0; y < ycount; y++) - [set addObject:[NSNumber numberWithInt:gg[y][x]]]; - first = YES; - for (number in set) { - if ([number intValue] == -1) - continue; - gc = (gridChild *) [self->children objectAtIndex:[number intValue]]; - if (first) { - firstView = [gc view]; - first = NO; - continue; - } - c = mkConstraint([gc view], NSLayoutAttributeLeading, - NSLayoutRelationEqual, - firstView, NSLayoutAttributeLeading, - 1, 0, - @"uiGrid column left edge constraint"); - [self addConstraint:c]; - [self->edges addObject:c]; - } - } + // leading and trailing edges for (y = 0; y < ycount; y++) { - [set removeAllObjects]; - for (x = 0; x < xcount; x++) - [set addObject:[NSNumber numberWithInt:gg[y][x]]]; - first = YES; - for (number in set) { - if ([number intValue] == -1) - continue; - gc = (gridChild *) [self->children objectAtIndex:[number intValue]]; - if (first) { - firstView = [gc view]; - first = NO; - continue; - } - c = mkConstraint([gc view], NSLayoutAttributeTop, - NSLayoutRelationEqual, - firstView, NSLayoutAttributeTop, - 1, 0, - @"uiGrid row top edge constraint"); - [self addConstraint:c]; - [self->edges addObject:c]; - } + c = mkConstraint(self, NSLayoutAttributeLeading, + NSLayoutRelationEqual, + gv[y][0], NSLayoutAttributeLeading, + 1, 0, + @"uiGrid leading edge constraint"); + [self addConstraint:c]; + [self->edges addObject:c]; + c = mkConstraint(self, NSLayoutAttributeTrailing, + NSLayoutRelationEqual, + gv[y][xcount - 1], NSLayoutAttributeTrailing, + 1, 0, + @"uiGrid trailing edge constraint"); + [self addConstraint:c]; + [self->edges addObject:c]; } - -// TODO -return; - - // now go through every row and column and extract SOME view from that row and column for the inner constraints - // if it turns out that a row or column is totally empty, duplicate the one to the left (this has the effect of collapsing empty rows) - // note that the edges cannot be empty because we built a smallest fitting rectangle way back in step 1 - colviews = (NSView **) uiAlloc(xcount * sizeof (NSView *), "NSView *[]"); + // top and bottom edges for (x = 0; x < xcount; x++) { - for (y = 0; y < ycount; y++) - if (gg[y][x] != -1) { - gc = (gridChild *) [self->children objectAtIndex:gg[y][x]]; - colviews[x] = [gc view]; - break; - } - if (colviews[x] == nil) - colviews[x] = colviews[x - 1]; - } - rowviews = (NSView **) uiAlloc(ycount * sizeof (NSView *), "NSView *[]"); - for (y = 0; y < ycount; y++) { - for (x = 0; x < xcount; x++) - if (gg[y][x] != -1) { - gc = (gridChild *) [self->children objectAtIndex:gg[y][x]]; - rowviews[y] = [gc view]; - break; - } - if (rowviews[y] == nil) - rowviews[y] = rowviews[y - 1]; - } - - // now string all the views together - for (gc in self->children) { - if (gc.left != xmin) { - c = mkConstraint([gc view], NSLayoutAttributeLeading, - NSLayoutRelationEqual, - colviews[(gc.left - 1) - xmin], NSLayoutAttributeTrailing, - 1, padding, - @"uiGrid leading constraint"); - [self addConstraint:c]; - [self->inBetweens addObject:c]; - } - if (gc.top != ymin) { - c = mkConstraint([gc view], NSLayoutAttributeTop, - NSLayoutRelationEqual, - rowviews[(gc.top - 1) - ymin], NSLayoutAttributeBottom, - 1, padding, - @"uiGrid top constraint"); - [self addConstraint:c]; - [self->inBetweens addObject:c]; - } - if ((gc.left + gc.xspan) != xmax) { - c = mkConstraint([gc view], NSLayoutAttributeTrailing, - NSLayoutRelationEqual, - colviews[(gc.left + gc.xspan) - xmin], NSLayoutAttributeLeading, - 1, -padding, - @"uiGrid trailing constraint"); - [self addConstraint:c]; - [self->inBetweens addObject:c]; - } - if ((gc.top + gc.yspan) != ymax) { - c = mkConstraint([gc view], NSLayoutAttributeBottom, - NSLayoutRelationEqual, - rowviews[(gc.top + gc.yspan) - ymin], NSLayoutAttributeTop, - 1, -padding, - @"uiGrid bottom constraint"); - [self addConstraint:c]; - [self->inBetweens addObject:c]; - } + c = mkConstraint(self, NSLayoutAttributeTop, + NSLayoutRelationEqual, + gv[0][x], NSLayoutAttributeTop, + 1, 0, + @"uiGrid top edge constraint"); + [self addConstraint:c]; + [self->edges addObject:c]; + c = mkConstraint(self, NSLayoutAttributeBottom, + NSLayoutRelationEqual, + gv[ycount - 1][x], NSLayoutAttributeBottom, + 1, 0, + @"uiGrid bottom edge constraint"); + [self addConstraint:c]; + [self->edges addObject:c]; } // TODO make all expanding rows/columns the same height/width // and finally clean up - uiFree(colviews); - uiFree(rowviews); - [set release]; - for (y = 0; y < ycount; y++) + for (y = 0; y < ycount; y++) { uiFree(gg[y]); + uiFree(gv[y]); + } uiFree(gg); + uiFree(gv); } - (void)append:(gridChild *)gc From 7908972d344df721aee34b78114cd562471a1855 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 12 Jun 2016 15:08:07 -0400 Subject: [PATCH 0296/1329] Continuing the current approach. This might work out better... --- darwin/grid.m | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/darwin/grid.m b/darwin/grid.m index 6c22e67e..6cc2ed4f 100644 --- a/darwin/grid.m +++ b/darwin/grid.m @@ -240,6 +240,52 @@ struct uiGrid { [self->edges addObject:c]; } + // now align leading and top edges + for (x = 0; x < xcount; x++) + for (y = 1; y < ycount; y++) { + c = mkConstraint(gv[0][x], NSLayoutAttributeLeading, + NSLayoutRelationEqual, + gv[y][x], NSLayoutAttributeLeading, + 1, 0, + @"uiGrid column leading constraint"); + [self addConstraint:c]; + [self->edges addObject:c]; + } + for (y = 0; y < ycount; y++) + for (x = 1; x < xcount; x++) { + c = mkConstraint(gv[y][0], NSLayoutAttributeTop, + NSLayoutRelationEqual, + gv[y][x], NSLayoutAttributeTop, + 1, 0, + @"uiGrid row top constraint"); + [self addConstraint:c]; + [self->edges addObject:c]; + } + + // now string adjacent views together + for (y = 0; y < ycount; y++) + for (x = 1; x < xcount; x++) + if (gv[y][x - 1] != gv[y][x]) { + c = mkConstraint(gv[y][x - 1], NSLayoutAttributeTrailing, + NSLayoutRelationEqual, + gv[y][x], NSLayoutAttributeLeading, + 1, -padding, + @"uiGrid internal horizontal constraint"); + [self addConstraint:c]; + [self->inBetweens addObject:c]; + } + for (x = 0; x < xcount; x++) + for (y = 1; y < ycount; y++) + if (gv[y - 1][x] != gv[y][x]) { + c = mkConstraint(gv[y - 1][x], NSLayoutAttributeBottom, + NSLayoutRelationEqual, + gv[y][x], NSLayoutAttributeTop, + 1, -padding, + @"uiGrid internal vertical constraint"); + [self addConstraint:c]; + [self->inBetweens addObject:c]; + } + // TODO make all expanding rows/columns the same height/width // and finally clean up From 0ce4fd6efd5d28f5d71a32db5150c3f869177963 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 12 Jun 2016 15:30:36 -0400 Subject: [PATCH 0297/1329] FIgured out that we need to ignore spanning cells; added support for that. Not yet perfect; still need to do a bit more... --- darwin/grid.m | 39 ++++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/darwin/grid.m b/darwin/grid.m index 6cc2ed4f..e97a2468 100644 --- a/darwin/grid.m +++ b/darwin/grid.m @@ -142,9 +142,11 @@ struct uiGrid { BOOL first; int **gg; NSView ***gv; + BOOL **gspan; intmax_t x, y; int i; NSLayoutConstraint *c; + intmax_t firstx, firsty; [self removeOurConstraints]; if ([self->children count] == 0) @@ -175,17 +177,23 @@ struct uiGrid { ycount = ymax - ymin; // now build a topological map of the grid gg[y][x] + // also figure out which cells contain spanned views so they can be ignored later gg = (int **) uiAlloc(ycount * sizeof (int *), "int[][]"); + gspan = (BOOL **) uiAlloc(ycount * sizeof (BOOL *), "BOOL[][]"); for (y = 0; y < ycount; y++) { gg[y] = (int *) uiAlloc(xcount * sizeof (int), "int[]"); + gspan[y] = (BOOL *) uiAlloc(xcount * sizeof (BOOL), "BOOL[]"); for (x = 0; x < xcount; x++) gg[y][x] = -1; // empty } for (i = 0; i < [self->children count]; i++) { gc = (gridChild *) [self->children objectAtIndex:i]; for (y = gc.top; y < gc.top + gc.yspan; y++) - for (x = gc.left; x < gc.left + gc.xspan; x++) + for (x = gc.left; x < gc.left + gc.xspan; x++) { gg[y - ymin][x - xmin] = i; + if (x != gc.left || y != gc.top) + gspan[y - ymin][x - xmin] = YES; + } } // now build a topological map of the grid's views gv[y][x] @@ -241,9 +249,16 @@ struct uiGrid { } // now align leading and top edges - for (x = 0; x < xcount; x++) - for (y = 1; y < ycount; y++) { - c = mkConstraint(gv[0][x], NSLayoutAttributeLeading, + // do NOT align spanning cells! + for (x = 0; x < xcount; x++) { + for (y = 0; y < ycount; y++) + if (!gspan[y][x]) + break; + firsty = y; + for (y++; y < ycount; y++) { + if (gspan[y][x]) + continue; + c = mkConstraint(gv[firsty][x], NSLayoutAttributeLeading, NSLayoutRelationEqual, gv[y][x], NSLayoutAttributeLeading, 1, 0, @@ -251,9 +266,16 @@ struct uiGrid { [self addConstraint:c]; [self->edges addObject:c]; } - for (y = 0; y < ycount; y++) - for (x = 1; x < xcount; x++) { - c = mkConstraint(gv[y][0], NSLayoutAttributeTop, + } + for (y = 0; y < ycount; y++) { + for (x = 0; x < xcount; x++) + if (!gspan[y][x]) + break; + firstx = x; + for (x++; x < xcount; x++) { + if (gspan[y][x]) + continue; + c = mkConstraint(gv[y][firstx], NSLayoutAttributeTop, NSLayoutRelationEqual, gv[y][x], NSLayoutAttributeTop, 1, 0, @@ -261,6 +283,7 @@ struct uiGrid { [self addConstraint:c]; [self->edges addObject:c]; } + } // now string adjacent views together for (y = 0; y < ycount; y++) @@ -292,9 +315,11 @@ struct uiGrid { for (y = 0; y < ycount; y++) { uiFree(gg[y]); uiFree(gv[y]); + uiFree(gspan[y]); } uiFree(gg); uiFree(gv); + uiFree(gspan); } - (void)append:(gridChild *)gc From 629c242a835062aedabbcb95760113b7224a794e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 12 Jun 2016 15:35:54 -0400 Subject: [PATCH 0298/1329] Forgot to make the dummy views ignore autoresizing masks. --- darwin/grid.m | 1 + 1 file changed, 1 insertion(+) diff --git a/darwin/grid.m b/darwin/grid.m index e97a2468..369dc94b 100644 --- a/darwin/grid.m +++ b/darwin/grid.m @@ -204,6 +204,7 @@ struct uiGrid { for (x = 0; x < xcount; x++) if (gg[y][x] == -1) { gv[y][x] = [[NSView alloc] initWithFrame:NSZeroRect]; + [gv[y][x] setTranslatesAutoresizingMaskIntoConstraints:NO]; [self addSubview:gv[y][x]]; [self->emptyCellViews addObject:gv[y][x]]; } else { From f89feba680a07ba936fa2c41d3aa1e2e7c849b5c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 12 Jun 2016 16:27:29 -0400 Subject: [PATCH 0299/1329] Included spanning views in hugging priority management. --- darwin/grid.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/darwin/grid.m b/darwin/grid.m index 369dc94b..8af275f8 100644 --- a/darwin/grid.m +++ b/darwin/grid.m @@ -333,16 +333,16 @@ struct uiGrid { uiDarwinControlSetSuperview(uiDarwinControl(gc.c), self); uiDarwinControlSyncEnableState(uiDarwinControl(gc.c), uiControlEnabledToUser(uiControl(self->g))); - // if a control expands horizontally, it should not hug horizontally + // if a control expands horizontally OR spans horizontally, it should not hug horizontally // otherwise, it should *forcibly* hug - if (gc.hexpand) + if (gc.hexpand || gc.xspan != 1) priority = NSLayoutPriorityDefaultLow; else // LONGTERM will default high work? priority = NSLayoutPriorityRequired; uiDarwinControlSetHuggingPriority(uiDarwinControl(gc.c), priority, NSLayoutConstraintOrientationHorizontal); // same for vertical direction - if (gc.vexpand) + if (gc.vexpand || gc.yspan != 1) priority = NSLayoutPriorityDefaultLow; else // LONGTERM will default high work? From 99e660377c3d109eddc64b2098cbbea33764e731 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 12 Jun 2016 16:56:06 -0400 Subject: [PATCH 0300/1329] More experiments. --- _grid/expand.m | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 _grid/expand.m diff --git a/_grid/expand.m b/_grid/expand.m new file mode 100644 index 00000000..8bd15eec --- /dev/null +++ b/_grid/expand.m @@ -0,0 +1,36 @@ + // now figure out which rows and columns really expand + hexpand = (BOOL *) uiAlloc(xcount * sizeof (BOOL), "BOOL[]"); + vexpand = (BOOL *) uiAlloc(ycount * sizeof (BOOL), "BOOL[]"); + // first, which don't span + for (gc in self->children) { + if (gc.hexpand && gc.xspan == 1) + hexpand[gc.left - xmin] = YES; + if (gc.vexpand && gc.yspan == 1) + vexpand[gc.top - ymin] = YES; + } + // second, which do span + // the way we handle this is simple: if none of the spanned rows/columns expand, make all rows/columns expand + for (gc in self->children) { + if (gc.hexpand && gc.xspan != 1) { + doit = YES; + for (x = gc.left; x < gc.left + gc.xspan; x++) + if (hexpand[x - xmin]) { + doit = NO; + break; + } + if (doit) + for (x = gc.left; x < gc.left + gc.xspan; x++) + hexpand[x - xmin] = YES; + } + if (gc.vexpand && gc.yspan != 1) { + doit = YES; + for (y = gc.top; y < gc.top + gc.yspan; y++) + if (vexpand[y - ymin]) { + doit = NO; + break; + } + if (doit) + for (y = gc.top; y < gc.top + gc.yspan; y++) + vexpand[y - ymin] = YES; + } + } From 2cd97c8e6007049fa9ce918120d592c938157ce8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 12 Jun 2016 18:23:43 -0400 Subject: [PATCH 0301/1329] Reworked hugging priorities a bit. We're getting closer... --- darwin/grid.m | 77 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 62 insertions(+), 15 deletions(-) diff --git a/darwin/grid.m b/darwin/grid.m index 8af275f8..095c1a76 100644 --- a/darwin/grid.m +++ b/darwin/grid.m @@ -147,6 +147,8 @@ struct uiGrid { int i; NSLayoutConstraint *c; intmax_t firstx, firsty; + BOOL *hexpand, *vexpand; + BOOL doit; [self removeOurConstraints]; if ([self->children count] == 0) @@ -213,6 +215,43 @@ struct uiGrid { } } + // now figure out which rows and columns really expand + hexpand = (BOOL *) uiAlloc(xcount * sizeof (BOOL), "BOOL[]"); + vexpand = (BOOL *) uiAlloc(ycount * sizeof (BOOL), "BOOL[]"); + // first, which don't span + for (gc in self->children) { + if (gc.hexpand && gc.xspan == 1) + hexpand[gc.left - xmin] = YES; + if (gc.vexpand && gc.yspan == 1) + vexpand[gc.top - ymin] = YES; + } + // second, which do span + // the way we handle this is simple: if none of the spanned rows/columns expand, make all rows/columns expand + for (gc in self->children) { + if (gc.hexpand && gc.xspan != 1) { + doit = YES; + for (x = gc.left; x < gc.left + gc.xspan; x++) + if (hexpand[x - xmin]) { + doit = NO; + break; + } + if (doit) + for (x = gc.left; x < gc.left + gc.xspan; x++) + hexpand[x - xmin] = YES; + } + if (gc.vexpand && gc.yspan != 1) { + doit = YES; + for (y = gc.top; y < gc.top + gc.yspan; y++) + if (vexpand[y - ymin]) { + doit = NO; + break; + } + if (doit) + for (y = gc.top; y < gc.top + gc.yspan; y++) + vexpand[y - ymin] = YES; + } + } + // now establish all the edge constraints // leading and trailing edges for (y = 0; y < ycount; y++) { @@ -310,9 +349,31 @@ struct uiGrid { [self->inBetweens addObject:c]; } + // now set priorities for all widgets that expand or not + // if a cell is in an expanding row, OR If it spans, then it must be willing to stretch + // otherwise, it tries not to + // note we don't use NSLayoutPriorityRequired as that will cause things to squish when they shouldn't + for (gc in self->children) { + NSLayoutPriority priority; + + if (hexpand[gc.left - xmin] || gc.xspan != 1) + priority = NSLayoutPriorityDefaultLow; + else + priority = NSLayoutPriorityDefaultHigh; + uiDarwinControlSetHuggingPriority(uiDarwinControl(gc.c), priority, NSLayoutConstraintOrientationHorizontal); + // same for vertical direction + if (vexpand[gc.top - ymin] || gc.yspan != 1) + priority = NSLayoutPriorityDefaultLow; + else + priority = NSLayoutPriorityDefaultHigh; + uiDarwinControlSetHuggingPriority(uiDarwinControl(gc.c), priority, NSLayoutConstraintOrientationVertical); + } + // TODO make all expanding rows/columns the same height/width // and finally clean up + uiFree(hexpand); + uiFree(vexpand); for (y = 0; y < ycount; y++) { uiFree(gg[y]); uiFree(gv[y]); @@ -333,21 +394,7 @@ struct uiGrid { uiDarwinControlSetSuperview(uiDarwinControl(gc.c), self); uiDarwinControlSyncEnableState(uiDarwinControl(gc.c), uiControlEnabledToUser(uiControl(self->g))); - // if a control expands horizontally OR spans horizontally, it should not hug horizontally - // otherwise, it should *forcibly* hug - if (gc.hexpand || gc.xspan != 1) - priority = NSLayoutPriorityDefaultLow; - else - // LONGTERM will default high work? - priority = NSLayoutPriorityRequired; - uiDarwinControlSetHuggingPriority(uiDarwinControl(gc.c), priority, NSLayoutConstraintOrientationHorizontal); - // same for vertical direction - if (gc.vexpand || gc.yspan != 1) - priority = NSLayoutPriorityDefaultLow; - else - // LONGTERM will default high work? - priority = NSLayoutPriorityRequired; - uiDarwinControlSetHuggingPriority(uiDarwinControl(gc.c), priority, NSLayoutConstraintOrientationVertical); + // no need to set priority here; that's done in establishOurConstraints [self->children addObject:gc]; From 144366c775664845b4f15ac58364a3cd933a0e0d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 12 Jun 2016 18:26:13 -0400 Subject: [PATCH 0302/1329] And got the last bit of grid working :D --- darwin/grid.m | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/darwin/grid.m b/darwin/grid.m index 095c1a76..87175159 100644 --- a/darwin/grid.m +++ b/darwin/grid.m @@ -198,6 +198,41 @@ struct uiGrid { } } + // if a row or column only contains emptys and spanning cells of a opposite-direction spannings, remove it by duplicating the previous row or column + BOOL onlyEmptyAndSpanning; + for (y = 0; y < ycount; y++) { + onlyEmptyAndSpanning = YES; + for (x = 0; x < xcount; x++) + if (gg[y][x] != -1) { + gc = (gridChild *) [self->children objectAtIndex:gg[y][x]]; + if (gc.yspan == 1 || gc.top - ymin == y) { + onlyEmptyAndSpanning = NO; + break; + } + } + if (onlyEmptyAndSpanning) + for (x = 0; x < xcount; x++) { + gg[y][x] = gg[y - 1][x]; + gspan[y][x] = YES; + } + } + for (x = 0; x < xcount; x++) { + onlyEmptyAndSpanning = YES; + for (y = 0; y < ycount; y++) + if (gg[y][x] != -1) { + gc = (gridChild *) [self->children objectAtIndex:gg[y][x]]; + if (gc.xspan == 1 || gc.left - xmin == x) { + onlyEmptyAndSpanning = NO; + break; + } + } + if (onlyEmptyAndSpanning) + for (y = 0; y < ycount; y++) { + gg[y][x] = gg[y][x - 1]; + gspan[y][x] = YES; + } + } + // now build a topological map of the grid's views gv[y][x] // for any empty cell, create a dummy view gv = (NSView ***) uiAlloc(ycount * sizeof (NSView **), "NSView *[][]"); From 9f4092dab551330e5c6313b6d47b4a5311313424 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 12 Jun 2016 18:28:34 -0400 Subject: [PATCH 0303/1329] And put uiGrid in the updates. Yay! --- README.md | 3 +++ _grid/adjacent.m | 40 ---------------------------- _grid/expand.m | 36 ------------------------- _grid/reduce.m | 30 --------------------- _grid/samerowcol.m | 47 --------------------------------- _grid/tieany.m | 66 ---------------------------------------------- 6 files changed, 3 insertions(+), 219 deletions(-) delete mode 100644 _grid/adjacent.m delete mode 100644 _grid/expand.m delete mode 100644 _grid/reduce.m delete mode 100644 _grid/samerowcol.m delete mode 100644 _grid/tieany.m diff --git a/README.md b/README.md index bb4fb2a1..8ab69cbf 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,9 @@ This README is being written.
*Note that today's entry may be updated later today.* +* **12 June 2016** + * Added `uiGrid`, a new container control that arranges controls in rows and columns, with stretchy ("expanding") rows, stretchy ("expanding") columns, cells that span rows and columns, and cells whose content is aligned in either direction rather than just filling. It's quite powerful, is it? =P + * **8 June 2016** * Added `uiForm`, a new container control that arranges controls vertically, with properly aligned labels on each. Have fun! diff --git a/_grid/adjacent.m b/_grid/adjacent.m deleted file mode 100644 index 5bf80468..00000000 --- a/_grid/adjacent.m +++ /dev/null @@ -1,40 +0,0 @@ - // now find all horizontally adjacent views and string them together - for (y = 0; y < ycount; y++) - for (x = 0; x < xcount - 1; x++) { - if (gg[y][x] == -1) - continue; - if (gg[y][x + 1] == -1) - continue; - if (gg[y][x] == gg[y][x + 1]) // spanning - continue; - gc = (gridChild *) [self->children objectAtIndex:gg[y][x]]; - firstView = [gc view]; - gc = (gridChild *) [self->children objectAtIndex:gg[y][x + 1]]; - c = mkConstraint(firstView, NSLayoutAttributeTrailing, - NSLayoutRelationEqual, - [gc view], NSLayoutAttributeLeading, - 1, -padding, - @"uiGrid inside trailing attribute"); - [self addConstraint:c]; - [self->inBetweens addObject:c]; - } - // and same for vertically adjacent - for (x = 0; x < xcount; x++) - for (y = 0; y < ycount - 1; y++) { - if (gg[y][x] == -1) - continue; - if (gg[y + 1][x] == -1) - continue; - if (gg[y][x] == gg[y + 1][x]) // spanning - continue; - gc = (gridChild *) [self->children objectAtIndex:gg[y][x]]; - firstView = [gc view]; - gc = (gridChild *) [self->children objectAtIndex:gg[y + 1][x]]; - c = mkConstraint(firstView, NSLayoutAttributeBottom, - NSLayoutRelationEqual, - [gc view], NSLayoutAttributeTop, - 1, -padding, - @"uiGrid inside bottom attribute"); - [self addConstraint:c]; - [self->inBetweens addObject:c]; - } diff --git a/_grid/expand.m b/_grid/expand.m deleted file mode 100644 index 8bd15eec..00000000 --- a/_grid/expand.m +++ /dev/null @@ -1,36 +0,0 @@ - // now figure out which rows and columns really expand - hexpand = (BOOL *) uiAlloc(xcount * sizeof (BOOL), "BOOL[]"); - vexpand = (BOOL *) uiAlloc(ycount * sizeof (BOOL), "BOOL[]"); - // first, which don't span - for (gc in self->children) { - if (gc.hexpand && gc.xspan == 1) - hexpand[gc.left - xmin] = YES; - if (gc.vexpand && gc.yspan == 1) - vexpand[gc.top - ymin] = YES; - } - // second, which do span - // the way we handle this is simple: if none of the spanned rows/columns expand, make all rows/columns expand - for (gc in self->children) { - if (gc.hexpand && gc.xspan != 1) { - doit = YES; - for (x = gc.left; x < gc.left + gc.xspan; x++) - if (hexpand[x - xmin]) { - doit = NO; - break; - } - if (doit) - for (x = gc.left; x < gc.left + gc.xspan; x++) - hexpand[x - xmin] = YES; - } - if (gc.vexpand && gc.yspan != 1) { - doit = YES; - for (y = gc.top; y < gc.top + gc.yspan; y++) - if (vexpand[y - ymin]) { - doit = NO; - break; - } - if (doit) - for (y = gc.top; y < gc.top + gc.yspan; y++) - vexpand[y - ymin] = YES; - } - } diff --git a/_grid/reduce.m b/_grid/reduce.m deleted file mode 100644 index e3c9b676..00000000 --- a/_grid/reduce.m +++ /dev/null @@ -1,30 +0,0 @@ - // if a row or column only contains emptys and spanning cells of a opposite-direction spannings, remove it by duplicating the previous row or column - BOOL onlyEmptyAndSpanning; - for (y = 0; y < ycount; y++) { - onlyEmptyAndSpanning = YES; - for (x = 0; x < xcount; x++) - if (gg[y][x] != -1) { - gc = (gridChild *) [self->children objectAtIndex:gg[y][x]]; - if (gc.yspan == 1 || gc.top - ymin == y) { - onlyEmptyAndSpanning = NO; - break; - } - } - if (onlyEmptyAndSpanning) - for (x = 0; x < xcount; x++) - gg[y][x] = gg[y - 1][x]; - } - for (x = 0; x < xcount; x++) { - onlyEmptyAndSpanning = YES; - for (y = 0; y < ycount; y++) - if (gg[y][x] != -1) { - gc = (gridChild *) [self->children objectAtIndex:gg[y][x]]; - if (gc.xspan == 1 || gc.left - xmin == x) { - onlyEmptyAndSpanning = NO; - break; - } - } - if (onlyEmptyAndSpanning) - for (y = 0; y < ycount; y++) - gg[y][x] = gg[y][x - 1]; - } diff --git a/_grid/samerowcol.m b/_grid/samerowcol.m deleted file mode 100644 index cf20f8ce..00000000 --- a/_grid/samerowcol.m +++ /dev/null @@ -1,47 +0,0 @@ - // now put all the views in the same row and column together - for (x = 0; x < xcount; x++) { - [set removeAllObjects]; - for (y = 0; y < ycount; y++) - [set addObject:[NSNumber numberWithInt:gg[y][x]]]; - first = YES; - for (number in set) { - if ([number intValue] == -1) - continue; - gc = (gridChild *) [self->children objectAtIndex:[number intValue]]; - if (first) { - firstView = [gc view]; - first = NO; - continue; - } - c = mkConstraint([gc view], NSLayoutAttributeLeading, - NSLayoutRelationEqual, - firstView, NSLayoutAttributeLeading, - 1, 0, - @"uiGrid column left edge constraint"); - [self addConstraint:c]; - [self->edges addObject:c]; - } - } - for (y = 0; y < ycount; y++) { - [set removeAllObjects]; - for (x = 0; x < xcount; x++) - [set addObject:[NSNumber numberWithInt:gg[y][x]]]; - first = YES; - for (number in set) { - if ([number intValue] == -1) - continue; - gc = (gridChild *) [self->children objectAtIndex:[number intValue]]; - if (first) { - firstView = [gc view]; - first = NO; - continue; - } - c = mkConstraint([gc view], NSLayoutAttributeTop, - NSLayoutRelationEqual, - firstView, NSLayoutAttributeTop, - 1, 0, - @"uiGrid row top edge constraint"); - [self addConstraint:c]; - [self->edges addObject:c]; - } - } diff --git a/_grid/tieany.m b/_grid/tieany.m deleted file mode 100644 index e8da979e..00000000 --- a/_grid/tieany.m +++ /dev/null @@ -1,66 +0,0 @@ - NSView **colviews, **rowviews; - // now go through every row and column and extract SOME view from that row and column for the inner constraints - // if it turns out that a row or column is totally empty, duplicate the one to the left (this has the effect of collapsing empty rows) - // note that the edges cannot be empty because we built a smallest fitting rectangle way back in step 1 - colviews = (NSView **) uiAlloc(xcount * sizeof (NSView *), "NSView *[]"); - for (x = 0; x < xcount; x++) { - for (y = 0; y < ycount; y++) - if (gg[y][x] != -1) { - gc = (gridChild *) [self->children objectAtIndex:gg[y][x]]; - colviews[x] = [gc view]; - break; - } - if (colviews[x] == nil) - colviews[x] = colviews[x - 1]; - } - rowviews = (NSView **) uiAlloc(ycount * sizeof (NSView *), "NSView *[]"); - for (y = 0; y < ycount; y++) { - for (x = 0; x < xcount; x++) - if (gg[y][x] != -1) { - gc = (gridChild *) [self->children objectAtIndex:gg[y][x]]; - rowviews[y] = [gc view]; - break; - } - if (rowviews[y] == nil) - rowviews[y] = rowviews[y - 1]; - } - - // now string all the views together - for (gc in self->children) { - if (gc.left != xmin) { - c = mkConstraint([gc view], NSLayoutAttributeLeading, - NSLayoutRelationEqual, - colviews[(gc.left - 1) - xmin], NSLayoutAttributeTrailing, - 1, padding, - @"uiGrid leading constraint"); - [self addConstraint:c]; - [self->inBetweens addObject:c]; - } - if (gc.top != ymin) { - c = mkConstraint([gc view], NSLayoutAttributeTop, - NSLayoutRelationEqual, - rowviews[(gc.top - 1) - ymin], NSLayoutAttributeBottom, - 1, padding, - @"uiGrid top constraint"); - [self addConstraint:c]; - [self->inBetweens addObject:c]; - } - if ((gc.left + gc.xspan) != xmax) { - c = mkConstraint([gc view], NSLayoutAttributeTrailing, - NSLayoutRelationEqual, - colviews[(gc.left + gc.xspan) - xmin], NSLayoutAttributeLeading, - 1, -padding, - @"uiGrid trailing constraint"); - [self addConstraint:c]; - [self->inBetweens addObject:c]; - } - if ((gc.top + gc.yspan) != ymax) { - c = mkConstraint([gc view], NSLayoutAttributeBottom, - NSLayoutRelationEqual, - rowviews[(gc.top + gc.yspan) - ymin], NSLayoutAttributeTop, - 1, -padding, - @"uiGrid bottom constraint"); - [self addConstraint:c]; - [self->inBetweens addObject:c]; - } - } From 41ec54cb47bb2553518301e8c61499302311a295 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 12 Jun 2016 19:08:08 -0400 Subject: [PATCH 0304/1329] Started a new controlgallery. Fixed some glitches in OS X uiForm. --- darwin/form.m | 25 ++++++-- examples/controlgallery/main.c | 109 ++++++++++++++++++++++++++++++--- unix/form.c | 1 - 3 files changed, 120 insertions(+), 15 deletions(-) diff --git a/darwin/form.m b/darwin/form.m index 3a61336b..ffebc67e 100644 --- a/darwin/form.m +++ b/darwin/form.m @@ -260,11 +260,8 @@ struct uiForm { prev = [fc view]; prevlabel = fc; } - relation = NSLayoutRelationEqual; - if (self->nStretchy != 0) - relation = NSLayoutRelationLessThanOrEqual; self->last = mkConstraint(prev, NSLayoutAttributeBottom, - relation, + NSLayoutRelationEqual, self, NSLayoutAttributeBottom, 1, 0, @"uiForm last vertical constraint"); @@ -314,6 +311,25 @@ struct uiForm { [self->trailings addObject:c]; } + // and make all stretchy controls have the same height + prev = nil; + for (fc in self->children) { + if (!fc.stretchy) + continue; + if (prev == nil) { + prev = [fc view]; + continue; + } + c = mkConstraint([fc view], NSLayoutAttributeHeight, + NSLayoutRelationEqual, + prev, NSLayoutAttributeHeight, + 1, 0, + @"uiForm stretchy constraint"); + [self addConstraint:c]; + // TODO make a dedicated array for this + [self->leadings addObject:c]; + } + // we don't arrange the labels vertically; that's done when we add the control since those constraints don't need to change (they just need to be at their baseline) } @@ -333,7 +349,6 @@ struct uiForm { [self addSubview:fc]; uiControlSetParent(fc.c, uiControl(self->f)); - // TODO fix this it's wrong uiDarwinControlSetSuperview(uiDarwinControl(fc.c), self); uiDarwinControlSyncEnableState(uiDarwinControl(fc.c), uiControlEnabledToUser(uiControl(self->f))); diff --git a/examples/controlgallery/main.c b/examples/controlgallery/main.c index 1e426041..a3bc099c 100644 --- a/examples/controlgallery/main.c +++ b/examples/controlgallery/main.c @@ -3,25 +3,114 @@ #include #include "../../ui.h" -// TODOs -// - rename variables in main() -// - make both columns the same size? - -static uiWindow *mainwin; - static int onClosing(uiWindow *w, void *data) { - uiControlDestroy(uiControl(mainwin)); uiQuit(); - return 0; + return 1; } -static int shouldQuit(void *data) +static int onShouldQuit(void *data) { + uiWindow *mainwin = uiWindow(data); + uiControlDestroy(uiControl(mainwin)); return 1; } +static uiControl *makeBasicControlsPage(void) +{ + uiBox *vbox; + uiBox *hbox; + uiGroup *group; + uiForm *entryForm; + + vbox = uiNewVerticalBox(); + uiBoxSetPadded(vbox, 1); + + hbox = uiNewHorizontalBox(); + uiBoxSetPadded(hbox, 1); + uiBoxAppend(vbox, uiControl(hbox), 0); + + uiBoxAppend(hbox, + uiControl(uiNewButton("Button")), + 0); + uiBoxAppend(hbox, + uiControl(uiNewCheckbox("Checkbox")), + 0); + + uiBoxAppend(vbox, + uiControl(uiNewLabel("This is a label. Right now, labels can only span one line.")), + 0); + + uiBoxAppend(vbox, + uiControl(uiNewHorizontalSeparator()), + 0); + + group = uiNewGroup("Entries"); + uiGroupSetMargined(group, 1); + uiBoxAppend(vbox, uiControl(group), 1); + + entryForm = uiNewForm(); + uiFormSetPadded(entryForm, 1); + uiGroupSetChild(group, uiControl(entryForm)); + + uiFormAppend(entryForm, + "Entry", + uiControl(uiNewEntry()), + 0); + uiFormAppend(entryForm, + "Password Entry", + uiControl(uiNewPasswordEntry()), + 0); + uiFormAppend(entryForm, + "Search Entry", + uiControl(uiNewSearchEntry()), + 0); + uiFormAppend(entryForm, + "Multiline Entry", + uiControl(uiNewMultilineEntry()), + 1); + uiFormAppend(entryForm, + "Multiline Entry No Wrap", + uiControl(uiNewNonWrappingMultilineEntry()), + 1); + + return uiControl(vbox); +} + +int main(void) +{ + uiInitOptions options; + const char *err; + uiWindow *mainwin; + uiTab *tab; + + memset(&options, 0, sizeof (uiInitOptions)); + err = uiInit(&options); + if (err != NULL) { + fprintf(stderr, "error initializing libui: %s", err); + uiFreeInitError(err); + return 1; + } + + mainwin = uiNewWindow("libui Control Gallery", 640, 480, 1); + uiWindowOnClosing(mainwin, onClosing, NULL); + uiOnShouldQuit(onShouldQuit, mainwin); + + tab = uiNewTab(); + uiWindowSetChild(mainwin, uiControl(tab)); + uiWindowSetMargined(mainwin, 1); + + uiTabAppend(tab, "Basic Controls", makeBasicControlsPage()); + uiTabSetMargined(tab, 0, 1); + + uiControlShow(uiControl(mainwin)); + uiMain(); + return 0; +} + +#if 0 + static void openClicked(uiMenuItem *item, uiWindow *w, void *data) { char *filename; @@ -230,3 +319,5 @@ int main(void) uiUninit(); return 0; } + +#endif diff --git a/unix/form.c b/unix/form.c index bf0c9b52..4e1f7fa0 100644 --- a/unix/form.c +++ b/unix/form.c @@ -17,7 +17,6 @@ struct uiForm { GtkGrid *grid; GArray *children; int padded; - // TODO OS X is missing this GtkSizeGroup *stretchygroup; // ensures all stretchy controls have the same size }; From f08cd96688cf1fa5c50f6ac4eee22a76322ab04d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 12 Jun 2016 19:55:35 -0400 Subject: [PATCH 0305/1329] More control gallery rewriting. Removed some debugging code in the OS X grid. --- darwin/grid.m | 2 + examples/controlgallery/main.c | 178 ++++++++++++++++++++++++++++++++- 2 files changed, 179 insertions(+), 1 deletion(-) diff --git a/darwin/grid.m b/darwin/grid.m index 87175159..766b0f80 100644 --- a/darwin/grid.m +++ b/darwin/grid.m @@ -500,11 +500,13 @@ struct uiGrid { CGFloat padding; NSLayoutConstraint *c; +#if 0 /* TODO */ dispatch_after( dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ [[self window] visualizeConstraints:[self constraints]]; } ); +#endif self->padded = p; padding = [self paddingAmount]; for (c in self->inBetweens) diff --git a/examples/controlgallery/main.c b/examples/controlgallery/main.c index a3bc099c..24e8130f 100644 --- a/examples/controlgallery/main.c +++ b/examples/controlgallery/main.c @@ -78,11 +78,181 @@ static uiControl *makeBasicControlsPage(void) return uiControl(vbox); } +// TODO make these not global +static uiSpinbox *spinbox; +static uiSlider *slider; +static uiProgressBar *pbar; + +static void onSpinboxChanged(uiSpinbox *s, void *data) +{ + uiSliderSetValue(slider, uiSpinboxValue(s)); + uiProgressBarSetValue(pbar, uiSpinboxValue(s)); +} + +static void onSliderChanged(uiSlider *s, void *data) +{ + uiSpinboxSetValue(spinbox, uiSliderValue(s)); + uiProgressBarSetValue(pbar, uiSliderValue(s)); +} + +static uiControl *makeNumbersPage() +{ + uiBox *hbox; + uiGroup *group; + uiBox *vbox; + uiCombobox *cbox; + uiEditableCombobox *ecbox; + uiRadioButtons *rb; + + hbox = uiNewHorizontalBox(); + uiBoxSetPadded(hbox, 1); + + group = uiNewGroup("Numbers"); + uiGroupSetMargined(group, 1); + uiBoxAppend(hbox, uiControl(group), 1); + + vbox = uiNewVerticalBox(); + uiBoxSetPadded(vbox, 1); + uiGroupSetChild(group, uiControl(vbox)); + + spinbox = uiNewSpinbox(0, 100); + slider = uiNewSlider(0, 100); + pbar = uiNewProgressBar(); + uiSpinboxOnChanged(spinbox, onSpinboxChanged, NULL); + uiSliderOnChanged(slider, onSliderChanged, NULL); + uiBoxAppend(vbox, uiControl(spinbox), 0); + uiBoxAppend(vbox, uiControl(slider), 0); + uiBoxAppend(vbox, uiControl(pbar), 0); + + group = uiNewGroup("Lists"); + uiGroupSetMargined(group, 1); + uiBoxAppend(hbox, uiControl(group), 1); + + vbox = uiNewVerticalBox(); + uiBoxSetPadded(vbox, 1); + uiGroupSetChild(group, uiControl(vbox)); + + cbox = uiNewCombobox(); + uiComboboxAppend(cbox, "Combobox Item 1"); + uiComboboxAppend(cbox, "Combobox Item 2"); + uiComboboxAppend(cbox, "Combobox Item 3"); + uiBoxAppend(vbox, uiControl(cbox), 0); + + ecbox = uiNewEditableCombobox(); + uiEditableComboboxAppend(ecbox, "Editable Item 1"); + uiEditableComboboxAppend(ecbox, "Editable Item 2"); + uiEditableComboboxAppend(ecbox, "Editable Item 3"); + uiBoxAppend(vbox, uiControl(ecbox), 0); + + rb = uiNewRadioButtons(); + uiRadioButtonsAppend(rb, "Radio Button 1"); + uiRadioButtonsAppend(rb, "Radio Button 2"); + uiRadioButtonsAppend(rb, "Radio Button 3"); + uiBoxAppend(vbox, uiControl(rb), 0); + + return uiControl(hbox); +} + +// TODO make this not global +static uiWindow *mainwin; + +static void onOpenFileClicked(uiButton *b, void *data) +{ + uiEntry *entry = uiEntry(data); + char *filename; + + filename = uiOpenFile(mainwin); + if (filename == NULL) { + uiEntrySetText(entry, "(cancelled)"); + return; + } + uiEntrySetText(entry, filename); + uiFreeText(filename); +} + +static void onSaveFileClicked(uiButton *b, void *data) +{ + uiEntry *entry = uiEntry(data); + char *filename; + + filename = uiSaveFile(mainwin); + if (filename == NULL) { + uiEntrySetText(entry, "(cancelled)"); + return; + } + uiEntrySetText(entry, filename); + uiFreeText(filename); +} + +static uiControl *makeDataChoosersPage(void) +{ + uiBox *hbox; + uiBox *vbox; + uiGrid *grid; + uiButton *button; + uiEntry *entry; + + hbox = uiNewHorizontalBox(); + uiBoxSetPadded(hbox, 1); + + vbox = uiNewVerticalBox(); + uiBoxSetPadded(vbox, 1); + uiBoxAppend(hbox, uiControl(vbox), 0); + + uiBoxAppend(vbox, + uiControl(uiNewDatePicker()), + 0); + uiBoxAppend(vbox, + uiControl(uiNewTimePicker()), + 0); + uiBoxAppend(vbox, + uiControl(uiNewDateTimePicker()), + 0); + + uiBoxAppend(vbox, + uiControl(uiNewFontButton()), + 0); + uiBoxAppend(vbox, + uiControl(uiNewColorButton()), + 0); + + vbox = uiNewVerticalBox(); + uiBoxSetPadded(vbox, 1); + uiBoxAppend(hbox, uiControl(vbox), 1); + + grid = uiNewGrid(); + uiGridSetPadded(grid, 1); + uiBoxAppend(vbox, uiControl(grid), 0); + + button = uiNewButton("Open File"); + entry = uiNewEntry(); + uiEntrySetReadOnly(entry, 1); + uiButtonOnClicked(button, onOpenFileClicked, entry); + uiGridAppend(grid, uiControl(button), + 0, 0, 1, 1, + 0, uiAlignFill, 0, uiAlignFill); + uiGridAppend(grid, uiControl(entry), + 1, 0, 1, 1, + 1, uiAlignFill, 0, uiAlignFill); + + button = uiNewButton("Save File"); + entry = uiNewEntry(); + uiEntrySetReadOnly(entry, 1); + uiButtonOnClicked(button, onSaveFileClicked, entry); + uiGridAppend(grid, uiControl(button), + 0, 1, 1, 1, + 0, uiAlignFill, 0, uiAlignFill); + uiGridAppend(grid, uiControl(entry), + 1, 1, 1, 1, + 1, uiAlignFill, 0, uiAlignFill); + + return uiControl(hbox); +} + int main(void) { uiInitOptions options; const char *err; - uiWindow *mainwin; uiTab *tab; memset(&options, 0, sizeof (uiInitOptions)); @@ -104,6 +274,12 @@ int main(void) uiTabAppend(tab, "Basic Controls", makeBasicControlsPage()); uiTabSetMargined(tab, 0, 1); + uiTabAppend(tab, "Numbers and Lists", makeNumbersPage()); + uiTabSetMargined(tab, 1, 1); + + uiTabAppend(tab, "Data Choosers", makeDataChoosersPage()); + uiTabSetMargined(tab, 2, 1); + uiControlShow(uiControl(mainwin)); uiMain(); return 0; From ba8e5b80eab01902443b6190b840389df28bbc68 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 13 Jun 2016 08:12:01 -0400 Subject: [PATCH 0306/1329] More uiGrid tests. OS X falls apart as usual :D --- test/page14.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/test/page14.c b/test/page14.c index 0e9f1f41..e30833db 100644 --- a/test/page14.c +++ b/test/page14.c @@ -180,6 +180,98 @@ static uiControl *spanningGrid(void) return uiControl(g); } +static uiControl *assorted(void) +{ + uiGrid *outergrid; + uiGrid *innergrid; + uiButton *b, *b2; + + outergrid = newGrid(); + + innergrid = newGrid(); + b2 = uiNewButton("Test"); + uiGridAppend(innergrid, uiControl(b2), + 1, 1, 1, 1, + 0, uiAlignFill, 0, uiAlignFill); + b = uiNewButton("Hide One"); + uiGridAppend(innergrid, uiControl(b), + 0, 1, 1, 1, + 0, uiAlignFill, 0, uiAlignFill); + b = uiNewButton("Show One"); + uiGridAppend(innergrid, uiControl(b), + 2, 1, 1, 1, + 0, uiAlignFill, 0, uiAlignFill); + b = uiNewButton("Hide All"); + uiGridAppend(innergrid, uiControl(b), + 1, 0, 1, 1, + 0, uiAlignFill, 0, uiAlignFill); + b = uiNewButton("Show All"); + uiGridAppend(innergrid, uiControl(b), + 1, 2, 1, 1, + 0, uiAlignFill, 0, uiAlignFill); + uiGridAppend(outergrid, uiControl(innergrid), + 0, 0, 1, 1, + 1, uiAlignFill, 1, uiAlignFill); + + innergrid = newGrid(); + b = uiNewButton("Insert Trailing"); + uiGridAppend(innergrid, uiControl(b), + 0, 0, 1, 1, + 1, uiAlignFill, 0, uiAlignFill); + b = uiNewButton("Insert Bottom"); + uiGridAppend(innergrid, uiControl(b), + 1, 0, 1, 1, + 1, uiAlignFill, 0, uiAlignFill); + b = uiNewButton("Insert Leading"); + uiGridAppend(innergrid, uiControl(b), + 1, 1, 1, 1, + 1, uiAlignFill, 0, uiAlignFill); + b = uiNewButton("Insert Top"); + uiGridAppend(innergrid, uiControl(b), + 0, 1, 1, 1, + 1, uiAlignFill, 0, uiAlignFill); + uiGridAppend(outergrid, uiControl(innergrid), + 1, 0, 1, 1, + 1, uiAlignFill, 1, uiAlignFill); + + innergrid = newGrid(); + uiGridAppend(innergrid, uiControl(uiNewColorButton()), + 0, 0, 1, 1, + 1, uiAlignFill, 0, uiAlignFill); + uiGridAppend(innergrid, uiControl(uiNewColorButton()), + 0, 1, 1, 1, + 1, uiAlignStart, 0, uiAlignFill); + uiGridAppend(innergrid, uiControl(uiNewColorButton()), + 0, 2, 1, 1, + 1, uiAlignCenter, 0, uiAlignFill); + uiGridAppend(innergrid, uiControl(uiNewColorButton()), + 0, 3, 1, 1, + 1, uiAlignEnd, 0, uiAlignFill); + uiGridAppend(outergrid, uiControl(innergrid), + 0, 1, 1, 1, + 1, uiAlignFill, 1, uiAlignFill); + + // TODO with only this, wrong size on OS X — expand sizing thing? + innergrid = newGrid(); + uiGridAppend(innergrid, uiControl(uiNewColorButton()), + 0, 0, 1, 1, + 0, uiAlignFill, 1, uiAlignFill); + uiGridAppend(innergrid, uiControl(uiNewColorButton()), + 1, 0, 1, 1, + 0, uiAlignFill, 1, uiAlignStart); + uiGridAppend(innergrid, uiControl(uiNewColorButton()), + 2, 0, 1, 1, + 0, uiAlignFill, 1, uiAlignCenter); + uiGridAppend(innergrid, uiControl(uiNewColorButton()), + 3, 0, 1, 1, + 0, uiAlignFill, 1, uiAlignEnd); + uiGridAppend(outergrid, uiControl(innergrid), + 1, 1, 1, 1, + 1, uiAlignFill, 1, uiAlignFill); + + return uiControl(outergrid); +} + static const struct { const char *name; uiControl *(*f)(void); @@ -190,6 +282,8 @@ static const struct { { "Empty Line", emptyLine }, { "Empty Grid", emptyGrid }, { "Spanning Grid", spanningGrid }, + // my own + { "Assorted", assorted }, { NULL, NULL }, }; From e46554f481d89dafb2c143db4ace0eaeaf61b74f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 13 Jun 2016 13:57:20 -0400 Subject: [PATCH 0307/1329] More work on the uiGrid test. --- test/page14.c | 61 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 54 insertions(+), 7 deletions(-) diff --git a/test/page14.c b/test/page14.c index e30833db..880534ce 100644 --- a/test/page14.c +++ b/test/page14.c @@ -180,32 +180,75 @@ static uiControl *spanningGrid(void) return uiControl(g); } +// TODO make non-global +static uiButton *hideOne, *one, *showOne; + +static void onHideOne(uiButton *b, void *data) +{ + uiControlHide(uiControl(one)); +} + +static void onShowOne(uiButton *b, void *data) +{ + uiControlShow(uiControl(one)); +} + +static void onHideAll(uiButton *b, void *data) +{ + uiControlHide(uiControl(hideOne)); + uiControlHide(uiControl(one)); + uiControlHide(uiControl(showOne)); +} + +static void onShowAll(uiButton *b, void *data) +{ + uiControlShow(uiControl(hideOne)); + uiControlShow(uiControl(one)); + uiControlShow(uiControl(showOne)); +} + +#define AT(x) static void onInsert ## x(uiButton *b, void *data) \ + { \ + uiGrid *g = uiGrid(data); \ + uiGridInsertAt(g, uiControl(uiNewButton("Button")), \ + uiControl(b), uiAt ## x, 1, 1, \ + 0, uiAlignFill, 0, uiAlignFill); \ + } +AT(Leading) +AT(Top) +AT(Trailing) +AT(Bottom) + static uiControl *assorted(void) { uiGrid *outergrid; uiGrid *innergrid; - uiButton *b, *b2; + uiButton *b; outergrid = newGrid(); innergrid = newGrid(); - b2 = uiNewButton("Test"); - uiGridAppend(innergrid, uiControl(b2), + one = uiNewButton("Test"); + uiGridAppend(innergrid, uiControl(one), 1, 1, 1, 1, 0, uiAlignFill, 0, uiAlignFill); - b = uiNewButton("Hide One"); - uiGridAppend(innergrid, uiControl(b), + hideOne = uiNewButton("Hide One"); + uiButtonOnClicked(hideOne, onHideOne, NULL); + uiGridAppend(innergrid, uiControl(hideOne), 0, 1, 1, 1, 0, uiAlignFill, 0, uiAlignFill); - b = uiNewButton("Show One"); - uiGridAppend(innergrid, uiControl(b), + showOne = uiNewButton("Show One"); + uiButtonOnClicked(showOne, onShowOne, NULL); + uiGridAppend(innergrid, uiControl(showOne), 2, 1, 1, 1, 0, uiAlignFill, 0, uiAlignFill); b = uiNewButton("Hide All"); + uiButtonOnClicked(b, onHideAll, NULL); uiGridAppend(innergrid, uiControl(b), 1, 0, 1, 1, 0, uiAlignFill, 0, uiAlignFill); b = uiNewButton("Show All"); + uiButtonOnClicked(b, onShowAll, NULL); uiGridAppend(innergrid, uiControl(b), 1, 2, 1, 1, 0, uiAlignFill, 0, uiAlignFill); @@ -215,18 +258,22 @@ static uiControl *assorted(void) innergrid = newGrid(); b = uiNewButton("Insert Trailing"); + uiButtonOnClicked(b, onInsertTrailing, innergrid); uiGridAppend(innergrid, uiControl(b), 0, 0, 1, 1, 1, uiAlignFill, 0, uiAlignFill); b = uiNewButton("Insert Bottom"); + uiButtonOnClicked(b, onInsertBottom, innergrid); uiGridAppend(innergrid, uiControl(b), 1, 0, 1, 1, 1, uiAlignFill, 0, uiAlignFill); b = uiNewButton("Insert Leading"); + uiButtonOnClicked(b, onInsertLeading, innergrid); uiGridAppend(innergrid, uiControl(b), 1, 1, 1, 1, 1, uiAlignFill, 0, uiAlignFill); b = uiNewButton("Insert Top"); + uiButtonOnClicked(b, onInsertTop, innergrid); uiGridAppend(innergrid, uiControl(b), 0, 1, 1, 1, 1, uiAlignFill, 0, uiAlignFill); From 857bbbf5062f86ef7c7ac175a1505b39088097e7 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 13 Jun 2016 15:15:40 -0400 Subject: [PATCH 0308/1329] Don't use 10.10-specific selectors in uiNewSearchEntry(). Fixes #132. --- darwin/entry.m | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/darwin/entry.m b/darwin/entry.m index 382e66a1..219d0805 100644 --- a/darwin/entry.m +++ b/darwin/entry.m @@ -241,8 +241,9 @@ uiEntry *uiNewSearchEntry(void) e = finishNewEntry([libui_intrinsicWidthNSSearchField class]); s = (NSSearchField *) (e->textfield); - [s setSendsSearchStringImmediately:NO]; - [s setSendsWholeSearchString:NO]; + // TODO these are only on 10.10 +// [s setSendsSearchStringImmediately:NO]; +// [s setSendsWholeSearchString:NO]; [s setBordered:NO]; [s setBezelStyle:NSTextFieldRoundedBezel]; [s setBezeled:YES]; From b26354d1e7b49cf051b8e707b0477bddce443f77 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 13 Jun 2016 20:46:11 -0400 Subject: [PATCH 0309/1329] Started replacement of intmax_t and uintmax_t with int. --- darwin/box.m | 10 +++++----- ui.h | 2 +- unix/box.c | 2 +- windows/box.cpp | 27 +++++++++++++-------------- 4 files changed, 20 insertions(+), 21 deletions(-) diff --git a/darwin/box.m b/darwin/box.m index 8661d902..4b688e65 100644 --- a/darwin/box.m +++ b/darwin/box.m @@ -14,7 +14,7 @@ NSMutableArray *children; BOOL vertical; int padded; - uintmax_t nStretchy; + int nStretchy; NSLayoutConstraint *first; NSMutableArray *inBetweens; @@ -36,7 +36,7 @@ - (CGFloat)paddingAmount; - (void)establishOurConstraints; - (void)append:(uiControl *)c stretchy:(int)stretchy; -- (void)delete:(uintmax_t)n; +- (void)delete:(int)n; - (int)isPadded; - (void)setPadded:(int)p; - (BOOL)hugsTrailing; @@ -249,7 +249,7 @@ struct uiBox { { boxChild *bc; NSLayoutPriority priority; - uintmax_t oldnStretchy; + int oldnStretchy; bc = [boxChild new]; bc.c = c; @@ -285,7 +285,7 @@ struct uiBox { [bc release]; // we don't need the initial reference now } -- (void)delete:(uintmax_t)n +- (void)delete:(int)n { boxChild *bc; int stretchy; @@ -405,7 +405,7 @@ void uiBoxAppend(uiBox *b, uiControl *c, int stretchy) [b->view append:c stretchy:stretchy]; } -void uiBoxDelete(uiBox *b, uintmax_t n) +void uiBoxDelete(uiBox *b, int n) { [b->view delete:n]; } diff --git a/ui.h b/ui.h index 20d23eee..ee12efd4 100644 --- a/ui.h +++ b/ui.h @@ -113,7 +113,7 @@ _UI_EXTERN uiButton *uiNewButton(const char *text); typedef struct uiBox uiBox; #define uiBox(this) ((uiBox *) (this)) _UI_EXTERN void uiBoxAppend(uiBox *b, uiControl *child, int stretchy); -_UI_EXTERN void uiBoxDelete(uiBox *b, uintmax_t index); +_UI_EXTERN void uiBoxDelete(uiBox *b, int index); _UI_EXTERN int uiBoxPadded(uiBox *b); _UI_EXTERN void uiBoxSetPadded(uiBox *b, int padded); _UI_EXTERN uiBox *uiNewHorizontalBox(void); diff --git a/unix/box.c b/unix/box.c index 47efe278..23fb7f7c 100644 --- a/unix/box.c +++ b/unix/box.c @@ -88,7 +88,7 @@ void uiBoxAppend(uiBox *b, uiControl *c, int stretchy) g_array_append_val(b->controls, bc); } -void uiBoxDelete(uiBox *b, uintmax_t index) +void uiBoxDelete(uiBox *b, int index) { struct boxChild *bc; GtkWidget *widget; diff --git a/windows/box.cpp b/windows/box.cpp index 01413ebe..d87f73e2 100644 --- a/windows/box.cpp +++ b/windows/box.cpp @@ -4,8 +4,8 @@ struct boxChild { uiControl *c; int stretchy; - intmax_t width; - intmax_t height; + int width; + int height; }; struct uiBox { @@ -31,12 +31,12 @@ static void boxPadding(uiBox *b, int *xpadding, int *ypadding) static void boxRelayout(uiBox *b) { RECT r; - intmax_t x, y, width, height; + int x, y, width, height; int xpadding, ypadding; - uintmax_t nStretchy; - intmax_t stretchywid, stretchyht; - uintmax_t i; - intmax_t minimumWidth, minimumHeight; + int nStretchy; + int stretchywid, stretchyht; + int i; + int minimumWidth, minimumHeight; uiWindowsSizing *d; if (b->controls->size() == 0) @@ -149,16 +149,16 @@ static void uiBoxSyncEnableState(uiWindowsControl *c, int enabled) uiWindowsControlDefaultSetParentHWND(uiBox) -static void uiBoxMinimumSize(uiWindowsControl *c, intmax_t *width, intmax_t *height) +static void uiBoxMinimumSize(uiWindowsControl *c, int *width, int *height) { uiBox *b = uiBox(c); int xpadding, ypadding; - uintmax_t nStretchy; + int nStretchy; // these two contain the largest minimum width and height of all stretchy controls in the box // all stretchy controls will use this value to determine the final minimum size - intmax_t maxStretchyWidth, maxStretchyHeight; - uintmax_t i; - intmax_t minimumWidth, minimumHeight; + int maxStretchyWidth, maxStretchyHeight; + int i; + int minimumWidth, minimumHeight; uiWindowsSizing sizing; *width = 0; @@ -230,7 +230,6 @@ static void boxArrangeChildren(uiBox *b) { LONG_PTR controlID; HWND insertAfter; - uintmax_t i; controlID = 100; insertAfter = NULL; @@ -251,7 +250,7 @@ void uiBoxAppend(uiBox *b, uiControl *c, int stretchy) uiWindowsControlMinimumSizeChanged(uiWindowsControl(b)); } -void uiBoxDelete(uiBox *b, uintmax_t index) +void uiBoxDelete(uiBox *b, int index) { uiControl *c; From 8d48d4220188532414b96a39b908eabd8b5cd930 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 13 Jun 2016 20:55:50 -0400 Subject: [PATCH 0310/1329] More intmax_t elminiation. --- darwin/slider.m | 9 ++++----- darwin/spinbox.m | 8 ++++---- darwin/tab.m | 10 +++++----- ui.h | 24 +++++++++++++----------- unix/slider.c | 10 +++++----- unix/spinbox.c | 10 +++++----- unix/tab.c | 10 +++++----- windows/slider.cpp | 12 ++++++------ windows/spinbox.cpp | 16 ++++++++-------- windows/tab.cpp | 16 ++++++++-------- 10 files changed, 63 insertions(+), 62 deletions(-) diff --git a/darwin/slider.m b/darwin/slider.m index 53274574..f00da50f 100644 --- a/darwin/slider.m +++ b/darwin/slider.m @@ -88,13 +88,12 @@ static void uiSliderDestroy(uiControl *c) uiFreeControl(uiControl(s)); } -intmax_t uiSliderValue(uiSlider *s) +int uiSliderValue(uiSlider *s) { - // NSInteger is the most similar to intmax_t return [s->slider integerValue]; } -void uiSliderSetValue(uiSlider *s, intmax_t value) +void uiSliderSetValue(uiSlider *s, int value) { [s->slider setIntegerValue:value]; } @@ -110,11 +109,11 @@ static void defaultOnChanged(uiSlider *s, void *data) // do nothing } -uiSlider *uiNewSlider(intmax_t min, intmax_t max) +uiSlider *uiNewSlider(int min, int max) { uiSlider *s; NSSliderCell *cell; - intmax_t temp; + int temp; if (min >= max) { temp = min; diff --git a/darwin/spinbox.m b/darwin/spinbox.m index 30fbc00b..73474d04 100644 --- a/darwin/spinbox.m +++ b/darwin/spinbox.m @@ -169,12 +169,12 @@ static CGFloat stepperYDelta(void) uiDarwinControlAllDefaults(uiSpinbox, spinbox) -intmax_t uiSpinboxValue(uiSpinbox *s) +int uiSpinboxValue(uiSpinbox *s) { return [s->spinbox libui_value]; } -void uiSpinboxSetValue(uiSpinbox *s, intmax_t value) +void uiSpinboxSetValue(uiSpinbox *s, int value) { [s->spinbox libui_setValue:value]; } @@ -190,10 +190,10 @@ static void defaultOnChanged(uiSpinbox *s, void *data) // do nothing } -uiSpinbox *uiNewSpinbox(intmax_t min, intmax_t max) +uiSpinbox *uiNewSpinbox(int min, int max) { uiSpinbox *s; - intmax_t temp; + int temp; if (min >= max) { temp = min; diff --git a/darwin/tab.m b/darwin/tab.m index c3fac1d1..0d03f3e6 100644 --- a/darwin/tab.m +++ b/darwin/tab.m @@ -185,7 +185,7 @@ void uiTabAppend(uiTab *t, const char *name, uiControl *child) uiTabInsertAt(t, name, [t->pages count], child); } -void uiTabInsertAt(uiTab *t, const char *name, uintmax_t n, uiControl *child) +void uiTabInsertAt(uiTab *t, const char *name, int n, uiControl *child) { tabPage *page; NSView *view; @@ -220,7 +220,7 @@ void uiTabInsertAt(uiTab *t, const char *name, uintmax_t n, uiControl *child) tabRelayout(t); } -void uiTabDelete(uiTab *t, uintmax_t n) +void uiTabDelete(uiTab *t, int n) { tabPage *page; uiControl *child; @@ -244,12 +244,12 @@ void uiTabDelete(uiTab *t, uintmax_t n) tabRelayout(t); } -uintmax_t uiTabNumPages(uiTab *t) +int uiTabNumPages(uiTab *t) { return [t->pages count]; } -int uiTabMargined(uiTab *t, uintmax_t n) +int uiTabMargined(uiTab *t, int n) { tabPage *page; @@ -257,7 +257,7 @@ int uiTabMargined(uiTab *t, uintmax_t n) return [page isMargined]; } -void uiTabSetMargined(uiTab *t, uintmax_t n, int margined) +void uiTabSetMargined(uiTab *t, int n, int margined) { tabPage *page; diff --git a/ui.h b/ui.h index ee12efd4..50a76ed9 100644 --- a/ui.h +++ b/ui.h @@ -32,6 +32,8 @@ extern "C" { // This comes from Go's math.Pi, which in turn comes from http://oeis.org/A000796. #define uiPi 3.14159265358979323846264338327950288419716939937510582097494459 +// TODO uiBool? + typedef struct uiInitOptions uiInitOptions; struct uiInitOptions { @@ -148,11 +150,11 @@ _UI_EXTERN uiLabel *uiNewLabel(const char *text); typedef struct uiTab uiTab; #define uiTab(this) ((uiTab *) (this)) _UI_EXTERN void uiTabAppend(uiTab *t, const char *name, uiControl *c); -_UI_EXTERN void uiTabInsertAt(uiTab *t, const char *name, uintmax_t before, uiControl *c); -_UI_EXTERN void uiTabDelete(uiTab *t, uintmax_t index); -_UI_EXTERN uintmax_t uiTabNumPages(uiTab *t); -_UI_EXTERN int uiTabMargined(uiTab *t, uintmax_t page); -_UI_EXTERN void uiTabSetMargined(uiTab *t, uintmax_t page, int margined); +_UI_EXTERN void uiTabInsertAt(uiTab *t, const char *name, int before, uiControl *c); +_UI_EXTERN void uiTabDelete(uiTab *t, int index); +_UI_EXTERN int uiTabNumPages(uiTab *t); +_UI_EXTERN int uiTabMargined(uiTab *t, int page); +_UI_EXTERN void uiTabSetMargined(uiTab *t, int page, int margined); _UI_EXTERN uiTab *uiNewTab(void); typedef struct uiGroup uiGroup; @@ -171,17 +173,17 @@ _UI_EXTERN uiGroup *uiNewGroup(const char *title); typedef struct uiSpinbox uiSpinbox; #define uiSpinbox(this) ((uiSpinbox *) (this)) -_UI_EXTERN intmax_t uiSpinboxValue(uiSpinbox *s); -_UI_EXTERN void uiSpinboxSetValue(uiSpinbox *s, intmax_t value); +_UI_EXTERN int uiSpinboxValue(uiSpinbox *s); +_UI_EXTERN void uiSpinboxSetValue(uiSpinbox *s, int value); _UI_EXTERN void uiSpinboxOnChanged(uiSpinbox *s, void (*f)(uiSpinbox *s, void *data), void *data); -_UI_EXTERN uiSpinbox *uiNewSpinbox(intmax_t min, intmax_t max); +_UI_EXTERN uiSpinbox *uiNewSpinbox(int min, int max); typedef struct uiSlider uiSlider; #define uiSlider(this) ((uiSlider *) (this)) -_UI_EXTERN intmax_t uiSliderValue(uiSlider *s); -_UI_EXTERN void uiSliderSetValue(uiSlider *s, intmax_t value); +_UI_EXTERN int uiSliderValue(uiSlider *s); +_UI_EXTERN void uiSliderSetValue(uiSlider *s, int value); _UI_EXTERN void uiSliderOnChanged(uiSlider *s, void (*f)(uiSlider *s, void *data), void *data); -_UI_EXTERN uiSlider *uiNewSlider(intmax_t min, intmax_t max); +_UI_EXTERN uiSlider *uiNewSlider(int min, int max); typedef struct uiProgressBar uiProgressBar; #define uiProgressBar(this) ((uiProgressBar *) (this)) diff --git a/unix/slider.c b/unix/slider.c index 9ede700f..7f0cc24a 100644 --- a/unix/slider.c +++ b/unix/slider.c @@ -25,12 +25,12 @@ static void defaultOnChanged(uiSlider *s, void *data) // do nothing } -intmax_t uiSliderValue(uiSlider *s) +int uiSliderValue(uiSlider *s) { - return (intmax_t) gtk_range_get_value(s->range); + return gtk_range_get_value(s->range); } -void uiSliderSetValue(uiSlider *s, intmax_t value) +void uiSliderSetValue(uiSlider *s, int value) { // we need to inhibit sending of ::value-changed because this WILL send a ::value-changed otherwise g_signal_handler_block(s->range, s->onChangedSignal); @@ -44,10 +44,10 @@ void uiSliderOnChanged(uiSlider *s, void (*f)(uiSlider *, void *), void *data) s->onChangedData = data; } -uiSlider *uiNewSlider(intmax_t min, intmax_t max) +uiSlider *uiNewSlider(int min, int max) { uiSlider *s; - intmax_t temp; + int temp; if (min >= max) { temp = min; diff --git a/unix/spinbox.c b/unix/spinbox.c index d819c21f..90a5d3c1 100644 --- a/unix/spinbox.c +++ b/unix/spinbox.c @@ -25,12 +25,12 @@ static void defaultOnChanged(uiSpinbox *s, void *data) // do nothing } -intmax_t uiSpinboxValue(uiSpinbox *s) +int uiSpinboxValue(uiSpinbox *s) { - return (intmax_t) gtk_spin_button_get_value(s->spinButton); + return gtk_spin_button_get_value(s->spinButton); } -void uiSpinboxSetValue(uiSpinbox *s, intmax_t value) +void uiSpinboxSetValue(uiSpinbox *s, int value) { // we need to inhibit sending of ::value-changed because this WILL send a ::value-changed otherwise g_signal_handler_block(s->spinButton, s->onChangedSignal); @@ -45,10 +45,10 @@ void uiSpinboxOnChanged(uiSpinbox *s, void (*f)(uiSpinbox *, void *), void *data s->onChangedData = data; } -uiSpinbox *uiNewSpinbox(intmax_t min, intmax_t max) +uiSpinbox *uiNewSpinbox(int min, int max) { uiSpinbox *s; - intmax_t temp; + int temp; if (min >= max) { temp = min; diff --git a/unix/tab.c b/unix/tab.c index 1ab7f6ad..552e0e31 100644 --- a/unix/tab.c +++ b/unix/tab.c @@ -34,7 +34,7 @@ void uiTabAppend(uiTab *t, const char *name, uiControl *child) uiTabInsertAt(t, name, t->pages->len, child); } -void uiTabInsertAt(uiTab *t, const char *name, uintmax_t n, uiControl *child) +void uiTabInsertAt(uiTab *t, const char *name, int n, uiControl *child) { struct child *page; @@ -47,7 +47,7 @@ void uiTabInsertAt(uiTab *t, const char *name, uintmax_t n, uiControl *child) g_array_insert_val(t->pages, n, page); } -void uiTabDelete(uiTab *t, uintmax_t n) +void uiTabDelete(uiTab *t, int n) { struct child *page; @@ -57,12 +57,12 @@ void uiTabDelete(uiTab *t, uintmax_t n) g_array_remove_index(t->pages, n); } -uintmax_t uiTabNumPages(uiTab *t) +int uiTabNumPages(uiTab *t) { return t->pages->len; } -int uiTabMargined(uiTab *t, uintmax_t n) +int uiTabMargined(uiTab *t, int n) { struct child *page; @@ -70,7 +70,7 @@ int uiTabMargined(uiTab *t, uintmax_t n) return childFlag(page); } -void uiTabSetMargined(uiTab *t, uintmax_t n, int margined) +void uiTabSetMargined(uiTab *t, int n, int margined) { struct child *page; diff --git a/windows/slider.cpp b/windows/slider.cpp index 0783627c..5c671dda 100644 --- a/windows/slider.cpp +++ b/windows/slider.cpp @@ -32,7 +32,7 @@ uiWindowsControlAllDefaultsExceptDestroy(uiSlider); #define sliderWidth 107 /* this is actually the shorter progress bar width, but Microsoft doesn't indicate a width */ #define sliderHeight 15 -static void uiSliderMinimumSize(uiWindowsControl *c, intmax_t *width, intmax_t *height) +static void uiSliderMinimumSize(uiWindowsControl *c, int *width, int *height) { uiSlider *s = uiSlider(c); uiWindowsSizing sizing; @@ -51,12 +51,12 @@ static void defaultOnChanged(uiSlider *s, void *data) // do nothing } -intmax_t uiSliderValue(uiSlider *s) +int uiSliderValue(uiSlider *s) { - return (intmax_t) SendMessageW(s->hwnd, TBM_GETPOS, 0, 0); + return SendMessageW(s->hwnd, TBM_GETPOS, 0, 0); } -void uiSliderSetValue(uiSlider *s, intmax_t value) +void uiSliderSetValue(uiSlider *s, int value) { // don't use TBM_SETPOSNOTIFY; that triggers an event SendMessageW(s->hwnd, TBM_SETPOS, (WPARAM) TRUE, (LPARAM) value); @@ -68,10 +68,10 @@ void uiSliderOnChanged(uiSlider *s, void (*f)(uiSlider *, void *), void *data) s->onChangedData = data; } -uiSlider *uiNewSlider(intmax_t min, intmax_t max) +uiSlider *uiNewSlider(int min, int max) { uiSlider *s; - intmax_t temp; + int temp; if (min >= max) { temp = min; diff --git a/windows/spinbox.cpp b/windows/spinbox.cpp index 1857975d..2b6af66d 100644 --- a/windows/spinbox.cpp +++ b/windows/spinbox.cpp @@ -13,7 +13,7 @@ struct uiSpinbox { // utility functions -static intmax_t value(uiSpinbox *s) +static int value(uiSpinbox *s) { BOOL neededCap = FALSE; LRESULT val; @@ -27,7 +27,7 @@ static intmax_t value(uiSpinbox *s) SendMessageW(s->updown, UDM_SETPOS32, 0, (LPARAM) val); s->inhibitChanged = FALSE; } - return (intmax_t) val; + return val; } // control implementation @@ -76,7 +76,7 @@ uiWindowsControlAllDefaultsExceptDestroy(uiSpinbox) #define entryWidth 107 /* this is actually the shorter progress bar width, but Microsoft only indicates as wide as necessary */ #define entryHeight 14 -static void uiSpinboxMinimumSize(uiWindowsControl *c, intmax_t *width, intmax_t *height) +static void uiSpinboxMinimumSize(uiWindowsControl *c, int *width, int *height) { uiSpinbox *s = uiSpinbox(c); uiWindowsSizing sizing; @@ -108,7 +108,7 @@ static void spinboxArrangeChildren(uiSpinbox *s) static void recreateUpDown(uiSpinbox *s) { BOOL preserve = FALSE; - intmax_t current; + int current; // Microsoft's commctrl.h says to use this type INT min, max; @@ -156,12 +156,12 @@ static void defaultOnChanged(uiSpinbox *s, void *data) // do nothing } -intmax_t uiSpinboxValue(uiSpinbox *s) +int uiSpinboxValue(uiSpinbox *s) { return value(s); } -void uiSpinboxSetValue(uiSpinbox *s, intmax_t value) +void uiSpinboxSetValue(uiSpinbox *s, int value) { s->inhibitChanged = TRUE; SendMessageW(s->updown, UDM_SETPOS32, 0, (LPARAM) value); @@ -179,10 +179,10 @@ static void onResize(uiWindowsControl *c) spinboxRelayout(uiSpinbox(c)); } -uiSpinbox *uiNewSpinbox(intmax_t min, intmax_t max) +uiSpinbox *uiNewSpinbox(int min, int max) { uiSpinbox *s; - intmax_t temp; + int temp; if (min >= max) { temp = min; diff --git a/windows/tab.cpp b/windows/tab.cpp index 2dade325..2af2bff3 100644 --- a/windows/tab.cpp +++ b/windows/tab.cpp @@ -19,7 +19,7 @@ static LRESULT curpage(uiTab *t) return SendMessageW(t->tabHWND, TCM_GETCURSEL, 0, 0); } -static struct tabPage *tabPage(uiTab *t, intmax_t i) +static struct tabPage *tabPage(uiTab *t, int i) { return (*(t->pages))[i]; } @@ -127,10 +127,10 @@ static void uiTabSyncEnableState(uiWindowsControl *c, int enabled) uiWindowsControlDefaultSetParentHWND(uiTab) -static void uiTabMinimumSize(uiWindowsControl *c, intmax_t *width, intmax_t *height) +static void uiTabMinimumSize(uiWindowsControl *c, int *width, int *height) { uiTab *t = uiTab(c); - intmax_t pagewid, pageht; + int pagewid, pageht; struct tabPage *page; RECT r; @@ -182,7 +182,7 @@ void uiTabAppend(uiTab *t, const char *name, uiControl *child) uiTabInsertAt(t, name, t->pages->size(), child); } -void uiTabInsertAt(uiTab *t, const char *name, uintmax_t n, uiControl *child) +void uiTabInsertAt(uiTab *t, const char *name, int n, uiControl *child) { struct tabPage *page; LRESULT hide, show; @@ -216,7 +216,7 @@ void uiTabInsertAt(uiTab *t, const char *name, uintmax_t n, uiControl *child) } } -void uiTabDelete(uiTab *t, uintmax_t n) +void uiTabDelete(uiTab *t, int n) { struct tabPage *page; @@ -233,17 +233,17 @@ void uiTabDelete(uiTab *t, uintmax_t n) t->pages->erase(t->pages->begin() + n); } -uintmax_t uiTabNumPages(uiTab *t) +int uiTabNumPages(uiTab *t) { return t->pages->size(); } -int uiTabMargined(uiTab *t, uintmax_t n) +int uiTabMargined(uiTab *t, int n) { return tabPage(t, n)->margined; } -void uiTabSetMargined(uiTab *t, uintmax_t n, int margined) +void uiTabSetMargined(uiTab *t, int n, int margined) { struct tabPage *page; From 440635447d7a43c3c87d766a2438e1230f8df166 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 13 Jun 2016 21:05:40 -0400 Subject: [PATCH 0311/1329] More intmax_t removal. --- darwin/area.m | 4 ++-- darwin/combobox.m | 4 ++-- darwin/radiobuttons.m | 6 +++--- ui.h | 12 ++++++------ unix/area.c | 8 ++++---- unix/combobox.c | 4 ++-- unix/radiobuttons.c | 4 ++-- windows/area.cpp | 6 +++--- windows/area.hpp | 4 ++-- windows/combobox.cpp | 8 ++++---- windows/radiobuttons.cpp | 13 ++++++------- 11 files changed, 36 insertions(+), 37 deletions(-) diff --git a/darwin/area.m b/darwin/area.m index 656a1488..40d719a5 100644 --- a/darwin/area.m +++ b/darwin/area.m @@ -381,7 +381,7 @@ int sendAreaEvents(NSEvent *e) return 0; } -void uiAreaSetSize(uiArea *a, intmax_t width, intmax_t height) +void uiAreaSetSize(uiArea *a, int width, int height) { if (!a->scrolling) userbug("You cannot call uiAreaSetSize() on a non-scrolling uiArea. (area: %p)", a); @@ -417,7 +417,7 @@ uiArea *uiNewArea(uiAreaHandler *ah) return a; } -uiArea *uiNewScrollingArea(uiAreaHandler *ah, intmax_t width, intmax_t height) +uiArea *uiNewScrollingArea(uiAreaHandler *ah, int width, int height) { uiArea *a; struct scrollViewCreateParams p; diff --git a/darwin/combobox.m b/darwin/combobox.m index c1bffdce..89a2e28c 100644 --- a/darwin/combobox.m +++ b/darwin/combobox.m @@ -81,12 +81,12 @@ void uiComboboxAppend(uiCombobox *c, const char *text) [c->pbac addObject:toNSString(text)]; } -intmax_t uiComboboxSelected(uiCombobox *c) +int uiComboboxSelected(uiCombobox *c) { return [c->pb indexOfSelectedItem]; } -void uiComboboxSetSelected(uiCombobox *c, intmax_t n) +void uiComboboxSetSelected(uiCombobox *c, int n) { [c->pb selectItemAtIndex:n]; } diff --git a/darwin/radiobuttons.m b/darwin/radiobuttons.m index c9bcfe9f..9a38981a 100644 --- a/darwin/radiobuttons.m +++ b/darwin/radiobuttons.m @@ -74,7 +74,7 @@ static void uiRadioButtonsDestroy(uiControl *c) uiFreeControl(uiControl(r)); } -static NSButton *buttonAt(uiRadioButtons *r, uintmax_t n) +static NSButton *buttonAt(uiRadioButtons *r, int n) { return (NSButton *) [r->buttons objectAtIndex:n]; } @@ -152,7 +152,7 @@ void uiRadioButtonsAppend(uiRadioButtons *r, const char *text) [r->lastv retain]; } -intmax_t uiRadioButtonsSelected(uiRadioButtons *r) +int uiRadioButtonsSelected(uiRadioButtons *r) { NSButton *b; NSUInteger i; @@ -165,7 +165,7 @@ intmax_t uiRadioButtonsSelected(uiRadioButtons *r) return -1; } -void uiRadioButtonsSetSelected(uiRadioButtons *r, intmax_t n) +void uiRadioButtonsSetSelected(uiRadioButtons *r, int n) { NSButton *b; NSInteger state; diff --git a/ui.h b/ui.h index 50a76ed9..8f1dd9fe 100644 --- a/ui.h +++ b/ui.h @@ -198,8 +198,8 @@ _UI_EXTERN uiSeparator *uiNewHorizontalSeparator(void); typedef struct uiCombobox uiCombobox; #define uiCombobox(this) ((uiCombobox *) (this)) _UI_EXTERN void uiComboboxAppend(uiCombobox *c, const char *text); -_UI_EXTERN intmax_t uiComboboxSelected(uiCombobox *c); -_UI_EXTERN void uiComboboxSetSelected(uiCombobox *c, intmax_t n); +_UI_EXTERN int uiComboboxSelected(uiCombobox *c); +_UI_EXTERN void uiComboboxSetSelected(uiCombobox *c, int n); _UI_EXTERN void uiComboboxOnSelected(uiCombobox *c, void (*f)(uiCombobox *c, void *data), void *data); _UI_EXTERN uiCombobox *uiNewCombobox(void); @@ -215,8 +215,8 @@ _UI_EXTERN uiEditableCombobox *uiNewEditableCombobox(void); typedef struct uiRadioButtons uiRadioButtons; #define uiRadioButtons(this) ((uiRadioButtons *) (this)) _UI_EXTERN void uiRadioButtonsAppend(uiRadioButtons *r, const char *text); -_UI_EXTERN intmax_t uiRadioButtonsSelected(uiRadioButtons *r); -_UI_EXTERN void uiRadioButtonsSetSelected(uiRadioButtons *r, intmax_t n); +_UI_EXTERN int uiRadioButtonsSelected(uiRadioButtons *r); +_UI_EXTERN void uiRadioButtonsSetSelected(uiRadioButtons *r, int n); _UI_EXTERN void uiRadioButtonsOnSelected(uiRadioButtons *r, void (*f)(uiRadioButtons *, void *), void *data); _UI_EXTERN uiRadioButtons *uiNewRadioButtons(void); @@ -283,12 +283,12 @@ struct uiAreaHandler { #define uiArea(this) ((uiArea *) (this)) // TODO give a better name // TODO document the types of width and height -_UI_EXTERN void uiAreaSetSize(uiArea *a, intmax_t width, intmax_t height); +_UI_EXTERN void uiAreaSetSize(uiArea *a, int width, int height); // TODO uiAreaQueueRedraw() _UI_EXTERN void uiAreaQueueRedrawAll(uiArea *a); _UI_EXTERN void uiAreaScrollTo(uiArea *a, double x, double y, double width, double height); _UI_EXTERN uiArea *uiNewArea(uiAreaHandler *ah); -_UI_EXTERN uiArea *uiNewScrollingArea(uiAreaHandler *ah, intmax_t width, intmax_t height); +_UI_EXTERN uiArea *uiNewScrollingArea(uiAreaHandler *ah, int width, int height); struct uiAreaDrawParams { uiDrawContext *Context; diff --git a/unix/area.c b/unix/area.c index 3a420f00..eab01681 100644 --- a/unix/area.c +++ b/unix/area.c @@ -41,8 +41,8 @@ struct uiArea { uiAreaHandler *ah; gboolean scrolling; - intmax_t scrollWidth; - intmax_t scrollHeight; + int scrollWidth; + int scrollHeight; // note that this is a pointer; see above clickCounter *cc; @@ -482,7 +482,7 @@ static void areaWidget_class_init(areaWidgetClass *class) uiUnixControlAllDefaults(uiArea) -void uiAreaSetSize(uiArea *a, intmax_t width, intmax_t height) +void uiAreaSetSize(uiArea *a, int width, int height) { if (!a->scrolling) userbug("You cannot call uiAreaSetSize() on a non-scrolling uiArea. (area: %p)", a); @@ -522,7 +522,7 @@ uiArea *uiNewArea(uiAreaHandler *ah) return a; } -uiArea *uiNewScrollingArea(uiAreaHandler *ah, intmax_t width, intmax_t height) +uiArea *uiNewScrollingArea(uiAreaHandler *ah, int width, int height) { uiArea *a; diff --git a/unix/combobox.c b/unix/combobox.c index 65c0e59a..6fed804b 100644 --- a/unix/combobox.c +++ b/unix/combobox.c @@ -30,12 +30,12 @@ void uiComboboxAppend(uiCombobox *c, const char *text) gtk_combo_box_text_append(c->comboboxText, NULL, text); } -intmax_t uiComboboxSelected(uiCombobox *c) +int uiComboboxSelected(uiCombobox *c) { return gtk_combo_box_get_active(c->combobox); } -void uiComboboxSetSelected(uiCombobox *c, intmax_t n) +void uiComboboxSetSelected(uiCombobox *c, int n) { // we need to inhibit sending of ::changed because this WILL send a ::changed otherwise g_signal_handler_block(c->combobox, c->onSelectedSignal); diff --git a/unix/radiobuttons.c b/unix/radiobuttons.c index 4f6e24e5..da41107e 100644 --- a/unix/radiobuttons.c +++ b/unix/radiobuttons.c @@ -64,7 +64,7 @@ void uiRadioButtonsAppend(uiRadioButtons *r, const char *text) gtk_widget_show(rb); } -intmax_t uiRadioButtonsSelected(uiRadioButtons *r) +int uiRadioButtonsSelected(uiRadioButtons *r) { GtkToggleButton *tb; guint i; @@ -77,7 +77,7 @@ intmax_t uiRadioButtonsSelected(uiRadioButtons *r) return -1; } -void uiRadioButtonsSetSelected(uiRadioButtons *r, intmax_t n) +void uiRadioButtonsSetSelected(uiRadioButtons *r, int n) { GtkToggleButton *tb; gboolean active; diff --git a/windows/area.cpp b/windows/area.cpp index b4a87517..17110d33 100644 --- a/windows/area.cpp +++ b/windows/area.cpp @@ -51,7 +51,7 @@ static LRESULT CALLBACK areaWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM uiWindowsControlAllDefaults(uiArea) -static void uiAreaMinimumSize(uiWindowsControl *c, intmax_t *width, intmax_t *height) +static void uiAreaMinimumSize(uiWindowsControl *c, int *width, int *height) { // TODO *width = 0; @@ -80,7 +80,7 @@ void unregisterArea(void) logLastError(L"error unregistering uiArea window class"); } -void uiAreaSetSize(uiArea *a, intmax_t width, intmax_t height) +void uiAreaSetSize(uiArea *a, int width, int height) { a->scrollWidth = width; a->scrollHeight = height; @@ -118,7 +118,7 @@ uiArea *uiNewArea(uiAreaHandler *ah) return a; } -uiArea *uiNewScrollingArea(uiAreaHandler *ah, intmax_t width, intmax_t height) +uiArea *uiNewScrollingArea(uiAreaHandler *ah, int width, int height) { uiArea *a; diff --git a/windows/area.hpp b/windows/area.hpp index 6b82532d..e733b84e 100644 --- a/windows/area.hpp +++ b/windows/area.hpp @@ -11,8 +11,8 @@ struct uiArea { uiAreaHandler *ah; BOOL scrolling; - intmax_t scrollWidth; - intmax_t scrollHeight; + int scrollWidth; + int scrollHeight; intmax_t hscrollpos; intmax_t vscrollpos; int hwheelCarry; diff --git a/windows/combobox.cpp b/windows/combobox.cpp index ac10f211..87c999ea 100644 --- a/windows/combobox.cpp +++ b/windows/combobox.cpp @@ -36,7 +36,7 @@ uiWindowsControlAllDefaultsExceptDestroy(uiCombobox) #define comboboxWidth 107 /* this is actually the shorter progress bar width, but Microsoft only indicates as wide as necessary; LONGTERM */ #define comboboxHeight 14 /* LONGTERM: is this too high? */ -static void uiComboboxMinimumSize(uiWindowsControl *cc, intmax_t *width, intmax_t *height) +static void uiComboboxMinimumSize(uiWindowsControl *cc, int *width, int *height) { uiCombobox *c = uiCombobox(cc); uiWindowsSizing sizing; @@ -69,17 +69,17 @@ void uiComboboxAppend(uiCombobox *c, const char *text) uiFree(wtext); } -intmax_t uiComboboxSelected(uiCombobox *c) +int uiComboboxSelected(uiCombobox *c) { LRESULT n; n = SendMessage(c->hwnd, CB_GETCURSEL, 0, 0); if (n == (LRESULT) CB_ERR) return -1; - return (intmax_t) n; + return n; } -void uiComboboxSetSelected(uiCombobox *c, intmax_t n) +void uiComboboxSetSelected(uiCombobox *c, int n) { // TODO error check SendMessageW(c->hwnd, CB_SETCURSEL, (WPARAM) n, 0); diff --git a/windows/radiobuttons.cpp b/windows/radiobuttons.cpp index 3063d3cd..29cd2e66 100644 --- a/windows/radiobuttons.cpp +++ b/windows/radiobuttons.cpp @@ -20,7 +20,6 @@ static BOOL onWM_COMMAND(uiControl *c, HWND clicked, WORD code, LRESULT *lResult { uiRadioButtons *r = uiRadioButtons(c); WPARAM check; - uintmax_t i; if (code != BN_CLICKED) return FALSE; @@ -61,10 +60,10 @@ uiWindowsControlAllDefaultsExceptDestroy(uiRadioButtons) // from http://msdn.microsoft.com/en-us/library/windows/desktop/bb226818%28v=vs.85%29.aspx #define radiobuttonXFromLeftOfBoxToLeftOfLabel 12 -static void uiRadioButtonsMinimumSize(uiWindowsControl *c, intmax_t *width, intmax_t *height) +static void uiRadioButtonsMinimumSize(uiWindowsControl *c, int *width, int *height) { uiRadioButtons *r = uiRadioButtons(c); - intmax_t wid, maxwid; + int wid, maxwid; uiWindowsSizing sizing; int x, y; @@ -92,7 +91,7 @@ static void uiRadioButtonsMinimumSize(uiWindowsControl *c, intmax_t *width, intm static void radiobuttonsRelayout(uiRadioButtons *r) { RECT client; - intmax_t x, y, width, height; + int x, y, width, height; int height1; uiWindowsSizing sizing; @@ -149,7 +148,7 @@ void uiRadioButtonsAppend(uiRadioButtons *r, const char *text) uiWindowsControlMinimumSizeChanged(uiWindowsControl(r)); } -intmax_t uiRadioButtonsSelected(uiRadioButtons *r) +int uiRadioButtonsSelected(uiRadioButtons *r) { size_t i; @@ -159,9 +158,9 @@ intmax_t uiRadioButtonsSelected(uiRadioButtons *r) return -1; } -void uiRadioButtonsSetSelected(uiRadioButtons *r, intmax_t n) +void uiRadioButtonsSetSelected(uiRadioButtons *r, int n) { - intmax_t m; + int m; m = uiRadioButtonsSelected(r); if (m != -1) From 155299cdb9739a5b0f791d71ef058a7a62cfa0d6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 13 Jun 2016 21:11:59 -0400 Subject: [PATCH 0312/1329] More intmax_t removal. --- darwin/drawtext.m | 8 ++++---- ui.h | 8 ++++---- unix/drawtext.c | 8 ++++---- windows/drawtext.cpp | 10 +++++----- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index d252f0e5..810a3a43 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -20,12 +20,12 @@ uiDrawFontFamilies *uiDrawListFontFamilies(void) return ff; } -uintmax_t uiDrawFontFamiliesNumFamilies(uiDrawFontFamilies *ff) +int uiDrawFontFamiliesNumFamilies(uiDrawFontFamilies *ff) { return CFArrayGetCount(ff->fonts); } -char *uiDrawFontFamiliesFamily(uiDrawFontFamilies *ff, uintmax_t n) +char *uiDrawFontFamiliesFamily(uiDrawFontFamilies *ff, int n) { CFStringRef familystr; char *family; @@ -616,7 +616,7 @@ void doDrawText(CGContextRef c, CGFloat cheight, double x, double y, uiDrawTextL CGContextSetTextPosition(c, x, y); #endif -static CFRange charsToRange(uiDrawTextLayout *layout, intmax_t startChar, intmax_t endChar) +static CFRange charsToRange(uiDrawTextLayout *layout, int startChar, int endChar) { CFRange start, end; CFRange out; @@ -630,7 +630,7 @@ static CFRange charsToRange(uiDrawTextLayout *layout, intmax_t startChar, intmax #define rangeToCFRange() charsToRange(layout, startChar, endChar) -void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, intmax_t startChar, intmax_t endChar, double r, double g, double b, double a) +void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, int startChar, int endChar, double r, double g, double b, double a) { CGColorSpaceRef colorspace; CGFloat components[4]; diff --git a/ui.h b/ui.h index 8f1dd9fe..153c5eea 100644 --- a/ui.h +++ b/ui.h @@ -445,12 +445,12 @@ _UI_EXTERN void uiDrawRestore(uiDrawContext *c); // TODO manage the use of Text, Font, and TextFont, and of the uiDrawText prefix in general -///// TODO +///// TODO reconsider this typedef struct uiDrawFontFamilies uiDrawFontFamilies; _UI_EXTERN uiDrawFontFamilies *uiDrawListFontFamilies(void); -_UI_EXTERN uintmax_t uiDrawFontFamiliesNumFamilies(uiDrawFontFamilies *ff); -_UI_EXTERN char *uiDrawFontFamiliesFamily(uiDrawFontFamilies *ff, uintmax_t n); +_UI_EXTERN int uiDrawFontFamiliesNumFamilies(uiDrawFontFamilies *ff); +_UI_EXTERN char *uiDrawFontFamiliesFamily(uiDrawFontFamilies *ff, int n); _UI_EXTERN void uiDrawFreeFontFamilies(uiDrawFontFamilies *ff); ///// END TODO @@ -524,7 +524,7 @@ _UI_EXTERN void uiDrawTextLayoutSetWidth(uiDrawTextLayout *layout, double width) _UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *layout, double *width, double *height); // and the attributes that you can set on a text layout -_UI_EXTERN void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, intmax_t startChar, intmax_t endChar, double r, double g, double b, double a); +_UI_EXTERN void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, int startChar, int endChar, double r, double g, double b, double a); _UI_EXTERN void uiDrawText(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout); diff --git a/unix/drawtext.c b/unix/drawtext.c index a9b856fe..d12fcee9 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -19,12 +19,12 @@ uiDrawFontFamilies *uiDrawListFontFamilies(void) return ff; } -uintmax_t uiDrawFontFamiliesNumFamilies(uiDrawFontFamilies *ff) +int uiDrawFontFamiliesNumFamilies(uiDrawFontFamilies *ff) { return ff->n; } -char *uiDrawFontFamiliesFamily(uiDrawFontFamilies *ff, uintmax_t n) +char *uiDrawFontFamiliesFamily(uiDrawFontFamilies *ff, int n) { PangoFontFamily *f; @@ -265,7 +265,7 @@ void uiDrawText(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout) g_object_unref(pl); } -static void addAttr(uiDrawTextLayout *layout, PangoAttribute *attr, intmax_t startChar, intmax_t endChar) +static void addAttr(uiDrawTextLayout *layout, PangoAttribute *attr, int startChar, int endChar) { attr->start_index = layout->graphemes[startChar]; attr->end_index = layout->graphemes[endChar]; @@ -292,7 +292,7 @@ static void try138(void) dlclose(handle); } -void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, intmax_t startChar, intmax_t endChar, double r, double g, double b, double a) +void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, int startChar, int endChar, double r, double g, double b, double a) { PangoAttribute *attr; guint16 rr, gg, bb, aa; diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 08855e4e..023fcdd9 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -23,12 +23,12 @@ uiDrawFontFamilies *uiDrawListFontFamilies(void) return ff; } -uintmax_t uiDrawFontFamiliesNumFamilies(uiDrawFontFamilies *ff) +int uiDrawFontFamiliesNumFamilies(uiDrawFontFamilies *ff) { return ff->fc->fonts->GetFontFamilyCount(); } -char *uiDrawFontFamiliesFamily(uiDrawFontFamilies *ff, uintmax_t n) +char *uiDrawFontFamiliesFamily(uiDrawFontFamilies *ff, int n) { IDWriteFontFamily *family; WCHAR *wname; @@ -331,8 +331,8 @@ enum layoutAttrType { struct layoutAttr { enum layoutAttrType type; - intmax_t start; - intmax_t end; + int start; + int end; double components[4]; }; @@ -516,7 +516,7 @@ void uiDrawText(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout) black->Release(); } -void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, intmax_t startChar, intmax_t endChar, double r, double g, double b, double a) +void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, int startChar, int endChar, double r, double g, double b, double a) { struct layoutAttr attr; From 864c6c25118ef10d2a95c2ecb34b5412c9a0a4ca Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 13 Jun 2016 21:20:20 -0400 Subject: [PATCH 0313/1329] Finished stripping ui.h of intmax. --- darwin/area.m | 2 +- darwin/grid.m | 30 +++++++++---------- ui.h | 10 +++---- unix/grid.c | 4 +-- windows/areaevents.cpp | 4 +-- windows/grid.cpp | 66 +++++++++++++++++++++--------------------- 6 files changed, 58 insertions(+), 58 deletions(-) diff --git a/darwin/area.m b/darwin/area.m index 40d719a5..b98aa429 100644 --- a/darwin/area.m +++ b/darwin/area.m @@ -130,7 +130,7 @@ struct uiArea { uiArea *a = self->libui_a; uiAreaMouseEvent me; NSPoint point; - uintmax_t buttonNumber; + int buttonNumber; NSUInteger pmb; unsigned int i, max; diff --git a/darwin/grid.m b/darwin/grid.m index 766b0f80..93f9e9fe 100644 --- a/darwin/grid.m +++ b/darwin/grid.m @@ -7,10 +7,10 @@ // maybe it's easier to do it regardless of align @interface gridChild : NSObject @property uiControl *c; -@property intmax_t left; -@property intmax_t top; -@property intmax_t xspan; -@property intmax_t yspan; +@property int left; +@property int top; +@property int xspan; +@property int yspan; @property int hexpand; @property uiAlign halign; @property int vexpand; @@ -25,8 +25,8 @@ uiGrid *g; NSMutableArray *children; int padded; - uintmax_t nhexpand; - uintmax_t nvexpand; + int nhexpand; + int nvexpand; NSMutableArray *edges; NSMutableArray *inBetweens; @@ -136,17 +136,17 @@ struct uiGrid { { gridChild *gc; CGFloat padding; - intmax_t xmin, ymin; - intmax_t xmax, ymax; - intmax_t xcount, ycount; + int xmin, ymin; + int xmax, ymax; + int xcount, ycount; BOOL first; int **gg; NSView ***gv; BOOL **gspan; - intmax_t x, y; + int x, y; int i; NSLayoutConstraint *c; - intmax_t firstx, firsty; + int firstx, firsty; BOOL *hexpand, *vexpand; BOOL doit; @@ -423,7 +423,7 @@ struct uiGrid { { NSLayoutPriority priority; BOOL update; - intmax_t oldn; + int oldn; uiControlSetParent(gc.c, uiControl(self->g)); uiDarwinControlSetSuperview(uiDarwinControl(gc.c), self); @@ -591,7 +591,7 @@ static void uiGridChildEdgeHuggingChanged(uiDarwinControl *c) uiDarwinControlDefaultHuggingPriority(uiGrid, view) uiDarwinControlDefaultSetHuggingPriority(uiGrid, view) -static gridChild *toChild(uiControl *c, intmax_t xspan, intmax_t yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign) +static gridChild *toChild(uiControl *c, int xspan, int yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign) { gridChild *gc; @@ -612,7 +612,7 @@ static gridChild *toChild(uiControl *c, intmax_t xspan, intmax_t yspan, int hexp return gc; } -void uiGridAppend(uiGrid *g, uiControl *c, intmax_t left, intmax_t top, intmax_t xspan, intmax_t yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign) +void uiGridAppend(uiGrid *g, uiControl *c, int left, int top, int xspan, int yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign) { gridChild *gc; @@ -626,7 +626,7 @@ void uiGridAppend(uiGrid *g, uiControl *c, intmax_t left, intmax_t top, intmax_t [g->view append:gc]; } -void uiGridInsertAt(uiGrid *g, uiControl *c, uiControl *existing, uiAt at, intmax_t xspan, intmax_t yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign) +void uiGridInsertAt(uiGrid *g, uiControl *c, uiControl *existing, uiAt at, int xspan, int yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign) { gridChild *gc; diff --git a/ui.h b/ui.h index 153c5eea..7527ed11 100644 --- a/ui.h +++ b/ui.h @@ -545,10 +545,10 @@ struct uiAreaMouseEvent { double AreaWidth; double AreaHeight; - uintmax_t Down; - uintmax_t Up; + int Down; + int Up; - uintmax_t Count; + int Count; uiModifiers Modifiers; @@ -645,8 +645,8 @@ _UI_ENUM(uiAt) { typedef struct uiGrid uiGrid; #define uiGrid(this) ((uiGrid *) (this)) -_UI_EXTERN void uiGridAppend(uiGrid *g, uiControl *c, intmax_t left, intmax_t top, intmax_t xspan, intmax_t yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign); -_UI_EXTERN void uiGridInsertAt(uiGrid *g, uiControl *c, uiControl *existing, uiAt at, intmax_t xspan, intmax_t yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign); +_UI_EXTERN void uiGridAppend(uiGrid *g, uiControl *c, int left, int top, int xspan, int yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign); +_UI_EXTERN void uiGridInsertAt(uiGrid *g, uiControl *c, uiControl *existing, uiAt at, int xspan, int yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign); _UI_EXTERN int uiGridPadded(uiGrid *g); _UI_EXTERN void uiGridSetPadded(uiGrid *g, int padded); _UI_EXTERN uiGrid *uiNewGrid(void); diff --git a/unix/grid.c b/unix/grid.c index 3ed60ec5..6d9813b3 100644 --- a/unix/grid.c +++ b/unix/grid.c @@ -80,7 +80,7 @@ static GtkWidget *prepare(struct gridChild *gc, uiControl *c, int hexpand, uiAli return widget; } -void uiGridAppend(uiGrid *g, uiControl *c, intmax_t left, intmax_t top, intmax_t xspan, intmax_t yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign) +void uiGridAppend(uiGrid *g, uiControl *c, int left, int top, int xspan, int yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign) { struct gridChild gc; GtkWidget *widget; @@ -94,7 +94,7 @@ void uiGridAppend(uiGrid *g, uiControl *c, intmax_t left, intmax_t top, intmax_t g_array_append_val(g->children, gc); } -void uiGridInsertAt(uiGrid *g, uiControl *c, uiControl *existing, uiAt at, intmax_t xspan, intmax_t yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign) +void uiGridInsertAt(uiGrid *g, uiControl *c, uiControl *existing, uiAt at, int xspan, int yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign) { struct gridChild gc; GtkWidget *widget; diff --git a/windows/areaevents.cpp b/windows/areaevents.cpp index 386b49f8..0aa5bee9 100644 --- a/windows/areaevents.cpp +++ b/windows/areaevents.cpp @@ -73,10 +73,10 @@ static void capture(uiArea *a, BOOL capturing) logLastError(L"error releasing capture on drag"); } -static void areaMouseEvent(uiArea *a, uintmax_t down, uintmax_t up, WPARAM wParam, LPARAM lParam) +static void areaMouseEvent(uiArea *a, int down, int up, WPARAM wParam, LPARAM lParam) { uiAreaMouseEvent me; - uintmax_t button; + int button; POINT clientpt; RECT client; BOOL inClient; diff --git a/windows/grid.cpp b/windows/grid.cpp index a262045b..bcf89980 100644 --- a/windows/grid.cpp +++ b/windows/grid.cpp @@ -10,19 +10,19 @@ struct gridChild { uiControl *c; - intmax_t left; - intmax_t top; - intmax_t xspan; - intmax_t yspan; + int left; + int top; + int xspan; + int yspan; int hexpand; uiAlign halign; int vexpand; uiAlign valign; // have these here so they don't need to be reallocated each relayout - intmax_t finalx, finaly; - intmax_t finalwidth, finalheight; - intmax_t minwidth, minheight; + int finalx, finaly; + int finalwidth, finalheight; + int minwidth, minheight; }; struct uiGrid { @@ -32,8 +32,8 @@ struct uiGrid { std::map *indexof; int padded; - intmax_t xmin, ymin; - intmax_t xmax, ymax; + int xmin, ymin; + int xmax, ymax; }; #define xcount(g) ((g)->xmax - (g)->xmin) @@ -44,20 +44,20 @@ struct uiGrid { class gridLayoutData { size_t ycount; public: - intmax_t **gg; // topological map gg[y][x] = control index - intmax_t *colwidths; - intmax_t *rowheights; + int **gg; // topological map gg[y][x] = control index + int *colwidths; + int *rowheights; bool *hexpand; bool *vexpand; gridLayoutData(uiGrid *g) { size_t i; - intmax_t x, y; + int x, y; - this->gg = new intmax_t *[ycount(g)]; + this->gg = new int *[ycount(g)]; for (y = 0; y < ycount(g); y++) { - this->gg[y] = new intmax_t[xcount(g)]; + this->gg[y] = new int[xcount(g)]; for (x = 0; x < xcount(g); x++) this->gg[y][x] = -1; } @@ -71,10 +71,10 @@ public: this->gg[toyindex(g, y)][toxindex(g, x)] = i; } - this->colwidths = new intmax_t[xcount(g)]; - ZeroMemory(this->colwidths, xcount(g) * sizeof (intmax_t)); - this->rowheights = new intmax_t[ycount(g)]; - ZeroMemory(this->rowheights, ycount(g) * sizeof (intmax_t)); + this->colwidths = new int[xcount(g)]; + ZeroMemory(this->colwidths, xcount(g) * sizeof (int)); + this->rowheights = new int[ycount(g)]; + ZeroMemory(this->rowheights, ycount(g) * sizeof (int)); this->hexpand = new bool[xcount(g)]; ZeroMemory(this->hexpand, xcount(g) * sizeof (bool)); this->vexpand = new bool[ycount(g)]; @@ -112,14 +112,14 @@ static void gridPadding(uiGrid *g, int *xpadding, int *ypadding) static void gridRelayout(uiGrid *g) { RECT r; - intmax_t x, y, width, height; + int x, y, width, height; gridLayoutData *ld; int xpadding, ypadding; - intmax_t ix, iy; - intmax_t iwidth, iheight; + int ix, iy; + int iwidth, iheight; int i; struct gridChild *gc; - intmax_t nhexpand, nvexpand; + int nhexpand, nvexpand; if (g->children->size() == 0) return; // nothing to do @@ -229,7 +229,7 @@ static void gridRelayout(uiGrid *g) // 6) compute cell positions and sizes for (iy = 0; iy < ycount(g); iy++) { - intmax_t curx; + int curx; int prev; curx = 0; @@ -251,7 +251,7 @@ static void gridRelayout(uiGrid *g) } } for (ix = 0; ix < xcount(g); ix++) { - intmax_t cury; + int cury; int prev; cury = 0; @@ -357,16 +357,16 @@ static void uiGridSyncEnableState(uiWindowsControl *c, int enabled) uiWindowsControlDefaultSetParentHWND(uiGrid) -static void uiGridMinimumSize(uiWindowsControl *c, intmax_t *width, intmax_t *height) +static void uiGridMinimumSize(uiWindowsControl *c, int *width, int *height) { uiGrid *g = uiGrid(c); int xpadding, ypadding; gridLayoutData *ld; - intmax_t x, y; + int x, y; int i; struct gridChild *gc; - intmax_t minwid, minht; - intmax_t colwidth, rowheight; + int minwid, minht; + int colwidth, rowheight; *width = 0; *height = 0; @@ -429,7 +429,7 @@ static void gridArrangeChildren(uiGrid *g) HWND insertAfter; gridLayoutData *ld; bool *visited; - intmax_t x, y; + int x, y; int i; struct gridChild *gc; @@ -479,7 +479,7 @@ static void gridRecomputeMinMax(uiGrid *g) } } -static struct gridChild *toChild(uiControl *c, intmax_t xspan, intmax_t yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign) +static struct gridChild *toChild(uiControl *c, int xspan, int yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign) { struct gridChild *gc; @@ -509,7 +509,7 @@ static void add(uiGrid *g, struct gridChild *gc) uiWindowsControlMinimumSizeChanged(uiWindowsControl(g)); } -void uiGridAppend(uiGrid *g, uiControl *c, intmax_t left, intmax_t top, intmax_t xspan, intmax_t yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign) +void uiGridAppend(uiGrid *g, uiControl *c, int left, int top, int xspan, int yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign) { struct gridChild *gc; @@ -520,7 +520,7 @@ void uiGridAppend(uiGrid *g, uiControl *c, intmax_t left, intmax_t top, intmax_t } // TODO decide what happens if existing is NULL -void uiGridInsertAt(uiGrid *g, uiControl *c, uiControl *existing, uiAt at, intmax_t xspan, intmax_t yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign) +void uiGridInsertAt(uiGrid *g, uiControl *c, uiControl *existing, uiAt at, int xspan, int yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign) { struct gridChild *gc; struct gridChild *other; From 2affdab8377f7bf700f6a7a594a4b52589c6adb5 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 13 Jun 2016 21:23:05 -0400 Subject: [PATCH 0314/1329] Removed intmax_t from the rest of the public API. --- ui_windows.h | 9 ++++----- windows/control.cpp | 4 ++-- windows/text.cpp | 2 +- windows/winpublic.cpp | 2 +- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/ui_windows.h b/ui_windows.h index 81185f51..3ea511c9 100644 --- a/ui_windows.h +++ b/ui_windows.h @@ -22,8 +22,7 @@ struct uiWindowsControl { BOOL visible; void (*SyncEnableState)(uiWindowsControl *, int); void (*SetParentHWND)(uiWindowsControl *, HWND); - // TODO consider changing these from intmax_t to int - void (*MinimumSize)(uiWindowsControl *, intmax_t *, intmax_t *); + void (*MinimumSize)(uiWindowsControl *, int *, int *); void (*MinimumSizeChanged)(uiWindowsControl *); void (*LayoutRect)(uiWindowsControl *c, RECT *r); void (*AssignControlIDZOrder)(uiWindowsControl *, LONG_PTR *, HWND *); @@ -32,7 +31,7 @@ struct uiWindowsControl { // TODO document _UI_EXTERN void uiWindowsControlSyncEnableState(uiWindowsControl *, int); _UI_EXTERN void uiWindowsControlSetParentHWND(uiWindowsControl *, HWND); -_UI_EXTERN void uiWindowsControlMinimumSize(uiWindowsControl *, intmax_t *, intmax_t *); +_UI_EXTERN void uiWindowsControlMinimumSize(uiWindowsControl *, int *, int *); _UI_EXTERN void uiWindowsControlMinimumSizeChanged(uiWindowsControl *); _UI_EXTERN void uiWindowsControlLayoutRect(uiWindowsControl *, RECT *); _UI_EXTERN void uiWindowsControlAssignControlIDZOrder(uiWindowsControl *, LONG_PTR *, HWND *); @@ -201,11 +200,11 @@ _UI_EXTERN char *uiWindowsWindowText(HWND hwnd); _UI_EXTERN void uiWindowsSetWindowText(HWND hwnd, const char *text); // TODO document -_UI_EXTERN intmax_t uiWindowsWindowTextWidth(HWND hwnd); +_UI_EXTERN int uiWindowsWindowTextWidth(HWND hwnd); // TODO document // TODO point out this should only be used in a resize cycle -_UI_EXTERN void uiWindowsEnsureMoveWindowDuringResize(HWND hwnd, intmax_t x, intmax_t y, intmax_t width, intmax_t height); +_UI_EXTERN void uiWindowsEnsureMoveWindowDuringResize(HWND hwnd, int x, int y, int width, int height); // TODO document _UI_EXTERN void uiWindowsRegisterWM_COMMANDHandler(HWND hwnd, BOOL (*handler)(uiControl *, HWND, WORD, LRESULT *), uiControl *c); diff --git a/windows/control.cpp b/windows/control.cpp index 2afbbc27..d59132b9 100644 --- a/windows/control.cpp +++ b/windows/control.cpp @@ -11,7 +11,7 @@ void uiWindowsControlSetParentHWND(uiWindowsControl *c, HWND parent) (*(c->SetParentHWND))(c, parent); } -void uiWindowsControlMinimumSize(uiWindowsControl *c, intmax_t *width, intmax_t *height) +void uiWindowsControlMinimumSize(uiWindowsControl *c, int *width, int *height) { (*(c->MinimumSize))(c, width, height); } @@ -87,7 +87,7 @@ void uiWindowsControlAssignSoleControlIDZOrder(uiWindowsControl *c) BOOL uiWindowsControlTooSmall(uiWindowsControl *c) { RECT r; - intmax_t width, height; + int width, height; uiWindowsControlLayoutRect(c, &r); uiWindowsControlMinimumSize(c, &width, &height); diff --git a/windows/text.cpp b/windows/text.cpp index 4beeccd7..af79fb80 100644 --- a/windows/text.cpp +++ b/windows/text.cpp @@ -38,7 +38,7 @@ void uiFreeText(char *text) uiFree(text); } -intmax_t uiWindowsWindowTextWidth(HWND hwnd) +int uiWindowsWindowTextWidth(HWND hwnd) { LRESULT len; WCHAR *text; diff --git a/windows/winpublic.cpp b/windows/winpublic.cpp index 1cc9129d..397a3b54 100644 --- a/windows/winpublic.cpp +++ b/windows/winpublic.cpp @@ -23,7 +23,7 @@ void uiWindowsEnsureAssignControlIDZOrder(HWND hwnd, LONG_PTR *controlID, HWND * *insertAfter = hwnd; } -void uiWindowsEnsureMoveWindowDuringResize(HWND hwnd, intmax_t x, intmax_t y, intmax_t width, intmax_t height) +void uiWindowsEnsureMoveWindowDuringResize(HWND hwnd, int x, int y, int width, int height) { RECT r; From 75a8ee9bf9cdbec73fea074fea4dc0681d375cba Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 13 Jun 2016 21:37:50 -0400 Subject: [PATCH 0315/1329] Removed intmax_t from everything else EXCEPT the Windows code. Now it's time for THAT... --- common/areaevents.c | 2 +- common/uipriv.h | 14 +++++++------- darwin/editablecombo.m | 2 +- darwin/form.m | 8 ++++---- examples/controlgallery/main.c | 2 +- test/drawtests.c | 2 +- test/page4.c | 2 +- test/page8.c | 2 +- test/spaced.c | 12 ++++++------ test/test.h | 2 +- unix/area.c | 3 +++ windows/areaevents.cpp | 1 + 12 files changed, 28 insertions(+), 24 deletions(-) diff --git a/common/areaevents.c b/common/areaevents.c index c4abf102..cf3c288c 100644 --- a/common/areaevents.c +++ b/common/areaevents.c @@ -14,7 +14,7 @@ Thanks to mclasen, garnacho_, halfline, and tristan in irc.gimp.net/#gtk+. // x, y, xdist, ydist, and c.rect must have the same units // so must time, maxTime, and c.prevTime -uintmax_t clickCounterClick(clickCounter *c, uintmax_t button, intmax_t x, intmax_t y, uintptr_t time, uintptr_t maxTime, intmax_t xdist, intmax_t ydist) +int clickCounterClick(clickCounter *c, int button, int x, int y, uintptr_t time, uintptr_t maxTime, int32_t xdist, int32_t ydist) { // different button than before? if so, don't count if (button != c->curButton) diff --git a/common/uipriv.h b/common/uipriv.h index 73210719..63f44467 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -36,15 +36,15 @@ typedef struct clickCounter clickCounter; // you should call Reset() to zero-initialize a new instance // it doesn't matter that all the non-count fields are zero: the first click will fail the curButton test straightaway, so it'll return 1 and set the rest of the structure accordingly struct clickCounter { - uintmax_t curButton; - intmax_t rectX0; - intmax_t rectY0; - intmax_t rectX1; - intmax_t rectY1; + int curButton; + int rectX0; + int rectY0; + int rectX1; + int rectY1; uintptr_t prevTime; - uintmax_t count; + int count; }; -extern uintmax_t clickCounterClick(clickCounter *, uintmax_t, intmax_t, intmax_t, uintptr_t, uintptr_t, intmax_t, intmax_t); +extern int clickCounterClick(clickCounter *, int, int, int, uintptr_t, uintptr_t, int32_t, int32_t); extern void clickCounterReset(clickCounter *); extern int fromScancode(uintptr_t, uiAreaKeyEvent *); diff --git a/darwin/editablecombo.m b/darwin/editablecombo.m index c676c562..434add70 100644 --- a/darwin/editablecombo.m +++ b/darwin/editablecombo.m @@ -127,7 +127,7 @@ void uiEditableComboboxSetText(uiEditableCombobox *c, const char *text) #if 0 // LONGTERM -void uiEditableComboboxSetSelected(uiEditableCombobox *c, intmax_t n) +void uiEditableComboboxSetSelected(uiEditableCombobox *c, int n) { if (c->editable) { // see https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ComboBox/Tasks/SettingComboBoxValue.html#//apple_ref/doc/uid/20000256 diff --git a/darwin/form.m b/darwin/form.m index ffebc67e..cdc3563b 100644 --- a/darwin/form.m +++ b/darwin/form.m @@ -23,7 +23,7 @@ uiForm *f; NSMutableArray *children; int padded; - uintmax_t nStretchy; + int nStretchy; NSLayoutConstraint *first; NSMutableArray *inBetweens; @@ -40,7 +40,7 @@ - (CGFloat)paddingAmount; - (void)establishOurConstraints; - (void)append:(NSString *)label c:(uiControl *)c stretchy:(int)stretchy; -//TODO- (void)delete:(uintmax_t)n; +//TODO- (void)delete:(int)n; - (int)isPadded; - (void)setPadded:(int)p; - (BOOL)hugsTrailing; @@ -338,7 +338,7 @@ struct uiForm { formChild *fc; NSLayoutPriority priority; NSLayoutAttribute attribute; - uintmax_t oldnStretchy; + int oldnStretchy; fc = [[formChild alloc] initWithLabel:newLabel(label)]; fc.c = c; @@ -389,7 +389,7 @@ struct uiForm { [fc release]; // we don't need the initial reference now } -//TODO- (void)delete:(uintmax_t)n +//TODO- (void)delete:(int)n - (int)isPadded { diff --git a/examples/controlgallery/main.c b/examples/controlgallery/main.c index 24e8130f..fd9c8ea5 100644 --- a/examples/controlgallery/main.c +++ b/examples/controlgallery/main.c @@ -317,7 +317,7 @@ static uiSpinbox *spinbox; static uiSlider *slider; static uiProgressBar *progressbar; -static void update(intmax_t value) +static void update(int value) { uiSpinboxSetValue(spinbox, value); uiSliderSetValue(slider, value); diff --git a/test/drawtests.c b/test/drawtests.c index 09ecac45..b6de753f 100644 --- a/test/drawtests.c +++ b/test/drawtests.c @@ -1964,7 +1964,7 @@ static const struct drawtest tests[] = { { NULL, NULL }, }; -void runDrawTest(intmax_t n, uiAreaDrawParams *p) +void runDrawTest(int n, uiAreaDrawParams *p) { (*(tests[n].draw))(p); } diff --git a/test/page4.c b/test/page4.c index 5a448c44..ce4a6afb 100644 --- a/test/page4.c +++ b/test/page4.c @@ -8,7 +8,7 @@ static uiProgressBar *pbar; #define CHANGED(what) \ static void on ## what ## Changed(ui ## what *this, void *data) \ { \ - uintmax_t value; \ + int value; \ printf("on %s changed\n", #what); \ value = ui ## what ## Value(this); \ uiSpinboxSetValue(spinbox, value); \ diff --git a/test/page8.c b/test/page8.c index 3819257d..7d855560 100644 --- a/test/page8.c +++ b/test/page8.c @@ -5,7 +5,7 @@ static void onListFonts(uiButton *b, void *data) { uiDrawFontFamilies *ff; char *this; - uintmax_t i, n; + int i, n; uiMultilineEntrySetText(uiMultilineEntry(data), ""); ff = uiDrawListFontFamilies(); diff --git a/test/spaced.c b/test/spaced.c index a54a0089..02db99ae 100644 --- a/test/spaced.c +++ b/test/spaced.c @@ -7,8 +7,8 @@ struct thing { }; static struct thing *things = NULL; -static uintmax_t len = 0; -static uintmax_t cap = 0; +static size_t len = 0; +static size_t cap = 0; #define grow 32 @@ -37,9 +37,9 @@ enum types { void setSpaced(int spaced) { - uintmax_t i; + size_t i; void *p; - uintmax_t j, n; + size_t j, n; for (i = 0; i < len; i++) { p = things[i].ptr; @@ -72,9 +72,9 @@ void querySpaced(char out[12]) // more than enough { int m = 0; int p = 0; - uintmax_t i; + size_t i; void *pp; - uintmax_t j, n; + size_t j, n; for (i = 0; i < len; i++) { pp = things[i].ptr; diff --git a/test/test.h b/test/test.h index 2afe3e39..e1b16299 100644 --- a/test/test.h +++ b/test/test.h @@ -50,7 +50,7 @@ extern uiBox *makePage5(uiWindow *); extern uiBox *makePage6(void); // drawtests.c -extern void runDrawTest(intmax_t, uiAreaDrawParams *); +extern void runDrawTest(int, uiAreaDrawParams *); extern void populateComboboxWithTests(uiCombobox *); // page7.c diff --git a/unix/area.c b/unix/area.c index eab01681..c5b490d6 100644 --- a/unix/area.c +++ b/unix/area.c @@ -251,6 +251,9 @@ static gboolean areaWidget_button_press_event(GtkWidget *w, GdkEventButton *e) "gtk-double-click-distance", &maxDistance, NULL); // don't unref settings; it's transfer-none (thanks gregier in irc.gimp.net/#gtk+) + // e->time is guint32 + // e->x and e->y are floating-point; just make them 32-bit integers + // maxTime and maxDistance... are gint, which *should* fit, hopefully... me.Count = clickCounterClick(a->cc, me.Down, e->x, e->y, e->time, maxTime, diff --git a/windows/areaevents.cpp b/windows/areaevents.cpp index 0aa5bee9..7d391b85 100644 --- a/windows/areaevents.cpp +++ b/windows/areaevents.cpp @@ -117,6 +117,7 @@ static void areaMouseEvent(uiArea *a, int down, int up, WPARAM wParam, LPARAM l if (me.Down != 0) // GetMessageTime() returns LONG and GetDoubleClckTime() returns UINT, which are int32 and uint32, respectively, but we don't need to worry about the signedness because for the same bit widths and two's complement arithmetic, s1-s2 == u1-u2 if bits(s1)==bits(s2) and bits(u1)==bits(u2) (and Windows requires two's complement: http://blogs.msdn.com/b/oldnewthing/archive/2005/05/27/422551.aspx) // signedness isn't much of an issue for these calls anyway because http://stackoverflow.com/questions/24022225/what-are-the-sign-extension-rules-for-calling-windows-api-functions-stdcall-t and that we're only using unsigned values (think back to how you (didn't) handle signedness in assembly language) AND because of the above AND because the statistics below (time interval and width/height) really don't make sense if negative + // GetSystemMetrics() returns int, which is int32 me.Count = clickCounterClick(&(a->cc), me.Down, me.X, me.Y, GetMessageTime(), GetDoubleClickTime(), From cb3f10f243355b1fef3c3d283abb9677d586592b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 13 Jun 2016 21:38:48 -0400 Subject: [PATCH 0316/1329] Quick fix in the meantime. --- common/uipriv.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/uipriv.h b/common/uipriv.h index 63f44467..d6b54e89 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -44,7 +44,7 @@ struct clickCounter { uintptr_t prevTime; int count; }; -extern int clickCounterClick(clickCounter *, int, int, int, uintptr_t, uintptr_t, int32_t, int32_t); +int clickCounterClick(clickCounter *c, int button, int x, int y, uintptr_t time, uintptr_t maxTime, int32_t xdist, int32_t ydist); extern void clickCounterReset(clickCounter *); extern int fromScancode(uintptr_t, uiAreaKeyEvent *); From 8c974e7b773f1fa746055484c77e0ca40684bd6c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 13 Jun 2016 21:47:04 -0400 Subject: [PATCH 0317/1329] Started removing intmax_ts, first with uipriv_windows.hpp. --- windows/tabpage.cpp | 2 +- windows/uipriv_windows.hpp | 6 +++--- windows/utf16.cpp | 2 +- windows/winutil.cpp | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/windows/tabpage.cpp b/windows/tabpage.cpp index 7f95071d..5283ce79 100644 --- a/windows/tabpage.cpp +++ b/windows/tabpage.cpp @@ -117,7 +117,7 @@ void tabPageDestroy(struct tabPage *tp) uiFree(tp); } -void tabPageMinimumSize(struct tabPage *tp, intmax_t *width, intmax_t *height) +void tabPageMinimumSize(struct tabPage *tp, int *width, int *height) { int mx, my; diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index d33203f3..6ffe09f1 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -38,7 +38,7 @@ extern WCHAR *vstrf(const WCHAR *format, va_list ap); extern char *LFtoCRLF(const char *lfonly); extern void CRLFtoLF(char *s); extern WCHAR *ftoutf16(double d); -extern WCHAR *itoutf16(intmax_t i); +extern WCHAR *itoutf16(int i); // debug.cpp // see http://stackoverflow.com/questions/14421656/is-there-widely-available-wide-character-variant-of-file @@ -69,7 +69,7 @@ extern DWORD getStyle(HWND hwnd); extern void setStyle(HWND hwnd, DWORD style); extern DWORD getExStyle(HWND hwnd); extern void setExStyle(HWND hwnd, DWORD exstyle); -extern void clientSizeToWindowSize(HWND hwnd, intmax_t *width, intmax_t *height, BOOL hasMenubar); +extern void clientSizeToWindowSize(HWND hwnd, int *width, int *height, BOOL hasMenubar); extern HWND parentOf(HWND child); extern HWND parentToplevel(HWND child); extern void setWindowInsertAfter(HWND hwnd, HWND insertAfter); @@ -134,7 +134,7 @@ struct tabPage { }; extern struct tabPage *newTabPage(uiControl *child); extern void tabPageDestroy(struct tabPage *tp); -extern void tabPageMinimumSize(struct tabPage *tp, intmax_t *width, intmax_t *height); +extern void tabPageMinimumSize(struct tabPage *tp, int *width, int *height); // colordialog.cpp struct colorDialogRGBA { diff --git a/windows/utf16.cpp b/windows/utf16.cpp index 9acc5e82..98954d0a 100644 --- a/windows/utf16.cpp +++ b/windows/utf16.cpp @@ -142,7 +142,7 @@ WCHAR *ftoutf16(double d) } // to complement the above -WCHAR *itoutf16(intmax_t i) +WCHAR *itoutf16(int i) { std::wostringstream ss; std::wstring s; diff --git a/windows/winutil.cpp b/windows/winutil.cpp index 7f7716ac..507c5a3f 100644 --- a/windows/winutil.cpp +++ b/windows/winutil.cpp @@ -77,7 +77,7 @@ void setExStyle(HWND hwnd, DWORD exstyle) } // see http://blogs.msdn.com/b/oldnewthing/archive/2003/09/11/54885.aspx and http://blogs.msdn.com/b/oldnewthing/archive/2003/09/13/54917.aspx -void clientSizeToWindowSize(HWND hwnd, intmax_t *width, intmax_t *height, BOOL hasMenubar) +void clientSizeToWindowSize(HWND hwnd, int *width, int *height, BOOL hasMenubar) { RECT window; From 4524ffce5ef6fdda30f4870f34154d7c2c96f78d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 13 Jun 2016 21:54:15 -0400 Subject: [PATCH 0318/1329] Removed most of the intmax_ts, which are all MinimumSize()s. --- windows/button.cpp | 2 +- windows/checkbox.cpp | 2 +- windows/colorbutton.cpp | 2 +- windows/datetimepicker.cpp | 2 +- windows/editablecombo.cpp | 2 +- windows/entry.cpp | 2 +- windows/fontbutton.cpp | 2 +- windows/form.cpp | 30 +++++++++++++++--------------- windows/group.cpp | 6 +++--- windows/label.cpp | 2 +- windows/multilineentry.cpp | 2 +- windows/progressbar.cpp | 2 +- windows/separator.cpp | 2 +- windows/window.cpp | 6 +++--- 14 files changed, 32 insertions(+), 32 deletions(-) diff --git a/windows/button.cpp b/windows/button.cpp index 057d8763..3b12e726 100644 --- a/windows/button.cpp +++ b/windows/button.cpp @@ -33,7 +33,7 @@ uiWindowsControlAllDefaultsExceptDestroy(uiButton) // from http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing #define buttonHeight 14 -static void uiButtonMinimumSize(uiWindowsControl *c, intmax_t *width, intmax_t *height) +static void uiButtonMinimumSize(uiWindowsControl *c, int *width, int *height) { uiButton *b = uiButton(c); SIZE size; diff --git a/windows/checkbox.cpp b/windows/checkbox.cpp index 7c30c113..be425c00 100644 --- a/windows/checkbox.cpp +++ b/windows/checkbox.cpp @@ -43,7 +43,7 @@ uiWindowsControlAllDefaultsExceptDestroy(uiCheckbox) // from http://msdn.microsoft.com/en-us/library/windows/desktop/bb226818%28v=vs.85%29.aspx #define checkboxXFromLeftOfBoxToLeftOfLabel 12 -static void uiCheckboxMinimumSize(uiWindowsControl *cc, intmax_t *width, intmax_t *height) +static void uiCheckboxMinimumSize(uiWindowsControl *cc, int *width, int *height) { uiCheckbox *c = uiCheckbox(cc); uiWindowsSizing sizing; diff --git a/windows/colorbutton.cpp b/windows/colorbutton.cpp index 462c0ddb..c1ba6954 100644 --- a/windows/colorbutton.cpp +++ b/windows/colorbutton.cpp @@ -112,7 +112,7 @@ uiWindowsControlAllDefaultsExceptDestroy(uiColorButton) #define buttonHeight 14 // TODO check widths -static void uiColorButtonMinimumSize(uiWindowsControl *c, intmax_t *width, intmax_t *height) +static void uiColorButtonMinimumSize(uiWindowsControl *c, int *width, int *height) { uiColorButton *b = uiColorButton(c); SIZE size; diff --git a/windows/datetimepicker.cpp b/windows/datetimepicker.cpp index 4f9a00fa..e105c2fd 100644 --- a/windows/datetimepicker.cpp +++ b/windows/datetimepicker.cpp @@ -113,7 +113,7 @@ uiWindowsControlAllDefaultsExceptDestroy(uiDateTimePicker) // from http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing #define entryHeight 14 -static void uiDateTimePickerMinimumSize(uiWindowsControl *c, intmax_t *width, intmax_t *height) +static void uiDateTimePickerMinimumSize(uiWindowsControl *c, int *width, int *height) { uiDateTimePicker *d = uiDateTimePicker(c); SIZE s; diff --git a/windows/editablecombo.cpp b/windows/editablecombo.cpp index 38d38aab..9e1fdbfb 100644 --- a/windows/editablecombo.cpp +++ b/windows/editablecombo.cpp @@ -46,7 +46,7 @@ uiWindowsControlAllDefaultsExceptDestroy(uiEditableCombobox) #define comboboxWidth 107 /* this is actually the shorter progress bar width, but Microsoft only indicates as wide as necessary; LONGTERM */ #define comboboxHeight 14 /* LONGTERM: is this too high? */ -static void uiEditableComboboxMinimumSize(uiWindowsControl *cc, intmax_t *width, intmax_t *height) +static void uiEditableComboboxMinimumSize(uiWindowsControl *cc, int *width, int *height) { uiEditableCombobox *c = uiEditableCombobox(cc); uiWindowsSizing sizing; diff --git a/windows/entry.cpp b/windows/entry.cpp index 13bae178..a7a077f2 100644 --- a/windows/entry.cpp +++ b/windows/entry.cpp @@ -37,7 +37,7 @@ uiWindowsControlAllDefaultsExceptDestroy(uiEntry) #define entryWidth 107 /* this is actually the shorter progress bar width, but Microsoft only indicates as wide as necessary */ #define entryHeight 14 -static void uiEntryMinimumSize(uiWindowsControl *c, intmax_t *width, intmax_t *height) +static void uiEntryMinimumSize(uiWindowsControl *c, int *width, int *height) { uiEntry *e = uiEntry(c); uiWindowsSizing sizing; diff --git a/windows/fontbutton.cpp b/windows/fontbutton.cpp index b0f2c047..d2d4dabf 100644 --- a/windows/fontbutton.cpp +++ b/windows/fontbutton.cpp @@ -55,7 +55,7 @@ uiWindowsControlAllDefaultsExceptDestroy(uiFontButton) // from http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing #define buttonHeight 14 -static void uiFontButtonMinimumSize(uiWindowsControl *c, intmax_t *width, intmax_t *height) +static void uiFontButtonMinimumSize(uiWindowsControl *c, int *width, int *height) { uiFontButton *b = uiFontButton(c); SIZE size; diff --git a/windows/form.cpp b/windows/form.cpp index 64b55a31..e15e9571 100644 --- a/windows/form.cpp +++ b/windows/form.cpp @@ -5,7 +5,7 @@ struct formChild { uiControl *c; HWND label; int stretchy; - intmax_t height; + int height; }; struct uiForm { @@ -34,13 +34,13 @@ static void formPadding(uiForm *f, int *xpadding, int *ypadding) static void formRelayout(uiForm *f) { RECT r; - intmax_t x, y, width, height; + int x, y, width, height; int xpadding, ypadding; - uintmax_t nStretchy; - intmax_t labelwid, stretchyht; - intmax_t thiswid; - uintmax_t i; - intmax_t minimumWidth, minimumHeight; + int nStretchy; + int labelwid, stretchyht; + int thiswid; + int i; + int minimumWidth, minimumHeight; uiWindowsSizing sizing; int labelht, labelyoff; @@ -152,18 +152,18 @@ static void uiFormSyncEnableState(uiWindowsControl *c, int enabled) uiWindowsControlDefaultSetParentHWND(uiForm) -static void uiFormMinimumSize(uiWindowsControl *c, intmax_t *width, intmax_t *height) +static void uiFormMinimumSize(uiWindowsControl *c, int *width, int *height) { uiForm *f = uiForm(c); int xpadding, ypadding; - uintmax_t nStretchy; + int nStretchy; // these two contain the largest minimum width and height of all stretchy controls in the form // all stretchy controls will use this value to determine the final minimum size - intmax_t maxLabelWidth, maxControlWidth; - intmax_t maxStretchyHeight; - intmax_t labelwid; - uintmax_t i; - intmax_t minimumWidth, minimumHeight; + int maxLabelWidth, maxControlWidth; + int maxStretchyHeight; + int labelwid; + int i; + int minimumWidth, minimumHeight; uiWindowsSizing sizing; *width = 0; @@ -226,7 +226,7 @@ static void formArrangeChildren(uiForm *f) { LONG_PTR controlID; HWND insertAfter; - uintmax_t i; + int i; controlID = 100; insertAfter = NULL; diff --git a/windows/group.cpp b/windows/group.cpp index b1e4c653..9e2cf6e1 100644 --- a/windows/group.cpp +++ b/windows/group.cpp @@ -89,11 +89,11 @@ static void uiGroupSyncEnableState(uiWindowsControl *c, int enabled) uiWindowsControlDefaultSetParentHWND(uiGroup) -static void uiGroupMinimumSize(uiWindowsControl *c, intmax_t *width, intmax_t *height) +static void uiGroupMinimumSize(uiWindowsControl *c, int *width, int *height) { uiGroup *g = uiGroup(c); int mx, mtop, mbottom; - intmax_t labelWidth; + int labelWidth; *width = 0; *height = 0; @@ -164,7 +164,7 @@ static LRESULT CALLBACK groupSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM uiGroup *g = uiGroup(dwRefData); WINDOWPOS *wp = (WINDOWPOS *) lParam; MINMAXINFO *mmi = (MINMAXINFO *) lParam; - intmax_t minwid, minht; + int minwid, minht; LRESULT lResult; if (handleParentMessages(hwnd, uMsg, wParam, lParam, &lResult) != FALSE) diff --git a/windows/label.cpp b/windows/label.cpp index 6a401b0f..d74b7d18 100644 --- a/windows/label.cpp +++ b/windows/label.cpp @@ -11,7 +11,7 @@ uiWindowsControlAllDefaults(uiLabel) // via http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing #define labelHeight 8 -static void uiLabelMinimumSize(uiWindowsControl *c, intmax_t *width, intmax_t *height) +static void uiLabelMinimumSize(uiWindowsControl *c, int *width, int *height) { uiLabel *l = uiLabel(c); uiWindowsSizing sizing; diff --git a/windows/multilineentry.cpp b/windows/multilineentry.cpp index 7151cdd0..a32960cb 100644 --- a/windows/multilineentry.cpp +++ b/windows/multilineentry.cpp @@ -40,7 +40,7 @@ uiWindowsControlAllDefaultsExceptDestroy(uiMultilineEntry) // LONGTERM change this for multiline text boxes (longterm because how?) #define entryHeight 14 -static void uiMultilineEntryMinimumSize(uiWindowsControl *c, intmax_t *width, intmax_t *height) +static void uiMultilineEntryMinimumSize(uiWindowsControl *c, int *width, int *height) { uiMultilineEntry *e = uiMultilineEntry(c); uiWindowsSizing sizing; diff --git a/windows/progressbar.cpp b/windows/progressbar.cpp index 2700a790..d16569ad 100644 --- a/windows/progressbar.cpp +++ b/windows/progressbar.cpp @@ -12,7 +12,7 @@ uiWindowsControlAllDefaults(uiProgressBar) #define pbarWidth 237 #define pbarHeight 8 -static void uiProgressBarMinimumSize(uiWindowsControl *c, intmax_t *width, intmax_t *height) +static void uiProgressBarMinimumSize(uiWindowsControl *c, int *width, int *height) { uiProgressBar *p = uiProgressBar(c); uiWindowsSizing sizing; diff --git a/windows/separator.cpp b/windows/separator.cpp index 9c4be072..7c15a6d8 100644 --- a/windows/separator.cpp +++ b/windows/separator.cpp @@ -15,7 +15,7 @@ uiWindowsControlAllDefaults(uiSeparator) // via https://msdn.microsoft.com/en-us/library/windows/desktop/bb226818%28v=vs.85%29.aspx #define separatorHeight 1 -static void uiSeparatorMinimumSize(uiWindowsControl *c, intmax_t *width, intmax_t *height) +static void uiSeparatorMinimumSize(uiWindowsControl *c, int *width, int *height) { uiSeparator *s = uiSeparator(c); uiWindowsSizing sizing; diff --git a/windows/window.cpp b/windows/window.cpp index 0906619a..0edd7183 100644 --- a/windows/window.cpp +++ b/windows/window.cpp @@ -64,7 +64,7 @@ static LRESULT CALLBACK windowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARA CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam; WINDOWPOS *wp = (WINDOWPOS *) lParam; MINMAXINFO *mmi = (MINMAXINFO *) lParam; - intmax_t width, height; + int width, height; LRESULT lResult; ww = GetWindowLongPtrW(hwnd, GWLP_USERDATA); @@ -221,7 +221,7 @@ uiWindowsControlDefaultSyncEnableState(uiWindow) // TODO uiWindowsControlDefaultSetParentHWND(uiWindow) -static void uiWindowMinimumSize(uiWindowsControl *c, intmax_t *width, intmax_t *height) +static void uiWindowMinimumSize(uiWindowsControl *c, int *width, int *height) { uiWindow *w = uiWindow(c); uiWindowsSizing sizing; @@ -375,7 +375,7 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) // this cannot queue a resize because it's called by the resize handler void ensureMinimumWindowSize(uiWindow *w) { - intmax_t width, height; + int width, height; RECT r; uiWindowsControlMinimumSize(uiWindowsControl(w), &width, &height); From fa4542f217f1557490ba4b7dddf493ddf616b5d7 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 13 Jun 2016 22:00:18 -0400 Subject: [PATCH 0319/1329] Got rid of the last bit of intmax_t. Now to verify everything. --- windows/area.hpp | 4 ++-- windows/areascroll.cpp | 20 ++++++++++---------- windows/container.cpp | 4 +++- windows/menu.cpp | 28 ++++++++++++++-------------- 4 files changed, 29 insertions(+), 27 deletions(-) diff --git a/windows/area.hpp b/windows/area.hpp index e733b84e..86a62de6 100644 --- a/windows/area.hpp +++ b/windows/area.hpp @@ -13,8 +13,8 @@ struct uiArea { BOOL scrolling; int scrollWidth; int scrollHeight; - intmax_t hscrollpos; - intmax_t vscrollpos; + int hscrollpos; + int vscrollpos; int hwheelCarry; int vwheelCarry; diff --git a/windows/areascroll.cpp b/windows/areascroll.cpp index 3e05d0aa..f18d0ad8 100644 --- a/windows/areascroll.cpp +++ b/windows/areascroll.cpp @@ -12,14 +12,14 @@ // - error if these are called without scrollbars? struct scrollParams { - intmax_t *pos; - intmax_t pagesize; - intmax_t length; + int *pos; + int pagesize; + int length; int *wheelCarry; UINT wheelSPIAction; }; -static void scrollto(uiArea *a, int which, struct scrollParams *p, intmax_t pos) +static void scrollto(uiArea *a, int which, struct scrollParams *p, int pos) { SCROLLINFO si; @@ -48,14 +48,14 @@ static void scrollto(uiArea *a, int which, struct scrollParams *p, intmax_t pos) SetScrollInfo(a->hwnd, which, &si, TRUE); } -static void scrollby(uiArea *a, int which, struct scrollParams *p, intmax_t delta) +static void scrollby(uiArea *a, int which, struct scrollParams *p, int delta) { scrollto(a, which, p, *(p->pos) + delta); } static void scroll(uiArea *a, int which, struct scrollParams *p, WPARAM wParam, LPARAM lParam) { - intmax_t pos; + int pos; SCROLLINFO si; pos = *(p->pos); @@ -134,7 +134,7 @@ static void hscrollParams(uiArea *a, struct scrollParams *p) p->wheelSPIAction = SPI_GETWHEELSCROLLCHARS; } -static void hscrollto(uiArea *a, intmax_t pos) +static void hscrollto(uiArea *a, int pos) { struct scrollParams p; @@ -142,7 +142,7 @@ static void hscrollto(uiArea *a, intmax_t pos) scrollto(a, SB_HORZ, &p, pos); } -static void hscrollby(uiArea *a, intmax_t delta) +static void hscrollby(uiArea *a, int delta) { struct scrollParams p; @@ -179,7 +179,7 @@ static void vscrollParams(uiArea *a, struct scrollParams *p) p->wheelSPIAction = SPI_GETWHEELSCROLLLINES; } -static void vscrollto(uiArea *a, intmax_t pos) +static void vscrollto(uiArea *a, int pos) { struct scrollParams p; @@ -187,7 +187,7 @@ static void vscrollto(uiArea *a, intmax_t pos) scrollto(a, SB_VERT, &p, pos); } -static void vscrollby(uiArea *a, intmax_t delta) +static void vscrollby(uiArea *a, int delta) { struct scrollParams p; diff --git a/windows/container.cpp b/windows/container.cpp index 31c93a23..9ec1e280 100644 --- a/windows/container.cpp +++ b/windows/container.cpp @@ -6,6 +6,8 @@ // - uiRadioButtons // - uiSpinbox // - uiTab +// - uiForm +// - uiGrid struct containerInit { uiWindowsControl *c; @@ -23,7 +25,7 @@ static LRESULT CALLBACK containerWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LP struct containerInit *init; uiWindowsControl *c; void (*onResize)(uiWindowsControl *); - intmax_t minwid, minht; + int minwid, minht; LRESULT lResult; if (handleParentMessages(hwnd, uMsg, wParam, lParam, &lResult) != FALSE) diff --git a/windows/menu.cpp b/windows/menu.cpp index bfac0129..6112fc13 100644 --- a/windows/menu.cpp +++ b/windows/menu.cpp @@ -4,8 +4,8 @@ // LONGTERM migrate to std::vector static uiMenu **menus = NULL; -static uintmax_t len = 0; -static uintmax_t cap = 0; +static size_t len = 0; +static size_t cap = 0; static BOOL menusFinalized = FALSE; static WORD curID = 100; // start somewhere safe static BOOL hasQuit = FALSE; @@ -15,8 +15,8 @@ static BOOL hasAbout = FALSE; struct uiMenu { WCHAR *name; uiMenuItem **items; - uintmax_t len; - uintmax_t cap; + size_t len; + size_t cap; }; struct uiMenuItem { @@ -28,8 +28,8 @@ struct uiMenuItem { BOOL disabled; // template for new instances; kept in sync with everything else BOOL checked; HMENU *hmenus; - uintmax_t len; - uintmax_t cap; + size_t len; + size_t cap; }; enum { @@ -45,7 +45,7 @@ enum { static void sync(uiMenuItem *item) { - uintmax_t i; + size_t i; MENUITEMINFOW mi; ZeroMemory(&mi, sizeof (MENUITEMINFOW)); @@ -246,7 +246,7 @@ static void appendMenuItem(HMENU menu, uiMenuItem *item) static HMENU makeMenu(uiMenu *m) { HMENU menu; - uintmax_t i; + size_t i; menu = CreatePopupMenu(); if (menu == NULL) @@ -260,7 +260,7 @@ HMENU makeMenubar(void) { HMENU menubar; HMENU menu; - uintmax_t i; + size_t i; menusFinalized = TRUE; @@ -281,7 +281,7 @@ void runMenuEvent(WORD id, uiWindow *w) { uiMenu *m; uiMenuItem *item; - uintmax_t i, j; + size_t i, j; // this isn't optimal, but it works, and it should be just fine for most cases for (i = 0; i < len; i++) { @@ -306,9 +306,9 @@ found: static void freeMenu(uiMenu *m, HMENU submenu) { - uintmax_t i; + size_t i; uiMenuItem *item; - uintmax_t j; + size_t j; for (i = 0; i < m->len; i++) { item = m->items[i]; @@ -326,7 +326,7 @@ static void freeMenu(uiMenu *m, HMENU submenu) void freeMenubar(HMENU menubar) { - uintmax_t i; + size_t i; MENUITEMINFOW mi; for (i = 0; i < len; i++) { @@ -344,7 +344,7 @@ void uninitMenus(void) { uiMenu *m; uiMenuItem *item; - uintmax_t i, j; + size_t i, j; for (i = 0; i < len; i++) { m = menus[i]; From 207340f16f1566adfcfb366cb3325d6340aafddd Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 13 Jun 2016 22:42:11 -0400 Subject: [PATCH 0320/1329] Finished the migration. --- README.md | 3 +++ darwin/form.m | 1 - darwin/grid.m | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8ab69cbf..a77e0883 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,9 @@ This README is being written.
*Note that today's entry may be updated later today.* +* **13 June 2016** + * `intmax_t` and `uintmax_t` are no longer used in libui at all; now we only have `int`. This should make things much easier for bindings. `int` should be at least 32 bits wide; this should be sufficient for all but the most extreme cases. + * **12 June 2016** * Added `uiGrid`, a new container control that arranges controls in rows and columns, with stretchy ("expanding") rows, stretchy ("expanding") columns, cells that span rows and columns, and cells whose content is aligned in either direction rather than just filling. It's quite powerful, is it? =P diff --git a/darwin/form.m b/darwin/form.m index cdc3563b..acc33a3e 100644 --- a/darwin/form.m +++ b/darwin/form.m @@ -212,7 +212,6 @@ struct uiForm { CGFloat padding; NSView *prev, *prevlabel; NSLayoutConstraint *c; - NSLayoutRelation relation; [self removeOurConstraints]; if ([self->children count] == 0) diff --git a/darwin/grid.m b/darwin/grid.m index 93f9e9fe..40021a00 100644 --- a/darwin/grid.m +++ b/darwin/grid.m @@ -421,7 +421,6 @@ struct uiGrid { - (void)append:(gridChild *)gc { - NSLayoutPriority priority; BOOL update; int oldn; From 48546f6b444badb52357f79d185d695df02f767b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 13 Jun 2016 22:44:11 -0400 Subject: [PATCH 0321/1329] Clarified the update. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a77e0883..1c06f42f 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ This README is being written.
*Note that today's entry may be updated later today.* * **13 June 2016** - * `intmax_t` and `uintmax_t` are no longer used in libui at all; now we only have `int`. This should make things much easier for bindings. `int` should be at least 32 bits wide; this should be sufficient for all but the most extreme cases. + * `intmax_t` and `uintmax_t` are no longer used for libui API functions; now we use `int`. This should make things much easier for bindings. `int` should be at least 32 bits wide; this should be sufficient for all but the most extreme cases. * **12 June 2016** * Added `uiGrid`, a new container control that arranges controls in rows and columns, with stretchy ("expanding") rows, stretchy ("expanding") columns, cells that span rows and columns, and cells whose content is aligned in either direction rather than just filling. It's quite powerful, is it? =P From d54f7dd6827411993e8eb59aec1cd00abf9c0fae Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 14 Jun 2016 10:37:19 -0400 Subject: [PATCH 0322/1329] Added a facility to the OS X port to be notified when a child's visibility has changed; this is necessary for implementing hiding and showing properly. --- darwin/box.m | 7 +++++++ darwin/control.m | 14 ++++++++++++++ darwin/form.m | 7 +++++++ darwin/grid.m | 7 +++++++ darwin/group.m | 7 +++++++ darwin/tab.m | 7 +++++++ darwin/window.m | 7 +++++++ test/page11.c | 1 + ui_darwin.h | 16 ++++++++++++++-- 9 files changed, 71 insertions(+), 2 deletions(-) diff --git a/darwin/box.m b/darwin/box.m index 4b688e65..aefd17aa 100644 --- a/darwin/box.m +++ b/darwin/box.m @@ -396,6 +396,13 @@ static void uiBoxChildEdgeHuggingChanged(uiDarwinControl *c) uiDarwinControlDefaultHuggingPriority(uiBox, view) uiDarwinControlDefaultSetHuggingPriority(uiBox, view) +static void uiBoxChildVisibilityChanged(uiDarwinControl *c) +{ + uiBox *b = uiBox(c); + + [b->view establishOurConstraints]; +} + void uiBoxAppend(uiBox *b, uiControl *c, int stretchy) { // LONGTERM on other platforms diff --git a/darwin/control.m b/darwin/control.m index 4451ee9a..9eaf47a2 100644 --- a/darwin/control.m +++ b/darwin/control.m @@ -36,6 +36,11 @@ void uiDarwinControlSetHuggingPriority(uiDarwinControl *c, NSLayoutPriority prio (*(c->SetHuggingPriority))(c, priority, orientation); } +void uiDarwinControlChildVisibilityChanged(uiDarwinControl *c) +{ + (*(c->ChildVisibilityChanged))(c); +} + void uiDarwinSetControlFont(NSControl *c, NSControlSize size) { [c setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:size]]]; @@ -68,3 +73,12 @@ void uiDarwinNotifyEdgeHuggingChanged(uiDarwinControl *c) if (parent != NULL) uiDarwinControlChildEdgeHuggingChanged(uiDarwinControl(parent)); } + +void uiDarwinNotifyVisibilityChanged(uiDarwinControl *c) +{ + uiControl *parent; + + parent = uiControlParent(uiControl(c)); + if (parent != NULL) + uiDarwinControlChildVisibilityChanged(uiDarwinControl(parent)); +} diff --git a/darwin/form.m b/darwin/form.m index acc33a3e..75edf23a 100644 --- a/darwin/form.m +++ b/darwin/form.m @@ -476,6 +476,13 @@ static void uiFormChildEdgeHuggingChanged(uiDarwinControl *c) uiDarwinControlDefaultHuggingPriority(uiForm, view) uiDarwinControlDefaultSetHuggingPriority(uiForm, view) +static void uiFormChildVisibilityChanged(uiDarwinControl *c) +{ + uiForm *f = uiForm(c); + + [f->view establishOurConstraints]; +} + void uiFormAppend(uiForm *f, const char *label, uiControl *c, int stretchy) { // LONGTERM on other platforms diff --git a/darwin/grid.m b/darwin/grid.m index 40021a00..b6d6a513 100644 --- a/darwin/grid.m +++ b/darwin/grid.m @@ -590,6 +590,13 @@ static void uiGridChildEdgeHuggingChanged(uiDarwinControl *c) uiDarwinControlDefaultHuggingPriority(uiGrid, view) uiDarwinControlDefaultSetHuggingPriority(uiGrid, view) +static void uiGridChildVisibilityChanged(uiDarwinControl *c) +{ + uiGrid *g = uiGrid(c); + + [g->view establishOurConstraints]; +} + static gridChild *toChild(uiControl *c, int xspan, int yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign) { gridChild *gc; diff --git a/darwin/group.m b/darwin/group.m index 8dba62c3..0050bbdd 100644 --- a/darwin/group.m +++ b/darwin/group.m @@ -117,6 +117,13 @@ static void uiGroupSetHuggingPriority(uiDarwinControl *c, NSLayoutPriority prior uiDarwinNotifyEdgeHuggingChanged(uiDarwinControl(g)); } +static void uiGroupChildVisibilityChanged(uiDarwinControl *c) +{ + uiGroup *g = uiGroup(c); + + groupRelayout(g); +} + char *uiGroupTitle(uiGroup *g) { return uiDarwinNSStringToText([g->box title]); diff --git a/darwin/tab.m b/darwin/tab.m index 0d03f3e6..3d2ca9f0 100644 --- a/darwin/tab.m +++ b/darwin/tab.m @@ -180,6 +180,13 @@ static void uiTabSetHuggingPriority(uiDarwinControl *c, NSLayoutPriority priorit uiDarwinNotifyEdgeHuggingChanged(uiDarwinControl(t)); } +static void uiTabChildVisibilityChanged(uiDarwinControl *c) +{ + uiTab *t = uiTab(c); + + tabRelayout(t); +} + void uiTabAppend(uiTab *t, const char *name, uiControl *child) { uiTabInsertAt(t, name, [t->pages count], child); diff --git a/darwin/window.m b/darwin/window.m index 2d8d2e45..ffb6b30d 100644 --- a/darwin/window.m +++ b/darwin/window.m @@ -187,6 +187,13 @@ uiDarwinControlDefaultHuggingPriority(uiWindow, window) uiDarwinControlDefaultSetHuggingPriority(uiWindow, window) // end TODO +static void uiWindowChildVisibilityChanged(uiDarwinControl *c) +{ + uiWindow *w = uiWindow(c); + + windowRelayout(w); +} + char *uiWindowTitle(uiWindow *w) { return uiDarwinNSStringToText([w->window title]); diff --git a/test/page11.c b/test/page11.c index cbe3d6cd..02ad213a 100644 --- a/test/page11.c +++ b/test/page11.c @@ -2,6 +2,7 @@ #include "test.h" // TODO add a test for childless windows +// TODO add tests for contianers with all controls hidden static uiGroup *newg(const char *n, int s) { diff --git a/ui_darwin.h b/ui_darwin.h index 81e71f2c..c9c6ad54 100644 --- a/ui_darwin.h +++ b/ui_darwin.h @@ -24,6 +24,7 @@ struct uiDarwinControl { void (*ChildEdgeHuggingChanged)(uiDarwinControl *); NSLayoutPriority (*HuggingPriority)(uiDarwinControl *, NSLayoutConstraintOrientation); void (*SetHuggingPriority)(uiDarwinControl *, NSLayoutPriority, NSLayoutConstraintOrientation); + void (*ChildVisibilityChanged)(uiDarwinControl *); }; #define uiDarwinControl(this) ((uiDarwinControl *) (this)) // TODO document @@ -34,6 +35,7 @@ _UI_EXTERN BOOL uiDarwinControlHugsBottom(uiDarwinControl *); _UI_EXTERN void uiDarwinControlChildEdgeHuggingChanged(uiDarwinControl *); _UI_EXTERN NSLayoutPriority uiDarwinControlHuggingPriority(uiDarwinControl *, NSLayoutConstraintOrientation); _UI_EXTERN void uiDarwinControlSetHuggingPriority(uiDarwinControl *, NSLayoutPriority, NSLayoutConstraintOrientation); +_UI_EXTERN void uiDarwinControlChildVisibilityChanged(uiDarwinControl *); #define uiDarwinControlDefaultDestroy(type, handlefield) \ static void type ## Destroy(uiControl *c) \ @@ -72,12 +74,14 @@ _UI_EXTERN void uiDarwinControlSetHuggingPriority(uiDarwinControl *, NSLayoutPri { \ uiDarwinControl(c)->visible = YES; \ [type(c)->handlefield setHidden:NO]; \ + uiDarwinNotifyVisibilityChanged(uiDarwinControl(c)); \ } #define uiDarwinControlDefaultHide(type, handlefield) \ static void type ## Hide(uiControl *c) \ { \ uiDarwinControl(c)->visible = NO; \ [type(c)->handlefield setHidden:YES]; \ + uiDarwinNotifyVisibilityChanged(uiDarwinControl(c)); \ } #define uiDarwinControlDefaultEnabled(type, handlefield) \ static int type ## Enabled(uiControl *c) \ @@ -102,7 +106,7 @@ _UI_EXTERN void uiDarwinControlSetHuggingPriority(uiDarwinControl *, NSLayoutPri if (uiDarwinShouldStopSyncEnableState(c, enabled)) \ return; \ if ([type(c)->handlefield respondsToSelector:@selector(setEnabled:)]) \ - [((id) type(c)->handlefield) setEnabled:enabled]; /* id cast to make compiler happy; thanks mikeash in irc.freenode.net/#macdev */ \ + [((id) (type(c)->handlefield)) setEnabled:enabled]; /* id cast to make compiler happy; thanks mikeash in irc.freenode.net/#macdev */ \ } #define uiDarwinControlDefaultSetSuperview(type, handlefield) \ static void type ## SetSuperview(uiDarwinControl *c, NSView *superview) \ @@ -138,6 +142,11 @@ _UI_EXTERN void uiDarwinControlSetHuggingPriority(uiDarwinControl *, NSLayoutPri { \ [type(c)->handlefield setContentHuggingPriority:priority forOrientation:orientation]; \ } +#define uiDarwinControlDefaultChildVisibilityChanged(type, handlefield) \ + static void type ## ChildVisibilityChanged(uiDarwinControl *c) \ + { \ + /* do nothing */ \ + } #define uiDarwinControlAllDefaultsExceptDestroy(type, handlefield) \ uiDarwinControlDefaultHandle(type, handlefield) \ @@ -156,7 +165,8 @@ _UI_EXTERN void uiDarwinControlSetHuggingPriority(uiDarwinControl *, NSLayoutPri uiDarwinControlDefaultHugsBottom(type, handlefield) \ uiDarwinControlDefaultChildEdgeHuggingChanged(type, handlefield) \ uiDarwinControlDefaultHuggingPriority(type, handlefield) \ - uiDarwinControlDefaultSetHuggingPriority(type, handlefield) + uiDarwinControlDefaultSetHuggingPriority(type, handlefield) \ + uiDarwinControlDefaultChildVisibilityChanged(type, handlefield) #define uiDarwinControlAllDefaults(type, handlefield) \ uiDarwinControlDefaultDestroy(type, handlefield) \ @@ -183,6 +193,7 @@ _UI_EXTERN void uiDarwinControlSetHuggingPriority(uiDarwinControl *, NSLayoutPri uiDarwinControl(var)->ChildEdgeHuggingChanged = type ## ChildEdgeHuggingChanged; \ uiDarwinControl(var)->HuggingPriority = type ## HuggingPriority; \ uiDarwinControl(var)->SetHuggingPriority = type ## SetHuggingPriority; \ + uiDarwinControl(var)->ChildVisibilityChanged = type ## ChildVisibilityChanged; \ uiDarwinControl(var)->visible = YES; \ uiDarwinControl(var)->enabled = YES; // TODO document @@ -199,6 +210,7 @@ _UI_EXTERN BOOL uiDarwinShouldStopSyncEnableState(uiDarwinControl *, BOOL); // TODO document _UI_EXTERN void uiDarwinNotifyEdgeHuggingChanged(uiDarwinControl *); +_UI_EXTERN void uiDarwinNotifyVisibilityChanged(uiDarwinControl *c); // TODO document // TODO document that values should not be cached From 44b4fa9c684b97badce9c27301d7a805001ef855 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 14 Jun 2016 10:54:16 -0400 Subject: [PATCH 0323/1329] Fixed hiding on OS X uiBox. --- darwin/box.m | 46 +++++++++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/darwin/box.m b/darwin/box.m index aefd17aa..18d536d5 100644 --- a/darwin/box.m +++ b/darwin/box.m @@ -1,6 +1,8 @@ // 15 august 2015 #import "uipriv_darwin.h" +// TODO hiding all stretchy controls still hugs trailing edge + @interface boxChild : NSObject @property uiControl *c; @property BOOL stretchy; @@ -14,7 +16,6 @@ NSMutableArray *children; BOOL vertical; int padded; - int nStretchy; NSLayoutConstraint *first; NSMutableArray *inBetweens; @@ -41,6 +42,7 @@ - (void)setPadded:(int)p; - (BOOL)hugsTrailing; - (BOOL)hugsBottom; +- (int)nStretchy; @end struct uiBox { @@ -68,7 +70,6 @@ struct uiBox { self->vertical = vert; self->padded = 0; self->children = [NSMutableArray new]; - self->nStretchy = 0; self->inBetweens = [NSMutableArray new]; self->otherConstraints = [NSMutableArray new]; @@ -163,6 +164,8 @@ struct uiBox { // first arrange in the primary direction prev = nil; for (bc in self->children) { + if (!uiControlVisible(bc.c)) + continue; if (prev == nil) { // first view self->first = mkConstraint(self, self->primaryStart, NSLayoutRelationEqual, @@ -184,6 +187,8 @@ struct uiBox { [self->inBetweens addObject:c]; prev = [bc view]; } + if (prev == nil) // no control visible; act as if no controls + return; self->last = mkConstraint(prev, self->primaryEnd, NSLayoutRelationEqual, self, self->primaryEnd, @@ -197,6 +202,8 @@ struct uiBox { if (!self->vertical) hugsSecondary = uiDarwinControlHugsBottom; for (bc in self->children) { + if (!uiControlVisible(bc.c)) + continue; c = mkConstraint(self, self->secondaryStart, NSLayoutRelationEqual, [bc view], self->secondaryStart, @@ -225,10 +232,12 @@ struct uiBox { } // and make all stretchy controls the same size - if (self->nStretchy == 0) + if ([self nStretchy] == 0) return; prev = nil; // first stretchy view for (bc in self->children) { + if (!uiControlVisible(bc.c)) + continue; if (!bc.stretchy) continue; if (prev == nil) { @@ -272,15 +281,13 @@ struct uiBox { // make sure controls don't hug their secondary direction so they fill the width of the view uiDarwinControlSetHuggingPriority(uiDarwinControl(bc.c), NSLayoutPriorityDefaultLow, self->secondaryOrientation); + oldnStretchy = [self nStretchy]; [self->children addObject:bc]; [self establishOurConstraints]; - if (bc.stretchy) { - oldnStretchy = self->nStretchy; - self->nStretchy++; + if (bc.stretchy) if (oldnStretchy == 0) uiDarwinNotifyEdgeHuggingChanged(uiDarwinControl(self->b)); - } [bc release]; // we don't need the initial reference now } @@ -302,11 +309,9 @@ struct uiBox { [self->children removeObjectAtIndex:n]; [self establishOurConstraints]; - if (stretchy) { - self->nStretchy--; - if (self->nStretchy == 0) + if (stretchy) + if ([self nStretchy] == 0) uiDarwinNotifyEdgeHuggingChanged(uiDarwinControl(self->b)); - } } - (int)isPadded @@ -329,14 +334,29 @@ struct uiBox { { if (self->vertical) // always hug if vertical return YES; - return self->nStretchy != 0; + return [self nStretchy] != 0; } - (BOOL)hugsBottom { if (!self->vertical) // always hug if horizontal return YES; - return self->nStretchy != 0; + return [self nStretchy] != 0; +} + +- (int)nStretchy +{ + boxChild *bc; + int n; + + n = 0; + for (bc in self->children) { + if (!uiControlVisible(bc.c)) + continue; + if (bc.stretchy) + n++; + } + return n; } @end From cb81518e0fbb863d85e65d9761c48cccf4aaa537 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 14 Jun 2016 11:18:38 -0400 Subject: [PATCH 0324/1329] Implemented proper hiding and showing behavior on OS X on uiForm and uiGrid. --- README.md | 5 +++- darwin/form.m | 35 ++++++++++++++++++++----- darwin/grid.m | 71 +++++++++++++++++++++++++++++++++++++++------------ test/page13.c | 16 +++++++++++- 4 files changed, 101 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 1c06f42f..04fa0104 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,10 @@ This README is being written.
## Updates -*Note that today's entry may be updated later today.* +*Note that today's entry may be updated later today Eastern Time.* + +* **14 June 2016** + * uiDarwinControl now has a `ChildVisibilityChanged()` method and a corresponding `NotifyVisibilityChanged()` function that is called by the default show/hide handlers. This is used to make visibility changes work on OS X; uiBox, uiForm, and uiGrid all respect these now. * **13 June 2016** * `intmax_t` and `uintmax_t` are no longer used for libui API functions; now we use `int`. This should make things much easier for bindings. `int` should be at least 32 bits wide; this should be sufficient for all but the most extreme cases. diff --git a/darwin/form.m b/darwin/form.m index 75edf23a..966a1495 100644 --- a/darwin/form.m +++ b/darwin/form.m @@ -23,7 +23,6 @@ uiForm *f; NSMutableArray *children; int padded; - int nStretchy; NSLayoutConstraint *first; NSMutableArray *inBetweens; @@ -45,6 +44,7 @@ - (void)setPadded:(int)p; - (BOOL)hugsTrailing; - (BOOL)hugsBottom; +- (int)nStretchy; @end struct uiForm { @@ -123,7 +123,6 @@ struct uiForm { self->f = ff; self->padded = 0; self->children = [NSMutableArray new]; - self->nStretchy = 0; self->inBetweens = [NSMutableArray new]; self->widths = [NSMutableArray new]; @@ -221,6 +220,9 @@ struct uiForm { // first arrange the children vertically and make them the same width prev = nil; for (fc in self->children) { + [fc setHidden:!uiControlVisible(fc.c)]; + if (!uiControlVisible(fc.c)) + continue; if (prev == nil) { // first view self->first = mkConstraint(self, NSLayoutAttributeTop, NSLayoutRelationEqual, @@ -259,6 +261,8 @@ struct uiForm { prev = [fc view]; prevlabel = fc; } + if (prev == nil) // all hidden; act as if nothing there + return; self->last = mkConstraint(prev, NSLayoutAttributeBottom, NSLayoutRelationEqual, self, NSLayoutAttributeBottom, @@ -269,6 +273,8 @@ struct uiForm { // now arrange the controls horizontally for (fc in self->children) { + if (!uiControlVisible(fc.c)) + continue; c = mkConstraint(self, NSLayoutAttributeLeading, NSLayoutRelationEqual, fc, NSLayoutAttributeLeading, @@ -313,6 +319,8 @@ struct uiForm { // and make all stretchy controls have the same height prev = nil; for (fc in self->children) { + if (!uiControlVisible(fc.c)) + continue; if (!fc.stretchy) continue; if (prev == nil) { @@ -375,15 +383,13 @@ struct uiForm { @"uiForm baseline constraint"); [self addConstraint:fc.baseline]; + oldnStretchy = [self nStretchy]; [self->children addObject:fc]; [self establishOurConstraints]; - if (fc.stretchy) { - oldnStretchy = self->nStretchy; - self->nStretchy++; + if (fc.stretchy) if (oldnStretchy == 0) uiDarwinNotifyEdgeHuggingChanged(uiDarwinControl(self->f)); - } [fc release]; // we don't need the initial reference now } @@ -416,7 +422,22 @@ struct uiForm { - (BOOL)hugsBottom { // only hug if we have stretchy - return self->nStretchy != 0; + return [self nStretchy] != 0; +} + +- (int)nStretchy +{ + formChild *fc; + int n; + + n = 0; + for (fc in self->children) { + if (!uiControlVisible(fc.c)) + continue; + if (fc.stretchy) + n++; + } + return n; } @end diff --git a/darwin/grid.m b/darwin/grid.m index b6d6a513..200c2a1b 100644 --- a/darwin/grid.m +++ b/darwin/grid.m @@ -25,8 +25,6 @@ uiGrid *g; NSMutableArray *children; int padded; - int nhexpand; - int nvexpand; NSMutableArray *edges; NSMutableArray *inBetweens; @@ -45,6 +43,8 @@ - (void)setPadded:(int)p; - (BOOL)hugsTrailing; - (BOOL)hugsBottom; +- (int)nhexpand; +- (int)nvexpand; @end struct uiGrid { @@ -70,8 +70,6 @@ struct uiGrid { self->g = gg; self->padded = 0; self->children = [NSMutableArray new]; - self->nhexpand = 0; - self->nvexpand = 0; self->edges = [NSMutableArray new]; self->inBetweens = [NSMutableArray new]; @@ -132,6 +130,7 @@ struct uiGrid { return uiDarwinPaddingAmount(NULL); } +// LONGTERM stop early if all controls are hidden - (void)establishOurConstraints { gridChild *gc; @@ -156,8 +155,11 @@ struct uiGrid { padding = [self paddingAmount]; // first, figure out the minimum and maximum row and column numbers + // ignore hidden controls first = YES; for (gc in self->children) { + if (!uiControlVisible(gc.c)) + continue; if (first) { xmin = gc.left; ymin = gc.top; @@ -180,6 +182,7 @@ struct uiGrid { // now build a topological map of the grid gg[y][x] // also figure out which cells contain spanned views so they can be ignored later + // treat hidden controls by keeping the indices -1 gg = (int **) uiAlloc(ycount * sizeof (int *), "int[][]"); gspan = (BOOL **) uiAlloc(ycount * sizeof (BOOL *), "BOOL[][]"); for (y = 0; y < ycount; y++) { @@ -190,6 +193,8 @@ struct uiGrid { } for (i = 0; i < [self->children count]; i++) { gc = (gridChild *) [self->children objectAtIndex:i]; + if (!uiControlVisible(gc.c)) + continue; for (y = gc.top; y < gc.top + gc.yspan; y++) for (x = gc.left; x < gc.left + gc.xspan; x++) { gg[y - ymin][x - xmin] = i; @@ -255,6 +260,8 @@ struct uiGrid { vexpand = (BOOL *) uiAlloc(ycount * sizeof (BOOL), "BOOL[]"); // first, which don't span for (gc in self->children) { + if (!uiControlVisible(gc.c)) + continue; if (gc.hexpand && gc.xspan == 1) hexpand[gc.left - xmin] = YES; if (gc.vexpand && gc.yspan == 1) @@ -263,6 +270,8 @@ struct uiGrid { // second, which do span // the way we handle this is simple: if none of the spanned rows/columns expand, make all rows/columns expand for (gc in self->children) { + if (!uiControlVisible(gc.c)) + continue; if (gc.hexpand && gc.xspan != 1) { doit = YES; for (x = gc.left; x < gc.left + gc.xspan; x++) @@ -391,6 +400,8 @@ struct uiGrid { for (gc in self->children) { NSLayoutPriority priority; + if (!uiControlVisible(gc.c)) + continue; if (hexpand[gc.left - xmin] || gc.xspan != 1) priority = NSLayoutPriorityDefaultLow; else @@ -422,7 +433,7 @@ struct uiGrid { - (void)append:(gridChild *)gc { BOOL update; - int oldn; + int oldnh, oldnv; uiControlSetParent(gc.c, uiControl(self->g)); uiDarwinControlSetSuperview(uiDarwinControl(gc.c), self); @@ -430,22 +441,18 @@ struct uiGrid { // no need to set priority here; that's done in establishOurConstraints + oldnh = [self nhexpand]; + oldnv = [self nvexpand]; [self->children addObject:gc]; [self establishOurConstraints]; update = NO; - if (gc.hexpand) { - oldn = self->nhexpand; - self->nhexpand++; - if (oldn == 0) + if (gc.hexpand) + if (oldnh == 0) update = YES; - } - if (gc.vexpand) { - oldn = self->nvexpand; - self->nvexpand++; - if (oldn == 0) + if (gc.vexpand) + if (oldnv == 0) update = YES; - } if (update) uiDarwinNotifyEdgeHuggingChanged(uiDarwinControl(self->g)); @@ -524,13 +531,43 @@ dispatch_get_main_queue(), - (BOOL)hugsTrailing { // only hug if we have horizontally expanding - return self->nhexpand != 0; + return [self nhexpand] != 0; } - (BOOL)hugsBottom { // only hug if we have vertically expanding - return self->nvexpand != 0; + return [self nvexpand] != 0; +} + +- (int)nhexpand +{ + gridChild *gc; + int n; + + n = 0; + for (gc in self->children) { + if (!uiControlVisible(gc.c)) + continue; + if (gc.hexpand) + n++; + } + return n; +} + +- (int)nvexpand +{ + gridChild *gc; + int n; + + n = 0; + for (gc in self->children) { + if (!uiControlVisible(gc.c)) + continue; + if (gc.vexpand) + n++; + } + return n; } @end diff --git a/test/page13.c b/test/page13.c index 514fe94f..519fb347 100644 --- a/test/page13.c +++ b/test/page13.c @@ -65,6 +65,16 @@ static void entryChanged(uiEntry *e, void *data) uiFreeText(text); } +static void showHide(uiButton *b, void *data) +{ + uiControl *c = uiControl(data); + + if (uiControlVisible(c)) + uiControlHide(c); + else + uiControlShow(c); +} + uiBox *makePage13(void) { uiBox *page13; @@ -95,7 +105,6 @@ uiBox *makePage13(void) uiBoxAppend(page13, uiControl(b), 0); f = newForm(); - uiBoxAppend(page13, uiControl(f), 1); e = uiNewPasswordEntry(); uiEntryOnChanged(e, entryChanged, "password"); @@ -107,5 +116,10 @@ uiBox *makePage13(void) uiFormAppend(f, "MLE", uiControl(uiNewMultilineEntry()), 1); + b = uiNewButton("Show/Hide"); + uiButtonOnClicked(b, showHide, e); + uiBoxAppend(page13, uiControl(b), 0); + uiBoxAppend(page13, uiControl(f), 1); + return page13; } From 6e5cf97623c54b67e2de142c1fcf13a3e5212528 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 14 Jun 2016 11:31:10 -0400 Subject: [PATCH 0325/1329] Tied uiForm label visibility on GTK+ to the visibility of the control. This is the only visibility change needed on GTK+, fortunately. --- unix/form.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/unix/form.c b/unix/form.c index 4e1f7fa0..02571d0c 100644 --- a/unix/form.c +++ b/unix/form.c @@ -8,6 +8,7 @@ struct formChild { GtkAlign oldhalign; gboolean oldvexpand; GtkAlign oldvalign; + GBinding *labelBinding; }; struct uiForm { @@ -82,7 +83,10 @@ void uiFormAppend(uiForm *f, const char *label, uiControl *c, int stretchy) gtk_grid_attach(f->grid, fc.label, 0, row, 1, 1); - gtk_widget_show_all(fc.label); + // and make them share visibility so if the control is hidden, so is its label + fc.labelBinding = g_object_bind_property(GTK_WIDGET(uiControlHandle(fc.c)), "visible", + fc.label, "visible", + G_BINDING_SYNC_CREATE); uiControlSetParent(fc.c, uiControl(f)); uiUnixControlSetContainer(uiUnixControl(fc.c), f->container, FALSE); From 997c8aac357a1d87d449379f655d1fb2016669e0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 14 Jun 2016 15:55:24 -0400 Subject: [PATCH 0326/1329] Implemented visibility change detection on Windows. Now to refine the actual implementation of hidden controls. --- README.md | 2 ++ doc/form | 1 + ui_windows.h | 16 +++++++++++++++- windows/box.cpp | 6 ++++++ windows/control.cpp | 13 +++++++++++++ windows/form.cpp | 6 ++++++ windows/grid.cpp | 6 ++++++ windows/group.cpp | 6 ++++++ windows/tab.cpp | 6 ++++++ windows/window.cpp | 6 ++++++ 10 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 doc/form diff --git a/README.md b/README.md index 04fa0104..445b3ce3 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,8 @@ This README is being written.
* **14 June 2016** * uiDarwinControl now has a `ChildVisibilityChanged()` method and a corresponding `NotifyVisibilityChanged()` function that is called by the default show/hide handlers. This is used to make visibility changes work on OS X; uiBox, uiForm, and uiGrid all respect these now. + * The same has been done on the Windows side as well. + * Hiding a control in a uiForm now hides its label on all platforms. * **13 June 2016** * `intmax_t` and `uintmax_t` are no longer used for libui API functions; now we use `int`. This should make things much easier for bindings. `int` should be at least 32 bits wide; this should be sufficient for all but the most extreme cases. diff --git a/doc/form b/doc/form new file mode 100644 index 00000000..f24a94dc --- /dev/null +++ b/doc/form @@ -0,0 +1 @@ +hiding a control also hides its label diff --git a/ui_windows.h b/ui_windows.h index 3ea511c9..69dda366 100644 --- a/ui_windows.h +++ b/ui_windows.h @@ -26,6 +26,7 @@ struct uiWindowsControl { void (*MinimumSizeChanged)(uiWindowsControl *); void (*LayoutRect)(uiWindowsControl *c, RECT *r); void (*AssignControlIDZOrder)(uiWindowsControl *, LONG_PTR *, HWND *); + void (*ChildVisibilityChanged)(uiWindowsControl *); }; #define uiWindowsControl(this) ((uiWindowsControl *) (this)) // TODO document @@ -35,6 +36,7 @@ _UI_EXTERN void uiWindowsControlMinimumSize(uiWindowsControl *, int *, int *); _UI_EXTERN void uiWindowsControlMinimumSizeChanged(uiWindowsControl *); _UI_EXTERN void uiWindowsControlLayoutRect(uiWindowsControl *, RECT *); _UI_EXTERN void uiWindowsControlAssignControlIDZOrder(uiWindowsControl *, LONG_PTR *, HWND *); +_UI_EXTERN void uiWindowsControlChildVisibilityChanged(uiWindowsControl *); // TODO document #define uiWindowsControlDefaultDestroy(type) \ @@ -74,12 +76,14 @@ _UI_EXTERN void uiWindowsControlAssignControlIDZOrder(uiWindowsControl *, LONG_P { \ uiWindowsControl(c)->visible = 1; \ ShowWindow(type(c)->hwnd, SW_SHOW); \ + uiWindowsControlNotifyVisibilityChanged(uiWindowsControl(c)); \ } #define uiWindowsControlDefaultHide(type) \ static void type ## Hide(uiControl *c) \ { \ uiWindowsControl(c)->visible = 0; \ ShowWindow(type(c)->hwnd, SW_HIDE); \ + uiWindowsControlNotifyVisibilityChanged(uiWindowsControl(c)); \ } #define uiWindowsControlDefaultEnabled(type) \ static int type ## Enabled(uiControl *c) \ @@ -131,6 +135,11 @@ _UI_EXTERN void uiWindowsControlAssignControlIDZOrder(uiWindowsControl *, LONG_P { \ uiWindowsEnsureAssignControlIDZOrder(type(c)->hwnd, controlID, insertAfter); \ } +#define uiWindowsControlDefaultChildVisibilityChanged(type) \ + static void type ## ChildVisibilityChanged(uiWindowsControl *c) \ + { \ + /* do nothing */ \ + } #define uiWindowsControlAllDefaultsExceptDestroy(type) \ uiWindowsControlDefaultHandle(type) \ @@ -147,7 +156,8 @@ _UI_EXTERN void uiWindowsControlAssignControlIDZOrder(uiWindowsControl *, LONG_P uiWindowsControlDefaultSetParentHWND(type) \ uiWindowsControlDefaultMinimumSizeChanged(type) \ uiWindowsControlDefaultLayoutRect(type) \ - uiWindowsControlDefaultAssignControlIDZOrder(type) + uiWindowsControlDefaultAssignControlIDZOrder(type) \ + uiWindowsControlDefaultChildVisibilityChanged(type) #define uiWindowsControlAllDefaults(type) \ uiWindowsControlDefaultDestroy(type) \ @@ -173,6 +183,7 @@ _UI_EXTERN void uiWindowsControlAssignControlIDZOrder(uiWindowsControl *, LONG_P uiWindowsControl(var)->MinimumSizeChanged = type ## MinimumSizeChanged; \ uiWindowsControl(var)->LayoutRect = type ## LayoutRect; \ uiWindowsControl(var)->AssignControlIDZOrder = type ## AssignControlIDZOrder; \ + uiWindowsControl(var)->ChildVisibilityChanged = type ## ChildVisibilityChanged; \ uiWindowsControl(var)->visible = 1; \ uiWindowsControl(var)->enabled = 1; // TODO document @@ -246,6 +257,9 @@ _UI_EXTERN void uiWindowsControlAssignSoleControlIDZOrder(uiWindowsControl *); // TODO document _UI_EXTERN BOOL uiWindowsShouldStopSyncEnableState(uiWindowsControl *c, int enabled); +// TODO document +_UI_EXTERN void uiWindowsControlNotifyVisibilityChanged(uiWindowsControl *c); + #ifdef __cplusplus } #endif diff --git a/windows/box.cpp b/windows/box.cpp index d87f73e2..334f20f2 100644 --- a/windows/box.cpp +++ b/windows/box.cpp @@ -226,6 +226,12 @@ static void uiBoxMinimumSizeChanged(uiWindowsControl *c) uiWindowsControlDefaultLayoutRect(uiBox) uiWindowsControlDefaultAssignControlIDZOrder(uiBox) +static void uiBoxChildVisibilityChanged(uiWindowsControl *c) +{ + // TODO eliminate the redundancy + uiWindowsControlMinimumSizeChanged(c); +} + static void boxArrangeChildren(uiBox *b) { LONG_PTR controlID; diff --git a/windows/control.cpp b/windows/control.cpp index d59132b9..ce953cf9 100644 --- a/windows/control.cpp +++ b/windows/control.cpp @@ -21,6 +21,7 @@ void uiWindowsControlMinimumSizeChanged(uiWindowsControl *c) (*(c->MinimumSizeChanged))(c); } +// TODO get rid of this void uiWindowsControlLayoutRect(uiWindowsControl *c, RECT *r) { (*(c->LayoutRect))(c, r); @@ -31,6 +32,11 @@ void uiWindowsControlAssignControlIDZOrder(uiWindowsControl *c, LONG_PTR *contro (*(c->AssignControlIDZOrder))(c, controlID, insertAfter); } +void uiWindowsControlChildVisibilityChanged(uiWindowsControl *c) +{ + (*(c->ChildVisibilityChanged))(c); +} + HWND uiWindowsEnsureCreateControlHWND(DWORD dwExStyle, LPCWSTR lpClassName, LPCWSTR lpWindowName, DWORD dwStyle, HINSTANCE hInstance, LPVOID lpParam, BOOL useStandardControlFont) { HWND hwnd; @@ -106,3 +112,10 @@ void uiWindowsControlContinueMinimumSizeChanged(uiWindowsControl *c) if (parent != NULL) uiWindowsControlMinimumSizeChanged(uiWindowsControl(parent)); } + +// TODO rename this nad the OS X this and hugging ones to NotifyChild +void uiWindowsControlNotifyVisibilityChanged(uiWindowsControl *c) +{ + // TODO we really need to figure this out; the duplication is a mess + uiWindowsControlContinueMinimumSizeChanged(c); +} diff --git a/windows/form.cpp b/windows/form.cpp index e15e9571..6c91834e 100644 --- a/windows/form.cpp +++ b/windows/form.cpp @@ -222,6 +222,12 @@ static void uiFormMinimumSizeChanged(uiWindowsControl *c) uiWindowsControlDefaultLayoutRect(uiForm) uiWindowsControlDefaultAssignControlIDZOrder(uiForm) +static void uiFormChildVisibilityChanged(uiWindowsControl *c) +{ + // TODO eliminate the redundancy + uiWindowsControlMinimumSizeChanged(c); +} + static void formArrangeChildren(uiForm *f) { LONG_PTR controlID; diff --git a/windows/grid.cpp b/windows/grid.cpp index bcf89980..280f6344 100644 --- a/windows/grid.cpp +++ b/windows/grid.cpp @@ -422,6 +422,12 @@ static void uiGridMinimumSizeChanged(uiWindowsControl *c) uiWindowsControlDefaultLayoutRect(uiGrid) uiWindowsControlDefaultAssignControlIDZOrder(uiGrid) +static void uiGridChildVisibilityChanged(uiWindowsControl *c) +{ + // TODO eliminate the redundancy + uiWindowsControlMinimumSizeChanged(c); +} + // must have called gridRecomputeMinMax() first static void gridArrangeChildren(uiGrid *g) { diff --git a/windows/group.cpp b/windows/group.cpp index 9e2cf6e1..8824c5a4 100644 --- a/windows/group.cpp +++ b/windows/group.cpp @@ -121,6 +121,12 @@ static void uiGroupMinimumSizeChanged(uiWindowsControl *c) uiWindowsControlDefaultLayoutRect(uiGroup) uiWindowsControlDefaultAssignControlIDZOrder(uiGroup) +static void uiGroupChildVisibilityChanged(uiWindowsControl *c) +{ + // TODO eliminate the redundancy + uiWindowsControlMinimumSizeChanged(c); +} + char *uiGroupTitle(uiGroup *g) { return uiWindowsWindowText(g->hwnd); diff --git a/windows/tab.cpp b/windows/tab.cpp index 2af2bff3..365f5a1f 100644 --- a/windows/tab.cpp +++ b/windows/tab.cpp @@ -166,6 +166,12 @@ static void uiTabMinimumSizeChanged(uiWindowsControl *c) uiWindowsControlDefaultLayoutRect(uiTab) uiWindowsControlDefaultAssignControlIDZOrder(uiTab) +static void uiTabChildVisibilityChanged(uiWindowsControl *c) +{ + // TODO eliminate the redundancy + uiWindowsControlMinimumSizeChanged(c); +} + static void tabArrangePages(uiTab *t) { LONG_PTR controlID = 100; diff --git a/windows/window.cpp b/windows/window.cpp index 0edd7183..411cb86e 100644 --- a/windows/window.cpp +++ b/windows/window.cpp @@ -260,6 +260,12 @@ static void uiWindowLayoutRect(uiWindowsControl *c, RECT *r) uiWindowsControlDefaultAssignControlIDZOrder(uiWindow) +static void uiWindowChildVisibilityChanged(uiWindowsControl *c) +{ + // TODO eliminate the redundancy + uiWindowsControlMinimumSizeChanged(c); +} + char *uiWindowTitle(uiWindow *w) { return uiWindowsWindowText(w->hwnd); From b6cb429d1a4fbd3afe9897885584c51687b27151 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 14 Jun 2016 16:06:39 -0400 Subject: [PATCH 0327/1329] Fixed uiBox hidden control nonsense on Windows. FINALLY. --- windows/box.cpp | 61 +++++++++++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/windows/box.cpp b/windows/box.cpp index 334f20f2..9567954b 100644 --- a/windows/box.cpp +++ b/windows/box.cpp @@ -37,6 +37,7 @@ static void boxRelayout(uiBox *b) int stretchywid, stretchyht; int i; int minimumWidth, minimumHeight; + int nVisible; uiWindowsSizing *d; if (b->controls->size() == 0) @@ -51,21 +52,16 @@ static void boxRelayout(uiBox *b) // -1) get this Box's padding boxPadding(b, &xpadding, &ypadding); - // 0) inset the available rect by the needed padding - // TODO this is incorrect if any controls are hidden - if (b->vertical) - height -= (b->controls->size() - 1) * ypadding; - else - width -= (b->controls->size() - 1) * xpadding; - // 1) get width and height of non-stretchy controls // this will tell us how much space will be left for stretchy controls stretchywid = width; stretchyht = height; nStretchy = 0; + nVisible = 0; for (struct boxChild &bc : *(b->controls)) { if (!uiControlVisible(bc.c)) continue; + nVisible++; if (bc.stretchy) { nStretchy++; continue; @@ -81,24 +77,35 @@ static void boxRelayout(uiBox *b) stretchywid -= minimumWidth; } } + if (nVisible == 0) // nothing to do + return; - // 2) now get the size of stretchy controls - if (nStretchy != 0) + // 2) now inset the available rect by the needed padding + if (b->vertical) { + height -= (nVisible - 1) * ypadding; + stretchyht -= (nVisible - 1) * ypadding; + } else { + width -= (nVisible - 1) * xpadding; + stretchywid -= (nVisible - 1) * xpadding; + } + + // 3) now get the size of stretchy controls + if (nStretchy != 0) { if (b->vertical) stretchyht /= nStretchy; else stretchywid /= nStretchy; - // TODO put this in the above if - for (struct boxChild &bc : *(b->controls)) { - if (!uiControlVisible(bc.c)) - continue; - if (bc.stretchy) { - bc.width = stretchywid; - bc.height = stretchyht; + for (struct boxChild &bc : *(b->controls)) { + if (!uiControlVisible(bc.c)) + continue; + if (bc.stretchy) { + bc.width = stretchywid; + bc.height = stretchyht; + } } } - // 3) now we can position controls + // 4) now we can position controls // first, make relative to the top-left corner of the container x = 0; y = 0; @@ -159,6 +166,7 @@ static void uiBoxMinimumSize(uiWindowsControl *c, int *width, int *height) int maxStretchyWidth, maxStretchyHeight; int i; int minimumWidth, minimumHeight; + int nVisible; uiWindowsSizing sizing; *width = 0; @@ -169,21 +177,16 @@ static void uiBoxMinimumSize(uiWindowsControl *c, int *width, int *height) // 0) get this Box's padding boxPadding(b, &xpadding, &ypadding); - // 1) initialize the desired rect with the needed padding - // TODO this is wrong if any controls are hidden - if (b->vertical) - *height = (b->controls->size() - 1) * ypadding; - else - *width = (b->controls->size() - 1) * xpadding; - - // 2) add in the size of non-stretchy controls and get (but not add in) the largest widths and heights of stretchy controls + // 1) add in the size of non-stretchy controls and get (but not add in) the largest widths and heights of stretchy controls // we still add in like direction of stretchy controls nStretchy = 0; maxStretchyWidth = 0; maxStretchyHeight = 0; + nVisible = 0; for (const struct boxChild &bc : *(b->controls)) { if (!uiControlVisible(bc.c)) continue; + nVisible++; uiWindowsControlMinimumSize(uiWindowsControl(bc.c), &minimumWidth, &minimumHeight); if (bc.stretchy) { nStretchy++; @@ -204,6 +207,14 @@ static void uiBoxMinimumSize(uiWindowsControl *c, int *width, int *height) *height = minimumHeight; } } + if (nVisible == 0) // just return 0x0 + return; + + // 2) now outset the desired rect with the needed padding + if (b->vertical) + *height += (nVisible - 1) * ypadding; + else + *width += (nVisible - 1) * xpadding; // 3) and now we can add in stretchy controls if (b->vertical) From 52bd3b2c3597018d22f355395a10ef0053c5f79a Mon Sep 17 00:00:00 2001 From: emersion Date: Tue, 14 Jun 2016 22:41:37 +0200 Subject: [PATCH 0328/1329] Adds uiFormDelete() --- darwin/form.m | 5 +++++ ui.h | 1 + unix/form.c | 25 +++++++++++++++++++++++++ windows/form.cpp | 12 ++++++++++++ 4 files changed, 43 insertions(+) diff --git a/darwin/form.m b/darwin/form.m index acc33a3e..3d27520b 100644 --- a/darwin/form.m +++ b/darwin/form.m @@ -485,6 +485,11 @@ void uiFormAppend(uiForm *f, const char *label, uiControl *c, int stretchy) [f->view append:toNSString(label) c:c stretchy:stretchy]; } +void uiFormDelete(uiForm *f, int n) +{ + [f->view delete:n]; +} + int uiFormPadded(uiForm *f) { return [f->view isPadded]; diff --git a/ui.h b/ui.h index 7527ed11..85c2d302 100644 --- a/ui.h +++ b/ui.h @@ -625,6 +625,7 @@ _UI_EXTERN uiColorButton *uiNewColorButton(void); typedef struct uiForm uiForm; #define uiForm(this) ((uiForm *) (this)) _UI_EXTERN void uiFormAppend(uiForm *f, const char *label, uiControl *c, int stretchy); +_UI_EXTERN void uiFormDelete(uiForm *f, int index); _UI_EXTERN int uiFormPadded(uiForm *f); _UI_EXTERN void uiFormSetPadded(uiForm *f, int padded); _UI_EXTERN uiForm *uiNewForm(void); diff --git a/unix/form.c b/unix/form.c index 4e1f7fa0..7aa1a675 100644 --- a/unix/form.c +++ b/unix/form.c @@ -3,6 +3,7 @@ struct formChild { uiControl *c; + int stretchy; GtkWidget *label; gboolean oldhexpand; GtkAlign oldhalign; @@ -54,6 +55,7 @@ void uiFormAppend(uiForm *f, const char *label, uiControl *c, int stretchy) fc.c = c; widget = GTK_WIDGET(uiControlHandle(fc.c)); + fc.stretchy = stretchy; fc.oldhexpand = gtk_widget_get_hexpand(widget); fc.oldhalign = gtk_widget_get_halign(widget); fc.oldvexpand = gtk_widget_get_vexpand(widget); @@ -95,6 +97,29 @@ void uiFormAppend(uiForm *f, const char *label, uiControl *c, int stretchy) NULL); } +void uiFormDelete(uiForm *f, int index) +{ + struct formChild *fc; + GtkWidget *widget; + + fc = ctrl(f, index); + widget = GTK_WIDGET(uiControlHandle(fc->c)); + + gtk_widget_destroy(fc->label); + + uiControlSetParent(fc->c, NULL); + uiUnixControlSetContainer(uiUnixControl(fc->c), f->container, TRUE); + + if (fc->stretchy) + gtk_size_group_remove_widget(f->stretchygroup, widget); + gtk_widget_set_hexpand(widget, fc->oldhexpand); + gtk_widget_set_halign(widget, fc->oldhalign); + gtk_widget_set_vexpand(widget, fc->oldvexpand); + gtk_widget_set_valign(widget, fc->oldvalign); + + g_array_remove_index(f->children, index); +} + int uiFormPadded(uiForm *f) { return f->padded; diff --git a/windows/form.cpp b/windows/form.cpp index e15e9571..e22da976 100644 --- a/windows/form.cpp +++ b/windows/form.cpp @@ -258,6 +258,18 @@ void uiFormAppend(uiForm *f, const char *label, uiControl *c, int stretchy) uiWindowsControlMinimumSizeChanged(uiWindowsControl(f)); } +void uiFormDelete(uiForm *f, int index) +{ + uiControl *c; + + c = (*(f->controls))[index].c; + uiControlSetParent(c, NULL); + uiWindowsControlSetParentHWND(uiWindowsControl(c), NULL); + f->controls->erase(f->controls->begin() + index); + formArrangeChildren(f); + uiWindowsControlMinimumSizeChanged(uiWindowsControl(f)); +} + int uiFormPadded(uiForm *f) { return f->padded; From b817a16c051f59bba08a9217a8d3c8eee1a63c1f Mon Sep 17 00:00:00 2001 From: emersion Date: Tue, 14 Jun 2016 22:55:55 +0200 Subject: [PATCH 0329/1329] Adds missing delete() method for darwin --- darwin/form.m | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/darwin/form.m b/darwin/form.m index 3d27520b..08a3b6da 100644 --- a/darwin/form.m +++ b/darwin/form.m @@ -40,7 +40,7 @@ - (CGFloat)paddingAmount; - (void)establishOurConstraints; - (void)append:(NSString *)label c:(uiControl *)c stretchy:(int)stretchy; -//TODO- (void)delete:(int)n; +- (void)delete:(int)n; - (int)isPadded; - (void)setPadded:(int)p; - (BOOL)hugsTrailing; @@ -388,7 +388,31 @@ struct uiForm { [fc release]; // we don't need the initial reference now } -//TODO- (void)delete:(int)n +- (void)delete:(int)n +{ + formChild *fc; + int stretchy; + + fc = (formChild *) [self->children objectAtIndex:n]; + stretchy = fc.stretchy; + + uiControlSetParent(fc.c, NULL); + uiDarwinControlSetSuperview(uiDarwinControl(fc.c), nil); + + uiDarwinControlSetHuggingPriority(uiDarwinControl(fc.c), fc.oldHorzHuggingPri, NSLayoutConstraintOrientationHorizontal); + uiDarwinControlSetHuggingPriority(uiDarwinControl(fc.c), fc.oldVertHuggingPri, NSLayoutConstraintOrientationVertical); + + [fc.label removeFromSuperview]; + + [self->children removeObjectAtIndex:n]; + + [self establishOurConstraints]; + if (stretchy) { + self->nStretchy--; + if (self->nStretchy == 0) + uiDarwinNotifyEdgeHuggingChanged(uiDarwinControl(self->f)); + } +} - (int)isPadded { From 8ae0823eee91ff19a8e123a3acdb99178c3ce0de Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 14 Jun 2016 18:12:30 -0400 Subject: [PATCH 0330/1329] Implemented hidden controls properly on Windows uiForm. --- windows/form.cpp | 44 ++++++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/windows/form.cpp b/windows/form.cpp index 6c91834e..1db4c4d7 100644 --- a/windows/form.cpp +++ b/windows/form.cpp @@ -43,6 +43,7 @@ static void formRelayout(uiForm *f) int minimumWidth, minimumHeight; uiWindowsSizing sizing; int labelht, labelyoff; + int nVisible; if (f->controls->size() == 0) return; @@ -53,22 +54,22 @@ static void formRelayout(uiForm *f) width = r.right - r.left; height = r.bottom - r.top; - // -1) get this Form's padding + // 0) get this Form's padding formPadding(f, &xpadding, &ypadding); - // 0) inset the available rect by the needed padding - // TODO this is incorrect if any controls are hidden - width -= xpadding; - height -= (f->controls->size() - 1) * ypadding; - // 1) get width of labels and height of non-stretchy controls // this will tell us how much space will be left for controls labelwid = 0; stretchyht = height; nStretchy = 0; + nVisible = 0; for (struct formChild &fc : *(f->controls)) { - if (!uiControlVisible(fc.c)) + if (!uiControlVisible(fc.c)) { + ShowWindow(fc.label, SW_HIDE); continue; + } + ShowWindow(fc.label, SW_SHOW); + nVisible++; thiswid = uiWindowsWindowTextWidth(fc.label); if (labelwid < thiswid) labelwid = thiswid; @@ -80,8 +81,15 @@ static void formRelayout(uiForm *f) fc.height = minimumHeight; stretchyht -= minimumHeight; } + if (nVisible == 0) // nothing to do + return; - // 2) now get the width of controls and the height of stretchy controls + // 2) inset the available rect by the needed padding + width -= xpadding; + height -= (nVisible - 1) * ypadding; + stretchyht -= (nVisible - 1) * ypadding; + + // 3) now get the width of controls and the height of stretchy controls width -= labelwid; if (nStretchy != 0) { stretchyht /= nStretchy; @@ -93,12 +101,12 @@ static void formRelayout(uiForm *f) } } - // 3) get the y offset + // 4) get the y offset labelyoff = labelYOffset; uiWindowsGetSizing(f->hwnd, &sizing); uiWindowsSizingDlgUnitsToPixels(&sizing, NULL, &labelyoff); - // 4) now we can position controls + // 5) now we can position controls // first, make relative to the top-left corner of the container // also prefer left alignment on Windows x = labelwid + xpadding; @@ -164,6 +172,7 @@ static void uiFormMinimumSize(uiWindowsControl *c, int *width, int *height) int labelwid; int i; int minimumWidth, minimumHeight; + int nVisible; uiWindowsSizing sizing; *width = 0; @@ -174,20 +183,17 @@ static void uiFormMinimumSize(uiWindowsControl *c, int *width, int *height) // 0) get this Form's padding formPadding(f, &xpadding, &ypadding); - // 1) initialize the desired rect with the needed padding - // TODO this is wrong if any controls are hidden - *width = xpadding; - *height = (f->controls->size() - 1) * ypadding; - - // 2) determine the longest width of all controls and labels; add in the height of non-stretchy controls and get (but not add in) the largest heights of stretchy controls + // 1) determine the longest width of all controls and labels; add in the height of non-stretchy controls and get (but not add in) the largest heights of stretchy controls // we still add in like direction of stretchy controls nStretchy = 0; maxLabelWidth = 0; maxControlWidth = 0; maxStretchyHeight = 0; + nVisible = 0; for (const struct formChild &fc : *(f->controls)) { if (!uiControlVisible(fc.c)) continue; + nVisible++; labelwid = uiWindowsWindowTextWidth(fc.label); if (maxLabelWidth < labelwid) maxLabelWidth = labelwid; @@ -202,8 +208,14 @@ static void uiFormMinimumSize(uiWindowsControl *c, int *width, int *height) if (!fc.stretchy) *height += minimumHeight; } + if (nVisible == 0) // nothing to show; return 0x0 + return; *width += maxLabelWidth + maxControlWidth; + // 2) outset the desired rect with the needed padding + *width += xpadding; + *height += (nVisible - 1) * ypadding; + // 3) and now we can add in stretchy controls *height += nStretchy * maxStretchyHeight; } From 96ce336a8be13c9df62272c5e52be8a28e83ddc9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 14 Jun 2016 19:44:28 -0400 Subject: [PATCH 0331/1329] Handled hidden controls in uiGrid properly. Spacing, on the other hand... --- windows/grid.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/windows/grid.cpp b/windows/grid.cpp index 280f6344..ee7a4edc 100644 --- a/windows/grid.cpp +++ b/windows/grid.cpp @@ -66,6 +66,8 @@ public: struct gridChild *gc; gc = (*(g->children))[i]; + if (!uiControlVisible(gc->c)) + continue; for (y = gc->top; y < gc->top + gc->yspan; y++) for (x = gc->left; x < gc->left + gc->xspan; x++) this->gg[toyindex(g, y)][toxindex(g, x)] = i; @@ -134,6 +136,7 @@ static void gridRelayout(uiGrid *g) ld = new gridLayoutData(g); // 0) discount padding from width/height + // TODO this doesn't work if any rows or columns are completely blank width -= (xcount(g) - 1) * xpadding; height -= (ycount(g) - 1) * ypadding; @@ -161,6 +164,8 @@ static void gridRelayout(uiGrid *g) // we need to know which expanding rows/columns don't span before we can handle the ones that do for (i = 0; i < g->children->size(); i++) { gc = (*(g->children))[i]; + if (!uiControlVisible(gc->c)) + continue; if (gc->hexpand && gc->xspan == 1) ld->hexpand[toxindex(g, gc->left)] = true; if (gc->vexpand && gc->yspan == 1) @@ -171,6 +176,8 @@ static void gridRelayout(uiGrid *g) // the way we handle this is simple: if none of the spanned rows/columns expand, make all rows/columns expand for (i = 0; i < g->children->size(); i++) { gc = (*(g->children))[i]; + if (!uiControlVisible(gc->c)) + continue; if (gc->hexpand && gc->xspan != 1) { bool doit = true; @@ -221,6 +228,8 @@ static void gridRelayout(uiGrid *g) // 5) reset the final coordinates for the next step for (i = 0; i < g->children->size(); i++) { gc = (*(g->children))[i]; + if (!uiControlVisible(gc->c)) + continue; gc->finalx = 0; gc->finaly = 0; gc->finalwidth = 0; @@ -277,6 +286,8 @@ static void gridRelayout(uiGrid *g) // this is why we saved minwidth/minheight above for (i = 0; i < g->children->size(); i++) { gc = (*(g->children))[i]; + if (!uiControlVisible(gc->c)) + continue; if (gc->halign != uiAlignFill) { switch (gc->halign) { case uiAlignEnd: @@ -404,8 +415,10 @@ static void uiGridMinimumSize(uiWindowsControl *c, int *width, int *height) rowheight += ld->rowheights[y]; // and that's it; just account for padding - *width = colwidth + (g->xmax-1) * xpadding; - *height = rowheight + (g->ymax-1) * ypadding; + // TODO this is wrong if any columns or rows are completely empty + // TODO this seems to be wrong in general... + *width = colwidth + (g->xmax - 1) * xpadding; + *height = rowheight + (g->ymax - 1) * ypadding; } static void uiGridMinimumSizeChanged(uiWindowsControl *c) From 0a4a8ae4f380861ab564e3cc2aaf30067014e952 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 14 Jun 2016 20:57:21 -0400 Subject: [PATCH 0332/1329] Getting closer. Adding hidden views makes things harder... hm. --- README.md | 1 + windows/grid.cpp | 62 +++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 445b3ce3..e693e7a6 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ This README is being written.
* **14 June 2016** * uiDarwinControl now has a `ChildVisibilityChanged()` method and a corresponding `NotifyVisibilityChanged()` function that is called by the default show/hide handlers. This is used to make visibility changes work on OS X; uiBox, uiForm, and uiGrid all respect these now. * The same has been done on the Windows side as well. + * Hiding and showing controls and padding calculations are now correct on Windows at long last. * Hiding a control in a uiForm now hides its label on all platforms. * **13 June 2016** diff --git a/windows/grid.cpp b/windows/grid.cpp index ee7a4edc..865ea683 100644 --- a/windows/grid.cpp +++ b/windows/grid.cpp @@ -42,13 +42,15 @@ struct uiGrid { #define toyindex(g, y) ((y) - (g)->ymin) class gridLayoutData { - size_t ycount; + int ycount; public: int **gg; // topological map gg[y][x] = control index int *colwidths; int *rowheights; bool *hexpand; bool *vexpand; + int nVisibleRows; + int nVisibleColumns; gridLayoutData(uiGrid *g) { @@ -83,6 +85,24 @@ public: ZeroMemory(this->vexpand, ycount(g) * sizeof (bool)); this->ycount = ycount(g); + + // if a row or column only contains emptys and spanning cells of a opposite-direction spannings, it is invisible and should not be considered for padding amount calculations + // furthermore, remove it by duplicating the previous row or column + // note that the first row and column will always be visible because the first for loop above computed a smallest fitting rectangle + this->nVisibleRows = 0; + for (y = 0; y < this->ycount; y++) + if (this->visibleRow(g, y)) + this->nVisibleRows++; + else + for (x = 0; x < xcount(g); x++) + this->gg[y][x] = this->gg[y - 1][x]; + this->nVisibleColumns = 0; + for (x = 0; x < xcount(g); x++) + if (this->visibleColumn(g, x)) + this->nVisibleColumns++; + else + for (y = 0; y < ycount(g); y++) + this->gg[y][x] = this->gg[y][x - 1]; } ~gridLayoutData() @@ -97,6 +117,35 @@ public: delete[] this->gg[y]; delete[] this->gg; } + +private: + bool visibleRow(uiGrid *g, int y) + { + int x; + struct gridChild *gc; + + for (x = 0; x < xcount(g); x++) + if (this->gg[y][x] != -1) { + gc = (*(g->children))[this->gg[y][x]]; + if (gc->yspan == 1 || gc->top - g->ymin == y) + return true; + } + return false; + } + + bool visibleColumn(uiGrid *g, int x) + { + int y; + struct gridChild *gc; + + for (y = 0; y < this->ycount; y++) + if (this->gg[y][x] != -1) { + gc = (*(g->children))[this->gg[y][x]]; + if (gc->xspan == 1 || gc->left - g->xmin == x) + return true; + } + return false; + } }; static void gridPadding(uiGrid *g, int *xpadding, int *ypadding) @@ -136,9 +185,8 @@ static void gridRelayout(uiGrid *g) ld = new gridLayoutData(g); // 0) discount padding from width/height - // TODO this doesn't work if any rows or columns are completely blank - width -= (xcount(g) - 1) * xpadding; - height -= (ycount(g) - 1) * ypadding; + width -= (ld->nVisibleColumns - 1) * xpadding; + height -= (ld->nVisibleRows - 1) * ypadding; // 1) compute colwidths and rowheights before handling expansion // we only count non-spanning controls to avoid weirdness @@ -415,10 +463,8 @@ static void uiGridMinimumSize(uiWindowsControl *c, int *width, int *height) rowheight += ld->rowheights[y]; // and that's it; just account for padding - // TODO this is wrong if any columns or rows are completely empty - // TODO this seems to be wrong in general... - *width = colwidth + (g->xmax - 1) * xpadding; - *height = rowheight + (g->ymax - 1) * ypadding; + *width = colwidth + (ld->nVisibleColumns - 1) * xpadding; + *height = rowheight + (ld->nVisibleRows - 1) * ypadding; } static void uiGridMinimumSizeChanged(uiWindowsControl *c) From cdbe48cc83851b46e297c27ee3e22ef3e1b06392 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 14 Jun 2016 20:58:44 -0400 Subject: [PATCH 0333/1329] Plugged a potential memory corruption in darwin/grid.m. Now we have to do the same on Windows: ensure that hidden cells are taken into account when computing xmin/xmax/ymin/ymax and abort if there's nothing there. --- darwin/grid.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/darwin/grid.m b/darwin/grid.m index 200c2a1b..aaf7a142 100644 --- a/darwin/grid.m +++ b/darwin/grid.m @@ -177,6 +177,8 @@ struct uiGrid { if (ymax < (gc.top + gc.yspan)) ymax = gc.top + gc.yspan; } + if (first == YES) // the entire grid is hidden; do nothing + return; xcount = xmax - xmin; ycount = ymax - ymin; From cf8c1c67fe60df116d4560bda417f9ee7bc190df Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 14 Jun 2016 21:00:57 -0400 Subject: [PATCH 0334/1329] Clarified the previous commit. --- darwin/grid.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/darwin/grid.m b/darwin/grid.m index aaf7a142..9f91e1cb 100644 --- a/darwin/grid.m +++ b/darwin/grid.m @@ -148,6 +148,7 @@ struct uiGrid { int firstx, firsty; BOOL *hexpand, *vexpand; BOOL doit; + BOOL onlyEmptyAndSpanning; [self removeOurConstraints]; if ([self->children count] == 0) @@ -158,6 +159,7 @@ struct uiGrid { // ignore hidden controls first = YES; for (gc in self->children) { + // this bit is important: it ensures row ymin and column xmin have at least one cell to draw, so the onlyEmptyAndSpanning logic below will never run on those rows if (!uiControlVisible(gc.c)) continue; if (first) { @@ -206,7 +208,6 @@ struct uiGrid { } // if a row or column only contains emptys and spanning cells of a opposite-direction spannings, remove it by duplicating the previous row or column - BOOL onlyEmptyAndSpanning; for (y = 0; y < ycount; y++) { onlyEmptyAndSpanning = YES; for (x = 0; x < xcount; x++) From 106d4b544a211d3c92cd6dc62f0b614544565b44 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 14 Jun 2016 21:55:14 -0400 Subject: [PATCH 0335/1329] Edging ever closer to getting grids working right on Windows. --- darwin/grid.m | 2 +- windows/grid.cpp | 63 +++++++++++++++++++++++++++++------------------- 2 files changed, 39 insertions(+), 26 deletions(-) diff --git a/darwin/grid.m b/darwin/grid.m index 9f91e1cb..5e391fd5 100644 --- a/darwin/grid.m +++ b/darwin/grid.m @@ -179,7 +179,7 @@ struct uiGrid { if (ymax < (gc.top + gc.yspan)) ymax = gc.top + gc.yspan; } - if (first == YES) // the entire grid is hidden; do nothing + if (first != NO) // the entire grid is hidden; do nothing return; xcount = xmax - xmin; ycount = ymax - ymin; diff --git a/windows/grid.cpp b/windows/grid.cpp index 865ea683..9a52a6bf 100644 --- a/windows/grid.cpp +++ b/windows/grid.cpp @@ -36,6 +36,34 @@ struct uiGrid { int xmax, ymax; }; +static bool gridRecomputeMinMax(uiGrid *g) +{ + bool first = true; + + for (struct gridChild *gc : *(g->children)) { + // this is important; we want g->xmin/g->ymin to satisfy gridLayoutData::visibleRow()/visibleColumn() + if (!uiControlVisible(gc->c)) + continue; + if (first) { + g->xmin = gc->left; + g->ymin = gc->top; + g->xmax = gc->left + gc->xspan; + g->ymax = gc->top + gc->yspan; + first = false; + continue; + } + if (g->xmin > gc->left) + g->xmin = gc->left; + if (g->ymin > gc->top) + g->ymin = gc->top; + if (g->xmax < (gc->left + gc->xspan)) + g->xmax = gc->left + gc->xspan; + if (g->ymax < (gc->top + gc->yspan)) + g->ymax = gc->top + gc->yspan; + } + return first != false; +} + #define xcount(g) ((g)->xmax - (g)->xmin) #define ycount(g) ((g)->ymax - (g)->ymin) #define toxindex(g, x) ((x) - (g)->xmin) @@ -52,11 +80,15 @@ public: int nVisibleRows; int nVisibleColumns; + bool noVisible; + gridLayoutData(uiGrid *g) { size_t i; int x, y; + this->noVisible = gridRecomputeMinMax(g); + this->gg = new int *[ycount(g)]; for (y = 0; y < ycount(g); y++) { this->gg[y] = new int[xcount(g)]; @@ -89,6 +121,8 @@ public: // if a row or column only contains emptys and spanning cells of a opposite-direction spannings, it is invisible and should not be considered for padding amount calculations // furthermore, remove it by duplicating the previous row or column // note that the first row and column will always be visible because the first for loop above computed a smallest fitting rectangle + if (this->noVisible) + return; this->nVisibleRows = 0; for (y = 0; y < this->ycount; y++) if (this->visibleRow(g, y)) @@ -183,6 +217,8 @@ static void gridRelayout(uiGrid *g) gridPadding(g, &xpadding, &ypadding); ld = new gridLayoutData(g); + if (ld->noVisible) // nothing to do + return; // 0) discount padding from width/height width -= (ld->nVisibleColumns - 1) * xpadding; @@ -252,7 +288,6 @@ static void gridRelayout(uiGrid *g) } } - // 4) compute and assign expanded widths/heights nhexpand = 0; nvexpand = 0; @@ -434,6 +469,8 @@ static void uiGridMinimumSize(uiWindowsControl *c, int *width, int *height) gridPadding(g, &xpadding, &ypadding); ld = new gridLayoutData(g); + if (ld->noVisible) // nothing to do; return 0x0 + return; // 1) compute colwidths and rowheights before handling expansion // TODO put this in its own function (but careful about the spanning calculation in gridRelayout()) @@ -520,30 +557,6 @@ static void gridArrangeChildren(uiGrid *g) delete ld; } -static void gridRecomputeMinMax(uiGrid *g) -{ - bool first = true; - - for (struct gridChild *gc : *(g->children)) { - if (first) { - g->xmin = gc->left; - g->ymin = gc->top; - g->xmax = gc->left + gc->xspan; - g->ymax = gc->top + gc->yspan; - first = false; - continue; - } - if (g->xmin > gc->left) - g->xmin = gc->left; - if (g->ymin > gc->top) - g->ymin = gc->top; - if (g->xmax < (gc->left + gc->xspan)) - g->xmax = gc->left + gc->xspan; - if (g->ymax < (gc->top + gc->yspan)) - g->ymax = gc->top + gc->yspan; - } -} - static struct gridChild *toChild(uiControl *c, int xspan, int yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign) { struct gridChild *gc; From 4b2d64634523ff07ec8c6f73ebeb097003f71969 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 14 Jun 2016 23:00:30 -0400 Subject: [PATCH 0336/1329] Fixed most of the grid problems. Now we just need to figure why the Assorted page both clips and doesn't position controls correctly... --- windows/grid.cpp | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/windows/grid.cpp b/windows/grid.cpp index 9a52a6bf..74f2aeb5 100644 --- a/windows/grid.cpp +++ b/windows/grid.cpp @@ -119,24 +119,17 @@ public: this->ycount = ycount(g); // if a row or column only contains emptys and spanning cells of a opposite-direction spannings, it is invisible and should not be considered for padding amount calculations - // furthermore, remove it by duplicating the previous row or column - // note that the first row and column will always be visible because the first for loop above computed a smallest fitting rectangle + // note that the first row and column will always be visible because gridRecomputeMinMax() computed a smallest fitting rectangle if (this->noVisible) return; this->nVisibleRows = 0; for (y = 0; y < this->ycount; y++) if (this->visibleRow(g, y)) this->nVisibleRows++; - else - for (x = 0; x < xcount(g); x++) - this->gg[y][x] = this->gg[y - 1][x]; this->nVisibleColumns = 0; for (x = 0; x < xcount(g); x++) if (this->visibleColumn(g, x)) this->nVisibleColumns++; - else - for (y = 0; y < ycount(g); y++) - this->gg[y][x] = this->gg[y][x - 1]; } ~gridLayoutData() @@ -152,7 +145,6 @@ public: delete[] this->gg; } -private: bool visibleRow(uiGrid *g, int y) { int x; @@ -327,6 +319,8 @@ static void gridRelayout(uiGrid *g) curx = 0; prev = -1; for (ix = 0; ix < xcount(g); ix++) { + if (!ld->visibleColumn(g, ix)) + continue; i = ld->gg[iy][ix]; if (i != -1) { gc = (*(g->children))[i]; @@ -349,6 +343,8 @@ static void gridRelayout(uiGrid *g) cury = 0; prev = -1; for (iy = 0; iy < ycount(g); iy++) { + if (!ld->visibleRow(g, iy)) + continue; i = ld->gg[iy][ix]; if (i != -1) { gc = (*(g->children))[i]; From c8dd5468046f7085247dae2addf689a7e7fb7986 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 14 Jun 2016 23:03:40 -0400 Subject: [PATCH 0337/1329] I don't know what's up. Fix a leak anyway. --- windows/grid.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/windows/grid.cpp b/windows/grid.cpp index 74f2aeb5..c63cd1e4 100644 --- a/windows/grid.cpp +++ b/windows/grid.cpp @@ -6,7 +6,7 @@ // - what happens if you call Append() twice? // TODOs -// - make ALL the controls handle hidden children right +// - the Assorted page has clipping and repositioning issues struct gridChild { uiControl *c; @@ -209,8 +209,10 @@ static void gridRelayout(uiGrid *g) gridPadding(g, &xpadding, &ypadding); ld = new gridLayoutData(g); - if (ld->noVisible) // nothing to do + if (ld->noVisible) { // nothing to do + delete ld; return; + } // 0) discount padding from width/height width -= (ld->nVisibleColumns - 1) * xpadding; @@ -465,8 +467,10 @@ static void uiGridMinimumSize(uiWindowsControl *c, int *width, int *height) gridPadding(g, &xpadding, &ypadding); ld = new gridLayoutData(g); - if (ld->noVisible) // nothing to do; return 0x0 + if (ld->noVisible) { // nothing to do; return 0x0 + delete ld; return; + } // 1) compute colwidths and rowheights before handling expansion // TODO put this in its own function (but careful about the spanning calculation in gridRelayout()) From c60cbb72f96ef20c057431fda429b2d045838d9a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 15 Jun 2016 00:04:39 -0400 Subject: [PATCH 0338/1329] More TODOs. --- TODO.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TODO.md b/TODO.md index 5ba8193d..d0aa26cf 100644 --- a/TODO.md +++ b/TODO.md @@ -90,3 +90,5 @@ don't forget LONGTERMs as well notes - http://blogs.msdn.com/b/oldnewthing/archive/2004/03/29/101121.aspx on accelerators + +- group and tab should act as if they have no child if the child is hidden From 16b4409f380ba17bef6d9d156a3f437b5d99e687 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 15 Jun 2016 00:04:55 -0400 Subject: [PATCH 0339/1329] Amended TODO. --- TODO.md | 1 + 1 file changed, 1 insertion(+) diff --git a/TODO.md b/TODO.md index d0aa26cf..981deef1 100644 --- a/TODO.md +++ b/TODO.md @@ -92,3 +92,4 @@ notes - http://blogs.msdn.com/b/oldnewthing/archive/2004/03/29/101121.aspx on accelerators - group and tab should act as if they have no child if the child is hidden +on windows From 831fe1e73bd07be2523630c5056208a6efa17da8 Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 15 Jun 2016 16:39:23 +0200 Subject: [PATCH 0340/1329] Updates darwin to work with upstream changes --- darwin/form.m | 45 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/darwin/form.m b/darwin/form.m index 08a3b6da..614c6114 100644 --- a/darwin/form.m +++ b/darwin/form.m @@ -23,7 +23,6 @@ uiForm *f; NSMutableArray *children; int padded; - int nStretchy; NSLayoutConstraint *first; NSMutableArray *inBetweens; @@ -45,6 +44,7 @@ - (void)setPadded:(int)p; - (BOOL)hugsTrailing; - (BOOL)hugsBottom; +- (int)nStretchy; @end struct uiForm { @@ -123,7 +123,6 @@ struct uiForm { self->f = ff; self->padded = 0; self->children = [NSMutableArray new]; - self->nStretchy = 0; self->inBetweens = [NSMutableArray new]; self->widths = [NSMutableArray new]; @@ -221,6 +220,9 @@ struct uiForm { // first arrange the children vertically and make them the same width prev = nil; for (fc in self->children) { + [fc setHidden:!uiControlVisible(fc.c)]; + if (!uiControlVisible(fc.c)) + continue; if (prev == nil) { // first view self->first = mkConstraint(self, NSLayoutAttributeTop, NSLayoutRelationEqual, @@ -259,6 +261,8 @@ struct uiForm { prev = [fc view]; prevlabel = fc; } + if (prev == nil) // all hidden; act as if nothing there + return; self->last = mkConstraint(prev, NSLayoutAttributeBottom, NSLayoutRelationEqual, self, NSLayoutAttributeBottom, @@ -269,6 +273,8 @@ struct uiForm { // now arrange the controls horizontally for (fc in self->children) { + if (!uiControlVisible(fc.c)) + continue; c = mkConstraint(self, NSLayoutAttributeLeading, NSLayoutRelationEqual, fc, NSLayoutAttributeLeading, @@ -313,6 +319,8 @@ struct uiForm { // and make all stretchy controls have the same height prev = nil; for (fc in self->children) { + if (!uiControlVisible(fc.c)) + continue; if (!fc.stretchy) continue; if (prev == nil) { @@ -375,15 +383,13 @@ struct uiForm { @"uiForm baseline constraint"); [self addConstraint:fc.baseline]; + oldnStretchy = [self nStretchy]; [self->children addObject:fc]; [self establishOurConstraints]; - if (fc.stretchy) { - oldnStretchy = self->nStretchy; - self->nStretchy++; + if (fc.stretchy) if (oldnStretchy == 0) uiDarwinNotifyEdgeHuggingChanged(uiDarwinControl(self->f)); - } [fc release]; // we don't need the initial reference now } @@ -408,8 +414,7 @@ struct uiForm { [self establishOurConstraints]; if (stretchy) { - self->nStretchy--; - if (self->nStretchy == 0) + if ([self nStretchy] == 0) uiDarwinNotifyEdgeHuggingChanged(uiDarwinControl(self->f)); } } @@ -440,7 +445,22 @@ struct uiForm { - (BOOL)hugsBottom { // only hug if we have stretchy - return self->nStretchy != 0; + return [self nStretchy] != 0; +} + +- (int)nStretchy +{ + formChild *fc; + int n; + + n = 0; + for (fc in self->children) { + if (!uiControlVisible(fc.c)) + continue; + if (fc.stretchy) + n++; + } + return n; } @end @@ -500,6 +520,13 @@ static void uiFormChildEdgeHuggingChanged(uiDarwinControl *c) uiDarwinControlDefaultHuggingPriority(uiForm, view) uiDarwinControlDefaultSetHuggingPriority(uiForm, view) +static void uiFormChildVisibilityChanged(uiDarwinControl *c) +{ + uiForm *f = uiForm(c); + + [f->view establishOurConstraints]; +} + void uiFormAppend(uiForm *f, const char *label, uiControl *c, int stretchy) { // LONGTERM on other platforms From 3173fc6cdca14258ce00130633c01d52a2ac6320 Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 15 Jun 2016 17:07:52 +0200 Subject: [PATCH 0341/1329] Fixes windows static library copy --- windows/CMakeLists.txt | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index a2506705..0f1d61de 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -68,16 +68,11 @@ if(NOT BUILD_SHARED_LIBS) set(_LIBUI_STATIC_RES ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/libui.res PARENT_SCOPE) endif() macro(_handle_static) - if(MSVC) - set(_res_suffix res) - else() - set(_res_suffix obj) - endif() # TODO this full path feels hacky add_custom_command( TARGET libui POST_BUILD COMMAND - ${CMAKE_COMMAND} -E copy $/CMakeFiles/libui.dir/windows/resources.rc.${_res_suffix} ${_LIBUI_STATIC_RES} + ${CMAKE_COMMAND} -E copy $/CMakeFiles/libui.dir/windows/resources.rc.* ${_LIBUI_STATIC_RES} COMMENT "Copying libui.res") set(_res_suffix) endmacro() From cea52e2df60ecd5e8ef904c576448a360f5421cd Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 15 Jun 2016 11:58:26 -0400 Subject: [PATCH 0342/1329] Preemptive README change for next merge. --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index e693e7a6..1aea4549 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,9 @@ This README is being written.
*Note that today's entry may be updated later today Eastern Time.* +* **15 June 2016** + * Added `uiFormDelete()`; thanks to @emersion. + * **14 June 2016** * uiDarwinControl now has a `ChildVisibilityChanged()` method and a corresponding `NotifyVisibilityChanged()` function that is called by the default show/hide handlers. This is used to make visibility changes work on OS X; uiBox, uiForm, and uiGrid all respect these now. * The same has been done on the Windows side as well. From dda58c93237467dade3526f630e1803ee6dc5fbf Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 15 Jun 2016 12:04:11 -0400 Subject: [PATCH 0343/1329] Fixed leaking issues with the previous commit. --- darwin/form.m | 6 ++---- windows/form.cpp | 9 +++++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/darwin/form.m b/darwin/form.m index 614c6114..7cdb965a 100644 --- a/darwin/form.m +++ b/darwin/form.m @@ -408,15 +408,13 @@ struct uiForm { uiDarwinControlSetHuggingPriority(uiDarwinControl(fc.c), fc.oldHorzHuggingPri, NSLayoutConstraintOrientationHorizontal); uiDarwinControlSetHuggingPriority(uiDarwinControl(fc.c), fc.oldVertHuggingPri, NSLayoutConstraintOrientationVertical); - [fc.label removeFromSuperview]; - + [fc onDestroy]; [self->children removeObjectAtIndex:n]; [self establishOurConstraints]; - if (stretchy) { + if (stretchy) if ([self nStretchy] == 0) uiDarwinNotifyEdgeHuggingChanged(uiDarwinControl(self->f)); - } } - (int)isPadded diff --git a/windows/form.cpp b/windows/form.cpp index d0607235..febcc693 100644 --- a/windows/form.cpp +++ b/windows/form.cpp @@ -278,11 +278,12 @@ void uiFormAppend(uiForm *f, const char *label, uiControl *c, int stretchy) void uiFormDelete(uiForm *f, int index) { - uiControl *c; + struct formChild fc; - c = (*(f->controls))[index].c; - uiControlSetParent(c, NULL); - uiWindowsControlSetParentHWND(uiWindowsControl(c), NULL); + fc = (*(f->controls))[index]; + uiControlSetParent(fc.c, NULL); + uiWindowsControlSetParentHWND(uiWindowsControl(fc.c), NULL); + uiWindowsEnsureDestroyWindow(fc.label); f->controls->erase(f->controls->begin() + index); formArrangeChildren(f); uiWindowsControlMinimumSizeChanged(uiWindowsControl(f)); From efe207ca372a493142c54658a9327502e8dc7338 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 15 Jun 2016 12:06:19 -0400 Subject: [PATCH 0344/1329] Added a test of uiFormDelete(). --- test/page13.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/page13.c b/test/page13.c index 519fb347..0f3f358c 100644 --- a/test/page13.c +++ b/test/page13.c @@ -75,6 +75,13 @@ static void showHide(uiButton *b, void *data) uiControlShow(c); } +static void deleteFirst(uiButton *b, void *data) +{ + uiForm *f = uiForm(data); + + uiFormDelete(f, 0); +} + uiBox *makePage13(void) { uiBox *page13; @@ -119,6 +126,9 @@ uiBox *makePage13(void) b = uiNewButton("Show/Hide"); uiButtonOnClicked(b, showHide, e); uiBoxAppend(page13, uiControl(b), 0); + b = uiNewButton("Delete First"); + uiButtonOnClicked(b, deleteFirst, f); + uiBoxAppend(page13, uiControl(b), 0); uiBoxAppend(page13, uiControl(f), 1); return page13; From e07a7b3d0541b152e3d5d1d269b6fcc7e44ff1a7 Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 15 Jun 2016 18:51:12 +0200 Subject: [PATCH 0345/1329] Adds uiProgressBarValue() in unix --- ui.h | 4 +++- unix/progressbar.c | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/ui.h b/ui.h index 85c2d302..0131fe0d 100644 --- a/ui.h +++ b/ui.h @@ -187,8 +187,10 @@ _UI_EXTERN uiSlider *uiNewSlider(int min, int max); typedef struct uiProgressBar uiProgressBar; #define uiProgressBar(this) ((uiProgressBar *) (this)) -// TODO uiProgressBarValue() +_UI_EXTERN int uiProgressBarValue(uiProgressBar *p); _UI_EXTERN void uiProgressBarSetValue(uiProgressBar *p, int n); +_UI_EXTERN int uiProgressBarIndeterminate(uiProgressBar *p); +_UI_EXTERN void uiProgressBarSetindeterminate(uiProgressBar *p, int indeterminate); _UI_EXTERN uiProgressBar *uiNewProgressBar(void); typedef struct uiSeparator uiSeparator; diff --git a/unix/progressbar.c b/unix/progressbar.c index 40306e6c..725a6f5f 100644 --- a/unix/progressbar.c +++ b/unix/progressbar.c @@ -9,6 +9,11 @@ struct uiProgressBar { uiUnixControlAllDefaults(uiProgressBar) +int uiProgressBarValue(uiProgressBar *p) +{ + return (int) (gtk_progress_bar_get_fraction(p->pbar) * 100); +} + void uiProgressBarSetValue(uiProgressBar *p, int value) { if (value < 0 || value > 100) From 4465d37d2e47fb53c585944133be741f37e3516a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 15 Jun 2016 13:21:07 -0400 Subject: [PATCH 0346/1329] Started uiWindow positioning stuff. --- test/CMakeLists.txt | 1 + test/main.c | 4 ++++ test/page15.c | 52 +++++++++++++++++++++++++++++++++++++++++++++ test/test.h | 4 ++++ ui.h | 2 ++ 5 files changed, 63 insertions(+) create mode 100644 test/page15.c diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c6511e80..e4924bb3 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -25,6 +25,7 @@ _add_exec(tester page12.c page13.c page14.c + page15.c spaced.c ${_TEST_RESOURCES_RC} ) diff --git a/test/main.c b/test/main.c index 63f21b86..ae3e8af7 100644 --- a/test/main.c +++ b/test/main.c @@ -49,6 +49,7 @@ int main(int argc, char *argv[]) uiBox *page6, *page7, *page8, *page9, *page10; uiBox *page11, *page12, *page13; uiTab *page14; + uiBox *page15; uiTab *outerTab; uiTab *innerTab; int nomenus = 0; @@ -148,6 +149,9 @@ int main(int argc, char *argv[]) page14 = makePage14(); uiTabAppend(innerTab, "Page 14", uiControl(page14)); + page15 = makePage15(w); + uiTabAppend(innerTab, "Page 15", uiControl(page15)); + if (startspaced) setSpaced(1); diff --git a/test/page15.c b/test/page15.c new file mode 100644 index 00000000..ff3a5a26 --- /dev/null +++ b/test/page15.c @@ -0,0 +1,52 @@ +// 15 june 2016 +#include "test.h" + +void moveX(uiSpinbox *s, void *data) +{ + uiWindow *w = uiWindow(data); + int x, y; + + uiWindowPosition(w, &x, &y); + x = uiSpinboxValue(s); + uiWindowSetPosition(w, x, y); +} + +void moveX(uiSpinbox *s, void *data) +{ + uiWindow *w = uiWindow(data); + int x, y; + + uiWindowPosition(w, &x, &y); + y = uiSpinboxValue(s); + uiWindowSetPosition(w, x, y); +} + +// TODO onMove + +uiBox *makePage15(uiWindow *w) +{ + uiBox *page15; + uiBox *hbox; + uiSpinbox *x; + uiSpinbox *y; + int curx, cury; + + page15 = newVerticalBox(); + + hbox = newHorizontalBox(); + uiBoxAppend(page15, uiControl(hbox), 1); + + uiBoxAppend(hbox, uiControl(uiNewLabel("Position")), 0); + x = uiNewSpinbox(INT_MIN, INT_MAX); + uiBoxAppend(hbox, uiControl(x), 1); + y = uiNewSpinbox(INT_MIN, INT_MAX); + uiBoxAppend(hbox, uiControl(y), 1); + + uiSpinboxOnChanged(x, moveX, w); + uiSpinboxOnChanged(y, moveY, w); + uiWindowPosition(w, &curX, &curY); + uiSpinboxSetValue(x, curX); + uiSpinboxSetValue(y, curY); + + return page15; +} diff --git a/test/test.h b/test/test.h index e1b16299..66b1baa7 100644 --- a/test/test.h +++ b/test/test.h @@ -5,6 +5,7 @@ #include #include #include +#include #include "../ui.h" // main.c @@ -85,3 +86,6 @@ extern uiBox *makePage13(void); // page14.c extern uiTab *makePage14(void); + +// page15.c +extern uiBox *makePage15(uiWindow *); diff --git a/ui.h b/ui.h index 85c2d302..ee2b3357 100644 --- a/ui.h +++ b/ui.h @@ -99,6 +99,8 @@ typedef struct uiWindow uiWindow; #define uiWindow(this) ((uiWindow *) (this)) _UI_EXTERN char *uiWindowTitle(uiWindow *w); _UI_EXTERN void uiWindowSetTitle(uiWindow *w, const char *title); +_UI_EXTERN void uiWindowPosition(uiWindow *w, int *x, int *y); +_UI_EXTERN void uiWindowSetPosition(uiWindow *w, int x, int y); _UI_EXTERN void uiWindowOnClosing(uiWindow *w, int (*f)(uiWindow *w, void *data), void *data); _UI_EXTERN void uiWindowSetChild(uiWindow *w, uiControl *child); _UI_EXTERN int uiWindowMargined(uiWindow *w); From 53bec81925079f2651e350a95d5fcf7b6d864ec1 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 15 Jun 2016 14:57:52 -0400 Subject: [PATCH 0347/1329] More uiWindow positioning refinement and implementation on OS X. --- darwin/window.m | 61 +++++++++++++++++++++++++++++++++++++++++++++++++ test/page15.c | 60 +++++++++++++++++++++++++++++++++--------------- ui.h | 2 ++ 3 files changed, 105 insertions(+), 18 deletions(-) diff --git a/darwin/window.m b/darwin/window.m index ffb6b30d..7cab10b4 100644 --- a/darwin/window.m +++ b/darwin/window.m @@ -9,12 +9,16 @@ struct uiWindow { int (*onClosing)(uiWindow *, void *); void *onClosingData; struct singleChildConstraints constraints; + void (*onPositionChanged)(uiWindow *, void *); + void *onPositionChangedData; + BOOL suppressPositionChanged; }; @interface windowDelegateClass : NSObject { struct mapTable *windows; } - (BOOL)windowShouldClose:(id)sender; +- (void)windowDidMove:(NSNotification *)note; - (void)registerWindow:(uiWindow *)w; - (void)unregisterWindow:(uiWindow *)w; - (uiWindow *)lookupWindow:(NSWindow *)w; @@ -47,6 +51,16 @@ struct uiWindow { return NO; } +// TODO doesn't happen live +- (void)windowDidMove:(NSNotification *)note +{ + uiWindow *w; + + w = [self lookupWindow:((NSWindow *) [note object])]; + if (!w->suppressPositionChanged) + (*(w->onPositionChanged))(w, w->onPositionChangedData); +} + - (void)registerWindow:(uiWindow *)w { mapSet(self->windows, w->window, w); @@ -204,6 +218,47 @@ void uiWindowSetTitle(uiWindow *w, const char *title) [w->window setTitle:toNSString(title)]; } +void uiWindowPosition(uiWindow *w, int *x, int *y) +{ + NSScreen *screen; + NSRect r; + + r = [w->window frame]; + *x = r.origin.x; + // this is the right screen to use; thanks mikeash in irc.freenode.net/#macdev + // -mainScreen is useless for positioning (it's just the key window's screen) + // and we use -frame, not -visibleFrame, for dealing with absolute positions + screen = (NSScreen *) [[NSScreen screens] objectAtIndex:0]; + *y = ([screen frame].size.height - r.origin.y) - r.size.height; +} + +void uiWindowSetPosition(uiWindow *w, int x, int y) +{ + // -[NSWindow setFrameTopLeftPoint:] is acting weird so... + NSRect r; + NSScreen *screen; + + // this fires windowDidMove: + w->suppressPositionChanged = YES; + r = [w->window frame]; + r.origin.x = x; + screen = (NSScreen *) [[NSScreen screens] objectAtIndex:0]; + r.origin.y = [screen frame].size.height - (y + r.size.height); + [w->window setFrameOrigin:r.origin]; + w->suppressPositionChanged = NO; +} + +void uiWindowCenter(uiWindow *w) +{ + [w->window center]; +} + +void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data) +{ + w->onPositionChanged = f; + w->onPositionChangedData = data; +} + void uiWindowOnClosing(uiWindow *w, int (*f)(uiWindow *, void *), void *data) { w->onClosing = f; @@ -245,6 +300,11 @@ static int defaultOnClosing(uiWindow *w, void *data) return 0; } +static void defaultOnPositionChanged(uiWindow *w, void *data) +{ + // do nothing +} + uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) { uiWindow *w; @@ -269,6 +329,7 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) } [windowDelegate registerWindow:w]; uiWindowOnClosing(w, defaultOnClosing, NULL); + uiWindowOnPositionChanged(w, defaultOnPositionChanged, NULL); return w; } diff --git a/test/page15.c b/test/page15.c index ff3a5a26..7ab534fd 100644 --- a/test/page15.c +++ b/test/page15.c @@ -1,52 +1,76 @@ // 15 june 2016 #include "test.h" -void moveX(uiSpinbox *s, void *data) +static uiSpinbox *x, *y; + +static void moveX(uiSpinbox *s, void *data) { uiWindow *w = uiWindow(data); - int x, y; + int xp, yp; - uiWindowPosition(w, &x, &y); - x = uiSpinboxValue(s); - uiWindowSetPosition(w, x, y); + uiWindowPosition(w, &xp, &yp); + xp = uiSpinboxValue(x); + uiWindowSetPosition(w, xp, yp); } -void moveX(uiSpinbox *s, void *data) +static void moveY(uiSpinbox *s, void *data) { uiWindow *w = uiWindow(data); - int x, y; + int xp, yp; - uiWindowPosition(w, &x, &y); - y = uiSpinboxValue(s); - uiWindowSetPosition(w, x, y); + uiWindowPosition(w, &xp, &yp); + yp = uiSpinboxValue(y); + uiWindowSetPosition(w, xp, yp); } -// TODO onMove +static void update(uiWindow *w) +{ + int xp, yp; + + uiWindowPosition(w, &xp, &yp); + uiSpinboxSetValue(x, xp); + uiSpinboxSetValue(y, yp); +} + +static void center(uiButton *b, void *data) +{ + uiWindow *w = uiWindow(data); + + uiWindowCenter(w); + update(w); +} + +void onMove(uiWindow *w, void *data) +{ + printf("move\n"); + update(w); +} uiBox *makePage15(uiWindow *w) { uiBox *page15; uiBox *hbox; - uiSpinbox *x; - uiSpinbox *y; - int curx, cury; + uiButton *button; page15 = newVerticalBox(); hbox = newHorizontalBox(); - uiBoxAppend(page15, uiControl(hbox), 1); + // TODO if I make this 1 and not add anything else, on OS X the box won't be able to grow vertically + uiBoxAppend(page15, uiControl(hbox), 0); uiBoxAppend(hbox, uiControl(uiNewLabel("Position")), 0); x = uiNewSpinbox(INT_MIN, INT_MAX); uiBoxAppend(hbox, uiControl(x), 1); y = uiNewSpinbox(INT_MIN, INT_MAX); uiBoxAppend(hbox, uiControl(y), 1); + button = uiNewButton("Center"); + uiBoxAppend(hbox, uiControl(button), 0); uiSpinboxOnChanged(x, moveX, w); uiSpinboxOnChanged(y, moveY, w); - uiWindowPosition(w, &curX, &curY); - uiSpinboxSetValue(x, curX); - uiSpinboxSetValue(y, curY); + uiButtonOnClicked(button, center, w); + uiWindowOnPositionChanged(w, onMove, NULL); + update(w); return page15; } diff --git a/ui.h b/ui.h index ee2b3357..37821208 100644 --- a/ui.h +++ b/ui.h @@ -101,6 +101,8 @@ _UI_EXTERN char *uiWindowTitle(uiWindow *w); _UI_EXTERN void uiWindowSetTitle(uiWindow *w, const char *title); _UI_EXTERN void uiWindowPosition(uiWindow *w, int *x, int *y); _UI_EXTERN void uiWindowSetPosition(uiWindow *w, int x, int y); +_UI_EXTERN void uiWindowCenter(uiWindow *w); +_UI_EXTERN void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data); _UI_EXTERN void uiWindowOnClosing(uiWindow *w, int (*f)(uiWindow *w, void *data), void *data); _UI_EXTERN void uiWindowSetChild(uiWindow *w, uiControl *child); _UI_EXTERN int uiWindowMargined(uiWindow *w); From 560cca5bc990dc413cd50ba5f759a0771dede206 Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 15 Jun 2016 21:51:08 +0200 Subject: [PATCH 0348/1329] Adds uiProgressBar(Set)Indeterminate for unix --- test/page13.c | 13 +++++++++++++ ui.h | 2 +- unix/progressbar.c | 23 +++++++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/test/page13.c b/test/page13.c index 0f3f358c..7908b780 100644 --- a/test/page13.c +++ b/test/page13.c @@ -75,6 +75,12 @@ static void showHide(uiButton *b, void *data) uiControlShow(c); } +static void setIndeterminate(uiButton *b, void *data) +{ + uiProgressBar *p = uiProgressBar(data); + uiProgressBarSetIndeterminate(p, !uiProgressBarIndeterminate(p)); +} + static void deleteFirst(uiButton *b, void *data) { uiForm *f = uiForm(data); @@ -89,6 +95,7 @@ uiBox *makePage13(void) uiButton *b; uiForm *f; uiEntry *e; + uiProgressBar *p; page13 = newVerticalBox(); @@ -123,6 +130,12 @@ uiBox *makePage13(void) uiFormAppend(f, "MLE", uiControl(uiNewMultilineEntry()), 1); + p = uiNewProgressBar(); + uiBoxAppend(page13, uiControl(p), 0); + b = uiNewButton("Toggle indeterminate"); + uiButtonOnClicked(b, setIndeterminate, p); + uiBoxAppend(page13, uiControl(b), 0); + b = uiNewButton("Show/Hide"); uiButtonOnClicked(b, showHide, e); uiBoxAppend(page13, uiControl(b), 0); diff --git a/ui.h b/ui.h index 0131fe0d..42370453 100644 --- a/ui.h +++ b/ui.h @@ -190,7 +190,7 @@ typedef struct uiProgressBar uiProgressBar; _UI_EXTERN int uiProgressBarValue(uiProgressBar *p); _UI_EXTERN void uiProgressBarSetValue(uiProgressBar *p, int n); _UI_EXTERN int uiProgressBarIndeterminate(uiProgressBar *p); -_UI_EXTERN void uiProgressBarSetindeterminate(uiProgressBar *p, int indeterminate); +_UI_EXTERN void uiProgressBarSetIndeterminate(uiProgressBar *p, int indeterminate); _UI_EXTERN uiProgressBar *uiNewProgressBar(void); typedef struct uiSeparator uiSeparator; diff --git a/unix/progressbar.c b/unix/progressbar.c index 725a6f5f..4d502f34 100644 --- a/unix/progressbar.c +++ b/unix/progressbar.c @@ -5,6 +5,7 @@ struct uiProgressBar { uiUnixControl c; GtkWidget *widget; GtkProgressBar *pbar; + int indeterminate; }; uiUnixControlAllDefaults(uiProgressBar) @@ -21,6 +22,28 @@ void uiProgressBarSetValue(uiProgressBar *p, int value) gtk_progress_bar_set_fraction(p->pbar, ((gdouble) value) / 100); } +int uiProgressBarIndeterminate(uiProgressBar *p) +{ + return p->indeterminate; +} + +gboolean uiProgressBarPulse(uiProgressBar *p) +{ + if (!GTK_IS_WIDGET(p->pbar) || !p->indeterminate) + return 0; + + gtk_progress_bar_pulse(p->pbar); + return 1; +} + +void uiProgressBarSetIndeterminate(uiProgressBar *p, int indeterminate) +{ + p->indeterminate = indeterminate; + + if (indeterminate) + g_timeout_add(100, uiProgressBarPulse, p); +} + uiProgressBar *uiNewProgressBar(void) { uiProgressBar *p; From f98318fb3af36d6a6ece2321c7e56541957159ed Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 15 Jun 2016 16:45:49 -0400 Subject: [PATCH 0349/1329] Started implementing the new uiWindows stuff on GTK+. --- test/page15.c | 2 +- unix/window.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/test/page15.c b/test/page15.c index 7ab534fd..4fdadf67 100644 --- a/test/page15.c +++ b/test/page15.c @@ -55,7 +55,7 @@ uiBox *makePage15(uiWindow *w) page15 = newVerticalBox(); hbox = newHorizontalBox(); - // TODO if I make this 1 and not add anything else, on OS X the box won't be able to grow vertically + // TODO if I make this 1 and not add anything else AND not call uiWindowOnPositionChanged(), on OS X the box won't be able to grow vertically uiBoxAppend(page15, uiControl(hbox), 0); uiBoxAppend(hbox, uiControl(uiNewLabel("Position")), 0); diff --git a/unix/window.c b/unix/window.c index cca767e8..702c50f4 100644 --- a/unix/window.c +++ b/unix/window.c @@ -19,6 +19,8 @@ struct uiWindow { int (*onClosing)(uiWindow *, void *); void *onClosingData; + void (*onPositionChanged)(uiWindow *, void *); + void *onPositionChangedData; }; static gboolean onClosing(GtkWidget *win, GdkEvent *e, gpointer data) @@ -37,6 +39,11 @@ static int defaultOnClosing(uiWindow *w, void *data) return 0; } +static void defaultOnPositionChanged(uiWindow *w, void *data) +{ + // do nothing +} + static void uiWindowDestroy(uiControl *c) { uiWindow *w = uiWindow(c); @@ -101,6 +108,50 @@ void uiWindowSetTitle(uiWindow *w, const char *title) gtk_window_set_title(w->window, title); } +// TODO allow specifying either as NULL on all platforms +void uiWindowPosition(uiWindow *w, int *x, int *y) +{ + gint rx, ry; + + gtk_window_get_position(w->window, &rx, &ry); + *x = rx; + *y = ry; +} + +void uiWindowSetPosition(uiWindow *w, int x, int y) +{ + gtk_window_move(w->window, x, y); +} + +// TODO after calling this I have to call get_position() a few times before it actually works +void uiWindowCenter(uiWindow *w) +{ + gint x, y; + GtkAllocation winalloc; + GdkWindow *gdkwin; + GdkScreen *screen; + GdkRectangle workarea; + + gtk_widget_get_allocation(w->widget, &winalloc); + gdkwin = gtk_widget_get_window(w->widget); + screen = gdk_window_get_screen(gdkwin); + gdk_screen_get_monitor_workarea(screen, + gdk_screen_get_monitor_at_window(screen, gdkwin), + &workarea); + + x = (workarea.width - winalloc.width) / 2; + y = (workarea.height - winalloc.height) / 2; + // TODO move up slightly? see what Mutter or GNOME Shell does? + gtk_window_move(w->window, x, y); +} + +// TODO find a signal to connect to +void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data) +{ + w->onPositionChanged = f; + w->onPositionChangedData = data; +} + void uiWindowOnClosing(uiWindow *w, int (*f)(uiWindow *, void *), void *data) { w->onClosing = f; @@ -160,9 +211,10 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) // show everything in the vbox, but not the GtkWindow itself gtk_widget_show_all(w->vboxWidget); - // and connect our OnClosing() event + // and connect our events g_signal_connect(w->widget, "delete-event", G_CALLBACK(onClosing), w); uiWindowOnClosing(w, defaultOnClosing, NULL); + uiWindowOnPositionChanged(w, defaultOnPositionChanged, NULL); // normally it's SetParent() that does this, but we can't call SetParent() on a uiWindow // TODO we really need to clean this up From 983a53d3c3b5b163d003851be5bb7c6c71a7901a Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 15 Jun 2016 23:17:23 +0200 Subject: [PATCH 0350/1329] Replaces uiProgressBar(Set)Indeterminate() by value=-1 --- test/page13.c | 10 +++++++++- ui.h | 2 -- unix/progressbar.c | 34 ++++++++++++++++++---------------- 3 files changed, 27 insertions(+), 19 deletions(-) diff --git a/test/page13.c b/test/page13.c index 7908b780..6eb19f29 100644 --- a/test/page13.c +++ b/test/page13.c @@ -78,7 +78,15 @@ static void showHide(uiButton *b, void *data) static void setIndeterminate(uiButton *b, void *data) { uiProgressBar *p = uiProgressBar(data); - uiProgressBarSetIndeterminate(p, !uiProgressBarIndeterminate(p)); + + int value = uiProgressBarValue(p); + if (value == -1) { + value = 0; + } else { + value = -1; + } + + uiProgressBarSetValue(p, value); } static void deleteFirst(uiButton *b, void *data) diff --git a/ui.h b/ui.h index 42370453..696140f1 100644 --- a/ui.h +++ b/ui.h @@ -189,8 +189,6 @@ typedef struct uiProgressBar uiProgressBar; #define uiProgressBar(this) ((uiProgressBar *) (this)) _UI_EXTERN int uiProgressBarValue(uiProgressBar *p); _UI_EXTERN void uiProgressBarSetValue(uiProgressBar *p, int n); -_UI_EXTERN int uiProgressBarIndeterminate(uiProgressBar *p); -_UI_EXTERN void uiProgressBarSetIndeterminate(uiProgressBar *p, int indeterminate); _UI_EXTERN uiProgressBar *uiNewProgressBar(void); typedef struct uiSeparator uiSeparator; diff --git a/unix/progressbar.c b/unix/progressbar.c index 4d502f34..9e3fd4a4 100644 --- a/unix/progressbar.c +++ b/unix/progressbar.c @@ -12,23 +12,16 @@ uiUnixControlAllDefaults(uiProgressBar) int uiProgressBarValue(uiProgressBar *p) { + if (p->indeterminate) + return -1; + return (int) (gtk_progress_bar_get_fraction(p->pbar) * 100); } -void uiProgressBarSetValue(uiProgressBar *p, int value) +gboolean uiProgressBarPulse(void* data) { - if (value < 0 || value > 100) - userbug("Value %d is out of range for a uiProgressBar.", value); - gtk_progress_bar_set_fraction(p->pbar, ((gdouble) value) / 100); -} + uiProgressBar *p = (uiProgressBar*) data; -int uiProgressBarIndeterminate(uiProgressBar *p) -{ - return p->indeterminate; -} - -gboolean uiProgressBarPulse(uiProgressBar *p) -{ if (!GTK_IS_WIDGET(p->pbar) || !p->indeterminate) return 0; @@ -36,12 +29,21 @@ gboolean uiProgressBarPulse(uiProgressBar *p) return 1; } -void uiProgressBarSetIndeterminate(uiProgressBar *p, int indeterminate) +void uiProgressBarSetValue(uiProgressBar *p, int value) { - p->indeterminate = indeterminate; + if (value == -1) { + if (!p->indeterminate) { + p->indeterminate = 1; + g_timeout_add(100, uiProgressBarPulse, p); + } + return; + } - if (indeterminate) - g_timeout_add(100, uiProgressBarPulse, p); + if (value < 0 || value > 100) + userbug("Value %d is out of range for a uiProgressBar.", value); + + p->indeterminate = 0; + gtk_progress_bar_set_fraction(p->pbar, ((gdouble) value) / 100); } uiProgressBar *uiNewProgressBar(void) From 48c13c738aef8fd24e8d0b477a20ed1408415a76 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 15 Jun 2016 20:45:10 -0400 Subject: [PATCH 0351/1329] Fixed the GTK+ window code. --- unix/window.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/unix/window.c b/unix/window.c index 702c50f4..cdb3ab84 100644 --- a/unix/window.c +++ b/unix/window.c @@ -21,6 +21,7 @@ struct uiWindow { void *onClosingData; void (*onPositionChanged)(uiWindow *, void *); void *onPositionChangedData; + gboolean changingPosition; }; static gboolean onClosing(GtkWidget *win, GdkEvent *e, gpointer data) @@ -34,6 +35,19 @@ static gboolean onClosing(GtkWidget *win, GdkEvent *e, gpointer data) return TRUE; } +static gboolean onConfigure(GtkWidget *win, GdkEvent *e, gpointer data) +{ + uiWindow *w = uiWindow(data); + + // there doesn't seem to be a way to determine if only moving or only resizing is happening :/ + if (w->changingPosition) + w->changingPosition = FALSE; + else + (*(w->onPositionChanged))(w, w->onPositionChangedData); + // always continue handling + return FALSE; +} + static int defaultOnClosing(uiWindow *w, void *data) { return 0; @@ -120,10 +134,16 @@ void uiWindowPosition(uiWindow *w, int *x, int *y) void uiWindowSetPosition(uiWindow *w, int x, int y) { + w->changingPosition = TRUE; gtk_window_move(w->window, x, y); + // gtk_window_move() is asynchronous + // we need to wait for a configure-event + // thanks to hergertme in irc.gimp.net/#gtk+ + while (w->changingPosition) + if (gtk_main_iteration() != FALSE) + break; // stop early if gtk_main_quit() called } -// TODO after calling this I have to call get_position() a few times before it actually works void uiWindowCenter(uiWindow *w) { gint x, y; @@ -141,11 +161,10 @@ void uiWindowCenter(uiWindow *w) x = (workarea.width - winalloc.width) / 2; y = (workarea.height - winalloc.height) / 2; - // TODO move up slightly? see what Mutter or GNOME Shell does? - gtk_window_move(w->window, x, y); + // TODO move up slightly? see what Mutter or GNOME Shell or GNOME Terminal do(es)? + uiWindowSetPosition(w, x, y); } -// TODO find a signal to connect to void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data) { w->onPositionChanged = f; @@ -213,6 +232,7 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) // and connect our events g_signal_connect(w->widget, "delete-event", G_CALLBACK(onClosing), w); + g_signal_connect(w->widget, "configure-event", G_CALLBACK(onConfigure), w); uiWindowOnClosing(w, defaultOnClosing, NULL); uiWindowOnPositionChanged(w, defaultOnPositionChanged, NULL); From c3777da0f45c9557a8b514a90ab85198cdd7c4a2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 15 Jun 2016 21:55:42 -0400 Subject: [PATCH 0352/1329] And added the new uiWindow methods on Windows. --- README.md | 1 + windows/window.cpp | 81 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/README.md b/README.md index 1aea4549..86a83fa7 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ This README is being written.
* **15 June 2016** * Added `uiFormDelete()`; thanks to @emersion. + * Added `uiWindowPosition()`, `uiWindowSetPosition()`, `uiWindowCenter()`, and `uiWindowOnPositionChanged()`, methods for manipulating uiWindow position. * **14 June 2016** * uiDarwinControl now has a `ChildVisibilityChanged()` method and a corresponding `NotifyVisibilityChanged()` function that is called by the default show/hide handlers. This is used to make visibility changes work on OS X; uiBox, uiForm, and uiGrid all respect these now. diff --git a/windows/window.cpp b/windows/window.cpp index 411cb86e..f753d93c 100644 --- a/windows/window.cpp +++ b/windows/window.cpp @@ -14,6 +14,9 @@ struct uiWindow { void *onClosingData; int margined; BOOL hasMenubar; + void (*onPositionChanged)(uiWindow *, void *); + void *onPositionChangedData; + BOOL changingPosition; }; // from https://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing @@ -87,6 +90,10 @@ static LRESULT CALLBACK windowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARA runMenuEvent(LOWORD(wParam), uiWindow(w)); return 0; case WM_WINDOWPOSCHANGED: + if ((wp->flags & SWP_NOMOVE) == 0) + if (!w->changingPosition) + (*(w->onPositionChanged))(w, w->onPositionChangedData); + // and continue anyway if ((wp->flags & SWP_NOSIZE) != 0) break; windowRelayout(w); @@ -138,6 +145,11 @@ static int defaultOnClosing(uiWindow *w, void *data) return 0; } +static void defaultOnPositionChanged(uiWindow *w, void *data) +{ + // do nothing +} + static std::map windows; static void uiWindowDestroy(uiControl *c) @@ -277,6 +289,74 @@ void uiWindowSetTitle(uiWindow *w, const char *title) // don't queue resize; the caption isn't part of what affects layout and sizing of the client area (it'll be ellipsized if too long) } +void uiWindowPosition(uiWindow *w, int *x, int *y) +{ + RECT r; + + uiWindowsEnsureGetWindowRect(w->hwnd, &r); + *x = r.left; + *y = r.top; +} + +void uiWindowSetPosition(uiWindow *w, int x, int y) +{ + w->changingPosition = TRUE; + if (SetWindowPos(w->hwnd, NULL, x, y, 0, 0, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOZORDER) == 0) + logLastError(L"error moving window"); + w->changingPosition = FALSE; +} + +// this is used for both fullscreening and centering +// see also https://blogs.msdn.microsoft.com/oldnewthing/20100412-00/?p=14353 and https://blogs.msdn.microsoft.com/oldnewthing/20050505-04/?p=35703 +static void windowMonitorRect(HWND hwnd, RECT *r) +{ + HMONITOR monitor; + MONITORINFO mi; + + monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY); + ZeroMemory(&mi, sizeof (MONITORINFO)); + mi.cbSize = sizeof (MONITORINFO); + if (GetMonitorInfoW(monitor, &mi) == 0) { + logLastError(L"error getting window monitor rect"); + // default to SM_CXSCREEN x SM_CYSCREEN to be safe + r->left = 0; + r->top = 0; + r->right = GetSystemMetrics(SM_CXSCREEN); + r->bottom = GetSystemMetrics(SM_CYSCREEN); + return; + } + *r = mi.rcMonitor; +} + +// TODO use the work rect instead? +void uiWindowCenter(uiWindow *w) +{ + RECT wr, mr; + int x, y; + LONG wwid, mwid; + LONG wht, mht; + + uiWindowsEnsureGetWindowRect(w->hwnd, &wr); + windowMonitorRect(w->hwnd, &mr); + wwid = wr.right - wr.left; + mwid = mr.right - mr.left; + x = (mwid - wwid) / 2; + wht = wr.bottom - wr.top; + mht = mr.bottom - mr.top; + y = (mht - wht) / 2; + // y is now evenly divided, however https://msdn.microsoft.com/en-us/library/windows/desktop/dn742502(v=vs.85).aspx says that 45% should go above and 55% should go below + // so just move 5% of the way up + // TODO is this correct? + y -= y / 20; + uiWindowSetPosition(w, x, y); +} + +void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data) +{ + w->onPositionChanged = f; + w->onPositionChangedData = data; +} + void uiWindowOnClosing(uiWindow *w, int (*f)(uiWindow *, void *), void *data) { w->onClosing = f; @@ -373,6 +453,7 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) setClientSize(w, width, height, hasMenubarBOOL, style, exstyle); uiWindowOnClosing(w, defaultOnClosing, NULL); + uiWindowOnPositionChanged(w, defaultOnPositionChanged, NULL); windows[w] = true; return w; From 5fbe85c21a887aa1b9c80c165da815726b1894b8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 15 Jun 2016 22:28:44 -0400 Subject: [PATCH 0353/1329] Started adding the uiWindow size code. --- test/page15.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++---- ui.h | 3 +++ 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/test/page15.c b/test/page15.c index 4fdadf67..64b23a5c 100644 --- a/test/page15.c +++ b/test/page15.c @@ -2,6 +2,7 @@ #include "test.h" static uiSpinbox *x, *y; +static uiSpinbox *width, *height; static void moveX(uiSpinbox *s, void *data) { @@ -23,7 +24,7 @@ static void moveY(uiSpinbox *s, void *data) uiWindowSetPosition(w, xp, yp); } -static void update(uiWindow *w) +static void updatepos(uiWindow *w) { int xp, yp; @@ -37,13 +38,48 @@ static void center(uiButton *b, void *data) uiWindow *w = uiWindow(data); uiWindowCenter(w); - update(w); + updatepos(w); } void onMove(uiWindow *w, void *data) { printf("move\n"); - update(w); + updatepos(w); +} + +static void sizeWidth(uiSpinbox *s, void *data) +{ + uiWindow *w = uiWindow(data); + int xp, yp; + + uiWindowsContentSize(w, &xp, &yp); + xp = uiSpinboxValue(width); + uiWindowSetContentSize(w, xp, yp); +} + +static void sizeHeight(uiSpinbox *s, void *data) +{ + uiWindow *w = uiWindow(data); + int xp, yp; + + uiWindowContentSize(w, &xp, &yp); + yp = uiSpinboxValue(height); + uiWindowSetContentSize(w, xp, yp); +} + +static void updatesize(uiWindow *w) +{ + int xp, yp; + + uiWindowContentSize(w, &xp, &yp); + uiSpinboxSetValue(width, xp); + uiSpinboxSetValue(height, yp); +} + +void onSize(uiWindow *w, void *data) +{ + printf("size\n"); + updatesize(w); } uiBox *makePage15(uiWindow *w) @@ -70,7 +106,24 @@ uiBox *makePage15(uiWindow *w) uiSpinboxOnChanged(y, moveY, w); uiButtonOnClicked(button, center, w); uiWindowOnPositionChanged(w, onMove, NULL); - update(w); + updatepos(w); + + hbox = newHorizontalBox(); + uiBoxAppend(page15, uiControl(hbox), 0); + + uiBoxAppend(hbox, uiControl(uiNewLabel("Size")), 0); + width = uiNewSpinbox(INT_MIN, INT_MAX); + uiBoxAppend(hbox, uiControl(width), 1); + height = uiNewSpinbox(INT_MIN, INT_MAX); + uiBoxAppend(hbox, uiControl(height), 1); +// button = uiNewButton("Center"); +// uiBoxAppend(hbox, uiControl(button), 0); + + uiSpinboxOnChanged(width, sizeWidth, w); + uiSpinboxOnChanged(height, sizeHeight, w); +// uiButtonOnClicked(button, center, w); + uiWindowOnContentSizeChanged(w, onSize, NULL); + updatesize(w); return page15; } diff --git a/ui.h b/ui.h index 37821208..1ab01e91 100644 --- a/ui.h +++ b/ui.h @@ -103,6 +103,9 @@ _UI_EXTERN void uiWindowPosition(uiWindow *w, int *x, int *y); _UI_EXTERN void uiWindowSetPosition(uiWindow *w, int x, int y); _UI_EXTERN void uiWindowCenter(uiWindow *w); _UI_EXTERN void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data); +_UI_EXTERN void uiWindowContentSize(uiWindow *w, int *width, int *height); +_UI_EXTERN void uiWindowSetContentSize(uiWindow *w, int width, int height); +_UI_EXTERN void uiWindowOnContentSizeChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data); _UI_EXTERN void uiWindowOnClosing(uiWindow *w, int (*f)(uiWindow *w, void *data), void *data); _UI_EXTERN void uiWindowSetChild(uiWindow *w, uiControl *child); _UI_EXTERN int uiWindowMargined(uiWindow *w); From 6c56f1e1ce41f3e56dde0664fec0c9c2f2c5a1dd Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 15 Jun 2016 22:52:35 -0400 Subject: [PATCH 0354/1329] Implemented the window size stuff on Windows. --- test/page15.c | 2 +- windows/window.cpp | 40 +++++++++++++++++++++++++++++++++++----- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/test/page15.c b/test/page15.c index 64b23a5c..618ac709 100644 --- a/test/page15.c +++ b/test/page15.c @@ -52,7 +52,7 @@ static void sizeWidth(uiSpinbox *s, void *data) uiWindow *w = uiWindow(data); int xp, yp; - uiWindowsContentSize(w, &xp, &yp); + uiWindowContentSize(w, &xp, &yp); xp = uiSpinboxValue(width); uiWindowSetContentSize(w, xp, yp); } diff --git a/windows/window.cpp b/windows/window.cpp index f753d93c..64a1853f 100644 --- a/windows/window.cpp +++ b/windows/window.cpp @@ -16,7 +16,10 @@ struct uiWindow { BOOL hasMenubar; void (*onPositionChanged)(uiWindow *, void *); void *onPositionChangedData; - BOOL changingPosition; + BOOL changingPosition; // to avoid triggering the above when programmatically doing this + void (*onContentSizeChanged)(uiWindow *, void *); + void *onContentSizeChangedData; + BOOL changingSize; }; // from https://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing @@ -38,7 +41,6 @@ static void windowMargins(uiWindow *w, int *mx, int *my) static void windowRelayout(uiWindow *w) { - uiWindowsSizing sizing; int x, y, width, height; RECT r; int mx, my; @@ -96,6 +98,9 @@ static LRESULT CALLBACK windowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARA // and continue anyway if ((wp->flags & SWP_NOSIZE) != 0) break; + if (w->onContentSizeChanged != NULL) // TODO figure out why this is happening too early + if (!w->changingSize) + (*(w->onContentSizeChanged))(w, w->onContentSizeChangedData); windowRelayout(w); return 0; case WM_GETMINMAXINFO: @@ -145,7 +150,7 @@ static int defaultOnClosing(uiWindow *w, void *data) return 0; } -static void defaultOnPositionChanged(uiWindow *w, void *data) +static void defaultOnPositionContentSizeChanged(uiWindow *w, void *data) { // do nothing } @@ -236,7 +241,6 @@ uiWindowsControlDefaultSetParentHWND(uiWindow) static void uiWindowMinimumSize(uiWindowsControl *c, int *width, int *height) { uiWindow *w = uiWindow(c); - uiWindowsSizing sizing; int mx, my; *width = 0; @@ -351,6 +355,31 @@ void uiWindowCenter(uiWindow *w) uiWindowSetPosition(w, x, y); } +void uiWindowContentSize(uiWindow *w, int *width, int *height) +{ + RECT r; + + uiWindowsEnsureGetClientRect(w->hwnd, &r); + *width = r.right - r.left; + *height = r.bottom - r.top; +} + +// TODO should this disallow too small? +void uiWindowSetContentSize(uiWindow *w, int width, int height) +{ + w->changingSize = TRUE; + clientSizeToWindowSize(w->hwnd, &width, &height, w->hasMenubar); + if (SetWindowPos(w->hwnd, NULL, 0, 0, width, height, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER) == 0) + logLastError(L"error resizing window"); + w->changingSize = FALSE; +} + +void uiWindowOnContentSizeChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data) +{ + w->onContentSizeChanged = f; + w->onContentSizeChangedData = data; +} + void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data) { w->onPositionChanged = f; @@ -453,7 +482,8 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) setClientSize(w, width, height, hasMenubarBOOL, style, exstyle); uiWindowOnClosing(w, defaultOnClosing, NULL); - uiWindowOnPositionChanged(w, defaultOnPositionChanged, NULL); + uiWindowOnPositionChanged(w, defaultOnPositionContentSizeChanged, NULL); + uiWindowOnContentSizeChanged(w, defaultOnPositionContentSizeChanged, NULL); windows[w] = true; return w; From e5064de86ba9586e31d69cba3911e3b7287d6f80 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 15 Jun 2016 23:00:26 -0400 Subject: [PATCH 0355/1329] More TODOs. --- windows/window.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/windows/window.cpp b/windows/window.cpp index 64a1853f..140f1c87 100644 --- a/windows/window.cpp +++ b/windows/window.cpp @@ -350,7 +350,8 @@ void uiWindowCenter(uiWindow *w) y = (mht - wht) / 2; // y is now evenly divided, however https://msdn.microsoft.com/en-us/library/windows/desktop/dn742502(v=vs.85).aspx says that 45% should go above and 55% should go below // so just move 5% of the way up - // TODO is this correct? + // TODO should this be on the work area? + // TODO is this calculation correct? y -= y / 20; uiWindowSetPosition(w, x, y); } From 88bb697bbdb5d6215e3be138550d9bfedced849a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 16 Jun 2016 00:45:23 -0400 Subject: [PATCH 0356/1329] Implemented the new uiWindow stuff on GTK+. --- unix/window.c | 85 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 71 insertions(+), 14 deletions(-) diff --git a/unix/window.c b/unix/window.c index cdb3ab84..f0d88be4 100644 --- a/unix/window.c +++ b/unix/window.c @@ -12,9 +12,12 @@ struct uiWindow { GtkContainer *vboxContainer; GtkBox *vbox; + GtkWidget *childHolderWidget; + GtkContainer *childHolderContainer; + GtkWidget *menubar; - struct child *child; + uiControl *child; int margined; int (*onClosing)(uiWindow *, void *); @@ -22,6 +25,9 @@ struct uiWindow { void (*onPositionChanged)(uiWindow *, void *); void *onPositionChangedData; gboolean changingPosition; + void (*onContentSizeChanged)(uiWindow *, void *); + void *onContentSizeChangedData; + gboolean changingSize; }; static gboolean onClosing(GtkWidget *win, GdkEvent *e, gpointer data) @@ -48,12 +54,22 @@ static gboolean onConfigure(GtkWidget *win, GdkEvent *e, gpointer data) return FALSE; } +static void onSizeAllocate(GtkWidget *widget, GdkRectangle *allocation, gpointer data) +{ + uiWindow *w = uiWindow(data); + + if (w->changingSize) + w->changingSize = FALSE; + else + (*(w->onContentSizeChanged))(w, w->onContentSizeChangedData); +} + static int defaultOnClosing(uiWindow *w, void *data) { return 0; } -static void defaultOnPositionChanged(uiWindow *w, void *data) +static void defaultOnPositionContentSizeChanged(uiWindow *w, void *data) { // do nothing } @@ -65,11 +81,15 @@ static void uiWindowDestroy(uiControl *c) // first hide ourselves gtk_widget_hide(w->widget); // now destroy the child - if (w->child != NULL) - childDestroy(w->child); + if (w->child != NULL) { + uiControlSetParent(w->child, NULL); + uiUnixControlSetContainer(uiUnixControl(w->child), w->childHolderContainer, TRUE); + uiControlDestroy(w->child); + } // now destroy the menus, if any if (w->menubar != NULL) freeMenubar(w->menubar); + gtk_widget_destroy(w->childHolderWidget); gtk_widget_destroy(w->vboxWidget); // and finally free ourselves g_object_unref(w->widget); @@ -171,22 +191,50 @@ void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void w->onPositionChangedData = data; } +void uiWindowContentSize(uiWindow *w, int *width, int *height) +{ + GtkAllocation allocation; + + gtk_widget_get_allocation(w->childHolderWidget, &allocation); + *width = allocation.width; + *height = allocation.height; +} + +// TODO what happens if the size is already the current one? +// TODO a spurious size-allocate gets sent after this function returns +void uiWindowSetContentSize(uiWindow *w, int width, int height) +{ + w->changingSize = TRUE; + gtk_widget_set_size_request(w->childHolderWidget, width, height); + while (w->changingSize) + if (gtk_main_iteration() != FALSE) + break; // stop early if gtk_main_quit() called + gtk_widget_set_size_request(w->childHolderWidget, -1, -1); +} + +void uiWindowOnContentSizeChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data) +{ + w->onContentSizeChanged = f; + w->onContentSizeChangedData = data; +} + void uiWindowOnClosing(uiWindow *w, int (*f)(uiWindow *, void *), void *data) { w->onClosing = f; w->onClosingData = data; } +// TODO save and restore expands and aligns void uiWindowSetChild(uiWindow *w, uiControl *child) { - if (w->child != NULL) - childRemove(w->child); - w->child = newChildWithBox(child, uiControl(w), w->vboxContainer, w->margined); if (w->child != NULL) { - gtk_widget_set_hexpand(childBox(w->child), TRUE); - gtk_widget_set_halign(childBox(w->child), GTK_ALIGN_FILL); - gtk_widget_set_vexpand(childBox(w->child), TRUE); - gtk_widget_set_valign(childBox(w->child), GTK_ALIGN_FILL); + uiControlSetParent(w->child, NULL); + uiUnixControlSetContainer(uiUnixControl(w->child), w->childHolderContainer, TRUE); + } + w->child = child; + if (w->child != NULL) { + uiControlSetParent(w->child, uiControl(w)); + uiUnixControlSetContainer(uiUnixControl(w->child), w->childHolderContainer, FALSE); } } @@ -198,8 +246,7 @@ int uiWindowMargined(uiWindow *w) void uiWindowSetMargined(uiWindow *w, int margined) { w->margined = margined; - if (w->child != NULL) - childSetMargined(w->child, w->margined); + setMargined(w->childHolderContainer, w->margined); } uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) @@ -227,14 +274,24 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) gtk_container_add(w->vboxContainer, w->menubar); } + w->childHolderWidget = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + w->childHolderContainer = GTK_CONTAINER(w->childHolderWidget); + gtk_widget_set_hexpand(w->childHolderWidget, TRUE); + gtk_widget_set_halign(w->childHolderWidget, GTK_ALIGN_FILL); + gtk_widget_set_vexpand(w->childHolderWidget, TRUE); + gtk_widget_set_valign(w->childHolderWidget, GTK_ALIGN_FILL); + gtk_container_add(w->vboxContainer, w->childHolderWidget); + // show everything in the vbox, but not the GtkWindow itself gtk_widget_show_all(w->vboxWidget); // and connect our events g_signal_connect(w->widget, "delete-event", G_CALLBACK(onClosing), w); g_signal_connect(w->widget, "configure-event", G_CALLBACK(onConfigure), w); + g_signal_connect(w->childHolderWidget, "size-allocate", G_CALLBACK(onSizeAllocate), w); uiWindowOnClosing(w, defaultOnClosing, NULL); - uiWindowOnPositionChanged(w, defaultOnPositionChanged, NULL); + uiWindowOnPositionChanged(w, defaultOnPositionContentSizeChanged, NULL); + uiWindowOnContentSizeChanged(w, defaultOnPositionContentSizeChanged, NULL); // normally it's SetParent() that does this, but we can't call SetParent() on a uiWindow // TODO we really need to clean this up From caec39281bcaac4cd26b6d81f1418b086d0476c0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 16 Jun 2016 00:58:40 -0400 Subject: [PATCH 0357/1329] And implemented the new sizing stuff on OS X. --- README.md | 5 ++++- darwin/window.m | 42 ++++++++++++++++++++++++++++++++++++++++-- doc/windowmovesize | 3 +++ 3 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 doc/windowmovesize diff --git a/README.md b/README.md index 86a83fa7..4b615c8e 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,10 @@ This README is being written.
## Updates -*Note that today's entry may be updated later today Eastern Time.* +*Note that today's entry (Eastern Time) may be updated later today.* + +* **16 June 2016** + * Added `uiWindowContentSize()`, `uiWindowSetContentSize()`, and `uiWindowOnContentSizeChanged()` methods for manipulating uiWindow content sizes. Note the use of "content size"; the size you work with does NOT include window decorations (titlebars, menus, etc.). * **15 June 2016** * Added `uiFormDelete()`; thanks to @emersion. diff --git a/darwin/window.m b/darwin/window.m index 7cab10b4..03c86ac1 100644 --- a/darwin/window.m +++ b/darwin/window.m @@ -12,6 +12,9 @@ struct uiWindow { void (*onPositionChanged)(uiWindow *, void *); void *onPositionChangedData; BOOL suppressPositionChanged; + void (*onContentSizeChanged)(uiWindow *, void *); + void *onContentSizeChangedData; + BOOL suppressSizeChanged; }; @interface windowDelegateClass : NSObject { @@ -19,6 +22,7 @@ struct uiWindow { } - (BOOL)windowShouldClose:(id)sender; - (void)windowDidMove:(NSNotification *)note; +- (void)windowDidResize:(NSNotification *)note; - (void)registerWindow:(uiWindow *)w; - (void)unregisterWindow:(uiWindow *)w; - (uiWindow *)lookupWindow:(NSWindow *)w; @@ -61,6 +65,15 @@ struct uiWindow { (*(w->onPositionChanged))(w, w->onPositionChangedData); } +- (void)windowDidResize:(NSNotification *)note +{ + uiWindow *w; + + w = [self lookupWindow:((NSWindow *) [note object])]; + if (!w->suppressSizeChanged) + (*(w->onContentSizeChanged))(w, w->onContentSizeChangedData); +} + - (void)registerWindow:(uiWindow *)w { mapSet(self->windows, w->window, w); @@ -250,7 +263,9 @@ void uiWindowSetPosition(uiWindow *w, int x, int y) void uiWindowCenter(uiWindow *w) { + w->suppressPositionChanged = YES; [w->window center]; + w->suppressPositionChanged = NO; } void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data) @@ -259,6 +274,28 @@ void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void w->onPositionChangedData = data; } +void uiWindowContentSize(uiWindow *w, int *width, int *height) +{ + NSRect r; + + r = [w->window contentRectForFrameRect:[w->window frame]]; + *width = r.size.width; + *height = r.size.height; +} + +void uiWindowSetContentSize(uiWindow *w, int width, int height) +{ + w->suppressSizeChanged = YES; + [w->window setContentSize:NSMakeSize(width, height)]; + w->suppressSizeChanged = NO; +} + +void uiWindowOnContentSizeChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data) +{ + w->onContentSizeChanged = f; + w->onContentSizeChangedData = data; +} + void uiWindowOnClosing(uiWindow *w, int (*f)(uiWindow *, void *), void *data) { w->onClosing = f; @@ -300,7 +337,7 @@ static int defaultOnClosing(uiWindow *w, void *data) return 0; } -static void defaultOnPositionChanged(uiWindow *w, void *data) +static void defaultOnPositionContentSizeChanged(uiWindow *w, void *data) { // do nothing } @@ -329,7 +366,8 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) } [windowDelegate registerWindow:w]; uiWindowOnClosing(w, defaultOnClosing, NULL); - uiWindowOnPositionChanged(w, defaultOnPositionChanged, NULL); + uiWindowOnPositionChanged(w, defaultOnPositionContentSizeChanged, NULL); + uiWindowOnContentSizeChanged(w, defaultOnPositionContentSizeChanged, NULL); return w; } diff --git a/doc/windowmovesize b/doc/windowmovesize new file mode 100644 index 00000000..ec8bd966 --- /dev/null +++ b/doc/windowmovesize @@ -0,0 +1,3 @@ +you should never need to use these functions +they are provided only for the cases when ABSOLUTELY NECESSARY +the operating system may ignore your requests, for instance, if you are giving it invalid numbers or a size too small to fit; this is all system-defined From 7770b5c85043dbc030985fc544f7483c96a6bd95 Mon Sep 17 00:00:00 2001 From: emersion Date: Thu, 16 Jun 2016 09:32:13 +0200 Subject: [PATCH 0358/1329] Adds indeterminate progressbar to windows --- windows/progressbar.cpp | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/windows/progressbar.cpp b/windows/progressbar.cpp index d16569ad..2d155de1 100644 --- a/windows/progressbar.cpp +++ b/windows/progressbar.cpp @@ -26,14 +26,40 @@ static void uiProgressBarMinimumSize(uiWindowsControl *c, int *width, int *heigh *height = y; } +int uiProgressBarValue(uiProgressBar *p) +{ + LONG_PTR style = GetWindowLongPtr(p->hwnd, GWL_STYLE); + if ((style & PBS_MARQUEE) != 0) { + return -1; + } + + return (int) SendMessage(p->hwnd, PBM_GETPOS, 0, 0); +} + // unfortunately, as of Vista progress bars have a forced animation on increase // we have to set the progress bar to value + 1 and decrease it back to value if we want an "instant" change // see http://stackoverflow.com/questions/2217688/windows-7-aero-theme-progress-bar-bug // it's not ideal/perfect, but it will have to do void uiProgressBarSetValue(uiProgressBar *p, int value) { + LONG_PTR style = GetWindowLongPtr(p->hwnd, GWL_STYLE); + + if (value == -1) { + if ((style & PBS_MARQUEE) == 0) { + SetWindowLongPtr(p->hwnd, GWL_STYLE, style | PBS_MARQUEE); + SendMessage(p->hwnd, PBM_SETMARQUEE, 1, 0); + } + return; + } + + if ((style & PBS_MARQUEE) != 0) { + SetWindowLongPtr(p->hwnd, GWL_STYLE, style ^ PBS_MARQUEE); + SendMessage(p->hwnd, PBM_SETMARQUEE, 0, 0); + } + if (value < 0 || value > 100) userbug("Value %d is out of range for uiProgressBars.", value); + if (value == 100) { // because we can't 101 SendMessageW(p->hwnd, PBM_SETRANGE32, 0, 101); SendMessageW(p->hwnd, PBM_SETPOS, 101, 0); From cff5dcf9c91d2ce35eec0709e8f89ae2e18fa61b Mon Sep 17 00:00:00 2001 From: emersion Date: Thu, 16 Jun 2016 09:45:08 +0200 Subject: [PATCH 0359/1329] Adds indeterminate progressbar to darwin (not yet tested) --- darwin/progressbar.m | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/darwin/progressbar.m b/darwin/progressbar.m index a26874f1..87a9c8d2 100644 --- a/darwin/progressbar.m +++ b/darwin/progressbar.m @@ -27,10 +27,31 @@ struct uiProgressBar { uiDarwinControlAllDefaults(uiProgressBar, pi) +int uiProgressBarValue(uiProgressBar *p) +{ + if ([p->pi getIndeterminate]) { + return -1; + } + + return (int) [p->pi getDoubleValue]; +} + void uiProgressBarSetValue(uiProgressBar *p, int value) { + if (value == -1) { + [p->pi setIndeterminate:YES]; + [p->pi startAnimation:p->pi]; + return; + } + + if ([p->pi getIndeterminate]) { + [p->pi setIndeterminate:NO]; + [p->pi stopAnimation:p->pi]; + } + if (value < 0 || value > 100) userbug("Value %d out of range for a uiProgressBar.", value); + // on 10.8 there's an animation when the progress bar increases, just like with Aero if (value == 100) { [p->pi setMaxValue:101]; From 69cafde7f30c215621574dbc0e1a79548cc79fdb Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 16 Jun 2016 08:07:50 -0400 Subject: [PATCH 0360/1329] Started the fullscreen window stuff. --- test/page15.c | 16 +++++++++++++--- ui.h | 2 ++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/test/page15.c b/test/page15.c index 618ac709..0bd92c4f 100644 --- a/test/page15.c +++ b/test/page15.c @@ -3,6 +3,7 @@ static uiSpinbox *x, *y; static uiSpinbox *width, *height; +static uiCheckbox *fullscreen; static void moveX(uiSpinbox *s, void *data) { @@ -74,6 +75,7 @@ static void updatesize(uiWindow *w) uiWindowContentSize(w, &xp, &yp); uiSpinboxSetValue(width, xp); uiSpinboxSetValue(height, yp); + uiCheckboxSetChecked(fullscreen, uiWindowFullscreen(w)); } void onSize(uiWindow *w, void *data) @@ -82,6 +84,14 @@ void onSize(uiWindow *w, void *data) updatesize(w); } +void setFullscreen(uiCheckbox *cb, void *data) +{ + uiWindow *w = uiWindow(data); + + uiWindowSetFullscreen(w, uiCheckboxChecked(tb)); + updatesize(w); +} + uiBox *makePage15(uiWindow *w) { uiBox *page15; @@ -116,12 +126,12 @@ uiBox *makePage15(uiWindow *w) uiBoxAppend(hbox, uiControl(width), 1); height = uiNewSpinbox(INT_MIN, INT_MAX); uiBoxAppend(hbox, uiControl(height), 1); -// button = uiNewButton("Center"); -// uiBoxAppend(hbox, uiControl(button), 0); + fullscreen = uiNewCheckbox("Fullscreen"); + uiBoxAppend(hbox, uiControl(fullscreen), 0); uiSpinboxOnChanged(width, sizeWidth, w); uiSpinboxOnChanged(height, sizeHeight, w); -// uiButtonOnClicked(button, center, w); + uiCheckboxOnToggled(fullscreen, setFullscreen, w); uiWindowOnContentSizeChanged(w, onSize, NULL); updatesize(w); diff --git a/ui.h b/ui.h index 1ab01e91..b5419482 100644 --- a/ui.h +++ b/ui.h @@ -105,6 +105,8 @@ _UI_EXTERN void uiWindowCenter(uiWindow *w); _UI_EXTERN void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data); _UI_EXTERN void uiWindowContentSize(uiWindow *w, int *width, int *height); _UI_EXTERN void uiWindowSetContentSize(uiWindow *w, int width, int height); +_UI_EXTERN int uiWindowFullscreen(uiWindow *w); +_UI_EXTERN void uiWindowSetFullscreen(uiWindow *w, int fullscreen); _UI_EXTERN void uiWindowOnContentSizeChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data); _UI_EXTERN void uiWindowOnClosing(uiWindow *w, int (*f)(uiWindow *w, void *data), void *data); _UI_EXTERN void uiWindowSetChild(uiWindow *w, uiControl *child); From fd9af041238845059e70da90852e1d40259d2f80 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 16 Jun 2016 09:29:28 -0400 Subject: [PATCH 0361/1329] Implemented fullscreen on OS X. --- darwin/window.m | 38 ++++++++++++++++++++++++++++++++++++++ test/page15.c | 3 ++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/darwin/window.m b/darwin/window.m index 03c86ac1..0315e6a3 100644 --- a/darwin/window.m +++ b/darwin/window.m @@ -15,6 +15,7 @@ struct uiWindow { void (*onContentSizeChanged)(uiWindow *, void *); void *onContentSizeChangedData; BOOL suppressSizeChanged; + int fullscreen; }; @interface windowDelegateClass : NSObject { @@ -23,6 +24,8 @@ struct uiWindow { - (BOOL)windowShouldClose:(id)sender; - (void)windowDidMove:(NSNotification *)note; - (void)windowDidResize:(NSNotification *)note; +- (void)windowDidEnterFullScreen:(NSNotification *)note; +- (void)windowDidExitFullScreen:(NSNotification *)note; - (void)registerWindow:(uiWindow *)w; - (void)unregisterWindow:(uiWindow *)w; - (uiWindow *)lookupWindow:(NSWindow *)w; @@ -74,6 +77,24 @@ struct uiWindow { (*(w->onContentSizeChanged))(w, w->onContentSizeChangedData); } +- (void)windowDidEnterFullScreen:(NSNotification *)note +{ + uiWindow *w; + + w = [self lookupWindow:((NSWindow *) [note object])]; + if (!w->suppressSizeChanged) + w->fullscreen = 1; +} + +- (void)windowDidExitFullScreen:(NSNotification *)note +{ + uiWindow *w; + + w = [self lookupWindow:((NSWindow *) [note object])]; + if (!w->suppressSizeChanged) + w->fullscreen = 0; +} + - (void)registerWindow:(uiWindow *)w { mapSet(self->windows, w->window, w); @@ -290,6 +311,23 @@ void uiWindowSetContentSize(uiWindow *w, int width, int height) w->suppressSizeChanged = NO; } +int uiWindowFullscreen(uiWindow *w) +{ + return w->fullscreen; +} + +void uiWindowSetFullscreen(uiWindow *w, int fullscreen) +{ + if (w->fullscreen && fullscreen) + return; + if (!w->fullscreen && !fullscreen) + return; + w->fullscreen = fullscreen; + w->suppressSizeChanged = YES; + [w->window toggleFullScreen:w->window]; + w->suppressSizeChanged = NO; +} + void uiWindowOnContentSizeChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data) { w->onContentSizeChanged = f; diff --git a/test/page15.c b/test/page15.c index 0bd92c4f..da5f90f5 100644 --- a/test/page15.c +++ b/test/page15.c @@ -75,6 +75,7 @@ static void updatesize(uiWindow *w) uiWindowContentSize(w, &xp, &yp); uiSpinboxSetValue(width, xp); uiSpinboxSetValue(height, yp); + // TODO on OS X this is updated AFTER sending the size change, not before uiCheckboxSetChecked(fullscreen, uiWindowFullscreen(w)); } @@ -88,7 +89,7 @@ void setFullscreen(uiCheckbox *cb, void *data) { uiWindow *w = uiWindow(data); - uiWindowSetFullscreen(w, uiCheckboxChecked(tb)); + uiWindowSetFullscreen(w, uiCheckboxChecked(fullscreen)); updatesize(w); } From 132d925b708a7dc896f00fee6a5ad72afc2692d9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 16 Jun 2016 10:03:35 -0400 Subject: [PATCH 0362/1329] Implemented the fullscreen stuff on GTK+. --- unix/window.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/unix/window.c b/unix/window.c index f0d88be4..8b953a4a 100644 --- a/unix/window.c +++ b/unix/window.c @@ -28,6 +28,7 @@ struct uiWindow { void (*onContentSizeChanged)(uiWindow *, void *); void *onContentSizeChangedData; gboolean changingSize; + gboolean fullscreen; }; static gboolean onClosing(GtkWidget *win, GdkEvent *e, gpointer data) @@ -212,6 +213,22 @@ void uiWindowSetContentSize(uiWindow *w, int width, int height) gtk_widget_set_size_request(w->childHolderWidget, -1, -1); } +int uiWindowFullscreen(uiWindow *w) +{ + return w->fullscreen; +} + +// TODO use window-state-event to track +// TODO does this send an extra size changed? +void uiWindowSetFullscreen(uiWindow *w, int fullscreen) +{ + w->fullscreen = fullscreen; + if (w->fullscreen) + gtk_window_fullscreen(w->window); + else + gtk_window_unfullscreen(w->window); +} + void uiWindowOnContentSizeChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data) { w->onContentSizeChanged = f; From aafdb75a98a54ae2bdacf29c6fd860187ac4d2fd Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 16 Jun 2016 10:30:44 -0400 Subject: [PATCH 0363/1329] And implemented the fullscreen stuff on Windows. --- README.md | 1 + windows/window.cpp | 53 ++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 4b615c8e..d71399ce 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ This README is being written.
* **16 June 2016** * Added `uiWindowContentSize()`, `uiWindowSetContentSize()`, and `uiWindowOnContentSizeChanged()` methods for manipulating uiWindow content sizes. Note the use of "content size"; the size you work with does NOT include window decorations (titlebars, menus, etc.). + * Added `uiWindowFullscreen()` and `uiWindowSetFullscreen()` to allow making fullscreen uiWindows, taking advantage of OS facilities for fullscreen and without changing the screen resolution (!). * **15 June 2016** * Added `uiFormDelete()`; thanks to @emersion. diff --git a/windows/window.cpp b/windows/window.cpp index 140f1c87..aea9c225 100644 --- a/windows/window.cpp +++ b/windows/window.cpp @@ -20,6 +20,8 @@ struct uiWindow { void (*onContentSizeChanged)(uiWindow *, void *); void *onContentSizeChangedData; BOOL changingSize; + int fullscreen; + WINDOWPLACEMENT fsPrevPlacement; }; // from https://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing @@ -356,6 +358,12 @@ void uiWindowCenter(uiWindow *w) uiWindowSetPosition(w, x, y); } +void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data) +{ + w->onPositionChanged = f; + w->onPositionChangedData = data; +} + void uiWindowContentSize(uiWindow *w, int *width, int *height) { RECT r; @@ -375,18 +383,51 @@ void uiWindowSetContentSize(uiWindow *w, int width, int height) w->changingSize = FALSE; } +int uiWindowFullscreen(uiWindow *w) +{ + return w->fullscreen; +} + +void uiWindowSetFullscreen(uiWindow *w, int fullscreen) +{ + RECT r; + + if (w->fullscreen && fullscreen) + return; + if (!w->fullscreen && !fullscreen) + return; + w->fullscreen = fullscreen; + w->changingSize = TRUE; + if (w->fullscreen) { + ZeroMemory(&(w->fsPrevPlacement), sizeof (WINDOWPLACEMENT)); + w->fsPrevPlacement.length = sizeof (WINDOWPLACEMENT); + if (GetWindowPlacement(w->hwnd, &(w->fsPrevPlacement)) == 0) + logLastError(L"error getting old window placement"); + windowMonitorRect(w->hwnd, &r); + setStyle(w->hwnd, getStyle(w->hwnd) & ~WS_OVERLAPPEDWINDOW); + if (SetWindowPos(w->hwnd, HWND_TOP, + r.left, r.top, + r.right - r.left, r.bottom - r.top, + SWP_FRAMECHANGED | SWP_NOOWNERZORDER) == 0) + logLastError(L"error making window fullscreen"); + } else { + setStyle(w->hwnd, getStyle(w->hwnd) | WS_OVERLAPPEDWINDOW); + if (SetWindowPlacement(w->hwnd, &(w->fsPrevPlacement)) == 0) + logLastError(L"error leaving fullscreen"); + if (SetWindowPos(w->hwnd, NULL, + 0, 0, 0, 0, + SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOZORDER) == 0) + logLastError(L"error restoring window border after fullscreen"); + } + w->changingSize = FALSE; +} + void uiWindowOnContentSizeChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data) { w->onContentSizeChanged = f; w->onContentSizeChangedData = data; } -void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data) -{ - w->onPositionChanged = f; - w->onPositionChangedData = data; -} - void uiWindowOnClosing(uiWindow *w, int (*f)(uiWindow *, void *), void *data) { w->onClosing = f; From 377f46814a3c34fa915d42df1e372bbaccb7dcd7 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 16 Jun 2016 11:20:28 -0400 Subject: [PATCH 0364/1329] Started adding borderless uiWindow support; implemented on Windows. --- test/page15.c | 12 ++++++++++++ ui.h | 2 ++ windows/window.cpp | 19 ++++++++++++++++++- 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/test/page15.c b/test/page15.c index da5f90f5..50cf155a 100644 --- a/test/page15.c +++ b/test/page15.c @@ -93,11 +93,19 @@ void setFullscreen(uiCheckbox *cb, void *data) updatesize(w); } +static void borderless(uiCheckbox *c, void *data) +{ + uiWindow *w = uiWindow(data); + + uiWindowSetBorderless(w, uiCheckboxChecked(c)); +} + uiBox *makePage15(uiWindow *w) { uiBox *page15; uiBox *hbox; uiButton *button; + uiCheckbox *checkbox; page15 = newVerticalBox(); @@ -136,5 +144,9 @@ uiBox *makePage15(uiWindow *w) uiWindowOnContentSizeChanged(w, onSize, NULL); updatesize(w); + checkbox = uiNewCheckbox("Borderless"); + uiCheckboxOnToggled(checkbox, borderless, w); + uiBoxAppend(page15, uiControl(checkbox), 0); + return page15; } diff --git a/ui.h b/ui.h index b5419482..f6d6dd83 100644 --- a/ui.h +++ b/ui.h @@ -109,6 +109,8 @@ _UI_EXTERN int uiWindowFullscreen(uiWindow *w); _UI_EXTERN void uiWindowSetFullscreen(uiWindow *w, int fullscreen); _UI_EXTERN void uiWindowOnContentSizeChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data); _UI_EXTERN void uiWindowOnClosing(uiWindow *w, int (*f)(uiWindow *w, void *data), void *data); +_UI_EXTERN int uiWindowBorderless(uiWindow *w); +_UI_EXTERN void uiWindowSetBorderless(uiWindow *w, int borderless); _UI_EXTERN void uiWindowSetChild(uiWindow *w, uiControl *child); _UI_EXTERN int uiWindowMargined(uiWindow *w); _UI_EXTERN void uiWindowSetMargined(uiWindow *w, int margined); diff --git a/windows/window.cpp b/windows/window.cpp index aea9c225..760702ba 100644 --- a/windows/window.cpp +++ b/windows/window.cpp @@ -22,6 +22,7 @@ struct uiWindow { BOOL changingSize; int fullscreen; WINDOWPLACEMENT fsPrevPlacement; + int borderless; }; // from https://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing @@ -411,7 +412,8 @@ void uiWindowSetFullscreen(uiWindow *w, int fullscreen) SWP_FRAMECHANGED | SWP_NOOWNERZORDER) == 0) logLastError(L"error making window fullscreen"); } else { - setStyle(w->hwnd, getStyle(w->hwnd) | WS_OVERLAPPEDWINDOW); + if (!w->borderless) // keep borderless until that is turned off + setStyle(w->hwnd, getStyle(w->hwnd) | WS_OVERLAPPEDWINDOW); if (SetWindowPlacement(w->hwnd, &(w->fsPrevPlacement)) == 0) logLastError(L"error leaving fullscreen"); if (SetWindowPos(w->hwnd, NULL, @@ -434,6 +436,21 @@ void uiWindowOnClosing(uiWindow *w, int (*f)(uiWindow *, void *), void *data) w->onClosingData = data; } +int uiWindowBorderless(uiWindow *w) +{ + return w->borderless; +} + +void uiWindowSetBorderless(uiWindow *w, int borderless) +{ + w->borderless = borderless; + if (w->borderless) + setStyle(w->hwnd, getStyle(w->hwnd) & ~WS_OVERLAPPEDWINDOW); + else + if (!w->fullscreen) // keep borderless until leaving fullscreen + setStyle(w->hwnd, getStyle(w->hwnd) | WS_OVERLAPPEDWINDOW); +} + void uiWindowSetChild(uiWindow *w, uiControl *child) { if (w->child != NULL) { From 256a452fbd7f09625651c71e8b6a01147f4cde3a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 16 Jun 2016 11:34:19 -0400 Subject: [PATCH 0365/1329] Implemented borderless windows on GTK+. --- unix/window.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/unix/window.c b/unix/window.c index 8b953a4a..bd75b9b5 100644 --- a/unix/window.c +++ b/unix/window.c @@ -241,6 +241,16 @@ void uiWindowOnClosing(uiWindow *w, int (*f)(uiWindow *, void *), void *data) w->onClosingData = data; } +int uiWindowBorderless(uiWindow *w) +{ + return gtk_window_get_decorated(w->window) == FALSE; +} + +void uiWindowSetBorderless(uiWindow *w, int borderless) +{ + gtk_window_set_decorated(w->window, borderless == 0); +} + // TODO save and restore expands and aligns void uiWindowSetChild(uiWindow *w, uiControl *child) { From dd2ee507107d75aad886ea1829e4e50ccf58dfb1 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 16 Jun 2016 11:46:58 -0400 Subject: [PATCH 0366/1329] And implemented borderless windows on OS X. --- README.md | 1 + darwin/window.m | 32 +++++++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d71399ce..23bdec5b 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ This README is being written.
* **16 June 2016** * Added `uiWindowContentSize()`, `uiWindowSetContentSize()`, and `uiWindowOnContentSizeChanged()` methods for manipulating uiWindow content sizes. Note the use of "content size"; the size you work with does NOT include window decorations (titlebars, menus, etc.). * Added `uiWindowFullscreen()` and `uiWindowSetFullscreen()` to allow making fullscreen uiWindows, taking advantage of OS facilities for fullscreen and without changing the screen resolution (!). + * Added `uiWindowBorderless()` and `uiWindowSetBorderless()` for allowing borderless uiWindows. * **15 June 2016** * Added `uiFormDelete()`; thanks to @emersion. diff --git a/darwin/window.m b/darwin/window.m index 0315e6a3..8236bee1 100644 --- a/darwin/window.m +++ b/darwin/window.m @@ -1,6 +1,8 @@ // 15 august 2015 #import "uipriv_darwin.h" +#define defaultStyleMask (NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask) + struct uiWindow { uiDarwinControl c; NSWindow *window; @@ -16,6 +18,7 @@ struct uiWindow { void *onContentSizeChangedData; BOOL suppressSizeChanged; int fullscreen; + int borderless; }; @interface windowDelegateClass : NSObject { @@ -323,9 +326,13 @@ void uiWindowSetFullscreen(uiWindow *w, int fullscreen) if (!w->fullscreen && !fullscreen) return; w->fullscreen = fullscreen; + if (w->fullscreen && w->borderless) // borderless doesn't play nice with fullscreen; don't toggle while borderless + return; w->suppressSizeChanged = YES; [w->window toggleFullScreen:w->window]; w->suppressSizeChanged = NO; + if (!w->fullscreen && w->borderless) // borderless doesn't play nice with fullscreen; restore borderless after removing + [w->window setStyleMask:NSBorderlessWindowMask]; } void uiWindowOnContentSizeChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data) @@ -340,6 +347,29 @@ void uiWindowOnClosing(uiWindow *w, int (*f)(uiWindow *, void *), void *data) w->onClosingData = data; } +int uiWindowBorderless(uiWindow *w) +{ + return w->borderless; +} + +void uiWindowSetBorderless(uiWindow *w, int borderless) +{ + w->borderless = borderless; + if (w->borderless) { + // borderless doesn't play nice with fullscreen; wait for later + if (!w->fullscreen) + [w->window setStyleMask:NSBorderlessWindowMask]; + } else { + [w->window setStyleMask:defaultStyleMask]; + // borderless doesn't play nice with fullscreen; restore state + if (w->fullscreen) { + w->suppressSizeChanged = YES; + [w->window toggleFullScreen:w->window]; + w->suppressSizeChanged = NO; + } + } +} + void uiWindowSetChild(uiWindow *w, uiControl *child) { NSView *childView; @@ -389,7 +419,7 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) uiDarwinNewControl(uiWindow, w); w->window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, (CGFloat) width, (CGFloat) height) - styleMask:(NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask) + styleMask:defaultStyleMask backing:NSBackingStoreBuffered defer:YES]; [w->window setTitle:toNSString(title)]; From b4d0e08a22145e9bd207a5ab8188e2f11a92e1a2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 16 Jun 2016 13:41:13 -0400 Subject: [PATCH 0367/1329] Started the work to fix uiMainStep(). --- test/main.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/test/main.c b/test/main.c index ae3e8af7..d5e31bf6 100644 --- a/test/main.c +++ b/test/main.c @@ -54,6 +54,7 @@ int main(int argc, char *argv[]) uiTab *innerTab; int nomenus = 0; int startspaced = 0; + int steps = 0; newhbox = uiNewHorizontalBox; newvbox = uiNewVerticalBox; @@ -67,7 +68,9 @@ int main(int argc, char *argv[]) else if (strcmp(argv[i], "swaphv") == 0) { newhbox = uiNewVerticalBox; newvbox = uiNewHorizontalBox; - } else { + } else if (strcmp(argv[i], "steps") == 0) + steps = 1; + else { fprintf(stderr, "%s: unrecognized option %s\n", argv[0], argv[i]); return 1; } @@ -156,7 +159,11 @@ int main(int argc, char *argv[]) setSpaced(1); uiControlShow(uiControl(w)); - uiMain(); + if (!steps) + uiMain(); + else + while (uiMainStep(1)) + ; printf("after uiMain()\n"); uiUninit(); printf("after uiUninit()\n"); From da4b396aaf48204a2da8d94289d813612d798aa9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 16 Jun 2016 13:45:24 -0400 Subject: [PATCH 0368/1329] Added uiMainSteps(), which sets things up to use uiMainStep() for the main loop. Implemented on OS X. --- darwin/main.m | 23 ++++++++++++++++++++--- test/main.c | 4 +++- ui.h | 1 + 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/darwin/main.m b/darwin/main.m index 97821201..2c934330 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -3,8 +3,11 @@ static BOOL canQuit = NO; static NSAutoreleasePool *globalPool; -static applicationClass* app; -static appDelegate* delegate; +static applicationClass *app; +static appDelegate *delegate; + +static BOOL (^isRunning)(void); +static BOOL stepsIsRunning; @implementation applicationClass @@ -67,6 +70,9 @@ static appDelegate* delegate; data1:0 data2:0]; [realNSApp() postEvent:e atStart:NO]; // let pending events take priority (this is what PostQuitMessage() on Windows does so we have to do it here too for parity; thanks to mikeash in irc.freenode.net/#macdev for confirming that this parameter should indeed be NO) + + // and in case uiMainSteps() was called + stepsIsRunning = NO; } @end @@ -147,9 +153,20 @@ void uiFreeInitError(const char *err) void uiMain(void) { + isRunning = ^{ + return [realNSApp() isRunning]; + }; [realNSApp() run]; } +void uiMainSteps(void) +{ + isRunning = ^{ + return stepsIsRunning; + }; + stepsIsRunning = YES; +} + // see also: // - http://www.cocoawithlove.com/2009/01/demystifying-nsapplication-by.html // - https://github.com/gnustep/gui/blob/master/Source/NSApplication.m @@ -166,7 +183,7 @@ int uiMainStep(int wait) if (wait) // but this is normal so it will work expire = [NSDate distantFuture]; - if (![realNSApp() isRunning]) + if (!isRunning()) return 0; e = [realNSApp() nextEventMatchingMask:NSAnyEventMask diff --git a/test/main.c b/test/main.c index d5e31bf6..803326b5 100644 --- a/test/main.c +++ b/test/main.c @@ -161,9 +161,11 @@ int main(int argc, char *argv[]) uiControlShow(uiControl(w)); if (!steps) uiMain(); - else + else { + uiMainSteps(); while (uiMainStep(1)) ; + } printf("after uiMain()\n"); uiUninit(); printf("after uiUninit()\n"); diff --git a/ui.h b/ui.h index f6d6dd83..6b7a1e8b 100644 --- a/ui.h +++ b/ui.h @@ -45,6 +45,7 @@ _UI_EXTERN void uiUninit(void); _UI_EXTERN void uiFreeInitError(const char *err); _UI_EXTERN void uiMain(void); +_UI_EXTERN void uiMainSteps(void); _UI_EXTERN int uiMainStep(int wait); _UI_EXTERN void uiQuit(void); From d4492c06a96a1fd9d6951afe43c4578cdf9c8212 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 16 Jun 2016 13:49:50 -0400 Subject: [PATCH 0369/1329] Changed the uiMainSteps() API as the GTK+ port will need it to be done this way... --- darwin/main.m | 3 ++- test/main.c | 13 ++++++++----- ui.h | 2 +- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/darwin/main.m b/darwin/main.m index 2c934330..6faef4f4 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -159,12 +159,13 @@ void uiMain(void) [realNSApp() run]; } -void uiMainSteps(void) +void uiMainSteps(void (*f)(void *), void *data) { isRunning = ^{ return stepsIsRunning; }; stepsIsRunning = YES; + (*f)(data); } // see also: diff --git a/test/main.c b/test/main.c index 803326b5..e69feee7 100644 --- a/test/main.c +++ b/test/main.c @@ -39,6 +39,12 @@ uiTab *mainTab; uiBox *(*newhbox)(void); uiBox *(*newvbox)(void); +static void stepsLoop(void *data) +{ + while (uiMainStep(1)) + ; +} + int main(int argc, char *argv[]) { uiInitOptions o; @@ -161,11 +167,8 @@ int main(int argc, char *argv[]) uiControlShow(uiControl(w)); if (!steps) uiMain(); - else { - uiMainSteps(); - while (uiMainStep(1)) - ; - } + else + uiMainSteps(stepsLoop, NULL); printf("after uiMain()\n"); uiUninit(); printf("after uiUninit()\n"); diff --git a/ui.h b/ui.h index 6b7a1e8b..4fdd9e3e 100644 --- a/ui.h +++ b/ui.h @@ -45,7 +45,7 @@ _UI_EXTERN void uiUninit(void); _UI_EXTERN void uiFreeInitError(const char *err); _UI_EXTERN void uiMain(void); -_UI_EXTERN void uiMainSteps(void); +_UI_EXTERN void uiMainSteps(void (*f)(void *), void *data); _UI_EXTERN int uiMainStep(int wait); _UI_EXTERN void uiQuit(void); From c001c164f1fb610bb3773f6e9b0e9312b59e4699 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 16 Jun 2016 13:59:35 -0400 Subject: [PATCH 0370/1329] Implemented uiMainSteps() on GTK+. --- unix/main.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/unix/main.c b/unix/main.c index bfd05448..59590886 100644 --- a/unix/main.c +++ b/unix/main.c @@ -34,6 +34,30 @@ void uiMain(void) gtk_main(); } +struct mainStepsData { + void (*f)(void *); + void *data; +}; + +static gboolean mainSteps(gpointer data) +{ + struct mainStepsData *d = (struct mainStepsData *) data; + + (*(d->f))(d->data); + // TODO call gtk_main_quit() here again? + return FALSE; +} + +void uiMainSteps(void (*f)(void *), void *data) +{ + struct mainStepsData d; + + d.f = f; + d.data = data; + gdk_threads_add_idle(mainSteps, &d); + gtk_main(); +} + int uiMainStep(int wait) { gboolean block; From 179042025ac3849b26222709ae1b267b5c2ffd65 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 16 Jun 2016 14:12:47 -0400 Subject: [PATCH 0371/1329] And implemented on Windows, and more TODOs on top of that. --- README.md | 1 + TODO.md | 5 +++++ doc/mainsteps | 1 + windows/main.cpp | 5 +++++ 4 files changed, 12 insertions(+) create mode 100644 doc/mainsteps diff --git a/README.md b/README.md index 23bdec5b..e3bba8e1 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ This README is being written.
* Added `uiWindowContentSize()`, `uiWindowSetContentSize()`, and `uiWindowOnContentSizeChanged()` methods for manipulating uiWindow content sizes. Note the use of "content size"; the size you work with does NOT include window decorations (titlebars, menus, etc.). * Added `uiWindowFullscreen()` and `uiWindowSetFullscreen()` to allow making fullscreen uiWindows, taking advantage of OS facilities for fullscreen and without changing the screen resolution (!). * Added `uiWindowBorderless()` and `uiWindowSetBorderless()` for allowing borderless uiWindows. + * Added `uiMainSteps()`. You call this instead of `uiMain()` if you want to run the main loop yourself. You pass in a function that will be called; within that function, you call `uiMainStep()` repeatedly until it returns 0, doing whatever you need to do in the meantime. (This was needed because just having `uiMainStep()` by itself only worked on some systems.) * **15 June 2016** * Added `uiFormDelete()`; thanks to @emersion. diff --git a/TODO.md b/TODO.md index 981deef1..d91a3eab 100644 --- a/TODO.md +++ b/TODO.md @@ -93,3 +93,8 @@ notes - group and tab should act as if they have no child if the child is hidden on windows + + + +- a way to do recursive main loops + - how do we handle 0 returns from non-recursive uiMainStep() calls that aren't the main loop? (event handlers, for instance) diff --git a/doc/mainsteps b/doc/mainsteps new file mode 100644 index 00000000..f572b21c --- /dev/null +++ b/doc/mainsteps @@ -0,0 +1 @@ +the function passed to mainsteps must not return until uiQuit itself has been called; otherwise the results are undefined diff --git a/windows/main.cpp b/windows/main.cpp index 1db790cb..5cc34332 100644 --- a/windows/main.cpp +++ b/windows/main.cpp @@ -77,6 +77,11 @@ void uiMain(void) ; } +void uiMainSteps(void (*f)(void *), void *data) +{ + (*f)(data); +} + static int peekMessage(MSG *msg) { BOOL res; From 015008976f1a087fcb797d5182003b7edb3d423c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 16 Jun 2016 16:44:53 -0400 Subject: [PATCH 0372/1329] More documentation stuff. --- doc/areahandler | 1 + 1 file changed, 1 insertion(+) create mode 100644 doc/areahandler diff --git a/doc/areahandler b/doc/areahandler new file mode 100644 index 00000000..4c559db5 --- /dev/null +++ b/doc/areahandler @@ -0,0 +1 @@ +Yes, you keep ownership of the uiAreaHandler. libui only cares about the address you give uiNewArea(); it doesn't copy anything. You can even use the same uiAreaHandler on multiple uiAreas, which is why you get the uiArea as a parameter in each function. From 99545e8775a7c53e957fa4f9e09e3fcfbcf40688 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 16 Jun 2016 17:43:04 -0400 Subject: [PATCH 0373/1329] Fixed up the progressbar changes. --- README.md | 1 + darwin/progressbar.m | 8 +++----- test/page13.c | 14 +++++++------- unix/progressbar.c | 23 ++++++++++++----------- windows/progressbar.cpp | 24 ++++++++++-------------- 5 files changed, 33 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index e3bba8e1..8a71e539 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ This README is being written.
* Added `uiWindowFullscreen()` and `uiWindowSetFullscreen()` to allow making fullscreen uiWindows, taking advantage of OS facilities for fullscreen and without changing the screen resolution (!). * Added `uiWindowBorderless()` and `uiWindowSetBorderless()` for allowing borderless uiWindows. * Added `uiMainSteps()`. You call this instead of `uiMain()` if you want to run the main loop yourself. You pass in a function that will be called; within that function, you call `uiMainStep()` repeatedly until it returns 0, doing whatever you need to do in the meantime. (This was needed because just having `uiMainStep()` by itself only worked on some systems.) + * Added `uiProgressBarValue()` and allowed passing -1 to `uiProgressBarSetValue()` to make an indeterminate progress bar. Thanks to @emersion. * **15 June 2016** * Added `uiFormDelete()`; thanks to @emersion. diff --git a/darwin/progressbar.m b/darwin/progressbar.m index 87a9c8d2..b5382281 100644 --- a/darwin/progressbar.m +++ b/darwin/progressbar.m @@ -29,11 +29,9 @@ uiDarwinControlAllDefaults(uiProgressBar, pi) int uiProgressBarValue(uiProgressBar *p) { - if ([p->pi getIndeterminate]) { + if ([p->pi isIndeterminate]) return -1; - } - - return (int) [p->pi getDoubleValue]; + return [p->pi doubleValue]; } void uiProgressBarSetValue(uiProgressBar *p, int value) @@ -44,7 +42,7 @@ void uiProgressBarSetValue(uiProgressBar *p, int value) return; } - if ([p->pi getIndeterminate]) { + if ([p->pi isIndeterminate]) { [p->pi setIndeterminate:NO]; [p->pi stopAnimation:p->pi]; } diff --git a/test/page13.c b/test/page13.c index 6eb19f29..cda91ce7 100644 --- a/test/page13.c +++ b/test/page13.c @@ -78,14 +78,13 @@ static void showHide(uiButton *b, void *data) static void setIndeterminate(uiButton *b, void *data) { uiProgressBar *p = uiProgressBar(data); + int value; - int value = uiProgressBarValue(p); - if (value == -1) { - value = 0; - } else { + value = uiProgressBarValue(p); + if (value == -1) + value = 50; + else value = -1; - } - uiProgressBarSetValue(p, value); } @@ -139,8 +138,9 @@ uiBox *makePage13(void) uiFormAppend(f, "MLE", uiControl(uiNewMultilineEntry()), 1); p = uiNewProgressBar(); + uiProgressBarSetValue(p, 50); uiBoxAppend(page13, uiControl(p), 0); - b = uiNewButton("Toggle indeterminate"); + b = uiNewButton("Toggle Indeterminate"); uiButtonOnClicked(b, setIndeterminate, p); uiBoxAppend(page13, uiControl(b), 0); diff --git a/unix/progressbar.c b/unix/progressbar.c index 9e3fd4a4..08b45f33 100644 --- a/unix/progressbar.c +++ b/unix/progressbar.c @@ -5,7 +5,8 @@ struct uiProgressBar { uiUnixControl c; GtkWidget *widget; GtkProgressBar *pbar; - int indeterminate; + gboolean indeterminate; + guint pulser; }; uiUnixControlAllDefaults(uiProgressBar) @@ -14,35 +15,35 @@ int uiProgressBarValue(uiProgressBar *p) { if (p->indeterminate) return -1; - return (int) (gtk_progress_bar_get_fraction(p->pbar) * 100); } -gboolean uiProgressBarPulse(void* data) +static gboolean pulse(void* data) { - uiProgressBar *p = (uiProgressBar*) data; - - if (!GTK_IS_WIDGET(p->pbar) || !p->indeterminate) - return 0; + uiProgressBar *p = uiProgressBar(data); gtk_progress_bar_pulse(p->pbar); - return 1; + return TRUE; } void uiProgressBarSetValue(uiProgressBar *p, int value) { if (value == -1) { if (!p->indeterminate) { - p->indeterminate = 1; - g_timeout_add(100, uiProgressBarPulse, p); + p->indeterminate = TRUE; + // TODO verify the timeout + p->pulser = g_timeout_add(100, pulse, p); } return; } + if (p->indeterminate) { + p->indeterminate = FALSE; + g_source_remove(p->pulser); + } if (value < 0 || value > 100) userbug("Value %d is out of range for a uiProgressBar.", value); - p->indeterminate = 0; gtk_progress_bar_set_fraction(p->pbar, ((gdouble) value) / 100); } diff --git a/windows/progressbar.cpp b/windows/progressbar.cpp index 2d155de1..3750eb6a 100644 --- a/windows/progressbar.cpp +++ b/windows/progressbar.cpp @@ -26,14 +26,13 @@ static void uiProgressBarMinimumSize(uiWindowsControl *c, int *width, int *heigh *height = y; } +#define indeterminate(p) ((getStyle(p->hwnd) & PBS_MARQUEE) != 0) + int uiProgressBarValue(uiProgressBar *p) { - LONG_PTR style = GetWindowLongPtr(p->hwnd, GWL_STYLE); - if ((style & PBS_MARQUEE) != 0) { + if (indeterminate(p)) return -1; - } - - return (int) SendMessage(p->hwnd, PBM_GETPOS, 0, 0); + return SendMessage(p->hwnd, PBM_GETPOS, 0, 0); } // unfortunately, as of Vista progress bars have a forced animation on increase @@ -42,19 +41,16 @@ int uiProgressBarValue(uiProgressBar *p) // it's not ideal/perfect, but it will have to do void uiProgressBarSetValue(uiProgressBar *p, int value) { - LONG_PTR style = GetWindowLongPtr(p->hwnd, GWL_STYLE); - if (value == -1) { - if ((style & PBS_MARQUEE) == 0) { - SetWindowLongPtr(p->hwnd, GWL_STYLE, style | PBS_MARQUEE); - SendMessage(p->hwnd, PBM_SETMARQUEE, 1, 0); + if (!indeterminate(p)) { + setStyle(p->hwnd, getStyle(p->hwnd) | PBS_MARQUEE); + SendMessageW(p->hwnd, PBM_SETMARQUEE, (WPARAM) TRUE, 0); } return; } - - if ((style & PBS_MARQUEE) != 0) { - SetWindowLongPtr(p->hwnd, GWL_STYLE, style ^ PBS_MARQUEE); - SendMessage(p->hwnd, PBM_SETMARQUEE, 0, 0); + if (indeterminate(p)) { + SendMessageW(p->hwnd, PBM_SETMARQUEE, (WPARAM) FALSE, 0); + setStyle(p->hwnd, getStyle(p->hwnd) & ~PBS_MARQUEE); } if (value < 0 || value > 100) From cc4c5f7bd52dae1c6267180d11ba787590cc3647 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 17 Jun 2016 09:16:30 -0400 Subject: [PATCH 0374/1329] Tried to see if we can fix up uiMainSteps() to not need parameters after all. Now to actually apply it. --- test/main.c | 13 +++++-------- unix/main.c | 31 +++++++++++++------------------ unix/window.c | 10 ++++++---- 3 files changed, 24 insertions(+), 30 deletions(-) diff --git a/test/main.c b/test/main.c index e69feee7..1ae5ec12 100644 --- a/test/main.c +++ b/test/main.c @@ -39,12 +39,6 @@ uiTab *mainTab; uiBox *(*newhbox)(void); uiBox *(*newvbox)(void); -static void stepsLoop(void *data) -{ - while (uiMainStep(1)) - ; -} - int main(int argc, char *argv[]) { uiInitOptions o; @@ -167,8 +161,11 @@ int main(int argc, char *argv[]) uiControlShow(uiControl(w)); if (!steps) uiMain(); - else - uiMainSteps(stepsLoop, NULL); + else { + uiMainSteps(NULL, NULL); + while (uiMainStep(1)) + ; + } printf("after uiMain()\n"); uiUninit(); printf("after uiUninit()\n"); diff --git a/unix/main.c b/unix/main.c index 59590886..bdd22eeb 100644 --- a/unix/main.c +++ b/unix/main.c @@ -29,33 +29,25 @@ void uiFreeInitError(const char *err) g_free((gpointer) err); } +static gboolean (*iteration)(gboolean) = NULL; + void uiMain(void) { + iteration = gtk_main_iteration_do; gtk_main(); } -struct mainStepsData { - void (*f)(void *); - void *data; -}; +static gboolean stepsQuit = FALSE; -static gboolean mainSteps(gpointer data) +static gboolean stepsIteration(gboolean block) { - struct mainStepsData *d = (struct mainStepsData *) data; - - (*(d->f))(d->data); - // TODO call gtk_main_quit() here again? - return FALSE; + gtk_main_iteration_do(block); + return stepsQuit; } void uiMainSteps(void (*f)(void *), void *data) { - struct mainStepsData d; - - d.f = f; - d.data = data; - gdk_threads_add_idle(mainSteps, &d); - gtk_main(); + iteration = stepsIteration; } int uiMainStep(int wait) @@ -65,7 +57,7 @@ int uiMainStep(int wait) block = FALSE; if (wait) block = TRUE; - return gtk_main_iteration_do(block) == FALSE; + return (*iteration)(block) == FALSE; } // gtk_main_quit() may run immediately, or it may wait for other pending events; "it depends" (thanks mclasen in irc.gimp.net/#gtk+) @@ -73,7 +65,10 @@ int uiMainStep(int wait) // we'll do it by using an idle callback static gboolean quit(gpointer data) { - gtk_main_quit(); + if (iteration == stepsIteration) + stepsQuit = TRUE; + else + gtk_main_quit(); return FALSE; } diff --git a/unix/window.c b/unix/window.c index bd75b9b5..71e47aa9 100644 --- a/unix/window.c +++ b/unix/window.c @@ -161,8 +161,8 @@ void uiWindowSetPosition(uiWindow *w, int x, int y) // we need to wait for a configure-event // thanks to hergertme in irc.gimp.net/#gtk+ while (w->changingPosition) - if (gtk_main_iteration() != FALSE) - break; // stop early if gtk_main_quit() called + if (!uiMainStep(1)) + break; // stop early if uiQuit() called } void uiWindowCenter(uiWindow *w) @@ -186,6 +186,7 @@ void uiWindowCenter(uiWindow *w) uiWindowSetPosition(w, x, y); } +// TODO this and size changed get set during uiWindowDestroy void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data) { w->onPositionChanged = f; @@ -203,13 +204,14 @@ void uiWindowContentSize(uiWindow *w, int *width, int *height) // TODO what happens if the size is already the current one? // TODO a spurious size-allocate gets sent after this function returns +// TODO can't reduce the size this way void uiWindowSetContentSize(uiWindow *w, int width, int height) { w->changingSize = TRUE; gtk_widget_set_size_request(w->childHolderWidget, width, height); while (w->changingSize) - if (gtk_main_iteration() != FALSE) - break; // stop early if gtk_main_quit() called + if (!uiMainStep(1)) + break; // stop early if uiQuit() called gtk_widget_set_size_request(w->childHolderWidget, -1, -1); } From ae14542c9a96e1c10a73f9e06fd3f62dd42d088e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 17 Jun 2016 09:22:31 -0400 Subject: [PATCH 0375/1329] Improved uiMainSteps(). --- README.md | 3 +++ darwin/main.m | 3 +-- test/main.c | 2 +- ui.h | 2 +- unix/main.c | 4 +++- windows/main.cpp | 4 ++-- 6 files changed, 11 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 8a71e539..6d165442 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,9 @@ This README is being written.
*Note that today's entry (Eastern Time) may be updated later today.* +* **17 June 2016** + * `uiMainSteps()` no longer takes any arguments and no longer needs to invoke a function to do the work. You still need to call it, but once you do, it will return immediately and you can then get right to your main loop. + * **16 June 2016** * Added `uiWindowContentSize()`, `uiWindowSetContentSize()`, and `uiWindowOnContentSizeChanged()` methods for manipulating uiWindow content sizes. Note the use of "content size"; the size you work with does NOT include window decorations (titlebars, menus, etc.). * Added `uiWindowFullscreen()` and `uiWindowSetFullscreen()` to allow making fullscreen uiWindows, taking advantage of OS facilities for fullscreen and without changing the screen resolution (!). diff --git a/darwin/main.m b/darwin/main.m index 6faef4f4..2c934330 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -159,13 +159,12 @@ void uiMain(void) [realNSApp() run]; } -void uiMainSteps(void (*f)(void *), void *data) +void uiMainSteps(void) { isRunning = ^{ return stepsIsRunning; }; stepsIsRunning = YES; - (*f)(data); } // see also: diff --git a/test/main.c b/test/main.c index 1ae5ec12..803326b5 100644 --- a/test/main.c +++ b/test/main.c @@ -162,7 +162,7 @@ int main(int argc, char *argv[]) if (!steps) uiMain(); else { - uiMainSteps(NULL, NULL); + uiMainSteps(); while (uiMainStep(1)) ; } diff --git a/ui.h b/ui.h index 82079371..7012dc16 100644 --- a/ui.h +++ b/ui.h @@ -45,7 +45,7 @@ _UI_EXTERN void uiUninit(void); _UI_EXTERN void uiFreeInitError(const char *err); _UI_EXTERN void uiMain(void); -_UI_EXTERN void uiMainSteps(void (*f)(void *), void *data); +_UI_EXTERN void uiMainSteps(void); _UI_EXTERN int uiMainStep(int wait); _UI_EXTERN void uiQuit(void); diff --git a/unix/main.c b/unix/main.c index bdd22eeb..d6eff223 100644 --- a/unix/main.c +++ b/unix/main.c @@ -39,13 +39,15 @@ void uiMain(void) static gboolean stepsQuit = FALSE; +// the only difference is we ignore the return value from gtk_main_iteration_do(), since it will always be TRUE if gtk_main() was never called +// gtk_main_iteration_do() will still run the main loop regardless static gboolean stepsIteration(gboolean block) { gtk_main_iteration_do(block); return stepsQuit; } -void uiMainSteps(void (*f)(void *), void *data) +void uiMainSteps(void) { iteration = stepsIteration; } diff --git a/windows/main.cpp b/windows/main.cpp index 5cc34332..eb6d8492 100644 --- a/windows/main.cpp +++ b/windows/main.cpp @@ -77,9 +77,9 @@ void uiMain(void) ; } -void uiMainSteps(void (*f)(void *), void *data) +void uiMainSteps(void) { - (*f)(data); + // don't need to do anything here } static int peekMessage(MSG *msg) From fca6edd5a25c87cdaebc10ed881bc68013c74b6b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 17 Jun 2016 09:24:15 -0400 Subject: [PATCH 0376/1329] More TODOs. --- unix/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/unix/main.c b/unix/main.c index d6eff223..13f0a913 100644 --- a/unix/main.c +++ b/unix/main.c @@ -69,6 +69,7 @@ static gboolean quit(gpointer data) { if (iteration == stepsIteration) stepsQuit = TRUE; + // TODO run a gtk_main() here just to do the cleanup steps of syncing the clipboard and other stuff gtk_main() does before it returns else gtk_main_quit(); return FALSE; From 8c332591c916beff3b7de4d40605d8eea1f23140 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 17 Jun 2016 09:56:47 -0400 Subject: [PATCH 0377/1329] Switched minimum cmake version to 3.1.0. Will announce after updating the Travis files. --- CMakeLists.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4b647c6e..1f870770 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,6 @@ # 3 june 2016 -cmake_minimum_required(VERSION 2.8.11) +# see https://cmake.org/gitweb?p=cmake.git;a=commit;h=95cdf132489c79e88a10fdf7a7566fa002c7680b (thanks ngladitz in irc.freenode.net/#cmake) +cmake_minimum_required(VERSION 3.1.0) # TODOs # - silence entering/leaving messages? @@ -8,6 +9,7 @@ cmake_minimum_required(VERSION 2.8.11) # - debian DESTDIR? https://github.com/andlabs/libui/pull/10 # - libui-combined* needs to be deleted so that custom command can run every time # - add notelemetry.obj to *ALL TARGETS* on VS2015 and up - https://www.infoq.com/news/2016/06/visual-cpp-telemetry +# - switch to 3.1.0 features # the docs say we need to set this up prior to project() set(CMAKE_OSX_DEPLOYMENT_TARGET "10.8") @@ -142,10 +144,12 @@ target_compile_options(${_LIBUINAME} PUBLIC ${_COMMON_CFLAGS} PRIVATE ${_LIBUI_CFLAGS}) # TODO link directories? -# because we need 2.8.11 for CentOS, we can't use target_link_libraries(INTERFACE) for static executables :( if(BUILD_SHARED_LIBS) target_link_libraries(${_LIBUINAME} PRIVATE ${_LIBUI_LIBS}) +else() + target_link_libraries(${_LIBUINAME} + INTERFACE ${_LIBUI_LIBS}) endif() # on Windows the linker for static libraries is different; don't give it the flags if(BUILD_SHARED_LIBS) @@ -198,10 +202,6 @@ macro(_add_exec _name) set_property(TARGET ${_name} PROPERTY POSITION_INDEPENDENT_CODE True) endif() - # because we need 2.8.11 for CentOS, we can't use target_link_libraries(PUBLIC) for static executables :( - if(NOT BUILD_SHARED_LIBS) - target_link_libraries(${_name} ${_LIBUI_LIBS}) - endif() # TODOfor some reason these don't propagate if(NOT WIN32) From 721a934bf0b01079b3d1590e0197ba740c188871 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 17 Jun 2016 10:01:07 -0400 Subject: [PATCH 0378/1329] Round 2 --- .travis.yml | 1 + CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 5c287abd..e71145bf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,7 @@ script: - if [ "$TRAVIS_OS_NAME" == "linux" ]; then sudo apt-get install libgtk-3-dev -y || sudo apt-cache search libgtk3; fi - mkdir build - cd build + - cmake --version - cmake .. -G "Unix Makefiles" - make tester examples - rm -rf * diff --git a/CMakeLists.txt b/CMakeLists.txt index 1f870770..2226cec7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ # 3 june 2016 # see https://cmake.org/gitweb?p=cmake.git;a=commit;h=95cdf132489c79e88a10fdf7a7566fa002c7680b (thanks ngladitz in irc.freenode.net/#cmake) -cmake_minimum_required(VERSION 3.1.0) +cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR) # TODOs # - silence entering/leaving messages? From f17483ccd2a261e63fdb94c393d28a1025b4547c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 17 Jun 2016 10:09:47 -0400 Subject: [PATCH 0379/1329] Round 3 --- .travis.yml | 5 +++++ CMakeLists.txt | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e71145bf..edd561be 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,10 +5,15 @@ os: # This makes us use Ubuntu 14 instead of 12 dist: trusty +# Notes: +# - Travis uses cmake 3.0.2 on OS X; we need 3.1 or newer (thanks tbodt) + language: c script: - if [ "$TRAVIS_OS_NAME" == "linux" ]; then sudo apt-get update; fi - if [ "$TRAVIS_OS_NAME" == "linux" ]; then sudo apt-get install libgtk-3-dev -y || sudo apt-cache search libgtk3; fi + - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew update; fi + - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew upgrade cmake; fi - mkdir build - cd build - cmake --version diff --git a/CMakeLists.txt b/CMakeLists.txt index 2226cec7..64339a0d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -148,7 +148,7 @@ if(BUILD_SHARED_LIBS) target_link_libraries(${_LIBUINAME} PRIVATE ${_LIBUI_LIBS}) else() - target_link_libraries(${_LIBUINAME} + target_link_libraries(libui INTERFACE ${_LIBUI_LIBS}) endif() # on Windows the linker for static libraries is different; don't give it the flags From b95bbbd70b6b834a5501436c3d9979c03db5c760 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 17 Jun 2016 10:13:46 -0400 Subject: [PATCH 0380/1329] Round 4 --- CMakeLists.txt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 64339a0d..35a5ab9e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -147,10 +147,8 @@ target_compile_options(${_LIBUINAME} if(BUILD_SHARED_LIBS) target_link_libraries(${_LIBUINAME} PRIVATE ${_LIBUI_LIBS}) -else() - target_link_libraries(libui - INTERFACE ${_LIBUI_LIBS}) endif() +# TODO INTERFACE libs don't inherit to grandhcildren? # on Windows the linker for static libraries is different; don't give it the flags if(BUILD_SHARED_LIBS) _target_link_options_private(${_LIBUINAME} @@ -202,6 +200,11 @@ macro(_add_exec _name) set_property(TARGET ${_name} PROPERTY POSITION_INDEPENDENT_CODE True) endif() + # TODO see above about INTERFACE + if(NOT BUILD_SHARED_LIBS) + target_link_libraries(${_name} + PRIVATE ${_LIBUI_LIBS}) + endif() # TODOfor some reason these don't propagate if(NOT WIN32) From 3499e8335b53f56356c7c82ec337fb0354235d94 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 17 Jun 2016 10:16:53 -0400 Subject: [PATCH 0381/1329] Round 5 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 35a5ab9e..03fc963c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -203,7 +203,7 @@ macro(_add_exec _name) # TODO see above about INTERFACE if(NOT BUILD_SHARED_LIBS) target_link_libraries(${_name} - PRIVATE ${_LIBUI_LIBS}) + ${_LIBUI_LIBS}) endif() # TODOfor some reason these don't propagate From ebdcf7bf170967567f0b1271acaf56ce0973889d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 17 Jun 2016 10:24:30 -0400 Subject: [PATCH 0382/1329] And THAT did it. --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6d165442..35ef1129 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,12 @@ This README is being written.
[![Build Status](https://travis-ci.org/andlabs/libui.png)](https://travis-ci.org/andlabs/libui) -*(currently failing because the version of cmake that Travis uses treats Objective-C files as C++; if you know the fix please file a PR)* ## Announcements +* **17 June 2016** + * **CMake 3.1.0 is now required.** This is due to CMake's rapid development pace in the past few years adding things libui needs to build on as many systems as possible. If your OS is supported by libui but its repositories ship with an older version of CMake, you will need to find an updated one somewhere. + * **5 June 2016** * **Alpha 3.1 is here.** This was a much-needed update to Alpha 3 that changes a few things: * **The build system is now cmake.** cmake 2.8.11 or higher is needed. @@ -20,6 +22,7 @@ This README is being written.
* **17 June 2016** * `uiMainSteps()` no longer takes any arguments and no longer needs to invoke a function to do the work. You still need to call it, but once you do, it will return immediately and you can then get right to your main loop. + * **CMake 3.1.0 is now required.** This is due to CMake's rapid development pace in the past few years adding things libui needs to build on as many systems as possible. If your OS is supported by libui but its repositories ship with an older version of CMake, you will need to find an updated one somewhere. * **16 June 2016** * Added `uiWindowContentSize()`, `uiWindowSetContentSize()`, and `uiWindowOnContentSizeChanged()` methods for manipulating uiWindow content sizes. Note the use of "content size"; the size you work with does NOT include window decorations (titlebars, menus, etc.). @@ -65,7 +68,7 @@ This README is being written.
## Build Requirements * All platforms: - * CMake 2.8.11 or newer + * CMake 3.1.0 or newer * Windows: either * Microsoft Visual Studio 2013 or newer (2013 is needed for `va_copy()`) — you can build either a static or a shared library * MinGW-w64 (other flavors of MinGW may not work) — **you can only build a static library**; shared library support will be re-added once the following features come in: From d36ef03630503e422dde287f7f9d6ef5ae63043b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 17 Jun 2016 10:28:31 -0400 Subject: [PATCH 0383/1329] More TODOs. --- TODO.md | 1 + 1 file changed, 1 insertion(+) diff --git a/TODO.md b/TODO.md index d91a3eab..86b06801 100644 --- a/TODO.md +++ b/TODO.md @@ -98,3 +98,4 @@ on windows - a way to do recursive main loops - how do we handle 0 returns from non-recursive uiMainStep() calls that aren't the main loop? (event handlers, for instance) +- should repeated calls to uiMainStep() after uiQuit() return 0 reliably? this will be needed for non-recursive loops From 605b09f2be7b46de77a253363b6528e02ac5948d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 17 Jun 2016 10:31:17 -0400 Subject: [PATCH 0384/1329] Applied @tcyrus's README.md suggestions. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 35ef1129..573e947a 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # libui: a portable GUI library for C This README is being written.
-[![Build Status](https://travis-ci.org/andlabs/libui.png)](https://travis-ci.org/andlabs/libui) +[![Build Status](https://travis-ci.org/andlabs/libui.svg)](https://travis-ci.org/andlabs/libui) ## Announcements @@ -111,7 +111,7 @@ Can be built from AUR: https://aur.archlinux.org/packages/libui-git/ ## Documentation -Needs to be written. Consult ui.h and the examples for details for now. +Needs to be written. Consult `ui.h` and the examples for details for now. ## Language Bindings From 9656a81c7724a06042a76048ec7953b645d0a395 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 17 Jun 2016 11:02:17 -0400 Subject: [PATCH 0385/1329] Added uiNewVerticalSeparator(). --- README.md | 1 + darwin/separator.m | 17 +++++++++++++++++ examples/controlgallery/main.c | 4 ++++ test/page13.c | 1 + test/page15.c | 5 +++++ ui.h | 1 + unix/separator.c | 12 ++++++++++++ windows/separator.cpp | 31 ++++++++++++++++++++++++++++--- 8 files changed, 69 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 573e947a..beb1b553 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ This README is being written.
* **17 June 2016** * `uiMainSteps()` no longer takes any arguments and no longer needs to invoke a function to do the work. You still need to call it, but once you do, it will return immediately and you can then get right to your main loop. * **CMake 3.1.0 is now required.** This is due to CMake's rapid development pace in the past few years adding things libui needs to build on as many systems as possible. If your OS is supported by libui but its repositories ship with an older version of CMake, you will need to find an updated one somewhere. + * Added `uiNewVerticalSeparator()` to complement `uiNewHorizontalSeparator()`. * **16 June 2016** * Added `uiWindowContentSize()`, `uiWindowSetContentSize()`, and `uiWindowOnContentSizeChanged()` methods for manipulating uiWindow content sizes. Note the use of "content size"; the size you work with does NOT include window decorations (titlebars, menus, etc.). diff --git a/darwin/separator.m b/darwin/separator.m index fa82aa46..a37a376e 100644 --- a/darwin/separator.m +++ b/darwin/separator.m @@ -3,6 +3,7 @@ // TODO make this intrinsic #define separatorWidth 96 +#define separatorHeight 96 struct uiSeparator { uiDarwinControl c; @@ -26,3 +27,19 @@ uiSeparator *uiNewHorizontalSeparator(void) return s; } + +uiSeparator *uiNewVerticalSeparator(void) +{ + uiSeparator *s; + + uiDarwinNewControl(uiSeparator, s); + + // make the initial height >= initial width to force vertical + s->box = [[NSBox alloc] initWithFrame:NSMakeRect(0, 0, 1, 100)]; + [s->box setBoxType:NSBoxSeparator]; + [s->box setBorderType:NSGrooveBorder]; + [s->box setTransparent:NO]; + [s->box setTitlePosition:NSNoTitle]; + + return s; +} diff --git a/examples/controlgallery/main.c b/examples/controlgallery/main.c index fd9c8ea5..ad22c585 100644 --- a/examples/controlgallery/main.c +++ b/examples/controlgallery/main.c @@ -216,6 +216,10 @@ static uiControl *makeDataChoosersPage(void) uiControl(uiNewColorButton()), 0); + uiBoxAppend(hbox, + uiControl(uiNewVerticalSeparator()), + 0); + vbox = uiNewVerticalBox(); uiBoxSetPadded(vbox, 1); uiBoxAppend(hbox, uiControl(vbox), 1); diff --git a/test/page13.c b/test/page13.c index cda91ce7..5e6fd52c 100644 --- a/test/page13.c +++ b/test/page13.c @@ -47,6 +47,7 @@ static void openTestWindow(uiBox *(*mkf)(void)) BA(uiNewColorButton()); BA(uiNewPasswordEntry()); BA(uiNewSearchEntry()); + BA(uiNewVerticalSeparator()); uiControlShow(uiControl(w)); } diff --git a/test/page15.c b/test/page15.c index 50cf155a..7dd078c7 100644 --- a/test/page15.c +++ b/test/page15.c @@ -148,5 +148,10 @@ uiBox *makePage15(uiWindow *w) uiCheckboxOnToggled(checkbox, borderless, w); uiBoxAppend(page15, uiControl(checkbox), 0); + hbox = newHorizontalBox(); + uiBoxAppend(page15, uiControl(hbox), 1); + + uiBoxAppend(hbox, uiControl(uiNewVerticalSeparator()), 0); + return page15; } diff --git a/ui.h b/ui.h index 7012dc16..036a89c1 100644 --- a/ui.h +++ b/ui.h @@ -206,6 +206,7 @@ _UI_EXTERN uiProgressBar *uiNewProgressBar(void); typedef struct uiSeparator uiSeparator; #define uiSeparator(this) ((uiSeparator *) (this)) _UI_EXTERN uiSeparator *uiNewHorizontalSeparator(void); +_UI_EXTERN uiSeparator *uiNewVerticalSeparator(void); typedef struct uiCombobox uiCombobox; #define uiCombobox(this) ((uiCombobox *) (this)) diff --git a/unix/separator.c b/unix/separator.c index 63217b72..02c75da5 100644 --- a/unix/separator.c +++ b/unix/separator.c @@ -20,3 +20,15 @@ uiSeparator *uiNewHorizontalSeparator(void) return s; } + +uiSeparator *uiNewVerticalSeparator(void) +{ + uiSeparator *s; + + uiUnixNewControl(uiSeparator, s); + + s->widget = gtk_separator_new(GTK_ORIENTATION_VERTICAL); + s->separator = GTK_SEPARATOR(s->widget); + + return s; +} diff --git a/windows/separator.cpp b/windows/separator.cpp index 7c15a6d8..e123e275 100644 --- a/windows/separator.cpp +++ b/windows/separator.cpp @@ -8,6 +8,7 @@ struct uiSeparator { uiWindowsControl c; HWND hwnd; + BOOL vertical; }; uiWindowsControlAllDefaults(uiSeparator) @@ -15,17 +16,25 @@ uiWindowsControlAllDefaults(uiSeparator) // via https://msdn.microsoft.com/en-us/library/windows/desktop/bb226818%28v=vs.85%29.aspx #define separatorHeight 1 +// TODO +#define separatorWidth 1 + static void uiSeparatorMinimumSize(uiWindowsControl *c, int *width, int *height) { uiSeparator *s = uiSeparator(c); uiWindowsSizing sizing; - int y; + int x, y; *width = 1; // TODO + *height = 1; + x = separatorWidth; y = separatorHeight; uiWindowsGetSizing(s->hwnd, &sizing); - uiWindowsSizingDlgUnitsToPixels(&sizing, NULL, &y); - *height = y; + uiWindowsSizingDlgUnitsToPixels(&sizing, &x, &y); + if (s->vertical) + *width = x; + else + *height = y; } uiSeparator *uiNewHorizontalSeparator(void) @@ -42,3 +51,19 @@ uiSeparator *uiNewHorizontalSeparator(void) return s; } + +uiSeparator *uiNewVerticalSeparator(void) +{ + uiSeparator *s; + + uiWindowsNewControl(uiSeparator, s); + + s->hwnd = uiWindowsEnsureCreateControlHWND(0, + L"static", L"", + SS_ETCHEDHORZ, + hInstance, NULL, + TRUE); + s->vertical = TRUE; + + return s; +} From 9785565c8cf3de5ff069094db6fa6a5a01695be9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 17 Jun 2016 11:34:42 -0400 Subject: [PATCH 0386/1329] Expanded the control gallery a bit further. --- examples/controlgallery/main.c | 37 ++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/examples/controlgallery/main.c b/examples/controlgallery/main.c index ad22c585..c0d536c1 100644 --- a/examples/controlgallery/main.c +++ b/examples/controlgallery/main.c @@ -100,6 +100,7 @@ static uiControl *makeNumbersPage() uiBox *hbox; uiGroup *group; uiBox *vbox; + uiProgressBar *ip; uiCombobox *cbox; uiEditableCombobox *ecbox; uiRadioButtons *rb; @@ -124,6 +125,10 @@ static uiControl *makeNumbersPage() uiBoxAppend(vbox, uiControl(slider), 0); uiBoxAppend(vbox, uiControl(pbar), 0); + ip = uiNewProgressBar(); + uiProgressBarSetValue(ip, -1); + uiBoxAppend(vbox, uiControl(ip), 0); + group = uiNewGroup("Lists"); uiGroupSetMargined(group, 1); uiBoxAppend(hbox, uiControl(group), 1); @@ -184,6 +189,20 @@ static void onSaveFileClicked(uiButton *b, void *data) uiFreeText(filename); } +static void onMsgBoxClicked(uiButton *b, void *data) +{ + uiMsgBox(mainwin, + "This is a normal message box.", + "More detailed information can be shown here."); +} + +static void onMsgBoxErrorClicked(uiButton *b, void *data) +{ + uiMsgBoxError(mainwin, + "This message box describes an error.", + "More detailed information can be shown here."); +} + static uiControl *makeDataChoosersPage(void) { uiBox *hbox; @@ -191,6 +210,7 @@ static uiControl *makeDataChoosersPage(void) uiGrid *grid; uiButton *button; uiEntry *entry; + uiGrid *msggrid; hbox = uiNewHorizontalBox(); uiBoxSetPadded(hbox, 1); @@ -250,6 +270,23 @@ static uiControl *makeDataChoosersPage(void) 1, 1, 1, 1, 1, uiAlignFill, 0, uiAlignFill); + msggrid = uiNewGrid(); + uiGridSetPadded(msggrid, 1); + uiGridAppend(grid, uiControl(msggrid), + 0, 2, 2, 1, + 0, uiAlignCenter, 0, uiAlignStart); + + button = uiNewButton("Message Box"); + uiButtonOnClicked(button, onMsgBoxClicked, NULL); + uiGridAppend(msggrid, uiControl(button), + 0, 0, 1, 1, + 0, uiAlignFill, 0, uiAlignFill); + button = uiNewButton("Error Box"); + uiButtonOnClicked(button, onMsgBoxErrorClicked, NULL); + uiGridAppend(msggrid, uiControl(button), + 1, 0, 1, 1, + 0, uiAlignFill, 0, uiAlignFill); + return uiControl(hbox); } From 087a89dac9f1c4c687198f335575ed0bce87d34b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 17 Jun 2016 13:39:36 -0400 Subject: [PATCH 0387/1329] Added menu planning. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index beb1b553..d9c3dcf0 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ This README is being written.
* **17 June 2016** * **CMake 3.1.0 is now required.** This is due to CMake's rapid development pace in the past few years adding things libui needs to build on as many systems as possible. If your OS is supported by libui but its repositories ship with an older version of CMake, you will need to find an updated one somewhere. + * Please help [plan out a better menu API](https://github.com/andlabs/libui/issues/152). * **5 June 2016** * **Alpha 3.1 is here.** This was a much-needed update to Alpha 3 that changes a few things: From f2c0719e9527b8d525d7909f69f23e72d9ae338f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 17 Jun 2016 23:18:31 -0400 Subject: [PATCH 0388/1329] More TODOs. --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 03fc963c..eb1696d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -102,6 +102,7 @@ else() -fvisibility=hidden ) # don't use C_VERSION or CXX_VERSION because they use GNU standards + # TODO we can actually do this; set both C_EXTENSIONS and CXX_EXTENSIONS to OFF set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --std=c99") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11") From a07e2afc9c514e72e3face57cd2b9baa6225f048 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 18 Jun 2016 13:32:10 -0400 Subject: [PATCH 0389/1329] Made alignment work on uiGrid on OS X. --- darwin/grid.m | 130 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 114 insertions(+), 16 deletions(-) diff --git a/darwin/grid.m b/darwin/grid.m index 5e391fd5..d5c5fb1e 100644 --- a/darwin/grid.m +++ b/darwin/grid.m @@ -1,11 +1,9 @@ // 11 june 2016 #import "uipriv_darwin.h" -// TODO adjust all containers to handle hidden cells properly +// TODO the assorted test doesn't work right at all -// TODO wrap the child in a view if its align isn't fill -// maybe it's easier to do it regardless of align -@interface gridChild : NSObject +@interface gridChild : NSView @property uiControl *c; @property int left; @property int top; @@ -16,8 +14,17 @@ @property int vexpand; @property uiAlign valign; +@property (strong) NSLayoutConstraint *leadingc; +@property (strong) NSLayoutConstraint *topc; +@property (strong) NSLayoutConstraint *trailingc; +@property (strong) NSLayoutConstraint *bottomc; +@property (strong) NSLayoutConstraint *xcenterc; +@property (strong) NSLayoutConstraint *ycenterc; + @property NSLayoutPriority oldHorzHuggingPri; @property NSLayoutPriority oldVertHuggingPri; +- (void)setC:(uiControl *)c grid:(uiGrid *)g; +- (void)onDestroy; - (NSView *)view; @end @@ -54,6 +61,100 @@ struct uiGrid { @implementation gridChild +- (void)setC:(uiControl *)c grid:(uiGrid *)g +{ + self.c = c; + self.oldHorzHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(self.c), NSLayoutConstraintOrientationHorizontal); + self.oldVertHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(self.c), NSLayoutConstraintOrientationVertical); + + uiControlSetParent(self.c, uiControl(g)); + uiDarwinControlSetSuperview(uiDarwinControl(self.c), self); + uiDarwinControlSyncEnableState(uiDarwinControl(self.c), uiControlEnabledToUser(uiControl(g))); + + if (self.halign == uiAlignStart || self.halign == uiAlignFill) { + self.leadingc = mkConstraint(self, NSLayoutAttributeLeading, + NSLayoutRelationEqual, + [self view], NSLayoutAttributeLeading, + 1, 0, + @"uiGrid child horizontal alignment start constraint"); + [self addConstraint:self.leadingc]; + } + if (self.halign == uiAlignCenter) { + self.xcenterc = mkConstraint(self, NSLayoutAttributeCenterX, + NSLayoutRelationEqual, + [self view], NSLayoutAttributeCenterX, + 1, 0, + @"uiGrid child horizontal alignment center constraint"); + [self addConstraint:self.xcenterc]; + } + if (self.halign == uiAlignEnd || self.halign == uiAlignFill) { + self.trailingc = mkConstraint(self, NSLayoutAttributeTrailing, + NSLayoutRelationEqual, + [self view], NSLayoutAttributeTrailing, + 1, 0, + @"uiGrid child horizontal alignment end constraint"); + [self addConstraint:self.trailingc]; + } + + if (self.valign == uiAlignStart || self.valign == uiAlignFill) { + self.topc = mkConstraint(self, NSLayoutAttributeTop, + NSLayoutRelationEqual, + [self view], NSLayoutAttributeTop, + 1, 0, + @"uiGrid child vertical alignment start constraint"); + [self addConstraint:self.topc]; + } + if (self.valign == uiAlignCenter) { + self.ycenterc = mkConstraint(self, NSLayoutAttributeCenterY, + NSLayoutRelationEqual, + [self view], NSLayoutAttributeCenterY, + 1, 0, + @"uiGrid child vertical alignment center constraint"); + [self addConstraint:self.ycenterc]; + } + if (self.valign == uiAlignEnd || self.valign == uiAlignFill) { + self.bottomc = mkConstraint(self, NSLayoutAttributeBottom, + NSLayoutRelationEqual, + [self view], NSLayoutAttributeBottom, + 1, 0, + @"uiGrid child vertical alignment end constraint"); + [self addConstraint:self.bottomc]; + } +} + +- (void)onDestroy +{ + if (self.leadingc != nil) { + [self removeConstraint:self.leadingc]; + self.leadingc = nil; + } + if (self.topc != nil) { + [self removeConstraint:self.topc]; + self.topc = nil; + } + if (self.trailingc != nil) { + [self removeConstraint:self.trailingc]; + self.trailingc = nil; + } + if (self.bottomc != nil) { + [self removeConstraint:self.bottomc]; + self.bottomc = nil; + } + if (self.xcenterc != nil) { + [self removeConstraint:self.xcenterc]; + self.xcenterc = nil; + } + if (self.ycenterc != nil) { + [self removeConstraint:self.ycenterc]; + self.ycenterc = nil; + } + + uiControlSetParent(self.c, NULL); + uiDarwinControlSetSuperview(uiDarwinControl(self.c), nil); + uiDarwinControlSetHuggingPriority(uiDarwinControl(self.c), self.oldHorzHuggingPri, NSLayoutConstraintOrientationHorizontal); + uiDarwinControlSetHuggingPriority(uiDarwinControl(self.c), self.oldVertHuggingPri, NSLayoutConstraintOrientationVertical); +} + - (NSView *)view { return (NSView *) uiControlHandle(self.c); @@ -90,9 +191,9 @@ struct uiGrid { [self->emptyCellViews release]; for (gc in self->children) { - uiControlSetParent(gc.c, NULL); - uiDarwinControlSetSuperview(uiDarwinControl(gc.c), nil); + [gc onDestroy]; uiControlDestroy(gc.c); + [gc removeFromSuperview]; } [self->children release]; } @@ -254,7 +355,7 @@ struct uiGrid { [self->emptyCellViews addObject:gv[y][x]]; } else { gc = (gridChild *) [self->children objectAtIndex:gg[y][x]]; - gv[y][x] = [gc view]; + gv[y][x] = gc; } } @@ -438,9 +539,8 @@ struct uiGrid { BOOL update; int oldnh, oldnv; - uiControlSetParent(gc.c, uiControl(self->g)); - uiDarwinControlSetSuperview(uiDarwinControl(gc.c), self); - uiDarwinControlSyncEnableState(uiDarwinControl(gc.c), uiControlEnabledToUser(uiControl(self->g))); + [gc setTranslatesAutoresizingMaskIntoConstraints:NO]; + [self addSubview:gc]; // no need to set priority here; that's done in establishOurConstraints @@ -637,7 +737,7 @@ static void uiGridChildVisibilityChanged(uiDarwinControl *c) [g->view establishOurConstraints]; } -static gridChild *toChild(uiControl *c, int xspan, int yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign) +static gridChild *toChild(uiControl *c, int xspan, int yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign, uiGrid *g) { gridChild *gc; @@ -646,15 +746,13 @@ static gridChild *toChild(uiControl *c, int xspan, int yspan, int hexpand, uiAli if (yspan < 0) userbug("You cannot have a negative yspan in a uiGrid cell."); gc = [gridChild new]; - gc.c = c; gc.xspan = xspan; gc.yspan = yspan; gc.hexpand = hexpand; gc.halign = halign; gc.vexpand = vexpand; gc.valign = valign; - gc.oldHorzHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(gc.c), NSLayoutConstraintOrientationHorizontal); - gc.oldVertHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(gc.c), NSLayoutConstraintOrientationVertical); + [gc setC:c grid:g]; return gc; } @@ -666,7 +764,7 @@ void uiGridAppend(uiGrid *g, uiControl *c, int left, int top, int xspan, int ysp // or at leat allow this and implicitly turn it into a spacer if (c == NULL) userbug("You cannot add NULL to a uiGrid."); - gc = toChild(c, xspan, yspan, hexpand, halign, vexpand, valign); + gc = toChild(c, xspan, yspan, hexpand, halign, vexpand, valign, g); gc.left = left; gc.top = top; [g->view append:gc]; @@ -676,7 +774,7 @@ void uiGridInsertAt(uiGrid *g, uiControl *c, uiControl *existing, uiAt at, int x { gridChild *gc; - gc = toChild(c, xspan, yspan, hexpand, halign, vexpand, valign); + gc = toChild(c, xspan, yspan, hexpand, halign, vexpand, valign, g); [g->view insert:gc after:existing at:at]; } From ec15e256940ec6b19126290d7c9c50e8dc0063c0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 18 Jun 2016 22:08:17 -0400 Subject: [PATCH 0390/1329] More announcements. --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index d9c3dcf0..67636543 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,9 @@ This README is being written.
## Announcements +* **18 June 2016** + * Help decide [the design of tables and trees in libui](https://github.com/andlabs/libui/issues/159); the implementation starts within the next few days, if not tomorrow! + * **17 June 2016** * **CMake 3.1.0 is now required.** This is due to CMake's rapid development pace in the past few years adding things libui needs to build on as many systems as possible. If your OS is supported by libui but its repositories ship with an older version of CMake, you will need to find an updated one somewhere. * Please help [plan out a better menu API](https://github.com/andlabs/libui/issues/152). From 80b8fddbea397a7970532b5ac44c552d90cb3c81 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 20 Jun 2016 22:14:26 -0400 Subject: [PATCH 0391/1329] Started mapping out uiTable and uiTree. --- ui.h | 3 +++ uitable.h | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 uitable.h diff --git a/ui.h b/ui.h index 036a89c1..70c2f121 100644 --- a/ui.h +++ b/ui.h @@ -665,6 +665,9 @@ _UI_EXTERN int uiGridPadded(uiGrid *g); _UI_EXTERN void uiGridSetPadded(uiGrid *g, int padded); _UI_EXTERN uiGrid *uiNewGrid(void); +// TODO merge +#include "uitable.h" + #ifdef __cplusplus } #endif diff --git a/uitable.h b/uitable.h new file mode 100644 index 00000000..645096bd --- /dev/null +++ b/uitable.h @@ -0,0 +1,41 @@ +// 20 june 2016 +// kept in a separate file for now + +typedef struct uiTableModel uiTableModel; +typedef struct uiTableModelHandler uiTableModelHandler; + +_UI_ENUM(uiTableModelColumnType) { + uiTableModelColumnString, +}; + +struct uiTableModelHandler { + int (*NumColumns)(uiTableModel *); + uiTableModelColumnType (*ColumnType)(uiTableModel *, int); + int (*NumRows)(uiTableModel *); + void *(*CellValue)(uiTableModel *, int, int); + void (*SetCellValue)(uiTableModel *, int, int, void *); +}; + +_UI_EXTERN void *uiTableModelStrdup(const char *str); + +_UI_EXTERN uiTableModel *uiNewTableModel(uiTableModelHandler *mh); +_UI_EXTERN void uiFreeTableModel(uiTableModel *m); +_UI_EXTERN void uiTableModelRowInserted(uiTableModel *m, int newIndex); +_UI_EXTERN void uiTableModelRowChanged(uiTableModel *m, int index); +_UI_EXTERN void uiTableModelRowDeleted(uiTableModel *m, int oldIndex); + +typedef struct uiTableCellLayout uiTableCellLayout; +typedef struct uiTableCellPart uiTableCellPart; + +_UI_EXTERN uiTableCellLayout *uiNewTableCellLayout(void); +_UI_EXTERN void uiFreeTableCellLayout(uiTableCellLayout *c); +_UI_ExTERN void uiTableCellLayoutAppend(uiTableCellLayout *c, uiTableCellPart *part, int expand); + +_UI_EXTERN uiTableCellPart *uiNewTableTextPart(int modelColumn); +_UI_EXTERN void uiFreeTableCellPart(uiTableCellPart *p); + +typedef struct uiTable uiTable; +#define uiTable(this) ((uiTable *) (this)) +_UI_EXTERN void uiTableAppendColumn(uiTable *t, const char *name, uiTableCellLayout *layout); +_UI_EXTERN void uiTableAppendTextColumn(uiTable *t, const char *name, int modelColumn); +_UI_EXTERN uiTable *uiNewTable(uiTableModel *model); From 10480db89536bd690cf55ac61c133e0fb90e14c1 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 21 Jun 2016 12:15:38 -0400 Subject: [PATCH 0392/1329] Call gtk_widget_destroy() instead of g_object_unref() when destroying uiWindows. Fixes #165. --- unix/window.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/unix/window.c b/unix/window.c index 71e47aa9..a1d09005 100644 --- a/unix/window.c +++ b/unix/window.c @@ -93,7 +93,8 @@ static void uiWindowDestroy(uiControl *c) gtk_widget_destroy(w->childHolderWidget); gtk_widget_destroy(w->vboxWidget); // and finally free ourselves - g_object_unref(w->widget); + // use gtk_widget_destroy() instead of g_object_unref() because GTK+ has internal references (see #165) + gtk_widget_destroy(w->widget); uiFreeControl(uiControl(w)); } @@ -323,7 +324,7 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) uiWindowOnContentSizeChanged(w, defaultOnPositionContentSizeChanged, NULL); // normally it's SetParent() that does this, but we can't call SetParent() on a uiWindow - // TODO we really need to clean this up + // TODO we really need to clean this up, especially since see uiWindowDestroy() above g_object_ref(w->widget); return w; From 2d4f6eb2b8e1c0dbe614c42efd1f2d2541e3b0c2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 21 Jun 2016 12:39:53 -0400 Subject: [PATCH 0393/1329] Stop uiProgressBar pulsing on destroy on GTK+. Fixes #163. --- uitable.h | 2 +- unix/progressbar.c | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/uitable.h b/uitable.h index 645096bd..a9008369 100644 --- a/uitable.h +++ b/uitable.h @@ -29,7 +29,7 @@ typedef struct uiTableCellPart uiTableCellPart; _UI_EXTERN uiTableCellLayout *uiNewTableCellLayout(void); _UI_EXTERN void uiFreeTableCellLayout(uiTableCellLayout *c); -_UI_ExTERN void uiTableCellLayoutAppend(uiTableCellLayout *c, uiTableCellPart *part, int expand); +_UI_EXTERN void uiTableCellLayoutAppend(uiTableCellLayout *c, uiTableCellPart *part, int expand); _UI_EXTERN uiTableCellPart *uiNewTableTextPart(int modelColumn); _UI_EXTERN void uiFreeTableCellPart(uiTableCellPart *p); diff --git a/unix/progressbar.c b/unix/progressbar.c index 08b45f33..9b543b04 100644 --- a/unix/progressbar.c +++ b/unix/progressbar.c @@ -9,7 +9,18 @@ struct uiProgressBar { guint pulser; }; -uiUnixControlAllDefaults(uiProgressBar) +uiUnixControlAllDefaultsExceptDestroy(uiProgressBar) + +static void uiProgressBarDestroy(uiControl *c) +{ + uiProgressBar *p = uiProgressBar(c); + + // be sure to stop the timeout now + if (p->indeterminate) + g_source_remove(p->pulser); + g_object_unref(p->widget); + uiFreeControl(uiControl(p)); +} int uiProgressBarValue(uiProgressBar *p) { From d93bb2c48f0c6a521c8dd089ab84c73977ce3697 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 21 Jun 2016 12:45:04 -0400 Subject: [PATCH 0394/1329] Formatting fixes. --- test/main.c | 2 +- test/menus.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/main.c b/test/main.c index ca092f95..43b54ad0 100644 --- a/test/main.c +++ b/test/main.c @@ -87,7 +87,7 @@ int main(int argc, char *argv[]) w = newWindow("Main Window", 320, 240, 1); uiWindowOnClosing(w, onClosing, NULL); - printf("main window %p\n", (void*)w); + printf("main window %p\n", (void *) w); uiOnShouldQuit(onShouldQuit, w); diff --git a/test/menus.c b/test/menus.c index d8fc44fa..87ff80a3 100644 --- a/test/menus.c +++ b/test/menus.c @@ -47,7 +47,7 @@ static void forceOff(uiMenuItem *item, uiWindow *w, void *data) static void whatWindow(uiMenuItem *item, uiWindow *w, void *data) { - printf("menu item clicked on window %p\n", (void*)w); + printf("menu item clicked on window %p\n", (void *) w); } void initMenus(void) From aa2e8cf4f53c261c863df2143ac9ff5881cbabd4 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 21 Jun 2016 22:07:39 -0400 Subject: [PATCH 0395/1329] Simplified the uiTable cell layout stuff a bit: rather than implying that a cell layout can be used by multiple columns, just have the column *be* the cell layout. --- uitable.h | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/uitable.h b/uitable.h index a9008369..ed5d96ec 100644 --- a/uitable.h +++ b/uitable.h @@ -24,18 +24,16 @@ _UI_EXTERN void uiTableModelRowInserted(uiTableModel *m, int newIndex); _UI_EXTERN void uiTableModelRowChanged(uiTableModel *m, int index); _UI_EXTERN void uiTableModelRowDeleted(uiTableModel *m, int oldIndex); -typedef struct uiTableCellLayout uiTableCellLayout; +typedef struct uiTableColumn uiTableColumn; typedef struct uiTableCellPart uiTableCellPart; -_UI_EXTERN uiTableCellLayout *uiNewTableCellLayout(void); -_UI_EXTERN void uiFreeTableCellLayout(uiTableCellLayout *c); -_UI_EXTERN void uiTableCellLayoutAppend(uiTableCellLayout *c, uiTableCellPart *part, int expand); +_UI_EXTERN void uiTableColumnAppend(uiTableColumn *c, uiTableCellPart *part, int expand); _UI_EXTERN uiTableCellPart *uiNewTableTextPart(int modelColumn); _UI_EXTERN void uiFreeTableCellPart(uiTableCellPart *p); typedef struct uiTable uiTable; #define uiTable(this) ((uiTable *) (this)) -_UI_EXTERN void uiTableAppendColumn(uiTable *t, const char *name, uiTableCellLayout *layout); -_UI_EXTERN void uiTableAppendTextColumn(uiTable *t, const char *name, int modelColumn); +_UI_EXTERN uiTableColumn *uiTableAppendColumn(uiTable *t, const char *name); +_UI_EXTERN uiTableColumn *uiTableAppendTextColumn(uiTable *t, const char *name, int modelColumn); _UI_EXTERN uiTable *uiNewTable(uiTableModel *model); From b21ec6cf6b4d6afa8cd7a51c2a710648027c8cde Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 21 Jun 2016 22:22:13 -0400 Subject: [PATCH 0396/1329] Laid the foundation for uiTable: common code and a test. Now to start actually implementing it. --- common/CMakeLists.txt | 1 + common/table.c | 13 +++++++++ test/CMakeLists.txt | 1 + test/main.c | 9 +++++- test/page16.c | 68 +++++++++++++++++++++++++++++++++++++++++++ test/test.h | 3 ++ 6 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 common/table.c create mode 100644 test/page16.c diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 91d79493..7f704032 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -6,6 +6,7 @@ list(APPEND _LIBUI_SOURCES common/debug.c common/matrix.c common/shouldquit.c + common/table.c common/userbugs.c ) set(_LIBUI_SOURCES ${_LIBUI_SOURCES} PARENT_SCOPE) diff --git a/common/table.c b/common/table.c new file mode 100644 index 00000000..01abe36b --- /dev/null +++ b/common/table.c @@ -0,0 +1,13 @@ +// 21 june 2016 +#include "uipriv.h" + +uiTableColumn *uiTableAppendTextColumn(uiTable *t, const char *name, int modelColumn) +{ + uiTableColumn *tc; + uiTableCellPart *part; + + part = uiNewTableTextPart(modelColumn); + tc = uiTableAppendColumn(t, name); + uiTableColumnAppend(tc, part, 1); + return tc; +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e4924bb3..596f02ff 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -26,6 +26,7 @@ _add_exec(tester page13.c page14.c page15.c + page16.c spaced.c ${_TEST_RESOURCES_RC} ) diff --git a/test/main.c b/test/main.c index 43b54ad0..f33f30ab 100644 --- a/test/main.c +++ b/test/main.c @@ -50,6 +50,7 @@ int main(int argc, char *argv[]) uiBox *page11, *page12, *page13; uiTab *page14; uiBox *page15; + uiBox *page16; uiTab *outerTab; uiTab *innerTab; int nomenus = 0; @@ -138,7 +139,7 @@ int main(int argc, char *argv[]) uiTabAppend(innerTab, "Page 10", uiControl(page10)); innerTab = newTab(); - uiTabAppend(outerTab, "Pages 11-?", uiControl(innerTab)); + uiTabAppend(outerTab, "Pages 11-15", uiControl(innerTab)); // page11 = makePage11(); // uiTabAppend(innerTab, "Page 11", uiControl(page11)); @@ -155,6 +156,12 @@ int main(int argc, char *argv[]) page15 = makePage15(w); uiTabAppend(innerTab, "Page 15", uiControl(page15)); + innerTab = newTab(); + uiTabAppend(outerTab, "Pages 16-?", uiControl(innerTab)); + + page16 = makePage16(); + uiTabAppend(innerTab, "Page 16", uiControl(page16)); + if (startspaced) setSpaced(1); diff --git a/test/page16.c b/test/page16.c new file mode 100644 index 00000000..0dd74421 --- /dev/null +++ b/test/page16.c @@ -0,0 +1,68 @@ +// 21 june 2016 +#include "test.h" + +static uiTableModelHandler mh; + +static nt modelNumColumns(uiTableModel *m) +{ + return 3; +} + +static uiTableModelColumnType modelColumnType(uiTableModel *m, int column) +{ + return uiTableModelColumnString; +} + +static int modelNumRows(uiTableModel *m) +{ + return 15; +} + +static void *modelCellValue(uiTableModel *m, int row, int col) +{ + char buf[256]; + + switch (col) { + case 0: + sprintf(buf, "Row %d", row); + break; + case 1: + case 2: + strcpy(buf, "Part"); + break; + } + return uiTableModelStrdup(buf); +} + +static void modelSetCellValue(uiTableModel *m, int row, int col, void *val) +{ + // not implemented yet +} + +uiBox *makePage16(void) +{ + uiBox *page16; + uiTableModel *m; + uiTable *t; + uiTableColumn *tc; + + page16 = newVerticalBox(); + + mh.NumColumns = modelNumColumns; + mh.ColumnType = modelColumnType; + mh.NumRows = modelNumRows; + mh.CellValue = modelCellValue; + mh.SetCellValue = modelSetCellValue; + m = uiNewTableModel(&mh); + + t = uiNewTable(m); + uiBoxAppend(page16, uiControl(t), 1); + + uiTableAppendTextColumn(t, "Column 1", 0); + + tc = uiTableAppendColumn(t, "Column 2"); + uiTableColumnAppend(tc, uiNewTableTextPart(1), 0); + uiTableColumnAppend(tc, uiNewTableTextPart(2), 1); + + return page16; +} diff --git a/test/test.h b/test/test.h index 66b1baa7..427b3561 100644 --- a/test/test.h +++ b/test/test.h @@ -89,3 +89,6 @@ extern uiTab *makePage14(void); // page15.c extern uiBox *makePage15(uiWindow *); + +// page16.c +extern uiBox *makePage16(void); From 2f08ec683a87b113032dee6f460ccc848f3b6a0c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 21 Jun 2016 23:58:17 -0400 Subject: [PATCH 0397/1329] Started the OS X uiTable implementation. --- darwin/CMakeLists.txt | 1 + darwin/table.m | 239 ++++++++++++++++++++++++++++++++++++++++++ test/page16.c | 10 +- uitable.h | 11 +- 4 files changed, 251 insertions(+), 10 deletions(-) create mode 100644 darwin/table.m diff --git a/darwin/CMakeLists.txt b/darwin/CMakeLists.txt index e7fe4507..c8d1f3be 100644 --- a/darwin/CMakeLists.txt +++ b/darwin/CMakeLists.txt @@ -34,6 +34,7 @@ list(APPEND _LIBUI_SOURCES darwin/spinbox.m darwin/stddialogs.m darwin/tab.m + darwin/table.m darwin/text.m darwin/util.m darwin/window.m diff --git a/darwin/table.m b/darwin/table.m new file mode 100644 index 00000000..9ae9e184 --- /dev/null +++ b/darwin/table.m @@ -0,0 +1,239 @@ +// 21 june 2016 +#import "uipriv_darwin.h" + +@interface tableModel : NSObject { + uiTableModel *libui_m; +} +- (id)initWithModel:(uiTableModel *)m; +@end + +enum { + partText, +}; + +@interface tablePart : NSObject +@property int type; +@property int mainColumn; +@property int expand; +- (NSView *)mkView:(uiTableModel *)m row:(int)row; +@end + +@interface tableColumn : NSTableColumn +@property uiTableColumn *libui_col; +@end + +struct uiTableModel { + uiTableModelHandler *mh; + tableModel *m; + NSMutableArray *tables; +}; + +// TODO better memory management for this +// note how expand is part of this +struct uiTableCellPart { + tablePart *part; +} + +struct uiTableColumn { + NSMutableArray *parts; +}; + +struct uiTable { + uiDarwinControl c; + NSScrollView *sv; + NSTableView *tv; +}; + +@implementation tableModel + +- (id)initWithModel:(uiTableModel *)m +{ + self = [super init]; + if (self) + self->libui_m = m; + return self; +} + +- (NSInteger)numberOfRowsInTableView:(NSTableView *)tv +{ + uiTableModelHandler *mh = self->libui_m->mh; + + return (*(mh->NumRows))(mh, m); +} + +// these are according to Interface Builder +#define xleft 2 +#define xmiddle 7 /* between images and text, anyway; let's just use it for everything to be simpler */ +#define xright 3 + + - (NSView *)tableView:(NSTableView *)tv viewForTableColumn:(NSTableColumn *)cc row:(NSInteger)row +{ + NSView *v; + tableColumn *c = (tableColumn *) cc; + tablePart *part; + NSMutableArray *views; + NSView *view, *prev; + + v = [[NSView alloc] initWithFrame:NSZeroRect]; + + views = [NSMutableArray new]; + for (part in c.libui_col->parts) + [views addObject:[part mkView:self->libui_m row:row]]; + if ([views count] == 0) // empty (TODO allow?) + goto done; + + // arrange horizontally + prev = nil; + for (view in views) { + if (prev == nil) { // first view + [v addConstraint:mkConstraint(v, NSLayoutAttributeLeading, + NSLayoutRelationEqual, + view, NSLayoutAttributeLeading, + 1, -xleft, + @"uiTableColumn first part horizontal constraint")]; + prev = view; + continue; + } + [v addConstraint:mkConstraint(prev, NSLayoutAttributeTrailing, + NSLayoutRelationEqual, + view, NSLayoutAttributeLeading, + 1, -xmiddle, + @"uiTableColumn middle horizontal constraint")]; + prev = view; + } + [v addConstraint:mkConstraint(prev, NSLayoutAttributeTrailing, + NSLayoutRelationEqual, + v, NSLayoutAttributeTrailing, + 1, -xright, + @"uiTableColumn last part horizontal constraint")]; + + // and vertically + for (view in views) + [v addConstraint:mkConstraint(view, NSLayoutAttributeCenterY, + NSLayoutRelationEqual, + v, NSLayoutAttributeCenterY, + 1, 0, + @"uiTableColumn part vertical constraint")]; + +done: + [views release]; + // TODO autorelease? + return v; +} + +@end + +@implementation tablePart + +- (NSView *)mkView:(uiTableModel *)m row:(int)row; +{ + void *data; + NSString *str; + NSView *view; + NSTextField *tf; + + data = (*(m->mh->CellValue))(m->mh, m, row, self.mainColumn); + switch (self.type) { + case partText: + str = toNSString((char *) data); + uiFree(data); + tf = newLabel(str); + // TODO set wrap and ellipsize modes + view = tf; + break; + } + + // if stretchy, don't hug, otherwise hug forcibly + if (self.expand) + [view setContentHuggingPriority:NSLayoutPriorityDefaultLow forOrientation:NSLayoutConstraintOrientationHorizontal]; + else + [view setContentHuggingPriority:NSLayoutPriorityRequired forOrientation:NSLayoutConstraintOrientationHorizontal]; + // TODO autorelease? + return view; +} + +@end + +@implementation tableColumn +@end + +void *uiTableModelStrdup(const char *str) +{ + // TODO don't we have this already? + char *dup; + + dup = (char *) uiAlloc((strlen(str) + 1) * sizeof (char), "char[]"); + strcpy(dup, str); + return dup; +} + +uiTableModel *uiNewTableModel(uITableModelHandler *mh) +{ + uiTableModel *m; + + m = uiNew(uiTableModel); + m->mh = mh; + m->m = [[tableModel alloc] initWithModel:m]; + m->tables = [NSMutableArray new]; + return m; +} + +void uiFreeTableModel(uiTableModel *m) +{ + if ([m->tables count] != 0) + userbug("You cannot free a uiTableModel while uiTables are using it."); + [m->tables release]; + [m->m release]; + uiFree(m); +} + +void uiTableModelRowInserted(uiTableModel *m, int newIndex) +{ + NSTableView *tv; + NSIndexSet *set; + + set = [NSIndexSet indexSetWithIndex:index]; + for (tv in m->tables) + [tv insertRowsAtIndexes:set withAnimation:NSTableViewAnimationEffectNone]; + // set is autoreleased +} + +void uiTableModelRowChanged(uiTableModel *m, int index) +{ + NSTableView *tv; + NSIndexSet *set, *cols; + + set = [NSIndexSet indexSetWithIndex:index]; + for (tv in m->tables) { + cols = [[NSIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, [[tv tableColumns] count])]; + [tv reloadDataForRowIndexes:set columnIndexes:cols]; + [cols release]; + } + // set is autoreleased +} + +void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) +{ + NSTableView *tv; + NSIndexSet *set; + + set = [NSIndexSet indexSetWithIndex:index]; + for (tv in m->tables) + [tv removeRowsAtIndexes:set withAnimation:NSTableViewAnimationEffectNone]; + // set is autoreleased +} + +void uiTableColumnAppend(uiTableColumn *c, uiTableCellPart *part, int expand) +{ + part->part.expand = expand; + [c->parts addObject:part->part]; +} + +_UI_EXTERN uiTableCellPart *uiNewTableTextPart(int modelColumn); +_UI_EXTERN void uiFreeTableCellPart(uiTableCellPart *p); + +typedef struct uiTable uiTable; +#define uiTable(this) ((uiTable *) (this)) +_UI_EXTERN uiTableColumn *uiTableAppendColumn(uiTable *t, const char *name); +_UI_EXTERN uiTableColumn *uiTableAppendTextColumn(uiTable *t, const char *name, int modelColumn); +_UI_EXTERN uiTable *uiNewTable(uiTableModel *model); diff --git a/test/page16.c b/test/page16.c index 0dd74421..129aeaf8 100644 --- a/test/page16.c +++ b/test/page16.c @@ -3,22 +3,22 @@ static uiTableModelHandler mh; -static nt modelNumColumns(uiTableModel *m) +static nt modelNumColumns(uiTableModelHandler *mh, uiTableModel *m) { return 3; } -static uiTableModelColumnType modelColumnType(uiTableModel *m, int column) +static uiTableModelColumnType modelColumnType(uiTableModelHandler *mh, uiTableModel *m, int column) { return uiTableModelColumnString; } -static int modelNumRows(uiTableModel *m) +static int modelNumRows(uiTableModelHandler *mh, uiTableModel *m) { return 15; } -static void *modelCellValue(uiTableModel *m, int row, int col) +static void *modelCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, int col) { char buf[256]; @@ -34,7 +34,7 @@ static void *modelCellValue(uiTableModel *m, int row, int col) return uiTableModelStrdup(buf); } -static void modelSetCellValue(uiTableModel *m, int row, int col, void *val) +static void modelSetCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, int col, void *val) { // not implemented yet } diff --git a/uitable.h b/uitable.h index ed5d96ec..4b7d5cf7 100644 --- a/uitable.h +++ b/uitable.h @@ -9,11 +9,11 @@ _UI_ENUM(uiTableModelColumnType) { }; struct uiTableModelHandler { - int (*NumColumns)(uiTableModel *); - uiTableModelColumnType (*ColumnType)(uiTableModel *, int); - int (*NumRows)(uiTableModel *); - void *(*CellValue)(uiTableModel *, int, int); - void (*SetCellValue)(uiTableModel *, int, int, void *); + int (*NumColumns)(uiTableModelHandler *, uiTableModel *); + uiTableModelColumnType (*ColumnType)(uiTableModelHandler *, uiTableModel *, int); + int (*NumRows)(uiTableModelHandler *, uiTableModel *); + void *(*CellValue)(uiTableModelHandler *, uiTableModel *, int, int); + void (*SetCellValue)(uiTableModelHandler *, uiTableModel *, int, int, void *); }; _UI_EXTERN void *uiTableModelStrdup(const char *str); @@ -23,6 +23,7 @@ _UI_EXTERN void uiFreeTableModel(uiTableModel *m); _UI_EXTERN void uiTableModelRowInserted(uiTableModel *m, int newIndex); _UI_EXTERN void uiTableModelRowChanged(uiTableModel *m, int index); _UI_EXTERN void uiTableModelRowDeleted(uiTableModel *m, int oldIndex); +// TODO reordering/moving typedef struct uiTableColumn uiTableColumn; typedef struct uiTableCellPart uiTableCellPart; From c26f438d3bddf8e09261efad3f92a7b9815c0613 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 22 Jun 2016 00:40:30 -0400 Subject: [PATCH 0398/1329] Finished the initial implementation of OS X uiTable. Now to test. --- darwin/table.m | 92 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 85 insertions(+), 7 deletions(-) diff --git a/darwin/table.m b/darwin/table.m index 9ae9e184..f54fc0f0 100644 --- a/darwin/table.m +++ b/darwin/table.m @@ -35,6 +35,7 @@ struct uiTableCellPart { } struct uiTableColumn { + tableColumn *c; NSMutableArray *parts; }; @@ -42,6 +43,7 @@ struct uiTable { uiDarwinControl c; NSScrollView *sv; NSTableView *tv; + struct scrollViewData *d; }; @implementation tableModel @@ -229,11 +231,87 @@ void uiTableColumnAppend(uiTableColumn *c, uiTableCellPart *part, int expand) [c->parts addObject:part->part]; } -_UI_EXTERN uiTableCellPart *uiNewTableTextPart(int modelColumn); -_UI_EXTERN void uiFreeTableCellPart(uiTableCellPart *p); +uiTableCellPart *uiNewTableTextPart(int modelColumn) +{ + uiTableCellPart *p; -typedef struct uiTable uiTable; -#define uiTable(this) ((uiTable *) (this)) -_UI_EXTERN uiTableColumn *uiTableAppendColumn(uiTable *t, const char *name); -_UI_EXTERN uiTableColumn *uiTableAppendTextColumn(uiTable *t, const char *name, int modelColumn); -_UI_EXTERN uiTable *uiNewTable(uiTableModel *model); + p = uiNew(uiTableCellPart); + p->part = [tablePart new]; + p->part.type = partText; + p->part.mainColumn = modelColumn; + return p; +} + +void uiFreeTableCellPart(uiTableCellPart *p) +{ + // TODO disallow if in use + [p->part release]; + uiFree(p); +} + +uiDarwinControlAllDefaultsExceptDestroy(uiTable, sv) + +static void uiTableDestroy(uiControl *c) +{ + uiTable *t = uiTable(c); + + // TODO + [t->sv release]; + uiFreeControl(uiControl(t)); +} + +uiTableColumn *uiTableAppendColumn(uiTable *t, const char *name) +{ + uiTableColumn *c; + + c = new(uiTableColumn); + c->c = [[tableColumn alloc] initWithIdentifier:@""]; + // via Interface Builder + [c->c setResizingMask:(NSTableColumnAutoresizingMask | NSTableColumnUserResizingMask)]; + [c->c setTitle:toNSString(name)]; + // TODO is this sufficient? + [[c->c headerCell] setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]]; + c->parts = [NSMutableArray new]; + [t->tv addTableColumn:c->c]; + return c; +} + +uiTable *uiNewTable(uiTableModel *model) +{ + uiTable *t; + struct scrollViewCreateParams p; + + uiDarwinNewControl(uiTable, t); + + e->tv = [[NSTableView alloc] initWIthFrame:NSZeroRect]; + + [e->tv setDataSource:model->m]; + [e->tv setDelegate:model->m]; + [e->tv reloadAllData]; + [model->tables addObject:e->tv]; + + // TODO is this sufficient? + [e->tv setAllowsColumnReordering:NO]; + [e->tv setAllowsColumnResizing:YES]; + [e->tv setAllowsMultipleSelection:NO]; + [e->tv setAllowsEmptySelection:YES]; + [e->tv setAllowsColumnSelection:NO]; + [e->tv setUsesAlternatingRowBackgroundColors:YES]; + [e->tv setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleRegular]; + [e->tv setGridStyleMask:NSTableViewGridNone]; + [e->tv setAllowsTypeSelect:YES]; + // TODO floatsGroupRows — do we even allow group rows? + + memset(&p, 0, sizeof (struct scrollViewCreateParams)); + p.DocumentView = t->tv; + // this is what Interface Builder sets it to + // TODO verify + p.BackgroundColor = [NSColor colorWithCalibratedWhite:1.0 alpha:1.0]; + p.DrawsBackground = YES; + p.Bordered = YES; + p.HScroll = YES; + p.VScroll = YES; + t->sv = mkScrollView(&p, &(t->d)); + + return t; +} From 1c70edaef14c76c14cca34bb4cf9071527219113 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 22 Jun 2016 00:58:10 -0400 Subject: [PATCH 0399/1329] Fixed initial OS X uiTable. It works! --- common/controlsigs.h | 1 + common/table.c | 1 + darwin/table.m | 52 +++++++++++++++++++++++++------------------- test/page16.c | 2 +- 4 files changed, 33 insertions(+), 23 deletions(-) diff --git a/common/controlsigs.h b/common/controlsigs.h index 03e675cc..1cbf18d5 100644 --- a/common/controlsigs.h +++ b/common/controlsigs.h @@ -21,4 +21,5 @@ #define uiSliderSignature 0x536C6964 #define uiSpinboxSignature 0x5370696E #define uiTabSignature 0x54616273 +#define uiTableSignature 0x5461626C #define uiWindowSignature 0x57696E64 diff --git a/common/table.c b/common/table.c index 01abe36b..d65c557e 100644 --- a/common/table.c +++ b/common/table.c @@ -1,4 +1,5 @@ // 21 june 2016 +#include "../ui.h" #include "uipriv.h" uiTableColumn *uiTableAppendTextColumn(uiTable *t, const char *name, int modelColumn) diff --git a/darwin/table.m b/darwin/table.m index f54fc0f0..00309aae 100644 --- a/darwin/table.m +++ b/darwin/table.m @@ -1,6 +1,10 @@ // 21 june 2016 #import "uipriv_darwin.h" +// TODOs +// - can't seem to grow the table view vertically beyond a certain height +// - header cell seems off + @interface tableModel : NSObject { uiTableModel *libui_m; } @@ -32,7 +36,7 @@ struct uiTableModel { // note how expand is part of this struct uiTableCellPart { tablePart *part; -} +}; struct uiTableColumn { tableColumn *c; @@ -60,7 +64,7 @@ struct uiTable { { uiTableModelHandler *mh = self->libui_m->mh; - return (*(mh->NumRows))(mh, m); + return (*(mh->NumRows))(mh, self->libui_m); } // these are according to Interface Builder @@ -84,9 +88,10 @@ struct uiTable { if ([views count] == 0) // empty (TODO allow?) goto done; - // arrange horizontally + // add to v and arrange horizontally prev = nil; for (view in views) { + [v addSubview:view]; if (prev == nil) { // first view [v addConstraint:mkConstraint(v, NSLayoutAttributeLeading, NSLayoutRelationEqual, @@ -119,6 +124,7 @@ struct uiTable { done: [views release]; + [v setTranslatesAutoresizingMaskIntoConstraints:NO]; // TODO autorelease? return v; } @@ -127,7 +133,7 @@ done: @implementation tablePart -- (NSView *)mkView:(uiTableModel *)m row:(int)row; +- (NSView *)mkView:(uiTableModel *)m row:(int)row { void *data; NSString *str; @@ -150,6 +156,7 @@ done: [view setContentHuggingPriority:NSLayoutPriorityDefaultLow forOrientation:NSLayoutConstraintOrientationHorizontal]; else [view setContentHuggingPriority:NSLayoutPriorityRequired forOrientation:NSLayoutConstraintOrientationHorizontal]; + [view setTranslatesAutoresizingMaskIntoConstraints:NO]; // TODO autorelease? return view; } @@ -169,7 +176,7 @@ void *uiTableModelStrdup(const char *str) return dup; } -uiTableModel *uiNewTableModel(uITableModelHandler *mh) +uiTableModel *uiNewTableModel(uiTableModelHandler *mh) { uiTableModel *m; @@ -194,7 +201,7 @@ void uiTableModelRowInserted(uiTableModel *m, int newIndex) NSTableView *tv; NSIndexSet *set; - set = [NSIndexSet indexSetWithIndex:index]; + set = [NSIndexSet indexSetWithIndex:newIndex]; for (tv in m->tables) [tv insertRowsAtIndexes:set withAnimation:NSTableViewAnimationEffectNone]; // set is autoreleased @@ -219,7 +226,7 @@ void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) NSTableView *tv; NSIndexSet *set; - set = [NSIndexSet indexSetWithIndex:index]; + set = [NSIndexSet indexSetWithIndex:oldIndex]; for (tv in m->tables) [tv removeRowsAtIndexes:set withAnimation:NSTableViewAnimationEffectNone]; // set is autoreleased @@ -264,8 +271,9 @@ uiTableColumn *uiTableAppendColumn(uiTable *t, const char *name) { uiTableColumn *c; - c = new(uiTableColumn); + c = uiNew(uiTableColumn); c->c = [[tableColumn alloc] initWithIdentifier:@""]; + c->c.libui_col = c; // via Interface Builder [c->c setResizingMask:(NSTableColumnAutoresizingMask | NSTableColumnUserResizingMask)]; [c->c setTitle:toNSString(name)]; @@ -283,23 +291,23 @@ uiTable *uiNewTable(uiTableModel *model) uiDarwinNewControl(uiTable, t); - e->tv = [[NSTableView alloc] initWIthFrame:NSZeroRect]; + t->tv = [[NSTableView alloc] initWithFrame:NSZeroRect]; - [e->tv setDataSource:model->m]; - [e->tv setDelegate:model->m]; - [e->tv reloadAllData]; - [model->tables addObject:e->tv]; + [t->tv setDataSource:model->m]; + [t->tv setDelegate:model->m]; + [t->tv reloadData]; + [model->tables addObject:t->tv]; // TODO is this sufficient? - [e->tv setAllowsColumnReordering:NO]; - [e->tv setAllowsColumnResizing:YES]; - [e->tv setAllowsMultipleSelection:NO]; - [e->tv setAllowsEmptySelection:YES]; - [e->tv setAllowsColumnSelection:NO]; - [e->tv setUsesAlternatingRowBackgroundColors:YES]; - [e->tv setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleRegular]; - [e->tv setGridStyleMask:NSTableViewGridNone]; - [e->tv setAllowsTypeSelect:YES]; + [t->tv setAllowsColumnReordering:NO]; + [t->tv setAllowsColumnResizing:YES]; + [t->tv setAllowsMultipleSelection:NO]; + [t->tv setAllowsEmptySelection:YES]; + [t->tv setAllowsColumnSelection:NO]; + [t->tv setUsesAlternatingRowBackgroundColors:YES]; + [t->tv setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleRegular]; + [t->tv setGridStyleMask:NSTableViewGridNone]; + [t->tv setAllowsTypeSelect:YES]; // TODO floatsGroupRows — do we even allow group rows? memset(&p, 0, sizeof (struct scrollViewCreateParams)); diff --git a/test/page16.c b/test/page16.c index 129aeaf8..929dda91 100644 --- a/test/page16.c +++ b/test/page16.c @@ -3,7 +3,7 @@ static uiTableModelHandler mh; -static nt modelNumColumns(uiTableModelHandler *mh, uiTableModel *m) +static int modelNumColumns(uiTableModelHandler *mh, uiTableModel *m) { return 3; } From 93923bbcb34e02fc1d7bb7e8335cd07a7fd82b5c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 22 Jun 2016 00:59:04 -0400 Subject: [PATCH 0400/1329] More TODOs. --- darwin/table.m | 1 + 1 file changed, 1 insertion(+) diff --git a/darwin/table.m b/darwin/table.m index 00309aae..c01a2ad2 100644 --- a/darwin/table.m +++ b/darwin/table.m @@ -4,6 +4,7 @@ // TODOs // - can't seem to grow the table view vertically beyond a certain height // - header cell seems off +// - text view cell parts don't change color when selected @interface tableModel : NSObject { uiTableModel *libui_m; From a82835fff389f213cb2f5a1def136dc189a72a2c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 22 Jun 2016 01:06:47 -0400 Subject: [PATCH 0401/1329] Fixed uiTable selection colors on OS X. --- darwin/table.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/darwin/table.m b/darwin/table.m index c01a2ad2..c46f4249 100644 --- a/darwin/table.m +++ b/darwin/table.m @@ -4,7 +4,6 @@ // TODOs // - can't seem to grow the table view vertically beyond a certain height // - header cell seems off -// - text view cell parts don't change color when selected @interface tableModel : NSObject { uiTableModel *libui_m; @@ -75,13 +74,13 @@ struct uiTable { - (NSView *)tableView:(NSTableView *)tv viewForTableColumn:(NSTableColumn *)cc row:(NSInteger)row { - NSView *v; + NSTableCellView *v; tableColumn *c = (tableColumn *) cc; tablePart *part; NSMutableArray *views; NSView *view, *prev; - v = [[NSView alloc] initWithFrame:NSZeroRect]; + v = [[NSTableCellView alloc] initWithFrame:NSZeroRect]; views = [NSMutableArray new]; for (part in c.libui_col->parts) @@ -93,6 +92,7 @@ struct uiTable { prev = nil; for (view in views) { [v addSubview:view]; + // TODO set [v imageView] and [v textField] as appropriate? if (prev == nil) { // first view [v addConstraint:mkConstraint(v, NSLayoutAttributeLeading, NSLayoutRelationEqual, From 0ef01e1685cf9e38d3313c48180afc427f9fb755 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 22 Jun 2016 13:28:12 -0400 Subject: [PATCH 0402/1329] Added some debugging code to help figure out tables. --- darwin/table.m | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/darwin/table.m b/darwin/table.m index c46f4249..862e0bd9 100644 --- a/darwin/table.m +++ b/darwin/table.m @@ -322,5 +322,16 @@ uiTable *uiNewTable(uiTableModel *model) p.VScroll = YES; t->sv = mkScrollView(&p, &(t->d)); + dispatch_after( + dispatch_time(DISPATCH_TIME_NOW,3*NSEC_PER_SEC), + dispatch_get_main_queue(), + ^{ + for(NSView *v in t->sv.subviews){ + NSLog(@"%@ %p %@", [v class], v, NSStringFromRect([v frame])); + for(NSView *v2 in v.subviews) + NSLog(@" %@ %p %@", [v2 class], v2, NSStringFromRect([v2 frame])); + } + }); + return t; } From ddd0e82c90a846d8e0fa7403558a83dcb8bec945 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 22 Jun 2016 22:58:51 -0400 Subject: [PATCH 0403/1329] Removed auto layout from scrollviews. Need to do the same to text views too. --- darwin/scrollview.m | 76 +-------------------------------------------- darwin/table.m | 11 ------- 2 files changed, 1 insertion(+), 86 deletions(-) diff --git a/darwin/scrollview.m b/darwin/scrollview.m index 5767e00f..ec24cbd5 100644 --- a/darwin/scrollview.m +++ b/darwin/scrollview.m @@ -1,15 +1,11 @@ // 27 may 2016 #include "uipriv_darwin.h" -// TODO scrolled drawing test may have a random size outside the area size +// see http://stackoverflow.com/questions/37979445/how-do-i-properly-set-up-a-scrolling-nstableview-using-auto-layout-what-ive-tr for why we don't use auto layout struct scrollViewData { - NSLayoutConstraint *documentLeading; - NSLayoutConstraint *documentTop; BOOL hscroll; - NSLayoutConstraint *documentTrailing; BOOL vscroll; - NSLayoutConstraint *documentBottom; }; NSScrollView *mkScrollView(struct scrollViewCreateParams *p, struct scrollViewData **dout) @@ -41,7 +37,6 @@ NSScrollView *mkScrollView(struct scrollViewCreateParams *p, struct scrollViewDa [sv setVerticalScrollElasticity:NSScrollElasticityAutomatic]; [sv setAllowsMagnification:NO]; - [p->DocumentView setTranslatesAutoresizingMaskIntoConstraints:NO]; [sv setDocumentView:p->DocumentView]; d = uiNew(struct scrollViewData); scrollViewSetScrolling(sv, d, p->HScroll, p->VScroll); @@ -50,85 +45,16 @@ NSScrollView *mkScrollView(struct scrollViewCreateParams *p, struct scrollViewDa return sv; } -static void scrollViewConstraintsRemove(NSScrollView *sv, struct scrollViewData *d) -{ - if (d->documentLeading != nil) { - [sv removeConstraint:d->documentLeading]; - [d->documentLeading release]; - d->documentLeading = nil; - } - if (d->documentTop != nil) { - [sv removeConstraint:d->documentTop]; - [d->documentTop release]; - d->documentTop = nil; - } - if (d->documentTrailing != nil) { - [sv removeConstraint:d->documentTrailing]; - [d->documentTrailing release]; - d->documentTrailing = nil; - } - if (d->documentBottom != nil) { - [sv removeConstraint:d->documentBottom]; - [d->documentBottom release]; - d->documentBottom = nil; - } -} - // based on http://blog.bjhomer.com/2014/08/nsscrollview-and-autolayout.html because (as pointed out there) Apple's official guide is really only for iOS void scrollViewSetScrolling(NSScrollView *sv, struct scrollViewData *d, BOOL hscroll, BOOL vscroll) { - NSView *cv, *dv; - NSLayoutRelation rel; - - scrollViewConstraintsRemove(sv, d); - cv = [sv contentView]; - dv = [sv documentView]; - - d->documentLeading = mkConstraint(dv, NSLayoutAttributeLeading, - NSLayoutRelationEqual, - cv, NSLayoutAttributeLeading, - 1, 0, - @"NSScrollView document leading constraint"); - [sv addConstraint:d->documentLeading]; - [d->documentLeading retain]; - - d->documentTop = mkConstraint(dv, NSLayoutAttributeTop, - NSLayoutRelationEqual, - cv, NSLayoutAttributeTop, - 1, 0, - @"NSScrollView document top constraint"); - [sv addConstraint:d->documentTop]; - [d->documentTop retain]; - d->hscroll = hscroll; [sv setHasHorizontalScroller:d->hscroll]; - rel = NSLayoutRelationGreaterThanOrEqual; - if (!d->hscroll) - rel = NSLayoutRelationEqual; - d->documentTrailing = mkConstraint(dv, NSLayoutAttributeTrailing, - rel, - cv, NSLayoutAttributeTrailing, - 1, 0, - @"NSScrollView document trailing constraint"); - [sv addConstraint:d->documentTrailing]; - [d->documentTrailing retain]; - d->vscroll = vscroll; [sv setHasVerticalScroller:d->vscroll]; - rel = NSLayoutRelationGreaterThanOrEqual; - if (!d->vscroll) - rel = NSLayoutRelationEqual; - d->documentBottom = mkConstraint(dv, NSLayoutAttributeBottom, - rel, - cv, NSLayoutAttributeBottom, - 1, 0, - @"NSScrollView document bottom constraint"); - [sv addConstraint:d->documentBottom]; - [d->documentBottom retain]; } void scrollViewFreeData(NSScrollView *sv, struct scrollViewData *d) { - scrollViewConstraintsRemove(sv, d); uiFree(d); } diff --git a/darwin/table.m b/darwin/table.m index 862e0bd9..c46f4249 100644 --- a/darwin/table.m +++ b/darwin/table.m @@ -322,16 +322,5 @@ uiTable *uiNewTable(uiTableModel *model) p.VScroll = YES; t->sv = mkScrollView(&p, &(t->d)); - dispatch_after( - dispatch_time(DISPATCH_TIME_NOW,3*NSEC_PER_SEC), - dispatch_get_main_queue(), - ^{ - for(NSView *v in t->sv.subviews){ - NSLog(@"%@ %p %@", [v class], v, NSStringFromRect([v frame])); - for(NSView *v2 in v.subviews) - NSLog(@" %@ %p %@", [v2 class], v2, NSStringFromRect([v2 frame])); - } - }); - return t; } From cbb07113cad4f5f3fe45cfee46b1327c425a075c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 22 Jun 2016 23:07:59 -0400 Subject: [PATCH 0404/1329] Fixed uiMultilineEntry to no longer use Auto Layout. --- darwin/multilineentry.m | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/darwin/multilineentry.m b/darwin/multilineentry.m index 3019c8b2..605e9004 100644 --- a/darwin/multilineentry.m +++ b/darwin/multilineentry.m @@ -184,12 +184,19 @@ static uiMultilineEntry *finishMultilineEntry(BOOL hscroll) // now just to be safe; this will do some of the above but whatever disableAutocorrect(e->tv); + // see https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/TextUILayer/Tasks/TextInScrollView.html + // notice we don't use the Auto Layout code; see scrollview.m for more details + [e->tv setMaxSize:NSMakeSize(CGFLOAT_MAX, CGFLOAT_MAX)]; + [e->tv setVerticallyResizable:YES]; + [e->tv setHorizontallyResizable:hscroll]; if (hscroll) { - // see https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/TextUILayer/Tasks/TextInScrollView.html - [e->tv setHorizontallyResizable:YES]; + [e->tv setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)]; [[e->tv textContainer] setWidthTracksTextView:NO]; - [[e->tv textContainer] setContainerSize:NSMakeSize(CGFLOAT_MAX, CGFLOAT_MAX)]; + } else { + [e->tv setAutoresizingMask:NSViewWidthSizable]; + [[e->tv textContainer] setWidthTracksTextView:YES]; } + [[e->tv textContainer] setContainerSize:NSMakeSize(CGFLOAT_MAX, CGFLOAT_MAX)]; // don't use uiDarwinSetControlFont() directly; we have to do a little extra work to set the font font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]]; From a57bef13e57aeb60ffca602f8a91d4daa18ad4cf Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 22 Jun 2016 23:08:39 -0400 Subject: [PATCH 0405/1329] TODO updates. --- darwin/table.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/darwin/table.m b/darwin/table.m index c46f4249..08dc5e09 100644 --- a/darwin/table.m +++ b/darwin/table.m @@ -2,7 +2,7 @@ #import "uipriv_darwin.h" // TODOs -// - can't seem to grow the table view vertically beyond a certain height +// - initial state of table view is off // - header cell seems off @interface tableModel : NSObject { From f02fbd2ecf95619c9ad33827c6559c520d6a45df Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 23 Jun 2016 09:56:24 -0400 Subject: [PATCH 0406/1329] Some more API cleanup. Don't separate table cell parts from columns. --- common/table.c | 4 +--- darwin/table.m | 37 +++++++++---------------------------- test/page16.c | 4 ++-- uitable.h | 6 +----- 4 files changed, 13 insertions(+), 38 deletions(-) diff --git a/common/table.c b/common/table.c index d65c557e..c2190b3b 100644 --- a/common/table.c +++ b/common/table.c @@ -5,10 +5,8 @@ uiTableColumn *uiTableAppendTextColumn(uiTable *t, const char *name, int modelColumn) { uiTableColumn *tc; - uiTableCellPart *part; - part = uiNewTableTextPart(modelColumn); tc = uiTableAppendColumn(t, name); - uiTableColumnAppend(tc, part, 1); + uiTableColumnAppendTextPart(tc, modelColumn, 1); return tc; } diff --git a/darwin/table.m b/darwin/table.m index 08dc5e09..4e046a86 100644 --- a/darwin/table.m +++ b/darwin/table.m @@ -17,7 +17,7 @@ enum { @interface tablePart : NSObject @property int type; -@property int mainColumn; +@property int textColumn; @property int expand; - (NSView *)mkView:(uiTableModel *)m row:(int)row; @end @@ -32,12 +32,6 @@ struct uiTableModel { NSMutableArray *tables; }; -// TODO better memory management for this -// note how expand is part of this -struct uiTableCellPart { - tablePart *part; -}; - struct uiTableColumn { tableColumn *c; NSMutableArray *parts; @@ -141,7 +135,7 @@ done: NSView *view; NSTextField *tf; - data = (*(m->mh->CellValue))(m->mh, m, row, self.mainColumn); + data = (*(m->mh->CellValue))(m->mh, m, row, self.textColumn); switch (self.type) { case partText: str = toNSString((char *) data); @@ -233,28 +227,15 @@ void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) // set is autoreleased } -void uiTableColumnAppend(uiTableColumn *c, uiTableCellPart *part, int expand) +void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand) { - part->part.expand = expand; - [c->parts addObject:part->part]; -} + tablePart *part; -uiTableCellPart *uiNewTableTextPart(int modelColumn) -{ - uiTableCellPart *p; - - p = uiNew(uiTableCellPart); - p->part = [tablePart new]; - p->part.type = partText; - p->part.mainColumn = modelColumn; - return p; -} - -void uiFreeTableCellPart(uiTableCellPart *p) -{ - // TODO disallow if in use - [p->part release]; - uiFree(p); + part = [tablePart new]; + part.type = partText; + part.textColumn = modelColumn; + part.expand = expand; + [c->parts addObject:part]; } uiDarwinControlAllDefaultsExceptDestroy(uiTable, sv) diff --git a/test/page16.c b/test/page16.c index 929dda91..f210f89b 100644 --- a/test/page16.c +++ b/test/page16.c @@ -61,8 +61,8 @@ uiBox *makePage16(void) uiTableAppendTextColumn(t, "Column 1", 0); tc = uiTableAppendColumn(t, "Column 2"); - uiTableColumnAppend(tc, uiNewTableTextPart(1), 0); - uiTableColumnAppend(tc, uiNewTableTextPart(2), 1); + uiTableColumnAppendTextPart(tc, 1, 0); + uiTableColumnAppendTextPart(tc, 2, 1); return page16; } diff --git a/uitable.h b/uitable.h index 4b7d5cf7..fc0e5ad5 100644 --- a/uitable.h +++ b/uitable.h @@ -26,12 +26,8 @@ _UI_EXTERN void uiTableModelRowDeleted(uiTableModel *m, int oldIndex); // TODO reordering/moving typedef struct uiTableColumn uiTableColumn; -typedef struct uiTableCellPart uiTableCellPart; -_UI_EXTERN void uiTableColumnAppend(uiTableColumn *c, uiTableCellPart *part, int expand); - -_UI_EXTERN uiTableCellPart *uiNewTableTextPart(int modelColumn); -_UI_EXTERN void uiFreeTableCellPart(uiTableCellPart *p); +_UI_EXTERN void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand); typedef struct uiTable uiTable; #define uiTable(this) ((uiTable *) (this)) From 67e8db9efd09b132df52f364f73c4480bebf0abe Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 23 Jun 2016 11:29:43 -0400 Subject: [PATCH 0407/1329] Added row background colors. --- darwin/table.m | 41 +++++++++++++++++++++++++++++++++++++++-- test/page16.c | 13 ++++++++++++- uitable.h | 5 +++++ 3 files changed, 56 insertions(+), 3 deletions(-) diff --git a/darwin/table.m b/darwin/table.m index 4e046a86..0fa8ac86 100644 --- a/darwin/table.m +++ b/darwin/table.m @@ -26,6 +26,10 @@ enum { @property uiTableColumn *libui_col; @end +@interface tableView : NSTableView +@property uiTable *libui_t; +@end + struct uiTableModel { uiTableModelHandler *mh; tableModel *m; @@ -40,8 +44,9 @@ struct uiTableColumn { struct uiTable { uiDarwinControl c; NSScrollView *sv; - NSTableView *tv; + tableView *tv; struct scrollViewData *d; + int backgroundColumn; }; @implementation tableModel @@ -124,6 +129,22 @@ done: return v; } +- (void)tableView:(NSTableView *)nstv didAddRowView:(NSTableRowView *)rv forRow:(NSInteger)row +{ + uiTableModel *m = self->libui_m; + tableView *tv = (tableView *) nstv; + uiTable *t = tv.libui_t; + NSColor *color; + + if (t->backgroundColumn == -1) + return; + color = (NSColor *) ((*(m->mh->CellValue))(m->mh, m, row, t->backgroundColumn)); + if (color == nil) + return; + [rv setBackgroundColor:color]; + // TODO autorelease color? or release it? +} + @end @implementation tablePart @@ -161,6 +182,9 @@ done: @implementation tableColumn @end +@implementation tableView +@end + void *uiTableModelStrdup(const char *str) { // TODO don't we have this already? @@ -182,6 +206,11 @@ uiTableModel *uiNewTableModel(uiTableModelHandler *mh) return m; } +void *uiTableModelGiveColor(double r, double g, double b, double a) +{ + return [[NSColor colorWithSRGBRed:r green:g blue:b alpha:a] retain]; +} + void uiFreeTableModel(uiTableModel *m) { if ([m->tables count] != 0) @@ -266,6 +295,11 @@ uiTableColumn *uiTableAppendColumn(uiTable *t, const char *name) return c; } +void uiTableSetRowBackgroundColorModelColumn(uiTable *t, int modelColumn) +{ + t->backgroundColumn = modelColumn; +} + uiTable *uiNewTable(uiTableModel *model) { uiTable *t; @@ -273,7 +307,8 @@ uiTable *uiNewTable(uiTableModel *model) uiDarwinNewControl(uiTable, t); - t->tv = [[NSTableView alloc] initWithFrame:NSZeroRect]; + t->tv = [[tableView alloc] initWithFrame:NSZeroRect]; + t->tv.libui_t = t; [t->tv setDataSource:model->m]; [t->tv setDelegate:model->m]; @@ -303,5 +338,7 @@ uiTable *uiNewTable(uiTableModel *model) p.VScroll = YES; t->sv = mkScrollView(&p, &(t->d)); + t->backgroundColumn = -1; + return t; } diff --git a/test/page16.c b/test/page16.c index f210f89b..66df42b1 100644 --- a/test/page16.c +++ b/test/page16.c @@ -5,11 +5,13 @@ static uiTableModelHandler mh; static int modelNumColumns(uiTableModelHandler *mh, uiTableModel *m) { - return 3; + return 4; } static uiTableModelColumnType modelColumnType(uiTableModelHandler *mh, uiTableModel *m, int column) { + if (column == 3) + return uiTableModelColumnColor; return uiTableModelColumnString; } @@ -22,6 +24,13 @@ static void *modelCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, i { char buf[256]; + if (col == 3) { + if (row == 3) + return uiTableModelGiveColor(1, 0, 0, 1); + if (row == 11) + return uiTableModelGiveColor(0, 0.5, 1, 0.5); + return NULL; + } switch (col) { case 0: sprintf(buf, "Row %d", row); @@ -64,5 +73,7 @@ uiBox *makePage16(void) uiTableColumnAppendTextPart(tc, 1, 0); uiTableColumnAppendTextPart(tc, 2, 1); + uiTableSetRowBackgroundColorModelColumn(t, 3); + return page16; } diff --git a/uitable.h b/uitable.h index fc0e5ad5..05c5acd2 100644 --- a/uitable.h +++ b/uitable.h @@ -6,6 +6,7 @@ typedef struct uiTableModelHandler uiTableModelHandler; _UI_ENUM(uiTableModelColumnType) { uiTableModelColumnString, + uiTableModelColumnColor, }; struct uiTableModelHandler { @@ -17,6 +18,8 @@ struct uiTableModelHandler { }; _UI_EXTERN void *uiTableModelStrdup(const char *str); +// TODO rename the strdup one to this too +_UI_EXTERN void *uiTableModelGiveColor(double r, double g, double b, double a); _UI_EXTERN uiTableModel *uiNewTableModel(uiTableModelHandler *mh); _UI_EXTERN void uiFreeTableModel(uiTableModel *m); @@ -33,4 +36,6 @@ typedef struct uiTable uiTable; #define uiTable(this) ((uiTable *) (this)) _UI_EXTERN uiTableColumn *uiTableAppendColumn(uiTable *t, const char *name); _UI_EXTERN uiTableColumn *uiTableAppendTextColumn(uiTable *t, const char *name, int modelColumn); +// TODO getter? +_UI_EXTERN void uiTableSetRowBackgroundColorModelColumn(uiTable *t, int modelColumn); _UI_EXTERN uiTable *uiNewTable(uiTableModel *model); From 78e8dd3883c0561e52fd22a6de38bfbcff8cfc28 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 23 Jun 2016 14:57:40 -0400 Subject: [PATCH 0408/1329] More TODOs. --- darwin/scrollview.m | 1 + 1 file changed, 1 insertion(+) diff --git a/darwin/scrollview.m b/darwin/scrollview.m index ec24cbd5..b0b4040c 100644 --- a/darwin/scrollview.m +++ b/darwin/scrollview.m @@ -2,6 +2,7 @@ #include "uipriv_darwin.h" // see http://stackoverflow.com/questions/37979445/how-do-i-properly-set-up-a-scrolling-nstableview-using-auto-layout-what-ive-tr for why we don't use auto layout +// TODO do the same with uiGroup and uiTab? struct scrollViewData { BOOL hscroll; From 9d22d741c6cc00a2480d82f38ccc3932f37b3f10 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 23 Jun 2016 15:06:07 -0400 Subject: [PATCH 0409/1329] More TODOs. --- darwin/table.m | 1 + 1 file changed, 1 insertion(+) diff --git a/darwin/table.m b/darwin/table.m index 0fa8ac86..01dcdc78 100644 --- a/darwin/table.m +++ b/darwin/table.m @@ -4,6 +4,7 @@ // TODOs // - initial state of table view is off // - header cell seems off +// - background color shows up for a line or two below selection @interface tableModel : NSObject { uiTableModel *libui_m; From 4914d0c64c3425f227e41352e867d1d335b9a652 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 23 Jun 2016 22:16:25 -0400 Subject: [PATCH 0410/1329] Added a way to set the text color of a part. --- darwin/table.m | 29 ++++++++++++++++++++++++++++- test/page16.c | 10 ++++++++-- uitable.h | 1 + 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/darwin/table.m b/darwin/table.m index 01dcdc78..7bbbddb9 100644 --- a/darwin/table.m +++ b/darwin/table.m @@ -19,6 +19,7 @@ enum { @interface tablePart : NSObject @property int type; @property int textColumn; +@property int textColorColumn; @property int expand; - (NSView *)mkView:(uiTableModel *)m row:(int)row; @end @@ -150,6 +151,16 @@ done: @implementation tablePart +- (id)init +{ + self = [super init]; + if (self) { + self.textColumn = -1; + self.textColorColumn = -1; + } + return self; +} + - (NSView *)mkView:(uiTableModel *)m row:(int)row { void *data; @@ -163,7 +174,15 @@ done: str = toNSString((char *) data); uiFree(data); tf = newLabel(str); - // TODO set wrap and ellipsize modes + // TODO set wrap and ellipsize modes? + if (self.textColorColumn != -1) { + NSColor *color; + + color = (NSColor *) ((*(m->mh->CellValue))(m->mh, m, row, self.textColorColumn)); + if (color != nil) + [tf setTextColor:color]; + // TODO release color + } view = tf; break; } @@ -268,6 +287,14 @@ void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand) [c->parts addObject:part]; } +void uiTableColumnPartSetTextColor(uiTableColumn *c, int part, int modelColumn) +{ + tablePart *p; + + p = (tablePart *) [c->parts objectAtIndex:part]; + p.textColorColumn = modelColumn; +} + uiDarwinControlAllDefaultsExceptDestroy(uiTable, sv) static void uiTableDestroy(uiControl *c) diff --git a/test/page16.c b/test/page16.c index 66df42b1..66ba268b 100644 --- a/test/page16.c +++ b/test/page16.c @@ -5,12 +5,12 @@ static uiTableModelHandler mh; static int modelNumColumns(uiTableModelHandler *mh, uiTableModel *m) { - return 4; + return 5; } static uiTableModelColumnType modelColumnType(uiTableModelHandler *mh, uiTableModel *m, int column) { - if (column == 3) + if (column == 3 || column == 4) return uiTableModelColumnColor; return uiTableModelColumnString; } @@ -31,6 +31,11 @@ static void *modelCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, i return uiTableModelGiveColor(0, 0.5, 1, 0.5); return NULL; } + if (col == 4) { + if ((row % 2) == 1) + return uiTableModelGiveColor(0.5, 0, 0.75, 1); + return NULL; + } switch (col) { case 0: sprintf(buf, "Row %d", row); @@ -72,6 +77,7 @@ uiBox *makePage16(void) tc = uiTableAppendColumn(t, "Column 2"); uiTableColumnAppendTextPart(tc, 1, 0); uiTableColumnAppendTextPart(tc, 2, 1); + uiTableColumnPartSetTextColor(tc, 1, 4); uiTableSetRowBackgroundColorModelColumn(t, 3); diff --git a/uitable.h b/uitable.h index 05c5acd2..5860bf57 100644 --- a/uitable.h +++ b/uitable.h @@ -31,6 +31,7 @@ _UI_EXTERN void uiTableModelRowDeleted(uiTableModel *m, int oldIndex); typedef struct uiTableColumn uiTableColumn; _UI_EXTERN void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand); +_UI_EXTERN void uiTableColumnPartSetTextColor(uiTableColumn *c, int part, int modelColumn); typedef struct uiTable uiTable; #define uiTable(this) ((uiTable *) (this)) From da24b7154bcf3b441728d68c3780692afa886fcd Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 25 Jun 2016 11:22:55 -0400 Subject: [PATCH 0411/1329] Started the work toward adding images. --- ui.h | 3 +++ uidrawimage.h | 15 +++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 uidrawimage.h diff --git a/ui.h b/ui.h index 70c2f121..883ad678 100644 --- a/ui.h +++ b/ui.h @@ -316,6 +316,9 @@ struct uiAreaDrawParams { double ClipHeight; }; +// TODO merge +#include "uidrawimage.h" + typedef struct uiDrawPath uiDrawPath; typedef struct uiDrawBrush uiDrawBrush; typedef struct uiDrawStrokeParams uiDrawStrokeParams; diff --git a/uidrawimage.h b/uidrawimage.h new file mode 100644 index 00000000..deade218 --- /dev/null +++ b/uidrawimage.h @@ -0,0 +1,15 @@ +// 25 june 2016 + +typedef struct uiDrawSingleResImage uiDrawSingleResImage; +typedef struct uiDrawMultiResImage uiDrawMultiResImage; + +_UI_EXTERN uiDrawSingleResImage *uiDrawNewSingleResImage(void *data, int width, int height, int stride); +_UI_EXTERN void uiDrawFreeImage(uiDrawSingleResImage *i); +_UI_EXTERN void uiDrawSingleResImageNativeSize(uiDrawSingleResImage *i, uiDrawContext *c, double *width, double *height); + +_UI_EXTERN uiDrawMultiResImage *uiDrawNewMultiResImage(double width, double height); +_UI_EXTERN void uiDrawFreeMultiResImage(uiDrawMultiResImage *i); +_UI_EXTERN void uiDrawMultiResImageAppend(void *data, int pixelWidth, int pixelHeight, int pixelStride); + +_UI_EXTERN void uiDrawSingleRes(uiDrawContext *c, double x, double y, double width, double height uiDrawSingleResImage *image, int interpolate); +_UI_EXTERN void uiDrawMultiRes(uiDrawContext *c, double x, double y, double width, double height, uiDrawMultiResImage *image, int interpolate); From 2da1273ec281f63ba4b1eaa2167d28529a1fd1db Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 25 Jun 2016 11:23:45 -0400 Subject: [PATCH 0412/1329] Drafted the README. --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 67636543..4f443363 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,10 @@ This README is being written.
*Note that today's entry (Eastern Time) may be updated later today.* +* ** Date: Sat, 25 Jun 2016 14:33:42 -0400 Subject: [PATCH 0413/1329] Got rid of uiDrawImage stuff. I need to decide if the drawing API should care about scaling or not. --- README.md | 1 - ui.h | 3 --- uidrawimage.h | 15 --------------- 3 files changed, 19 deletions(-) delete mode 100644 uidrawimage.h diff --git a/README.md b/README.md index 4f443363..1bceba59 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,6 @@ This README is being written.
* ** Date: Sat, 25 Jun 2016 16:27:29 -0400 Subject: [PATCH 0414/1329] Okay, decided on something for images. Let's try this. --- darwin/CMakeLists.txt | 1 + darwin/image.m | 48 +++++++++++++++++++++++++++++++++++++++++++ uitable.h | 6 ++++++ 3 files changed, 55 insertions(+) create mode 100644 darwin/image.m diff --git a/darwin/CMakeLists.txt b/darwin/CMakeLists.txt index c8d1f3be..2bc34ec9 100644 --- a/darwin/CMakeLists.txt +++ b/darwin/CMakeLists.txt @@ -21,6 +21,7 @@ list(APPEND _LIBUI_SOURCES darwin/form.m darwin/grid.m darwin/group.m + darwin/image.m darwin/label.m darwin/main.m darwin/map.m diff --git a/darwin/image.m b/darwin/image.m new file mode 100644 index 00000000..7ec98bad --- /dev/null +++ b/darwin/image.m @@ -0,0 +1,48 @@ +// 25 june 2016 +#import "uipriv_darwin.h" + +struct uiImage { + NSImage *i; + NSSize size; +}; + +uiImage *uiNewImage(double width, double height) +{ + uiImage *i; + + i = uiNew(uiImage); + i->size = NSMakeSize(width, height); + i->i = [[NSImage alloc] initWithSize:i->size]; + return i; +} + +void uiFreeImage(uiImage *i) +{ + [i->i release]; + uiFree(i); +} + +void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int pixelStride) +{ + NSBitmapImageRep *repCalibrated, *repsRGB; + char *pix[1]; + + pix[0] = (char *) pixels; + repCalibrated = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:pix + pixelsWide:pixelWidth + pixelsHigh:pixelHeight + bitsPerSample:8 + samplesPerPixel:4 + hasAlpha:YES + isPlanar:NO + colorSpaceName:NSCalibratedRGBColorSpace + bitmapFormat:0 + bytesPerRow:pixelStride + bitsPerPixel:32]; + repsRGB = [repCalibrated bitmapImageRepByRetaggingWithColorSpace:[NSColorSpace sRGBColorSpace]]; + [repCalibrated release]; + + [i->i addRepresentation:repsRGB]; + [repsRGB setSize:i->size]; + [repsRGB release]; +} diff --git a/uitable.h b/uitable.h index 5860bf57..4c6f5c3a 100644 --- a/uitable.h +++ b/uitable.h @@ -1,6 +1,12 @@ // 20 june 2016 // kept in a separate file for now +typedef struct uiImage uiImage; + +_UI_EXTERN uiImage *uiNewImage(double width, double height); +_UI_EXTERN void uiFreeImage(uiImage *i); +_UI_EXTERN void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int pixelStride); + typedef struct uiTableModel uiTableModel; typedef struct uiTableModelHandler uiTableModelHandler; From 93bbf39aa475b2a02c2587dc5e6754dd9dc98c75 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 25 Jun 2016 17:26:50 -0400 Subject: [PATCH 0415/1329] Added some test images. Now to add image columns to uiTable and see what happens. --- darwin/image.m | 4 +- test/CMakeLists.txt | 1 + test/images.c | 202 ++++++++++++++++++ test/images/andlabs_16x16test_24june2016.png | Bin 0 -> 272 bytes test/images/andlabs_32x32test_24june2016.png | Bin 0 -> 432 bytes test/images/gen.go | 98 +++++++++ ...heme-0.8.90_16x16_x-office-spreadsheet.png | Bin 0 -> 704 bytes ...heme-0.8.90_32x32_x-office-spreadsheet.png | Bin 0 -> 1518 bytes test/test.h | 3 + uitable.h | 1 + 10 files changed, 307 insertions(+), 2 deletions(-) create mode 100644 test/images.c create mode 100644 test/images/andlabs_16x16test_24june2016.png create mode 100644 test/images/andlabs_32x32test_24june2016.png create mode 100644 test/images/gen.go create mode 100644 test/images/tango-icon-theme-0.8.90_16x16_x-office-spreadsheet.png create mode 100644 test/images/tango-icon-theme-0.8.90_32x32_x-office-spreadsheet.png diff --git a/darwin/image.m b/darwin/image.m index 7ec98bad..56213d24 100644 --- a/darwin/image.m +++ b/darwin/image.m @@ -25,9 +25,9 @@ void uiFreeImage(uiImage *i) void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int pixelStride) { NSBitmapImageRep *repCalibrated, *repsRGB; - char *pix[1]; + unsigned char *pix[1]; - pix[0] = (char *) pixels; + pix[0] = (unsigned char *) pixels; repCalibrated = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:pix pixelsWide:pixelWidth pixelsHigh:pixelHeight diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 596f02ff..b753a7d4 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -6,6 +6,7 @@ endif() _add_exec(tester drawtests.c + images.c main.c menus.c page1.c diff --git a/test/images.c b/test/images.c new file mode 100644 index 00000000..df21b6eb --- /dev/null +++ b/test/images.c @@ -0,0 +1,202 @@ +// auto-generated by images/gen.go +#include "test.h" + +static const uint32_t dat0[] = { + 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, 0xFF8AC3FF, 0xFF8AC3FF, 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF89C57C, + 0xFF89C57C, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF89C57C, + 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, +}; + +static const uint32_t dat1[] = { + 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, + 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, + 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, + 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, + 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, + 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, + 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF89C57C, + 0xFF89C57C, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF89C57C, + 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, + 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, +}; + +static const uint32_t dat2[] = { + 0xAC676767, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0x562B2B2B, 0x00000000, 0x00000000, + 0xFF818181, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, + 0xFF818181, 0xFFFFFFFF, 0xFFB7B7B7, 0xFFB7B7B7, 0xFFB7B7B8, 0xFF9E9E9F, 0xFFB8B8B8, 0xFFB8B8B8, 0xFF9F9F9F, 0xFFB8B8B9, 0xFFB8B8B9, 0xFF9F9F9F, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, + 0xFF818181, 0xFFFFFFFF, 0xFF9E9E9E, 0xFF9F9F9E, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, + 0xFF818181, 0xFFFFFFFF, 0xFFB7B8B7, 0xFFC3C3C3, 0xFFC3C3C3, 0xFF9F9F9F, 0xFFEEEEEE, 0xFFEEEEEE, 0xFFC2C2C2, 0xFFEEEFEE, 0xFFEFEEEF, 0xFFC2C2C2, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, + 0xFF818181, 0xFFFFFFFF, 0xFFB1B1B1, 0xFFB8B8B8, 0xFFB9B8B9, 0xFF9F9F9F, 0xFFE0E0E0, 0xFFE0E1E0, 0xFFC2C2C2, 0xFFE1E0E0, 0xFFE1E1E1, 0xFFC2C2C2, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, + 0xFF818181, 0xFFFFFFFF, 0xFFB9B8B8, 0xFFC4C4C4, 0xFFC3C4C4, 0xFF9F9F9F, 0xFFEEEEEF, 0xFFEFEEEF, 0xFFC2C2C2, 0xFFEFEFEF, 0xFFF0EFEF, 0xFFC2C2C2, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, + 0xFF818181, 0xFFFFFFFF, 0xFFB1B1B1, 0xFFB9B9B8, 0xFFB9B9B9, 0xFF9F9F9F, 0xFFC9A6A5, 0xFFAE3A36, 0xFFA50F0C, 0xFFA40201, 0xFFA40201, 0xFFA40D0A, 0xFFB03B37, 0xFF8D6969, 0x00000000, 0x00000000, + 0xFF818181, 0xFFFFFFFF, 0xFFB9B9B9, 0xFFC4C4C4, 0xFFC4C4C4, 0xFF9E403D, 0xFFA70A07, 0xFFC66453, 0xFFCB715C, 0xFFC9735D, 0xFFC5715B, 0xFFBF6C59, 0xFFC06E61, 0xFFAC201D, 0xC17E2927, 0x19191919, + 0xFF818181, 0xFFFFFFFF, 0xFFB2B2B2, 0xFFB9B9B9, 0xFFA65C5B, 0xFFAF2017, 0xFFCB725D, 0xFFC7654D, 0xFFBB5338, 0xFFB65136, 0xFFB04E35, 0xFFB35D47, 0xFFAE5B45, 0xFFA95743, 0xFFAA2F28, 0xA1641716, + 0xFF818181, 0xFFFFFFFF, 0xFFB9B9B9, 0xFFC4C4C4, 0xFFA51412, 0xFFCA6F5A, 0xFFBF5439, 0xFFBA5237, 0xFFB44F36, 0xFFAF4D34, 0xFF886556, 0xFF765F5A, 0xFF715B56, 0xFF6B5853, 0xFF77605B, 0xF7980C0B, + 0xFF818181, 0xFFFFFFFF, 0xFFB2B2B2, 0xFFB9B9B9, 0xFFA40201, 0xFFC76951, 0xFFB85137, 0xFFB24E36, 0xFFAD4C34, 0xFFAE653A, 0xFF95995D, 0xFF1D84A0, 0xFF177F99, 0xFF3D91A5, 0xFF3B8EA0, 0xFFA20101, + 0xFF818181, 0xFFFFFFFF, 0xFFB9B9B9, 0xFFC4C4C4, 0xFF9F1212, 0xFFCA7E6D, 0xFFB85F48, 0xFFAB4C33, 0xFFA64932, 0xFFB18B44, 0xFFB8BC50, 0xFF358086, 0xFF509CAD, 0xFF278397, 0xFF496E77, 0xF7920808, + 0xFF818181, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFBC7878, 0xFF9C1D1A, 0xFFBD7E6D, 0xFFBE7F70, 0xFFBD8374, 0xFFCDC787, 0xFFCBD089, 0xFFB4B48A, 0xFF5C93A0, 0xFF44656D, 0xFF7B1618, 0xA1661D1D, + 0xB4696969, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF934040, 0xFF9E0605, 0xFF99463D, 0xFF9E624D, 0xFFA6A14C, 0xFF9F9B4A, 0xFF948845, 0xFF4B3C43, 0xFF9A0505, 0xBA771F1F, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x55351616, 0xD37F1717, 0xF99C0B0B, 0xFFA30201, 0xFFA20101, 0xF99C0B0B, 0xD3881E1E, 0x55412222, 0x00000000, 0x00000000, +}; + +static const uint32_t dat3[] = { + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x52303030, 0xF2919191, 0xFF999999, 0xFF9C9C9C, 0xFF9E9E9E, 0xFF9C9C9C, 0xFF999999, 0xFF969696, 0xFF939393, 0xFF8F8F8F, 0xFF8C8C8C, 0xFF888888, + 0xFF848484, 0xFF818181, 0xFF7D7D7D, 0xFF7A7A7A, 0xFF767676, 0xFF727272, 0xFF6F6F6F, 0xFF6B6B6B, 0xFF686868, 0xFF646464, 0xF25F5F5F, 0x521E1E1E, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xEF8D8D8D, 0xFFFDFDFD, 0xFFFDFDFD, 0xFFFEFEFE, 0xFFFEFEFE, 0xFFFEFEFE, 0xFFFDFDFD, 0xFFFDFDFD, 0xFFFDFDFD, 0xFFFDFDFD, 0xFFFCFCFC, 0xFFFCFCFC, + 0xFFFCFCFC, 0xFFFBFBFB, 0xFFFBFBFB, 0xFFFBFBFB, 0xFFFAFAFA, 0xFFFAFAFA, 0xFFFAFAFA, 0xFFF9F9F9, 0xFFF9F9F9, 0xFFF9F9F9, 0xFFF8F8F8, 0xEF5D5D5D, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF939393, 0xFFFDFDFD, 0xFFDCDCDC, 0xFFDDDDDD, 0xFFDEDEDE, 0xFFDEDEDE, 0xFFDFDFDF, 0xFFDFDFDF, 0xFFE0E0E0, 0xFFE1E1E1, 0xFFE1E1E1, 0xFFE1E1E1, + 0xFFE2E2E2, 0xFFE2E2E2, 0xFFE2E2E2, 0xFFE2E2E2, 0xFFE3E3E3, 0xFFE3E3E3, 0xFFE3E3E3, 0xFFE3E3E3, 0xFFE3E3E3, 0xFFE2E2E2, 0xFFF8F8F8, 0xFF5D5D5D, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF929292, 0xFFFEFEFE, 0xFFDDDDDD, 0xFF787878, 0xFF8D8D8D, 0xFF8E8E8E, 0xFF8F8F8F, 0xFF8F8F8F, 0xFF787878, 0xFF8F8F8F, 0xFF909090, 0xFF909090, + 0xFF7A7A7A, 0xFF919191, 0xFF919191, 0xFF919191, 0xFF7B7B7B, 0xFF919191, 0xFF919191, 0xFF919191, 0xFF7D7D7D, 0xFFE3E3E3, 0xFFF8F8F8, 0xFF5D5D5D, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF909090, 0xFFFEFEFE, 0xFFDEDEDE, 0xFF949494, 0xFFB0B0B0, 0xFFB1B1B1, 0xFFB1B1B1, 0xFFB1B1B1, 0xFF969696, 0xFFB2B2B2, 0xFFB3B3B3, 0xFFB3B3B3, + 0xFF989898, 0xFFB4B4B4, 0xFFB4B4B4, 0xFFB5B5B5, 0xFF999999, 0xFFB5B5B5, 0xFFB5B5B5, 0xFFB5B5B5, 0xFF999999, 0xFFE4E4E4, 0xFFF8F8F8, 0xFF5C5C5C, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF8E8E8E, 0xFFFDFDFD, 0xFFDFDFDF, 0xFF949494, 0xFFB1B1B1, 0xFFB1B1B1, 0xFFB2B2B2, 0xFFB2B2B2, 0xFF979797, 0xFFB3B3B3, 0xFFB4B4B4, 0xFFB4B4B4, + 0xFF999999, 0xFFB5B5B5, 0xFFB5B5B5, 0xFFB5B5B5, 0xFF999999, 0xFFB5B5B5, 0xFFB5B5B5, 0xFFB5B5B5, 0xFF999999, 0xFFE5E5E5, 0xFFF8F8F8, 0xFF5C5C5C, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF8B8B8B, 0xFFFDFDFD, 0xFFDFDFDF, 0xFF7A7A7A, 0xFF919191, 0xFF929292, 0xFF929292, 0xFF939393, 0xFF7D7D7D, 0xFF949494, 0xFF949494, 0xFF949494, + 0xFF7D7D7D, 0xFF949494, 0xFF949494, 0xFF959595, 0xFF7E7E7E, 0xFF959595, 0xFF959595, 0xFF959595, 0xFF7E7E7E, 0xFFE6E6E6, 0xFFF8F8F8, 0xFF5B5B5B, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF898989, 0xFFFDFDFD, 0xFFE0E0E0, 0xFF949494, 0xFFB0B0B0, 0xFFB0B0B0, 0xFFB1B1B1, 0xFFB2B2B2, 0xFF979797, 0xFFE2E2E2, 0xFFE3E3E3, 0xFFE3E3E3, + 0xFFC0C0C0, 0xFFE4E4E4, 0xFFE4E4E4, 0xFFE5E5E5, 0xFFC1C1C1, 0xFFE5E5E5, 0xFFE5E5E5, 0xFFE5E5E5, 0xFFC1C1C1, 0xFFE8E8E8, 0xFFF8F8F8, 0xFF5A5A5A, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF868686, 0xFFFDFDFD, 0xFFE1E1E1, 0xFF7B7B7B, 0xFF929292, 0xFF939393, 0xFF949494, 0xFF949494, 0xFF7D7D7D, 0xFFBDBDBD, 0xFFBDBDBD, 0xFFBDBDBD, + 0xFFA0A0A0, 0xFFBEBEBE, 0xFFBEBEBE, 0xFFBFBFBF, 0xFFA1A1A1, 0xFFBFBFBF, 0xFFBFBFBF, 0xFFBFBFBF, 0xFFA1A1A1, 0xFFE9E9E9, 0xFFF8F8F8, 0xFF595959, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF838383, 0xFFFDFDFD, 0xFFE1E1E1, 0xFF949494, 0xFFB1B1B1, 0xFFB2B2B2, 0xFFB3B3B3, 0xFFB3B3B3, 0xFF979797, 0xFFE4E4E4, 0xFFE5E5E5, 0xFFE5E5E5, + 0xFFC2C2C2, 0xFFE6E6E6, 0xFFE6E6E6, 0xFFE7E7E7, 0xFFC3C3C3, 0xFFE7E7E7, 0xFFE7E7E7, 0xFFE7E7E7, 0xFFC3C3C3, 0xFFEAEAEA, 0xFFF8F8F8, 0xFF585858, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF808080, 0xFFFCFCFC, 0xFFE2E2E2, 0xFF7C7C7C, 0xFF949494, 0xFF949494, 0xFF949494, 0xFF949494, 0xFF7E7E7E, 0xFFBEBEBE, 0xFFBEBEBE, 0xFFBFBFBF, + 0xFFA2A2A2, 0xFFC0C0C0, 0xFFC0C0C0, 0xFFC1C1C1, 0xFFA3A3A3, 0xFFC1C1C1, 0xFFC1C1C1, 0xFFC1C1C1, 0xFFA3A3A3, 0xFFEBEBEB, 0xFFF8F8F8, 0xFF575757, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF7D7D7D, 0xFFFCFCFC, 0xFFE3E3E3, 0xFF969696, 0xFFB3B3B3, 0xFFB3B3B3, 0xFFB3B3B3, 0xFFB4B4B4, 0xFF999999, 0xFFE6E6E6, 0xFFE6E6E6, 0xFFE7E7E7, + 0xFFC4C4C4, 0xFFE8E8E8, 0xFFE8E8E8, 0xFFE9E9E9, 0xFFC4C4C4, 0xFFE9E9E9, 0xFFE9E9E9, 0xFFE9E9E9, 0xFFC4C4C4, 0xFFECECEC, 0xFFF8F8F8, 0xFF555555, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF7A7A7A, 0xFFFCFCFC, 0xFFE3E3E3, 0xFF7D7D7D, 0xFF949494, 0xFF949494, 0xFF959595, 0xFF969696, 0xFF7F7F7F, 0xFFBFBFBF, 0xFFC0C0C0, 0xFFC1C1C1, + 0xFFA3A3A3, 0xFFC1C1C1, 0xFFC1C1C1, 0xFFC2C2C2, 0xFFA4A4A4, 0xFFC2C2C2, 0xFFC2C2C2, 0xFFC2C2C2, 0xFFA4A4A4, 0xFFEDEDED, 0xFFF8F8F8, 0xFF545454, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF777777, 0xFFFBFBFB, 0xFFE4E4E4, 0xFF979797, 0xFFB3B3B3, 0xFFB4B4B4, 0xFFB5B5B5, 0xFFB6B6B6, 0xFF999999, 0xFFE7E7E7, 0xFFE8E8E8, 0xFFE9E9E9, + 0xFFC4C4C4, 0xFFEAEAEA, 0xFFEAEAEA, 0xFFEBEBEB, 0xFFC6C6C6, 0xFFEBEBEB, 0xFFEBEBEB, 0xFFEBEBEB, 0xFFC6C6C6, 0xFFEEEEEE, 0xFFF8F8F8, 0xFF525252, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF737373, 0xFFFBFBFB, 0xFFE4E4E4, 0xFF7D7D7D, 0xFF949494, 0xFF959595, 0xFF969696, 0xFF979797, 0xFF7F7F7F, 0xFFC1C1C1, 0xFFC1C1C1, 0xFFC2C2C2, + 0xFFA3A2A2, 0xFFAB9191, 0xFF946060, 0xFF843D3D, 0xFF782525, 0xFF802727, 0xFF7E2B2B, 0xFF843D3D, 0xFF865252, 0xFFCCB2B2, 0xFFF6F5F5, 0xFF505050, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF707070, 0xFFFBFBFB, 0xFFE5E5E5, 0xFF979797, 0xFFB4B4B4, 0xFFB5B5B5, 0xFFB6B6B6, 0xFFB6B6B6, 0xFF9A9A9A, 0xFFE9E9E9, 0xFFE6E4E4, 0xFFB18585, + 0xFF7A1B1B, 0xFFA62F2F, 0xFFD35050, 0xFFF26767, 0xFFFF7070, 0xFFFE6E6E, 0xFFFC6969, 0xFFED5B5B, 0xFFCD4343, 0xFFA22525, 0xFF7E1D1D, 0xFF582C2C, 0x06020000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF6D6D6D, 0xFFFBFBFB, 0xFFE5E5E5, 0xFF7D7D7D, 0xFF959595, 0xFF969696, 0xFF979797, 0xFF979797, 0xFF808080, 0xFFB7ABAB, 0xFF7B2828, 0xFFAC3434, + 0xFFF56A6A, 0xFFFF7070, 0xFFFF7070, 0xFFFE6F6F, 0xFFFC6A6A, 0xFFFA6565, 0xFFF86060, 0xFFF55C5C, 0xFFF35757, 0xFFF15252, 0xFFE64848, 0xFFA11F1F, 0xCB530000, 0x1D0C0000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF6A6A6A, 0xFFFAFAFA, 0xFFE6E6E6, 0xFF989898, 0xFFB5B5B5, 0xFFB6B6B6, 0xFFB6B6B6, 0xFFB7B7B7, 0xFF989292, 0xFF7E2626, 0xFFCB5050, 0xFFFF7474, + 0xFFFF7070, 0xFFFF7070, 0xFFFD6B6B, 0xFFFA6767, 0xFFF86262, 0xFFF65D5D, 0xFFF45858, 0xFFF15353, 0xFFEF4E4E, 0xFFED4949, 0xFFEB4444, 0xFFE93F3F, 0xFFB62424, 0xD7570000, 0x0F060000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF666666, 0xFFFAFAFA, 0xFFE6E6E6, 0xFF7E7E7E, 0xFF969696, 0xFF979797, 0xFF979797, 0xFF989898, 0xFF733D3D, 0xFFA63737, 0xFFFF7979, 0xFFFF7272, + 0xFFFD6D6D, 0xFFFB6868, 0xFFF96363, 0xFFF65E5E, 0xFFF45959, 0xFFF25454, 0xFFF04F4F, 0xFFEE4A4A, 0xFFEB4545, 0xFFE94141, 0xFFE73C3C, 0xFFE53737, 0xFFE23535, 0xFF911313, 0x86360000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF636363, 0xFFFAFAFA, 0xFFE6E6E6, 0xFF989898, 0xFFB5B5B5, 0xFFB6B6B6, 0xFFB7B7B7, 0xFFB8B8B8, 0xFF6E1515, 0xFFDF6D6D, 0xFFFE7575, 0xFFFB6969, + 0xFFF96464, 0xFFF75F5F, 0xFFF55A5A, 0xFFF35555, 0xFFF05050, 0xFFEE4C4C, 0xFFEC4747, 0xFFEA4242, 0xFFE73D3D, 0xFFE53838, 0xFFE33333, 0xFFE12E2E, 0xFFDF2D2D, 0xFFC22828, 0xDE590000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF606060, 0xFFF9F9F9, 0xFFE6E6E6, 0xFF7E7E7E, 0xFF979797, 0xFF979797, 0xFF989898, 0xFF989898, 0xFF670101, 0xFFED7B7B, 0xFFFA6A6A, 0xFFF86060, + 0xFFF55B5B, 0xFFF35656, 0xFFF15252, 0xFFEF4D4D, 0xFFEC4848, 0xFFCD8478, 0xFF777CAE, 0xFF777BAD, 0xFF7275A7, 0xFF6B6FA1, 0xFF67699B, 0xFF606396, 0xFF5E6091, 0xFF7E5E82, 0xFC660000, 0x02000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF5D5D5D, 0xFFF9F9F9, 0xFFE6E6E6, 0xFF999999, 0xFFB6B6B6, 0xFFB6B6B6, 0xFFB7B7B7, 0xFFB8B8B8, 0xFF670000, 0xFFD86262, 0xFFF76D6D, 0xFFF45858, + 0xFFF15353, 0xFFEF4E4E, 0xFFED4949, 0xFFEB4444, 0xFFE84341, 0xFFC5E18C, 0xFF8A939F, 0xFF5889C7, 0xFF5182C1, 0xFF4B7CBA, 0xFF4475B4, 0xFF3D6EAE, 0xFF4572AD, 0xFF6A6087, 0xFF670000, 0x0E000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF595959, 0xFFF9F9F9, 0xFFE7E7E7, 0xFF7E7E7E, 0xFF979797, 0xFF979797, 0xFF989898, 0xFF989898, 0xFF690D0D, 0xFFA32424, 0xFFEF7B7B, 0xFFF15A5A, + 0xFFEE4A4A, 0xFFEB4545, 0xFFE94040, 0xFFE73C3C, 0xFFD86B4D, 0xFFADE773, 0xFFABC35F, 0xFF5585C0, 0xFF4D7EBD, 0xFF4778B7, 0xFF4071B0, 0xFF4372AE, 0xFF6B7AA6, 0xFF544267, 0xE85C0000, 0x16000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF565656, 0xFFF8F8F8, 0xFFE7E7E7, 0xFF999999, 0xFFB6B6B6, 0xFFB6B6B6, 0xFFB7B7B7, 0xFFB8B8B8, 0xFF773939, 0xFF7A0909, 0xFFBB3232, 0xFFE87171, + 0xFFED6161, 0xFFE84242, 0xFFE53838, 0xFFE33333, 0xFFBA9F4E, 0xFF99E053, 0xFF8FDC43, 0xFF848A66, 0xFF497AB9, 0xFF4777B4, 0xFF5882B9, 0xFF797FA6, 0xFF455E90, 0xFF651723, 0xAC3E0000, 0x13000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF535353, 0xFFF8F8F8, 0xFFE6E6E6, 0xFF7E7E7E, 0xFF979797, 0xFF979797, 0xFF989898, 0xFF989898, 0xFF766B6B, 0xFF6D0E0E, 0xFF881111, 0xFFB02323, + 0xFFCA4545, 0xFFE26666, 0xFFE85D5D, 0xFFE24F4B, 0xFFA1D656, 0xFF91DC47, 0xFF8BD93C, 0xFF90C93A, 0xFF7494BB, 0xFF848EB4, 0xFF70688E, 0xFF3B5E92, 0xFF602942, 0xEF5F0000, 0x400B0000, 0x08000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF4F4F4F, 0xFFF8F8F8, 0xFFE6E6E6, 0xFFE5E5E5, 0xFFE6E6E6, 0xFFE7E7E7, 0xFFE8E8E8, 0xFFE9E9E9, 0xFFE6E6E6, 0xFFBDA9A9, 0xFF711515, 0xFF7B0C0C, + 0xFFA21D1D, 0xFFAD1B1B, 0xFFB62727, 0xFFB24F33, 0xFF93A640, 0xFF96A840, 0xFF91A73C, 0xFF869F33, 0xFF6E675B, 0xFF375C93, 0xFF544D75, 0xFF6B1823, 0xEB5C0000, 0x59130000, 0x16000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x0D000000, 0xF14C4C4C, 0xFFF7F7F7, 0xFFF6F6F6, 0xFFF6F6F6, 0xFFF6F6F6, 0xFFF7F7F7, 0xFFF7F7F7, 0xFFF7F7F7, 0xFFF7F7F7, 0xFFF0F0F0, 0xFFD1C9C9, 0xFF905353, + 0xFF6A0505, 0xFF770A0A, 0xFF8E1414, 0xFF91331C, 0xFF728723, 0xFF709024, 0xFF6E8F23, 0xFF737C1F, 0xFF703C31, 0xFF6C141B, 0xFF6A0505, 0xFB531616, 0x52080000, 0x1D000000, 0x01000000, 0x00000000, + 0x00000000, 0x00000000, 0x0D000000, 0x22000000, 0x78191919, 0xF6535353, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF555555, 0xFF4F4F4F, + 0xFF4B4343, 0xFF522828, 0xFF5A1616, 0xFF610A0A, 0xFF650404, 0xFF670000, 0xFF650404, 0xFF600909, 0xFF591515, 0xFF502626, 0xF9453C3C, 0x93151515, 0x3A000000, 0x1B000000, 0x02000000, 0x00000000, + 0x00000000, 0x00000000, 0x06000000, 0x1D000000, 0x30000000, 0x40000000, 0x47000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, + 0x4C000000, 0x54000000, 0x5E000000, 0x65000000, 0x69000000, 0x6B000000, 0x6B000000, 0x69000000, 0x63000000, 0x52000000, 0x4B000000, 0x3B000000, 0x29000000, 0x0C000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x07000000, 0x0F000000, 0x15000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, + 0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x17000000, 0x18000000, 0x17000000, 0x17000000, 0x16000000, 0x14000000, 0x0F000000, 0x08000000, 0x01000000, 0x00000000, 0x00000000, 0x00000000, +}; + +static const struct { + const char *name; + void *data; + int width; + int height; + int stride; +} files[] = { + { "andlabs_16x16test_24june2016.png", dat0, 16, 16, 64 }, + { "andlabs_32x32test_24june2016.png", dat1, 32, 32, 128 }, + { "tango-icon-theme-0.8.90_16x16_x-office-spreadsheet.png", dat2, 16, 16, 64 }, + { "tango-icon-theme-0.8.90_32x32_x-office-spreadsheet.png", dat3, 32, 32, 128 }, +}; + +void appendImageNamed(uiImage *img, const char *name) +{ + int i; + + i = 0; + for (;;) { + if (strcmp(name, files[i].name) == 0) { + uiImageAppend(img, files[i].data, files[i].width, files[i].height, files[i].stride); + return; + } + i++; + } +} + diff --git a/test/images/andlabs_16x16test_24june2016.png b/test/images/andlabs_16x16test_24june2016.png new file mode 100644 index 0000000000000000000000000000000000000000..a4c27d9ae054c75cfa4e65a872e9287914f77c37 GIT binary patch literal 272 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 UWFB|2M*si-07*qoM6N<$f{C8cCjbBd literal 0 HcmV?d00001 diff --git a/test/test.h b/test/test.h index 427b3561..224ef667 100644 --- a/test/test.h +++ b/test/test.h @@ -92,3 +92,6 @@ extern uiBox *makePage15(uiWindow *); // page16.c extern uiBox *makePage16(void); + +// images.c +extern void appendImageNamed(uiImage *img, const char *name); diff --git a/uitable.h b/uitable.h index 4c6f5c3a..274d944e 100644 --- a/uitable.h +++ b/uitable.h @@ -3,6 +3,7 @@ typedef struct uiImage uiImage; +// TODO use const void * for const correctness _UI_EXTERN uiImage *uiNewImage(double width, double height); _UI_EXTERN void uiFreeImage(uiImage *i); _UI_EXTERN void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int pixelStride); From 49ab4a886fd58ee424986d50422f7b9500ce1e56 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 25 Jun 2016 19:18:25 -0400 Subject: [PATCH 0416/1329] Added image table cell parts; implemented on OS X --- darwin/image.m | 5 + darwin/table.m | 42 +++++++- darwin/uipriv_darwin.h | 5 +- test/images.c | 238 ++++++++++++++++++++--------------------- test/images/gen.go | 4 +- test/page16.c | 19 +++- uitable.h | 3 + 7 files changed, 191 insertions(+), 125 deletions(-) diff --git a/darwin/image.m b/darwin/image.m index 56213d24..6885e075 100644 --- a/darwin/image.m +++ b/darwin/image.m @@ -46,3 +46,8 @@ void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, in [repsRGB setSize:i->size]; [repsRGB release]; } + +NSImage *imageImage(uiImage *i) +{ + return i->i; +} diff --git a/darwin/table.m b/darwin/table.m index 7bbbddb9..baa17999 100644 --- a/darwin/table.m +++ b/darwin/table.m @@ -14,12 +14,14 @@ enum { partText, + partImage, }; @interface tablePart : NSObject @property int type; @property int textColumn; @property int textColorColumn; +@property int imageColumn; @property int expand; - (NSView *)mkView:(uiTableModel *)m row:(int)row; @end @@ -117,12 +119,20 @@ struct uiTable { @"uiTableColumn last part horizontal constraint")]; // and vertically - for (view in views) + for (view in views) { [v addConstraint:mkConstraint(view, NSLayoutAttributeCenterY, NSLayoutRelationEqual, v, NSLayoutAttributeCenterY, 1, 0, @"uiTableColumn part vertical constraint")]; + // TODO avoid the need for this hack + if ([view isKindOfClass:[NSImageView class]]) + [v addConstraint:mkConstraint(view, NSLayoutAttributeTop, + NSLayoutRelationEqual, + v, NSLayoutAttributeTop, + 1, 0, + @"uiTableColumn part vertical top constraint")]; + } done: [views release]; @@ -167,10 +177,11 @@ done: NSString *str; NSView *view; NSTextField *tf; + NSImageView *iv; - data = (*(m->mh->CellValue))(m->mh, m, row, self.textColumn); switch (self.type) { case partText: + data = (*(m->mh->CellValue))(m->mh, m, row, self.textColumn); str = toNSString((char *) data); uiFree(data); tf = newLabel(str); @@ -185,6 +196,22 @@ done: } view = tf; break; + case partImage: + data = (*(m->mh->CellValue))(m->mh, m, row, self.imageColumn); + iv = [[NSImageView alloc] initWithFrame:NSZeroRect]; + [iv setImage:imageImage((uiImage *) data)]; + [iv setImageFrameStyle:NSImageFrameNone]; + [iv setImageAlignment:NSImageAlignCenter]; + [iv setImageScaling:NSImageScaleProportionallyDown]; + [iv setAnimates:NO]; + [iv setEditable:NO]; + [iv addConstraint:mkConstraint(iv, NSLayoutAttributeWidth, + NSLayoutRelationEqual, + iv, NSLayoutAttributeHeight, + 1, 0, + @"uiTable image squareness constraint")]; + view = iv; + break; } // if stretchy, don't hug, otherwise hug forcibly @@ -287,6 +314,17 @@ void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand) [c->parts addObject:part]; } +void uiTableColumnAppendImagePart(uiTableColumn *c, int modelColumn, int expand) +{ + tablePart *part; + + part = [tablePart new]; + part.type = partImage; + part.imageColumn = modelColumn; + part.expand = expand; + [c->parts addObject:part]; +} + void uiTableColumnPartSetTextColor(uiTableColumn *c, int part, int modelColumn) { tablePart *p; diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 79f9a07a..61f5296f 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -121,5 +121,8 @@ extern NSScrollView *mkScrollView(struct scrollViewCreateParams *p, struct scrol extern void scrollViewSetScrolling(NSScrollView *sv, struct scrollViewData *d, BOOL hscroll, BOOL vscroll); extern void scrollViewFreeData(NSScrollView *sv, struct scrollViewData *d); -// label.cpp +// label.m extern NSTextField *newLabel(NSString *str); + +// image.m +extern NSImage *imageImage(uiImage *); diff --git a/test/images.c b/test/images.c index df21b6eb..39ab72f4 100644 --- a/test/images.c +++ b/test/images.c @@ -2,108 +2,108 @@ #include "test.h" static const uint32_t dat0[] = { - 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, 0xFF8AC3FF, 0xFF8AC3FF, 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF89C57C, - 0xFF89C57C, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF89C57C, - 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, + 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFC60E5, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFFFC60E5, 0xFF43FBFF, 0xFFFC60E5, 0xFF43FBFF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFC60E5, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFFFFC38A, 0xFFFFC38A, 0xFFFFC38A, 0xFFFFC38A, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF7CC589, + 0xFF7CC589, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF7CC589, + 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, }; static const uint32_t dat1[] = { - 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, - 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, - 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, - 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, - 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, - 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, - 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF89C57C, - 0xFF89C57C, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF89C57C, - 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, - 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, + 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, + 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, + 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, + 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, + 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, + 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFC60E5, + 0xFFFC60E5, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFC60E5, 0xFF43FBFF, + 0xFF43FBFF, 0xFFFC60E5, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFC60E5, 0xFF43FBFF, 0xFF43FBFF, + 0xFF43FBFF, 0xFF43FBFF, 0xFFFC60E5, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFFFC60E5, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, + 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFC60E5, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF43FBFF, 0xFFFC60E5, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, + 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFC60E5, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFC60E5, 0xFF43FBFF, 0xFF43FBFF, + 0xFF43FBFF, 0xFF43FBFF, 0xFFFC60E5, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFC60E5, 0xFF43FBFF, + 0xFF43FBFF, 0xFFFC60E5, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFC60E5, + 0xFFFC60E5, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, + 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, + 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFFFFC38A, + 0xFFFFC38A, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, + 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, + 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, + 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, + 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, + 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, + 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, + 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, + 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, + 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, + 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, + 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, + 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, + 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF7CC589, + 0xFF7CC589, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, + 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF7CC589, + 0xFF7CC589, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, + 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF7CC589, + 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, + 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, }; static const uint32_t dat2[] = { 0xAC676767, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0x562B2B2B, 0x00000000, 0x00000000, 0xFF818181, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, - 0xFF818181, 0xFFFFFFFF, 0xFFB7B7B7, 0xFFB7B7B7, 0xFFB7B7B8, 0xFF9E9E9F, 0xFFB8B8B8, 0xFFB8B8B8, 0xFF9F9F9F, 0xFFB8B8B9, 0xFFB8B8B9, 0xFF9F9F9F, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, - 0xFF818181, 0xFFFFFFFF, 0xFF9E9E9E, 0xFF9F9F9E, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, + 0xFF818181, 0xFFFFFFFF, 0xFFB7B7B7, 0xFFB7B7B7, 0xFFB8B7B7, 0xFF9F9E9E, 0xFFB8B8B8, 0xFFB8B8B8, 0xFF9F9F9F, 0xFFB9B8B8, 0xFFB9B8B8, 0xFF9F9F9F, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, + 0xFF818181, 0xFFFFFFFF, 0xFF9E9E9E, 0xFF9E9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, 0xFF818181, 0xFFFFFFFF, 0xFFB7B8B7, 0xFFC3C3C3, 0xFFC3C3C3, 0xFF9F9F9F, 0xFFEEEEEE, 0xFFEEEEEE, 0xFFC2C2C2, 0xFFEEEFEE, 0xFFEFEEEF, 0xFFC2C2C2, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, - 0xFF818181, 0xFFFFFFFF, 0xFFB1B1B1, 0xFFB8B8B8, 0xFFB9B8B9, 0xFF9F9F9F, 0xFFE0E0E0, 0xFFE0E1E0, 0xFFC2C2C2, 0xFFE1E0E0, 0xFFE1E1E1, 0xFFC2C2C2, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, - 0xFF818181, 0xFFFFFFFF, 0xFFB9B8B8, 0xFFC4C4C4, 0xFFC3C4C4, 0xFF9F9F9F, 0xFFEEEEEF, 0xFFEFEEEF, 0xFFC2C2C2, 0xFFEFEFEF, 0xFFF0EFEF, 0xFFC2C2C2, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, - 0xFF818181, 0xFFFFFFFF, 0xFFB1B1B1, 0xFFB9B9B8, 0xFFB9B9B9, 0xFF9F9F9F, 0xFFC9A6A5, 0xFFAE3A36, 0xFFA50F0C, 0xFFA40201, 0xFFA40201, 0xFFA40D0A, 0xFFB03B37, 0xFF8D6969, 0x00000000, 0x00000000, - 0xFF818181, 0xFFFFFFFF, 0xFFB9B9B9, 0xFFC4C4C4, 0xFFC4C4C4, 0xFF9E403D, 0xFFA70A07, 0xFFC66453, 0xFFCB715C, 0xFFC9735D, 0xFFC5715B, 0xFFBF6C59, 0xFFC06E61, 0xFFAC201D, 0xC17E2927, 0x19191919, - 0xFF818181, 0xFFFFFFFF, 0xFFB2B2B2, 0xFFB9B9B9, 0xFFA65C5B, 0xFFAF2017, 0xFFCB725D, 0xFFC7654D, 0xFFBB5338, 0xFFB65136, 0xFFB04E35, 0xFFB35D47, 0xFFAE5B45, 0xFFA95743, 0xFFAA2F28, 0xA1641716, - 0xFF818181, 0xFFFFFFFF, 0xFFB9B9B9, 0xFFC4C4C4, 0xFFA51412, 0xFFCA6F5A, 0xFFBF5439, 0xFFBA5237, 0xFFB44F36, 0xFFAF4D34, 0xFF886556, 0xFF765F5A, 0xFF715B56, 0xFF6B5853, 0xFF77605B, 0xF7980C0B, - 0xFF818181, 0xFFFFFFFF, 0xFFB2B2B2, 0xFFB9B9B9, 0xFFA40201, 0xFFC76951, 0xFFB85137, 0xFFB24E36, 0xFFAD4C34, 0xFFAE653A, 0xFF95995D, 0xFF1D84A0, 0xFF177F99, 0xFF3D91A5, 0xFF3B8EA0, 0xFFA20101, - 0xFF818181, 0xFFFFFFFF, 0xFFB9B9B9, 0xFFC4C4C4, 0xFF9F1212, 0xFFCA7E6D, 0xFFB85F48, 0xFFAB4C33, 0xFFA64932, 0xFFB18B44, 0xFFB8BC50, 0xFF358086, 0xFF509CAD, 0xFF278397, 0xFF496E77, 0xF7920808, - 0xFF818181, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFBC7878, 0xFF9C1D1A, 0xFFBD7E6D, 0xFFBE7F70, 0xFFBD8374, 0xFFCDC787, 0xFFCBD089, 0xFFB4B48A, 0xFF5C93A0, 0xFF44656D, 0xFF7B1618, 0xA1661D1D, - 0xB4696969, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF934040, 0xFF9E0605, 0xFF99463D, 0xFF9E624D, 0xFFA6A14C, 0xFF9F9B4A, 0xFF948845, 0xFF4B3C43, 0xFF9A0505, 0xBA771F1F, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x55351616, 0xD37F1717, 0xF99C0B0B, 0xFFA30201, 0xFFA20101, 0xF99C0B0B, 0xD3881E1E, 0x55412222, 0x00000000, 0x00000000, + 0xFF818181, 0xFFFFFFFF, 0xFFB1B1B1, 0xFFB8B8B8, 0xFFB9B8B9, 0xFF9F9F9F, 0xFFE0E0E0, 0xFFE0E1E0, 0xFFC2C2C2, 0xFFE0E0E1, 0xFFE1E1E1, 0xFFC2C2C2, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, + 0xFF818181, 0xFFFFFFFF, 0xFFB8B8B9, 0xFFC4C4C4, 0xFFC4C4C3, 0xFF9F9F9F, 0xFFEFEEEE, 0xFFEFEEEF, 0xFFC2C2C2, 0xFFEFEFEF, 0xFFEFEFF0, 0xFFC2C2C2, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, + 0xFF818181, 0xFFFFFFFF, 0xFFB1B1B1, 0xFFB8B9B9, 0xFFB9B9B9, 0xFF9F9F9F, 0xFFA5A6C9, 0xFF363AAE, 0xFF0C0FA5, 0xFF0102A4, 0xFF0102A4, 0xFF0A0DA4, 0xFF373BB0, 0xFF69698D, 0x00000000, 0x00000000, + 0xFF818181, 0xFFFFFFFF, 0xFFB9B9B9, 0xFFC4C4C4, 0xFFC4C4C4, 0xFF3D409E, 0xFF070AA7, 0xFF5364C6, 0xFF5C71CB, 0xFF5D73C9, 0xFF5B71C5, 0xFF596CBF, 0xFF616EC0, 0xFF1D20AC, 0xC127297E, 0x19191919, + 0xFF818181, 0xFFFFFFFF, 0xFFB2B2B2, 0xFFB9B9B9, 0xFF5B5CA6, 0xFF1720AF, 0xFF5D72CB, 0xFF4D65C7, 0xFF3853BB, 0xFF3651B6, 0xFF354EB0, 0xFF475DB3, 0xFF455BAE, 0xFF4357A9, 0xFF282FAA, 0xA1161764, + 0xFF818181, 0xFFFFFFFF, 0xFFB9B9B9, 0xFFC4C4C4, 0xFF1214A5, 0xFF5A6FCA, 0xFF3954BF, 0xFF3752BA, 0xFF364FB4, 0xFF344DAF, 0xFF566588, 0xFF5A5F76, 0xFF565B71, 0xFF53586B, 0xFF5B6077, 0xF70B0C98, + 0xFF818181, 0xFFFFFFFF, 0xFFB2B2B2, 0xFFB9B9B9, 0xFF0102A4, 0xFF5169C7, 0xFF3751B8, 0xFF364EB2, 0xFF344CAD, 0xFF3A65AE, 0xFF5D9995, 0xFFA0841D, 0xFF997F17, 0xFFA5913D, 0xFFA08E3B, 0xFF0101A2, + 0xFF818181, 0xFFFFFFFF, 0xFFB9B9B9, 0xFFC4C4C4, 0xFF12129F, 0xFF6D7ECA, 0xFF485FB8, 0xFF334CAB, 0xFF3249A6, 0xFF448BB1, 0xFF50BCB8, 0xFF868035, 0xFFAD9C50, 0xFF978327, 0xFF776E49, 0xF7080892, + 0xFF818181, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF7878BC, 0xFF1A1D9C, 0xFF6D7EBD, 0xFF707FBE, 0xFF7483BD, 0xFF87C7CD, 0xFF89D0CB, 0xFF8AB4B4, 0xFFA0935C, 0xFF6D6544, 0xFF18167B, 0xA11D1D66, + 0xB4696969, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF404093, 0xFF05069E, 0xFF3D4699, 0xFF4D629E, 0xFF4CA1A6, 0xFF4A9B9F, 0xFF458894, 0xFF433C4B, 0xFF05059A, 0xBA1F1F77, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x55161635, 0xD317177F, 0xF90B0B9C, 0xFF0102A3, 0xFF0101A2, 0xF90B0B9C, 0xD31E1E88, 0x55222241, 0x00000000, 0x00000000, }; static const uint32_t dat3[] = { @@ -140,33 +140,33 @@ static const uint32_t dat3[] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF777777, 0xFFFBFBFB, 0xFFE4E4E4, 0xFF979797, 0xFFB3B3B3, 0xFFB4B4B4, 0xFFB5B5B5, 0xFFB6B6B6, 0xFF999999, 0xFFE7E7E7, 0xFFE8E8E8, 0xFFE9E9E9, 0xFFC4C4C4, 0xFFEAEAEA, 0xFFEAEAEA, 0xFFEBEBEB, 0xFFC6C6C6, 0xFFEBEBEB, 0xFFEBEBEB, 0xFFEBEBEB, 0xFFC6C6C6, 0xFFEEEEEE, 0xFFF8F8F8, 0xFF525252, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF737373, 0xFFFBFBFB, 0xFFE4E4E4, 0xFF7D7D7D, 0xFF949494, 0xFF959595, 0xFF969696, 0xFF979797, 0xFF7F7F7F, 0xFFC1C1C1, 0xFFC1C1C1, 0xFFC2C2C2, - 0xFFA3A2A2, 0xFFAB9191, 0xFF946060, 0xFF843D3D, 0xFF782525, 0xFF802727, 0xFF7E2B2B, 0xFF843D3D, 0xFF865252, 0xFFCCB2B2, 0xFFF6F5F5, 0xFF505050, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF707070, 0xFFFBFBFB, 0xFFE5E5E5, 0xFF979797, 0xFFB4B4B4, 0xFFB5B5B5, 0xFFB6B6B6, 0xFFB6B6B6, 0xFF9A9A9A, 0xFFE9E9E9, 0xFFE6E4E4, 0xFFB18585, - 0xFF7A1B1B, 0xFFA62F2F, 0xFFD35050, 0xFFF26767, 0xFFFF7070, 0xFFFE6E6E, 0xFFFC6969, 0xFFED5B5B, 0xFFCD4343, 0xFFA22525, 0xFF7E1D1D, 0xFF582C2C, 0x06020000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF6D6D6D, 0xFFFBFBFB, 0xFFE5E5E5, 0xFF7D7D7D, 0xFF959595, 0xFF969696, 0xFF979797, 0xFF979797, 0xFF808080, 0xFFB7ABAB, 0xFF7B2828, 0xFFAC3434, - 0xFFF56A6A, 0xFFFF7070, 0xFFFF7070, 0xFFFE6F6F, 0xFFFC6A6A, 0xFFFA6565, 0xFFF86060, 0xFFF55C5C, 0xFFF35757, 0xFFF15252, 0xFFE64848, 0xFFA11F1F, 0xCB530000, 0x1D0C0000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF6A6A6A, 0xFFFAFAFA, 0xFFE6E6E6, 0xFF989898, 0xFFB5B5B5, 0xFFB6B6B6, 0xFFB6B6B6, 0xFFB7B7B7, 0xFF989292, 0xFF7E2626, 0xFFCB5050, 0xFFFF7474, - 0xFFFF7070, 0xFFFF7070, 0xFFFD6B6B, 0xFFFA6767, 0xFFF86262, 0xFFF65D5D, 0xFFF45858, 0xFFF15353, 0xFFEF4E4E, 0xFFED4949, 0xFFEB4444, 0xFFE93F3F, 0xFFB62424, 0xD7570000, 0x0F060000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF666666, 0xFFFAFAFA, 0xFFE6E6E6, 0xFF7E7E7E, 0xFF969696, 0xFF979797, 0xFF979797, 0xFF989898, 0xFF733D3D, 0xFFA63737, 0xFFFF7979, 0xFFFF7272, - 0xFFFD6D6D, 0xFFFB6868, 0xFFF96363, 0xFFF65E5E, 0xFFF45959, 0xFFF25454, 0xFFF04F4F, 0xFFEE4A4A, 0xFFEB4545, 0xFFE94141, 0xFFE73C3C, 0xFFE53737, 0xFFE23535, 0xFF911313, 0x86360000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF636363, 0xFFFAFAFA, 0xFFE6E6E6, 0xFF989898, 0xFFB5B5B5, 0xFFB6B6B6, 0xFFB7B7B7, 0xFFB8B8B8, 0xFF6E1515, 0xFFDF6D6D, 0xFFFE7575, 0xFFFB6969, - 0xFFF96464, 0xFFF75F5F, 0xFFF55A5A, 0xFFF35555, 0xFFF05050, 0xFFEE4C4C, 0xFFEC4747, 0xFFEA4242, 0xFFE73D3D, 0xFFE53838, 0xFFE33333, 0xFFE12E2E, 0xFFDF2D2D, 0xFFC22828, 0xDE590000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF606060, 0xFFF9F9F9, 0xFFE6E6E6, 0xFF7E7E7E, 0xFF979797, 0xFF979797, 0xFF989898, 0xFF989898, 0xFF670101, 0xFFED7B7B, 0xFFFA6A6A, 0xFFF86060, - 0xFFF55B5B, 0xFFF35656, 0xFFF15252, 0xFFEF4D4D, 0xFFEC4848, 0xFFCD8478, 0xFF777CAE, 0xFF777BAD, 0xFF7275A7, 0xFF6B6FA1, 0xFF67699B, 0xFF606396, 0xFF5E6091, 0xFF7E5E82, 0xFC660000, 0x02000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF5D5D5D, 0xFFF9F9F9, 0xFFE6E6E6, 0xFF999999, 0xFFB6B6B6, 0xFFB6B6B6, 0xFFB7B7B7, 0xFFB8B8B8, 0xFF670000, 0xFFD86262, 0xFFF76D6D, 0xFFF45858, - 0xFFF15353, 0xFFEF4E4E, 0xFFED4949, 0xFFEB4444, 0xFFE84341, 0xFFC5E18C, 0xFF8A939F, 0xFF5889C7, 0xFF5182C1, 0xFF4B7CBA, 0xFF4475B4, 0xFF3D6EAE, 0xFF4572AD, 0xFF6A6087, 0xFF670000, 0x0E000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF595959, 0xFFF9F9F9, 0xFFE7E7E7, 0xFF7E7E7E, 0xFF979797, 0xFF979797, 0xFF989898, 0xFF989898, 0xFF690D0D, 0xFFA32424, 0xFFEF7B7B, 0xFFF15A5A, - 0xFFEE4A4A, 0xFFEB4545, 0xFFE94040, 0xFFE73C3C, 0xFFD86B4D, 0xFFADE773, 0xFFABC35F, 0xFF5585C0, 0xFF4D7EBD, 0xFF4778B7, 0xFF4071B0, 0xFF4372AE, 0xFF6B7AA6, 0xFF544267, 0xE85C0000, 0x16000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF565656, 0xFFF8F8F8, 0xFFE7E7E7, 0xFF999999, 0xFFB6B6B6, 0xFFB6B6B6, 0xFFB7B7B7, 0xFFB8B8B8, 0xFF773939, 0xFF7A0909, 0xFFBB3232, 0xFFE87171, - 0xFFED6161, 0xFFE84242, 0xFFE53838, 0xFFE33333, 0xFFBA9F4E, 0xFF99E053, 0xFF8FDC43, 0xFF848A66, 0xFF497AB9, 0xFF4777B4, 0xFF5882B9, 0xFF797FA6, 0xFF455E90, 0xFF651723, 0xAC3E0000, 0x13000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF535353, 0xFFF8F8F8, 0xFFE6E6E6, 0xFF7E7E7E, 0xFF979797, 0xFF979797, 0xFF989898, 0xFF989898, 0xFF766B6B, 0xFF6D0E0E, 0xFF881111, 0xFFB02323, - 0xFFCA4545, 0xFFE26666, 0xFFE85D5D, 0xFFE24F4B, 0xFFA1D656, 0xFF91DC47, 0xFF8BD93C, 0xFF90C93A, 0xFF7494BB, 0xFF848EB4, 0xFF70688E, 0xFF3B5E92, 0xFF602942, 0xEF5F0000, 0x400B0000, 0x08000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF4F4F4F, 0xFFF8F8F8, 0xFFE6E6E6, 0xFFE5E5E5, 0xFFE6E6E6, 0xFFE7E7E7, 0xFFE8E8E8, 0xFFE9E9E9, 0xFFE6E6E6, 0xFFBDA9A9, 0xFF711515, 0xFF7B0C0C, - 0xFFA21D1D, 0xFFAD1B1B, 0xFFB62727, 0xFFB24F33, 0xFF93A640, 0xFF96A840, 0xFF91A73C, 0xFF869F33, 0xFF6E675B, 0xFF375C93, 0xFF544D75, 0xFF6B1823, 0xEB5C0000, 0x59130000, 0x16000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x0D000000, 0xF14C4C4C, 0xFFF7F7F7, 0xFFF6F6F6, 0xFFF6F6F6, 0xFFF6F6F6, 0xFFF7F7F7, 0xFFF7F7F7, 0xFFF7F7F7, 0xFFF7F7F7, 0xFFF0F0F0, 0xFFD1C9C9, 0xFF905353, - 0xFF6A0505, 0xFF770A0A, 0xFF8E1414, 0xFF91331C, 0xFF728723, 0xFF709024, 0xFF6E8F23, 0xFF737C1F, 0xFF703C31, 0xFF6C141B, 0xFF6A0505, 0xFB531616, 0x52080000, 0x1D000000, 0x01000000, 0x00000000, + 0xFFA2A2A3, 0xFF9191AB, 0xFF606094, 0xFF3D3D84, 0xFF252578, 0xFF272780, 0xFF2B2B7E, 0xFF3D3D84, 0xFF525286, 0xFFB2B2CC, 0xFFF5F5F6, 0xFF505050, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF707070, 0xFFFBFBFB, 0xFFE5E5E5, 0xFF979797, 0xFFB4B4B4, 0xFFB5B5B5, 0xFFB6B6B6, 0xFFB6B6B6, 0xFF9A9A9A, 0xFFE9E9E9, 0xFFE4E4E6, 0xFF8585B1, + 0xFF1B1B7A, 0xFF2F2FA6, 0xFF5050D3, 0xFF6767F2, 0xFF7070FF, 0xFF6E6EFE, 0xFF6969FC, 0xFF5B5BED, 0xFF4343CD, 0xFF2525A2, 0xFF1D1D7E, 0xFF2C2C58, 0x06000002, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF6D6D6D, 0xFFFBFBFB, 0xFFE5E5E5, 0xFF7D7D7D, 0xFF959595, 0xFF969696, 0xFF979797, 0xFF979797, 0xFF808080, 0xFFABABB7, 0xFF28287B, 0xFF3434AC, + 0xFF6A6AF5, 0xFF7070FF, 0xFF7070FF, 0xFF6F6FFE, 0xFF6A6AFC, 0xFF6565FA, 0xFF6060F8, 0xFF5C5CF5, 0xFF5757F3, 0xFF5252F1, 0xFF4848E6, 0xFF1F1FA1, 0xCB000053, 0x1D00000C, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF6A6A6A, 0xFFFAFAFA, 0xFFE6E6E6, 0xFF989898, 0xFFB5B5B5, 0xFFB6B6B6, 0xFFB6B6B6, 0xFFB7B7B7, 0xFF929298, 0xFF26267E, 0xFF5050CB, 0xFF7474FF, + 0xFF7070FF, 0xFF7070FF, 0xFF6B6BFD, 0xFF6767FA, 0xFF6262F8, 0xFF5D5DF6, 0xFF5858F4, 0xFF5353F1, 0xFF4E4EEF, 0xFF4949ED, 0xFF4444EB, 0xFF3F3FE9, 0xFF2424B6, 0xD7000057, 0x0F000006, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF666666, 0xFFFAFAFA, 0xFFE6E6E6, 0xFF7E7E7E, 0xFF969696, 0xFF979797, 0xFF979797, 0xFF989898, 0xFF3D3D73, 0xFF3737A6, 0xFF7979FF, 0xFF7272FF, + 0xFF6D6DFD, 0xFF6868FB, 0xFF6363F9, 0xFF5E5EF6, 0xFF5959F4, 0xFF5454F2, 0xFF4F4FF0, 0xFF4A4AEE, 0xFF4545EB, 0xFF4141E9, 0xFF3C3CE7, 0xFF3737E5, 0xFF3535E2, 0xFF131391, 0x86000036, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF636363, 0xFFFAFAFA, 0xFFE6E6E6, 0xFF989898, 0xFFB5B5B5, 0xFFB6B6B6, 0xFFB7B7B7, 0xFFB8B8B8, 0xFF15156E, 0xFF6D6DDF, 0xFF7575FE, 0xFF6969FB, + 0xFF6464F9, 0xFF5F5FF7, 0xFF5A5AF5, 0xFF5555F3, 0xFF5050F0, 0xFF4C4CEE, 0xFF4747EC, 0xFF4242EA, 0xFF3D3DE7, 0xFF3838E5, 0xFF3333E3, 0xFF2E2EE1, 0xFF2D2DDF, 0xFF2828C2, 0xDE000059, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF606060, 0xFFF9F9F9, 0xFFE6E6E6, 0xFF7E7E7E, 0xFF979797, 0xFF979797, 0xFF989898, 0xFF989898, 0xFF010167, 0xFF7B7BED, 0xFF6A6AFA, 0xFF6060F8, + 0xFF5B5BF5, 0xFF5656F3, 0xFF5252F1, 0xFF4D4DEF, 0xFF4848EC, 0xFF7884CD, 0xFFAE7C77, 0xFFAD7B77, 0xFFA77572, 0xFFA16F6B, 0xFF9B6967, 0xFF966360, 0xFF91605E, 0xFF825E7E, 0xFC000066, 0x02000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF5D5D5D, 0xFFF9F9F9, 0xFFE6E6E6, 0xFF999999, 0xFFB6B6B6, 0xFFB6B6B6, 0xFFB7B7B7, 0xFFB8B8B8, 0xFF000067, 0xFF6262D8, 0xFF6D6DF7, 0xFF5858F4, + 0xFF5353F1, 0xFF4E4EEF, 0xFF4949ED, 0xFF4444EB, 0xFF4143E8, 0xFF8CE1C5, 0xFF9F938A, 0xFFC78958, 0xFFC18251, 0xFFBA7C4B, 0xFFB47544, 0xFFAE6E3D, 0xFFAD7245, 0xFF87606A, 0xFF000067, 0x0E000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF595959, 0xFFF9F9F9, 0xFFE7E7E7, 0xFF7E7E7E, 0xFF979797, 0xFF979797, 0xFF989898, 0xFF989898, 0xFF0D0D69, 0xFF2424A3, 0xFF7B7BEF, 0xFF5A5AF1, + 0xFF4A4AEE, 0xFF4545EB, 0xFF4040E9, 0xFF3C3CE7, 0xFF4D6BD8, 0xFF73E7AD, 0xFF5FC3AB, 0xFFC08555, 0xFFBD7E4D, 0xFFB77847, 0xFFB07140, 0xFFAE7243, 0xFFA67A6B, 0xFF674254, 0xE800005C, 0x16000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF565656, 0xFFF8F8F8, 0xFFE7E7E7, 0xFF999999, 0xFFB6B6B6, 0xFFB6B6B6, 0xFFB7B7B7, 0xFFB8B8B8, 0xFF393977, 0xFF09097A, 0xFF3232BB, 0xFF7171E8, + 0xFF6161ED, 0xFF4242E8, 0xFF3838E5, 0xFF3333E3, 0xFF4E9FBA, 0xFF53E099, 0xFF43DC8F, 0xFF668A84, 0xFFB97A49, 0xFFB47747, 0xFFB98258, 0xFFA67F79, 0xFF905E45, 0xFF231765, 0xAC00003E, 0x13000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF535353, 0xFFF8F8F8, 0xFFE6E6E6, 0xFF7E7E7E, 0xFF979797, 0xFF979797, 0xFF989898, 0xFF989898, 0xFF6B6B76, 0xFF0E0E6D, 0xFF111188, 0xFF2323B0, + 0xFF4545CA, 0xFF6666E2, 0xFF5D5DE8, 0xFF4B4FE2, 0xFF56D6A1, 0xFF47DC91, 0xFF3CD98B, 0xFF3AC990, 0xFFBB9474, 0xFFB48E84, 0xFF8E6870, 0xFF925E3B, 0xFF422960, 0xEF00005F, 0x4000000B, 0x08000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF4F4F4F, 0xFFF8F8F8, 0xFFE6E6E6, 0xFFE5E5E5, 0xFFE6E6E6, 0xFFE7E7E7, 0xFFE8E8E8, 0xFFE9E9E9, 0xFFE6E6E6, 0xFFA9A9BD, 0xFF151571, 0xFF0C0C7B, + 0xFF1D1DA2, 0xFF1B1BAD, 0xFF2727B6, 0xFF334FB2, 0xFF40A693, 0xFF40A896, 0xFF3CA791, 0xFF339F86, 0xFF5B676E, 0xFF935C37, 0xFF754D54, 0xFF23186B, 0xEB00005C, 0x59000013, 0x16000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x0D000000, 0xF14C4C4C, 0xFFF7F7F7, 0xFFF6F6F6, 0xFFF6F6F6, 0xFFF6F6F6, 0xFFF7F7F7, 0xFFF7F7F7, 0xFFF7F7F7, 0xFFF7F7F7, 0xFFF0F0F0, 0xFFC9C9D1, 0xFF535390, + 0xFF05056A, 0xFF0A0A77, 0xFF14148E, 0xFF1C3391, 0xFF238772, 0xFF249070, 0xFF238F6E, 0xFF1F7C73, 0xFF313C70, 0xFF1B146C, 0xFF05056A, 0xFB161653, 0x52000008, 0x1D000000, 0x01000000, 0x00000000, 0x00000000, 0x00000000, 0x0D000000, 0x22000000, 0x78191919, 0xF6535353, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF555555, 0xFF4F4F4F, - 0xFF4B4343, 0xFF522828, 0xFF5A1616, 0xFF610A0A, 0xFF650404, 0xFF670000, 0xFF650404, 0xFF600909, 0xFF591515, 0xFF502626, 0xF9453C3C, 0x93151515, 0x3A000000, 0x1B000000, 0x02000000, 0x00000000, + 0xFF43434B, 0xFF282852, 0xFF16165A, 0xFF0A0A61, 0xFF040465, 0xFF000067, 0xFF040465, 0xFF090960, 0xFF151559, 0xFF262650, 0xF93C3C45, 0x93151515, 0x3A000000, 0x1B000000, 0x02000000, 0x00000000, 0x00000000, 0x00000000, 0x06000000, 0x1D000000, 0x30000000, 0x40000000, 0x47000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4C000000, 0x54000000, 0x5E000000, 0x65000000, 0x69000000, 0x6B000000, 0x6B000000, 0x69000000, 0x63000000, 0x52000000, 0x4B000000, 0x3B000000, 0x29000000, 0x0C000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x07000000, 0x0F000000, 0x15000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, diff --git a/test/images/gen.go b/test/images/gen.go index 1677b2d2..cdf8732d 100644 --- a/test/images/gen.go +++ b/test/images/gen.go @@ -58,9 +58,9 @@ func main() { } else { fmt.Printf(" ") } - d := uint32(im.data[j]) << 16 + d := uint32(im.data[j + 2]) << 16 d |= uint32(im.data[j + 1]) << 8 - d |= uint32(im.data[j + 2]) + d |= uint32(im.data[j + 0]) d |= uint32(im.data[j + 3]) << 24 fmt.Printf("0x%08X,", d) diff --git a/test/page16.c b/test/page16.c index 66ba268b..4de9983e 100644 --- a/test/page16.c +++ b/test/page16.c @@ -5,13 +5,15 @@ static uiTableModelHandler mh; static int modelNumColumns(uiTableModelHandler *mh, uiTableModel *m) { - return 5; + return 6; } static uiTableModelColumnType modelColumnType(uiTableModelHandler *mh, uiTableModel *m, int column) { if (column == 3 || column == 4) return uiTableModelColumnColor; + if (column == 5) + return uiTableModelColumnImage; return uiTableModelColumnString; } @@ -20,6 +22,8 @@ static int modelNumRows(uiTableModelHandler *mh, uiTableModel *m) return 15; } +static uiImage *img[2]; + static void *modelCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, int col) { char buf[256]; @@ -36,6 +40,11 @@ static void *modelCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, i return uiTableModelGiveColor(0.5, 0, 0.75, 1); return NULL; } + if (col == 5) { + if (row < 8) + return img[0]; + return img[1]; + } switch (col) { case 0: sprintf(buf, "Row %d", row); @@ -60,6 +69,13 @@ uiBox *makePage16(void) uiTable *t; uiTableColumn *tc; + img[0] = uiNewImage(16, 16); + appendImageNamed(img[0], "andlabs_16x16test_24june2016.png"); + appendImageNamed(img[0], "andlabs_32x32test_24june2016.png"); + img[1] = uiNewImage(16, 16); + appendImageNamed(img[1], "tango-icon-theme-0.8.90_16x16_x-office-spreadsheet.png"); + appendImageNamed(img[1], "tango-icon-theme-0.8.90_32x32_x-office-spreadsheet.png"); + page16 = newVerticalBox(); mh.NumColumns = modelNumColumns; @@ -75,6 +91,7 @@ uiBox *makePage16(void) uiTableAppendTextColumn(t, "Column 1", 0); tc = uiTableAppendColumn(t, "Column 2"); + uiTableColumnAppendImagePart(tc, 5, 0); uiTableColumnAppendTextPart(tc, 1, 0); uiTableColumnAppendTextPart(tc, 2, 1); uiTableColumnPartSetTextColor(tc, 1, 4); diff --git a/uitable.h b/uitable.h index 274d944e..508d3d8a 100644 --- a/uitable.h +++ b/uitable.h @@ -13,6 +13,7 @@ typedef struct uiTableModelHandler uiTableModelHandler; _UI_ENUM(uiTableModelColumnType) { uiTableModelColumnString, + uiTableModelColumnImage, uiTableModelColumnColor, }; @@ -38,6 +39,8 @@ _UI_EXTERN void uiTableModelRowDeleted(uiTableModel *m, int oldIndex); typedef struct uiTableColumn uiTableColumn; _UI_EXTERN void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand); +// TODO images shouldn't expand... +_UI_EXTERN void uiTableColumnAppendImagePart(uiTableColumn *c, int modelColumn, int expand); _UI_EXTERN void uiTableColumnPartSetTextColor(uiTableColumn *c, int part, int modelColumn); typedef struct uiTable uiTable; From e0230d73a6e6fe22f5718108d17c625f16c82b39 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 25 Jun 2016 23:04:49 -0400 Subject: [PATCH 0417/1329] Started editable uiTable elements. --- darwin/table.m | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ test/page16.c | 14 +++++++++++--- uitable.h | 4 +++- 3 files changed, 62 insertions(+), 4 deletions(-) diff --git a/darwin/table.m b/darwin/table.m index baa17999..e3b3686a 100644 --- a/darwin/table.m +++ b/darwin/table.m @@ -5,11 +5,15 @@ // - initial state of table view is off // - header cell seems off // - background color shows up for a line or two below selection +// - editable NSTextFields have no intrinsic width +// - changing a part property does not refresh views +// - the target/action thing is wrong; we need to pass the model column for col, not the view column @interface tableModel : NSObject { uiTableModel *libui_m; } - (id)initWithModel:(uiTableModel *)m; +- (IBAction)onAction:(id)sender; @end enum { @@ -23,6 +27,7 @@ enum { @property int textColorColumn; @property int imageColumn; @property int expand; +@property int editable; - (NSView *)mkView:(uiTableModel *)m row:(int)row; @end @@ -157,6 +162,36 @@ done: // TODO autorelease color? or release it? } +- (IBAction)onAction:(id)sender +{ + uiTableModel *m = self->libui_m; + NSView *view = (NSView *) sender; + NSTableView *tv; + NSInteger row; + const void *data; + + row = -1; + for (tv in m->tables) { + row = [tv rowForView:view]; + if (row != -1) + break; + } + if (row == -1) + implbug("table model action triggered on view with no associated table"); + + if ([view isKindOfClass:[NSTextField class]]) + data = [((NSTextField *) view) stringValue]; + else + implbug("table model editing action triggered on non-editable view"); + + (*(m->mh->SetCellValue))(m->mh, m, + row, [tv columnForView:view], + data); + // always refresh the value in case the model rejected it + // TODO only affect tv + uiTableModelRowChanged(m, row); +} + @end @implementation tablePart @@ -194,6 +229,11 @@ done: [tf setTextColor:color]; // TODO release color } + if (self.editable) { + [tf setEditable:YES]; + [tf setTarget:m->m]; + [tf setAction:@selector(onAction:)]; + } view = tf; break; case partImage: @@ -325,6 +365,14 @@ void uiTableColumnAppendImagePart(uiTableColumn *c, int modelColumn, int expand) [c->parts addObject:part]; } +void uiTableColumnPartSetEditable(uiTableColumn *c, int part, int editable) +{ + tablePart *p; + + p = (tablePart *) [c->parts objectAtIndex:part]; + p.editable = editable; +} + void uiTableColumnPartSetTextColor(uiTableColumn *c, int part, int modelColumn) { tablePart *p; diff --git a/test/page16.c b/test/page16.c index 4de9983e..890e2a3d 100644 --- a/test/page16.c +++ b/test/page16.c @@ -23,6 +23,7 @@ static int modelNumRows(uiTableModelHandler *mh, uiTableModel *m) } static uiImage *img[2]; +static char row9text[1024]; static void *modelCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, int col) { @@ -49,17 +50,21 @@ static void *modelCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, i case 0: sprintf(buf, "Row %d", row); break; - case 1: case 2: + if (row == 9) + return uiTableModelStrdup(row9text); + // fall through + case 1: strcpy(buf, "Part"); break; } return uiTableModelStrdup(buf); } -static void modelSetCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, int col, void *val) +static void modelSetCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, int col, const void *val) { - // not implemented yet + if (row == 9 && col == 2) + strcpy(row9text, (const char *) val); } uiBox *makePage16(void) @@ -76,6 +81,8 @@ uiBox *makePage16(void) appendImageNamed(img[1], "tango-icon-theme-0.8.90_16x16_x-office-spreadsheet.png"); appendImageNamed(img[1], "tango-icon-theme-0.8.90_32x32_x-office-spreadsheet.png"); + strcpy(row9text, "Part"); + page16 = newVerticalBox(); mh.NumColumns = modelNumColumns; @@ -95,6 +102,7 @@ uiBox *makePage16(void) uiTableColumnAppendTextPart(tc, 1, 0); uiTableColumnAppendTextPart(tc, 2, 1); uiTableColumnPartSetTextColor(tc, 1, 4); + uiTableColumnPartSetEditable(tc, 2, 1); uiTableSetRowBackgroundColorModelColumn(t, 3); diff --git a/uitable.h b/uitable.h index 508d3d8a..cc5b4d94 100644 --- a/uitable.h +++ b/uitable.h @@ -22,7 +22,7 @@ struct uiTableModelHandler { uiTableModelColumnType (*ColumnType)(uiTableModelHandler *, uiTableModel *, int); int (*NumRows)(uiTableModelHandler *, uiTableModel *); void *(*CellValue)(uiTableModelHandler *, uiTableModel *, int, int); - void (*SetCellValue)(uiTableModelHandler *, uiTableModel *, int, int, void *); + void (*SetCellValue)(uiTableModelHandler *, uiTableModel *, int, int, const void *); }; _UI_EXTERN void *uiTableModelStrdup(const char *str); @@ -41,6 +41,8 @@ typedef struct uiTableColumn uiTableColumn; _UI_EXTERN void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand); // TODO images shouldn't expand... _UI_EXTERN void uiTableColumnAppendImagePart(uiTableColumn *c, int modelColumn, int expand); +// TODO Editable? +_UI_EXTERN void uiTableColumnPartSetEditable(uiTableColumn *c, int part, int editable); _UI_EXTERN void uiTableColumnPartSetTextColor(uiTableColumn *c, int part, int modelColumn); typedef struct uiTable uiTable; From 2f9a38b5fe12f9707b33d8dffff73eef439c65e9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 25 Jun 2016 23:06:13 -0400 Subject: [PATCH 0418/1329] Quick bugfix. --- darwin/table.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/darwin/table.m b/darwin/table.m index e3b3686a..ba79cb5b 100644 --- a/darwin/table.m +++ b/darwin/table.m @@ -180,7 +180,7 @@ done: implbug("table model action triggered on view with no associated table"); if ([view isKindOfClass:[NSTextField class]]) - data = [((NSTextField *) view) stringValue]; + data = [[((NSTextField *) view) stringValue] UTF8String]; else implbug("table model editing action triggered on non-editable view"); From 15eca1372e7ddc537b6d9b10664e164e10991e85 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 26 Jun 2016 00:44:21 -0400 Subject: [PATCH 0419/1329] Fixed cell editing in table on OS X. --- darwin/table.m | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/darwin/table.m b/darwin/table.m index ba79cb5b..213bc644 100644 --- a/darwin/table.m +++ b/darwin/table.m @@ -7,7 +7,6 @@ // - background color shows up for a line or two below selection // - editable NSTextFields have no intrinsic width // - changing a part property does not refresh views -// - the target/action thing is wrong; we need to pass the model column for col, not the view column @interface tableModel : NSObject { uiTableModel *libui_m; @@ -22,6 +21,7 @@ enum { }; @interface tablePart : NSObject +@property int index; @property int type; @property int textColumn; @property int textColorColumn; @@ -184,8 +184,9 @@ done: else implbug("table model editing action triggered on non-editable view"); + // note the use of [view tag] — we need the model column, which we store in the view tag for relevant views below (*(m->mh->SetCellValue))(m->mh, m, - row, [tv columnForView:view], + row, [view tag], data); // always refresh the value in case the model rejected it // TODO only affect tv @@ -234,6 +235,7 @@ done: [tf setTarget:m->m]; [tf setAction:@selector(onAction:)]; } + [tf setTag:self.index]; view = tf; break; case partImage: @@ -250,6 +252,7 @@ done: iv, NSLayoutAttributeHeight, 1, 0, @"uiTable image squareness constraint")]; + [iv setTag:self.index]; view = iv; break; } @@ -348,6 +351,7 @@ void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand) tablePart *part; part = [tablePart new]; + part.index = [c->parts count]; part.type = partText; part.textColumn = modelColumn; part.expand = expand; @@ -359,6 +363,7 @@ void uiTableColumnAppendImagePart(uiTableColumn *c, int modelColumn, int expand) tablePart *part; part = [tablePart new]; + part.index = [c->parts count]; part.type = partImage; part.imageColumn = modelColumn; part.expand = expand; From 44a723b3148648fbd927fe9223e6e6ca3f3a6fe2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 26 Jun 2016 13:06:33 -0400 Subject: [PATCH 0420/1329] Added uiTable buttons. Not fully working on OS X. --- darwin/table.m | 45 +++++++++++++++++++++++++++++++++++++++------ test/page16.c | 13 ++++++++++++- uitable.h | 1 + 3 files changed, 52 insertions(+), 7 deletions(-) diff --git a/darwin/table.m b/darwin/table.m index 213bc644..178e3cd3 100644 --- a/darwin/table.m +++ b/darwin/table.m @@ -18,10 +18,10 @@ enum { partText, partImage, + partButton, }; @interface tablePart : NSObject -@property int index; @property int type; @property int textColumn; @property int textColorColumn; @@ -153,6 +153,7 @@ done: uiTable *t = tv.libui_t; NSColor *color; + [rv setBackgroundColor:nil]; // make default by default if (t->backgroundColumn == -1) return; color = (NSColor *) ((*(m->mh->CellValue))(m->mh, m, row, t->backgroundColumn)); @@ -181,6 +182,8 @@ done: if ([view isKindOfClass:[NSTextField class]]) data = [[((NSTextField *) view) stringValue] UTF8String]; + else if ([view isKindOfClass:[NSButton class]]) + data = NULL; else implbug("table model editing action triggered on non-editable view"); @@ -189,7 +192,7 @@ done: row, [view tag], data); // always refresh the value in case the model rejected it - // TODO only affect tv + // TODO only affect tv? uiTableModelRowChanged(m, row); } @@ -214,6 +217,7 @@ done: NSView *view; NSTextField *tf; NSImageView *iv; + NSButton *b; switch (self.type) { case partText: @@ -235,7 +239,7 @@ done: [tf setTarget:m->m]; [tf setAction:@selector(onAction:)]; } - [tf setTag:self.index]; + [tf setTag:self.textColumn]; view = tf; break; case partImage: @@ -252,9 +256,27 @@ done: iv, NSLayoutAttributeHeight, 1, 0, @"uiTable image squareness constraint")]; - [iv setTag:self.index]; + [iv setTag:self.imageColumn]; view = iv; break; + case partButton: + // TODO buttons get clipped + data = (*(m->mh->CellValue))(m->mh, m, row, self.textColumn); + str = toNSString((char *) data); + b = [[NSButton alloc] initWithFrame:NSZeroRect]; + [b setTitle:str]; + [b setButtonType:NSMomentaryPushInButton]; + [b setBordered:YES]; + [b setBezelStyle:NSRoundRectBezelStyle]; + uiDarwinSetControlFont(b, NSRegularControlSize); + if (self.editable) { + [b setTarget:m->m]; + [b setAction:@selector(onAction:)]; + } else + [b setEnabled:NO]; + [b setTag:self.textColumn]; + view = b; + break; } // if stretchy, don't hug, otherwise hug forcibly @@ -330,6 +352,7 @@ void uiTableModelRowChanged(uiTableModel *m, int index) for (tv in m->tables) { cols = [[NSIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, [[tv tableColumns] count])]; [tv reloadDataForRowIndexes:set columnIndexes:cols]; + // TODO this isn't enough [cols release]; } // set is autoreleased @@ -351,7 +374,6 @@ void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand) tablePart *part; part = [tablePart new]; - part.index = [c->parts count]; part.type = partText; part.textColumn = modelColumn; part.expand = expand; @@ -363,13 +385,24 @@ void uiTableColumnAppendImagePart(uiTableColumn *c, int modelColumn, int expand) tablePart *part; part = [tablePart new]; - part.index = [c->parts count]; part.type = partImage; part.imageColumn = modelColumn; part.expand = expand; [c->parts addObject:part]; } +void uiTableColumnAppendButtonPart(uiTableColumn *c, int modelColumn, int expand) +{ + tablePart *part; + + part = [tablePart new]; + part.type = partButton; + part.textColumn = modelColumn; + part.expand = expand; + part.editable = 1; // editable by default + [c->parts addObject:part]; +} + void uiTableColumnPartSetEditable(uiTableColumn *c, int part, int editable) { tablePart *p; diff --git a/test/page16.c b/test/page16.c index 890e2a3d..35239d0c 100644 --- a/test/page16.c +++ b/test/page16.c @@ -5,7 +5,7 @@ static uiTableModelHandler mh; static int modelNumColumns(uiTableModelHandler *mh, uiTableModel *m) { - return 6; + return 7; } static uiTableModelColumnType modelColumnType(uiTableModelHandler *mh, uiTableModel *m, int column) @@ -24,12 +24,15 @@ static int modelNumRows(uiTableModelHandler *mh, uiTableModel *m) static uiImage *img[2]; static char row9text[1024]; +static int yellowRow = -1; static void *modelCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, int col) { char buf[256]; if (col == 3) { + if (row == yellowRow) + return uiTableModelGiveColor(1, 1, 0, 1); if (row == 3) return uiTableModelGiveColor(1, 0, 0, 1); if (row == 11) @@ -57,6 +60,9 @@ static void *modelCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, i case 1: strcpy(buf, "Part"); break; + case 6: + strcpy(buf, "Make Yellow"); + break; } return uiTableModelStrdup(buf); } @@ -65,6 +71,8 @@ static void modelSetCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, { if (row == 9 && col == 2) strcpy(row9text, (const char *) val); + if (col == 6) + yellowRow = row; } uiBox *makePage16(void) @@ -106,5 +114,8 @@ uiBox *makePage16(void) uiTableSetRowBackgroundColorModelColumn(t, 3); + tc = uiTableAppendColumn(t, "Buttons"); + uiTableColumnAppendButtonPart(tc, 6, 1); + return page16; } diff --git a/uitable.h b/uitable.h index cc5b4d94..df87addb 100644 --- a/uitable.h +++ b/uitable.h @@ -41,6 +41,7 @@ typedef struct uiTableColumn uiTableColumn; _UI_EXTERN void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand); // TODO images shouldn't expand... _UI_EXTERN void uiTableColumnAppendImagePart(uiTableColumn *c, int modelColumn, int expand); +_UI_EXTERN void uiTableColumnAppendButtonPart(uiTableColumn *c, int modelColumn, int expand); // TODO Editable? _UI_EXTERN void uiTableColumnPartSetEditable(uiTableColumn *c, int part, int editable); _UI_EXTERN void uiTableColumnPartSetTextColor(uiTableColumn *c, int part, int modelColumn); From d7caa150b814a98639206fb7a4b3b4583c5160d2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 26 Jun 2016 15:36:46 -0400 Subject: [PATCH 0421/1329] Added checkbox table cells. Now just to add progressbar cells and we'll be good. --- common/table.c | 10 ++++++++++ darwin/table.m | 50 ++++++++++++++++++++++++++++++++++++++++++++++---- test/page16.c | 12 +++++++++++- uitable.h | 6 ++++++ 4 files changed, 73 insertions(+), 5 deletions(-) diff --git a/common/table.c b/common/table.c index c2190b3b..3726883a 100644 --- a/common/table.c +++ b/common/table.c @@ -2,6 +2,16 @@ #include "../ui.h" #include "uipriv.h" +void *uiTableModelGiveInt(int i) +{ + return (void *) ((intptr_t) i); +} + +int uiTableModelTakeInt(void *v) +{ + return (int) ((intptr_t) v); +} + uiTableColumn *uiTableAppendTextColumn(uiTable *t, const char *name, int modelColumn) { uiTableColumn *tc; diff --git a/darwin/table.m b/darwin/table.m index 178e3cd3..c0fd79ed 100644 --- a/darwin/table.m +++ b/darwin/table.m @@ -7,6 +7,7 @@ // - background color shows up for a line or two below selection // - editable NSTextFields have no intrinsic width // - changing a part property does not refresh views +// - is the Y position of checkbox cells correct? @interface tableModel : NSObject { uiTableModel *libui_m; @@ -19,6 +20,7 @@ enum { partText, partImage, partButton, + partCheckbox, }; @interface tablePart : NSObject @@ -26,6 +28,7 @@ enum { @property int textColumn; @property int textColorColumn; @property int imageColumn; +@property int valueColumn; @property int expand; @property int editable; - (NSView *)mkView:(uiTableModel *)m row:(int)row; @@ -153,7 +156,6 @@ done: uiTable *t = tv.libui_t; NSColor *color; - [rv setBackgroundColor:nil]; // make default by default if (t->backgroundColumn == -1) return; color = (NSColor *) ((*(m->mh->CellValue))(m->mh, m, row, t->backgroundColumn)); @@ -182,9 +184,16 @@ done: if ([view isKindOfClass:[NSTextField class]]) data = [[((NSTextField *) view) stringValue] UTF8String]; - else if ([view isKindOfClass:[NSButton class]]) - data = NULL; - else + else if ([view isKindOfClass:[NSButton class]]) { + NSButton *b; + + b = (NSButton *) view; +// if ([b buttonType] == NSSwitchButton) + data = uiTableModelGiveInt([b state] == NSOnState); +// TODO there is no buttonType getter +if(1); else + data = NULL; + } else implbug("table model editing action triggered on non-editable view"); // note the use of [view tag] — we need the model column, which we store in the view tag for relevant views below @@ -277,6 +286,27 @@ done: [b setTag:self.textColumn]; view = b; break; + case partCheckbox: + data = (*(m->mh->CellValue))(m->mh, m, row, self.valueColumn); + b = [[NSButton alloc] initWithFrame:NSZeroRect]; + [b setTitle:@""]; + [b setButtonType:NSSwitchButton]; + // doesn't seem to have an associated bezel style + [b setBordered:NO]; + [b setTransparent:NO]; + uiDarwinSetControlFont(b, NSRegularControlSize); + if (uiTableModelTakeInt(data) != 0) + [b setState:NSOnState]; + else + [b setState:NSOffState]; + if (self.editable) { + [b setTarget:m->m]; + [b setAction:@selector(onAction:)]; + } else + [b setEnabled:NO]; + [b setTag:self.valueColumn]; + view = b; + break; } // if stretchy, don't hug, otherwise hug forcibly @@ -403,6 +433,18 @@ void uiTableColumnAppendButtonPart(uiTableColumn *c, int modelColumn, int expand [c->parts addObject:part]; } +void uiTableColumnAppendCheckboxPart(uiTableColumn *c, int modelColumn, int expand) +{ + tablePart *part; + + part = [tablePart new]; + part.type = partCheckbox; + part.valueColumn = modelColumn; + part.expand = expand; + part.editable = 1; // editable by default + [c->parts addObject:part]; +} + void uiTableColumnPartSetEditable(uiTableColumn *c, int part, int editable) { tablePart *p; diff --git a/test/page16.c b/test/page16.c index 35239d0c..176290a9 100644 --- a/test/page16.c +++ b/test/page16.c @@ -5,7 +5,7 @@ static uiTableModelHandler mh; static int modelNumColumns(uiTableModelHandler *mh, uiTableModel *m) { - return 7; + return 8; } static uiTableModelColumnType modelColumnType(uiTableModelHandler *mh, uiTableModel *m, int column) @@ -14,6 +14,8 @@ static uiTableModelColumnType modelColumnType(uiTableModelHandler *mh, uiTableMo return uiTableModelColumnColor; if (column == 5) return uiTableModelColumnImage; + if (column == 7) + return uiTableModelColumnInt; return uiTableModelColumnString; } @@ -25,6 +27,7 @@ static int modelNumRows(uiTableModelHandler *mh, uiTableModel *m) static uiImage *img[2]; static char row9text[1024]; static int yellowRow = -1; +static int checkStates[15]; static void *modelCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, int col) { @@ -49,6 +52,8 @@ static void *modelCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, i return img[0]; return img[1]; } + if (col == 7) + return uiTableModelGiveInt(checkStates[row]); switch (col) { case 0: sprintf(buf, "Row %d", row); @@ -73,6 +78,8 @@ static void modelSetCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, strcpy(row9text, (const char *) val); if (col == 6) yellowRow = row; + if (col == 7) + checkStates[row] = uiTableModelTakeInt(val); } uiBox *makePage16(void) @@ -91,6 +98,8 @@ uiBox *makePage16(void) strcpy(row9text, "Part"); + memset(checkStates, 0, 15 * sizeof (int)); + page16 = newVerticalBox(); mh.NumColumns = modelNumColumns; @@ -115,6 +124,7 @@ uiBox *makePage16(void) uiTableSetRowBackgroundColorModelColumn(t, 3); tc = uiTableAppendColumn(t, "Buttons"); + uiTableColumnAppendCheckboxPart(tc, 7, 0); uiTableColumnAppendButtonPart(tc, 6, 1); return page16; diff --git a/uitable.h b/uitable.h index df87addb..aed49495 100644 --- a/uitable.h +++ b/uitable.h @@ -11,9 +11,11 @@ _UI_EXTERN void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixe typedef struct uiTableModel uiTableModel; typedef struct uiTableModelHandler uiTableModelHandler; +// TODO actually validate these _UI_ENUM(uiTableModelColumnType) { uiTableModelColumnString, uiTableModelColumnImage, + uiTableModelColumnInt, uiTableModelColumnColor, }; @@ -28,6 +30,8 @@ struct uiTableModelHandler { _UI_EXTERN void *uiTableModelStrdup(const char *str); // TODO rename the strdup one to this too _UI_EXTERN void *uiTableModelGiveColor(double r, double g, double b, double a); +_UI_EXTERN void *uiTableModelGiveInt(int i); +_UI_EXTERN int uiTableModelTakeInt(void *v); _UI_EXTERN uiTableModel *uiNewTableModel(uiTableModelHandler *mh); _UI_EXTERN void uiFreeTableModel(uiTableModel *m); @@ -42,6 +46,8 @@ _UI_EXTERN void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, i // TODO images shouldn't expand... _UI_EXTERN void uiTableColumnAppendImagePart(uiTableColumn *c, int modelColumn, int expand); _UI_EXTERN void uiTableColumnAppendButtonPart(uiTableColumn *c, int modelColumn, int expand); +// TODO should these have labels? +_UI_EXTERN void uiTableColumnAppendCheckboxPart(uiTableColumn *c, int modelColumn, int expand); // TODO Editable? _UI_EXTERN void uiTableColumnPartSetEditable(uiTableColumn *c, int part, int editable); _UI_EXTERN void uiTableColumnPartSetTextColor(uiTableColumn *c, int part, int modelColumn); From 6804f263d48764cf58aeed000f6cb6a3c4698686 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 26 Jun 2016 18:19:34 -0400 Subject: [PATCH 0422/1329] And implemented (mostly) the progressbar table parts. I think that'll do for uiTable features now. --- darwin/table.m | 42 ++++++++++++++++++++++++++++++++++++++++++ test/page16.c | 16 ++++++++++++++-- uitable.h | 3 +++ 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/darwin/table.m b/darwin/table.m index c0fd79ed..ff458caa 100644 --- a/darwin/table.m +++ b/darwin/table.m @@ -8,6 +8,10 @@ // - editable NSTextFields have no intrinsic width // - changing a part property does not refresh views // - is the Y position of checkbox cells correct? +// - progressbars appear ABOVE the table header + +// LONGTERM +// - reuse row views instead of creating a new one each time @interface tableModel : NSObject { uiTableModel *libui_m; @@ -21,6 +25,7 @@ enum { partImage, partButton, partCheckbox, + partProgressBar, }; @interface tablePart : NSObject @@ -227,6 +232,8 @@ if(1); else NSTextField *tf; NSImageView *iv; NSButton *b; + NSProgressIndicator *p; + int value; switch (self.type) { case partText: @@ -307,6 +314,30 @@ if(1); else [b setTag:self.valueColumn]; view = b; break; + case partProgressBar: + data = (*(m->mh->CellValue))(m->mh, m, row, self.valueColumn); + value = uiTableModelTakeInt(data); + // TODO no intrinsic width + p = [[NSProgressIndicator alloc] initWithFrame:NSZeroRect]; + [p setControlSize:NSRegularControlSize]; + [p setBezeled:YES]; + [p setStyle:NSProgressIndicatorBarStyle]; + if (value == -1) { + [p setIndeterminate:YES]; + [p startAnimation:p]; + } else if (value == 100) { + [p setIndeterminate:NO]; + [p setMaxValue:101]; + [p setDoubleValue:101]; + [p setDoubleValue:100]; + [p setMaxValue:100]; + } else { + [p setIndeterminate:NO]; + [p setDoubleValue:(value + 1)]; + [p setDoubleValue:value]; + } + view = p; + break; } // if stretchy, don't hug, otherwise hug forcibly @@ -445,6 +476,17 @@ void uiTableColumnAppendCheckboxPart(uiTableColumn *c, int modelColumn, int expa [c->parts addObject:part]; } +void uiTableColumnAppendProgressBarPart(uiTableColumn *c, int modelColumn, int expand) +{ + tablePart *part; + + part = [tablePart new]; + part.type = partProgressBar; + part.valueColumn = modelColumn; + part.expand = expand; + [c->parts addObject:part]; +} + void uiTableColumnPartSetEditable(uiTableColumn *c, int part, int editable) { tablePart *p; diff --git a/test/page16.c b/test/page16.c index 176290a9..80ac0139 100644 --- a/test/page16.c +++ b/test/page16.c @@ -5,7 +5,7 @@ static uiTableModelHandler mh; static int modelNumColumns(uiTableModelHandler *mh, uiTableModel *m) { - return 8; + return 9; } static uiTableModelColumnType modelColumnType(uiTableModelHandler *mh, uiTableModel *m, int column) @@ -14,7 +14,7 @@ static uiTableModelColumnType modelColumnType(uiTableModelHandler *mh, uiTableMo return uiTableModelColumnColor; if (column == 5) return uiTableModelColumnImage; - if (column == 7) + if (column == 7 || column == 8) return uiTableModelColumnInt; return uiTableModelColumnString; } @@ -54,6 +54,15 @@ static void *modelCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, i } if (col == 7) return uiTableModelGiveInt(checkStates[row]); + if (col == 8) { + if (row == 0) + return uiTableModelGiveInt(0); + if (row == 13) + return uiTableModelGiveInt(100); + if (row == 14) + return uiTableModelGiveInt(-1); + return uiTableModelGiveInt(50); + } switch (col) { case 0: sprintf(buf, "Row %d", row); @@ -127,5 +136,8 @@ uiBox *makePage16(void) uiTableColumnAppendCheckboxPart(tc, 7, 0); uiTableColumnAppendButtonPart(tc, 6, 1); + tc = uiTableAppendColumn(t, "Progress Bar"); + uiTableColumnAppendProgressBarPart(tc, 8, 0); + return page16; } diff --git a/uitable.h b/uitable.h index aed49495..66aeffaf 100644 --- a/uitable.h +++ b/uitable.h @@ -31,6 +31,8 @@ _UI_EXTERN void *uiTableModelStrdup(const char *str); // TODO rename the strdup one to this too _UI_EXTERN void *uiTableModelGiveColor(double r, double g, double b, double a); _UI_EXTERN void *uiTableModelGiveInt(int i); +// TODO TakeString +// TODO add const _UI_EXTERN int uiTableModelTakeInt(void *v); _UI_EXTERN uiTableModel *uiNewTableModel(uiTableModelHandler *mh); @@ -48,6 +50,7 @@ _UI_EXTERN void uiTableColumnAppendImagePart(uiTableColumn *c, int modelColumn, _UI_EXTERN void uiTableColumnAppendButtonPart(uiTableColumn *c, int modelColumn, int expand); // TODO should these have labels? _UI_EXTERN void uiTableColumnAppendCheckboxPart(uiTableColumn *c, int modelColumn, int expand); +_UI_EXTERN void uiTableColumnAppendProgressBarPart(uiTableColumn *c, int modelColumn, int expand); // TODO Editable? _UI_EXTERN void uiTableColumnPartSetEditable(uiTableColumn *c, int part, int editable); _UI_EXTERN void uiTableColumnPartSetTextColor(uiTableColumn *c, int part, int modelColumn); From f354d48bfd00a2b6aff81e9afc9aab8942919f71 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 26 Jun 2016 23:17:34 -0400 Subject: [PATCH 0423/1329] Started the GTK+ implementation of uiTable. --- uitable.h | 1 + unix/table.c | 396 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 397 insertions(+) create mode 100644 unix/table.c diff --git a/uitable.h b/uitable.h index 66aeffaf..a54398c5 100644 --- a/uitable.h +++ b/uitable.h @@ -19,6 +19,7 @@ _UI_ENUM(uiTableModelColumnType) { uiTableModelColumnColor, }; +// TODO validate ranges; validate types on each getter/setter call (? table columns only?) struct uiTableModelHandler { int (*NumColumns)(uiTableModelHandler *, uiTableModel *); uiTableModelColumnType (*ColumnType)(uiTableModelHandler *, uiTableModel *, int); diff --git a/unix/table.c b/unix/table.c new file mode 100644 index 00000000..f348642b --- /dev/null +++ b/unix/table.c @@ -0,0 +1,396 @@ +// 26 june 2016 +#include "uipriv_unix.h" + +#define uiTableModelType (uiTableModel_get_type()) +#define uiTableModel(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), uiTableModelType, uiTableModel)) +#define isuiTableModel(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), uiTableModelType)) +#define uiTableModelClass(class) (G_TYPE_CHECK_CLASS_CAST((class), uiTableModelType, uiTableModelClass)) +#define isuiTableModelClass(class) (G_TYPE_CHECK_CLASS_TYPE((class), uiTableModel)) +#define getuiTableModelClass(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), uiTableModelType, uiTableModelClass)) + +typedef struct uiTableModelClass uiTableModelClass; + +struct uiTableModel { + GObject parent_instance; + uiTableModelHandler *mh; +}; + +struct uiTableModelClass { + GObjectClass parent_class; +}; + +static void uiTableModel_gtk_tree_model_interface_init(GtkTreeModelInterface *iface); + +G_DEFINE_TYPE_WITH_CODE(uiTableModel, uiTableModel, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE(GTK_TYPE_TREE_MODEL, uiTableModel_gtk_tree_model_interface_init)) + +static void uiTableModel_init(uiTableModel *m) +{ + // nothing to do +} + +static void uiTableModel_dispose(GObject *obj) +{ + G_OBJECT_CLASS(uiTableModel_parent_class)->dispose(obj); +} + +static void uiTableModel_finalize(GObject *obj) +{ + G_OBJECT_CLASS(uiTableModel_parent_class)->finalize(obj); +} + +static GtkTreeModelFlags uiTableModel_get_flags(GtkTreeModel *mm) +{ + return GTK_TREE_MODEL_LIST_ONLY; +} + +static gint uiTableModel_get_n_columns(GtkTreeModel *mm) +{ + uiTableModel *m = uiTableModel(mm); + + return (*(m->mh->NumColumns))(m->mh, m); +} + +static GType uiTableModel_get_column_type(GtkTreeModel *mm, gint index) +{ + uiTableModel *m = uiTableModel(mm); + + switch ((*(m->mh->ColumnType))(m->mh, m, index)) { + case uiTableModelColumnString: + return G_TYPE_STRING; + case uiTableModelColumnImage: + // TODO + case uiTableModelColumnInt: + return G_TYPE_INT; + case uiTableModelColumnColor: + return GDK_TYPE_RGBA; + } + // TODO + return G_TYPE_INVALID; +} + +#define STAMP_GOOD 0x1234 +#define STAMP_BAD 0x5678 + +static gboolean uiTableModel_get_iter(GtkTreeModel *mm, GtkTreeIter *iter, GtkTreePath *path) +{ + uiTableModel *m = uiTableModel(mm); + gint row; + + if (gtk_tree_path_get_depth(path) != 1) + goto bad; + row = gtk_tree_path_get_indices(path)[0]; + if (row < 0) + goto bad; + if (row >= (*(m->mh->NumRows))(m->mh, m)) + goto bad; + iter->stamp = STAMP_GOOD; + iter->user_data = GINT_TO_POINTER(row); + return TRUE; +bad: + iter->stamp = STAMP_BAD; + return FALSE; +} + +// GtkListStore returns NULL on error; let's do that too +static GtkTreePath *uiTableModel_get_path(GtkTreeModel *mm, GtkTreeIter *iter) +{ + uiTableModel *m = uiTableModel(mm); + gint row; + + if (iter->stamp != STAMP_GOOD) + return NULL; + row = GPOINTER_TO_INT(iter->user_data); + return gtk_tree_path_new_from_indices(row, -1); +} + +// GtkListStore leaves value empty on failure; let's do the same +static void uiTableModel_get_value(GtkTreeModel *mm, GtkTreeIter *iter, gint column, GValue *value) +{ + uiTableModel *m = uiTableModel(mm); + gint row; + + if (iter->stamp != STAMP_GOOD) + return; + row = GPOINTER_TO_INT(iter->user_data); + data = (*(m->mh->CellValue))(m->mh, m, row, column); + switch ((*(m->mh->ColumnType))(m->mh, m, index)) { + case uiTableModelColumnString: + g_value_init(value, G_TYPE_STRING); + g_value_take_string(value, (char *) data); + return; + case uiTableModelColumnImage: + // TODO + return; + case uiTableModelColumnInt: + g_value_init(value, G_TYPE_INT); + g_value_set_int(value, uiTableModelTakeInt(data)); + return; + case uiTableModelColumnColor: + g_value_init(value, GDK_TYPE_RGBA); + g_value_take_boxed(value, data); + return; + } + // TODO +} + +static gboolean uiTreeModel_iter_next(GtkTreeModel *mm, GtkTreeIter *iter) +{ + uiTableModel *m = uiTableModel(mm); + gint row; + + gint row; + + if (iter->stamp != STAMP_GOOD) + return FALSE; + row = GPOINTER_TO_INT(iter->user_data); + row++; + if (row >= (*(m->mh->NumRows))(m->mh, m)) { + iter->stamp = STAMP_BAD; + return FALSE; + } + iter->user_data = GINT_TO_POINTER(row); + return TRUE; +} + +static gboolean uiTableModel_iter_previous(GtkTreeModel *mm, GtkTreeIter *iter) +{ + uiTableModel *m = uiTableModel(mm); + gint row; + + if (iter->stamp != STAMP_GOOD) + return FALSE; + row = GPOINTER_TO_INT(iter->user_data); + row--; + if (row < 0) { + iter->stamp = STAMP_BAD; + return FALSE; + } + iter->user_data = GINT_TO_POINTER(row); + return TRUE; +} + +static gboolean uiTableModel_iter_children(GtkTreeModel *mm, GtkTreeIter *iter, GtkTreeIter *parent) +{ + return gtk_tree_model_nth_child(mm, iter, parent, 0); +} + +static gboolean uiTableModel_iter_has_child(GtkTreeModel *mm, GtkTreeIter *iter) +{ + return FALSE; +} + +static gint uiTableModel_iter_n_children(GtkTreeModel *mm, GtkTreeIter *iter) +{ + uiTableModel *m = uiTableModel(mm); + + if (iter != NULL) + return 0; + return (*(m->mh->NumRows))(m->mh, m); +} + +static gboolean uiTableModel_iter_nth_child(GtkTreeModel *mm, GtkTreeIter *iter, GtkTreeIter *parent, gint n) +{ + uiTableModel *m = uiTableModel(mm); + + if (iter->stamp != STAMP_GOOD) + return FALSE; + if (parent != NULL) + goto bad; + if (n < 0) + goto bad; + if (n >= (*(m->mh->NumRows))(m->mh, m)) + goto bad; + iter->stamp = STAMP_GOOD; + iter->user_data = GINT_TO_POINTER(n); + return TRUE; +bad: + iter->stamp = STAMP_BAD; + return FALSE; +} + +gboolean uiTableModel_iter_parent(GtkTreeModel *mm, GtkTreeIter *iter, GtkTreeIter *child) +{ + iter->stamp = STAMP_BAD; + return FALSE; +} + +static void uiTableModel_class_init(uiTableModelClass *class) +{ + // nothing to do +} + +static void uiTableModel_gtk_tree_model_interface_init(GtkTreeModelInterface *iface) +{ + iface->get_flags = uiTableModel_get_flags; + iface->get_n_columns = uiTableModel_get_n_columns; + iface->get_column_type = uiTableModel_get_column_type; + iface->get_iter = uiTableModel_get_iter; + iface->get_path = uiTableModel_get_path; + iface->get_value = uiTableModel_get_value; + iface->iter_next = uiTableModel_iter_next; + iface->iter_previous = uiTableModel_iter_previous; + iface->iter_children = uiTableModel_iter_children; + iface->iter_has_child = uiTableModel_iter_has_child; + iface->iter_n_children = uiTableModel_iter_n_children; + iface->iter_nth_child = uiTableModel_iter_nth_child; + iface->iter_parent = uiTableModel_iter_parent; + // don't specify ref_node() or unref_node() +} + +void *uiTableModelStrdup(const char *str) +{ + return g_strdup(str); +} + +void *uiTableModelGiveColor(double r, double g, double b, double a) +{ + GdkRGBA rgba; + + rgba.red = r; + rgba.green = g; + rgba.blue = b; + rgba.alpha = a; + return gdk_rgba_copy(&rgba); +} + +uiTableModel *uiNewTableModel(uiTableModelHandler *mh) +{ + uiTableModel *m; + + m = uiTableModel(g_object_new(uiTableModelType, NULL)); + m->mh = mh; + return m; +} + +void uiFreeTableModel(uiTableModel *m) +{ + g_object_unref(m); +} + +void uiTableModelRowInserted(uiTableModel *m, int newIndex) +{ + GtkTreePath *path; + GtkTreeIter iter; + + path = gtk_tree_path_new_from_indices(newIndex, -1); + iter.stamp = STAMP_GOOD; + iter.user_data = GINT_TO_POINTER(newIndex); + gtk_tree_model_row_inserted(GTK_TREE_MODEL(m), path, iter); + gtk_tree_path_free(path); +} + +void uiTableModelRowChanged(uiTableModel *m, int index) +{ + GtkTreePath *path; + GtkTreeIter iter; + + path = gtk_tree_path_new_from_indices(newIndex, -1); + iter.stamp = STAMP_GOOD; + iter.user_data = GINT_TO_POINTER(newIndex); + gtk_tree_model_row_changed(GTK_TREE_MODEL(m), path, iter); + gtk_tree_path_free(path); +} + +void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) +{ + GtkTreePath *path; + + path = gtk_tree_path_new_from_indices(newIndex, -1); + gtk_tree_model_row_removed(GTK_TREE_MODEL(m), path); + gtk_tree_path_free(path); +} + +struct uiTableColumn { + GtkTreeViewColumn *c; + GtkTreeView *tv; // for pixbufs +}; + +void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand) +{ + GtkCellRenderer *r; + + r = gtk_cell_renderer_text_new(); + // TODO make uneditable + gtk_table_column_pack_start(c->c, r, expand != 0); + gtk_table_column_add_attribute(c->c, r, "text", modelColumn); + // TODO editing signal +} + +struct pixbufData { + GtkTreeView *tv; + int modelColumn; +}; + +static void pixbufDataFunc(GtkTreeViewColumn *c, GtkCellRenderer *r, GtkTreeModel *mm, GtkTreeIter *iter, gpointer data) +{ + struct pixbufData *d = (struct pixbufData *) data; + GValue value = G_VALUE_INIT; + uiImage *img; + + gtk_tree_model_get_value(mm, iter, d->modelColumn, &value); + img = (uiImage *) g_value_get_pointer(&value); + // TODO +} + +void uiTableColumnAppendImagePart(uiTableColumn *c, int modelColumn, int expand) +{ + GtkCellRenderer *r; + struct pixbufData *d; + + r = gtk_cell_renderer_pixbuf_new(); + // TODO make uneditable + gtk_table_column_pack_start(c->c, r, expand != 0); + // TODO use uiNew and uiFree (need to wrap the latter) + d = g_new0(1, struct pixbufData); + d->tv = c->tv; + d->modelColumn = modelColumn; + gtk_tree_view_column_set_cell_data_func(c->c, r, + pixbufDataFunc, d, g_free); +} + +void uiTableColumnAppendButtonPart(uiTableColumn *c, int modelColumn, int expand) +{ + // TODO +} + +void uiTableColumnAppendCheckboxPart(uiTableColumn *c, int modelColumn, int expand) +{ + GtkCellRenderer *r; + + r = gtk_cell_renderer_toggle_new(); + // TODO make editable + gtk_table_column_pack_start(c->c, r, expand != 0); + gtk_table_column_add_attribute(c->c, r, "active", modelColumn); + // TODO editing signal +} + +void uiTableColumnAppendProgressBarPart(uiTableColumn *c, int modelColumn, int expand) +{ + // TODO +} + +void uiTableColumnPartSetEditable(uiTableColumn *c, int part, int editable) +{ + // TODO +} + +void uiTableColumnPartSetTextColor(uiTableColumn *c, int part, int modelColumn) +{ + // TODO +} + +uiTableColumn *uiTableAppendColumn(uiTable *t, const char *name) +{ + // TODO +} + +void uiTableSetRowBackgroundColorModelColumn(uiTable *t, int modelColumn) +{ + // TODO +} + +uiTable *uiNewTable(uiTableModel *model) +{ + // TODO +} From f7e5c7dd25243c679f98af6a1d5a3ab0c997f7bf Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 27 Jun 2016 09:30:22 -0400 Subject: [PATCH 0424/1329] Finished the initial implemenetation of uiTable on GTK+. --- unix/CMakeLists.txt | 1 + unix/table.c | 205 +++++++++++++++++++++++++++++++++++++------- 2 files changed, 174 insertions(+), 32 deletions(-) diff --git a/unix/CMakeLists.txt b/unix/CMakeLists.txt index 33eedfac..dc9726b1 100644 --- a/unix/CMakeLists.txt +++ b/unix/CMakeLists.txt @@ -37,6 +37,7 @@ list(APPEND _LIBUI_SOURCES unix/spinbox.c unix/stddialogs.c unix/tab.c + unix/table.c unix/text.c unix/util.c unix/window.c diff --git a/unix/table.c b/unix/table.c index f348642b..4cd02a81 100644 --- a/unix/table.c +++ b/unix/table.c @@ -301,52 +301,133 @@ void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) gtk_tree_path_free(path); } +enum { + partText, + partImage, + partButton, + partCheckbox, + partProgressBar, +}; + +struct tablePart { + int type; + int textColumn; + int imageColumn; + int valueColumn; + uiTreeView *tv; // for pixbufs and background color + int editable; +}; + struct uiTableColumn { GtkTreeViewColumn *c; - GtkTreeView *tv; // for pixbufs + uiTreeView *tv; // for pixbufs and background color + GPtrArray *parts; }; +struct uiTreeView { + uiUnixControl c; + GtkWidget *widget; + GtkContainer *scontainer; + GtkScrolledWindow *sw; + GtkWidget *treeWidget; + GtkTreeView *tv; + GPtrArray *columns; + int backgroundColumn; +}; + +static void dataFunc(GtkTreeViewColumn *c, GtkCellRenderer *r, GtkTreeModel *mm, GtkTreeIter *iter, gpointer data) +{ + struct tablePart *part = (struct tablePart *) data; + GValue value = G_VALUE_INIT; + const gchar *str; + uiImage *img; + + switch (part->type) { + case partText: + gtk_tree_model_get_value(mm, iter, part->textColumn, &value); + str = g_value_get_string(&value); + g_object_set(r, "text", str, NULL); + if (part->editable) + g_object_set(r, "mode", GTK_CELL_RENDERER_MODE_EDITABLE, NULL); + else + g_object_set(r, "mode", GTK_CELL_RENDERER_MODE_INERT, NULL); + break; + case partImage: + gtk_tree_model_get_value(mm, iter, part->imageColumn, &value); + img = (uiImage *) g_value_get_pointer(&value); + // TODO + g_object_set(r, "mode", GTK_CELL_RENDERER_MODE_INERT, NULL); + break; + case partButton: + gtk_tree_model_get_value(mm, iter, part->textColumn, &value); + // TODO + if (part->editable) + g_object_set(r, "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL); + else + g_object_set(r, "mode", GTK_CELL_RENDERER_MODE_INERT, NULL); + break; + case partCheckbox: + gtk_tree_model_get_value(mm, iter, part->valueColumn, &value); + g_object_set(r, "active", g_value_get_int(&value) != 0, NULL); + if (part->editable) + g_object_set(r, "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL); + else + g_object_set(r, "mode", GTK_CELL_RENDERER_MODE_INERT, NULL); + break; + case partProgressBar: + gtk_tree_model_get_value(mm, iter, part->valueColumn, &value); + // TODO + g_object_set(r, "mode", GTK_CELL_RENDERER_MODE_INERT, NULL); + break; + } + g_value_unset(&value); + + if (part->tv->backgroundColumn != -1) { + GdkRGBA *rgba; + + gtk_tree_model_get_value(mm, &iter, part->tv->backgroundColumn, &value); + rgba = (GdkRGBA *) g_value_get_boxed(&value); + if (rgba != NULL) + g_object_set(r, "cell-background-rgba", rgba, NULL); + g_value_unset(&value); + } +} + void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand) { + struct tablePart *part; GtkCellRenderer *r; + part = uiNew(struct tablePart); + part->type = partText; + part->textColumn = modelColumn; + part->tv = c->tv; + part->editable = 0; + r = gtk_cell_renderer_text_new(); - // TODO make uneditable gtk_table_column_pack_start(c->c, r, expand != 0); - gtk_table_column_add_attribute(c->c, r, "text", modelColumn); + gtk_tree_view_column_set_cell_data_func(c->c, r, dataFunc, part, NULL); // TODO editing signal -} -struct pixbufData { - GtkTreeView *tv; - int modelColumn; -}; - -static void pixbufDataFunc(GtkTreeViewColumn *c, GtkCellRenderer *r, GtkTreeModel *mm, GtkTreeIter *iter, gpointer data) -{ - struct pixbufData *d = (struct pixbufData *) data; - GValue value = G_VALUE_INIT; - uiImage *img; - - gtk_tree_model_get_value(mm, iter, d->modelColumn, &value); - img = (uiImage *) g_value_get_pointer(&value); - // TODO + g_ptr_array_add(c->parts, part); } void uiTableColumnAppendImagePart(uiTableColumn *c, int modelColumn, int expand) { + struct tablePart *part; GtkCellRenderer *r; - struct pixbufData *d; + + part = uiNew(struct tablePart); + part->type = partImage; + part->textColumn = modelColumn; + part->tv = c->tv; + part->editable = 0; r = gtk_cell_renderer_pixbuf_new(); - // TODO make uneditable gtk_table_column_pack_start(c->c, r, expand != 0); - // TODO use uiNew and uiFree (need to wrap the latter) - d = g_new0(1, struct pixbufData); - d->tv = c->tv; - d->modelColumn = modelColumn; - gtk_tree_view_column_set_cell_data_func(c->c, r, - pixbufDataFunc, d, g_free); + gtk_tree_view_column_set_cell_data_func(c->c, r, dataFunc, part, NULL); + + g_ptr_array_add(c->parts, part); } void uiTableColumnAppendButtonPart(uiTableColumn *c, int modelColumn, int expand) @@ -356,18 +437,39 @@ void uiTableColumnAppendButtonPart(uiTableColumn *c, int modelColumn, int expand void uiTableColumnAppendCheckboxPart(uiTableColumn *c, int modelColumn, int expand) { + struct tablePart *part; GtkCellRenderer *r; + part = uiNew(struct tablePart); + part->type = partCheckbox; + part->valueColumn = modelColumn; + part->tv = c->tv; + part->editable = 1; // editable by default + r = gtk_cell_renderer_toggle_new(); - // TODO make editable gtk_table_column_pack_start(c->c, r, expand != 0); - gtk_table_column_add_attribute(c->c, r, "active", modelColumn); + gtk_tree_view_column_set_cell_data_func(c->c, r, dataFunc, part, NULL); // TODO editing signal + + g_ptr_array_add(c->parts, part); } void uiTableColumnAppendProgressBarPart(uiTableColumn *c, int modelColumn, int expand) { - // TODO + struct tablePart *part; + GtkCellRenderer *r; + + part = uiNew(struct tablePart); + part->type = partProgressBar; + part->valueColumn = modelColumn; + part->tv = c->tv; + part->editable = 0; + + r = gtk_cell_renderer_progress_new(); + gtk_table_column_pack_start(c->c, r, expand != 0); + gtk_tree_view_column_set_cell_data_func(c->c, r, dataFunc, part, NULL); + + g_ptr_array_add(c->parts, part); } void uiTableColumnPartSetEditable(uiTableColumn *c, int part, int editable) @@ -380,17 +482,56 @@ void uiTableColumnPartSetTextColor(uiTableColumn *c, int part, int modelColumn) // TODO } +uiUnixControlAllDefaultsExceptDestroy(uiTable) + +static void uiTableDestroy(uiControl *c) +{ + uiTable *t = uiTable(c); + + // TODO + g_object_unref(t->widget); + uiFreeControl(uiControl(t)); +} + uiTableColumn *uiTableAppendColumn(uiTable *t, const char *name) { - // TODO + uiTableColumn *c; + + c = uiNew(uiTableColumn); + c->c = gtk_tree_view_column_new(); + gtk_tree_view_column_set_title(c->c, name); + gtk_tree_view_append_column(t->tv, c->c); + c->tv = t; // TODO rename field to t, cascade + c->parts = g_ptr_array_new(); + return c; } void uiTableSetRowBackgroundColorModelColumn(uiTable *t, int modelColumn) { - // TODO + t->backgroundColumn = modelColumn; + // TODO refresh table } uiTable *uiNewTable(uiTableModel *model) { - // TODO + uiTable *t; + + uiUnixNewControl(uiTable, t); + + t->backgroundColumn = -1; + + t->widget = gtk_scrolled_window_new(NULL, NULL); + t->scontainer = GTK_CONTAINER(e->widget); + t->sw = GTK_SCROLLED_WINDOW(e->widget); + gtk_scrolled_window_set_shadow_type(t->sw, GTK_SHADOW_IN); + + t->treeviewWidget = gtk_tree_view_new_with_model(GTK_TREE_MODEL(m)); + t->tv = GTK_TREE_VIEW(t->treeviewWidget); + // TODO set up t->tv + + gtk_container_add(t->scontainer, t->treeviewWidget); + // and make the tree view visible; only the scrolled window's visibility is controlled by libui + gtk_widget_show(t->treeviewWidget); + + return t; } From 99a3462eb401b030d8a13be2ca22cfeeb74a325e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 27 Jun 2016 11:38:11 -0400 Subject: [PATCH 0425/1329] Added GTK+ images. Will hook it up to tables later. --- unix/CMakeLists.txt | 1 + unix/image.c | 59 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 unix/image.c diff --git a/unix/CMakeLists.txt b/unix/CMakeLists.txt index dc9726b1..14c957df 100644 --- a/unix/CMakeLists.txt +++ b/unix/CMakeLists.txt @@ -26,6 +26,7 @@ list(APPEND _LIBUI_SOURCES unix/graphemes.c unix/grid.c unix/group.c + unix/image.c unix/label.c unix/main.c unix/menu.c diff --git a/unix/image.c b/unix/image.c new file mode 100644 index 00000000..7a77cf02 --- /dev/null +++ b/unix/image.c @@ -0,0 +1,59 @@ +// 27 june 2016 +#include "uipriv_unix.h" + +struct uiImage { + double width; + double height; + GPtrArray *images; +}; + +static void freeImageRep(gpointer item) +{ + cairo_surface_t *cs = (cairo_surface_t *) item; + unsigned char *buf; + + buf = cairo_image_surface_get_data(cs); + cairo_surface_destroy(cs); + uiFree(buf); +} + +uiImage *uiNewImage(double width, double height) +{ + uiImage *i; + + i = uiNew(uiImage); + i->width = width; + i->height = height; + i->images = g_ptr_array_new_with_free_func(g_object_unref); + return i; +} + +void uiFreeImage(uiImage *i) +{ + g_ptr_array_free(i->images, TRUE); + uiFree(i); +} + +void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int pixelStride) +{ + cairo_surface_t *cs; + unsigned char *buf, *p; + uint8_t *src = (uint8_t *) pixels; + int cstride; + int y; + + cstride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, pixelWidth); + buf = (unsigned char *) uiAlloc((cstride * pixelHeight * 4) * sizeof (unsigned char), "unsigned char[]"); + p = buf; + for (y = 0; y < pixelWidth * pixelHeight; y += pixelStride) { + memmove(p, src + y, cstride); + p += cstride; + } + cs = cairo_image_surface_create_for_data(buf, CAIRO_FORMAT_ARGB32, + pixelWidth, pixelHeight, + cstride); + if (cairo_surface_status(cs) != CAIRO_STATUS_SUCCESS) + /* TODO */; + cairo_surface_flush(cs); + g_ptr_array_add(i->images, cs); +} From 40e943eb435b02bb90f7e46ef89414dc2deda233 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 27 Jun 2016 12:01:36 -0400 Subject: [PATCH 0426/1329] Fixed compile issues. Now to fix other issues. Yay! --- unix/image.c | 2 +- unix/table.c | 58 +++++++++++++++++++++++++--------------------------- 2 files changed, 29 insertions(+), 31 deletions(-) diff --git a/unix/image.c b/unix/image.c index 7a77cf02..522d6b66 100644 --- a/unix/image.c +++ b/unix/image.c @@ -24,7 +24,7 @@ uiImage *uiNewImage(double width, double height) i = uiNew(uiImage); i->width = width; i->height = height; - i->images = g_ptr_array_new_with_free_func(g_object_unref); + i->images = g_ptr_array_new_with_free_func(freeImageRep); return i; } diff --git a/unix/table.c b/unix/table.c index 4cd02a81..235d7171 100644 --- a/unix/table.c +++ b/unix/table.c @@ -19,7 +19,7 @@ struct uiTableModelClass { GObjectClass parent_class; }; -static void uiTableModel_gtk_tree_model_interface_init(GtkTreeModelInterface *iface); +static void uiTableModel_gtk_tree_model_interface_init(GtkTreeModelIface *iface); G_DEFINE_TYPE_WITH_CODE(uiTableModel, uiTableModel, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE(GTK_TYPE_TREE_MODEL, uiTableModel_gtk_tree_model_interface_init)) @@ -95,7 +95,6 @@ bad: // GtkListStore returns NULL on error; let's do that too static GtkTreePath *uiTableModel_get_path(GtkTreeModel *mm, GtkTreeIter *iter) { - uiTableModel *m = uiTableModel(mm); gint row; if (iter->stamp != STAMP_GOOD) @@ -109,12 +108,13 @@ static void uiTableModel_get_value(GtkTreeModel *mm, GtkTreeIter *iter, gint col { uiTableModel *m = uiTableModel(mm); gint row; + void *data; if (iter->stamp != STAMP_GOOD) return; row = GPOINTER_TO_INT(iter->user_data); data = (*(m->mh->CellValue))(m->mh, m, row, column); - switch ((*(m->mh->ColumnType))(m->mh, m, index)) { + switch ((*(m->mh->ColumnType))(m->mh, m, column)) { case uiTableModelColumnString: g_value_init(value, G_TYPE_STRING); g_value_take_string(value, (char *) data); @@ -134,13 +134,11 @@ static void uiTableModel_get_value(GtkTreeModel *mm, GtkTreeIter *iter, gint col // TODO } -static gboolean uiTreeModel_iter_next(GtkTreeModel *mm, GtkTreeIter *iter) +static gboolean uiTableModel_iter_next(GtkTreeModel *mm, GtkTreeIter *iter) { uiTableModel *m = uiTableModel(mm); gint row; - gint row; - if (iter->stamp != STAMP_GOOD) return FALSE; row = GPOINTER_TO_INT(iter->user_data); @@ -155,7 +153,6 @@ static gboolean uiTreeModel_iter_next(GtkTreeModel *mm, GtkTreeIter *iter) static gboolean uiTableModel_iter_previous(GtkTreeModel *mm, GtkTreeIter *iter) { - uiTableModel *m = uiTableModel(mm); gint row; if (iter->stamp != STAMP_GOOD) @@ -172,7 +169,7 @@ static gboolean uiTableModel_iter_previous(GtkTreeModel *mm, GtkTreeIter *iter) static gboolean uiTableModel_iter_children(GtkTreeModel *mm, GtkTreeIter *iter, GtkTreeIter *parent) { - return gtk_tree_model_nth_child(mm, iter, parent, 0); + return gtk_tree_model_iter_nth_child(mm, iter, parent, 0); } static gboolean uiTableModel_iter_has_child(GtkTreeModel *mm, GtkTreeIter *iter) @@ -217,10 +214,11 @@ gboolean uiTableModel_iter_parent(GtkTreeModel *mm, GtkTreeIter *iter, GtkTreeIt static void uiTableModel_class_init(uiTableModelClass *class) { - // nothing to do + G_OBJECT_CLASS(class)->dispose = uiTableModel_dispose; + G_OBJECT_CLASS(class)->finalize = uiTableModel_finalize; } -static void uiTableModel_gtk_tree_model_interface_init(GtkTreeModelInterface *iface) +static void uiTableModel_gtk_tree_model_interface_init(GtkTreeModelIface *iface) { iface->get_flags = uiTableModel_get_flags; iface->get_n_columns = uiTableModel_get_n_columns; @@ -276,7 +274,7 @@ void uiTableModelRowInserted(uiTableModel *m, int newIndex) path = gtk_tree_path_new_from_indices(newIndex, -1); iter.stamp = STAMP_GOOD; iter.user_data = GINT_TO_POINTER(newIndex); - gtk_tree_model_row_inserted(GTK_TREE_MODEL(m), path, iter); + gtk_tree_model_row_inserted(GTK_TREE_MODEL(m), path, &iter); gtk_tree_path_free(path); } @@ -285,10 +283,10 @@ void uiTableModelRowChanged(uiTableModel *m, int index) GtkTreePath *path; GtkTreeIter iter; - path = gtk_tree_path_new_from_indices(newIndex, -1); + path = gtk_tree_path_new_from_indices(index, -1); iter.stamp = STAMP_GOOD; - iter.user_data = GINT_TO_POINTER(newIndex); - gtk_tree_model_row_changed(GTK_TREE_MODEL(m), path, iter); + iter.user_data = GINT_TO_POINTER(index); + gtk_tree_model_row_changed(GTK_TREE_MODEL(m), path, &iter); gtk_tree_path_free(path); } @@ -296,8 +294,8 @@ void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) { GtkTreePath *path; - path = gtk_tree_path_new_from_indices(newIndex, -1); - gtk_tree_model_row_removed(GTK_TREE_MODEL(m), path); + path = gtk_tree_path_new_from_indices(oldIndex, -1); + gtk_tree_model_row_deleted(GTK_TREE_MODEL(m), path); gtk_tree_path_free(path); } @@ -314,17 +312,17 @@ struct tablePart { int textColumn; int imageColumn; int valueColumn; - uiTreeView *tv; // for pixbufs and background color + uiTable *tv; // for pixbufs and background color int editable; }; struct uiTableColumn { GtkTreeViewColumn *c; - uiTreeView *tv; // for pixbufs and background color + uiTable *tv; // for pixbufs and background color GPtrArray *parts; }; -struct uiTreeView { +struct uiTable { uiUnixControl c; GtkWidget *widget; GtkContainer *scontainer; @@ -385,7 +383,7 @@ static void dataFunc(GtkTreeViewColumn *c, GtkCellRenderer *r, GtkTreeModel *mm, if (part->tv->backgroundColumn != -1) { GdkRGBA *rgba; - gtk_tree_model_get_value(mm, &iter, part->tv->backgroundColumn, &value); + gtk_tree_model_get_value(mm, iter, part->tv->backgroundColumn, &value); rgba = (GdkRGBA *) g_value_get_boxed(&value); if (rgba != NULL) g_object_set(r, "cell-background-rgba", rgba, NULL); @@ -405,7 +403,7 @@ void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand) part->editable = 0; r = gtk_cell_renderer_text_new(); - gtk_table_column_pack_start(c->c, r, expand != 0); + gtk_tree_view_column_pack_start(c->c, r, expand != 0); gtk_tree_view_column_set_cell_data_func(c->c, r, dataFunc, part, NULL); // TODO editing signal @@ -424,7 +422,7 @@ void uiTableColumnAppendImagePart(uiTableColumn *c, int modelColumn, int expand) part->editable = 0; r = gtk_cell_renderer_pixbuf_new(); - gtk_table_column_pack_start(c->c, r, expand != 0); + gtk_tree_view_column_pack_start(c->c, r, expand != 0); gtk_tree_view_column_set_cell_data_func(c->c, r, dataFunc, part, NULL); g_ptr_array_add(c->parts, part); @@ -447,7 +445,7 @@ void uiTableColumnAppendCheckboxPart(uiTableColumn *c, int modelColumn, int expa part->editable = 1; // editable by default r = gtk_cell_renderer_toggle_new(); - gtk_table_column_pack_start(c->c, r, expand != 0); + gtk_tree_view_column_pack_start(c->c, r, expand != 0); gtk_tree_view_column_set_cell_data_func(c->c, r, dataFunc, part, NULL); // TODO editing signal @@ -466,7 +464,7 @@ void uiTableColumnAppendProgressBarPart(uiTableColumn *c, int modelColumn, int e part->editable = 0; r = gtk_cell_renderer_progress_new(); - gtk_table_column_pack_start(c->c, r, expand != 0); + gtk_tree_view_column_pack_start(c->c, r, expand != 0); gtk_tree_view_column_set_cell_data_func(c->c, r, dataFunc, part, NULL); g_ptr_array_add(c->parts, part); @@ -521,17 +519,17 @@ uiTable *uiNewTable(uiTableModel *model) t->backgroundColumn = -1; t->widget = gtk_scrolled_window_new(NULL, NULL); - t->scontainer = GTK_CONTAINER(e->widget); - t->sw = GTK_SCROLLED_WINDOW(e->widget); + t->scontainer = GTK_CONTAINER(t->widget); + t->sw = GTK_SCROLLED_WINDOW(t->widget); gtk_scrolled_window_set_shadow_type(t->sw, GTK_SHADOW_IN); - t->treeviewWidget = gtk_tree_view_new_with_model(GTK_TREE_MODEL(m)); - t->tv = GTK_TREE_VIEW(t->treeviewWidget); + t->treeWidget = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); + t->tv = GTK_TREE_VIEW(t->treeWidget); // TODO set up t->tv - gtk_container_add(t->scontainer, t->treeviewWidget); + gtk_container_add(t->scontainer, t->treeWidget); // and make the tree view visible; only the scrolled window's visibility is controlled by libui - gtk_widget_show(t->treeviewWidget); + gtk_widget_show(t->treeWidget); return t; } From af0dbd3a0e4166c9732a07464834be478549ac3d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 27 Jun 2016 12:24:14 -0400 Subject: [PATCH 0427/1329] Fixed a bunch of bugs in GTK+ table.c. --- unix/table.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/unix/table.c b/unix/table.c index 235d7171..22deb9bf 100644 --- a/unix/table.c +++ b/unix/table.c @@ -59,7 +59,7 @@ static GType uiTableModel_get_column_type(GtkTreeModel *mm, gint index) case uiTableModelColumnString: return G_TYPE_STRING; case uiTableModelColumnImage: - // TODO + return G_TYPE_POINTER; case uiTableModelColumnInt: return G_TYPE_INT; case uiTableModelColumnColor: @@ -120,7 +120,8 @@ static void uiTableModel_get_value(GtkTreeModel *mm, GtkTreeIter *iter, gint col g_value_take_string(value, (char *) data); return; case uiTableModelColumnImage: - // TODO + g_value_init(value, G_TYPE_POINTER); + g_value_set_pointer(value, data); return; case uiTableModelColumnInt: g_value_init(value, G_TYPE_INT); @@ -387,6 +388,8 @@ static void dataFunc(GtkTreeViewColumn *c, GtkCellRenderer *r, GtkTreeModel *mm, rgba = (GdkRGBA *) g_value_get_boxed(&value); if (rgba != NULL) g_object_set(r, "cell-background-rgba", rgba, NULL); + else + g_object_set(r, "cell-background-set", FALSE, NULL); g_value_unset(&value); } } @@ -417,7 +420,7 @@ void uiTableColumnAppendImagePart(uiTableColumn *c, int modelColumn, int expand) part = uiNew(struct tablePart); part->type = partImage; - part->textColumn = modelColumn; + part->imageColumn = modelColumn; part->tv = c->tv; part->editable = 0; From cf3182f4d2fc133e0b1d340cd0abd8145b96d6ea Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 27 Jun 2016 16:11:15 -0400 Subject: [PATCH 0428/1329] Put images on uiTables. We're going to have to swizzle on OS X after all :( --- unix/image.c | 63 +++++++++++++++++++++++++++++++++++++++++++++- unix/table.c | 24 +++++++++++++++++- unix/uipriv_unix.h | 4 +++ 3 files changed, 89 insertions(+), 2 deletions(-) diff --git a/unix/image.c b/unix/image.c index 522d6b66..a79e550f 100644 --- a/unix/image.c +++ b/unix/image.c @@ -45,7 +45,7 @@ void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, in cstride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, pixelWidth); buf = (unsigned char *) uiAlloc((cstride * pixelHeight * 4) * sizeof (unsigned char), "unsigned char[]"); p = buf; - for (y = 0; y < pixelWidth * pixelHeight; y += pixelStride) { + for (y = 0; y < pixelStride * pixelHeight; y += pixelStride) { memmove(p, src + y, cstride); p += cstride; } @@ -57,3 +57,64 @@ void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, in cairo_surface_flush(cs); g_ptr_array_add(i->images, cs); } + +struct matcher { + cairo_surface_t *best; + int distX; + int distY; + int targetX; + int targetY; + gboolean foundLarger; +}; + +// TODO is this the right algorithm? +static void match(gpointer surface, gpointer data) +{ + cairo_surface_t *cs = (cairo_surface_t *) surface; + struct matcher *m = (struct matcher *) data; + int x, y; + int x2, y2; + + x = cairo_image_surface_get_width(cs); + y = cairo_image_surface_get_height(cs); + if (m->best == NULL) + goto writeMatch; + + if (x < m->targetX && y < m->targetY) + if (m->foundLarger) + // always prefer larger ones + return; + if (x >= m->targetX && y >= m->targetY && !m->foundLarger) + // we set foundLarger below + goto writeMatch; + + x2 = abs(m->targetX - x); + y2 = abs(m->targetY - y); + if (x2 < m->distX && y2 < m->distY) + goto writeMatch; + + // TODO weight one dimension? threshhold? + return; + +writeMatch: + // must set this here too; otherwise the first image will never have ths set + if (x >= m->targetX && y >= m->targetY && !m->foundLarger) + m->foundLarger = TRUE; + m->best = cs; + m->distX = abs(m->targetX - x); + m->distY = abs(m->targetY - y); +} + +cairo_surface_t *imageAppropriateSurface(uiImage *i, GtkWidget *w) +{ + struct matcher m; + + m.best = NULL; + m.distX = G_MAXINT; + m.distY = G_MAXINT; + m.targetX = i->width * gtk_widget_get_scale_factor(w); + m.targetY = i->height * gtk_widget_get_scale_factor(w); + m.foundLarger = FALSE; + g_ptr_array_foreach(i->images, match, &m); + return m.best; +} diff --git a/unix/table.c b/unix/table.c index 22deb9bf..e0c61eae 100644 --- a/unix/table.c +++ b/unix/table.c @@ -334,6 +334,25 @@ struct uiTable { int backgroundColumn; }; +// use the same size as GtkFileChooserWidget's treeview +// TODO refresh when icon theme changes +// TODO doesn't work when scaled +// TODO is this even necessary? +static void setImageSize(GtkCellRenderer *r) +{ + gint size; + gint width, height; + gint xpad, ypad; + + size = 16; // fallback used by GtkFileChooserWidget + if (gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height) != FALSE) + size = MAX(width, height); + gtk_cell_renderer_get_padding(r, &xpad, &ypad); + gtk_cell_renderer_set_fixed_size(r, + 2 * xpad + size, + 2 * ypad + size); +} + static void dataFunc(GtkTreeViewColumn *c, GtkCellRenderer *r, GtkTreeModel *mm, GtkTreeIter *iter, gpointer data) { struct tablePart *part = (struct tablePart *) data; @@ -352,9 +371,12 @@ static void dataFunc(GtkTreeViewColumn *c, GtkCellRenderer *r, GtkTreeModel *mm, g_object_set(r, "mode", GTK_CELL_RENDERER_MODE_INERT, NULL); break; case partImage: +//TODO setImageSize(r); gtk_tree_model_get_value(mm, iter, part->imageColumn, &value); img = (uiImage *) g_value_get_pointer(&value); - // TODO + g_object_set(r, "surface", + imageAppropriateSurface(img, part->tv->treeWidget), + NULL); g_object_set(r, "mode", GTK_CELL_RENDERER_MODE_INERT, NULL); break; case partButton: diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index 785c5824..d8d639dd 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -8,6 +8,7 @@ #include // see drawtext.c #include #include +#include #include "../ui.h" #include "../ui_unix.h" #include "../common/uipriv.h" @@ -50,3 +51,6 @@ extern PangoFont *pangoDescToPangoFont(PangoFontDescription *pdesc); // graphemes.c extern ptrdiff_t *graphemes(const char *text, PangoContext *context); + +// image.c +extern cairo_surface_t *imageAppropriateSurface(uiImage *i, GtkWidget *w); From 53bd751461f3cf26dc433a3a3ea7c4d104109e9c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 27 Jun 2016 16:34:57 -0400 Subject: [PATCH 0429/1329] Fixed uiImage byte order on OS X. --- darwin/image.m | 31 +++++- test/images.c | 238 ++++++++++++++++++++++----------------------- test/images/gen.go | 4 +- 3 files changed, 151 insertions(+), 122 deletions(-) diff --git a/darwin/image.m b/darwin/image.m index 6885e075..b62de31d 100644 --- a/darwin/image.m +++ b/darwin/image.m @@ -4,6 +4,7 @@ struct uiImage { NSImage *i; NSSize size; + NSMutableArray *swizzled; }; uiImage *uiNewImage(double width, double height) @@ -13,21 +14,46 @@ uiImage *uiNewImage(double width, double height) i = uiNew(uiImage); i->size = NSMakeSize(width, height); i->i = [[NSImage alloc] initWithSize:i->size]; + i->swizzled = [NSMutableArray new]; return i; } void uiFreeImage(uiImage *i) { + NSValue *v; + [i->i release]; + // to be safe, do this after releasing the image + for (v in i->swizzled) + uiFree([v pointerValue]); + [i->swizzled release]; uiFree(i); } void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int pixelStride) { NSBitmapImageRep *repCalibrated, *repsRGB; + uint8_t *swizzled, *bp, *sp; + int x, y; unsigned char *pix[1]; - pix[0] = (unsigned char *) pixels; + // OS X demands that R and B are in the opposite order from what we expect + // we must swizzle :( + // LONGTERM test on a big-endian system + swizzled = (uint8_t *) uiAlloc((pixelStride * pixelHeight * 4) * sizeof (uint8_t), "uint8_t[]"); + bp = (uint8_t *) pixels; + sp = swizzled; + for (y = 0; y < pixelHeight * pixelStride; y += pixelStride) + for (x = 0; x < pixelStride; x++) { + sp[0] = bp[2]; + sp[1] = bp[1]; + sp[2] = bp[0]; + sp[3] = bp[3]; + sp += 4; + bp += 4; + } + + pix[0] = (unsigned char *) swizzled; repCalibrated = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:pix pixelsWide:pixelWidth pixelsHigh:pixelHeight @@ -45,6 +71,9 @@ void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, in [i->i addRepresentation:repsRGB]; [repsRGB setSize:i->size]; [repsRGB release]; + + // we need to keep swizzled alive for NSBitmapImageRep + [i->swizzled addObject:[NSValue valueWithPointer:swizzled]]; } NSImage *imageImage(uiImage *i) diff --git a/test/images.c b/test/images.c index 39ab72f4..df21b6eb 100644 --- a/test/images.c +++ b/test/images.c @@ -2,108 +2,108 @@ #include "test.h" static const uint32_t dat0[] = { - 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFC60E5, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFFFC60E5, 0xFF43FBFF, 0xFFFC60E5, 0xFF43FBFF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFC60E5, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFFFFC38A, 0xFFFFC38A, 0xFFFFC38A, 0xFFFFC38A, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF7CC589, - 0xFF7CC589, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF7CC589, - 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, + 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, 0xFF8AC3FF, 0xFF8AC3FF, 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF89C57C, + 0xFF89C57C, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF89C57C, + 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, }; static const uint32_t dat1[] = { - 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, - 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, - 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, - 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, - 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, - 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFC60E5, - 0xFFFC60E5, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFC60E5, 0xFF43FBFF, - 0xFF43FBFF, 0xFFFC60E5, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFC60E5, 0xFF43FBFF, 0xFF43FBFF, - 0xFF43FBFF, 0xFF43FBFF, 0xFFFC60E5, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFFFC60E5, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, - 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFC60E5, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF43FBFF, 0xFFFC60E5, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, - 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFC60E5, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFC60E5, 0xFF43FBFF, 0xFF43FBFF, - 0xFF43FBFF, 0xFF43FBFF, 0xFFFC60E5, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFC60E5, 0xFF43FBFF, - 0xFF43FBFF, 0xFFFC60E5, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFC60E5, - 0xFFFC60E5, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, - 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, - 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF7940FF, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFFFFC38A, - 0xFFFFC38A, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, - 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, - 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, - 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, - 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, - 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, - 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, - 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, - 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, - 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, - 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, - 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, - 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, - 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF43FBFF, 0xFF7CC589, - 0xFF7CC589, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, - 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF7CC589, - 0xFF7CC589, 0xFFFFC38A, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, - 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFF43FBFF, 0xFFFFC38A, 0xFF7CC589, - 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, - 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, 0xFF7CC589, + 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, + 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, + 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, + 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, + 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, + 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, + 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF89C57C, + 0xFF89C57C, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF89C57C, + 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, + 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, }; static const uint32_t dat2[] = { 0xAC676767, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0x562B2B2B, 0x00000000, 0x00000000, 0xFF818181, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, - 0xFF818181, 0xFFFFFFFF, 0xFFB7B7B7, 0xFFB7B7B7, 0xFFB8B7B7, 0xFF9F9E9E, 0xFFB8B8B8, 0xFFB8B8B8, 0xFF9F9F9F, 0xFFB9B8B8, 0xFFB9B8B8, 0xFF9F9F9F, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, - 0xFF818181, 0xFFFFFFFF, 0xFF9E9E9E, 0xFF9E9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, + 0xFF818181, 0xFFFFFFFF, 0xFFB7B7B7, 0xFFB7B7B7, 0xFFB7B7B8, 0xFF9E9E9F, 0xFFB8B8B8, 0xFFB8B8B8, 0xFF9F9F9F, 0xFFB8B8B9, 0xFFB8B8B9, 0xFF9F9F9F, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, + 0xFF818181, 0xFFFFFFFF, 0xFF9E9E9E, 0xFF9F9F9E, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, 0xFF818181, 0xFFFFFFFF, 0xFFB7B8B7, 0xFFC3C3C3, 0xFFC3C3C3, 0xFF9F9F9F, 0xFFEEEEEE, 0xFFEEEEEE, 0xFFC2C2C2, 0xFFEEEFEE, 0xFFEFEEEF, 0xFFC2C2C2, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, - 0xFF818181, 0xFFFFFFFF, 0xFFB1B1B1, 0xFFB8B8B8, 0xFFB9B8B9, 0xFF9F9F9F, 0xFFE0E0E0, 0xFFE0E1E0, 0xFFC2C2C2, 0xFFE0E0E1, 0xFFE1E1E1, 0xFFC2C2C2, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, - 0xFF818181, 0xFFFFFFFF, 0xFFB8B8B9, 0xFFC4C4C4, 0xFFC4C4C3, 0xFF9F9F9F, 0xFFEFEEEE, 0xFFEFEEEF, 0xFFC2C2C2, 0xFFEFEFEF, 0xFFEFEFF0, 0xFFC2C2C2, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, - 0xFF818181, 0xFFFFFFFF, 0xFFB1B1B1, 0xFFB8B9B9, 0xFFB9B9B9, 0xFF9F9F9F, 0xFFA5A6C9, 0xFF363AAE, 0xFF0C0FA5, 0xFF0102A4, 0xFF0102A4, 0xFF0A0DA4, 0xFF373BB0, 0xFF69698D, 0x00000000, 0x00000000, - 0xFF818181, 0xFFFFFFFF, 0xFFB9B9B9, 0xFFC4C4C4, 0xFFC4C4C4, 0xFF3D409E, 0xFF070AA7, 0xFF5364C6, 0xFF5C71CB, 0xFF5D73C9, 0xFF5B71C5, 0xFF596CBF, 0xFF616EC0, 0xFF1D20AC, 0xC127297E, 0x19191919, - 0xFF818181, 0xFFFFFFFF, 0xFFB2B2B2, 0xFFB9B9B9, 0xFF5B5CA6, 0xFF1720AF, 0xFF5D72CB, 0xFF4D65C7, 0xFF3853BB, 0xFF3651B6, 0xFF354EB0, 0xFF475DB3, 0xFF455BAE, 0xFF4357A9, 0xFF282FAA, 0xA1161764, - 0xFF818181, 0xFFFFFFFF, 0xFFB9B9B9, 0xFFC4C4C4, 0xFF1214A5, 0xFF5A6FCA, 0xFF3954BF, 0xFF3752BA, 0xFF364FB4, 0xFF344DAF, 0xFF566588, 0xFF5A5F76, 0xFF565B71, 0xFF53586B, 0xFF5B6077, 0xF70B0C98, - 0xFF818181, 0xFFFFFFFF, 0xFFB2B2B2, 0xFFB9B9B9, 0xFF0102A4, 0xFF5169C7, 0xFF3751B8, 0xFF364EB2, 0xFF344CAD, 0xFF3A65AE, 0xFF5D9995, 0xFFA0841D, 0xFF997F17, 0xFFA5913D, 0xFFA08E3B, 0xFF0101A2, - 0xFF818181, 0xFFFFFFFF, 0xFFB9B9B9, 0xFFC4C4C4, 0xFF12129F, 0xFF6D7ECA, 0xFF485FB8, 0xFF334CAB, 0xFF3249A6, 0xFF448BB1, 0xFF50BCB8, 0xFF868035, 0xFFAD9C50, 0xFF978327, 0xFF776E49, 0xF7080892, - 0xFF818181, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF7878BC, 0xFF1A1D9C, 0xFF6D7EBD, 0xFF707FBE, 0xFF7483BD, 0xFF87C7CD, 0xFF89D0CB, 0xFF8AB4B4, 0xFFA0935C, 0xFF6D6544, 0xFF18167B, 0xA11D1D66, - 0xB4696969, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF404093, 0xFF05069E, 0xFF3D4699, 0xFF4D629E, 0xFF4CA1A6, 0xFF4A9B9F, 0xFF458894, 0xFF433C4B, 0xFF05059A, 0xBA1F1F77, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x55161635, 0xD317177F, 0xF90B0B9C, 0xFF0102A3, 0xFF0101A2, 0xF90B0B9C, 0xD31E1E88, 0x55222241, 0x00000000, 0x00000000, + 0xFF818181, 0xFFFFFFFF, 0xFFB1B1B1, 0xFFB8B8B8, 0xFFB9B8B9, 0xFF9F9F9F, 0xFFE0E0E0, 0xFFE0E1E0, 0xFFC2C2C2, 0xFFE1E0E0, 0xFFE1E1E1, 0xFFC2C2C2, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, + 0xFF818181, 0xFFFFFFFF, 0xFFB9B8B8, 0xFFC4C4C4, 0xFFC3C4C4, 0xFF9F9F9F, 0xFFEEEEEF, 0xFFEFEEEF, 0xFFC2C2C2, 0xFFEFEFEF, 0xFFF0EFEF, 0xFFC2C2C2, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, + 0xFF818181, 0xFFFFFFFF, 0xFFB1B1B1, 0xFFB9B9B8, 0xFFB9B9B9, 0xFF9F9F9F, 0xFFC9A6A5, 0xFFAE3A36, 0xFFA50F0C, 0xFFA40201, 0xFFA40201, 0xFFA40D0A, 0xFFB03B37, 0xFF8D6969, 0x00000000, 0x00000000, + 0xFF818181, 0xFFFFFFFF, 0xFFB9B9B9, 0xFFC4C4C4, 0xFFC4C4C4, 0xFF9E403D, 0xFFA70A07, 0xFFC66453, 0xFFCB715C, 0xFFC9735D, 0xFFC5715B, 0xFFBF6C59, 0xFFC06E61, 0xFFAC201D, 0xC17E2927, 0x19191919, + 0xFF818181, 0xFFFFFFFF, 0xFFB2B2B2, 0xFFB9B9B9, 0xFFA65C5B, 0xFFAF2017, 0xFFCB725D, 0xFFC7654D, 0xFFBB5338, 0xFFB65136, 0xFFB04E35, 0xFFB35D47, 0xFFAE5B45, 0xFFA95743, 0xFFAA2F28, 0xA1641716, + 0xFF818181, 0xFFFFFFFF, 0xFFB9B9B9, 0xFFC4C4C4, 0xFFA51412, 0xFFCA6F5A, 0xFFBF5439, 0xFFBA5237, 0xFFB44F36, 0xFFAF4D34, 0xFF886556, 0xFF765F5A, 0xFF715B56, 0xFF6B5853, 0xFF77605B, 0xF7980C0B, + 0xFF818181, 0xFFFFFFFF, 0xFFB2B2B2, 0xFFB9B9B9, 0xFFA40201, 0xFFC76951, 0xFFB85137, 0xFFB24E36, 0xFFAD4C34, 0xFFAE653A, 0xFF95995D, 0xFF1D84A0, 0xFF177F99, 0xFF3D91A5, 0xFF3B8EA0, 0xFFA20101, + 0xFF818181, 0xFFFFFFFF, 0xFFB9B9B9, 0xFFC4C4C4, 0xFF9F1212, 0xFFCA7E6D, 0xFFB85F48, 0xFFAB4C33, 0xFFA64932, 0xFFB18B44, 0xFFB8BC50, 0xFF358086, 0xFF509CAD, 0xFF278397, 0xFF496E77, 0xF7920808, + 0xFF818181, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFBC7878, 0xFF9C1D1A, 0xFFBD7E6D, 0xFFBE7F70, 0xFFBD8374, 0xFFCDC787, 0xFFCBD089, 0xFFB4B48A, 0xFF5C93A0, 0xFF44656D, 0xFF7B1618, 0xA1661D1D, + 0xB4696969, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF934040, 0xFF9E0605, 0xFF99463D, 0xFF9E624D, 0xFFA6A14C, 0xFF9F9B4A, 0xFF948845, 0xFF4B3C43, 0xFF9A0505, 0xBA771F1F, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x55351616, 0xD37F1717, 0xF99C0B0B, 0xFFA30201, 0xFFA20101, 0xF99C0B0B, 0xD3881E1E, 0x55412222, 0x00000000, 0x00000000, }; static const uint32_t dat3[] = { @@ -140,33 +140,33 @@ static const uint32_t dat3[] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF777777, 0xFFFBFBFB, 0xFFE4E4E4, 0xFF979797, 0xFFB3B3B3, 0xFFB4B4B4, 0xFFB5B5B5, 0xFFB6B6B6, 0xFF999999, 0xFFE7E7E7, 0xFFE8E8E8, 0xFFE9E9E9, 0xFFC4C4C4, 0xFFEAEAEA, 0xFFEAEAEA, 0xFFEBEBEB, 0xFFC6C6C6, 0xFFEBEBEB, 0xFFEBEBEB, 0xFFEBEBEB, 0xFFC6C6C6, 0xFFEEEEEE, 0xFFF8F8F8, 0xFF525252, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF737373, 0xFFFBFBFB, 0xFFE4E4E4, 0xFF7D7D7D, 0xFF949494, 0xFF959595, 0xFF969696, 0xFF979797, 0xFF7F7F7F, 0xFFC1C1C1, 0xFFC1C1C1, 0xFFC2C2C2, - 0xFFA2A2A3, 0xFF9191AB, 0xFF606094, 0xFF3D3D84, 0xFF252578, 0xFF272780, 0xFF2B2B7E, 0xFF3D3D84, 0xFF525286, 0xFFB2B2CC, 0xFFF5F5F6, 0xFF505050, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF707070, 0xFFFBFBFB, 0xFFE5E5E5, 0xFF979797, 0xFFB4B4B4, 0xFFB5B5B5, 0xFFB6B6B6, 0xFFB6B6B6, 0xFF9A9A9A, 0xFFE9E9E9, 0xFFE4E4E6, 0xFF8585B1, - 0xFF1B1B7A, 0xFF2F2FA6, 0xFF5050D3, 0xFF6767F2, 0xFF7070FF, 0xFF6E6EFE, 0xFF6969FC, 0xFF5B5BED, 0xFF4343CD, 0xFF2525A2, 0xFF1D1D7E, 0xFF2C2C58, 0x06000002, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF6D6D6D, 0xFFFBFBFB, 0xFFE5E5E5, 0xFF7D7D7D, 0xFF959595, 0xFF969696, 0xFF979797, 0xFF979797, 0xFF808080, 0xFFABABB7, 0xFF28287B, 0xFF3434AC, - 0xFF6A6AF5, 0xFF7070FF, 0xFF7070FF, 0xFF6F6FFE, 0xFF6A6AFC, 0xFF6565FA, 0xFF6060F8, 0xFF5C5CF5, 0xFF5757F3, 0xFF5252F1, 0xFF4848E6, 0xFF1F1FA1, 0xCB000053, 0x1D00000C, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF6A6A6A, 0xFFFAFAFA, 0xFFE6E6E6, 0xFF989898, 0xFFB5B5B5, 0xFFB6B6B6, 0xFFB6B6B6, 0xFFB7B7B7, 0xFF929298, 0xFF26267E, 0xFF5050CB, 0xFF7474FF, - 0xFF7070FF, 0xFF7070FF, 0xFF6B6BFD, 0xFF6767FA, 0xFF6262F8, 0xFF5D5DF6, 0xFF5858F4, 0xFF5353F1, 0xFF4E4EEF, 0xFF4949ED, 0xFF4444EB, 0xFF3F3FE9, 0xFF2424B6, 0xD7000057, 0x0F000006, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF666666, 0xFFFAFAFA, 0xFFE6E6E6, 0xFF7E7E7E, 0xFF969696, 0xFF979797, 0xFF979797, 0xFF989898, 0xFF3D3D73, 0xFF3737A6, 0xFF7979FF, 0xFF7272FF, - 0xFF6D6DFD, 0xFF6868FB, 0xFF6363F9, 0xFF5E5EF6, 0xFF5959F4, 0xFF5454F2, 0xFF4F4FF0, 0xFF4A4AEE, 0xFF4545EB, 0xFF4141E9, 0xFF3C3CE7, 0xFF3737E5, 0xFF3535E2, 0xFF131391, 0x86000036, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF636363, 0xFFFAFAFA, 0xFFE6E6E6, 0xFF989898, 0xFFB5B5B5, 0xFFB6B6B6, 0xFFB7B7B7, 0xFFB8B8B8, 0xFF15156E, 0xFF6D6DDF, 0xFF7575FE, 0xFF6969FB, - 0xFF6464F9, 0xFF5F5FF7, 0xFF5A5AF5, 0xFF5555F3, 0xFF5050F0, 0xFF4C4CEE, 0xFF4747EC, 0xFF4242EA, 0xFF3D3DE7, 0xFF3838E5, 0xFF3333E3, 0xFF2E2EE1, 0xFF2D2DDF, 0xFF2828C2, 0xDE000059, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF606060, 0xFFF9F9F9, 0xFFE6E6E6, 0xFF7E7E7E, 0xFF979797, 0xFF979797, 0xFF989898, 0xFF989898, 0xFF010167, 0xFF7B7BED, 0xFF6A6AFA, 0xFF6060F8, - 0xFF5B5BF5, 0xFF5656F3, 0xFF5252F1, 0xFF4D4DEF, 0xFF4848EC, 0xFF7884CD, 0xFFAE7C77, 0xFFAD7B77, 0xFFA77572, 0xFFA16F6B, 0xFF9B6967, 0xFF966360, 0xFF91605E, 0xFF825E7E, 0xFC000066, 0x02000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF5D5D5D, 0xFFF9F9F9, 0xFFE6E6E6, 0xFF999999, 0xFFB6B6B6, 0xFFB6B6B6, 0xFFB7B7B7, 0xFFB8B8B8, 0xFF000067, 0xFF6262D8, 0xFF6D6DF7, 0xFF5858F4, - 0xFF5353F1, 0xFF4E4EEF, 0xFF4949ED, 0xFF4444EB, 0xFF4143E8, 0xFF8CE1C5, 0xFF9F938A, 0xFFC78958, 0xFFC18251, 0xFFBA7C4B, 0xFFB47544, 0xFFAE6E3D, 0xFFAD7245, 0xFF87606A, 0xFF000067, 0x0E000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF595959, 0xFFF9F9F9, 0xFFE7E7E7, 0xFF7E7E7E, 0xFF979797, 0xFF979797, 0xFF989898, 0xFF989898, 0xFF0D0D69, 0xFF2424A3, 0xFF7B7BEF, 0xFF5A5AF1, - 0xFF4A4AEE, 0xFF4545EB, 0xFF4040E9, 0xFF3C3CE7, 0xFF4D6BD8, 0xFF73E7AD, 0xFF5FC3AB, 0xFFC08555, 0xFFBD7E4D, 0xFFB77847, 0xFFB07140, 0xFFAE7243, 0xFFA67A6B, 0xFF674254, 0xE800005C, 0x16000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF565656, 0xFFF8F8F8, 0xFFE7E7E7, 0xFF999999, 0xFFB6B6B6, 0xFFB6B6B6, 0xFFB7B7B7, 0xFFB8B8B8, 0xFF393977, 0xFF09097A, 0xFF3232BB, 0xFF7171E8, - 0xFF6161ED, 0xFF4242E8, 0xFF3838E5, 0xFF3333E3, 0xFF4E9FBA, 0xFF53E099, 0xFF43DC8F, 0xFF668A84, 0xFFB97A49, 0xFFB47747, 0xFFB98258, 0xFFA67F79, 0xFF905E45, 0xFF231765, 0xAC00003E, 0x13000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF535353, 0xFFF8F8F8, 0xFFE6E6E6, 0xFF7E7E7E, 0xFF979797, 0xFF979797, 0xFF989898, 0xFF989898, 0xFF6B6B76, 0xFF0E0E6D, 0xFF111188, 0xFF2323B0, - 0xFF4545CA, 0xFF6666E2, 0xFF5D5DE8, 0xFF4B4FE2, 0xFF56D6A1, 0xFF47DC91, 0xFF3CD98B, 0xFF3AC990, 0xFFBB9474, 0xFFB48E84, 0xFF8E6870, 0xFF925E3B, 0xFF422960, 0xEF00005F, 0x4000000B, 0x08000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF4F4F4F, 0xFFF8F8F8, 0xFFE6E6E6, 0xFFE5E5E5, 0xFFE6E6E6, 0xFFE7E7E7, 0xFFE8E8E8, 0xFFE9E9E9, 0xFFE6E6E6, 0xFFA9A9BD, 0xFF151571, 0xFF0C0C7B, - 0xFF1D1DA2, 0xFF1B1BAD, 0xFF2727B6, 0xFF334FB2, 0xFF40A693, 0xFF40A896, 0xFF3CA791, 0xFF339F86, 0xFF5B676E, 0xFF935C37, 0xFF754D54, 0xFF23186B, 0xEB00005C, 0x59000013, 0x16000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x0D000000, 0xF14C4C4C, 0xFFF7F7F7, 0xFFF6F6F6, 0xFFF6F6F6, 0xFFF6F6F6, 0xFFF7F7F7, 0xFFF7F7F7, 0xFFF7F7F7, 0xFFF7F7F7, 0xFFF0F0F0, 0xFFC9C9D1, 0xFF535390, - 0xFF05056A, 0xFF0A0A77, 0xFF14148E, 0xFF1C3391, 0xFF238772, 0xFF249070, 0xFF238F6E, 0xFF1F7C73, 0xFF313C70, 0xFF1B146C, 0xFF05056A, 0xFB161653, 0x52000008, 0x1D000000, 0x01000000, 0x00000000, + 0xFFA3A2A2, 0xFFAB9191, 0xFF946060, 0xFF843D3D, 0xFF782525, 0xFF802727, 0xFF7E2B2B, 0xFF843D3D, 0xFF865252, 0xFFCCB2B2, 0xFFF6F5F5, 0xFF505050, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF707070, 0xFFFBFBFB, 0xFFE5E5E5, 0xFF979797, 0xFFB4B4B4, 0xFFB5B5B5, 0xFFB6B6B6, 0xFFB6B6B6, 0xFF9A9A9A, 0xFFE9E9E9, 0xFFE6E4E4, 0xFFB18585, + 0xFF7A1B1B, 0xFFA62F2F, 0xFFD35050, 0xFFF26767, 0xFFFF7070, 0xFFFE6E6E, 0xFFFC6969, 0xFFED5B5B, 0xFFCD4343, 0xFFA22525, 0xFF7E1D1D, 0xFF582C2C, 0x06020000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF6D6D6D, 0xFFFBFBFB, 0xFFE5E5E5, 0xFF7D7D7D, 0xFF959595, 0xFF969696, 0xFF979797, 0xFF979797, 0xFF808080, 0xFFB7ABAB, 0xFF7B2828, 0xFFAC3434, + 0xFFF56A6A, 0xFFFF7070, 0xFFFF7070, 0xFFFE6F6F, 0xFFFC6A6A, 0xFFFA6565, 0xFFF86060, 0xFFF55C5C, 0xFFF35757, 0xFFF15252, 0xFFE64848, 0xFFA11F1F, 0xCB530000, 0x1D0C0000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF6A6A6A, 0xFFFAFAFA, 0xFFE6E6E6, 0xFF989898, 0xFFB5B5B5, 0xFFB6B6B6, 0xFFB6B6B6, 0xFFB7B7B7, 0xFF989292, 0xFF7E2626, 0xFFCB5050, 0xFFFF7474, + 0xFFFF7070, 0xFFFF7070, 0xFFFD6B6B, 0xFFFA6767, 0xFFF86262, 0xFFF65D5D, 0xFFF45858, 0xFFF15353, 0xFFEF4E4E, 0xFFED4949, 0xFFEB4444, 0xFFE93F3F, 0xFFB62424, 0xD7570000, 0x0F060000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF666666, 0xFFFAFAFA, 0xFFE6E6E6, 0xFF7E7E7E, 0xFF969696, 0xFF979797, 0xFF979797, 0xFF989898, 0xFF733D3D, 0xFFA63737, 0xFFFF7979, 0xFFFF7272, + 0xFFFD6D6D, 0xFFFB6868, 0xFFF96363, 0xFFF65E5E, 0xFFF45959, 0xFFF25454, 0xFFF04F4F, 0xFFEE4A4A, 0xFFEB4545, 0xFFE94141, 0xFFE73C3C, 0xFFE53737, 0xFFE23535, 0xFF911313, 0x86360000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF636363, 0xFFFAFAFA, 0xFFE6E6E6, 0xFF989898, 0xFFB5B5B5, 0xFFB6B6B6, 0xFFB7B7B7, 0xFFB8B8B8, 0xFF6E1515, 0xFFDF6D6D, 0xFFFE7575, 0xFFFB6969, + 0xFFF96464, 0xFFF75F5F, 0xFFF55A5A, 0xFFF35555, 0xFFF05050, 0xFFEE4C4C, 0xFFEC4747, 0xFFEA4242, 0xFFE73D3D, 0xFFE53838, 0xFFE33333, 0xFFE12E2E, 0xFFDF2D2D, 0xFFC22828, 0xDE590000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF606060, 0xFFF9F9F9, 0xFFE6E6E6, 0xFF7E7E7E, 0xFF979797, 0xFF979797, 0xFF989898, 0xFF989898, 0xFF670101, 0xFFED7B7B, 0xFFFA6A6A, 0xFFF86060, + 0xFFF55B5B, 0xFFF35656, 0xFFF15252, 0xFFEF4D4D, 0xFFEC4848, 0xFFCD8478, 0xFF777CAE, 0xFF777BAD, 0xFF7275A7, 0xFF6B6FA1, 0xFF67699B, 0xFF606396, 0xFF5E6091, 0xFF7E5E82, 0xFC660000, 0x02000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF5D5D5D, 0xFFF9F9F9, 0xFFE6E6E6, 0xFF999999, 0xFFB6B6B6, 0xFFB6B6B6, 0xFFB7B7B7, 0xFFB8B8B8, 0xFF670000, 0xFFD86262, 0xFFF76D6D, 0xFFF45858, + 0xFFF15353, 0xFFEF4E4E, 0xFFED4949, 0xFFEB4444, 0xFFE84341, 0xFFC5E18C, 0xFF8A939F, 0xFF5889C7, 0xFF5182C1, 0xFF4B7CBA, 0xFF4475B4, 0xFF3D6EAE, 0xFF4572AD, 0xFF6A6087, 0xFF670000, 0x0E000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF595959, 0xFFF9F9F9, 0xFFE7E7E7, 0xFF7E7E7E, 0xFF979797, 0xFF979797, 0xFF989898, 0xFF989898, 0xFF690D0D, 0xFFA32424, 0xFFEF7B7B, 0xFFF15A5A, + 0xFFEE4A4A, 0xFFEB4545, 0xFFE94040, 0xFFE73C3C, 0xFFD86B4D, 0xFFADE773, 0xFFABC35F, 0xFF5585C0, 0xFF4D7EBD, 0xFF4778B7, 0xFF4071B0, 0xFF4372AE, 0xFF6B7AA6, 0xFF544267, 0xE85C0000, 0x16000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF565656, 0xFFF8F8F8, 0xFFE7E7E7, 0xFF999999, 0xFFB6B6B6, 0xFFB6B6B6, 0xFFB7B7B7, 0xFFB8B8B8, 0xFF773939, 0xFF7A0909, 0xFFBB3232, 0xFFE87171, + 0xFFED6161, 0xFFE84242, 0xFFE53838, 0xFFE33333, 0xFFBA9F4E, 0xFF99E053, 0xFF8FDC43, 0xFF848A66, 0xFF497AB9, 0xFF4777B4, 0xFF5882B9, 0xFF797FA6, 0xFF455E90, 0xFF651723, 0xAC3E0000, 0x13000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF535353, 0xFFF8F8F8, 0xFFE6E6E6, 0xFF7E7E7E, 0xFF979797, 0xFF979797, 0xFF989898, 0xFF989898, 0xFF766B6B, 0xFF6D0E0E, 0xFF881111, 0xFFB02323, + 0xFFCA4545, 0xFFE26666, 0xFFE85D5D, 0xFFE24F4B, 0xFFA1D656, 0xFF91DC47, 0xFF8BD93C, 0xFF90C93A, 0xFF7494BB, 0xFF848EB4, 0xFF70688E, 0xFF3B5E92, 0xFF602942, 0xEF5F0000, 0x400B0000, 0x08000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF4F4F4F, 0xFFF8F8F8, 0xFFE6E6E6, 0xFFE5E5E5, 0xFFE6E6E6, 0xFFE7E7E7, 0xFFE8E8E8, 0xFFE9E9E9, 0xFFE6E6E6, 0xFFBDA9A9, 0xFF711515, 0xFF7B0C0C, + 0xFFA21D1D, 0xFFAD1B1B, 0xFFB62727, 0xFFB24F33, 0xFF93A640, 0xFF96A840, 0xFF91A73C, 0xFF869F33, 0xFF6E675B, 0xFF375C93, 0xFF544D75, 0xFF6B1823, 0xEB5C0000, 0x59130000, 0x16000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x0D000000, 0xF14C4C4C, 0xFFF7F7F7, 0xFFF6F6F6, 0xFFF6F6F6, 0xFFF6F6F6, 0xFFF7F7F7, 0xFFF7F7F7, 0xFFF7F7F7, 0xFFF7F7F7, 0xFFF0F0F0, 0xFFD1C9C9, 0xFF905353, + 0xFF6A0505, 0xFF770A0A, 0xFF8E1414, 0xFF91331C, 0xFF728723, 0xFF709024, 0xFF6E8F23, 0xFF737C1F, 0xFF703C31, 0xFF6C141B, 0xFF6A0505, 0xFB531616, 0x52080000, 0x1D000000, 0x01000000, 0x00000000, 0x00000000, 0x00000000, 0x0D000000, 0x22000000, 0x78191919, 0xF6535353, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF555555, 0xFF4F4F4F, - 0xFF43434B, 0xFF282852, 0xFF16165A, 0xFF0A0A61, 0xFF040465, 0xFF000067, 0xFF040465, 0xFF090960, 0xFF151559, 0xFF262650, 0xF93C3C45, 0x93151515, 0x3A000000, 0x1B000000, 0x02000000, 0x00000000, + 0xFF4B4343, 0xFF522828, 0xFF5A1616, 0xFF610A0A, 0xFF650404, 0xFF670000, 0xFF650404, 0xFF600909, 0xFF591515, 0xFF502626, 0xF9453C3C, 0x93151515, 0x3A000000, 0x1B000000, 0x02000000, 0x00000000, 0x00000000, 0x00000000, 0x06000000, 0x1D000000, 0x30000000, 0x40000000, 0x47000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4C000000, 0x54000000, 0x5E000000, 0x65000000, 0x69000000, 0x6B000000, 0x6B000000, 0x69000000, 0x63000000, 0x52000000, 0x4B000000, 0x3B000000, 0x29000000, 0x0C000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x07000000, 0x0F000000, 0x15000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, diff --git a/test/images/gen.go b/test/images/gen.go index cdf8732d..910abc74 100644 --- a/test/images/gen.go +++ b/test/images/gen.go @@ -58,9 +58,9 @@ func main() { } else { fmt.Printf(" ") } - d := uint32(im.data[j + 2]) << 16 + d := uint32(im.data[j + 0]) << 16 d |= uint32(im.data[j + 1]) << 8 - d |= uint32(im.data[j + 0]) + d |= uint32(im.data[j + 2]) d |= uint32(im.data[j + 3]) << 24 fmt.Printf("0x%08X,", d) From 8b04f2062a3075904910f36a75d37f0888bab919 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 27 Jun 2016 19:12:08 -0400 Subject: [PATCH 0430/1329] More GTK+ uiTable work. --- unix/table.c | 121 +++++++++++++++++++++++++++++---------------------- 1 file changed, 69 insertions(+), 52 deletions(-) diff --git a/unix/table.c b/unix/table.c index e0c61eae..e095cc18 100644 --- a/unix/table.c +++ b/unix/table.c @@ -313,8 +313,9 @@ struct tablePart { int textColumn; int imageColumn; int valueColumn; + int colorColumn; + GtkCellRenderer *r; uiTable *tv; // for pixbufs and background color - int editable; }; struct uiTableColumn { @@ -353,22 +354,37 @@ static void setImageSize(GtkCellRenderer *r) 2 * ypad + size); } +static void applyColor(GtkTreeModel *mm, GtkTreeIter *iter, int modelColumn, GtkCellRenderer *r, const char *prop, const char *propSet) +{ + GValue value = G_VALUE_INIT; + GdkRGBA *rgba; + + gtk_tree_model_get_value(mm, iter, modelColumn, &value); + rgba = (GdkRGBA *) g_value_get_boxed(&value); + if (rgba != NULL) + g_object_set(r, prop, rgba, NULL); + else + g_object_set(r, propSet, FALSE, NULL); + g_value_unset(&value); +} + static void dataFunc(GtkTreeViewColumn *c, GtkCellRenderer *r, GtkTreeModel *mm, GtkTreeIter *iter, gpointer data) { struct tablePart *part = (struct tablePart *) data; GValue value = G_VALUE_INIT; const gchar *str; uiImage *img; + int pval; switch (part->type) { case partText: gtk_tree_model_get_value(mm, iter, part->textColumn, &value); str = g_value_get_string(&value); g_object_set(r, "text", str, NULL); - if (part->editable) - g_object_set(r, "mode", GTK_CELL_RENDERER_MODE_EDITABLE, NULL); - else - g_object_set(r, "mode", GTK_CELL_RENDERER_MODE_INERT, NULL); + if (part->colorColumn != -1) + applyColor(mm, iter, + part->colorColumn, + r, "foreground-rgba", "foreground-set"); break; case partImage: //TODO setImageSize(r); @@ -377,43 +393,41 @@ static void dataFunc(GtkTreeViewColumn *c, GtkCellRenderer *r, GtkTreeModel *mm, g_object_set(r, "surface", imageAppropriateSurface(img, part->tv->treeWidget), NULL); - g_object_set(r, "mode", GTK_CELL_RENDERER_MODE_INERT, NULL); break; case partButton: gtk_tree_model_get_value(mm, iter, part->textColumn, &value); // TODO - if (part->editable) - g_object_set(r, "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL); - else - g_object_set(r, "mode", GTK_CELL_RENDERER_MODE_INERT, NULL); break; case partCheckbox: gtk_tree_model_get_value(mm, iter, part->valueColumn, &value); g_object_set(r, "active", g_value_get_int(&value) != 0, NULL); - if (part->editable) - g_object_set(r, "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL); - else - g_object_set(r, "mode", GTK_CELL_RENDERER_MODE_INERT, NULL); break; case partProgressBar: gtk_tree_model_get_value(mm, iter, part->valueColumn, &value); - // TODO - g_object_set(r, "mode", GTK_CELL_RENDERER_MODE_INERT, NULL); + pval = g_value_get_int(&value); + if (pval == -1) { + // TODO + } else + g_object_set(r, + "pulse", -1, + "value", pval, + NULL); break; } g_value_unset(&value); - if (part->tv->backgroundColumn != -1) { - GdkRGBA *rgba; + if (part->tv->backgroundColumn != -1) + applyColor(mm, iter, + part->tv->backgroundColumn, + r, "cell-background-rgba", "cell-background-set"); +} - gtk_tree_model_get_value(mm, iter, part->tv->backgroundColumn, &value); - rgba = (GdkRGBA *) g_value_get_boxed(&value); - if (rgba != NULL) - g_object_set(r, "cell-background-rgba", rgba, NULL); - else - g_object_set(r, "cell-background-set", FALSE, NULL); - g_value_unset(&value); - } +static void appendPart(uiTableColumn *c, struct tablePart *part, GtkCellRenderer *r, int expand) +{ + part->r = r; + gtk_tree_view_column_pack_start(c->c, part->r, expand != 0); + gtk_tree_view_column_set_cell_data_func(c->c, part->r, dataFunc, part, NULL); + g_ptr_array_add(c->parts, part); } void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand) @@ -425,32 +439,26 @@ void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand) part->type = partText; part->textColumn = modelColumn; part->tv = c->tv; - part->editable = 0; + part->colorColumn = -1; r = gtk_cell_renderer_text_new(); - gtk_tree_view_column_pack_start(c->c, r, expand != 0); - gtk_tree_view_column_set_cell_data_func(c->c, r, dataFunc, part, NULL); + g_object_set(r, "editable", FALSE, NULL); // TODO editing signal - g_ptr_array_add(c->parts, part); + appendPart(c, part, r, expand); } void uiTableColumnAppendImagePart(uiTableColumn *c, int modelColumn, int expand) { struct tablePart *part; - GtkCellRenderer *r; part = uiNew(struct tablePart); part->type = partImage; part->imageColumn = modelColumn; part->tv = c->tv; - part->editable = 0; - - r = gtk_cell_renderer_pixbuf_new(); - gtk_tree_view_column_pack_start(c->c, r, expand != 0); - gtk_tree_view_column_set_cell_data_func(c->c, r, dataFunc, part, NULL); - - g_ptr_array_add(c->parts, part); + appendPart(c, part, + gtk_cell_renderer_pixbuf_new(), + expand); } void uiTableColumnAppendButtonPart(uiTableColumn *c, int modelColumn, int expand) @@ -467,42 +475,51 @@ void uiTableColumnAppendCheckboxPart(uiTableColumn *c, int modelColumn, int expa part->type = partCheckbox; part->valueColumn = modelColumn; part->tv = c->tv; - part->editable = 1; // editable by default r = gtk_cell_renderer_toggle_new(); - gtk_tree_view_column_pack_start(c->c, r, expand != 0); - gtk_tree_view_column_set_cell_data_func(c->c, r, dataFunc, part, NULL); + g_object_set(r, "sensitive", TRUE, NULL); // editable by default // TODO editing signal - g_ptr_array_add(c->parts, part); + appendPart(c, part, r, expand); } void uiTableColumnAppendProgressBarPart(uiTableColumn *c, int modelColumn, int expand) { struct tablePart *part; - GtkCellRenderer *r; part = uiNew(struct tablePart); part->type = partProgressBar; part->valueColumn = modelColumn; part->tv = c->tv; - part->editable = 0; - - r = gtk_cell_renderer_progress_new(); - gtk_tree_view_column_pack_start(c->c, r, expand != 0); - gtk_tree_view_column_set_cell_data_func(c->c, r, dataFunc, part, NULL); - - g_ptr_array_add(c->parts, part); + appendPart(c, part, + gtk_cell_renderer_progress_new(), + expand); } void uiTableColumnPartSetEditable(uiTableColumn *c, int part, int editable) { - // TODO + struct tablePart *p; + + p = (struct tablePart *) g_ptr_array_index(c->parts, part); + switch (p->type) { + case partImage: + case partProgressBar: + return; + case partButton: + case partCheckbox: + g_object_set(p->r, "sensitive", editable != 0, NULL); + return; + } + g_object_set(p->r, "editable", editable != 0, NULL); } void uiTableColumnPartSetTextColor(uiTableColumn *c, int part, int modelColumn) { - // TODO + struct tablePart *p; + + p = (struct tablePart *) g_ptr_array_index(c->parts, part); + p->colorColumn = modelColumn; + // TODO refresh table } uiUnixControlAllDefaultsExceptDestroy(uiTable) From 575f4f3053e9cac28642c7a21ceff5eb95728174 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 28 Jun 2016 12:00:35 -0400 Subject: [PATCH 0431/1329] Added a button cell renderer. No events yet. Now to test. --- unix/CMakeLists.txt | 1 + unix/cellrendererbutton.c | 231 ++++++++++++++++++++++++++++++++++++++ unix/uipriv_unix.h | 3 + 3 files changed, 235 insertions(+) create mode 100644 unix/cellrendererbutton.c diff --git a/unix/CMakeLists.txt b/unix/CMakeLists.txt index 14c957df..10d3d833 100644 --- a/unix/CMakeLists.txt +++ b/unix/CMakeLists.txt @@ -8,6 +8,7 @@ list(APPEND _LIBUI_SOURCES unix/area.c unix/box.c unix/button.c + unix/cellrendererbutton.c unix/checkbox.c unix/child.c unix/colorbutton.c diff --git a/unix/cellrendererbutton.c b/unix/cellrendererbutton.c new file mode 100644 index 00000000..acb75f27 --- /dev/null +++ b/unix/cellrendererbutton.c @@ -0,0 +1,231 @@ +// 28 june 2016 +#include "uipriv_unix.h" + +#define cellRendererButtonType (cellRendererButton_get_type()) +#define cellRendererButton(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), cellRendererButtonType, cellRendererButton)) +#define isCellRendererButton(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), cellRendererButtonType)) +#define cellRendererButtonClass(class) (G_TYPE_CHECK_CLASS_CAST((class), cellRendererButtonType, cellRendererButtonClass)) +#define isCellRendererButtonClass(class) (G_TYPE_CHECK_CLASS_TYPE((class), cellRendererButton)) +#define getCellRendererButtonClass(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), cellRendererButtonType, cellRendererButtonClass)) + +typedef struct cellRendererButton cellRendererButton; +typedef struct cellRendererButtonClass cellRendererButtonClass; + +struct cellRendererButton { + GtkCellRenderer parent_instance; + char *text; +}; + +struct cellRendererButtonClass { + GtkCellRendererClass parent_class; +}; + +G_DEFINE_TYPE(cellRendererButton, cellRendererButton, GTK_TYPE_CELL_RENDERER) + +static void cellRendererButton_init(cellRendererButton *m) +{ + // nothing to do +} + +static void cellRendererButton_dispose(GObject *obj) +{ + G_OBJECT_CLASS(cellRendererButton_parent_class)->dispose(obj); +} + +static void cellRendererButton_finalize(GObject *obj) +{ + cellRendererButton *c = cellRendererButton(obj); + + if (c->text != NULL) { + g_free(c->text); + c->text = NULL; + } + G_OBJECT_CLASS(cellRendererButton_parent_class)->finalize(obj); +} + +static GtkSizeRequestMode cellRendererButton_get_request_mode(GtkCellRenderer *r) +{ + return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH; +} + +// this is basically what GtkCellRendererToggle did in 3.10 +static GtkStyleContext *setButtonStyle(GtkWidget *widget) +{ + GtkStyleContext *context; + + context = gtk_widget_get_style_context(widget); + gtk_style_context_save(context); + gtk_style_context_add_class(context, GTK_STYLE_CLASS_BUTTON); + return context; +} + +// this is based on what GtkCellRendererText does +static void cellRendererButton_get_preferred_width(GtkCellRenderer *r, GtkWidget *widget, gint *minimum, gint *natural) +{ + cellRendererButton *c = cellRendererButton(r); + gint xpad; + GtkStyleContext *context; + PangoLayout *layout; + PangoRectangle rect; + gint out; + + gtk_cell_renderer_get_padding(GTK_CELL_RENDERER(c), &xpad, NULL); + + context = setButtonStyle(widget); + layout = gtk_widget_create_pango_layout(widget, c->text); + pango_layout_set_width(layout, -1); + pango_layout_get_extents(layout, NULL, &rect); + g_object_unref(layout); + gtk_style_context_restore(context); + + out = 2 * xpad + PANGO_PIXELS_CEIL(rect.width); + if (minimum != NULL) + *minimum = out; + if (natural != NULL) + *natural = out; +} + +// this is based on what GtkCellRendererText does +static void cellRendererButton_get_preferred_height_for_width(GtkCellRenderer *r, GtkWidget *widget, gint width, gint *minimum, gint *natural) +{ + cellRendererButton *c = cellRendererButton(r); + gint xpad, ypad; + GtkStyleContext *context; + PangoLayout *layout; + gint height; + gint out; + + gtk_cell_renderer_get_padding(GTK_CELL_RENDERER(c), &xpad, &ypad); + + context = setButtonStyle(widget); + layout = gtk_widget_create_pango_layout(widget, c->text); + pango_layout_set_width(layout, ((2 * xpad + width) * PANGO_SCALE)); + pango_layout_get_pixel_size(layout, NULL, &height); + g_object_unref(layout); + gtk_style_context_restore(context); + + out = 2 * ypad + height; + if (minimum != NULL) + *minimum = out; + if (natural != NULL) + *natural = out; +} + +// this is basically what GtkCellRendererText does +static void cellRendererButton_get_preferred_height(GtkCellRenderer *r, GtkWidget *widget, gint *minimum, gint *natural) +{ + gint width; + + gtk_cell_renderer_get_preferred_width(r, widget, &width, NULL); + gtk_cell_renderer_get_preferred_height_for_width(r, widget, width, minimum, natural); +} + +// this is based on what GtkCellRendererText does +static void cellRendererButton_get_aligned_area(GtkCellRenderer *r, GtkWidget *widget, GtkCellRendererState flags, const GdkRectangle *cell_area, GdkRectangle *aligned_area) +{ + cellRendererButton *c = cellRendererButton(r); + gint xpad, ypad; + GtkStyleContext *context; + PangoLayout *layout; + PangoRectangle rect; + gfloat xalign, yalign; + gint xoffset, yoffset; + + gtk_cell_renderer_get_padding(GTK_CELL_RENDERER(c), &xpad, &ypad); + + context = setButtonStyle(widget); + layout = gtk_widget_create_pango_layout(widget, c->text); + pango_layout_set_width(layout, -1); + pango_layout_get_pixel_extents(layout, NULL, &rect); + + xoffset = 0; + yoffset = 0; + if (cell_area != NULL) { + gtk_cell_renderer_get_alignment(GTK_CELL_RENDERER(c), &xalign, &yalign); + xoffset = cell_area->width - (2 * xpad + rect.width); + // use explicit casts just to be safe + if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL) + xoffset = ((gdouble) xoffset) * (1.0 - xalign); + else + xoffset *= ((gdouble) xoffset) * xalign; + yoffset = yalign * (cell_area->height - (2 * ypad + rect.height)); + yoffset = MAX(yoffset, 0); + } + + aligned_area->x = cell_area->x + xoffset; + aligned_area->y = cell_area->y + yoffset; + aligned_area->width = 2 * xpad + rect.width; + aligned_area->height = 2 * ypad + rect.height; + + g_object_unref(layout); + gtk_style_context_restore(context); +} + +// this is based on both what GtkCellRendererText does and what GtkCellRendererToggle does +static void cellRendererButton_render(GtkCellRenderer *r, cairo_t *cr, GtkWidget *widget, const GdkRectangle *background_area, const GdkRectangle *cell_area, GtkCellRendererState flags) +{ + cellRendererButton *c = cellRendererButton(r); + gint xpad, ypad; + GdkRectangle alignedArea; + gint xoffset, yoffset; + GtkStyleContext *context; + PangoLayout *layout; + PangoRectangle rect; + + gtk_cell_renderer_get_padding(GTK_CELL_RENDERER(c), &xpad, &ypad); + gtk_cell_renderer_get_aligned_area(GTK_CELL_RENDERER(c), widget, flags, cell_area, &alignedArea); + xoffset = alignedArea.x - cell_area->x; + yoffset = alignedArea.y - cell_area->y; + + context = setButtonStyle(widget); + layout = gtk_widget_create_pango_layout(widget, c->text); + + gtk_render_background(context, cr, + background_area->x + xoffset + xpad, + background_area->y + yoffset + ypad, + background_area->width - 2 * xpad, + background_area->height - 2 * ypad); + gtk_render_frame(context, cr, + background_area->x + xoffset + xpad, + background_area->y + yoffset + ypad, + background_area->width - 2 * xpad, + background_area->height - 2 * ypad); + + pango_layout_set_width(layout, -1); + pango_layout_get_pixel_extents(layout, NULL, &rect); + xoffset -= rect.x; + gtk_render_layout(context, cr, + cell_area->x + xoffset + xpad, + cell_area->y + yoffset + ypad, + layout); + + g_object_unref(layout); + gtk_style_context_restore(context); +} + +static gboolean cellRendererButton_activate(GtkCellRenderer *r, GdkEvent *e, GtkWidget *widget, const gchar *path, const GdkRectangle *background_area, const GdkRectangle *cell_area, GtkCellRendererState flags) +{ + // TODO + return FALSE; +} + +static void cellRendererButton_class_init(cellRendererButtonClass *class) +{ + G_OBJECT_CLASS(class)->dispose = cellRendererButton_dispose; + G_OBJECT_CLASS(class)->finalize = cellRendererButton_finalize; + GTK_CELL_RENDERER_CLASS(class)->get_request_mode = cellRendererButton_get_request_mode; + GTK_CELL_RENDERER_CLASS(class)->get_preferred_width = cellRendererButton_get_preferred_width; + GTK_CELL_RENDERER_CLASS(class)->get_preferred_height_for_width = cellRendererButton_get_preferred_height_for_width; + GTK_CELL_RENDERER_CLASS(class)->get_preferred_height = cellRendererButton_get_preferred_height; + // don't provide a get_preferred_width_for_height() + GTK_CELL_RENDERER_CLASS(class)->get_aligned_area = cellRendererButton_get_aligned_area; + // don't provide a get_size() + GTK_CELL_RENDERER_CLASS(class)->render = cellRendererButton_render; + GTK_CELL_RENDERER_CLASS(class)->activate = cellRendererButton_activate; + // don't provide a start_editing() +} + +GtkCellRenderer *newCellRendererButton(void) +{ + return GTK_CELL_RENDERER(g_object_new(cellRendererButtonType, NULL)); +} diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index d8d639dd..c246f371 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -54,3 +54,6 @@ extern ptrdiff_t *graphemes(const char *text, PangoContext *context); // image.c extern cairo_surface_t *imageAppropriateSurface(uiImage *i, GtkWidget *w); + +// cellrendererbutton.c +extern GtkCellRenderer *newCellRendererButton(void); From f484f568f64774543dd63c01d043180b26602a10 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 28 Jun 2016 13:09:44 -0400 Subject: [PATCH 0432/1329] Finished up and plugged in cellrendererbutton. --- unix/cellrendererbutton.c | 47 +++++++++++++++++++++++++++++++++++++-- unix/table.c | 17 ++++++++++++-- 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/unix/cellrendererbutton.c b/unix/cellrendererbutton.c index acb75f27..6cd8b4c9 100644 --- a/unix/cellrendererbutton.c +++ b/unix/cellrendererbutton.c @@ -1,6 +1,11 @@ // 28 june 2016 #include "uipriv_unix.h" +// TODOs +// - it's a rather tight fit +// - selected row text color is white +// - no held state? + #define cellRendererButtonType (cellRendererButton_get_type()) #define cellRendererButton(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), cellRendererButtonType, cellRendererButton)) #define isCellRendererButton(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), cellRendererButtonType)) @@ -22,9 +27,11 @@ struct cellRendererButtonClass { G_DEFINE_TYPE(cellRendererButton, cellRendererButton, GTK_TYPE_CELL_RENDERER) -static void cellRendererButton_init(cellRendererButton *m) +static void cellRendererButton_init(cellRendererButton *c) { - // nothing to do + g_object_set(c, "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL); + // the standard cell renderers all do this + gtk_cell_renderer_set_padding(GTK_CELL_RENDERER(c), 2, 2); } static void cellRendererButton_dispose(GObject *obj) @@ -209,10 +216,39 @@ static gboolean cellRendererButton_activate(GtkCellRenderer *r, GdkEvent *e, Gtk return FALSE; } +static GParamSpec *props[2] = { NULL, NULL }; + +static void cellRendererButton_set_property(GObject *object, guint prop, const GValue *value, GParamSpec *pspec) +{ + cellRendererButton *c = cellRendererButton(object); + + if (prop != 1) { + G_OBJECT_WARN_INVALID_PROPERTY_ID(c, prop, pspec); + return; + } + if (c->text != NULL) + g_free(c->text); + c->text = g_value_dup_string(value); + // GtkCellRendererText doesn't queue a redraw; we won't either +} + +static void cellRendererButton_get_property(GObject *object, guint prop, GValue *value, GParamSpec *pspec) +{ + cellRendererButton *c = cellRendererButton(object); + + if (prop != 1) { + G_OBJECT_WARN_INVALID_PROPERTY_ID(c, prop, pspec); + return; + } + g_value_set_string(value, c->text); +} + static void cellRendererButton_class_init(cellRendererButtonClass *class) { G_OBJECT_CLASS(class)->dispose = cellRendererButton_dispose; G_OBJECT_CLASS(class)->finalize = cellRendererButton_finalize; + G_OBJECT_CLASS(class)->set_property = cellRendererButton_set_property; + G_OBJECT_CLASS(class)->get_property = cellRendererButton_get_property; GTK_CELL_RENDERER_CLASS(class)->get_request_mode = cellRendererButton_get_request_mode; GTK_CELL_RENDERER_CLASS(class)->get_preferred_width = cellRendererButton_get_preferred_width; GTK_CELL_RENDERER_CLASS(class)->get_preferred_height_for_width = cellRendererButton_get_preferred_height_for_width; @@ -223,6 +259,13 @@ static void cellRendererButton_class_init(cellRendererButtonClass *class) GTK_CELL_RENDERER_CLASS(class)->render = cellRendererButton_render; GTK_CELL_RENDERER_CLASS(class)->activate = cellRendererButton_activate; // don't provide a start_editing() + + props[1] = g_param_spec_string("text", + "Text", + "Button text", + "", + G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); + g_object_class_install_properties(G_OBJECT_CLASS(class), 2, props); } GtkCellRenderer *newCellRendererButton(void) diff --git a/unix/table.c b/unix/table.c index e095cc18..7449e87a 100644 --- a/unix/table.c +++ b/unix/table.c @@ -396,7 +396,8 @@ static void dataFunc(GtkTreeViewColumn *c, GtkCellRenderer *r, GtkTreeModel *mm, break; case partButton: gtk_tree_model_get_value(mm, iter, part->textColumn, &value); - // TODO + str = g_value_get_string(&value); + g_object_set(r, "text", str, NULL); break; case partCheckbox: gtk_tree_model_get_value(mm, iter, part->valueColumn, &value); @@ -463,7 +464,19 @@ void uiTableColumnAppendImagePart(uiTableColumn *c, int modelColumn, int expand) void uiTableColumnAppendButtonPart(uiTableColumn *c, int modelColumn, int expand) { - // TODO + struct tablePart *part; + GtkCellRenderer *r; + + part = uiNew(struct tablePart); + part->type = partButton; + part->textColumn = modelColumn; + part->tv = c->tv; + + r = newCellRendererButton(); + g_object_set(r, "sensitive", TRUE, NULL); // editable by default + // TODO editing signal + + appendPart(c, part, r, expand); } void uiTableColumnAppendCheckboxPart(uiTableColumn *c, int modelColumn, int expand) From 6b0028d084c9892b10d26708cca5502906f1a052 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 28 Jun 2016 22:36:56 -0400 Subject: [PATCH 0433/1329] Added uiTable value setting and events on GTK+. --- unix/cellrendererbutton.c | 16 +++++++++-- unix/table.c | 60 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 70 insertions(+), 6 deletions(-) diff --git a/unix/cellrendererbutton.c b/unix/cellrendererbutton.c index 6cd8b4c9..0be23195 100644 --- a/unix/cellrendererbutton.c +++ b/unix/cellrendererbutton.c @@ -5,6 +5,8 @@ // - it's a rather tight fit // - selected row text color is white // - no held state? +// - top of button wrong? +// - resizing a column with a button in it crashes the program #define cellRendererButtonType (cellRendererButton_get_type()) #define cellRendererButton(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), cellRendererButtonType, cellRendererButton)) @@ -210,10 +212,12 @@ static void cellRendererButton_render(GtkCellRenderer *r, cairo_t *cr, GtkWidget gtk_style_context_restore(context); } +static guint clickedSignal; + static gboolean cellRendererButton_activate(GtkCellRenderer *r, GdkEvent *e, GtkWidget *widget, const gchar *path, const GdkRectangle *background_area, const GdkRectangle *cell_area, GtkCellRendererState flags) { - // TODO - return FALSE; + g_signal_emit(r, clickedSignal, 0, path); + return TRUE; } static GParamSpec *props[2] = { NULL, NULL }; @@ -266,6 +270,14 @@ static void cellRendererButton_class_init(cellRendererButtonClass *class) "", G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); g_object_class_install_properties(G_OBJECT_CLASS(class), 2, props); + + clickedSignal = g_signal_new("clicked", + G_TYPE_FROM_CLASS(class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, + 1, G_TYPE_STRING); } GtkCellRenderer *newCellRendererButton(void) diff --git a/unix/table.c b/unix/table.c index 7449e87a..a11844c2 100644 --- a/unix/table.c +++ b/unix/table.c @@ -332,6 +332,7 @@ struct uiTable { GtkWidget *treeWidget; GtkTreeView *tv; GPtrArray *columns; + uiTableModel *model; int backgroundColumn; }; @@ -423,6 +424,21 @@ static void dataFunc(GtkTreeViewColumn *c, GtkCellRenderer *r, GtkTreeModel *mm, r, "cell-background-rgba", "cell-background-set"); } +static void onEdited(struct tablePart *part, int column, const char *pathstr, const void *data) +{ + GtkTreePath *path; + int row; + uiTableModel *m; + + path = gtk_tree_path_new_from_string(pathstr); + row = gtk_tree_path_get_indices(path)[0]; + gtk_tree_path_free(path); + m = part->tv->model; + (*(m->mh->SetCellValue))(m->mh, m, row, column, data); + // and update + uiTableModelRowChanged(m, row); +} + static void appendPart(uiTableColumn *c, struct tablePart *part, GtkCellRenderer *r, int expand) { part->r = r; @@ -431,6 +447,13 @@ static void appendPart(uiTableColumn *c, struct tablePart *part, GtkCellRenderer g_ptr_array_add(c->parts, part); } +static void textEdited(GtkCellRendererText *renderer, gchar *path, gchar *newText, gpointer data) +{ + struct tablePart *part = (struct tablePart *) data; + + onEdited(part, part->textColumn, path, newText); +} + void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand) { struct tablePart *part; @@ -444,7 +467,7 @@ void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand) r = gtk_cell_renderer_text_new(); g_object_set(r, "editable", FALSE, NULL); - // TODO editing signal + g_signal_connect(r, "edited", G_CALLBACK(textEdited), part); appendPart(c, part, r, expand); } @@ -462,6 +485,14 @@ void uiTableColumnAppendImagePart(uiTableColumn *c, int modelColumn, int expand) expand); } +// TODO wrong type here +static void buttonClicked(GtkCellRenderer *r, gchar *pathstr, gpointer data) +{ + struct tablePart *part = (struct tablePart *) data; + + onEdited(part, part->textColumn, pathstr, NULL); +} + void uiTableColumnAppendButtonPart(uiTableColumn *c, int modelColumn, int expand) { struct tablePart *part; @@ -474,11 +505,30 @@ void uiTableColumnAppendButtonPart(uiTableColumn *c, int modelColumn, int expand r = newCellRendererButton(); g_object_set(r, "sensitive", TRUE, NULL); // editable by default - // TODO editing signal + g_signal_connect(r, "clicked", G_CALLBACK(buttonClicked), part); appendPart(c, part, r, expand); } +// yes, we need to do all this twice :| +static void checkboxToggled(GtkCellRendererToggle *r, gchar *pathstr, gpointer data) +{ + struct tablePart *part = (struct tablePart *) data; + GtkTreePath *path; + int row; + uiTableModel *m; + void *value; + int intval; + + path = gtk_tree_path_new_from_string(pathstr); + row = gtk_tree_path_get_indices(path)[0]; + gtk_tree_path_free(path); + m = part->tv->model; + value = (*(m->mh->CellValue))(m->mh, m, row, part->valueColumn); + intval = !uiTableModelTakeInt(value); + onEdited(part, part->valueColumn, pathstr, uiTableModelGiveInt(intval)); +} + void uiTableColumnAppendCheckboxPart(uiTableColumn *c, int modelColumn, int expand) { struct tablePart *part; @@ -491,7 +541,7 @@ void uiTableColumnAppendCheckboxPart(uiTableColumn *c, int modelColumn, int expa r = gtk_cell_renderer_toggle_new(); g_object_set(r, "sensitive", TRUE, NULL); // editable by default - // TODO editing signal + g_signal_connect(r, "toggled", G_CALLBACK(checkboxToggled), part); appendPart(c, part, r, expand); } @@ -552,6 +602,7 @@ uiTableColumn *uiTableAppendColumn(uiTable *t, const char *name) c = uiNew(uiTableColumn); c->c = gtk_tree_view_column_new(); + gtk_tree_view_column_set_resizable(c->c, TRUE); gtk_tree_view_column_set_title(c->c, name); gtk_tree_view_append_column(t->tv, c->c); c->tv = t; // TODO rename field to t, cascade @@ -571,6 +622,7 @@ uiTable *uiNewTable(uiTableModel *model) uiUnixNewControl(uiTable, t); + t->model = model; t->backgroundColumn = -1; t->widget = gtk_scrolled_window_new(NULL, NULL); @@ -578,7 +630,7 @@ uiTable *uiNewTable(uiTableModel *model) t->sw = GTK_SCROLLED_WINDOW(t->widget); gtk_scrolled_window_set_shadow_type(t->sw, GTK_SHADOW_IN); - t->treeWidget = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); + t->treeWidget = gtk_tree_view_new_with_model(GTK_TREE_MODEL(t->model)); t->tv = GTK_TREE_VIEW(t->treeWidget); // TODO set up t->tv From 621e301d5ff33b59d8e40c01eda29638899bb25b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 29 Jun 2016 08:53:49 -0400 Subject: [PATCH 0434/1329] Fixed backwards compatibility issues. --- darwin/table.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/darwin/table.m b/darwin/table.m index ff458caa..9e578c85 100644 --- a/darwin/table.m +++ b/darwin/table.m @@ -523,7 +523,8 @@ uiTableColumn *uiTableAppendColumn(uiTable *t, const char *name) c->c.libui_col = c; // via Interface Builder [c->c setResizingMask:(NSTableColumnAutoresizingMask | NSTableColumnUserResizingMask)]; - [c->c setTitle:toNSString(name)]; + // 10.10 adds -[NSTableColumn setTitle:]; before then we have to do this + [[c->c headerCell] setStringValue:toNSString(name)]; // TODO is this sufficient? [[c->c headerCell] setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]]; c->parts = [NSMutableArray new]; From 4fabbd18cf4a2b28a5384446de77441ffb0c6b27 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 29 Jun 2016 15:33:30 -0400 Subject: [PATCH 0435/1329] Split future symbols into a new file and added one we need to fix our button cell renderer on 3.20. --- unix/CMakeLists.txt | 1 + unix/drawtext.c | 26 ++------------------------ unix/future.c | 42 ++++++++++++++++++++++++++++++++++++++++++ unix/main.c | 1 + unix/uipriv_unix.h | 5 +++++ 5 files changed, 51 insertions(+), 24 deletions(-) create mode 100644 unix/future.c diff --git a/unix/CMakeLists.txt b/unix/CMakeLists.txt index 10d3d833..2147df24 100644 --- a/unix/CMakeLists.txt +++ b/unix/CMakeLists.txt @@ -24,6 +24,7 @@ list(APPEND _LIBUI_SOURCES unix/entry.c unix/fontbutton.c unix/form.c + unix/future.c unix/graphemes.c unix/grid.c unix/group.c diff --git a/unix/drawtext.c b/unix/drawtext.c index d12fcee9..d31c264c 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -273,25 +273,6 @@ static void addAttr(uiDrawTextLayout *layout, PangoAttribute *attr, int startCha // pango_attr_list_insert() takes attr; we don't free it } -// these attributes are only supported on 1.38 and higher; we need to support 1.36 -// use dynamic linking to make them work at least on newer systems -static PangoAttribute *(*newFGAlphaAttr)(guint16 alpha) = NULL; -static gboolean tried138 = FALSE; - -// note that we treat any error as "the 1.38 symbols aren't there" (and don't care if dlclose() failed) -static void try138(void) -{ - void *handle; - - tried138 = TRUE; - // dlsym() walks the dependency chain, so opening the current process should be sufficient - handle = dlopen(NULL, RTLD_LAZY); - if (handle == NULL) - return; - *((void **) (&newFGAlphaAttr)) = dlsym(handle, "pango_attr_foreground_alpha_new"); - dlclose(handle); -} - void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, int startChar, int endChar, double r, double g, double b, double a) { PangoAttribute *attr; @@ -305,11 +286,8 @@ void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, int startChar, int endCh attr = pango_attr_foreground_new(rr, gg, bb); addAttr(layout, attr, startChar, endChar); - if (!tried138) - try138(); // TODO what if aa == 0? - if (newFGAlphaAttr != NULL) { - attr = (*newFGAlphaAttr)(aa); + attr = FUTURE_pango_attr_foreground_alpha_new(aa); + if (attr != NULL) addAttr(layout, attr, startChar, endChar); - } } diff --git a/unix/future.c b/unix/future.c new file mode 100644 index 00000000..1f9f532b --- /dev/null +++ b/unix/future.c @@ -0,0 +1,42 @@ +// 29 june 2016 +#include "uipriv_unix.h" + +// functions FROM THE FUTURE! +// in some cases, because being held back by LTS releases sucks :/ +// in others, because parts of GTK+ being unstable until recently also sucks :/ + +// added in pango 1.38; we need 1.36 +static PangoAttribute *(*newFGAlphaAttr)(guint16 alpha) = NULL; + +// added in GTK+ 3.20; we need 3.10 +static void (*gwpIterSetObjectName)(GtkWidgetPath *path, gint pos, const char *name) = NULL; + +// note that we treat any error as "the symbols aren't there" (and don't care if dlclose() failed) +void loadFutures(void) +{ + void *handle; + + // dlsym() walks the dependency chain, so opening the current process should be sufficient + handle = dlopen(NULL, RTLD_LAZY); + if (handle == NULL) + return; +#define GET(var, fn) *((void **) (&var)) = dlsym(handle, #fn) + GET(newFGAlphaAttr, pango_attr_foreground_alpha_new); + GET(gwpIterSetObjectName, gtk_widget_path_iter_set_object_name); + dlclose(handle); +} + +PangoAttribute *FUTURE_pango_attr_foreground_alpha_new(guint16 alpha) +{ + if (newFGAlphaAttr == NULL) + return NULL; + return (*newFGAlphaAttr)(alpha); +} + +gboolean FUTURE_gtk_widget_path_iter_set_object_name(GtkWidgetPath *path, gint pos, const char *name) +{ + if (gwpIterSetObjectName == NULL) + return FALSE; + (*gwpIterSetObjectName)(path, pos, name); + return TRUE; +} diff --git a/unix/main.c b/unix/main.c index 13f0a913..9b78c24d 100644 --- a/unix/main.c +++ b/unix/main.c @@ -15,6 +15,7 @@ const char *uiInit(uiInitOptions *o) return msg; } initAlloc(); + loadFutures(); return NULL; } diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index c246f371..d52a83c2 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -57,3 +57,8 @@ extern cairo_surface_t *imageAppropriateSurface(uiImage *i, GtkWidget *w); // cellrendererbutton.c extern GtkCellRenderer *newCellRendererButton(void); + +// future.c +extern void loadFutures(void); +extern PangoAttribute *FUTURE_pango_attr_foreground_alpha_new(guint16 alpha); +extern gboolean FUTURE_gtk_widget_path_iter_set_object_name(GtkWidgetPath *path, gint pos, const char *name); From 9164e521e2530f1601d7fe0fe9a8326b0b8592a6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 29 Jun 2016 17:25:05 -0400 Subject: [PATCH 0436/1329] Changed the button cell renderer on GTK+ to actually work on 3.20. Thanks to baedert in irc.gimp.net/#gtk+ for suggestions. This actually has slightly fewer bugs! --- unix/cellrendererbutton.c | 46 +++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/unix/cellrendererbutton.c b/unix/cellrendererbutton.c index 0be23195..719b0371 100644 --- a/unix/cellrendererbutton.c +++ b/unix/cellrendererbutton.c @@ -4,9 +4,10 @@ // TODOs // - it's a rather tight fit // - selected row text color is white -// - no held state? -// - top of button wrong? // - resizing a column with a button in it crashes the program +// - accessibility +// - right side too big? +// - does prelight work on 3.10 and 3.20? #define cellRendererButtonType (cellRendererButton_get_type()) #define cellRendererButton(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), cellRendererButtonType, cellRendererButton)) @@ -57,35 +58,54 @@ static GtkSizeRequestMode cellRendererButton_get_request_mode(GtkCellRenderer *r return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH; } -// this is basically what GtkCellRendererToggle did in 3.10 +// this is basically what GtkCellRendererToggle did in 3.10 and does in 3.20, as well as what the Foreign Drawing gtk3-demo demo does static GtkStyleContext *setButtonStyle(GtkWidget *widget) { - GtkStyleContext *context; + GtkStyleContext *base, *context; + GtkWidgetPath *path; - context = gtk_widget_get_style_context(widget); - gtk_style_context_save(context); + base = gtk_widget_get_style_context(widget); + context = gtk_style_context_new(); + + path = gtk_widget_path_copy(gtk_style_context_get_path(base)); + gtk_widget_path_append_type(path, G_TYPE_NONE); + if (!FUTURE_gtk_widget_path_iter_set_object_name(path, -1, "button")) + // not on 3.20; try the type + gtk_widget_path_iter_set_object_type(path, -1, GTK_TYPE_BUTTON); + + gtk_style_context_set_path(context, path); + gtk_style_context_set_parent(context, base); + // the gtk3-demo example (which says we need to do this) uses gtk_widget_path_iter_get_state(path, -1) but that's not available until 3.14 + // TODO make a future for that too + gtk_style_context_set_state(context, gtk_style_context_get_state(base)); + gtk_widget_path_unref(path); + + // and if the above widget path screwery stil doesn't work, this will gtk_style_context_add_class(context, GTK_STYLE_CLASS_BUTTON); + return context; } +void unsetButtonStyle(GtkStyleContext *context) +{ + g_object_unref(context); +} + // this is based on what GtkCellRendererText does static void cellRendererButton_get_preferred_width(GtkCellRenderer *r, GtkWidget *widget, gint *minimum, gint *natural) { cellRendererButton *c = cellRendererButton(r); gint xpad; - GtkStyleContext *context; PangoLayout *layout; PangoRectangle rect; gint out; gtk_cell_renderer_get_padding(GTK_CELL_RENDERER(c), &xpad, NULL); - context = setButtonStyle(widget); layout = gtk_widget_create_pango_layout(widget, c->text); pango_layout_set_width(layout, -1); pango_layout_get_extents(layout, NULL, &rect); g_object_unref(layout); - gtk_style_context_restore(context); out = 2 * xpad + PANGO_PIXELS_CEIL(rect.width); if (minimum != NULL) @@ -99,19 +119,16 @@ static void cellRendererButton_get_preferred_height_for_width(GtkCellRenderer *r { cellRendererButton *c = cellRendererButton(r); gint xpad, ypad; - GtkStyleContext *context; PangoLayout *layout; gint height; gint out; gtk_cell_renderer_get_padding(GTK_CELL_RENDERER(c), &xpad, &ypad); - context = setButtonStyle(widget); layout = gtk_widget_create_pango_layout(widget, c->text); pango_layout_set_width(layout, ((2 * xpad + width) * PANGO_SCALE)); pango_layout_get_pixel_size(layout, NULL, &height); g_object_unref(layout); - gtk_style_context_restore(context); out = 2 * ypad + height; if (minimum != NULL) @@ -134,7 +151,6 @@ static void cellRendererButton_get_aligned_area(GtkCellRenderer *r, GtkWidget *w { cellRendererButton *c = cellRendererButton(r); gint xpad, ypad; - GtkStyleContext *context; PangoLayout *layout; PangoRectangle rect; gfloat xalign, yalign; @@ -142,7 +158,6 @@ static void cellRendererButton_get_aligned_area(GtkCellRenderer *r, GtkWidget *w gtk_cell_renderer_get_padding(GTK_CELL_RENDERER(c), &xpad, &ypad); - context = setButtonStyle(widget); layout = gtk_widget_create_pango_layout(widget, c->text); pango_layout_set_width(layout, -1); pango_layout_get_pixel_extents(layout, NULL, &rect); @@ -167,7 +182,6 @@ static void cellRendererButton_get_aligned_area(GtkCellRenderer *r, GtkWidget *w aligned_area->height = 2 * ypad + rect.height; g_object_unref(layout); - gtk_style_context_restore(context); } // this is based on both what GtkCellRendererText does and what GtkCellRendererToggle does @@ -209,7 +223,7 @@ static void cellRendererButton_render(GtkCellRenderer *r, cairo_t *cr, GtkWidget layout); g_object_unref(layout); - gtk_style_context_restore(context); + unsetButtonStyle(context); } static guint clickedSignal; From e20ce4e1887afd3b0823689787899c04a49c4507 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 29 Jun 2016 19:20:43 -0400 Subject: [PATCH 0437/1329] Some TODO resolution. --- unix/cellrendererbutton.c | 1 - 1 file changed, 1 deletion(-) diff --git a/unix/cellrendererbutton.c b/unix/cellrendererbutton.c index 719b0371..e3bbf48b 100644 --- a/unix/cellrendererbutton.c +++ b/unix/cellrendererbutton.c @@ -7,7 +7,6 @@ // - resizing a column with a button in it crashes the program // - accessibility // - right side too big? -// - does prelight work on 3.10 and 3.20? #define cellRendererButtonType (cellRendererButton_get_type()) #define cellRendererButton(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), cellRendererButtonType, cellRendererButton)) From ef689c10f17ae652d95b4adb9a8330a969400f7d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 30 Jun 2016 20:38:12 -0400 Subject: [PATCH 0438/1329] Started table part handling on Windows. --- windows/tablepart.cpp | 95 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 windows/tablepart.cpp diff --git a/windows/tablepart.cpp b/windows/tablepart.cpp new file mode 100644 index 00000000..c31932d7 --- /dev/null +++ b/windows/tablepart.cpp @@ -0,0 +1,95 @@ +// 30 june 2016 +// TODO includes + +typedef struct tablePartDrawParams tablePartDrawParams; +typedef struct tablePartMinimumSizeParams tableDrawMinimumSizeParams; +typedef struct tablePartEditingParams tablePartEditingParams; + +struct tablePartDrawParams { + HWND hwnd; + HDC hdc; + RECT *r; + bool selected; + bool focused; + bool hovering; + uiTableModel *model; + int row; +}; + +struct tablePartMinimumSizeParams { + HWND hwnd; + HDC hdc; + uiTableModel *model; + int row; +}; + +enum { + partEventDoNothing, + partEventRedraw, + partEventEdit, +}; + +struct tablePartEditingParams { + HWND newHWND; +}; + +enum { + partEditContinue, + partEditDone, +}; + +class tablePart { +public: + // needed so we can delete a tablePart + virtual ~tablePart() {} + + virtual HRESULT Draw(tablePartDrawParams *p) = 0; + virtual HRESULT MinimumSize(tablePartMinimumSizeParams *p, int *width, int *height) = 0; + + // returns a partEvent constant + virtual int MouseMove(int x, int y, RECT *cell) = 0; + virtual int MouseLeave(void) = 0; + virtual int LButtonDown(int x, int y, int count, RECT *cell) = 0; + virtual int LButtonUp(int x, int y, RECT *cell) = 0; + virtual int CaptureBroken(void) = 0; + virtual int KeyDown(void) = 0; + virtual int KeyUp(void) = 0; + + // editing; all optional + virtual int StartEditing(tablePartEditingParams *p) { return editDone; } + virtual int EditChildWM_COMMAND(WORD code, LRESULT *lr) { return editDone; } + virtual void FinishEditing(uiTableModel *model, int row) {} + virtual void CancelEditing(void) {} + + // TODO tooltips + // TODO timers and animations + + // optional methods + virtual void SetTextColorColumn(int col) {} + virtual void SetEditable(bool editable) {} +}; + +class tablePartText : public tablePart { + int textColumn; + int colorColumn; +public: + tablePartText(int tc) + { + this->textColumn = tc; + this->colorColumn = -1; + } + + // TODO figure out vertical alignment + virtual HRESULT Draw(tablePartDrawParams *p) + { + } + + virtual HRESULT MinimumSize(tablePartMinimumSizeParams *p, int *width, int *height) + { + } +}; + +tablePart *newTablePartText(int tc) +{ + return new tablePartText(tc); +} From 5f4e5ed8eb0c833184fae75c5af73676d0949519 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 5 Jul 2016 07:44:57 -0400 Subject: [PATCH 0439/1329] More bindings. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1bceba59..47419244 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,7 @@ Other people have made bindings to other languages: Language | Bindings --- | --- C#/.net | [LibUI.Binding](https://github.com/NattyNarwhal/LibUI.Binding), [SharpUI](https://github.com/benpye/sharpui/) +CHICKEN Scheme | [wasamasa/libui](https://github.com/wasamasa/libui) Crystal | [libui.cr](https://github.com/Fusion/libui.cr) D | [DerelictLibui](https://github.com/Extrawurst/DerelictLibui) Euphoria | [libui-euphoria](https://github.com/ghaberek/libui-euphoria) From 61bcb0d536045376f9ca70d37b9aa02a9038100c Mon Sep 17 00:00:00 2001 From: Boris Nagaev Date: Sun, 10 Jul 2016 22:14:55 +0300 Subject: [PATCH 0440/1329] cmake: do not reset unused variable _res_suffix --- windows/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 0f1d61de..4695eb4f 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -74,7 +74,6 @@ macro(_handle_static) COMMAND ${CMAKE_COMMAND} -E copy $/CMakeFiles/libui.dir/windows/resources.rc.* ${_LIBUI_STATIC_RES} COMMENT "Copying libui.res") - set(_res_suffix) endmacro() # notice that usp10 comes before gdi32 From bea844c18eb4e34c02482b410dda25c850f5c92c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 12 Jul 2016 17:50:43 -0400 Subject: [PATCH 0441/1329] More TODOs. --- TODO.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TODO.md b/TODO.md index 86b06801..3de1cb47 100644 --- a/TODO.md +++ b/TODO.md @@ -99,3 +99,5 @@ on windows - a way to do recursive main loops - how do we handle 0 returns from non-recursive uiMainStep() calls that aren't the main loop? (event handlers, for instance) - should repeated calls to uiMainStep() after uiQuit() return 0 reliably? this will be needed for non-recursive loops + +http://stackoverflow.com/questions/38338426/meaning-of-ampersand-in-rc-files/38338841?noredirect=1#comment64093084_38338841 From 013659e80569c6368ef90eb25ef513a50669041b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 8 Aug 2016 09:52:04 -0400 Subject: [PATCH 0442/1329] More TODOs. --- TODO.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TODO.md b/TODO.md index 3de1cb47..8aa57b4a 100644 --- a/TODO.md +++ b/TODO.md @@ -101,3 +101,5 @@ on windows - should repeated calls to uiMainStep() after uiQuit() return 0 reliably? this will be needed for non-recursive loops http://stackoverflow.com/questions/38338426/meaning-of-ampersand-in-rc-files/38338841?noredirect=1#comment64093084_38338841 + +label shortcut keys From 7324683ba229fee0e91ffd4edcc47d727f01c71e Mon Sep 17 00:00:00 2001 From: Neel Chauhan Date: Wed, 10 Aug 2016 20:37:42 -0400 Subject: [PATCH 0443/1329] Fix "cannot find -ldl" linking error with FreeBSD --- unix/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unix/CMakeLists.txt b/unix/CMakeLists.txt index 33eedfac..56d653ad 100644 --- a/unix/CMakeLists.txt +++ b/unix/CMakeLists.txt @@ -78,5 +78,5 @@ set(_LIBUI_CFLAGS PARENT_SCOPE) set(_LIBUI_LIBS - ${GTK_LDFLAGS} m dl + ${GTK_LDFLAGS} m ${CMAKE_DL_LIBS} PARENT_SCOPE) From bc670c0b5f711132055f49cd2e19e3a66f225170 Mon Sep 17 00:00:00 2001 From: Lin Xi Date: Fri, 19 Aug 2016 16:07:48 +0800 Subject: [PATCH 0444/1329] Add an oo binding reference in d --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 67636543..41c9a424 100644 --- a/README.md +++ b/README.md @@ -128,7 +128,7 @@ Language | Bindings --- | --- C#/.net | [LibUI.Binding](https://github.com/NattyNarwhal/LibUI.Binding), [SharpUI](https://github.com/benpye/sharpui/) Crystal | [libui.cr](https://github.com/Fusion/libui.cr) -D | [DerelictLibui](https://github.com/Extrawurst/DerelictLibui) +D | [DerelictLibui](https://github.com/Extrawurst/DerelictLibui), [libuid (complete oo interface)](https://github.com/mogud/libuid) Euphoria | [libui-euphoria](https://github.com/ghaberek/libui-euphoria) Haskell | [libui-haskell](https://github.com/ajnsit/libui-haskell) JavaScript | [libui.js (merged into libui-node?)](https://github.com/mavenave/libui.js) From 8964dff296a060eaa9ba6546455310b6f153d38f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 19 Aug 2016 08:34:20 -0400 Subject: [PATCH 0445/1329] Reworked the README a bit. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b7f1338a..0c62e4be 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,7 @@ Language | Bindings C#/.net | [LibUI.Binding](https://github.com/NattyNarwhal/LibUI.Binding), [SharpUI](https://github.com/benpye/sharpui/) CHICKEN Scheme | [wasamasa/libui](https://github.com/wasamasa/libui) Crystal | [libui.cr](https://github.com/Fusion/libui.cr) -D | [DerelictLibui](https://github.com/Extrawurst/DerelictLibui), [libuid (complete oo interface)](https://github.com/mogud/libuid) +D | [DerelictLibui (flat API)](https://github.com/Extrawurst/DerelictLibui), [libuid (object-oriented)](https://github.com/mogud/libuid) Euphoria | [libui-euphoria](https://github.com/ghaberek/libui-euphoria) Haskell | [libui-haskell](https://github.com/ajnsit/libui-haskell) JavaScript | [libui.js (merged into libui-node?)](https://github.com/mavenave/libui.js) From 0b5d4021f2e0ff541562665b62ce7eb3ae0f6a64 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 20 Aug 2016 10:09:53 -0400 Subject: [PATCH 0446/1329] More TODOs. --- TODO.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TODO.md b/TODO.md index 8aa57b4a..470b80d0 100644 --- a/TODO.md +++ b/TODO.md @@ -103,3 +103,5 @@ on windows http://stackoverflow.com/questions/38338426/meaning-of-ampersand-in-rc-files/38338841?noredirect=1#comment64093084_38338841 label shortcut keys + +- remove whining from source code From 2d5166c117b2100797fcf5cba2542f7388535ec7 Mon Sep 17 00:00:00 2001 From: Pedro Tacla Yamada Date: Tue, 6 Sep 2016 11:55:00 -0300 Subject: [PATCH 0447/1329] Add a complete Haskell binding to the README The currently linked Haskell bindings to this library are just an empty repository. We've published complete bindings as well as OSX specific extensions like WebViews and MapViews (WIP) at https://github.com/beijaflor-io/haskell-libui. This just adds a link to it. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 41c9a424..431c15b0 100644 --- a/README.md +++ b/README.md @@ -130,7 +130,7 @@ C#/.net | [LibUI.Binding](https://github.com/NattyNarwhal/LibUI.Binding), [Sharp Crystal | [libui.cr](https://github.com/Fusion/libui.cr) D | [DerelictLibui](https://github.com/Extrawurst/DerelictLibui), [libuid (complete oo interface)](https://github.com/mogud/libuid) Euphoria | [libui-euphoria](https://github.com/ghaberek/libui-euphoria) -Haskell | [libui-haskell](https://github.com/ajnsit/libui-haskell) +Haskell | [libui-haskell](https://github.com/ajnsit/libui-haskell), [beijaflor-io/haskell-libui (complete FFI bindings, extensions and higher-level API)](https://github.com/beijaflor-io/haskell-libui) JavaScript | [libui.js (merged into libui-node?)](https://github.com/mavenave/libui.js) Julia | [Libui.jl](https://github.com/joa-quim/Libui.jl) Lua | [libuilua](https://github.com/zevv/libuilua), [libui-lua](https://github.com/mdombroski/libui-lua) From 4bb9b58ec620af065521685d870ec1124d8f415d Mon Sep 17 00:00:00 2001 From: Steven Clukey Date: Thu, 15 Sep 2016 09:24:34 -0400 Subject: [PATCH 0448/1329] Add Swift binding --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 41c9a424..ee2dd5c4 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,7 @@ Node.js | [libui-node](https://github.com/parro-it/libui-node) Python | [pylibui](https://github.com/joaoventura/pylibui) Ruby | [libui-ruby](https://github.com/jamescook/libui-ruby) Rust | [libui-rs](https://github.com/pcwalton/libui-rs) +Swift | [libui-swift](https://github.com/sclukey/libui-swift) ## Screenshots From 084ed53e63a5a87fa75ce9f73b0c0dce63b89777 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 15 Oct 2016 01:21:04 -0400 Subject: [PATCH 0449/1329] Fixed a latent bug in uiFreeInitError() on Windows. --- windows/init.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/init.cpp b/windows/init.cpp index c91929f9..22874165 100644 --- a/windows/init.cpp +++ b/windows/init.cpp @@ -156,7 +156,7 @@ void uiUninit(void) void uiFreeInitError(const char *err) { if (*(err - 1) == '-') - uiFree((void *) err); + uiFree((void *) (err - 1)); } BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) From a9c7ff9c6c98a560b498fdc36095954a8cb6a9ae Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 17 Oct 2016 14:25:32 -0400 Subject: [PATCH 0450/1329] Travis now installs the right version of cmake by default on OS X. No more special hacks. --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index edd561be..d271bd8e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,6 @@ script: - if [ "$TRAVIS_OS_NAME" == "linux" ]; then sudo apt-get update; fi - if [ "$TRAVIS_OS_NAME" == "linux" ]; then sudo apt-get install libgtk-3-dev -y || sudo apt-cache search libgtk3; fi - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew update; fi - - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew upgrade cmake; fi - mkdir build - cd build - cmake --version From abbea5273f67f43de9d3f13c2eb0b03044bc486a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Boris=20Mom=C4=8Dilovi=C4=87?= Date: Thu, 20 Oct 2016 01:07:51 +0200 Subject: [PATCH 0451/1329] Mention PHP Binding Add a link to PHP extension --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 939989b7..48ea131e 100644 --- a/README.md +++ b/README.md @@ -136,6 +136,7 @@ Julia | [Libui.jl](https://github.com/joa-quim/Libui.jl) Lua | [libuilua](https://github.com/zevv/libuilua), [libui-lua](https://github.com/mdombroski/libui-lua) Nim | [ui](https://github.com/nim-lang/ui) Node.js | [libui-node](https://github.com/parro-it/libui-node) +PHP | [ui](https://github.com/krakjoe/ui) Python | [pylibui](https://github.com/joaoventura/pylibui) Ruby | [libui-ruby](https://github.com/jamescook/libui-ruby) Rust | [libui-rs](https://github.com/pcwalton/libui-rs) From f46edd097b7d394f574ff10fd798d918b66c68eb Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 21 Oct 2016 16:35:46 -0400 Subject: [PATCH 0452/1329] uiDrawTextWeightUtraBold. --- README.md | 3 +++ darwin/drawtext.m | 2 +- test/page9.c | 2 +- ui.h | 2 +- unix/drawtext.c | 2 +- windows/drawtext.cpp | 2 +- 6 files changed, 8 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 44c24516..9264fd82 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,9 @@ This README is being written.
## Announcements +* **21 October 2016** + * `uiDrawTextWeightUltraBold` is now spelled correctly. Thanks to @krakjoe. + * **18 June 2016** * Help decide [the design of tables and trees in libui](https://github.com/andlabs/libui/issues/159); the implementation starts within the next few days, if not tomorrow! diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 810a3a43..07038616 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -164,7 +164,7 @@ static const CGFloat ctWeights[] = { [uiDrawTextWeightSemiBold] = ourNSFontWeightSemibold, [uiDrawTextWeightBold] = ourNSFontWeightBold, // for this one let's go between Bold and Heavy - [uiDrawTextWeightUtraBold] = ourNSFontWeightBold + ((ourNSFontWeightHeavy - ourNSFontWeightBold) / 2), + [uiDrawTextWeightUltraBold] = ourNSFontWeightBold + ((ourNSFontWeightHeavy - ourNSFontWeightBold) / 2), [uiDrawTextWeightHeavy] = ourNSFontWeightHeavy, [uiDrawTextWeightUltraHeavy] = ourNSFontWeightBlack, }; diff --git a/test/page9.c b/test/page9.c index 5591c054..65b2d3a1 100644 --- a/test/page9.c +++ b/test/page9.c @@ -230,7 +230,7 @@ uiBox *makePage9(void) uiComboboxAppend(textWeight, "Medium"); uiComboboxAppend(textWeight, "Semi Bold"); uiComboboxAppend(textWeight, "Bold"); - uiComboboxAppend(textWeight, "Utra Bold"); + uiComboboxAppend(textWeight, "Ultra Bold"); uiComboboxAppend(textWeight, "Heavy"); uiComboboxAppend(textWeight, "Ultra Heavy"); uiComboboxSetSelected(textWeight, uiDrawTextWeightNormal); diff --git a/ui.h b/ui.h index 70c2f121..cedfb60e 100644 --- a/ui.h +++ b/ui.h @@ -481,7 +481,7 @@ _UI_ENUM(uiDrawTextWeight) { uiDrawTextWeightMedium, uiDrawTextWeightSemiBold, uiDrawTextWeightBold, - uiDrawTextWeightUtraBold, + uiDrawTextWeightUltraBold, uiDrawTextWeightHeavy, uiDrawTextWeightUltraHeavy, }; diff --git a/unix/drawtext.c b/unix/drawtext.c index d31c264c..7078e1ac 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -62,7 +62,7 @@ static const PangoWeight pangoWeights[] = { [uiDrawTextWeightMedium] = PANGO_WEIGHT_MEDIUM, [uiDrawTextWeightSemiBold] = PANGO_WEIGHT_SEMIBOLD, [uiDrawTextWeightBold] = PANGO_WEIGHT_BOLD, - [uiDrawTextWeightUtraBold] = PANGO_WEIGHT_ULTRABOLD, + [uiDrawTextWeightUltraBold] = PANGO_WEIGHT_ULTRABOLD, [uiDrawTextWeightHeavy] = PANGO_WEIGHT_HEAVY, [uiDrawTextWeightUltraHeavy] = PANGO_WEIGHT_ULTRAHEAVY, }; diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 023fcdd9..05a24f67 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -94,7 +94,7 @@ static const struct { { false, uiDrawTextWeightMedium, DWRITE_FONT_WEIGHT_MEDIUM }, { false, uiDrawTextWeightSemiBold, DWRITE_FONT_WEIGHT_SEMI_BOLD }, { false, uiDrawTextWeightBold, DWRITE_FONT_WEIGHT_BOLD }, - { false, uiDrawTextWeightUtraBold, DWRITE_FONT_WEIGHT_ULTRA_BOLD }, + { false, uiDrawTextWeightUltraBold, DWRITE_FONT_WEIGHT_ULTRA_BOLD }, { false, uiDrawTextWeightHeavy, DWRITE_FONT_WEIGHT_HEAVY }, { true, uiDrawTextWeightUltraHeavy, DWRITE_FONT_WEIGHT_ULTRA_BLACK, }, }; From 8b3ce21886b8bcf41d7bd1f2e834a9cabab047b0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 21 Oct 2016 18:27:40 -0400 Subject: [PATCH 0453/1329] More TODOs. --- TODO.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/TODO.md b/TODO.md index 470b80d0..d62049c9 100644 --- a/TODO.md +++ b/TODO.md @@ -1,3 +1,7 @@ +- documentation notes: + - static binaries do not link system libraries, meaning apps still depend on shared GTK+, etc. + - ui*Buttons are NOT compatible with uiButton functions + - more robust layout handling - uiFormTie() for ensuring multiple uiForms have the same label area widths - uiSizeGroup for size groups (GtkSizeGroup on GTK+, auto layout constraints on OS X; consider adding after 10.8 is gone) From 1369f53027a07e0b93f0d61644a083d396bd3e88 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 22 Oct 2016 09:01:26 -0400 Subject: [PATCH 0454/1329] Meh, let's push --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 9264fd82..362f0c52 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +# Note: Table stuff is currently experimental; do not use in production code. + # libui: a portable GUI library for C This README is being written.
From d344e1ae29dd6605e4640c063dc4a61dc1c183e7 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 22 Oct 2016 15:31:43 -0400 Subject: [PATCH 0455/1329] More bindings. Fixes #211. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 362f0c52..a3d29721 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,7 @@ CHICKEN Scheme | [wasamasa/libui](https://github.com/wasamasa/libui) Crystal | [libui.cr](https://github.com/Fusion/libui.cr) D | [DerelictLibui (flat API)](https://github.com/Extrawurst/DerelictLibui), [libuid (object-oriented)](https://github.com/mogud/libuid) Euphoria | [libui-euphoria](https://github.com/ghaberek/libui-euphoria) +Harbour | [HBUI](https://github.com/RJopek/HBUI) Haskell | [libui-haskell](https://github.com/ajnsit/libui-haskell), [beijaflor-io/haskell-libui (complete FFI bindings, extensions and higher-level API)](https://github.com/beijaflor-io/haskell-libui) JavaScript | [libui.js (merged into libui-node?)](https://github.com/mavenave/libui.js) Julia | [Libui.jl](https://github.com/joa-quim/Libui.jl) From 570b79465096252509102cf0ce0dd615d07a5fbb Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 22 Oct 2016 15:40:15 -0400 Subject: [PATCH 0456/1329] Started the work in removing the move and center functions from uiWindow; those simply cannot be done thanks to Wayland. --- _abort/windowevents/page15.c | 65 ++++++++++++++++++++++++++++++++++++ test/page15.c | 62 ---------------------------------- 2 files changed, 65 insertions(+), 62 deletions(-) create mode 100644 _abort/windowevents/page15.c diff --git a/_abort/windowevents/page15.c b/_abort/windowevents/page15.c new file mode 100644 index 00000000..0cceba86 --- /dev/null +++ b/_abort/windowevents/page15.c @@ -0,0 +1,65 @@ +static uiSpinbox *x, *y; + +static void moveX(uiSpinbox *s, void *data) +{ + uiWindow *w = uiWindow(data); + int xp, yp; + + uiWindowPosition(w, &xp, &yp); + xp = uiSpinboxValue(x); + uiWindowSetPosition(w, xp, yp); +} + +static void moveY(uiSpinbox *s, void *data) +{ + uiWindow *w = uiWindow(data); + int xp, yp; + + uiWindowPosition(w, &xp, &yp); + yp = uiSpinboxValue(y); + uiWindowSetPosition(w, xp, yp); +} + +static void updatepos(uiWindow *w) +{ + int xp, yp; + + uiWindowPosition(w, &xp, &yp); + uiSpinboxSetValue(x, xp); + uiSpinboxSetValue(y, yp); +} + +static void center(uiButton *b, void *data) +{ + uiWindow *w = uiWindow(data); + + uiWindowCenter(w); + updatepos(w); +} + +void onMove(uiWindow *w, void *data) +{ + printf("move\n"); + updatepos(w); +} + +uiBox *makePage15(uiWindow *w) +{ + hbox = newHorizontalBox(); + // TODO if I make this 1 and not add anything else AND not call uiWindowOnPositionChanged(), on OS X the box won't be able to grow vertically + uiBoxAppend(page15, uiControl(hbox), 0); + + uiBoxAppend(hbox, uiControl(uiNewLabel("Position")), 0); + x = uiNewSpinbox(INT_MIN, INT_MAX); + uiBoxAppend(hbox, uiControl(x), 1); + y = uiNewSpinbox(INT_MIN, INT_MAX); + uiBoxAppend(hbox, uiControl(y), 1); + button = uiNewButton("Center"); + uiBoxAppend(hbox, uiControl(button), 0); + + uiSpinboxOnChanged(x, moveX, w); + uiSpinboxOnChanged(y, moveY, w); + uiButtonOnClicked(button, center, w); + uiWindowOnPositionChanged(w, onMove, NULL); + updatepos(w); +} diff --git a/test/page15.c b/test/page15.c index 7dd078c7..24d96663 100644 --- a/test/page15.c +++ b/test/page15.c @@ -1,53 +1,9 @@ // 15 june 2016 #include "test.h" -static uiSpinbox *x, *y; static uiSpinbox *width, *height; static uiCheckbox *fullscreen; -static void moveX(uiSpinbox *s, void *data) -{ - uiWindow *w = uiWindow(data); - int xp, yp; - - uiWindowPosition(w, &xp, &yp); - xp = uiSpinboxValue(x); - uiWindowSetPosition(w, xp, yp); -} - -static void moveY(uiSpinbox *s, void *data) -{ - uiWindow *w = uiWindow(data); - int xp, yp; - - uiWindowPosition(w, &xp, &yp); - yp = uiSpinboxValue(y); - uiWindowSetPosition(w, xp, yp); -} - -static void updatepos(uiWindow *w) -{ - int xp, yp; - - uiWindowPosition(w, &xp, &yp); - uiSpinboxSetValue(x, xp); - uiSpinboxSetValue(y, yp); -} - -static void center(uiButton *b, void *data) -{ - uiWindow *w = uiWindow(data); - - uiWindowCenter(w); - updatepos(w); -} - -void onMove(uiWindow *w, void *data) -{ - printf("move\n"); - updatepos(w); -} - static void sizeWidth(uiSpinbox *s, void *data) { uiWindow *w = uiWindow(data); @@ -109,24 +65,6 @@ uiBox *makePage15(uiWindow *w) page15 = newVerticalBox(); - hbox = newHorizontalBox(); - // TODO if I make this 1 and not add anything else AND not call uiWindowOnPositionChanged(), on OS X the box won't be able to grow vertically - uiBoxAppend(page15, uiControl(hbox), 0); - - uiBoxAppend(hbox, uiControl(uiNewLabel("Position")), 0); - x = uiNewSpinbox(INT_MIN, INT_MAX); - uiBoxAppend(hbox, uiControl(x), 1); - y = uiNewSpinbox(INT_MIN, INT_MAX); - uiBoxAppend(hbox, uiControl(y), 1); - button = uiNewButton("Center"); - uiBoxAppend(hbox, uiControl(button), 0); - - uiSpinboxOnChanged(x, moveX, w); - uiSpinboxOnChanged(y, moveY, w); - uiButtonOnClicked(button, center, w); - uiWindowOnPositionChanged(w, onMove, NULL); - updatepos(w); - hbox = newHorizontalBox(); uiBoxAppend(page15, uiControl(hbox), 0); From 6d796b564284b8751f62003012bdf9f25c2b0897 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 22 Oct 2016 18:11:49 -0400 Subject: [PATCH 0457/1329] More removal of uiWindow move events. --- _abort/windowevents/darwin_window.m | 90 +++++++++++++++++++++++++++++ _abort/windowevents/ui.h | 6 ++ darwin/window.m | 58 ------------------- ui.h | 4 -- 4 files changed, 96 insertions(+), 62 deletions(-) create mode 100644 _abort/windowevents/darwin_window.m create mode 100644 _abort/windowevents/ui.h diff --git a/_abort/windowevents/darwin_window.m b/_abort/windowevents/darwin_window.m new file mode 100644 index 00000000..76180d84 --- /dev/null +++ b/_abort/windowevents/darwin_window.m @@ -0,0 +1,90 @@ +struct uiWindow { + // constraints + void (*onPositionChanged)(uiWindow *, void *); + void *onPositionChangedData; + BOOL suppressPositionChanged; + // onContentSizeChanged +}; + +@interface windowDelegateClass : NSObject { +// windowShouldClose: +- (void)windowDidMove:(NSNotification *)note; +// windowDidResize: +@end + +@implementation windowDelegateClass + +// - (BOOL)windowShouldClose:(id)sender + +// TODO doesn't happen live +- (void)windowDidMove:(NSNotification *)note +{ + uiWindow *w; + + w = [self lookupWindow:((NSWindow *) [note object])]; + if (!w->suppressPositionChanged) + (*(w->onPositionChanged))(w, w->onPositionChangedData); +} + +// - (void)windowDidResize:(NSNotification *)note + +// void uiWindowSetTitle(uiWindow *w, const char *title) + +void uiWindowPosition(uiWindow *w, int *x, int *y) +{ + NSScreen *screen; + NSRect r; + + r = [w->window frame]; + *x = r.origin.x; + // this is the right screen to use; thanks mikeash in irc.freenode.net/#macdev + // -mainScreen is useless for positioning (it's just the key window's screen) + // and we use -frame, not -visibleFrame, for dealing with absolute positions + screen = (NSScreen *) [[NSScreen screens] objectAtIndex:0]; + *y = ([screen frame].size.height - r.origin.y) - r.size.height; +} + +void uiWindowSetPosition(uiWindow *w, int x, int y) +{ + // -[NSWindow setFrameTopLeftPoint:] is acting weird so... + NSRect r; + NSScreen *screen; + + // this fires windowDidMove: + w->suppressPositionChanged = YES; + r = [w->window frame]; + r.origin.x = x; + screen = (NSScreen *) [[NSScreen screens] objectAtIndex:0]; + r.origin.y = [screen frame].size.height - (y + r.size.height); + [w->window setFrameOrigin:r.origin]; + w->suppressPositionChanged = NO; +} + +void uiWindowCenter(uiWindow *w) +{ + w->suppressPositionChanged = YES; + [w->window center]; + w->suppressPositionChanged = NO; +} + +void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data) +{ + w->onPositionChanged = f; + w->onPositionChangedData = data; +} + +// void uiWindowContentSize(uiWindow *w, int *width, int *height) + +// static int defaultOnClosing(uiWindow *w, void *data) + +static void defaultOnPositionContentSizeChanged(uiWindow *w, void *data) +{ + // do nothing +} + +uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) +{ +// uiWindowOnClosing(w, defaultOnClosing, NULL); + uiWindowOnPositionChanged(w, defaultOnPositionContentSizeChanged, NULL); +// uiWindowOnContentSizeChanged(w, defaultOnPositionContentSizeChanged, NULL); +} diff --git a/_abort/windowevents/ui.h b/_abort/windowevents/ui.h new file mode 100644 index 00000000..e0d24c7f --- /dev/null +++ b/_abort/windowevents/ui.h @@ -0,0 +1,6 @@ +// uiWindowSetTitle +_UI_EXTERN void uiWindowPosition(uiWindow *w, int *x, int *y); +_UI_EXTERN void uiWindowSetPosition(uiWindow *w, int x, int y); +_UI_EXTERN void uiWindowCenter(uiWindow *w); +_UI_EXTERN void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data); +// uiWindowContentSize diff --git a/darwin/window.m b/darwin/window.m index 8236bee1..bba2e1b4 100644 --- a/darwin/window.m +++ b/darwin/window.m @@ -11,9 +11,6 @@ struct uiWindow { int (*onClosing)(uiWindow *, void *); void *onClosingData; struct singleChildConstraints constraints; - void (*onPositionChanged)(uiWindow *, void *); - void *onPositionChangedData; - BOOL suppressPositionChanged; void (*onContentSizeChanged)(uiWindow *, void *); void *onContentSizeChangedData; BOOL suppressSizeChanged; @@ -25,7 +22,6 @@ struct uiWindow { struct mapTable *windows; } - (BOOL)windowShouldClose:(id)sender; -- (void)windowDidMove:(NSNotification *)note; - (void)windowDidResize:(NSNotification *)note; - (void)windowDidEnterFullScreen:(NSNotification *)note; - (void)windowDidExitFullScreen:(NSNotification *)note; @@ -61,16 +57,6 @@ struct uiWindow { return NO; } -// TODO doesn't happen live -- (void)windowDidMove:(NSNotification *)note -{ - uiWindow *w; - - w = [self lookupWindow:((NSWindow *) [note object])]; - if (!w->suppressPositionChanged) - (*(w->onPositionChanged))(w, w->onPositionChangedData); -} - - (void)windowDidResize:(NSNotification *)note { uiWindow *w; @@ -255,49 +241,6 @@ void uiWindowSetTitle(uiWindow *w, const char *title) [w->window setTitle:toNSString(title)]; } -void uiWindowPosition(uiWindow *w, int *x, int *y) -{ - NSScreen *screen; - NSRect r; - - r = [w->window frame]; - *x = r.origin.x; - // this is the right screen to use; thanks mikeash in irc.freenode.net/#macdev - // -mainScreen is useless for positioning (it's just the key window's screen) - // and we use -frame, not -visibleFrame, for dealing with absolute positions - screen = (NSScreen *) [[NSScreen screens] objectAtIndex:0]; - *y = ([screen frame].size.height - r.origin.y) - r.size.height; -} - -void uiWindowSetPosition(uiWindow *w, int x, int y) -{ - // -[NSWindow setFrameTopLeftPoint:] is acting weird so... - NSRect r; - NSScreen *screen; - - // this fires windowDidMove: - w->suppressPositionChanged = YES; - r = [w->window frame]; - r.origin.x = x; - screen = (NSScreen *) [[NSScreen screens] objectAtIndex:0]; - r.origin.y = [screen frame].size.height - (y + r.size.height); - [w->window setFrameOrigin:r.origin]; - w->suppressPositionChanged = NO; -} - -void uiWindowCenter(uiWindow *w) -{ - w->suppressPositionChanged = YES; - [w->window center]; - w->suppressPositionChanged = NO; -} - -void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data) -{ - w->onPositionChanged = f; - w->onPositionChangedData = data; -} - void uiWindowContentSize(uiWindow *w, int *width, int *height) { NSRect r; @@ -434,7 +377,6 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) } [windowDelegate registerWindow:w]; uiWindowOnClosing(w, defaultOnClosing, NULL); - uiWindowOnPositionChanged(w, defaultOnPositionContentSizeChanged, NULL); uiWindowOnContentSizeChanged(w, defaultOnPositionContentSizeChanged, NULL); return w; diff --git a/ui.h b/ui.h index cedfb60e..5cf01b50 100644 --- a/ui.h +++ b/ui.h @@ -100,10 +100,6 @@ typedef struct uiWindow uiWindow; #define uiWindow(this) ((uiWindow *) (this)) _UI_EXTERN char *uiWindowTitle(uiWindow *w); _UI_EXTERN void uiWindowSetTitle(uiWindow *w, const char *title); -_UI_EXTERN void uiWindowPosition(uiWindow *w, int *x, int *y); -_UI_EXTERN void uiWindowSetPosition(uiWindow *w, int x, int y); -_UI_EXTERN void uiWindowCenter(uiWindow *w); -_UI_EXTERN void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data); _UI_EXTERN void uiWindowContentSize(uiWindow *w, int *width, int *height); _UI_EXTERN void uiWindowSetContentSize(uiWindow *w, int width, int height); _UI_EXTERN int uiWindowFullscreen(uiWindow *w); From edd70b9fa5ba2708de42db874bfed0dfb0cd2daa Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 22 Oct 2016 18:35:41 -0400 Subject: [PATCH 0458/1329] More uiWindows cleanup. --- _abort/windowevents/windows_window.cpp | 86 ++++++++++++++++++++++++++ windows/window.cpp | 55 ---------------- 2 files changed, 86 insertions(+), 55 deletions(-) create mode 100644 _abort/windowevents/windows_window.cpp diff --git a/_abort/windowevents/windows_window.cpp b/_abort/windowevents/windows_window.cpp new file mode 100644 index 00000000..769c6db9 --- /dev/null +++ b/_abort/windowevents/windows_window.cpp @@ -0,0 +1,86 @@ +struct uiWindow { +// BOOL hasMenubar; + void (*onPositionChanged)(uiWindow *, void *); + void *onPositionChangedData; + BOOL changingPosition; // to avoid triggering the above when programmatically doing this +// void (*onContentSizeChanged)(uiWindow *, void *); +}; + +static LRESULT CALLBACK windowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + case WM_WINDOWPOSCHANGED: + if ((wp->flags & SWP_NOMOVE) == 0) + if (!w->changingPosition) + (*(w->onPositionChanged))(w, w->onPositionChangedData); + // and continue anyway +// if ((wp->flags & SWP_NOSIZE) != 0) +} + +// static int defaultOnClosing(uiWindow *w, void *data) + +static void defaultOnPositionContentSizeChanged(uiWindow *w, void *data) +{ + // do nothing +} + +// static std::map windows; + +// void uiWindowSetTitle(uiWindow *w, const char *title) + +void uiWindowPosition(uiWindow *w, int *x, int *y) +{ + RECT r; + + uiWindowsEnsureGetWindowRect(w->hwnd, &r); + *x = r.left; + *y = r.top; +} + +void uiWindowSetPosition(uiWindow *w, int x, int y) +{ + w->changingPosition = TRUE; + if (SetWindowPos(w->hwnd, NULL, x, y, 0, 0, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOZORDER) == 0) + logLastError(L"error moving window"); + w->changingPosition = FALSE; +} + +// static void windowMonitorRect(HWND hwnd, RECT *r) + +// TODO use the work rect instead? +void uiWindowCenter(uiWindow *w) +{ + RECT wr, mr; + int x, y; + LONG wwid, mwid; + LONG wht, mht; + + uiWindowsEnsureGetWindowRect(w->hwnd, &wr); + windowMonitorRect(w->hwnd, &mr); + wwid = wr.right - wr.left; + mwid = mr.right - mr.left; + x = (mwid - wwid) / 2; + wht = wr.bottom - wr.top; + mht = mr.bottom - mr.top; + y = (mht - wht) / 2; + // y is now evenly divided, however https://msdn.microsoft.com/en-us/library/windows/desktop/dn742502(v=vs.85).aspx says that 45% should go above and 55% should go below + // so just move 5% of the way up + // TODO should this be on the work area? + // TODO is this calculation correct? + y -= y / 20; + uiWindowSetPosition(w, x, y); +} + +void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data) +{ + w->onPositionChanged = f; + w->onPositionChangedData = data; +} + +// void uiWindowContentSize(uiWindow *w, int *width, int *height) + +uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) +{ +// uiWindowOnClosing(w, defaultOnClosing, NULL); + uiWindowOnPositionChanged(w, defaultOnPositionContentSizeChanged, NULL); +// uiWindowOnContentSizeChanged(w, defaultOnPositionContentSizeChanged, NULL); +} diff --git a/windows/window.cpp b/windows/window.cpp index 760702ba..e0961c28 100644 --- a/windows/window.cpp +++ b/windows/window.cpp @@ -14,9 +14,6 @@ struct uiWindow { void *onClosingData; int margined; BOOL hasMenubar; - void (*onPositionChanged)(uiWindow *, void *); - void *onPositionChangedData; - BOOL changingPosition; // to avoid triggering the above when programmatically doing this void (*onContentSizeChanged)(uiWindow *, void *); void *onContentSizeChangedData; BOOL changingSize; @@ -95,10 +92,6 @@ static LRESULT CALLBACK windowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARA runMenuEvent(LOWORD(wParam), uiWindow(w)); return 0; case WM_WINDOWPOSCHANGED: - if ((wp->flags & SWP_NOMOVE) == 0) - if (!w->changingPosition) - (*(w->onPositionChanged))(w, w->onPositionChangedData); - // and continue anyway if ((wp->flags & SWP_NOSIZE) != 0) break; if (w->onContentSizeChanged != NULL) // TODO figure out why this is happening too early @@ -296,23 +289,6 @@ void uiWindowSetTitle(uiWindow *w, const char *title) // don't queue resize; the caption isn't part of what affects layout and sizing of the client area (it'll be ellipsized if too long) } -void uiWindowPosition(uiWindow *w, int *x, int *y) -{ - RECT r; - - uiWindowsEnsureGetWindowRect(w->hwnd, &r); - *x = r.left; - *y = r.top; -} - -void uiWindowSetPosition(uiWindow *w, int x, int y) -{ - w->changingPosition = TRUE; - if (SetWindowPos(w->hwnd, NULL, x, y, 0, 0, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOZORDER) == 0) - logLastError(L"error moving window"); - w->changingPosition = FALSE; -} - // this is used for both fullscreening and centering // see also https://blogs.msdn.microsoft.com/oldnewthing/20100412-00/?p=14353 and https://blogs.msdn.microsoft.com/oldnewthing/20050505-04/?p=35703 static void windowMonitorRect(HWND hwnd, RECT *r) @@ -335,36 +311,6 @@ static void windowMonitorRect(HWND hwnd, RECT *r) *r = mi.rcMonitor; } -// TODO use the work rect instead? -void uiWindowCenter(uiWindow *w) -{ - RECT wr, mr; - int x, y; - LONG wwid, mwid; - LONG wht, mht; - - uiWindowsEnsureGetWindowRect(w->hwnd, &wr); - windowMonitorRect(w->hwnd, &mr); - wwid = wr.right - wr.left; - mwid = mr.right - mr.left; - x = (mwid - wwid) / 2; - wht = wr.bottom - wr.top; - mht = mr.bottom - mr.top; - y = (mht - wht) / 2; - // y is now evenly divided, however https://msdn.microsoft.com/en-us/library/windows/desktop/dn742502(v=vs.85).aspx says that 45% should go above and 55% should go below - // so just move 5% of the way up - // TODO should this be on the work area? - // TODO is this calculation correct? - y -= y / 20; - uiWindowSetPosition(w, x, y); -} - -void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data) -{ - w->onPositionChanged = f; - w->onPositionChangedData = data; -} - void uiWindowContentSize(uiWindow *w, int *width, int *height) { RECT r; @@ -541,7 +487,6 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) setClientSize(w, width, height, hasMenubarBOOL, style, exstyle); uiWindowOnClosing(w, defaultOnClosing, NULL); - uiWindowOnPositionChanged(w, defaultOnPositionContentSizeChanged, NULL); uiWindowOnContentSizeChanged(w, defaultOnPositionContentSizeChanged, NULL); windows[w] = true; From 57fbf78ef3823fd5252782576bfe790e0e0f835d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 22 Oct 2016 19:31:57 -0400 Subject: [PATCH 0459/1329] More removal. --- unix/window.c | 70 ++-------------------------------------------- windows/window.cpp | 2 ++ 2 files changed, 4 insertions(+), 68 deletions(-) diff --git a/unix/window.c b/unix/window.c index a1d09005..3184a5a8 100644 --- a/unix/window.c +++ b/unix/window.c @@ -22,9 +22,6 @@ struct uiWindow { int (*onClosing)(uiWindow *, void *); void *onClosingData; - void (*onPositionChanged)(uiWindow *, void *); - void *onPositionChangedData; - gboolean changingPosition; void (*onContentSizeChanged)(uiWindow *, void *); void *onContentSizeChangedData; gboolean changingSize; @@ -42,19 +39,6 @@ static gboolean onClosing(GtkWidget *win, GdkEvent *e, gpointer data) return TRUE; } -static gboolean onConfigure(GtkWidget *win, GdkEvent *e, gpointer data) -{ - uiWindow *w = uiWindow(data); - - // there doesn't seem to be a way to determine if only moving or only resizing is happening :/ - if (w->changingPosition) - w->changingPosition = FALSE; - else - (*(w->onPositionChanged))(w, w->onPositionChangedData); - // always continue handling - return FALSE; -} - static void onSizeAllocate(GtkWidget *widget, GdkRectangle *allocation, gpointer data) { uiWindow *w = uiWindow(data); @@ -144,56 +128,6 @@ void uiWindowSetTitle(uiWindow *w, const char *title) gtk_window_set_title(w->window, title); } -// TODO allow specifying either as NULL on all platforms -void uiWindowPosition(uiWindow *w, int *x, int *y) -{ - gint rx, ry; - - gtk_window_get_position(w->window, &rx, &ry); - *x = rx; - *y = ry; -} - -void uiWindowSetPosition(uiWindow *w, int x, int y) -{ - w->changingPosition = TRUE; - gtk_window_move(w->window, x, y); - // gtk_window_move() is asynchronous - // we need to wait for a configure-event - // thanks to hergertme in irc.gimp.net/#gtk+ - while (w->changingPosition) - if (!uiMainStep(1)) - break; // stop early if uiQuit() called -} - -void uiWindowCenter(uiWindow *w) -{ - gint x, y; - GtkAllocation winalloc; - GdkWindow *gdkwin; - GdkScreen *screen; - GdkRectangle workarea; - - gtk_widget_get_allocation(w->widget, &winalloc); - gdkwin = gtk_widget_get_window(w->widget); - screen = gdk_window_get_screen(gdkwin); - gdk_screen_get_monitor_workarea(screen, - gdk_screen_get_monitor_at_window(screen, gdkwin), - &workarea); - - x = (workarea.width - winalloc.width) / 2; - y = (workarea.height - winalloc.height) / 2; - // TODO move up slightly? see what Mutter or GNOME Shell or GNOME Terminal do(es)? - uiWindowSetPosition(w, x, y); -} - -// TODO this and size changed get set during uiWindowDestroy -void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data) -{ - w->onPositionChanged = f; - w->onPositionChangedData = data; -} - void uiWindowContentSize(uiWindow *w, int *width, int *height) { GtkAllocation allocation; @@ -210,6 +144,7 @@ void uiWindowSetContentSize(uiWindow *w, int width, int height) { w->changingSize = TRUE; gtk_widget_set_size_request(w->childHolderWidget, width, height); + // MAJOR TODO REMOVE THIS. while (w->changingSize) if (!uiMainStep(1)) break; // stop early if uiQuit() called @@ -223,6 +158,7 @@ int uiWindowFullscreen(uiWindow *w) // TODO use window-state-event to track // TODO does this send an extra size changed? +// TODO what behavior do we want? void uiWindowSetFullscreen(uiWindow *w, int fullscreen) { w->fullscreen = fullscreen; @@ -317,10 +253,8 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) // and connect our events g_signal_connect(w->widget, "delete-event", G_CALLBACK(onClosing), w); - g_signal_connect(w->widget, "configure-event", G_CALLBACK(onConfigure), w); g_signal_connect(w->childHolderWidget, "size-allocate", G_CALLBACK(onSizeAllocate), w); uiWindowOnClosing(w, defaultOnClosing, NULL); - uiWindowOnPositionChanged(w, defaultOnPositionContentSizeChanged, NULL); uiWindowOnContentSizeChanged(w, defaultOnPositionContentSizeChanged, NULL); // normally it's SetParent() that does this, but we can't call SetParent() on a uiWindow diff --git a/windows/window.cpp b/windows/window.cpp index e0961c28..9cf13a25 100644 --- a/windows/window.cpp +++ b/windows/window.cpp @@ -387,6 +387,8 @@ int uiWindowBorderless(uiWindow *w) return w->borderless; } +// TODO window should move to the old client position and should not have the extra space the borders left behind +// TODO extract the relevant styles from WS_OVERLAPPEDWINDOW? void uiWindowSetBorderless(uiWindow *w, int borderless) { w->borderless = borderless; From 57379474f16b45777ee4de33d99efefe4ac15ce4 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 22 Oct 2016 19:34:53 -0400 Subject: [PATCH 0460/1329] And finished offing the movement window events. I think the others can stay, though they need some tweaks. --- README.md | 3 + _abort/windowevents/unix_window.c | 97 +++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 _abort/windowevents/unix_window.c diff --git a/README.md b/README.md index a3d29721..817e8c83 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,9 @@ This README is being written.
## Announcements +* **22 October 2016** + * Due to being unable to guarantee they will work (especially as we move toward capability-driven window systems like Wayland), or being unable to work without hacking that breaks other things, the following functions have been removed: `uiWindowPosition()`, `uiWindowSetPosition()`,`uiWindowCenter()`, and `uiWindowOnPositionChanged()`. Centering may come back at some point in the future, albeit in a possibly restricted form. A function to initiate a user move when a part of a uiArea is clicked will be provided soon. + * **21 October 2016** * `uiDrawTextWeightUltraBold` is now spelled correctly. Thanks to @krakjoe. diff --git a/_abort/windowevents/unix_window.c b/_abort/windowevents/unix_window.c new file mode 100644 index 00000000..96af26aa --- /dev/null +++ b/_abort/windowevents/unix_window.c @@ -0,0 +1,97 @@ +struct uiWindow { +// void *onClosingData; + void (*onPositionChanged)(uiWindow *, void *); + void *onPositionChangedData; + gboolean changingPosition; +// void (*onContentSizeChanged)(uiWindow *, void *); +}; + +// static gboolean onClosing(GtkWidget *win, GdkEvent *e, gpointer data) + +static gboolean onConfigure(GtkWidget *win, GdkEvent *e, gpointer data) +{ + uiWindow *w = uiWindow(data); + + // there doesn't seem to be a way to determine if only moving or only resizing is happening :/ + if (w->changingPosition) + w->changingPosition = FALSE; + else + (*(w->onPositionChanged))(w, w->onPositionChangedData); + // always continue handling + return FALSE; +} + +// static void onSizeAllocate(GtkWidget *widget, GdkRectangle *allocation, gpointer data) + +// static int defaultOnClosing(uiWindow *w, void *data) + +static void defaultOnPositionContentSizeChanged(uiWindow *w, void *data) +{ + // do nothing +} + +// static void uiWindowDestroy(uiControl *c) + +// void uiWindowSetTitle(uiWindow *w, const char *title) + +// TODO allow specifying either as NULL on all platforms +void uiWindowPosition(uiWindow *w, int *x, int *y) +{ + gint rx, ry; + + gtk_window_get_position(w->window, &rx, &ry); + *x = rx; + *y = ry; +} + +void uiWindowSetPosition(uiWindow *w, int x, int y) +{ + w->changingPosition = TRUE; + gtk_window_move(w->window, x, y); + // gtk_window_move() is asynchronous + // we need to wait for a configure-event + // thanks to hergertme in irc.gimp.net/#gtk+ + while (w->changingPosition) + if (!uiMainStep(1)) + break; // stop early if uiQuit() called +} + +void uiWindowCenter(uiWindow *w) +{ + gint x, y; + GtkAllocation winalloc; + GdkWindow *gdkwin; + GdkScreen *screen; + GdkRectangle workarea; + + gtk_widget_get_allocation(w->widget, &winalloc); + gdkwin = gtk_widget_get_window(w->widget); + screen = gdk_window_get_screen(gdkwin); + gdk_screen_get_monitor_workarea(screen, + gdk_screen_get_monitor_at_window(screen, gdkwin), + &workarea); + + x = (workarea.width - winalloc.width) / 2; + y = (workarea.height - winalloc.height) / 2; + // TODO move up slightly? see what Mutter or GNOME Shell or GNOME Terminal do(es)? + uiWindowSetPosition(w, x, y); +} + +// TODO this and size changed get set during uiWindowDestroy +void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data) +{ + w->onPositionChanged = f; + w->onPositionChangedData = data; +} + +// void uiWindowContentSize(uiWindow *w, int *width, int *height) + +uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) +{ +// g_signal_connect(w->widget, "delete-event", G_CALLBACK(onClosing), w); + g_signal_connect(w->widget, "configure-event", G_CALLBACK(onConfigure), w); +// g_signal_connect(w->childHolderWidget, "size-allocate", G_CALLBACK(onSizeAllocate), w); +// uiWindowOnClosing(w, defaultOnClosing, NULL); + uiWindowOnPositionChanged(w, defaultOnPositionContentSizeChanged, NULL); +// uiWindowOnContentSizeChanged(w, defaultOnPositionContentSizeChanged, NULL); +} From ce37d12d230cea529bf6f5ac1d3bc76b9a75bbbd Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 22 Oct 2016 19:36:32 -0400 Subject: [PATCH 0461/1329] Typo fixes. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 817e8c83..db76c603 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ This README is being written.
## Announcements * **22 October 2016** - * Due to being unable to guarantee they will work (especially as we move toward capability-driven window systems like Wayland), or being unable to work without hacking that breaks other things, the following functions have been removed: `uiWindowPosition()`, `uiWindowSetPosition()`,`uiWindowCenter()`, and `uiWindowOnPositionChanged()`. Centering may come back at some point in the future, albeit in a possibly restricted form. A function to initiate a user move when a part of a uiArea is clicked will be provided soon. + * Due to being unable to guarantee they will work (especially as we move toward capability-driven window systems like Wayland), or being unable to work without hacking that breaks other things, the following functions have been removed: `uiWindowPosition()`, `uiWindowSetPosition()`, `uiWindowCenter()`, and `uiWindowOnPositionChanged()`. Centering may come back at some point in the future, albeit in a possibly restricted form. A function to initiate a user move when a part of a uiArea is clicked will be provided soon. * **21 October 2016** * `uiDrawTextWeightUltraBold` is now spelled correctly. Thanks to @krakjoe. From 0870a3065ed219c3dd2332972a6ffb3b95ce199a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 23 Oct 2016 12:42:00 -0400 Subject: [PATCH 0462/1329] Quick Windows fixes now localized. --- README.md | 2 +- nowintable.diff | 50 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 nowintable.diff diff --git a/README.md b/README.md index db76c603..b71b19f5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Note: Table stuff is currently experimental; do not use in production code. +# Note: Table stuff is currently experimental; do not use in production code. It will not build on Windows as that part has not been written yet; if you want to test other parts of the Windows code, apply `nowintable.diff`. # libui: a portable GUI library for C diff --git a/nowintable.diff b/nowintable.diff new file mode 100644 index 00000000..cfbab07d --- /dev/null +++ b/nowintable.diff @@ -0,0 +1,50 @@ +diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt +index 7f70403..e909569 100644 +--- a/common/CMakeLists.txt ++++ b/common/CMakeLists.txt +@@ -6,7 +6,7 @@ list(APPEND _LIBUI_SOURCES + common/debug.c + common/matrix.c + common/shouldquit.c +- common/table.c ++# common/table.c + common/userbugs.c + ) + set(_LIBUI_SOURCES ${_LIBUI_SOURCES} PARENT_SCOPE) +diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt +index b753a7d..a648c64 100644 +--- a/test/CMakeLists.txt ++++ b/test/CMakeLists.txt +@@ -6,7 +6,7 @@ endif() + + _add_exec(tester + drawtests.c +- images.c ++# images.c + main.c + menus.c + page1.c +@@ -27,7 +27,7 @@ _add_exec(tester + page13.c + page14.c + page15.c +- page16.c ++# page16.c + spaced.c + ${_TEST_RESOURCES_RC} + ) +diff --git a/test/main.c b/test/main.c +index f33f30a..18774dc 100644 +--- a/test/main.c ++++ b/test/main.c +@@ -159,8 +159,8 @@ int main(int argc, char *argv[]) + innerTab = newTab(); + uiTabAppend(outerTab, "Pages 16-?", uiControl(innerTab)); + +- page16 = makePage16(); +- uiTabAppend(innerTab, "Page 16", uiControl(page16)); ++// page16 = makePage16(); ++// uiTabAppend(innerTab, "Page 16", uiControl(page16)); + + if (startspaced) + setSpaced(1); From e17e69f2adf0de07093b7ea4147ab95fb180d288 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 23 Oct 2016 22:02:07 -0400 Subject: [PATCH 0463/1329] Let's experiment with making uiWindowSetSize() not require an event loop. --- test/page15.c | 2 +- unix/window.c | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/test/page15.c b/test/page15.c index 24d96663..3480fc80 100644 --- a/test/page15.c +++ b/test/page15.c @@ -37,7 +37,7 @@ static void updatesize(uiWindow *w) void onSize(uiWindow *w, void *data) { - printf("size\n"); +//TODO_REINSTATE printf("size\n"); updatesize(w); } diff --git a/unix/window.c b/unix/window.c index 3184a5a8..c4455d27 100644 --- a/unix/window.c +++ b/unix/window.c @@ -28,6 +28,23 @@ struct uiWindow { gboolean fullscreen; }; +static void dbgPrintSizes(uiWindow *w, const char *prefix) +{ +#if 1 + GtkAllocation a; + gint ww, wh; + + gtk_widget_get_allocation(w->widget, &a); + g_print("%-14s| window width %d height %d\n", prefix, a.width, a.height); + gtk_widget_get_allocation(w->childHolderWidget, &a); + g_print("%-14s| client width %d height %d\n", prefix, a.width, a.height); + gtk_window_get_size(w->window, &ww, &wh); + g_print("%-14s| winsiz width %d height %d\n", prefix, ww, wh); +#else +#error forgot to remove dbgPrintSizes() (TODO) +#endif +} + static gboolean onClosing(GtkWidget *win, GdkEvent *e, gpointer data) { uiWindow *w = uiWindow(data); @@ -43,6 +60,8 @@ static void onSizeAllocate(GtkWidget *widget, GdkRectangle *allocation, gpointer { uiWindow *w = uiWindow(data); + dbgPrintSizes(w, "SIZE-ALLOCATE"); + if (w->changingSize) w->changingSize = FALSE; else @@ -132,9 +151,13 @@ void uiWindowContentSize(uiWindow *w, int *width, int *height) { GtkAllocation allocation; + dbgPrintSizes(w, "BEFORE GET"); + gtk_widget_get_allocation(w->childHolderWidget, &allocation); *width = allocation.width; *height = allocation.height; + + dbgPrintSizes(w, "AFTER GET"); } // TODO what happens if the size is already the current one? @@ -142,6 +165,8 @@ void uiWindowContentSize(uiWindow *w, int *width, int *height) // TODO can't reduce the size this way void uiWindowSetContentSize(uiWindow *w, int width, int height) { + dbgPrintSizes(w, "BEFORE SET"); + w->changingSize = TRUE; gtk_widget_set_size_request(w->childHolderWidget, width, height); // MAJOR TODO REMOVE THIS. @@ -149,6 +174,8 @@ void uiWindowSetContentSize(uiWindow *w, int width, int height) if (!uiMainStep(1)) break; // stop early if uiQuit() called gtk_widget_set_size_request(w->childHolderWidget, -1, -1); + + dbgPrintSizes(w, "AFTER SET"); } int uiWindowFullscreen(uiWindow *w) @@ -215,6 +242,16 @@ void uiWindowSetMargined(uiWindow *w, int margined) setMargined(w->childHolderContainer, w->margined); } +static gboolean TODO_REMOVE(GtkWidget *win, GdkEvent *e, gpointer data) +{ + uiWindow *w = uiWindow(data); + + dbgPrintSizes(w, "CONFIGURE"); + + // always continue handling + return FALSE; +} + uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) { uiWindow *w; @@ -261,5 +298,7 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) // TODO we really need to clean this up, especially since see uiWindowDestroy() above g_object_ref(w->widget); + g_signal_connect(w->widget, "configure-event", G_CALLBACK(TODO_REMOVE), w); + return w; } From 3f1540b84ade4a0ffb725018afaa5991fb00a0b5 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 24 Oct 2016 14:39:10 -0400 Subject: [PATCH 0464/1329] And got rid of message pumping in unix/window.c. Woo! Gotta clean it up a bit first though... --- unix/window.c | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/unix/window.c b/unix/window.c index c4455d27..2a469874 100644 --- a/unix/window.c +++ b/unix/window.c @@ -165,15 +165,35 @@ void uiWindowContentSize(uiWindow *w, int *width, int *height) // TODO can't reduce the size this way void uiWindowSetContentSize(uiWindow *w, int width, int height) { + GtkAllocation childAlloc; + gint winWidth, winHeight; + dbgPrintSizes(w, "BEFORE SET"); - w->changingSize = TRUE; - gtk_widget_set_size_request(w->childHolderWidget, width, height); - // MAJOR TODO REMOVE THIS. - while (w->changingSize) - if (!uiMainStep(1)) - break; // stop early if uiQuit() called - gtk_widget_set_size_request(w->childHolderWidget, -1, -1); + // we need to resize the child holder widget to the given size + // we can't resize that without running the event loop + // but we can do gtk_window_set_size() + // so how do we deal with the differences in sizes? + // simple arithmetic, of course! + + // from what I can tell, the return from gtk_widget_get_allocation(w->window) and gtk_window_get_size(w->window) will be the same + // this is not affected by Wayland and not affected by GTK+ builtin CSD + // so we can safely juse use them to get the real window size! + // since we're using gtk_window_resize(), use the latter + gtk_window_get_size(w->window, &winWidth, &winHeight); + + // now get the child holder widget's current allocation + gtk_widget_get_allocation(w->childHolderWidget, &childAlloc); + // and punch that out of the window size + winWidth -= childAlloc.width; + winHeight -= childAlloc.height; + + // now we just need to add the new size back in + winWidth += width; + winHeight += height; + // and set it + // TODO will this move the window? + gtk_window_resize(w->window, width, height); dbgPrintSizes(w, "AFTER SET"); } From 0c209a8277f23c113bba4eeff7c2ca075e558b07 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 24 Oct 2016 15:44:02 -0400 Subject: [PATCH 0465/1329] Whoops, bugged that last one bad (yet it worked fine in X11 for some reason????). Fixed. --- unix/window.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/unix/window.c b/unix/window.c index 2a469874..167282a3 100644 --- a/unix/window.c +++ b/unix/window.c @@ -160,9 +160,6 @@ void uiWindowContentSize(uiWindow *w, int *width, int *height) dbgPrintSizes(w, "AFTER GET"); } -// TODO what happens if the size is already the current one? -// TODO a spurious size-allocate gets sent after this function returns -// TODO can't reduce the size this way void uiWindowSetContentSize(uiWindow *w, int width, int height) { GtkAllocation childAlloc; @@ -192,8 +189,8 @@ void uiWindowSetContentSize(uiWindow *w, int width, int height) winWidth += width; winHeight += height; // and set it - // TODO will this move the window? - gtk_window_resize(w->window, width, height); + // this will not move the window in my tests, so we're good + gtk_window_resize(w->window, winWidth, winHeight); dbgPrintSizes(w, "AFTER SET"); } From fc243aed00d2685646f2d903c5d1175bd8c8a5f7 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 24 Oct 2016 17:47:23 -0400 Subject: [PATCH 0466/1329] Cleaned up debugging code and added the main loop bugfix to the README. --- README.md | 3 +++ test/page15.c | 2 +- unix/window.c | 46 ++-------------------------------------------- 3 files changed, 6 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index b71b19f5..4041f936 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,9 @@ This README is being written.
## Announcements +* **widget, &a); - g_print("%-14s| window width %d height %d\n", prefix, a.width, a.height); - gtk_widget_get_allocation(w->childHolderWidget, &a); - g_print("%-14s| client width %d height %d\n", prefix, a.width, a.height); - gtk_window_get_size(w->window, &ww, &wh); - g_print("%-14s| winsiz width %d height %d\n", prefix, ww, wh); -#else -#error forgot to remove dbgPrintSizes() (TODO) -#endif -} - static gboolean onClosing(GtkWidget *win, GdkEvent *e, gpointer data) { uiWindow *w = uiWindow(data); @@ -60,12 +42,8 @@ static void onSizeAllocate(GtkWidget *widget, GdkRectangle *allocation, gpointer { uiWindow *w = uiWindow(data); - dbgPrintSizes(w, "SIZE-ALLOCATE"); - - if (w->changingSize) - w->changingSize = FALSE; - else - (*(w->onContentSizeChanged))(w, w->onContentSizeChangedData); + // TODO deal with spurious size-allocates + (*(w->onContentSizeChanged))(w, w->onContentSizeChangedData); } static int defaultOnClosing(uiWindow *w, void *data) @@ -151,13 +129,9 @@ void uiWindowContentSize(uiWindow *w, int *width, int *height) { GtkAllocation allocation; - dbgPrintSizes(w, "BEFORE GET"); - gtk_widget_get_allocation(w->childHolderWidget, &allocation); *width = allocation.width; *height = allocation.height; - - dbgPrintSizes(w, "AFTER GET"); } void uiWindowSetContentSize(uiWindow *w, int width, int height) @@ -165,8 +139,6 @@ void uiWindowSetContentSize(uiWindow *w, int width, int height) GtkAllocation childAlloc; gint winWidth, winHeight; - dbgPrintSizes(w, "BEFORE SET"); - // we need to resize the child holder widget to the given size // we can't resize that without running the event loop // but we can do gtk_window_set_size() @@ -191,8 +163,6 @@ void uiWindowSetContentSize(uiWindow *w, int width, int height) // and set it // this will not move the window in my tests, so we're good gtk_window_resize(w->window, winWidth, winHeight); - - dbgPrintSizes(w, "AFTER SET"); } int uiWindowFullscreen(uiWindow *w) @@ -259,16 +229,6 @@ void uiWindowSetMargined(uiWindow *w, int margined) setMargined(w->childHolderContainer, w->margined); } -static gboolean TODO_REMOVE(GtkWidget *win, GdkEvent *e, gpointer data) -{ - uiWindow *w = uiWindow(data); - - dbgPrintSizes(w, "CONFIGURE"); - - // always continue handling - return FALSE; -} - uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) { uiWindow *w; @@ -315,7 +275,5 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) // TODO we really need to clean this up, especially since see uiWindowDestroy() above g_object_ref(w->widget); - g_signal_connect(w->widget, "configure-event", G_CALLBACK(TODO_REMOVE), w); - return w; } From 8556fc7cd1b73ce9d180bddb575587a263c52f30 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 24 Oct 2016 23:08:55 -0400 Subject: [PATCH 0467/1329] Started work on custom user resizes. --- test/page15.c | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) diff --git a/test/page15.c b/test/page15.c index 24d96663..d8051ce1 100644 --- a/test/page15.c +++ b/test/page15.c @@ -1,6 +1,152 @@ // 15 june 2016 #include "test.h" +static uiAreaHandler borderAH; +static int borderAHInit = 0; +static double lastx = -1, lasty = -1; + +struct trect { + double left; + double top; + double right; + double bottom; + int in; +}; + +#define tsetrect(re, l, t, r, b) re.left = l; re.top = t; re.right = r; re.bottom = b; re.in = lastx >= re.left && lastx < re.right && lasty >= re.top && lasty < re.bottom + +struct tareas { + struct trect move; + struct trect leftresize; + struct trect topresize; + struct trect rightresize; + struct trect bottomresize; + // TODO have corner resize rects + // TODO have a close button rect +}; + +static void filltareas(double awid, double aht, struct tareas *ta) +{ + tsetrect(ta->move, 20, 20, awid - 20, 20 + 30); + tsetrect(ta->leftresize, 5, 20, 5 + 10, aht - 20); + tsetrect(ta->topresize, 20, 5, awid - 20, 5 + 10); + tsetrect(ta->rightresize, awid - 20 + 5, 20, awid - 5, aht - 20); + tsetrect(ta->bottomresize, 20, aht - 20 + 5, awid - 20, aht - 5); +} + +static void drawtrect(uiDrawContext *c, struct trect tr, double r, double g, double bl) +{ + uiDrawPath *p; + uiDrawBrush b; + + memset(&b, 0, sizeof (uiDrawBrush)); + b.Type = uiDrawBrushTypeSolid; + b.R = r; + b.G = g; + b.B = bl; + b.A = 1.0; + if (tr.in) { + b.R += b.R * 0.75; + b.G += b.G * 0.75; + b.B += b.B * 0.75; + } + p = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathAddRectangle(p, + tr.left, + tr.top, + tr.right - tr.left, + tr.bottom - tr.top); + uiDrawPathEnd(p); + uiDrawFill(c, p, &b); + uiDrawFreePath(p); +} + +static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *p) +{ + struct tareas ta; + + filltareas(p->AreaWidth, p->AreaHeight, &ta); + drawtrect(p->Context, ta.move, 0, 0.5, 0); + drawtrect(p->Context, ta.leftresize, 0, 0, 0.5); + drawtrect(p->Context, ta.topresize, 0, 0, 0.5); + drawtrect(p->Context, ta.rightresize, 0, 0, 0.5); + drawtrect(p->Context, ta.bottomresize, 0, 0, 0.5); +} + +static void handlerMouseEvent(uiAreaHandler *a, uiArea *area, uiAreaMouseEvent *e) +{ + struct tareas ta; + + lastx = e->X; + lasty = e->Y; + filltareas(e->AreaWidth, e->AreaHeight, &ta); + // redraw our highlighted rect + uiAreaQueueRedrawAll(area); + if (e->Down != 1) + return; + if (ta.move.in) { + // TODO + return; + } + if (ta.leftresize.in) { + // TODO + return; + } + if (ta.topresize.in) { + // TODO + return; + } + if (ta.rightresize.in) { + // TODO + return; + } + if (ta.bottomresize.in) { + // TODO + return; + } +} + +static void handlerMouseCrossed(uiAreaHandler *ah, uiArea *a, int left) +{ +} + +static void handlerDragBroken(uiAreaHandler *ah, uiArea *a) +{ +} + +static int handlerKeyEvent(uiAreaHandler *ah, uiArea *a, uiAreaKeyEvent *e) +{ + return 0; +} + +static void borderWindowOpen(uiButton *b, void *data) +{ + uiWindow *w; + uiArea *a; + + if (!borderAHInit) { + borderAH.Draw = handlerDraw; + borderAH.MouseEvent = handlerMouseEvent; + borderAH.MouseCrossed = handlerMouseCrossed; + borderAH.DragBroken = handlerDragBroken; + borderAH.KeyEvent = handlerKeyEvent; + borderAHInit = 1; + } + + w = uiNewWindow("Border Resize Test", 300, 500, 0); + uiWindowSetBorderless(w, 1); + + a = uiNewArea(&borderAH); +// uiWindowSetChild(w, uiControl(a)); +{uiBox *b; +b=uiNewHorizontalBox(); +uiBoxAppend(b,uiControl(a),1); +uiWindowSetChild(w,uiControl(b));} +//TODO why is this hack needed? GTK+ issue + + uiControlShow(uiControl(w)); +} + static uiSpinbox *width, *height; static uiCheckbox *fullscreen; @@ -86,6 +232,10 @@ uiBox *makePage15(uiWindow *w) uiCheckboxOnToggled(checkbox, borderless, w); uiBoxAppend(page15, uiControl(checkbox), 0); + button = uiNewButton("Borderless Resizes"); + uiButtonOnClicked(button, borderWindowOpen, NULL); + uiBoxAppend(page15, uiControl(button), 0); + hbox = newHorizontalBox(); uiBoxAppend(page15, uiControl(hbox), 1); From 1871f7139d8ceb899a94e83885258bccc683c02b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 24 Oct 2016 23:27:44 -0400 Subject: [PATCH 0468/1329] Some more test areas in the borderless resize test. Now to actually spec out the API (hint: it's two functions in uiArea). --- test/page15.c | 50 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/test/page15.c b/test/page15.c index d8051ce1..1b3facb4 100644 --- a/test/page15.c +++ b/test/page15.c @@ -17,21 +17,31 @@ struct trect { struct tareas { struct trect move; + struct trect alsomove; struct trect leftresize; struct trect topresize; struct trect rightresize; struct trect bottomresize; - // TODO have corner resize rects - // TODO have a close button rect + struct trect topleftresize; + struct trect toprightresize; + struct trect bottomleftresize; + struct trect bottomrightresize; + struct trect close; }; static void filltareas(double awid, double aht, struct tareas *ta) { tsetrect(ta->move, 20, 20, awid - 20, 20 + 30); - tsetrect(ta->leftresize, 5, 20, 5 + 10, aht - 20); - tsetrect(ta->topresize, 20, 5, awid - 20, 5 + 10); - tsetrect(ta->rightresize, awid - 20 + 5, 20, awid - 5, aht - 20); - tsetrect(ta->bottomresize, 20, aht - 20 + 5, awid - 20, aht - 5); + tsetrect(ta->alsomove, 30, 200, 100, 270); + tsetrect(ta->leftresize, 5, 20, 15, aht - 20); + tsetrect(ta->topresize, 20, 5, awid - 20, 15); + tsetrect(ta->rightresize, awid - 15, 20, awid - 5, aht - 20); + tsetrect(ta->bottomresize, 20, aht - 15, awid - 20, aht - 5); + tsetrect(ta->topleftresize, 5, 5, 15, 15); + tsetrect(ta->toprightresize, awid - 15, 5, awid - 5, 15); + tsetrect(ta->bottomleftresize, 5, aht - 15, 15, aht - 5); + tsetrect(ta->bottomrightresize, awid - 15, aht - 15, awid - 5, aht - 5); + tsetrect(ta->close, 130, 200, 200, 270); } static void drawtrect(uiDrawContext *c, struct trect tr, double r, double g, double bl) @@ -67,10 +77,16 @@ static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *p) filltareas(p->AreaWidth, p->AreaHeight, &ta); drawtrect(p->Context, ta.move, 0, 0.5, 0); + drawtrect(p->Context, ta.alsomove, 0, 0.5, 0); drawtrect(p->Context, ta.leftresize, 0, 0, 0.5); drawtrect(p->Context, ta.topresize, 0, 0, 0.5); drawtrect(p->Context, ta.rightresize, 0, 0, 0.5); drawtrect(p->Context, ta.bottomresize, 0, 0, 0.5); + drawtrect(p->Context, ta.topleftresize, 0, 0.5, 0.5); + drawtrect(p->Context, ta.toprightresize, 0, 0.5, 0.5); + drawtrect(p->Context, ta.bottomleftresize, 0, 0.5, 0.5); + drawtrect(p->Context, ta.bottomrightresize, 0, 0.5, 0.5); + drawtrect(p->Context, ta.close, 0.5, 0, 0); } static void handlerMouseEvent(uiAreaHandler *a, uiArea *area, uiAreaMouseEvent *e) @@ -84,7 +100,7 @@ static void handlerMouseEvent(uiAreaHandler *a, uiArea *area, uiAreaMouseEvent * uiAreaQueueRedrawAll(area); if (e->Down != 1) return; - if (ta.move.in) { + if (ta.move.in || ta.alsomove.in) { // TODO return; } @@ -104,6 +120,26 @@ static void handlerMouseEvent(uiAreaHandler *a, uiArea *area, uiAreaMouseEvent * // TODO return; } + if (ta.topleftresize.in) { + // TODO + return; + } + if (ta.toprightresize.in) { + // TODO + return; + } + if (ta.bottomleftresize.in) { + // TODO + return; + } + if (ta.bottomrightresize.in) { + // TODO + return; + } + if (ta.close.in) { + // TODO + return; + } } static void handlerMouseCrossed(uiAreaHandler *ah, uiArea *a, int left) From 67ff2fa8550f95d0aab63c96bbb4fa753a1f1799 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 24 Oct 2016 23:35:18 -0400 Subject: [PATCH 0469/1329] Laid out the interface for user window drags. --- test/page15.c | 43 ++++++++++--------------------------------- ui.h | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+), 33 deletions(-) diff --git a/test/page15.c b/test/page15.c index 1b3facb4..99e402e3 100644 --- a/test/page15.c +++ b/test/page15.c @@ -101,41 +101,18 @@ static void handlerMouseEvent(uiAreaHandler *a, uiArea *area, uiAreaMouseEvent * if (e->Down != 1) return; if (ta.move.in || ta.alsomove.in) { - // TODO - return; - } - if (ta.leftresize.in) { - // TODO - return; - } - if (ta.topresize.in) { - // TODO - return; - } - if (ta.rightresize.in) { - // TODO - return; - } - if (ta.bottomresize.in) { - // TODO - return; - } - if (ta.topleftresize.in) { - // TODO - return; - } - if (ta.toprightresize.in) { - // TODO - return; - } - if (ta.bottomleftresize.in) { - // TODO - return; - } - if (ta.bottomrightresize.in) { - // TODO + uiAreaBeginUserWindowMove(area); return; } +#define resize(cond, edge) if (cond) { uiAreaBeginUserWindowResize(area, edge); return; } + resize(ta.leftresize.in, uiWindowResizeEdgeLeft) + resize(ta.topresize.in, uiWindowResizeEdgeTop) + resize((ta.rightresize.in, uiWindowResizeEdgeRight) + resize(ta.bottomresize.in, uiWindowResizeEdgeBottom) + resize(ta.topleftresize.in, uiWindowResizeEdgeTopLeft) + resize(ta.toprightresize.in, uiWindowResizeEdgeTopRight) + resize(ta.bottomleftresize.in, uiWindowResizeEdgeBottomLeft) + resize(ta.bottomrightresize.in, uiWindowResizeEdgeBottomRight) if (ta.close.in) { // TODO return; diff --git a/ui.h b/ui.h index 5cf01b50..9dbc32b6 100644 --- a/ui.h +++ b/ui.h @@ -289,6 +289,20 @@ struct uiAreaHandler { int (*KeyEvent)(uiAreaHandler *, uiArea *, uiAreaKeyEvent *); }; +// TODO RTL layouts? +// TODO reconcile edge and corner naming +_UI_ENUM(uiWindowResizeEdge) { + uiWindowResizeEdgeLeft, + uiWindowResizeEdgeTop, + uiWindowResizeEdgeRight, + uiWindowResizeEdgeBottom, + uiWindowResizeEdgeTopLeft, + uiWindowResizeEdgeTopRight, + uiWindowResizeEdgeBottomLeft, + uiWindowResizeEdgeBottomRight, + // TODO have one for keyboard resizes? +}; + #define uiArea(this) ((uiArea *) (this)) // TODO give a better name // TODO document the types of width and height @@ -296,6 +310,10 @@ _UI_EXTERN void uiAreaSetSize(uiArea *a, int width, int height); // TODO uiAreaQueueRedraw() _UI_EXTERN void uiAreaQueueRedrawAll(uiArea *a); _UI_EXTERN void uiAreaScrollTo(uiArea *a, double x, double y, double width, double height); +// TODO document these can only be called within Mouse() handlers +// TODO should these be allowed on scrolling areas? +_UI_EXTERN void uiAreaBeginUserWindowMove(uiArea *a); +_UI_EXTERN void uiAreaBeginUserWindowResize(uiArea *a, uiWindowResizeEdge edge); _UI_EXTERN uiArea *uiNewArea(uiAreaHandler *ah); _UI_EXTERN uiArea *uiNewScrollingArea(uiAreaHandler *ah, int width, int height); From 22caa5e502f50bd399643df28ea127783f0374eb Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 25 Oct 2016 00:34:12 -0400 Subject: [PATCH 0470/1329] Implemented the window drag stuff on GTK+. It works! --- test/page15.c | 2 +- ui.h | 3 ++ unix/area.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 1 deletion(-) diff --git a/test/page15.c b/test/page15.c index 99e402e3..2c9728bf 100644 --- a/test/page15.c +++ b/test/page15.c @@ -107,7 +107,7 @@ static void handlerMouseEvent(uiAreaHandler *a, uiArea *area, uiAreaMouseEvent * #define resize(cond, edge) if (cond) { uiAreaBeginUserWindowResize(area, edge); return; } resize(ta.leftresize.in, uiWindowResizeEdgeLeft) resize(ta.topresize.in, uiWindowResizeEdgeTop) - resize((ta.rightresize.in, uiWindowResizeEdgeRight) + resize(ta.rightresize.in, uiWindowResizeEdgeRight) resize(ta.bottomresize.in, uiWindowResizeEdgeBottom) resize(ta.topleftresize.in, uiWindowResizeEdgeTopLeft) resize(ta.toprightresize.in, uiWindowResizeEdgeTopRight) diff --git a/ui.h b/ui.h index 9dbc32b6..518d366a 100644 --- a/ui.h +++ b/ui.h @@ -301,6 +301,8 @@ _UI_ENUM(uiWindowResizeEdge) { uiWindowResizeEdgeBottomLeft, uiWindowResizeEdgeBottomRight, // TODO have one for keyboard resizes? + // TODO GDK doesn't seem to have any others, including for keyboards... + // TODO way to bring up the system menu instead? }; #define uiArea(this) ((uiArea *) (this)) @@ -312,6 +314,7 @@ _UI_EXTERN void uiAreaQueueRedrawAll(uiArea *a); _UI_EXTERN void uiAreaScrollTo(uiArea *a, double x, double y, double width, double height); // TODO document these can only be called within Mouse() handlers // TODO should these be allowed on scrolling areas? +// TODO decide which mouse events should be accepted; Down is the only one guaranteed to work right now _UI_EXTERN void uiAreaBeginUserWindowMove(uiArea *a); _UI_EXTERN void uiAreaBeginUserWindowResize(uiArea *a, uiWindowResizeEdge edge); _UI_EXTERN uiArea *uiNewArea(uiAreaHandler *ah); diff --git a/unix/area.c b/unix/area.c index c5b490d6..c46447cc 100644 --- a/unix/area.c +++ b/unix/area.c @@ -46,6 +46,9 @@ struct uiArea { // note that this is a pointer; see above clickCounter *cc; + + // for user window drags + GdkEventButton *dragevent; }; G_DEFINE_TYPE(areaWidget, areaWidget, GTK_TYPE_DRAWING_AREA) @@ -261,7 +264,11 @@ static gboolean areaWidget_button_press_event(GtkWidget *w, GdkEventButton *e) me.Down = e->button; me.Up = 0; + + // and set things up for window drags + a->dragevent = e; finishMouseEvent(a, &me, e->button, e->x, e->y, e->state, e->window); + a->dragevent = NULL; return GDK_EVENT_PROPAGATE; } @@ -505,6 +512,76 @@ void uiAreaScrollTo(uiArea *a, double x, double y, double width, double height) // TODO adjust adjustments and find source for that } +void uiAreaBeginUserWindowMove(uiArea *a) +{ + GtkWidget *toplevel; + + if (a->dragevent == NULL) + userbug("cannot call uiAreaBeginUserWindowMove() outside of a Mouse() with Down != 0"); + // TODO don't we have a libui function for this? did I scrap it? + // TODO widget or areaWidget? + toplevel = gtk_widget_get_toplevel(a->widget); + if (toplevel == NULL) { + // TODO + return; + } + // the docs say to do this + if (!gtk_widget_is_toplevel(toplevel)) { + // TODO + return; + } + if (!GTK_IS_WINDOW(toplevel)) { + // TODO + return; + } + gtk_window_begin_move_drag(GTK_WINDOW(toplevel), + a->dragevent->button, + a->dragevent->x_root, // TODO are these correct? + a->dragevent->y_root, + a->dragevent->time); +} + +static const GdkWindowEdge edges[] = { + [uiWindowResizeEdgeLeft] = GDK_WINDOW_EDGE_WEST, + [uiWindowResizeEdgeTop] = GDK_WINDOW_EDGE_NORTH, + [uiWindowResizeEdgeRight] = GDK_WINDOW_EDGE_EAST, + [uiWindowResizeEdgeBottom] = GDK_WINDOW_EDGE_SOUTH, + [uiWindowResizeEdgeTopLeft] = GDK_WINDOW_EDGE_NORTH_WEST, + [uiWindowResizeEdgeTopRight] = GDK_WINDOW_EDGE_NORTH_EAST, + [uiWindowResizeEdgeBottomLeft] = GDK_WINDOW_EDGE_SOUTH_WEST, + [uiWindowResizeEdgeBottomRight] = GDK_WINDOW_EDGE_SOUTH_EAST, +}; + +void uiAreaBeginUserWindowResize(uiArea *a, uiWindowResizeEdge edge) +{ + GtkWidget *toplevel; + + if (a->dragevent == NULL) + userbug("cannot call uiAreaBeginUserWindowResize() outside of a Mouse() with Down != 0"); + // TODO don't we have a libui function for this? did I scrap it? + // TODO widget or areaWidget? + toplevel = gtk_widget_get_toplevel(a->widget); + if (toplevel == NULL) { + // TODO + return; + } + // the docs say to do this + if (!gtk_widget_is_toplevel(toplevel)) { + // TODO + return; + } + if (!GTK_IS_WINDOW(toplevel)) { + // TODO + return; + } + gtk_window_begin_resize_drag(GTK_WINDOW(toplevel), + edges[edge], + a->dragevent->button, + a->dragevent->x_root, // TODO are these correct? + a->dragevent->y_root, + a->dragevent->time); +} + uiArea *uiNewArea(uiAreaHandler *ah) { uiArea *a; From 1fbfc9dd67fa7d7c47eb29afa970d0425e7b9925 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 25 Oct 2016 01:00:18 -0400 Subject: [PATCH 0471/1329] More TODOs. --- test/page15.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/page15.c b/test/page15.c index 2c9728bf..e703bee2 100644 --- a/test/page15.c +++ b/test/page15.c @@ -87,6 +87,8 @@ static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *p) drawtrect(p->Context, ta.bottomleftresize, 0, 0.5, 0.5); drawtrect(p->Context, ta.bottomrightresize, 0, 0.5, 0.5); drawtrect(p->Context, ta.close, 0.5, 0, 0); + + // TODO add current position prints here } static void handlerMouseEvent(uiAreaHandler *a, uiArea *area, uiAreaMouseEvent *e) From 7abe97ec5786a7a1f6c102fe901e5e6717138b56 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 25 Oct 2016 01:11:03 -0400 Subject: [PATCH 0472/1329] More TODOs. --- ui.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ui.h b/ui.h index 518d366a..9f421d37 100644 --- a/ui.h +++ b/ui.h @@ -315,6 +315,7 @@ _UI_EXTERN void uiAreaScrollTo(uiArea *a, double x, double y, double width, doub // TODO document these can only be called within Mouse() handlers // TODO should these be allowed on scrolling areas? // TODO decide which mouse events should be accepted; Down is the only one guaranteed to work right now +// TODO what happens to events after calling this up to and including the next mouse up? _UI_EXTERN void uiAreaBeginUserWindowMove(uiArea *a); _UI_EXTERN void uiAreaBeginUserWindowResize(uiArea *a, uiWindowResizeEdge edge); _UI_EXTERN uiArea *uiNewArea(uiAreaHandler *ah); From 7fbbba37f617924fcdab5dc90cf1297b69764d09 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 25 Oct 2016 13:14:32 -0400 Subject: [PATCH 0473/1329] Added notes for adding the new uiArea functions to OS X. --- osxbordersize | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 osxbordersize diff --git a/osxbordersize b/osxbordersize new file mode 100644 index 00000000..83140bdf --- /dev/null +++ b/osxbordersize @@ -0,0 +1,9 @@ +https://developer.apple.com/library/content/samplecode/RoundTransparentWindow/Listings/Classes_CustomWindow_m.html#//apple_ref/doc/uid/DTS10000401-Classes_CustomWindow_m-DontLinkElementID_8 +https://git.gnome.org/browse/gtk+/tree/gdk/quartz/GdkQuartzNSWindow.c +http://www.cocoawithlove.com/2008/12/drawing-custom-window-on-mac-os-x.html +http://www.cocoabuilder.com/archive/cocoa/129590-moving-borderless-window.html +http://stackoverflow.com/questions/7245725/all-sides-resize-of-borderless-nswindow-in-lion +http://stackoverflow.com/questions/15782176/how-to-make-a-custom-window-resizable-from-all-sides +http://www.cocoabuilder.com/archive/cocoa/97795-resizable-borderless-window.html +http://www.cocoabuilder.com/archive/cocoa/229815-borderless-and-resize.html +https://web.archive.org/web/20160409030454/http://cocoadev.com/BorderlessWindow From 9d754bbf2a3afafb638f736d8bdf8f852f756416 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 25 Oct 2016 23:24:13 -0400 Subject: [PATCH 0474/1329] Implemented the new functions on Windows. Now to test. --- ui.h | 1 + windows/area.cpp | 60 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/ui.h b/ui.h index 9f421d37..5852bf6d 100644 --- a/ui.h +++ b/ui.h @@ -316,6 +316,7 @@ _UI_EXTERN void uiAreaScrollTo(uiArea *a, double x, double y, double width, doub // TODO should these be allowed on scrolling areas? // TODO decide which mouse events should be accepted; Down is the only one guaranteed to work right now // TODO what happens to events after calling this up to and including the next mouse up? +// TODO release capture? _UI_EXTERN void uiAreaBeginUserWindowMove(uiArea *a); _UI_EXTERN void uiAreaBeginUserWindowResize(uiArea *a, uiWindowResizeEdge edge); _UI_EXTERN uiArea *uiNewArea(uiAreaHandler *ah); diff --git a/windows/area.cpp b/windows/area.cpp index 17110d33..6d1bbeb9 100644 --- a/windows/area.cpp +++ b/windows/area.cpp @@ -98,6 +98,66 @@ void uiAreaScrollTo(uiArea *a, double x, double y, double width, double height) // TODO } +void uiAreaBeginUserWindowMove(uiArea *a) +{ + HWND toplevel; + + // TODO restrict execution + // TODO release capture + toplevel = xxxx(a->hwnd); + if (toplevel == NULL) { + // TODO + return; + } + // see http://stackoverflow.com/questions/40249940/how-do-i-initiate-a-user-mouse-driven-move-or-resize-for-custom-window-borders-o#40250654 + SendMessageW(toplevel, WM_SYSCOMMAND, + SC_MOVE | 2, 0); +} + +void uiAreaBeginUserWindowResize(uiArea *a, uiWindowResizeEdge edge) +{ + HWND toplevel; + WPARAM wParam; + + // TODO restrict execution + // TODO release capture + toplevel = parentToplevel(a->hwnd); + if (toplevel == NULL) { + // TODO + return; + } + // see http://stackoverflow.com/questions/40249940/how-do-i-initiate-a-user-mouse-driven-move-or-resize-for-custom-window-borders-o#40250654 + wParam = SC_SIZE; + switch (edge) { + case uiWindowResizeEdgeLeft, + wParam |= 1; + break + case uiWindowResizeEdgeTop, + wParam |= 3; + break + case uiWindowResizeEdgeRight, + wParam |= 2; + break + case uiWindowResizeEdgeBottom, + wParam |= 6; + break + case uiWindowResizeEdgeTopLeft, + wParam |= 4; + break + case uiWindowResizeEdgeTopRight, + wParam |= 5; + break + case uiWindowResizeEdgeBottomLeft, + wParam |= 7; + break + case uiWindowResizeEdgeBottomRight: + wParam |= 8; + break + } + SendMessageW(toplevel, WM_SYSCOMMAND, + wParam, 0); +} + uiArea *uiNewArea(uiAreaHandler *ah) { uiArea *a; From 8819d9cd5859534e047f75f56c322c5e3ed60611 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 25 Oct 2016 23:41:37 -0400 Subject: [PATCH 0475/1329] Fixed the new Windows uiArea functions. --- windows/area.cpp | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/windows/area.cpp b/windows/area.cpp index 6d1bbeb9..a86371e9 100644 --- a/windows/area.cpp +++ b/windows/area.cpp @@ -103,8 +103,8 @@ void uiAreaBeginUserWindowMove(uiArea *a) HWND toplevel; // TODO restrict execution - // TODO release capture - toplevel = xxxx(a->hwnd); + ReleaseCapture(); // TODO use properly and reset internal data structures + toplevel = parentToplevel(a->hwnd); if (toplevel == NULL) { // TODO return; @@ -120,7 +120,7 @@ void uiAreaBeginUserWindowResize(uiArea *a, uiWindowResizeEdge edge) WPARAM wParam; // TODO restrict execution - // TODO release capture + ReleaseCapture(); // TODO use properly and reset internal data structures toplevel = parentToplevel(a->hwnd); if (toplevel == NULL) { // TODO @@ -129,30 +129,30 @@ void uiAreaBeginUserWindowResize(uiArea *a, uiWindowResizeEdge edge) // see http://stackoverflow.com/questions/40249940/how-do-i-initiate-a-user-mouse-driven-move-or-resize-for-custom-window-borders-o#40250654 wParam = SC_SIZE; switch (edge) { - case uiWindowResizeEdgeLeft, + case uiWindowResizeEdgeLeft: wParam |= 1; - break - case uiWindowResizeEdgeTop, + break; + case uiWindowResizeEdgeTop: wParam |= 3; - break - case uiWindowResizeEdgeRight, + break; + case uiWindowResizeEdgeRight: wParam |= 2; - break - case uiWindowResizeEdgeBottom, + break; + case uiWindowResizeEdgeBottom: wParam |= 6; - break - case uiWindowResizeEdgeTopLeft, + break; + case uiWindowResizeEdgeTopLeft: wParam |= 4; - break - case uiWindowResizeEdgeTopRight, + break; + case uiWindowResizeEdgeTopRight: wParam |= 5; - break - case uiWindowResizeEdgeBottomLeft, + break; + case uiWindowResizeEdgeBottomLeft: wParam |= 7; - break + break; case uiWindowResizeEdgeBottomRight: wParam |= 8; - break + break; } SendMessageW(toplevel, WM_SYSCOMMAND, wParam, 0); From cc1942a929e4876d45c670be5337d59f21fc9254 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 26 Oct 2016 01:15:01 -0400 Subject: [PATCH 0476/1329] Prepared OS X for the new uiArea functions. --- darwin/area.m | 10 ++++++++++ darwin/uipriv_darwin.h | 4 ++++ darwin/window.m | 16 +++++++++++++++- 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/darwin/area.m b/darwin/area.m index b98aa429..6c6ac4b7 100644 --- a/darwin/area.m +++ b/darwin/area.m @@ -401,6 +401,16 @@ void uiAreaScrollTo(uiArea *a, double x, double y, double width, double height) // don't worry about the return value; it just says whether scrolling was needed } +void uiAreaBeginUserWindowMove(uiArea *a) +{ + // TODO +} + +void uiAreaBeginUserWindowResize(uiArea *a, uiWindowResizeEdge edge) +{ + // TODO +} + uiArea *uiNewArea(uiAreaHandler *ah) { uiArea *a; diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 61f5296f..a4e44fac 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -52,6 +52,10 @@ extern void finishNewTextField(NSTextField *, BOOL); extern NSTextField *newEditableTextField(void); // window.m +@interface libuiNSWindow : NSWindow +- (void)libui_doMove:(NSEvent *)initialEvent; +- (void)libui_doResize:(NSEvent *)initialEvent on:(uiWindowResizeEdge)edge; +@end extern uiWindow *windowFromNSWindow(NSWindow *); // alloc.m diff --git a/darwin/window.m b/darwin/window.m index bba2e1b4..4164f07a 100644 --- a/darwin/window.m +++ b/darwin/window.m @@ -18,6 +18,20 @@ struct uiWindow { int borderless; }; +@implementation libuiNSWindow + +- (void)libui_doMove:(NSEvent *)initialEvent +{ + // TODO +} + +- (void)libui_doResize:(NSEvent *)initialEvent on:(uiWindowResizeEdge)edge +{ + // TODO +} + +@end + @interface windowDelegateClass : NSObject { struct mapTable *windows; } @@ -361,7 +375,7 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) uiDarwinNewControl(uiWindow, w); - w->window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, (CGFloat) width, (CGFloat) height) + w->window = [[libuiNSWindow alloc] initWithContentRect:NSMakeRect(0, 0, (CGFloat) width, (CGFloat) height) styleMask:defaultStyleMask backing:NSBackingStoreBuffered defer:YES]; From 5de62d073f189a92af7b5386ad1ea184c21d1e14 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 26 Oct 2016 09:39:43 -0400 Subject: [PATCH 0477/1329] Cal [NSApp finishLaunching]. Fixes #182. --- darwin/main.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/darwin/main.m b/darwin/main.m index 2c934330..7dbea05f 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -161,6 +161,8 @@ void uiMain(void) void uiMainSteps(void) { + // SDL does this and it seems to be necessary for the menubar to work (see #182) + [realNSApp() finishLaunching]; isRunning = ^{ return stepsIsRunning; }; From 8cbae7d2b79733fc44fd7b5557c31d912756cda2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 27 Oct 2016 20:51:37 -0400 Subject: [PATCH 0478/1329] More TODOs. --- darwin/radiobuttons.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/darwin/radiobuttons.m b/darwin/radiobuttons.m index 9a38981a..25d773c9 100644 --- a/darwin/radiobuttons.m +++ b/darwin/radiobuttons.m @@ -1,6 +1,8 @@ // 14 august 2015 #import "uipriv_darwin.h" +// TODO resizing the controlgallery vertically causes the third button to still resize :| + // In the old days you would use a NSMatrix for this; as of OS X 10.8 this was deprecated and now you need just a bunch of NSButtons with the same superview AND same action method. // This is documented on the NSMatrix page, but the rest of the OS X documentation says to still use NSMatrix. // NSMatrix has weird quirks anyway... From 39fdf7457b1e53672808f4133bec9e17d9ea5f73 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 27 Oct 2016 23:32:33 -0400 Subject: [PATCH 0479/1329] More preparation for the OS X window move/resize code: uiMainStep() is split into an internal function that takes a pre-sendEvent: interception specifically intended for internal loops. --- darwin/main.m | 12 ++++++++++-- darwin/uipriv_darwin.h | 3 ++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/darwin/main.m b/darwin/main.m index 7dbea05f..054a79af 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -169,10 +169,17 @@ void uiMainSteps(void) stepsIsRunning = YES; } +int uiMainStep(int wait) +{ + return mainStep(wait, ^(NSEvent *e) { + return NO; + }); +} + // see also: // - http://www.cocoawithlove.com/2009/01/demystifying-nsapplication-by.html // - https://github.com/gnustep/gui/blob/master/Source/NSApplication.m -int uiMainStep(int wait) +int mainStep(int wait, BOOL (^interceptEvent)(NSEvent *e)) { NSDate *expire; NSEvent *e; @@ -196,7 +203,8 @@ int uiMainStep(int wait) return 1; type = [e type]; - [realNSApp() sendEvent:e]; + if (!interceptEvent(e)) + [realNSApp() sendEvent:e]; [realNSApp() updateWindows]; // GNUstep does this diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index a4e44fac..4b17268d 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -34,7 +34,7 @@ extern void finalizeMenus(void); extern void uninitMenus(void); -// init.m +// main.m @interface applicationClass : NSApplication @end // this is needed because NSApp is of type id, confusing clang @@ -43,6 +43,7 @@ extern void uninitMenus(void); @property (strong) menuManager *menuManager; @end #define appDelegate() ((appDelegate *) [realNSApp() delegate]) +extern int mainStep(int wait, BOOL (^interceptEvent)(NSEvent *)); // util.m extern void disableAutocorrect(NSTextView *); From a1d14b8773b00b1017ac73cdb1771afb15ca3066 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 28 Oct 2016 17:43:40 -0400 Subject: [PATCH 0480/1329] More TODOs. --- TODO.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/TODO.md b/TODO.md index d62049c9..b0a8a664 100644 --- a/TODO.md +++ b/TODO.md @@ -109,3 +109,19 @@ http://stackoverflow.com/questions/38338426/meaning-of-ampersand-in-rc-files/383 label shortcut keys - remove whining from source code + +[01:41:47] Hi. does pango support "fgalpha". I see that foreground="112233xx" works ( alpha=xx ), but fgalpha is a no-op +[01:52:29] pango_attr_foreground_alpha_new (32767) seems to be called in either case, but only the "foreground" attr works +[01:56:09] lolek (lolek@ip-91-244-230-76.simant.pl) joined the channel +[01:57:48] ok. seems like "foreground" is mandatory attr, 1. "foreground-without-alpha" + "alpha" works 2. "foreground-with-alpha" works. 3. "alpha" alone doesn +[01:57:52] 't work +[01:58:29] Is there a way to just specify alpha on the current foreground color ? +[02:00:23] lolek (lolek@ip-91-244-230-76.simant.pl) left the channel +[02:07:41] mjog (mjog@uniwide-pat-pool-129-94-8-98.gw.unsw.edu.au) left IRC (Quit: mjog) +[02:08:10] seb128 (seb128@53542B83.cm-6-5a.dynamic.ziggo.nl) joined the channel +[02:12:37] huh +[02:12:41] what version of pango? +[02:13:05] the latest . +[02:15:00] 1.40.3 +[02:20:46] I'll ahve to keep this in mind then, thanks +[02:20:59] if only there was a cairo-specific attribute for alpha... From 6779ae91d7630a38cfd0f72789e36a98df60911c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 28 Oct 2016 18:56:55 -0400 Subject: [PATCH 0481/1329] Started a FAQ with the OS X activation issue as the only question for now. Fixes #218. --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 4041f936..75c45210 100644 --- a/README.md +++ b/README.md @@ -158,6 +158,15 @@ Ruby | [libui-ruby](https://github.com/jamescook/libui-ruby) Rust | [libui-rs](https://github.com/pcwalton/libui-rs) Swift | [libui-swift](https://github.com/sclukey/libui-swift) +## Frequently Asked Questions + +### Why does my program start in the background on OS X if I run from the command line? +OS X normally does not start program executables directly; instead, it uses [Launch Services](https://developer.apple.com/reference/coreservices/1658613-launch_services?language=objc) to coordinate the launching of the program between the various parts of the system and the loading of info from an .app bundle. One of these coordination tasks is responsible for bringing a newly launched app into the foreground. This is called "activation". + +When you run a binary directly from the Terminal, however, you are running it directly, not through Launch Services. Therefore, the program starts in the background, because no one told it to activate! Now, it turns out [there is an API](https://developer.apple.com/reference/appkit/nsapplication/1428468-activateignoringotherapps) that we can use to force our app to be activated. But if we use it, then we'd be trampling over Launch Services, which already knows whether it should activate or not. Therefore, libui does not step over Launch Services, at the cost of requiring an extra user step if running directly from the command line. + +See also [this](https://github.com/andlabs/libui/pull/20#issuecomment-211381971) and [this](http://stackoverflow.com/questions/25318524/what-exactly-should-i-pass-to-nsapp-activateignoringotherapps-to-get-my-appl). + ## Screenshots From examples/controlgallery: From 211b11b80fcc605afeb318e1a3373aaf17b6dabc Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 31 Oct 2016 13:38:38 -0400 Subject: [PATCH 0482/1329] Fixed a threading issue in uiQueueMain() on GTK+. --- README.md | 5 ++++- unix/main.c | 6 ++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 75c45210..e9d6beac 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,10 @@ This README is being written.
## Announcements -* **f))(q->data); - uiFree(q); + g_free(q); return FALSE; } @@ -99,7 +99,9 @@ void uiQueueMain(void (*f)(void *data), void *data) { struct queued *q; - q = uiNew(struct queued); + // we have to use g_new0()/g_free() because uiAlloc() is only safe to call on the main thread + // for some reason it didn't affect me, but it did affect krakjoe + q = g_new0(struct queued, 1); q->f = f; q->data = data; gdk_threads_add_idle(doqueued, q); From d3b33e39ceb469f9b39acbf8ba21b2870d26e825 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 31 Oct 2016 14:33:11 -0400 Subject: [PATCH 0483/1329] Further enhancements to custom run loops on OS X for custom resize loops. --- darwin/main.m | 31 +++++++++++++++++++------------ darwin/uipriv_darwin.h | 9 ++++++++- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/darwin/main.m b/darwin/main.m index 054a79af..59a8683b 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -171,7 +171,20 @@ void uiMainSteps(void) int uiMainStep(int wait) { - return mainStep(wait, ^(NSEvent *e) { + struct nextEventArgs nea; + + nea.mask = NSAnyEventMask; + + // ProPuke did this in his original PR requesting this + // I'm not sure if this will work, but I assume it will... + nea.duration = [NSDate distantPast]; + if (wait) // but this is normal so it will work + nea.duration = [NSDate distantFuture]; + + nea.mode = NSDefaultRunLoopMode; + nea.dequeue = YES; + + return mainStep(&nea, ^(NSEvent *e) { return NO; }); } @@ -179,26 +192,20 @@ int uiMainStep(int wait) // see also: // - http://www.cocoawithlove.com/2009/01/demystifying-nsapplication-by.html // - https://github.com/gnustep/gui/blob/master/Source/NSApplication.m -int mainStep(int wait, BOOL (^interceptEvent)(NSEvent *e)) +int mainStep(struct nextEventArgs *nea, BOOL (^interceptEvent)(NSEvent *e)) { NSDate *expire; NSEvent *e; NSEventType type; @autoreleasepool { - // ProPuke did this in his original PR requesting this - // I'm not sure if this will work, but I assume it will... - expire = [NSDate distantPast]; - if (wait) // but this is normal so it will work - expire = [NSDate distantFuture]; - if (!isRunning()) return 0; - e = [realNSApp() nextEventMatchingMask:NSAnyEventMask - untilDate:expire - inMode:NSDefaultRunLoopMode - dequeue:YES]; + e = [realNSApp() nextEventMatchingMask:nea->mask + untilDate:nea->duration + inMode:nea->mode + dequeue:nea->dequeue]; if (e == nil) return 1; diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 4b17268d..eca2d9b0 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -43,7 +43,14 @@ extern void uninitMenus(void); @property (strong) menuManager *menuManager; @end #define appDelegate() ((appDelegate *) [realNSApp() delegate]) -extern int mainStep(int wait, BOOL (^interceptEvent)(NSEvent *)); +struct nextEventArgs { + NSEventMask mask; + NSDate *duration; + // LONGTERM no NSRunLoopMode? + NSString *mode; + BOOL dequeue; +}; +extern int mainStep(struct nextEventArgs *nea, BOOL (^interceptEvent)(NSEvent *)); // util.m extern void disableAutocorrect(NSTextView *); From 3eeb15bcdb4143f1a9f2387147ec242f8b05a1ca Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 1 Nov 2016 11:58:01 -0400 Subject: [PATCH 0484/1329] Added the initial implementation of the custom resize code. --- darwin/CMakeLists.txt | 1 + darwin/area.m | 25 ++++- darwin/uipriv_darwin.h | 3 + darwin/window.m | 2 +- darwin/winmoveresize.m | 210 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 237 insertions(+), 4 deletions(-) create mode 100644 darwin/winmoveresize.m diff --git a/darwin/CMakeLists.txt b/darwin/CMakeLists.txt index 2bc34ec9..f0c936b5 100644 --- a/darwin/CMakeLists.txt +++ b/darwin/CMakeLists.txt @@ -39,6 +39,7 @@ list(APPEND _LIBUI_SOURCES darwin/text.m darwin/util.m darwin/window.m + darwin/winmoveresize.m ) set(_LIBUI_SOURCES ${_LIBUI_SOURCES} PARENT_SCOPE) diff --git a/darwin/area.m b/darwin/area.m index 6c6ac4b7..6929f292 100644 --- a/darwin/area.m +++ b/darwin/area.m @@ -32,6 +32,7 @@ struct uiArea { struct scrollViewData *d; uiAreaHandler *ah; BOOL scrolling; + NSEvent *dragevent; }; @implementation areaView @@ -200,8 +201,12 @@ struct uiArea { me.Held1To64 |= j; } - if (self->libui_enabled) + if (self->libui_enabled) { + // and allow dragging here + a->dragevent = e; (*(a->ah->MouseEvent))(a->ah, a, &me); + a->dragevent = nil; + } } #define mouseEvent(name) \ @@ -403,12 +408,26 @@ void uiAreaScrollTo(uiArea *a, double x, double y, double width, double height) void uiAreaBeginUserWindowMove(uiArea *a) { - // TODO + libuiNSWindow *w; + + w = (libuiNSWindow *) [a->area window]; + if (w == nil) + return; // TODO + if (a->dragevent == nil) + return; // TODO + [w libui_doMove:a->dragevent]; } void uiAreaBeginUserWindowResize(uiArea *a, uiWindowResizeEdge edge) { - // TODO + libuiNSWindow *w; + + w = (libuiNSWindow *) [a->area window]; + if (w == nil) + return; // TODO + if (a->dragevent == nil) + return; // TODO + [w libui_doResize:a->dragevent on:edge]; } uiArea *uiNewArea(uiAreaHandler *ah) diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index eca2d9b0..7332b050 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -138,3 +138,6 @@ extern NSTextField *newLabel(NSString *str); // image.m extern NSImage *imageImage(uiImage *); + +// winmoveresize.m +extern void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdge edge); diff --git a/darwin/window.m b/darwin/window.m index 4164f07a..8456c767 100644 --- a/darwin/window.m +++ b/darwin/window.m @@ -27,7 +27,7 @@ struct uiWindow { - (void)libui_doResize:(NSEvent *)initialEvent on:(uiWindowResizeEdge)edge { - // TODO + doManualResize(self, initialEvent, edge); } @end diff --git a/darwin/winmoveresize.m b/darwin/winmoveresize.m new file mode 100644 index 00000000..58155683 --- /dev/null +++ b/darwin/winmoveresize.m @@ -0,0 +1,210 @@ +// 1 november 2016 +#import "uipriv_darwin.h" + +// see http://stackoverflow.com/a/40352996/3408572 +static void minMaxAutoLayoutSizes(NSWindow *w, NSSize *min, NSSize *max) +{ + NSLayoutConstraint *cw, *ch; + NSView *contentView; + NSRect prevFrame; + + // if adding these constraints causes the window to change size somehow, don't show it to the user and change it back afterwards + NSDisableScreenUpdates(); + prevFrame = [w frame]; + + // minimum: encourage the window to be as small as possible + contentView = [w contentView]; + cw = mkConstraint(contentView, NSLayoutAttributeWidth, + NSLayoutRelationEqual, + nil, NSLayoutAttributeNotAnAttribute, + 0, 0, + @"window minimum width finding constraint"); + [cw setPriority:NSLayoutPriorityDragThatCanResizeWindow]; + [contentView addConstraint:cw]; + ch = mkConstraint(contentView, NSLayoutAttributeHeight, + NSLayoutRelationEqual, + nil, NSLayoutAttributeNotAnAttribute, + 0, 0, + @"window minimum height finding constraint"); + [ch setPriority:NSLayoutPriorityDragThatCanResizeWindow]; + [contentView addConstraint:ch]; + *min = [contentView fittingSize]; + [contentView removeConstraint:cw]; + [contentView removeConstraint:ch]; + + // maximum: encourage the window to be as large as possible + contentView = [w contentView]; + cw = mkConstraint(contentView, NSLayoutAttributeWidth, + NSLayoutRelationEqual, + nil, NSLayoutAttributeNotAnAttribute, + 0, DBL_MAX, + @"window maximum width finding constraint"); + [cw setPriority:NSLayoutPriorityDragThatCanResizeWindow]; + [contentView addConstraint:cw]; + ch = mkConstraint(contentView, NSLayoutAttributeHeight, + NSLayoutRelationEqual, + nil, NSLayoutAttributeNotAnAttribute, + 0, DBL_MAX, + @"window maximum height finding constraint"); + [ch setPriority:NSLayoutPriorityDragThatCanResizeWindow]; + [contentView addConstraint:ch]; + *max = [contentView fittingSize]; + [contentView removeConstraint:cw]; + [contentView removeConstraint:ch]; + + [w setFrame:prevFrame display:YES]; // TODO really YES? + NSEnableScreenUpdates(); +} + +static void handleResizeLeft(NSRect *frame, NSPoint old, NSPoint new) +{ + frame->origin.x += new.x - old.x; +} + +// TODO properly handle crossing the menubar; this just stops at the menubar +static void handleResizeTop(NSRect *frame, NSPoint old, NSPoint new) +{ + CGFloat offset; + CGFloat newHeight; + CGFloat oldTop, newTop; + NSRect mainWorkArea; + CGFloat menubarBottom; + + offset = new.y - old.y; + newHeight = frame->size.height + offset; + + // we have gone too high if we started under the menubar AND we are about to cross it + oldTop = frame->origin.y + frame->size.height; + newTop = frame->origin.y + newHeight; + mainWorkArea = [[NSScreen mainScreen] visibleFrame]; + menubarBottom = mainWorkArea.origin.y + mainWorkArea.size.height; + if (oldTop < menubarBottom) + if (newTop >= menubarBottom) + return; + + frame->size.height = newHeight; +} + +static void handleResizeRight(NSRect *frame, NSPoint old, NSPoint new) +{ + frame->size.width += new.x - old.x; +} + + +// TODO properly handle crossing the menubar; this just stops at the menubar +static void handleResizeBottom(NSRect *frame, NSPoint old, NSPoint new) +{ + CGFloat offset; + CGFloat newY; + NSRect mainFrame; + CGFloat menubarTop; + + offset = new.y - old.y; + newY = frame->origin.y + offset; + + // we have gone too low if we started above the menubar AND we are about to cross it + mainFrame = [[NSScreen mainScreen] frame]; + menubarTop = mainFrame.origin.y + mainFrame.size.height; + if (frame->origin.y >= menubarTop) + if (newY < menubarTop) + return; + + frame->origin.y = newY; +} + +struct onResizeDragParams { + NSWindow *w; + NSPoint old; + uiWindowResizeEdge edge; + NSSize min; + NSSize max; +}; + +static void onResizeDrag(struct onResizeDragParams *p, NSEvent *e) +{ + NSPoint new; + NSRect frame; + + new = [e locationInWindow]; + frame = [p->w frame]; + +NSLog(@"old %@ new %@", NSStringFromPoint(p->old), NSStringFromPoint(new)); +NSLog(@"frame %@", NSStringFromRect(frame)); + + // horizontal + switch (p->edge) { + case uiWindowResizeEdgeLeft: + case uiWindowResizeEdgeTopLeft: + case uiWindowResizeEdgeBottomLeft: + handleResizeLeft(&frame, p->old, new); + break; + case uiWindowResizeEdgeRight: + case uiWindowResizeEdgeTopRight: + case uiWindowResizeEdgeBottomRight: + handleResizeRight(&frame, p->old, new); + break; + } + // vertical + switch (p->edge) { + case uiWindowResizeEdgeTop: + case uiWindowResizeEdgeTopLeft: + case uiWindowResizeEdgeTopRight: + handleResizeTop(&frame, p->old, new); + break; + case uiWindowResizeEdgeBottom: + case uiWindowResizeEdgeBottomLeft: + case uiWindowResizeEdgeBottomRight: + handleResizeBottom(&frame, p->old, new); + break; + } + + // constrain + if (frame.size.width < p->min.width) + frame.size.width = p->min.width; + if (frame.size.height < p->min.height) + frame.size.height = p->min.height; + // TODO > or >= ? + if (frame.size.width > p->max.width) + frame.size.width = p->max.width; + if (frame.size.height > p->max.height) + frame.size.height = p->max.height; + +NSLog(@"becomes %@", NSStringFromRect(frame)); + + [p->w setFrame:frame display:YES]; // and do reflect the new frame immediately + // and set it up for the next run + p->old = new; +} + +void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdge edge) +{ + __block struct onResizeDragParams rdp; + struct nextEventArgs nea; + BOOL (^handleEvent)(NSEvent *e); + __block BOOL done; + + rdp.w = w; + rdp.old = [initialEvent locationInWindow]; + rdp.edge = edge; + // TODO what happens if these change during the loop? + minMaxAutoLayoutSizes(rdp.w, &(rdp.min), &(rdp.max)); +NSLog(@"min %@", NSStringFromSize(rdp.min)); +NSLog(@"max %@", NSStringFromSize(rdp.max)); + + nea.mask = NSLeftMouseDraggedMask | NSLeftMouseUpMask; + nea.duration = [NSDate distantFuture]; + nea.mode = NSEventTrackingRunLoopMode; // nextEventMatchingMask: docs suggest using this for manual mouse tracking + nea.dequeue = YES; + handleEvent = ^(NSEvent *e) { + if ([e type] == NSLeftMouseUp) { + done = YES; + return YES; // do not send + } + onResizeDrag(&rdp, e); + return YES; // do not send + }; + done = NO; + while (mainStep(&nea, handleEvent)) + if (done) + break; +} From 0311679e375108d6f890dadf3d415a1b2caf0638 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 1 Nov 2016 13:31:34 -0400 Subject: [PATCH 0485/1329] Let's continue working on OS X resize drags. --- darwin/winmoveresize.m | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/darwin/winmoveresize.m b/darwin/winmoveresize.m index 58155683..f14e269e 100644 --- a/darwin/winmoveresize.m +++ b/darwin/winmoveresize.m @@ -80,7 +80,7 @@ static void handleResizeTop(NSRect *frame, NSPoint old, NSPoint new) menubarBottom = mainWorkArea.origin.y + mainWorkArea.size.height; if (oldTop < menubarBottom) if (newTop >= menubarBottom) - return; + newTop = menubarBottom; // TODO frame->size.height = newHeight; } @@ -95,26 +95,30 @@ static void handleResizeRight(NSRect *frame, NSPoint old, NSPoint new) static void handleResizeBottom(NSRect *frame, NSPoint old, NSPoint new) { CGFloat offset; - CGFloat newY; + CGFloat newY, newHeight; NSRect mainFrame; CGFloat menubarTop; offset = new.y - old.y; newY = frame->origin.y + offset; + newHeight = frame->size.height - offset; // we have gone too low if we started above the menubar AND we are about to cross it mainFrame = [[NSScreen mainScreen] frame]; menubarTop = mainFrame.origin.y + mainFrame.size.height; if (frame->origin.y >= menubarTop) if (newY < menubarTop) - return; + newY = menubarTop; + // TODO change newHeight too? frame->origin.y = newY; + frame->size.height = newHeight; } struct onResizeDragParams { NSWindow *w; - NSPoint old; + NSRect initialFrame; + NSPoint initialPoint; uiWindowResizeEdge edge; NSSize min; NSSize max; @@ -126,9 +130,9 @@ static void onResizeDrag(struct onResizeDragParams *p, NSEvent *e) NSRect frame; new = [e locationInWindow]; - frame = [p->w frame]; + frame = p->initialFrame; -NSLog(@"old %@ new %@", NSStringFromPoint(p->old), NSStringFromPoint(new)); +NSLog(@"old %@ new %@", NSStringFromPoint(p->initialPoint), NSStringFromPoint(new)); NSLog(@"frame %@", NSStringFromRect(frame)); // horizontal @@ -136,12 +140,12 @@ NSLog(@"frame %@", NSStringFromRect(frame)); case uiWindowResizeEdgeLeft: case uiWindowResizeEdgeTopLeft: case uiWindowResizeEdgeBottomLeft: - handleResizeLeft(&frame, p->old, new); + handleResizeLeft(&frame, p->initialPoint, new); break; case uiWindowResizeEdgeRight: case uiWindowResizeEdgeTopRight: case uiWindowResizeEdgeBottomRight: - handleResizeRight(&frame, p->old, new); + handleResizeRight(&frame, p->initialPoint, new); break; } // vertical @@ -149,16 +153,17 @@ NSLog(@"frame %@", NSStringFromRect(frame)); case uiWindowResizeEdgeTop: case uiWindowResizeEdgeTopLeft: case uiWindowResizeEdgeTopRight: - handleResizeTop(&frame, p->old, new); + handleResizeTop(&frame, p->initialPoint, new); break; case uiWindowResizeEdgeBottom: case uiWindowResizeEdgeBottomLeft: case uiWindowResizeEdgeBottomRight: - handleResizeBottom(&frame, p->old, new); + handleResizeBottom(&frame, p->initialPoint, new); break; } // constrain + // TODO should we constrain against anything else as well? minMaxAutoLayoutSizes() already gives us nonnegative sizes, but... if (frame.size.width < p->min.width) frame.size.width = p->min.width; if (frame.size.height < p->min.height) @@ -172,8 +177,6 @@ NSLog(@"frame %@", NSStringFromRect(frame)); NSLog(@"becomes %@", NSStringFromRect(frame)); [p->w setFrame:frame display:YES]; // and do reflect the new frame immediately - // and set it up for the next run - p->old = new; } void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdge edge) @@ -184,7 +187,8 @@ void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdge edge) __block BOOL done; rdp.w = w; - rdp.old = [initialEvent locationInWindow]; + rdp.initialFrame = [rdp.w frame]; + rdp.initialPoint = [initialEvent locationInWindow]; rdp.edge = edge; // TODO what happens if these change during the loop? minMaxAutoLayoutSizes(rdp.w, &(rdp.min), &(rdp.max)); From 4c429d5b7f5d0d1a1e3a6e5892bf309dfc029b35 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 1 Nov 2016 14:57:44 -0400 Subject: [PATCH 0486/1329] Removed the titlebar logic. Let's try to get the core code working first. --- darwin/winmoveresize.m | 46 ++++++------------------------------------ 1 file changed, 6 insertions(+), 40 deletions(-) diff --git a/darwin/winmoveresize.m b/darwin/winmoveresize.m index f14e269e..e0559288 100644 --- a/darwin/winmoveresize.m +++ b/darwin/winmoveresize.m @@ -59,30 +59,13 @@ static void minMaxAutoLayoutSizes(NSWindow *w, NSSize *min, NSSize *max) static void handleResizeLeft(NSRect *frame, NSPoint old, NSPoint new) { frame->origin.x += new.x - old.x; + frame->size.width -= new.x - old.x; } -// TODO properly handle crossing the menubar; this just stops at the menubar +// TODO properly handle the menubar static void handleResizeTop(NSRect *frame, NSPoint old, NSPoint new) { - CGFloat offset; - CGFloat newHeight; - CGFloat oldTop, newTop; - NSRect mainWorkArea; - CGFloat menubarBottom; - - offset = new.y - old.y; - newHeight = frame->size.height + offset; - - // we have gone too high if we started under the menubar AND we are about to cross it - oldTop = frame->origin.y + frame->size.height; - newTop = frame->origin.y + newHeight; - mainWorkArea = [[NSScreen mainScreen] visibleFrame]; - menubarBottom = mainWorkArea.origin.y + mainWorkArea.size.height; - if (oldTop < menubarBottom) - if (newTop >= menubarBottom) - newTop = menubarBottom; // TODO - - frame->size.height = newHeight; + frame->size.height += new.y - old.y; } static void handleResizeRight(NSRect *frame, NSPoint old, NSPoint new) @@ -91,28 +74,11 @@ static void handleResizeRight(NSRect *frame, NSPoint old, NSPoint new) } -// TODO properly handle crossing the menubar; this just stops at the menubar +// TODO properly handle the menubar static void handleResizeBottom(NSRect *frame, NSPoint old, NSPoint new) { - CGFloat offset; - CGFloat newY, newHeight; - NSRect mainFrame; - CGFloat menubarTop; - - offset = new.y - old.y; - newY = frame->origin.y + offset; - newHeight = frame->size.height - offset; - - // we have gone too low if we started above the menubar AND we are about to cross it - mainFrame = [[NSScreen mainScreen] frame]; - menubarTop = mainFrame.origin.y + mainFrame.size.height; - if (frame->origin.y >= menubarTop) - if (newY < menubarTop) - newY = menubarTop; - // TODO change newHeight too? - - frame->origin.y = newY; - frame->size.height = newHeight; + frame->origin.y += new.y - old.y; + frame->size.height -= new.y - old.y; } struct onResizeDragParams { From fae0bb061bb950642d2f7084cdf6290eab761cdd Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 1 Nov 2016 15:19:02 -0400 Subject: [PATCH 0487/1329] Fixed the obvious glitches with window resizes. --- darwin/winmoveresize.m | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/darwin/winmoveresize.m b/darwin/winmoveresize.m index e0559288..69abc700 100644 --- a/darwin/winmoveresize.m +++ b/darwin/winmoveresize.m @@ -90,12 +90,22 @@ struct onResizeDragParams { NSSize max; }; +static NSPoint makeIndependent(NSPoint p, NSWindow *w) +{ + NSRect r; + + r.origin = p; + // mikeash in irc.freenode.net/#macdev confirms both that any size will do and that we can safely ignore the resultant size + r.size = NSZeroSize; + return [w convertRectToScreen:r].origin; +} + static void onResizeDrag(struct onResizeDragParams *p, NSEvent *e) { NSPoint new; NSRect frame; - new = [e locationInWindow]; + new = makeIndependent([e locationInWindow], p->w); frame = p->initialFrame; NSLog(@"old %@ new %@", NSStringFromPoint(p->initialPoint), NSStringFromPoint(new)); @@ -154,7 +164,7 @@ void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdge edge) rdp.w = w; rdp.initialFrame = [rdp.w frame]; - rdp.initialPoint = [initialEvent locationInWindow]; + rdp.initialPoint = makeIndependent([initialEvent locationInWindow], rdp.w); rdp.edge = edge; // TODO what happens if these change during the loop? minMaxAutoLayoutSizes(rdp.w, &(rdp.min), &(rdp.max)); From 7199d4c847fb33983eeddc11153982a3f3624b1e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 1 Nov 2016 17:51:25 -0400 Subject: [PATCH 0488/1329] More notes. --- darwin/winmoveresize.m | 3 +++ 1 file changed, 3 insertions(+) diff --git a/darwin/winmoveresize.m b/darwin/winmoveresize.m index 69abc700..834cff74 100644 --- a/darwin/winmoveresize.m +++ b/darwin/winmoveresize.m @@ -90,6 +90,8 @@ struct onResizeDragParams { NSSize max; }; +// because we are changing the window frame each time the mouse moves, the successive -[NSEvent locationInWindow]s cannot be meaningfully used together +// make sure they are all following some sort of standard to avoid this problem; the screen is the most obvious possibility since it requires only one conversion (the only one that a NSWindow provides) static NSPoint makeIndependent(NSPoint p, NSWindow *w) { NSRect r; @@ -155,6 +157,7 @@ NSLog(@"becomes %@", NSStringFromRect(frame)); [p->w setFrame:frame display:YES]; // and do reflect the new frame immediately } +// TODO do our events get fired with this? *should* they? void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdge edge) { __block struct onResizeDragParams rdp; From 503e5d776833c49bd6e5f7bd5ae882b2f42104ef Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 2 Nov 2016 08:23:26 -0400 Subject: [PATCH 0489/1329] More notes. --- darwin/winmoveresize.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/darwin/winmoveresize.m b/darwin/winmoveresize.m index 834cff74..c9492101 100644 --- a/darwin/winmoveresize.m +++ b/darwin/winmoveresize.m @@ -83,6 +83,8 @@ static void handleResizeBottom(NSRect *frame, NSPoint old, NSPoint new) struct onResizeDragParams { NSWindow *w; + // using the previous point causes weird issues like the mouse seeming to fall behind the window edge... so do this instead + // TODO will this make things like the menubar and dock easier too? NSRect initialFrame; NSPoint initialPoint; uiWindowResizeEdge edge; From 81b95a59b1842be429dd8ff210134efda099fdd2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 2 Nov 2016 09:09:49 -0400 Subject: [PATCH 0490/1329] Minor fixups. --- darwin/winmoveresize.m | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/darwin/winmoveresize.m b/darwin/winmoveresize.m index c9492101..1a1cfe0d 100644 --- a/darwin/winmoveresize.m +++ b/darwin/winmoveresize.m @@ -37,14 +37,14 @@ static void minMaxAutoLayoutSizes(NSWindow *w, NSSize *min, NSSize *max) cw = mkConstraint(contentView, NSLayoutAttributeWidth, NSLayoutRelationEqual, nil, NSLayoutAttributeNotAnAttribute, - 0, DBL_MAX, + 0, CGFLOAT_MAX, @"window maximum width finding constraint"); [cw setPriority:NSLayoutPriorityDragThatCanResizeWindow]; [contentView addConstraint:cw]; ch = mkConstraint(contentView, NSLayoutAttributeHeight, NSLayoutRelationEqual, nil, NSLayoutAttributeNotAnAttribute, - 0, DBL_MAX, + 0, CGFLOAT_MAX, @"window maximum height finding constraint"); [ch setPriority:NSLayoutPriorityDragThatCanResizeWindow]; [contentView addConstraint:ch]; @@ -63,6 +63,7 @@ static void handleResizeLeft(NSRect *frame, NSPoint old, NSPoint new) } // TODO properly handle the menubar +// TODO wait, OS X does it for us?! static void handleResizeTop(NSRect *frame, NSPoint old, NSPoint new) { frame->size.height += new.y - old.y; @@ -112,9 +113,6 @@ static void onResizeDrag(struct onResizeDragParams *p, NSEvent *e) new = makeIndependent([e locationInWindow], p->w); frame = p->initialFrame; -NSLog(@"old %@ new %@", NSStringFromPoint(p->initialPoint), NSStringFromPoint(new)); -NSLog(@"frame %@", NSStringFromRect(frame)); - // horizontal switch (p->edge) { case uiWindowResizeEdgeLeft: @@ -154,8 +152,6 @@ NSLog(@"frame %@", NSStringFromRect(frame)); if (frame.size.height > p->max.height) frame.size.height = p->max.height; -NSLog(@"becomes %@", NSStringFromRect(frame)); - [p->w setFrame:frame display:YES]; // and do reflect the new frame immediately } @@ -173,8 +169,6 @@ void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdge edge) rdp.edge = edge; // TODO what happens if these change during the loop? minMaxAutoLayoutSizes(rdp.w, &(rdp.min), &(rdp.max)); -NSLog(@"min %@", NSStringFromSize(rdp.min)); -NSLog(@"max %@", NSStringFromSize(rdp.max)); nea.mask = NSLeftMouseDraggedMask | NSLeftMouseUpMask; nea.duration = [NSDate distantFuture]; From 17dc5f407e8a080ea45b678a1a997f4b37893cc2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 2 Nov 2016 09:29:44 -0400 Subject: [PATCH 0491/1329] And implemented moves on OS X. --- README.md | 3 ++ darwin/uipriv_darwin.h | 1 + darwin/window.m | 2 +- darwin/winmoveresize.m | 82 +++++++++++++++++++++++++++++++++++------- 4 files changed, 75 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index e9d6beac..322a0ece 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,9 @@ This README is being written.
## Announcements +* **2 November 2016** + * Added two new functions to replace the deleted `uiWindowPosition()` and friends: `uiAreaBeginUserWindowMove()` and `uiAreaBeginUserWindowResize()`. When used in a `uiAreaHandler.Mouse()` event handler, these let you initiate a user-driven mouse move or mouse resize of the window at any point in a uiArea. + * **31 October 2016** * @krakjoe noticed that I accidentally used thread-unsafe code in uiQueueMain() on Unix. Fixed. diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 7332b050..78f31af5 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -140,4 +140,5 @@ extern NSTextField *newLabel(NSString *str); extern NSImage *imageImage(uiImage *); // winmoveresize.m +extern void doManualMove(NSWindow *w, NSEvent *initialEvent); extern void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdge edge); diff --git a/darwin/window.m b/darwin/window.m index 8456c767..97c22e62 100644 --- a/darwin/window.m +++ b/darwin/window.m @@ -22,7 +22,7 @@ struct uiWindow { - (void)libui_doMove:(NSEvent *)initialEvent { - // TODO + doManualMove(self, initialEvent); } - (void)libui_doResize:(NSEvent *)initialEvent on:(uiWindowResizeEdge)edge diff --git a/darwin/winmoveresize.m b/darwin/winmoveresize.m index 1a1cfe0d..4b307016 100644 --- a/darwin/winmoveresize.m +++ b/darwin/winmoveresize.m @@ -1,6 +1,76 @@ // 1 november 2016 #import "uipriv_darwin.h" +// because we are changing the window frame each time the mouse moves, the successive -[NSEvent locationInWindow]s cannot be meaningfully used together +// make sure they are all following some sort of standard to avoid this problem; the screen is the most obvious possibility since it requires only one conversion (the only one that a NSWindow provides) +static NSPoint makeIndependent(NSPoint p, NSWindow *w) +{ + NSRect r; + + r.origin = p; + // mikeash in irc.freenode.net/#macdev confirms both that any size will do and that we can safely ignore the resultant size + r.size = NSZeroSize; + return [w convertRectToScreen:r].origin; +} + +struct onMoveDragParams { + NSWindow *w; + // using the previous point causes weird issues like the mouse seeming to fall behind the window edge... so do this instead + // TODO will this make things like the menubar and dock easier too? + NSRect initialFrame; + NSPoint initialPoint; +}; + +void onMoveDrag(struct onMoveDragParams *p, NSEvent *e) +{ + NSPoint new; + NSRect frame; + CGFloat offx, offy; + + new = makeIndependent([e locationInWindow], p->w); + frame = p->initialFrame; + + offx = new.x - p->initialPoint.x; + offy = new.y - p->initialPoint.y; + frame.origin.x += offx; + frame.origin.y += offy; + + // TODO handle the menubar + // TODO wait the system does this for us already?! + + [p->w setFrameOrigin:frame.origin]; +} + +// LONGTERM FUTURE -[NSWindow performWindowDragWithEvent:] would be better but that's 10.11-only +void doManualMove(NSWindow *w, NSEvent *initialEvent) +{ + __block struct onMoveDragParams mdp; + struct nextEventArgs nea; + BOOL (^handleEvent)(NSEvent *e); + __block BOOL done; + + mdp.w = w; + mdp.initialFrame = [mdp.w frame]; + mdp.initialPoint = makeIndependent([initialEvent locationInWindow], mdp.w); + + nea.mask = NSLeftMouseDraggedMask | NSLeftMouseUpMask; + nea.duration = [NSDate distantFuture]; + nea.mode = NSEventTrackingRunLoopMode; // nextEventMatchingMask: docs suggest using this for manual mouse tracking + nea.dequeue = YES; + handleEvent = ^(NSEvent *e) { + if ([e type] == NSLeftMouseUp) { + done = YES; + return YES; // do not send + } + onMoveDrag(&mdp, e); + return YES; // do not send + }; + done = NO; + while (mainStep(&nea, handleEvent)) + if (done) + break; +} + // see http://stackoverflow.com/a/40352996/3408572 static void minMaxAutoLayoutSizes(NSWindow *w, NSSize *min, NSSize *max) { @@ -93,18 +163,6 @@ struct onResizeDragParams { NSSize max; }; -// because we are changing the window frame each time the mouse moves, the successive -[NSEvent locationInWindow]s cannot be meaningfully used together -// make sure they are all following some sort of standard to avoid this problem; the screen is the most obvious possibility since it requires only one conversion (the only one that a NSWindow provides) -static NSPoint makeIndependent(NSPoint p, NSWindow *w) -{ - NSRect r; - - r.origin = p; - // mikeash in irc.freenode.net/#macdev confirms both that any size will do and that we can safely ignore the resultant size - r.size = NSZeroSize; - return [w convertRectToScreen:r].origin; -} - static void onResizeDrag(struct onResizeDragParams *p, NSEvent *e) { NSPoint new; From f56411fde197481c00ad950e1a545452d47efa55 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 2 Nov 2016 09:34:40 -0400 Subject: [PATCH 0492/1329] Use performWindowDragWithEvent: if available. --- darwin/winmoveresize.m | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/darwin/winmoveresize.m b/darwin/winmoveresize.m index 4b307016..9145b7bb 100644 --- a/darwin/winmoveresize.m +++ b/darwin/winmoveresize.m @@ -41,7 +41,6 @@ void onMoveDrag(struct onMoveDragParams *p, NSEvent *e) [p->w setFrameOrigin:frame.origin]; } -// LONGTERM FUTURE -[NSWindow performWindowDragWithEvent:] would be better but that's 10.11-only void doManualMove(NSWindow *w, NSEvent *initialEvent) { __block struct onMoveDragParams mdp; @@ -49,6 +48,13 @@ void doManualMove(NSWindow *w, NSEvent *initialEvent) BOOL (^handleEvent)(NSEvent *e); __block BOOL done; + // this is only available on 10.11 and newer (LONGTERM FUTURE) + // but use it if available; this lets us use the real OS dragging code, which means we can take advantage of OS features like Spaces + if ([w respondsToSelector:@selector(performWindowDragWithEvent:)]) { + [((id) w) performWindowDragWithEvent:initialEvent]; + return; + } + mdp.w = w; mdp.initialFrame = [mdp.w frame]; mdp.initialPoint = makeIndependent([initialEvent locationInWindow], mdp.w); From 92965068e1500e96500d717c4149d1f4a45860c1 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 2 Nov 2016 09:41:51 -0400 Subject: [PATCH 0493/1329] We no longer need this file. --- osxbordersize | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 osxbordersize diff --git a/osxbordersize b/osxbordersize deleted file mode 100644 index 83140bdf..00000000 --- a/osxbordersize +++ /dev/null @@ -1,9 +0,0 @@ -https://developer.apple.com/library/content/samplecode/RoundTransparentWindow/Listings/Classes_CustomWindow_m.html#//apple_ref/doc/uid/DTS10000401-Classes_CustomWindow_m-DontLinkElementID_8 -https://git.gnome.org/browse/gtk+/tree/gdk/quartz/GdkQuartzNSWindow.c -http://www.cocoawithlove.com/2008/12/drawing-custom-window-on-mac-os-x.html -http://www.cocoabuilder.com/archive/cocoa/129590-moving-borderless-window.html -http://stackoverflow.com/questions/7245725/all-sides-resize-of-borderless-nswindow-in-lion -http://stackoverflow.com/questions/15782176/how-to-make-a-custom-window-resizable-from-all-sides -http://www.cocoabuilder.com/archive/cocoa/97795-resizable-borderless-window.html -http://www.cocoabuilder.com/archive/cocoa/229815-borderless-and-resize.html -https://web.archive.org/web/20160409030454/http://cocoadev.com/BorderlessWindow From 6b33c62b84188f94c71e2609da9c16a6371fee51 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 8 Nov 2016 09:12:11 -0500 Subject: [PATCH 0494/1329] More TODOs. --- darwin/area.m | 1 + 1 file changed, 1 insertion(+) diff --git a/darwin/area.m b/darwin/area.m index 6929f292..23162e6c 100644 --- a/darwin/area.m +++ b/darwin/area.m @@ -309,6 +309,7 @@ mouseEvent(otherMouseUp) [self setNeedsDisplay:YES]; } +// TODO does this update the frame? - (void)setScrollingSize:(NSSize)s { self->libui_ss = s; From 287d59b5c5f439e6e5387a85ed72dc8ddd2cc64c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 11 Nov 2016 01:21:28 -0500 Subject: [PATCH 0495/1329] More TODOs. --- TODO.md | 2 ++ darwin/drawtext.m | 2 ++ 2 files changed, 4 insertions(+) diff --git a/TODO.md b/TODO.md index b0a8a664..33ac95af 100644 --- a/TODO.md +++ b/TODO.md @@ -1,3 +1,5 @@ +- make sure the last line of text layouts include leading + - documentation notes: - static binaries do not link system libraries, meaning apps still depend on shared GTK+, etc. - ui*Buttons are NOT compatible with uiButton functions diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 07038616..c376536a 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -445,6 +445,8 @@ uiDrawTextLayout *uiDrawNewTextLayout(const char *str, uiDrawTextFont *defaultFo layout = uiNew(uiDrawTextLayout); + // TODO docs say we need to use a different set of key callbacks + // TODO see if the font attribute key callbacks need to be the same attr = newAttrList(); // this will retain defaultFont->f; no need to worry CFDictionaryAddValue(attr, kCTFontAttributeName, defaultFont->f); From e3dec183aa77d36926518069163a3e53c3d4b55c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 27 Nov 2016 17:34:40 -0500 Subject: [PATCH 0496/1329] Started the process of splitting the table code into a separate branch. --- README.md | 5 +- common/CMakeLists.txt | 1 - common/table.c | 22 -- darwin/CMakeLists.txt | 1 - darwin/table.m | 581 -------------------------------------- test/CMakeLists.txt | 2 - test/images.c | 202 ------------- test/main.c | 4 +- test/page16.c | 143 ---------- test/test.h | 6 - ui.h | 3 - uitable.h | 65 ----- unix/CMakeLists.txt | 1 - unix/table.c | 642 ------------------------------------------ windows/area.cpp | 2 + windows/tablepart.cpp | 95 ------- 16 files changed, 7 insertions(+), 1768 deletions(-) delete mode 100644 common/table.c delete mode 100644 darwin/table.m delete mode 100644 test/images.c delete mode 100644 test/page16.c delete mode 100644 uitable.h delete mode 100644 unix/table.c delete mode 100644 windows/tablepart.cpp diff --git a/README.md b/README.md index 322a0ece..01275c0e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,3 @@ -# Note: Table stuff is currently experimental; do not use in production code. It will not build on Windows as that part has not been written yet; if you want to test other parts of the Windows code, apply `nowintable.diff`. - # libui: a portable GUI library for C This README is being written.
@@ -7,6 +5,9 @@ This README is being written.
## Announcements +* **27 November 2016** + * Decided to split the table stuff into its own branch. It will be developed independently of everything else, along with a few other features. + * **2 November 2016** * Added two new functions to replace the deleted `uiWindowPosition()` and friends: `uiAreaBeginUserWindowMove()` and `uiAreaBeginUserWindowResize()`. When used in a `uiAreaHandler.Mouse()` event handler, these let you initiate a user-driven mouse move or mouse resize of the window at any point in a uiArea. diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 7f704032..91d79493 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -6,7 +6,6 @@ list(APPEND _LIBUI_SOURCES common/debug.c common/matrix.c common/shouldquit.c - common/table.c common/userbugs.c ) set(_LIBUI_SOURCES ${_LIBUI_SOURCES} PARENT_SCOPE) diff --git a/common/table.c b/common/table.c deleted file mode 100644 index 3726883a..00000000 --- a/common/table.c +++ /dev/null @@ -1,22 +0,0 @@ -// 21 june 2016 -#include "../ui.h" -#include "uipriv.h" - -void *uiTableModelGiveInt(int i) -{ - return (void *) ((intptr_t) i); -} - -int uiTableModelTakeInt(void *v) -{ - return (int) ((intptr_t) v); -} - -uiTableColumn *uiTableAppendTextColumn(uiTable *t, const char *name, int modelColumn) -{ - uiTableColumn *tc; - - tc = uiTableAppendColumn(t, name); - uiTableColumnAppendTextPart(tc, modelColumn, 1); - return tc; -} diff --git a/darwin/CMakeLists.txt b/darwin/CMakeLists.txt index f0c936b5..dbef5d43 100644 --- a/darwin/CMakeLists.txt +++ b/darwin/CMakeLists.txt @@ -35,7 +35,6 @@ list(APPEND _LIBUI_SOURCES darwin/spinbox.m darwin/stddialogs.m darwin/tab.m - darwin/table.m darwin/text.m darwin/util.m darwin/window.m diff --git a/darwin/table.m b/darwin/table.m deleted file mode 100644 index 9e578c85..00000000 --- a/darwin/table.m +++ /dev/null @@ -1,581 +0,0 @@ -// 21 june 2016 -#import "uipriv_darwin.h" - -// TODOs -// - initial state of table view is off -// - header cell seems off -// - background color shows up for a line or two below selection -// - editable NSTextFields have no intrinsic width -// - changing a part property does not refresh views -// - is the Y position of checkbox cells correct? -// - progressbars appear ABOVE the table header - -// LONGTERM -// - reuse row views instead of creating a new one each time - -@interface tableModel : NSObject { - uiTableModel *libui_m; -} -- (id)initWithModel:(uiTableModel *)m; -- (IBAction)onAction:(id)sender; -@end - -enum { - partText, - partImage, - partButton, - partCheckbox, - partProgressBar, -}; - -@interface tablePart : NSObject -@property int type; -@property int textColumn; -@property int textColorColumn; -@property int imageColumn; -@property int valueColumn; -@property int expand; -@property int editable; -- (NSView *)mkView:(uiTableModel *)m row:(int)row; -@end - -@interface tableColumn : NSTableColumn -@property uiTableColumn *libui_col; -@end - -@interface tableView : NSTableView -@property uiTable *libui_t; -@end - -struct uiTableModel { - uiTableModelHandler *mh; - tableModel *m; - NSMutableArray *tables; -}; - -struct uiTableColumn { - tableColumn *c; - NSMutableArray *parts; -}; - -struct uiTable { - uiDarwinControl c; - NSScrollView *sv; - tableView *tv; - struct scrollViewData *d; - int backgroundColumn; -}; - -@implementation tableModel - -- (id)initWithModel:(uiTableModel *)m -{ - self = [super init]; - if (self) - self->libui_m = m; - return self; -} - -- (NSInteger)numberOfRowsInTableView:(NSTableView *)tv -{ - uiTableModelHandler *mh = self->libui_m->mh; - - return (*(mh->NumRows))(mh, self->libui_m); -} - -// these are according to Interface Builder -#define xleft 2 -#define xmiddle 7 /* between images and text, anyway; let's just use it for everything to be simpler */ -#define xright 3 - - - (NSView *)tableView:(NSTableView *)tv viewForTableColumn:(NSTableColumn *)cc row:(NSInteger)row -{ - NSTableCellView *v; - tableColumn *c = (tableColumn *) cc; - tablePart *part; - NSMutableArray *views; - NSView *view, *prev; - - v = [[NSTableCellView alloc] initWithFrame:NSZeroRect]; - - views = [NSMutableArray new]; - for (part in c.libui_col->parts) - [views addObject:[part mkView:self->libui_m row:row]]; - if ([views count] == 0) // empty (TODO allow?) - goto done; - - // add to v and arrange horizontally - prev = nil; - for (view in views) { - [v addSubview:view]; - // TODO set [v imageView] and [v textField] as appropriate? - if (prev == nil) { // first view - [v addConstraint:mkConstraint(v, NSLayoutAttributeLeading, - NSLayoutRelationEqual, - view, NSLayoutAttributeLeading, - 1, -xleft, - @"uiTableColumn first part horizontal constraint")]; - prev = view; - continue; - } - [v addConstraint:mkConstraint(prev, NSLayoutAttributeTrailing, - NSLayoutRelationEqual, - view, NSLayoutAttributeLeading, - 1, -xmiddle, - @"uiTableColumn middle horizontal constraint")]; - prev = view; - } - [v addConstraint:mkConstraint(prev, NSLayoutAttributeTrailing, - NSLayoutRelationEqual, - v, NSLayoutAttributeTrailing, - 1, -xright, - @"uiTableColumn last part horizontal constraint")]; - - // and vertically - for (view in views) { - [v addConstraint:mkConstraint(view, NSLayoutAttributeCenterY, - NSLayoutRelationEqual, - v, NSLayoutAttributeCenterY, - 1, 0, - @"uiTableColumn part vertical constraint")]; - // TODO avoid the need for this hack - if ([view isKindOfClass:[NSImageView class]]) - [v addConstraint:mkConstraint(view, NSLayoutAttributeTop, - NSLayoutRelationEqual, - v, NSLayoutAttributeTop, - 1, 0, - @"uiTableColumn part vertical top constraint")]; - } - -done: - [views release]; - [v setTranslatesAutoresizingMaskIntoConstraints:NO]; - // TODO autorelease? - return v; -} - -- (void)tableView:(NSTableView *)nstv didAddRowView:(NSTableRowView *)rv forRow:(NSInteger)row -{ - uiTableModel *m = self->libui_m; - tableView *tv = (tableView *) nstv; - uiTable *t = tv.libui_t; - NSColor *color; - - if (t->backgroundColumn == -1) - return; - color = (NSColor *) ((*(m->mh->CellValue))(m->mh, m, row, t->backgroundColumn)); - if (color == nil) - return; - [rv setBackgroundColor:color]; - // TODO autorelease color? or release it? -} - -- (IBAction)onAction:(id)sender -{ - uiTableModel *m = self->libui_m; - NSView *view = (NSView *) sender; - NSTableView *tv; - NSInteger row; - const void *data; - - row = -1; - for (tv in m->tables) { - row = [tv rowForView:view]; - if (row != -1) - break; - } - if (row == -1) - implbug("table model action triggered on view with no associated table"); - - if ([view isKindOfClass:[NSTextField class]]) - data = [[((NSTextField *) view) stringValue] UTF8String]; - else if ([view isKindOfClass:[NSButton class]]) { - NSButton *b; - - b = (NSButton *) view; -// if ([b buttonType] == NSSwitchButton) - data = uiTableModelGiveInt([b state] == NSOnState); -// TODO there is no buttonType getter -if(1); else - data = NULL; - } else - implbug("table model editing action triggered on non-editable view"); - - // note the use of [view tag] — we need the model column, which we store in the view tag for relevant views below - (*(m->mh->SetCellValue))(m->mh, m, - row, [view tag], - data); - // always refresh the value in case the model rejected it - // TODO only affect tv? - uiTableModelRowChanged(m, row); -} - -@end - -@implementation tablePart - -- (id)init -{ - self = [super init]; - if (self) { - self.textColumn = -1; - self.textColorColumn = -1; - } - return self; -} - -- (NSView *)mkView:(uiTableModel *)m row:(int)row -{ - void *data; - NSString *str; - NSView *view; - NSTextField *tf; - NSImageView *iv; - NSButton *b; - NSProgressIndicator *p; - int value; - - switch (self.type) { - case partText: - data = (*(m->mh->CellValue))(m->mh, m, row, self.textColumn); - str = toNSString((char *) data); - uiFree(data); - tf = newLabel(str); - // TODO set wrap and ellipsize modes? - if (self.textColorColumn != -1) { - NSColor *color; - - color = (NSColor *) ((*(m->mh->CellValue))(m->mh, m, row, self.textColorColumn)); - if (color != nil) - [tf setTextColor:color]; - // TODO release color - } - if (self.editable) { - [tf setEditable:YES]; - [tf setTarget:m->m]; - [tf setAction:@selector(onAction:)]; - } - [tf setTag:self.textColumn]; - view = tf; - break; - case partImage: - data = (*(m->mh->CellValue))(m->mh, m, row, self.imageColumn); - iv = [[NSImageView alloc] initWithFrame:NSZeroRect]; - [iv setImage:imageImage((uiImage *) data)]; - [iv setImageFrameStyle:NSImageFrameNone]; - [iv setImageAlignment:NSImageAlignCenter]; - [iv setImageScaling:NSImageScaleProportionallyDown]; - [iv setAnimates:NO]; - [iv setEditable:NO]; - [iv addConstraint:mkConstraint(iv, NSLayoutAttributeWidth, - NSLayoutRelationEqual, - iv, NSLayoutAttributeHeight, - 1, 0, - @"uiTable image squareness constraint")]; - [iv setTag:self.imageColumn]; - view = iv; - break; - case partButton: - // TODO buttons get clipped - data = (*(m->mh->CellValue))(m->mh, m, row, self.textColumn); - str = toNSString((char *) data); - b = [[NSButton alloc] initWithFrame:NSZeroRect]; - [b setTitle:str]; - [b setButtonType:NSMomentaryPushInButton]; - [b setBordered:YES]; - [b setBezelStyle:NSRoundRectBezelStyle]; - uiDarwinSetControlFont(b, NSRegularControlSize); - if (self.editable) { - [b setTarget:m->m]; - [b setAction:@selector(onAction:)]; - } else - [b setEnabled:NO]; - [b setTag:self.textColumn]; - view = b; - break; - case partCheckbox: - data = (*(m->mh->CellValue))(m->mh, m, row, self.valueColumn); - b = [[NSButton alloc] initWithFrame:NSZeroRect]; - [b setTitle:@""]; - [b setButtonType:NSSwitchButton]; - // doesn't seem to have an associated bezel style - [b setBordered:NO]; - [b setTransparent:NO]; - uiDarwinSetControlFont(b, NSRegularControlSize); - if (uiTableModelTakeInt(data) != 0) - [b setState:NSOnState]; - else - [b setState:NSOffState]; - if (self.editable) { - [b setTarget:m->m]; - [b setAction:@selector(onAction:)]; - } else - [b setEnabled:NO]; - [b setTag:self.valueColumn]; - view = b; - break; - case partProgressBar: - data = (*(m->mh->CellValue))(m->mh, m, row, self.valueColumn); - value = uiTableModelTakeInt(data); - // TODO no intrinsic width - p = [[NSProgressIndicator alloc] initWithFrame:NSZeroRect]; - [p setControlSize:NSRegularControlSize]; - [p setBezeled:YES]; - [p setStyle:NSProgressIndicatorBarStyle]; - if (value == -1) { - [p setIndeterminate:YES]; - [p startAnimation:p]; - } else if (value == 100) { - [p setIndeterminate:NO]; - [p setMaxValue:101]; - [p setDoubleValue:101]; - [p setDoubleValue:100]; - [p setMaxValue:100]; - } else { - [p setIndeterminate:NO]; - [p setDoubleValue:(value + 1)]; - [p setDoubleValue:value]; - } - view = p; - break; - } - - // if stretchy, don't hug, otherwise hug forcibly - if (self.expand) - [view setContentHuggingPriority:NSLayoutPriorityDefaultLow forOrientation:NSLayoutConstraintOrientationHorizontal]; - else - [view setContentHuggingPriority:NSLayoutPriorityRequired forOrientation:NSLayoutConstraintOrientationHorizontal]; - [view setTranslatesAutoresizingMaskIntoConstraints:NO]; - // TODO autorelease? - return view; -} - -@end - -@implementation tableColumn -@end - -@implementation tableView -@end - -void *uiTableModelStrdup(const char *str) -{ - // TODO don't we have this already? - char *dup; - - dup = (char *) uiAlloc((strlen(str) + 1) * sizeof (char), "char[]"); - strcpy(dup, str); - return dup; -} - -uiTableModel *uiNewTableModel(uiTableModelHandler *mh) -{ - uiTableModel *m; - - m = uiNew(uiTableModel); - m->mh = mh; - m->m = [[tableModel alloc] initWithModel:m]; - m->tables = [NSMutableArray new]; - return m; -} - -void *uiTableModelGiveColor(double r, double g, double b, double a) -{ - return [[NSColor colorWithSRGBRed:r green:g blue:b alpha:a] retain]; -} - -void uiFreeTableModel(uiTableModel *m) -{ - if ([m->tables count] != 0) - userbug("You cannot free a uiTableModel while uiTables are using it."); - [m->tables release]; - [m->m release]; - uiFree(m); -} - -void uiTableModelRowInserted(uiTableModel *m, int newIndex) -{ - NSTableView *tv; - NSIndexSet *set; - - set = [NSIndexSet indexSetWithIndex:newIndex]; - for (tv in m->tables) - [tv insertRowsAtIndexes:set withAnimation:NSTableViewAnimationEffectNone]; - // set is autoreleased -} - -void uiTableModelRowChanged(uiTableModel *m, int index) -{ - NSTableView *tv; - NSIndexSet *set, *cols; - - set = [NSIndexSet indexSetWithIndex:index]; - for (tv in m->tables) { - cols = [[NSIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, [[tv tableColumns] count])]; - [tv reloadDataForRowIndexes:set columnIndexes:cols]; - // TODO this isn't enough - [cols release]; - } - // set is autoreleased -} - -void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) -{ - NSTableView *tv; - NSIndexSet *set; - - set = [NSIndexSet indexSetWithIndex:oldIndex]; - for (tv in m->tables) - [tv removeRowsAtIndexes:set withAnimation:NSTableViewAnimationEffectNone]; - // set is autoreleased -} - -void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand) -{ - tablePart *part; - - part = [tablePart new]; - part.type = partText; - part.textColumn = modelColumn; - part.expand = expand; - [c->parts addObject:part]; -} - -void uiTableColumnAppendImagePart(uiTableColumn *c, int modelColumn, int expand) -{ - tablePart *part; - - part = [tablePart new]; - part.type = partImage; - part.imageColumn = modelColumn; - part.expand = expand; - [c->parts addObject:part]; -} - -void uiTableColumnAppendButtonPart(uiTableColumn *c, int modelColumn, int expand) -{ - tablePart *part; - - part = [tablePart new]; - part.type = partButton; - part.textColumn = modelColumn; - part.expand = expand; - part.editable = 1; // editable by default - [c->parts addObject:part]; -} - -void uiTableColumnAppendCheckboxPart(uiTableColumn *c, int modelColumn, int expand) -{ - tablePart *part; - - part = [tablePart new]; - part.type = partCheckbox; - part.valueColumn = modelColumn; - part.expand = expand; - part.editable = 1; // editable by default - [c->parts addObject:part]; -} - -void uiTableColumnAppendProgressBarPart(uiTableColumn *c, int modelColumn, int expand) -{ - tablePart *part; - - part = [tablePart new]; - part.type = partProgressBar; - part.valueColumn = modelColumn; - part.expand = expand; - [c->parts addObject:part]; -} - -void uiTableColumnPartSetEditable(uiTableColumn *c, int part, int editable) -{ - tablePart *p; - - p = (tablePart *) [c->parts objectAtIndex:part]; - p.editable = editable; -} - -void uiTableColumnPartSetTextColor(uiTableColumn *c, int part, int modelColumn) -{ - tablePart *p; - - p = (tablePart *) [c->parts objectAtIndex:part]; - p.textColorColumn = modelColumn; -} - -uiDarwinControlAllDefaultsExceptDestroy(uiTable, sv) - -static void uiTableDestroy(uiControl *c) -{ - uiTable *t = uiTable(c); - - // TODO - [t->sv release]; - uiFreeControl(uiControl(t)); -} - -uiTableColumn *uiTableAppendColumn(uiTable *t, const char *name) -{ - uiTableColumn *c; - - c = uiNew(uiTableColumn); - c->c = [[tableColumn alloc] initWithIdentifier:@""]; - c->c.libui_col = c; - // via Interface Builder - [c->c setResizingMask:(NSTableColumnAutoresizingMask | NSTableColumnUserResizingMask)]; - // 10.10 adds -[NSTableColumn setTitle:]; before then we have to do this - [[c->c headerCell] setStringValue:toNSString(name)]; - // TODO is this sufficient? - [[c->c headerCell] setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]]; - c->parts = [NSMutableArray new]; - [t->tv addTableColumn:c->c]; - return c; -} - -void uiTableSetRowBackgroundColorModelColumn(uiTable *t, int modelColumn) -{ - t->backgroundColumn = modelColumn; -} - -uiTable *uiNewTable(uiTableModel *model) -{ - uiTable *t; - struct scrollViewCreateParams p; - - uiDarwinNewControl(uiTable, t); - - t->tv = [[tableView alloc] initWithFrame:NSZeroRect]; - t->tv.libui_t = t; - - [t->tv setDataSource:model->m]; - [t->tv setDelegate:model->m]; - [t->tv reloadData]; - [model->tables addObject:t->tv]; - - // TODO is this sufficient? - [t->tv setAllowsColumnReordering:NO]; - [t->tv setAllowsColumnResizing:YES]; - [t->tv setAllowsMultipleSelection:NO]; - [t->tv setAllowsEmptySelection:YES]; - [t->tv setAllowsColumnSelection:NO]; - [t->tv setUsesAlternatingRowBackgroundColors:YES]; - [t->tv setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleRegular]; - [t->tv setGridStyleMask:NSTableViewGridNone]; - [t->tv setAllowsTypeSelect:YES]; - // TODO floatsGroupRows — do we even allow group rows? - - memset(&p, 0, sizeof (struct scrollViewCreateParams)); - p.DocumentView = t->tv; - // this is what Interface Builder sets it to - // TODO verify - p.BackgroundColor = [NSColor colorWithCalibratedWhite:1.0 alpha:1.0]; - p.DrawsBackground = YES; - p.Bordered = YES; - p.HScroll = YES; - p.VScroll = YES; - t->sv = mkScrollView(&p, &(t->d)); - - t->backgroundColumn = -1; - - return t; -} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index b753a7d4..e4924bb3 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -6,7 +6,6 @@ endif() _add_exec(tester drawtests.c - images.c main.c menus.c page1.c @@ -27,7 +26,6 @@ _add_exec(tester page13.c page14.c page15.c - page16.c spaced.c ${_TEST_RESOURCES_RC} ) diff --git a/test/images.c b/test/images.c deleted file mode 100644 index df21b6eb..00000000 --- a/test/images.c +++ /dev/null @@ -1,202 +0,0 @@ -// auto-generated by images/gen.go -#include "test.h" - -static const uint32_t dat0[] = { - 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, 0xFF8AC3FF, 0xFF8AC3FF, 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF89C57C, - 0xFF89C57C, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF89C57C, - 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, -}; - -static const uint32_t dat1[] = { - 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, - 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, - 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, - 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, - 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, - 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, - 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF89C57C, - 0xFF89C57C, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF89C57C, - 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, - 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, -}; - -static const uint32_t dat2[] = { - 0xAC676767, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0x562B2B2B, 0x00000000, 0x00000000, - 0xFF818181, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, - 0xFF818181, 0xFFFFFFFF, 0xFFB7B7B7, 0xFFB7B7B7, 0xFFB7B7B8, 0xFF9E9E9F, 0xFFB8B8B8, 0xFFB8B8B8, 0xFF9F9F9F, 0xFFB8B8B9, 0xFFB8B8B9, 0xFF9F9F9F, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, - 0xFF818181, 0xFFFFFFFF, 0xFF9E9E9E, 0xFF9F9F9E, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, - 0xFF818181, 0xFFFFFFFF, 0xFFB7B8B7, 0xFFC3C3C3, 0xFFC3C3C3, 0xFF9F9F9F, 0xFFEEEEEE, 0xFFEEEEEE, 0xFFC2C2C2, 0xFFEEEFEE, 0xFFEFEEEF, 0xFFC2C2C2, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, - 0xFF818181, 0xFFFFFFFF, 0xFFB1B1B1, 0xFFB8B8B8, 0xFFB9B8B9, 0xFF9F9F9F, 0xFFE0E0E0, 0xFFE0E1E0, 0xFFC2C2C2, 0xFFE1E0E0, 0xFFE1E1E1, 0xFFC2C2C2, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, - 0xFF818181, 0xFFFFFFFF, 0xFFB9B8B8, 0xFFC4C4C4, 0xFFC3C4C4, 0xFF9F9F9F, 0xFFEEEEEF, 0xFFEFEEEF, 0xFFC2C2C2, 0xFFEFEFEF, 0xFFF0EFEF, 0xFFC2C2C2, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, - 0xFF818181, 0xFFFFFFFF, 0xFFB1B1B1, 0xFFB9B9B8, 0xFFB9B9B9, 0xFF9F9F9F, 0xFFC9A6A5, 0xFFAE3A36, 0xFFA50F0C, 0xFFA40201, 0xFFA40201, 0xFFA40D0A, 0xFFB03B37, 0xFF8D6969, 0x00000000, 0x00000000, - 0xFF818181, 0xFFFFFFFF, 0xFFB9B9B9, 0xFFC4C4C4, 0xFFC4C4C4, 0xFF9E403D, 0xFFA70A07, 0xFFC66453, 0xFFCB715C, 0xFFC9735D, 0xFFC5715B, 0xFFBF6C59, 0xFFC06E61, 0xFFAC201D, 0xC17E2927, 0x19191919, - 0xFF818181, 0xFFFFFFFF, 0xFFB2B2B2, 0xFFB9B9B9, 0xFFA65C5B, 0xFFAF2017, 0xFFCB725D, 0xFFC7654D, 0xFFBB5338, 0xFFB65136, 0xFFB04E35, 0xFFB35D47, 0xFFAE5B45, 0xFFA95743, 0xFFAA2F28, 0xA1641716, - 0xFF818181, 0xFFFFFFFF, 0xFFB9B9B9, 0xFFC4C4C4, 0xFFA51412, 0xFFCA6F5A, 0xFFBF5439, 0xFFBA5237, 0xFFB44F36, 0xFFAF4D34, 0xFF886556, 0xFF765F5A, 0xFF715B56, 0xFF6B5853, 0xFF77605B, 0xF7980C0B, - 0xFF818181, 0xFFFFFFFF, 0xFFB2B2B2, 0xFFB9B9B9, 0xFFA40201, 0xFFC76951, 0xFFB85137, 0xFFB24E36, 0xFFAD4C34, 0xFFAE653A, 0xFF95995D, 0xFF1D84A0, 0xFF177F99, 0xFF3D91A5, 0xFF3B8EA0, 0xFFA20101, - 0xFF818181, 0xFFFFFFFF, 0xFFB9B9B9, 0xFFC4C4C4, 0xFF9F1212, 0xFFCA7E6D, 0xFFB85F48, 0xFFAB4C33, 0xFFA64932, 0xFFB18B44, 0xFFB8BC50, 0xFF358086, 0xFF509CAD, 0xFF278397, 0xFF496E77, 0xF7920808, - 0xFF818181, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFBC7878, 0xFF9C1D1A, 0xFFBD7E6D, 0xFFBE7F70, 0xFFBD8374, 0xFFCDC787, 0xFFCBD089, 0xFFB4B48A, 0xFF5C93A0, 0xFF44656D, 0xFF7B1618, 0xA1661D1D, - 0xB4696969, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF934040, 0xFF9E0605, 0xFF99463D, 0xFF9E624D, 0xFFA6A14C, 0xFF9F9B4A, 0xFF948845, 0xFF4B3C43, 0xFF9A0505, 0xBA771F1F, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x55351616, 0xD37F1717, 0xF99C0B0B, 0xFFA30201, 0xFFA20101, 0xF99C0B0B, 0xD3881E1E, 0x55412222, 0x00000000, 0x00000000, -}; - -static const uint32_t dat3[] = { - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x52303030, 0xF2919191, 0xFF999999, 0xFF9C9C9C, 0xFF9E9E9E, 0xFF9C9C9C, 0xFF999999, 0xFF969696, 0xFF939393, 0xFF8F8F8F, 0xFF8C8C8C, 0xFF888888, - 0xFF848484, 0xFF818181, 0xFF7D7D7D, 0xFF7A7A7A, 0xFF767676, 0xFF727272, 0xFF6F6F6F, 0xFF6B6B6B, 0xFF686868, 0xFF646464, 0xF25F5F5F, 0x521E1E1E, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xEF8D8D8D, 0xFFFDFDFD, 0xFFFDFDFD, 0xFFFEFEFE, 0xFFFEFEFE, 0xFFFEFEFE, 0xFFFDFDFD, 0xFFFDFDFD, 0xFFFDFDFD, 0xFFFDFDFD, 0xFFFCFCFC, 0xFFFCFCFC, - 0xFFFCFCFC, 0xFFFBFBFB, 0xFFFBFBFB, 0xFFFBFBFB, 0xFFFAFAFA, 0xFFFAFAFA, 0xFFFAFAFA, 0xFFF9F9F9, 0xFFF9F9F9, 0xFFF9F9F9, 0xFFF8F8F8, 0xEF5D5D5D, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF939393, 0xFFFDFDFD, 0xFFDCDCDC, 0xFFDDDDDD, 0xFFDEDEDE, 0xFFDEDEDE, 0xFFDFDFDF, 0xFFDFDFDF, 0xFFE0E0E0, 0xFFE1E1E1, 0xFFE1E1E1, 0xFFE1E1E1, - 0xFFE2E2E2, 0xFFE2E2E2, 0xFFE2E2E2, 0xFFE2E2E2, 0xFFE3E3E3, 0xFFE3E3E3, 0xFFE3E3E3, 0xFFE3E3E3, 0xFFE3E3E3, 0xFFE2E2E2, 0xFFF8F8F8, 0xFF5D5D5D, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF929292, 0xFFFEFEFE, 0xFFDDDDDD, 0xFF787878, 0xFF8D8D8D, 0xFF8E8E8E, 0xFF8F8F8F, 0xFF8F8F8F, 0xFF787878, 0xFF8F8F8F, 0xFF909090, 0xFF909090, - 0xFF7A7A7A, 0xFF919191, 0xFF919191, 0xFF919191, 0xFF7B7B7B, 0xFF919191, 0xFF919191, 0xFF919191, 0xFF7D7D7D, 0xFFE3E3E3, 0xFFF8F8F8, 0xFF5D5D5D, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF909090, 0xFFFEFEFE, 0xFFDEDEDE, 0xFF949494, 0xFFB0B0B0, 0xFFB1B1B1, 0xFFB1B1B1, 0xFFB1B1B1, 0xFF969696, 0xFFB2B2B2, 0xFFB3B3B3, 0xFFB3B3B3, - 0xFF989898, 0xFFB4B4B4, 0xFFB4B4B4, 0xFFB5B5B5, 0xFF999999, 0xFFB5B5B5, 0xFFB5B5B5, 0xFFB5B5B5, 0xFF999999, 0xFFE4E4E4, 0xFFF8F8F8, 0xFF5C5C5C, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF8E8E8E, 0xFFFDFDFD, 0xFFDFDFDF, 0xFF949494, 0xFFB1B1B1, 0xFFB1B1B1, 0xFFB2B2B2, 0xFFB2B2B2, 0xFF979797, 0xFFB3B3B3, 0xFFB4B4B4, 0xFFB4B4B4, - 0xFF999999, 0xFFB5B5B5, 0xFFB5B5B5, 0xFFB5B5B5, 0xFF999999, 0xFFB5B5B5, 0xFFB5B5B5, 0xFFB5B5B5, 0xFF999999, 0xFFE5E5E5, 0xFFF8F8F8, 0xFF5C5C5C, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF8B8B8B, 0xFFFDFDFD, 0xFFDFDFDF, 0xFF7A7A7A, 0xFF919191, 0xFF929292, 0xFF929292, 0xFF939393, 0xFF7D7D7D, 0xFF949494, 0xFF949494, 0xFF949494, - 0xFF7D7D7D, 0xFF949494, 0xFF949494, 0xFF959595, 0xFF7E7E7E, 0xFF959595, 0xFF959595, 0xFF959595, 0xFF7E7E7E, 0xFFE6E6E6, 0xFFF8F8F8, 0xFF5B5B5B, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF898989, 0xFFFDFDFD, 0xFFE0E0E0, 0xFF949494, 0xFFB0B0B0, 0xFFB0B0B0, 0xFFB1B1B1, 0xFFB2B2B2, 0xFF979797, 0xFFE2E2E2, 0xFFE3E3E3, 0xFFE3E3E3, - 0xFFC0C0C0, 0xFFE4E4E4, 0xFFE4E4E4, 0xFFE5E5E5, 0xFFC1C1C1, 0xFFE5E5E5, 0xFFE5E5E5, 0xFFE5E5E5, 0xFFC1C1C1, 0xFFE8E8E8, 0xFFF8F8F8, 0xFF5A5A5A, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF868686, 0xFFFDFDFD, 0xFFE1E1E1, 0xFF7B7B7B, 0xFF929292, 0xFF939393, 0xFF949494, 0xFF949494, 0xFF7D7D7D, 0xFFBDBDBD, 0xFFBDBDBD, 0xFFBDBDBD, - 0xFFA0A0A0, 0xFFBEBEBE, 0xFFBEBEBE, 0xFFBFBFBF, 0xFFA1A1A1, 0xFFBFBFBF, 0xFFBFBFBF, 0xFFBFBFBF, 0xFFA1A1A1, 0xFFE9E9E9, 0xFFF8F8F8, 0xFF595959, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF838383, 0xFFFDFDFD, 0xFFE1E1E1, 0xFF949494, 0xFFB1B1B1, 0xFFB2B2B2, 0xFFB3B3B3, 0xFFB3B3B3, 0xFF979797, 0xFFE4E4E4, 0xFFE5E5E5, 0xFFE5E5E5, - 0xFFC2C2C2, 0xFFE6E6E6, 0xFFE6E6E6, 0xFFE7E7E7, 0xFFC3C3C3, 0xFFE7E7E7, 0xFFE7E7E7, 0xFFE7E7E7, 0xFFC3C3C3, 0xFFEAEAEA, 0xFFF8F8F8, 0xFF585858, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF808080, 0xFFFCFCFC, 0xFFE2E2E2, 0xFF7C7C7C, 0xFF949494, 0xFF949494, 0xFF949494, 0xFF949494, 0xFF7E7E7E, 0xFFBEBEBE, 0xFFBEBEBE, 0xFFBFBFBF, - 0xFFA2A2A2, 0xFFC0C0C0, 0xFFC0C0C0, 0xFFC1C1C1, 0xFFA3A3A3, 0xFFC1C1C1, 0xFFC1C1C1, 0xFFC1C1C1, 0xFFA3A3A3, 0xFFEBEBEB, 0xFFF8F8F8, 0xFF575757, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF7D7D7D, 0xFFFCFCFC, 0xFFE3E3E3, 0xFF969696, 0xFFB3B3B3, 0xFFB3B3B3, 0xFFB3B3B3, 0xFFB4B4B4, 0xFF999999, 0xFFE6E6E6, 0xFFE6E6E6, 0xFFE7E7E7, - 0xFFC4C4C4, 0xFFE8E8E8, 0xFFE8E8E8, 0xFFE9E9E9, 0xFFC4C4C4, 0xFFE9E9E9, 0xFFE9E9E9, 0xFFE9E9E9, 0xFFC4C4C4, 0xFFECECEC, 0xFFF8F8F8, 0xFF555555, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF7A7A7A, 0xFFFCFCFC, 0xFFE3E3E3, 0xFF7D7D7D, 0xFF949494, 0xFF949494, 0xFF959595, 0xFF969696, 0xFF7F7F7F, 0xFFBFBFBF, 0xFFC0C0C0, 0xFFC1C1C1, - 0xFFA3A3A3, 0xFFC1C1C1, 0xFFC1C1C1, 0xFFC2C2C2, 0xFFA4A4A4, 0xFFC2C2C2, 0xFFC2C2C2, 0xFFC2C2C2, 0xFFA4A4A4, 0xFFEDEDED, 0xFFF8F8F8, 0xFF545454, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF777777, 0xFFFBFBFB, 0xFFE4E4E4, 0xFF979797, 0xFFB3B3B3, 0xFFB4B4B4, 0xFFB5B5B5, 0xFFB6B6B6, 0xFF999999, 0xFFE7E7E7, 0xFFE8E8E8, 0xFFE9E9E9, - 0xFFC4C4C4, 0xFFEAEAEA, 0xFFEAEAEA, 0xFFEBEBEB, 0xFFC6C6C6, 0xFFEBEBEB, 0xFFEBEBEB, 0xFFEBEBEB, 0xFFC6C6C6, 0xFFEEEEEE, 0xFFF8F8F8, 0xFF525252, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF737373, 0xFFFBFBFB, 0xFFE4E4E4, 0xFF7D7D7D, 0xFF949494, 0xFF959595, 0xFF969696, 0xFF979797, 0xFF7F7F7F, 0xFFC1C1C1, 0xFFC1C1C1, 0xFFC2C2C2, - 0xFFA3A2A2, 0xFFAB9191, 0xFF946060, 0xFF843D3D, 0xFF782525, 0xFF802727, 0xFF7E2B2B, 0xFF843D3D, 0xFF865252, 0xFFCCB2B2, 0xFFF6F5F5, 0xFF505050, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF707070, 0xFFFBFBFB, 0xFFE5E5E5, 0xFF979797, 0xFFB4B4B4, 0xFFB5B5B5, 0xFFB6B6B6, 0xFFB6B6B6, 0xFF9A9A9A, 0xFFE9E9E9, 0xFFE6E4E4, 0xFFB18585, - 0xFF7A1B1B, 0xFFA62F2F, 0xFFD35050, 0xFFF26767, 0xFFFF7070, 0xFFFE6E6E, 0xFFFC6969, 0xFFED5B5B, 0xFFCD4343, 0xFFA22525, 0xFF7E1D1D, 0xFF582C2C, 0x06020000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF6D6D6D, 0xFFFBFBFB, 0xFFE5E5E5, 0xFF7D7D7D, 0xFF959595, 0xFF969696, 0xFF979797, 0xFF979797, 0xFF808080, 0xFFB7ABAB, 0xFF7B2828, 0xFFAC3434, - 0xFFF56A6A, 0xFFFF7070, 0xFFFF7070, 0xFFFE6F6F, 0xFFFC6A6A, 0xFFFA6565, 0xFFF86060, 0xFFF55C5C, 0xFFF35757, 0xFFF15252, 0xFFE64848, 0xFFA11F1F, 0xCB530000, 0x1D0C0000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF6A6A6A, 0xFFFAFAFA, 0xFFE6E6E6, 0xFF989898, 0xFFB5B5B5, 0xFFB6B6B6, 0xFFB6B6B6, 0xFFB7B7B7, 0xFF989292, 0xFF7E2626, 0xFFCB5050, 0xFFFF7474, - 0xFFFF7070, 0xFFFF7070, 0xFFFD6B6B, 0xFFFA6767, 0xFFF86262, 0xFFF65D5D, 0xFFF45858, 0xFFF15353, 0xFFEF4E4E, 0xFFED4949, 0xFFEB4444, 0xFFE93F3F, 0xFFB62424, 0xD7570000, 0x0F060000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF666666, 0xFFFAFAFA, 0xFFE6E6E6, 0xFF7E7E7E, 0xFF969696, 0xFF979797, 0xFF979797, 0xFF989898, 0xFF733D3D, 0xFFA63737, 0xFFFF7979, 0xFFFF7272, - 0xFFFD6D6D, 0xFFFB6868, 0xFFF96363, 0xFFF65E5E, 0xFFF45959, 0xFFF25454, 0xFFF04F4F, 0xFFEE4A4A, 0xFFEB4545, 0xFFE94141, 0xFFE73C3C, 0xFFE53737, 0xFFE23535, 0xFF911313, 0x86360000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF636363, 0xFFFAFAFA, 0xFFE6E6E6, 0xFF989898, 0xFFB5B5B5, 0xFFB6B6B6, 0xFFB7B7B7, 0xFFB8B8B8, 0xFF6E1515, 0xFFDF6D6D, 0xFFFE7575, 0xFFFB6969, - 0xFFF96464, 0xFFF75F5F, 0xFFF55A5A, 0xFFF35555, 0xFFF05050, 0xFFEE4C4C, 0xFFEC4747, 0xFFEA4242, 0xFFE73D3D, 0xFFE53838, 0xFFE33333, 0xFFE12E2E, 0xFFDF2D2D, 0xFFC22828, 0xDE590000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF606060, 0xFFF9F9F9, 0xFFE6E6E6, 0xFF7E7E7E, 0xFF979797, 0xFF979797, 0xFF989898, 0xFF989898, 0xFF670101, 0xFFED7B7B, 0xFFFA6A6A, 0xFFF86060, - 0xFFF55B5B, 0xFFF35656, 0xFFF15252, 0xFFEF4D4D, 0xFFEC4848, 0xFFCD8478, 0xFF777CAE, 0xFF777BAD, 0xFF7275A7, 0xFF6B6FA1, 0xFF67699B, 0xFF606396, 0xFF5E6091, 0xFF7E5E82, 0xFC660000, 0x02000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF5D5D5D, 0xFFF9F9F9, 0xFFE6E6E6, 0xFF999999, 0xFFB6B6B6, 0xFFB6B6B6, 0xFFB7B7B7, 0xFFB8B8B8, 0xFF670000, 0xFFD86262, 0xFFF76D6D, 0xFFF45858, - 0xFFF15353, 0xFFEF4E4E, 0xFFED4949, 0xFFEB4444, 0xFFE84341, 0xFFC5E18C, 0xFF8A939F, 0xFF5889C7, 0xFF5182C1, 0xFF4B7CBA, 0xFF4475B4, 0xFF3D6EAE, 0xFF4572AD, 0xFF6A6087, 0xFF670000, 0x0E000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF595959, 0xFFF9F9F9, 0xFFE7E7E7, 0xFF7E7E7E, 0xFF979797, 0xFF979797, 0xFF989898, 0xFF989898, 0xFF690D0D, 0xFFA32424, 0xFFEF7B7B, 0xFFF15A5A, - 0xFFEE4A4A, 0xFFEB4545, 0xFFE94040, 0xFFE73C3C, 0xFFD86B4D, 0xFFADE773, 0xFFABC35F, 0xFF5585C0, 0xFF4D7EBD, 0xFF4778B7, 0xFF4071B0, 0xFF4372AE, 0xFF6B7AA6, 0xFF544267, 0xE85C0000, 0x16000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF565656, 0xFFF8F8F8, 0xFFE7E7E7, 0xFF999999, 0xFFB6B6B6, 0xFFB6B6B6, 0xFFB7B7B7, 0xFFB8B8B8, 0xFF773939, 0xFF7A0909, 0xFFBB3232, 0xFFE87171, - 0xFFED6161, 0xFFE84242, 0xFFE53838, 0xFFE33333, 0xFFBA9F4E, 0xFF99E053, 0xFF8FDC43, 0xFF848A66, 0xFF497AB9, 0xFF4777B4, 0xFF5882B9, 0xFF797FA6, 0xFF455E90, 0xFF651723, 0xAC3E0000, 0x13000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF535353, 0xFFF8F8F8, 0xFFE6E6E6, 0xFF7E7E7E, 0xFF979797, 0xFF979797, 0xFF989898, 0xFF989898, 0xFF766B6B, 0xFF6D0E0E, 0xFF881111, 0xFFB02323, - 0xFFCA4545, 0xFFE26666, 0xFFE85D5D, 0xFFE24F4B, 0xFFA1D656, 0xFF91DC47, 0xFF8BD93C, 0xFF90C93A, 0xFF7494BB, 0xFF848EB4, 0xFF70688E, 0xFF3B5E92, 0xFF602942, 0xEF5F0000, 0x400B0000, 0x08000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF4F4F4F, 0xFFF8F8F8, 0xFFE6E6E6, 0xFFE5E5E5, 0xFFE6E6E6, 0xFFE7E7E7, 0xFFE8E8E8, 0xFFE9E9E9, 0xFFE6E6E6, 0xFFBDA9A9, 0xFF711515, 0xFF7B0C0C, - 0xFFA21D1D, 0xFFAD1B1B, 0xFFB62727, 0xFFB24F33, 0xFF93A640, 0xFF96A840, 0xFF91A73C, 0xFF869F33, 0xFF6E675B, 0xFF375C93, 0xFF544D75, 0xFF6B1823, 0xEB5C0000, 0x59130000, 0x16000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x0D000000, 0xF14C4C4C, 0xFFF7F7F7, 0xFFF6F6F6, 0xFFF6F6F6, 0xFFF6F6F6, 0xFFF7F7F7, 0xFFF7F7F7, 0xFFF7F7F7, 0xFFF7F7F7, 0xFFF0F0F0, 0xFFD1C9C9, 0xFF905353, - 0xFF6A0505, 0xFF770A0A, 0xFF8E1414, 0xFF91331C, 0xFF728723, 0xFF709024, 0xFF6E8F23, 0xFF737C1F, 0xFF703C31, 0xFF6C141B, 0xFF6A0505, 0xFB531616, 0x52080000, 0x1D000000, 0x01000000, 0x00000000, - 0x00000000, 0x00000000, 0x0D000000, 0x22000000, 0x78191919, 0xF6535353, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF555555, 0xFF4F4F4F, - 0xFF4B4343, 0xFF522828, 0xFF5A1616, 0xFF610A0A, 0xFF650404, 0xFF670000, 0xFF650404, 0xFF600909, 0xFF591515, 0xFF502626, 0xF9453C3C, 0x93151515, 0x3A000000, 0x1B000000, 0x02000000, 0x00000000, - 0x00000000, 0x00000000, 0x06000000, 0x1D000000, 0x30000000, 0x40000000, 0x47000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, - 0x4C000000, 0x54000000, 0x5E000000, 0x65000000, 0x69000000, 0x6B000000, 0x6B000000, 0x69000000, 0x63000000, 0x52000000, 0x4B000000, 0x3B000000, 0x29000000, 0x0C000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x07000000, 0x0F000000, 0x15000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, - 0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x17000000, 0x18000000, 0x17000000, 0x17000000, 0x16000000, 0x14000000, 0x0F000000, 0x08000000, 0x01000000, 0x00000000, 0x00000000, 0x00000000, -}; - -static const struct { - const char *name; - void *data; - int width; - int height; - int stride; -} files[] = { - { "andlabs_16x16test_24june2016.png", dat0, 16, 16, 64 }, - { "andlabs_32x32test_24june2016.png", dat1, 32, 32, 128 }, - { "tango-icon-theme-0.8.90_16x16_x-office-spreadsheet.png", dat2, 16, 16, 64 }, - { "tango-icon-theme-0.8.90_32x32_x-office-spreadsheet.png", dat3, 32, 32, 128 }, -}; - -void appendImageNamed(uiImage *img, const char *name) -{ - int i; - - i = 0; - for (;;) { - if (strcmp(name, files[i].name) == 0) { - uiImageAppend(img, files[i].data, files[i].width, files[i].height, files[i].stride); - return; - } - i++; - } -} - diff --git a/test/main.c b/test/main.c index f33f30ab..18774dcd 100644 --- a/test/main.c +++ b/test/main.c @@ -159,8 +159,8 @@ int main(int argc, char *argv[]) innerTab = newTab(); uiTabAppend(outerTab, "Pages 16-?", uiControl(innerTab)); - page16 = makePage16(); - uiTabAppend(innerTab, "Page 16", uiControl(page16)); +// page16 = makePage16(); +// uiTabAppend(innerTab, "Page 16", uiControl(page16)); if (startspaced) setSpaced(1); diff --git a/test/page16.c b/test/page16.c deleted file mode 100644 index 80ac0139..00000000 --- a/test/page16.c +++ /dev/null @@ -1,143 +0,0 @@ -// 21 june 2016 -#include "test.h" - -static uiTableModelHandler mh; - -static int modelNumColumns(uiTableModelHandler *mh, uiTableModel *m) -{ - return 9; -} - -static uiTableModelColumnType modelColumnType(uiTableModelHandler *mh, uiTableModel *m, int column) -{ - if (column == 3 || column == 4) - return uiTableModelColumnColor; - if (column == 5) - return uiTableModelColumnImage; - if (column == 7 || column == 8) - return uiTableModelColumnInt; - return uiTableModelColumnString; -} - -static int modelNumRows(uiTableModelHandler *mh, uiTableModel *m) -{ - return 15; -} - -static uiImage *img[2]; -static char row9text[1024]; -static int yellowRow = -1; -static int checkStates[15]; - -static void *modelCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, int col) -{ - char buf[256]; - - if (col == 3) { - if (row == yellowRow) - return uiTableModelGiveColor(1, 1, 0, 1); - if (row == 3) - return uiTableModelGiveColor(1, 0, 0, 1); - if (row == 11) - return uiTableModelGiveColor(0, 0.5, 1, 0.5); - return NULL; - } - if (col == 4) { - if ((row % 2) == 1) - return uiTableModelGiveColor(0.5, 0, 0.75, 1); - return NULL; - } - if (col == 5) { - if (row < 8) - return img[0]; - return img[1]; - } - if (col == 7) - return uiTableModelGiveInt(checkStates[row]); - if (col == 8) { - if (row == 0) - return uiTableModelGiveInt(0); - if (row == 13) - return uiTableModelGiveInt(100); - if (row == 14) - return uiTableModelGiveInt(-1); - return uiTableModelGiveInt(50); - } - switch (col) { - case 0: - sprintf(buf, "Row %d", row); - break; - case 2: - if (row == 9) - return uiTableModelStrdup(row9text); - // fall through - case 1: - strcpy(buf, "Part"); - break; - case 6: - strcpy(buf, "Make Yellow"); - break; - } - return uiTableModelStrdup(buf); -} - -static void modelSetCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, int col, const void *val) -{ - if (row == 9 && col == 2) - strcpy(row9text, (const char *) val); - if (col == 6) - yellowRow = row; - if (col == 7) - checkStates[row] = uiTableModelTakeInt(val); -} - -uiBox *makePage16(void) -{ - uiBox *page16; - uiTableModel *m; - uiTable *t; - uiTableColumn *tc; - - img[0] = uiNewImage(16, 16); - appendImageNamed(img[0], "andlabs_16x16test_24june2016.png"); - appendImageNamed(img[0], "andlabs_32x32test_24june2016.png"); - img[1] = uiNewImage(16, 16); - appendImageNamed(img[1], "tango-icon-theme-0.8.90_16x16_x-office-spreadsheet.png"); - appendImageNamed(img[1], "tango-icon-theme-0.8.90_32x32_x-office-spreadsheet.png"); - - strcpy(row9text, "Part"); - - memset(checkStates, 0, 15 * sizeof (int)); - - page16 = newVerticalBox(); - - mh.NumColumns = modelNumColumns; - mh.ColumnType = modelColumnType; - mh.NumRows = modelNumRows; - mh.CellValue = modelCellValue; - mh.SetCellValue = modelSetCellValue; - m = uiNewTableModel(&mh); - - t = uiNewTable(m); - uiBoxAppend(page16, uiControl(t), 1); - - uiTableAppendTextColumn(t, "Column 1", 0); - - tc = uiTableAppendColumn(t, "Column 2"); - uiTableColumnAppendImagePart(tc, 5, 0); - uiTableColumnAppendTextPart(tc, 1, 0); - uiTableColumnAppendTextPart(tc, 2, 1); - uiTableColumnPartSetTextColor(tc, 1, 4); - uiTableColumnPartSetEditable(tc, 2, 1); - - uiTableSetRowBackgroundColorModelColumn(t, 3); - - tc = uiTableAppendColumn(t, "Buttons"); - uiTableColumnAppendCheckboxPart(tc, 7, 0); - uiTableColumnAppendButtonPart(tc, 6, 1); - - tc = uiTableAppendColumn(t, "Progress Bar"); - uiTableColumnAppendProgressBarPart(tc, 8, 0); - - return page16; -} diff --git a/test/test.h b/test/test.h index 224ef667..66b1baa7 100644 --- a/test/test.h +++ b/test/test.h @@ -89,9 +89,3 @@ extern uiTab *makePage14(void); // page15.c extern uiBox *makePage15(uiWindow *); - -// page16.c -extern uiBox *makePage16(void); - -// images.c -extern void appendImageNamed(uiImage *img, const char *name); diff --git a/ui.h b/ui.h index 5852bf6d..5a2069e8 100644 --- a/ui.h +++ b/ui.h @@ -684,9 +684,6 @@ _UI_EXTERN int uiGridPadded(uiGrid *g); _UI_EXTERN void uiGridSetPadded(uiGrid *g, int padded); _UI_EXTERN uiGrid *uiNewGrid(void); -// TODO merge -#include "uitable.h" - #ifdef __cplusplus } #endif diff --git a/uitable.h b/uitable.h deleted file mode 100644 index a54398c5..00000000 --- a/uitable.h +++ /dev/null @@ -1,65 +0,0 @@ -// 20 june 2016 -// kept in a separate file for now - -typedef struct uiImage uiImage; - -// TODO use const void * for const correctness -_UI_EXTERN uiImage *uiNewImage(double width, double height); -_UI_EXTERN void uiFreeImage(uiImage *i); -_UI_EXTERN void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int pixelStride); - -typedef struct uiTableModel uiTableModel; -typedef struct uiTableModelHandler uiTableModelHandler; - -// TODO actually validate these -_UI_ENUM(uiTableModelColumnType) { - uiTableModelColumnString, - uiTableModelColumnImage, - uiTableModelColumnInt, - uiTableModelColumnColor, -}; - -// TODO validate ranges; validate types on each getter/setter call (? table columns only?) -struct uiTableModelHandler { - int (*NumColumns)(uiTableModelHandler *, uiTableModel *); - uiTableModelColumnType (*ColumnType)(uiTableModelHandler *, uiTableModel *, int); - int (*NumRows)(uiTableModelHandler *, uiTableModel *); - void *(*CellValue)(uiTableModelHandler *, uiTableModel *, int, int); - void (*SetCellValue)(uiTableModelHandler *, uiTableModel *, int, int, const void *); -}; - -_UI_EXTERN void *uiTableModelStrdup(const char *str); -// TODO rename the strdup one to this too -_UI_EXTERN void *uiTableModelGiveColor(double r, double g, double b, double a); -_UI_EXTERN void *uiTableModelGiveInt(int i); -// TODO TakeString -// TODO add const -_UI_EXTERN int uiTableModelTakeInt(void *v); - -_UI_EXTERN uiTableModel *uiNewTableModel(uiTableModelHandler *mh); -_UI_EXTERN void uiFreeTableModel(uiTableModel *m); -_UI_EXTERN void uiTableModelRowInserted(uiTableModel *m, int newIndex); -_UI_EXTERN void uiTableModelRowChanged(uiTableModel *m, int index); -_UI_EXTERN void uiTableModelRowDeleted(uiTableModel *m, int oldIndex); -// TODO reordering/moving - -typedef struct uiTableColumn uiTableColumn; - -_UI_EXTERN void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand); -// TODO images shouldn't expand... -_UI_EXTERN void uiTableColumnAppendImagePart(uiTableColumn *c, int modelColumn, int expand); -_UI_EXTERN void uiTableColumnAppendButtonPart(uiTableColumn *c, int modelColumn, int expand); -// TODO should these have labels? -_UI_EXTERN void uiTableColumnAppendCheckboxPart(uiTableColumn *c, int modelColumn, int expand); -_UI_EXTERN void uiTableColumnAppendProgressBarPart(uiTableColumn *c, int modelColumn, int expand); -// TODO Editable? -_UI_EXTERN void uiTableColumnPartSetEditable(uiTableColumn *c, int part, int editable); -_UI_EXTERN void uiTableColumnPartSetTextColor(uiTableColumn *c, int part, int modelColumn); - -typedef struct uiTable uiTable; -#define uiTable(this) ((uiTable *) (this)) -_UI_EXTERN uiTableColumn *uiTableAppendColumn(uiTable *t, const char *name); -_UI_EXTERN uiTableColumn *uiTableAppendTextColumn(uiTable *t, const char *name, int modelColumn); -// TODO getter? -_UI_EXTERN void uiTableSetRowBackgroundColorModelColumn(uiTable *t, int modelColumn); -_UI_EXTERN uiTable *uiNewTable(uiTableModel *model); diff --git a/unix/CMakeLists.txt b/unix/CMakeLists.txt index e967f29a..9300bcb7 100644 --- a/unix/CMakeLists.txt +++ b/unix/CMakeLists.txt @@ -40,7 +40,6 @@ list(APPEND _LIBUI_SOURCES unix/spinbox.c unix/stddialogs.c unix/tab.c - unix/table.c unix/text.c unix/util.c unix/window.c diff --git a/unix/table.c b/unix/table.c deleted file mode 100644 index a11844c2..00000000 --- a/unix/table.c +++ /dev/null @@ -1,642 +0,0 @@ -// 26 june 2016 -#include "uipriv_unix.h" - -#define uiTableModelType (uiTableModel_get_type()) -#define uiTableModel(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), uiTableModelType, uiTableModel)) -#define isuiTableModel(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), uiTableModelType)) -#define uiTableModelClass(class) (G_TYPE_CHECK_CLASS_CAST((class), uiTableModelType, uiTableModelClass)) -#define isuiTableModelClass(class) (G_TYPE_CHECK_CLASS_TYPE((class), uiTableModel)) -#define getuiTableModelClass(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), uiTableModelType, uiTableModelClass)) - -typedef struct uiTableModelClass uiTableModelClass; - -struct uiTableModel { - GObject parent_instance; - uiTableModelHandler *mh; -}; - -struct uiTableModelClass { - GObjectClass parent_class; -}; - -static void uiTableModel_gtk_tree_model_interface_init(GtkTreeModelIface *iface); - -G_DEFINE_TYPE_WITH_CODE(uiTableModel, uiTableModel, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE(GTK_TYPE_TREE_MODEL, uiTableModel_gtk_tree_model_interface_init)) - -static void uiTableModel_init(uiTableModel *m) -{ - // nothing to do -} - -static void uiTableModel_dispose(GObject *obj) -{ - G_OBJECT_CLASS(uiTableModel_parent_class)->dispose(obj); -} - -static void uiTableModel_finalize(GObject *obj) -{ - G_OBJECT_CLASS(uiTableModel_parent_class)->finalize(obj); -} - -static GtkTreeModelFlags uiTableModel_get_flags(GtkTreeModel *mm) -{ - return GTK_TREE_MODEL_LIST_ONLY; -} - -static gint uiTableModel_get_n_columns(GtkTreeModel *mm) -{ - uiTableModel *m = uiTableModel(mm); - - return (*(m->mh->NumColumns))(m->mh, m); -} - -static GType uiTableModel_get_column_type(GtkTreeModel *mm, gint index) -{ - uiTableModel *m = uiTableModel(mm); - - switch ((*(m->mh->ColumnType))(m->mh, m, index)) { - case uiTableModelColumnString: - return G_TYPE_STRING; - case uiTableModelColumnImage: - return G_TYPE_POINTER; - case uiTableModelColumnInt: - return G_TYPE_INT; - case uiTableModelColumnColor: - return GDK_TYPE_RGBA; - } - // TODO - return G_TYPE_INVALID; -} - -#define STAMP_GOOD 0x1234 -#define STAMP_BAD 0x5678 - -static gboolean uiTableModel_get_iter(GtkTreeModel *mm, GtkTreeIter *iter, GtkTreePath *path) -{ - uiTableModel *m = uiTableModel(mm); - gint row; - - if (gtk_tree_path_get_depth(path) != 1) - goto bad; - row = gtk_tree_path_get_indices(path)[0]; - if (row < 0) - goto bad; - if (row >= (*(m->mh->NumRows))(m->mh, m)) - goto bad; - iter->stamp = STAMP_GOOD; - iter->user_data = GINT_TO_POINTER(row); - return TRUE; -bad: - iter->stamp = STAMP_BAD; - return FALSE; -} - -// GtkListStore returns NULL on error; let's do that too -static GtkTreePath *uiTableModel_get_path(GtkTreeModel *mm, GtkTreeIter *iter) -{ - gint row; - - if (iter->stamp != STAMP_GOOD) - return NULL; - row = GPOINTER_TO_INT(iter->user_data); - return gtk_tree_path_new_from_indices(row, -1); -} - -// GtkListStore leaves value empty on failure; let's do the same -static void uiTableModel_get_value(GtkTreeModel *mm, GtkTreeIter *iter, gint column, GValue *value) -{ - uiTableModel *m = uiTableModel(mm); - gint row; - void *data; - - if (iter->stamp != STAMP_GOOD) - return; - row = GPOINTER_TO_INT(iter->user_data); - data = (*(m->mh->CellValue))(m->mh, m, row, column); - switch ((*(m->mh->ColumnType))(m->mh, m, column)) { - case uiTableModelColumnString: - g_value_init(value, G_TYPE_STRING); - g_value_take_string(value, (char *) data); - return; - case uiTableModelColumnImage: - g_value_init(value, G_TYPE_POINTER); - g_value_set_pointer(value, data); - return; - case uiTableModelColumnInt: - g_value_init(value, G_TYPE_INT); - g_value_set_int(value, uiTableModelTakeInt(data)); - return; - case uiTableModelColumnColor: - g_value_init(value, GDK_TYPE_RGBA); - g_value_take_boxed(value, data); - return; - } - // TODO -} - -static gboolean uiTableModel_iter_next(GtkTreeModel *mm, GtkTreeIter *iter) -{ - uiTableModel *m = uiTableModel(mm); - gint row; - - if (iter->stamp != STAMP_GOOD) - return FALSE; - row = GPOINTER_TO_INT(iter->user_data); - row++; - if (row >= (*(m->mh->NumRows))(m->mh, m)) { - iter->stamp = STAMP_BAD; - return FALSE; - } - iter->user_data = GINT_TO_POINTER(row); - return TRUE; -} - -static gboolean uiTableModel_iter_previous(GtkTreeModel *mm, GtkTreeIter *iter) -{ - gint row; - - if (iter->stamp != STAMP_GOOD) - return FALSE; - row = GPOINTER_TO_INT(iter->user_data); - row--; - if (row < 0) { - iter->stamp = STAMP_BAD; - return FALSE; - } - iter->user_data = GINT_TO_POINTER(row); - return TRUE; -} - -static gboolean uiTableModel_iter_children(GtkTreeModel *mm, GtkTreeIter *iter, GtkTreeIter *parent) -{ - return gtk_tree_model_iter_nth_child(mm, iter, parent, 0); -} - -static gboolean uiTableModel_iter_has_child(GtkTreeModel *mm, GtkTreeIter *iter) -{ - return FALSE; -} - -static gint uiTableModel_iter_n_children(GtkTreeModel *mm, GtkTreeIter *iter) -{ - uiTableModel *m = uiTableModel(mm); - - if (iter != NULL) - return 0; - return (*(m->mh->NumRows))(m->mh, m); -} - -static gboolean uiTableModel_iter_nth_child(GtkTreeModel *mm, GtkTreeIter *iter, GtkTreeIter *parent, gint n) -{ - uiTableModel *m = uiTableModel(mm); - - if (iter->stamp != STAMP_GOOD) - return FALSE; - if (parent != NULL) - goto bad; - if (n < 0) - goto bad; - if (n >= (*(m->mh->NumRows))(m->mh, m)) - goto bad; - iter->stamp = STAMP_GOOD; - iter->user_data = GINT_TO_POINTER(n); - return TRUE; -bad: - iter->stamp = STAMP_BAD; - return FALSE; -} - -gboolean uiTableModel_iter_parent(GtkTreeModel *mm, GtkTreeIter *iter, GtkTreeIter *child) -{ - iter->stamp = STAMP_BAD; - return FALSE; -} - -static void uiTableModel_class_init(uiTableModelClass *class) -{ - G_OBJECT_CLASS(class)->dispose = uiTableModel_dispose; - G_OBJECT_CLASS(class)->finalize = uiTableModel_finalize; -} - -static void uiTableModel_gtk_tree_model_interface_init(GtkTreeModelIface *iface) -{ - iface->get_flags = uiTableModel_get_flags; - iface->get_n_columns = uiTableModel_get_n_columns; - iface->get_column_type = uiTableModel_get_column_type; - iface->get_iter = uiTableModel_get_iter; - iface->get_path = uiTableModel_get_path; - iface->get_value = uiTableModel_get_value; - iface->iter_next = uiTableModel_iter_next; - iface->iter_previous = uiTableModel_iter_previous; - iface->iter_children = uiTableModel_iter_children; - iface->iter_has_child = uiTableModel_iter_has_child; - iface->iter_n_children = uiTableModel_iter_n_children; - iface->iter_nth_child = uiTableModel_iter_nth_child; - iface->iter_parent = uiTableModel_iter_parent; - // don't specify ref_node() or unref_node() -} - -void *uiTableModelStrdup(const char *str) -{ - return g_strdup(str); -} - -void *uiTableModelGiveColor(double r, double g, double b, double a) -{ - GdkRGBA rgba; - - rgba.red = r; - rgba.green = g; - rgba.blue = b; - rgba.alpha = a; - return gdk_rgba_copy(&rgba); -} - -uiTableModel *uiNewTableModel(uiTableModelHandler *mh) -{ - uiTableModel *m; - - m = uiTableModel(g_object_new(uiTableModelType, NULL)); - m->mh = mh; - return m; -} - -void uiFreeTableModel(uiTableModel *m) -{ - g_object_unref(m); -} - -void uiTableModelRowInserted(uiTableModel *m, int newIndex) -{ - GtkTreePath *path; - GtkTreeIter iter; - - path = gtk_tree_path_new_from_indices(newIndex, -1); - iter.stamp = STAMP_GOOD; - iter.user_data = GINT_TO_POINTER(newIndex); - gtk_tree_model_row_inserted(GTK_TREE_MODEL(m), path, &iter); - gtk_tree_path_free(path); -} - -void uiTableModelRowChanged(uiTableModel *m, int index) -{ - GtkTreePath *path; - GtkTreeIter iter; - - path = gtk_tree_path_new_from_indices(index, -1); - iter.stamp = STAMP_GOOD; - iter.user_data = GINT_TO_POINTER(index); - gtk_tree_model_row_changed(GTK_TREE_MODEL(m), path, &iter); - gtk_tree_path_free(path); -} - -void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) -{ - GtkTreePath *path; - - path = gtk_tree_path_new_from_indices(oldIndex, -1); - gtk_tree_model_row_deleted(GTK_TREE_MODEL(m), path); - gtk_tree_path_free(path); -} - -enum { - partText, - partImage, - partButton, - partCheckbox, - partProgressBar, -}; - -struct tablePart { - int type; - int textColumn; - int imageColumn; - int valueColumn; - int colorColumn; - GtkCellRenderer *r; - uiTable *tv; // for pixbufs and background color -}; - -struct uiTableColumn { - GtkTreeViewColumn *c; - uiTable *tv; // for pixbufs and background color - GPtrArray *parts; -}; - -struct uiTable { - uiUnixControl c; - GtkWidget *widget; - GtkContainer *scontainer; - GtkScrolledWindow *sw; - GtkWidget *treeWidget; - GtkTreeView *tv; - GPtrArray *columns; - uiTableModel *model; - int backgroundColumn; -}; - -// use the same size as GtkFileChooserWidget's treeview -// TODO refresh when icon theme changes -// TODO doesn't work when scaled -// TODO is this even necessary? -static void setImageSize(GtkCellRenderer *r) -{ - gint size; - gint width, height; - gint xpad, ypad; - - size = 16; // fallback used by GtkFileChooserWidget - if (gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height) != FALSE) - size = MAX(width, height); - gtk_cell_renderer_get_padding(r, &xpad, &ypad); - gtk_cell_renderer_set_fixed_size(r, - 2 * xpad + size, - 2 * ypad + size); -} - -static void applyColor(GtkTreeModel *mm, GtkTreeIter *iter, int modelColumn, GtkCellRenderer *r, const char *prop, const char *propSet) -{ - GValue value = G_VALUE_INIT; - GdkRGBA *rgba; - - gtk_tree_model_get_value(mm, iter, modelColumn, &value); - rgba = (GdkRGBA *) g_value_get_boxed(&value); - if (rgba != NULL) - g_object_set(r, prop, rgba, NULL); - else - g_object_set(r, propSet, FALSE, NULL); - g_value_unset(&value); -} - -static void dataFunc(GtkTreeViewColumn *c, GtkCellRenderer *r, GtkTreeModel *mm, GtkTreeIter *iter, gpointer data) -{ - struct tablePart *part = (struct tablePart *) data; - GValue value = G_VALUE_INIT; - const gchar *str; - uiImage *img; - int pval; - - switch (part->type) { - case partText: - gtk_tree_model_get_value(mm, iter, part->textColumn, &value); - str = g_value_get_string(&value); - g_object_set(r, "text", str, NULL); - if (part->colorColumn != -1) - applyColor(mm, iter, - part->colorColumn, - r, "foreground-rgba", "foreground-set"); - break; - case partImage: -//TODO setImageSize(r); - gtk_tree_model_get_value(mm, iter, part->imageColumn, &value); - img = (uiImage *) g_value_get_pointer(&value); - g_object_set(r, "surface", - imageAppropriateSurface(img, part->tv->treeWidget), - NULL); - break; - case partButton: - gtk_tree_model_get_value(mm, iter, part->textColumn, &value); - str = g_value_get_string(&value); - g_object_set(r, "text", str, NULL); - break; - case partCheckbox: - gtk_tree_model_get_value(mm, iter, part->valueColumn, &value); - g_object_set(r, "active", g_value_get_int(&value) != 0, NULL); - break; - case partProgressBar: - gtk_tree_model_get_value(mm, iter, part->valueColumn, &value); - pval = g_value_get_int(&value); - if (pval == -1) { - // TODO - } else - g_object_set(r, - "pulse", -1, - "value", pval, - NULL); - break; - } - g_value_unset(&value); - - if (part->tv->backgroundColumn != -1) - applyColor(mm, iter, - part->tv->backgroundColumn, - r, "cell-background-rgba", "cell-background-set"); -} - -static void onEdited(struct tablePart *part, int column, const char *pathstr, const void *data) -{ - GtkTreePath *path; - int row; - uiTableModel *m; - - path = gtk_tree_path_new_from_string(pathstr); - row = gtk_tree_path_get_indices(path)[0]; - gtk_tree_path_free(path); - m = part->tv->model; - (*(m->mh->SetCellValue))(m->mh, m, row, column, data); - // and update - uiTableModelRowChanged(m, row); -} - -static void appendPart(uiTableColumn *c, struct tablePart *part, GtkCellRenderer *r, int expand) -{ - part->r = r; - gtk_tree_view_column_pack_start(c->c, part->r, expand != 0); - gtk_tree_view_column_set_cell_data_func(c->c, part->r, dataFunc, part, NULL); - g_ptr_array_add(c->parts, part); -} - -static void textEdited(GtkCellRendererText *renderer, gchar *path, gchar *newText, gpointer data) -{ - struct tablePart *part = (struct tablePart *) data; - - onEdited(part, part->textColumn, path, newText); -} - -void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand) -{ - struct tablePart *part; - GtkCellRenderer *r; - - part = uiNew(struct tablePart); - part->type = partText; - part->textColumn = modelColumn; - part->tv = c->tv; - part->colorColumn = -1; - - r = gtk_cell_renderer_text_new(); - g_object_set(r, "editable", FALSE, NULL); - g_signal_connect(r, "edited", G_CALLBACK(textEdited), part); - - appendPart(c, part, r, expand); -} - -void uiTableColumnAppendImagePart(uiTableColumn *c, int modelColumn, int expand) -{ - struct tablePart *part; - - part = uiNew(struct tablePart); - part->type = partImage; - part->imageColumn = modelColumn; - part->tv = c->tv; - appendPart(c, part, - gtk_cell_renderer_pixbuf_new(), - expand); -} - -// TODO wrong type here -static void buttonClicked(GtkCellRenderer *r, gchar *pathstr, gpointer data) -{ - struct tablePart *part = (struct tablePart *) data; - - onEdited(part, part->textColumn, pathstr, NULL); -} - -void uiTableColumnAppendButtonPart(uiTableColumn *c, int modelColumn, int expand) -{ - struct tablePart *part; - GtkCellRenderer *r; - - part = uiNew(struct tablePart); - part->type = partButton; - part->textColumn = modelColumn; - part->tv = c->tv; - - r = newCellRendererButton(); - g_object_set(r, "sensitive", TRUE, NULL); // editable by default - g_signal_connect(r, "clicked", G_CALLBACK(buttonClicked), part); - - appendPart(c, part, r, expand); -} - -// yes, we need to do all this twice :| -static void checkboxToggled(GtkCellRendererToggle *r, gchar *pathstr, gpointer data) -{ - struct tablePart *part = (struct tablePart *) data; - GtkTreePath *path; - int row; - uiTableModel *m; - void *value; - int intval; - - path = gtk_tree_path_new_from_string(pathstr); - row = gtk_tree_path_get_indices(path)[0]; - gtk_tree_path_free(path); - m = part->tv->model; - value = (*(m->mh->CellValue))(m->mh, m, row, part->valueColumn); - intval = !uiTableModelTakeInt(value); - onEdited(part, part->valueColumn, pathstr, uiTableModelGiveInt(intval)); -} - -void uiTableColumnAppendCheckboxPart(uiTableColumn *c, int modelColumn, int expand) -{ - struct tablePart *part; - GtkCellRenderer *r; - - part = uiNew(struct tablePart); - part->type = partCheckbox; - part->valueColumn = modelColumn; - part->tv = c->tv; - - r = gtk_cell_renderer_toggle_new(); - g_object_set(r, "sensitive", TRUE, NULL); // editable by default - g_signal_connect(r, "toggled", G_CALLBACK(checkboxToggled), part); - - appendPart(c, part, r, expand); -} - -void uiTableColumnAppendProgressBarPart(uiTableColumn *c, int modelColumn, int expand) -{ - struct tablePart *part; - - part = uiNew(struct tablePart); - part->type = partProgressBar; - part->valueColumn = modelColumn; - part->tv = c->tv; - appendPart(c, part, - gtk_cell_renderer_progress_new(), - expand); -} - -void uiTableColumnPartSetEditable(uiTableColumn *c, int part, int editable) -{ - struct tablePart *p; - - p = (struct tablePart *) g_ptr_array_index(c->parts, part); - switch (p->type) { - case partImage: - case partProgressBar: - return; - case partButton: - case partCheckbox: - g_object_set(p->r, "sensitive", editable != 0, NULL); - return; - } - g_object_set(p->r, "editable", editable != 0, NULL); -} - -void uiTableColumnPartSetTextColor(uiTableColumn *c, int part, int modelColumn) -{ - struct tablePart *p; - - p = (struct tablePart *) g_ptr_array_index(c->parts, part); - p->colorColumn = modelColumn; - // TODO refresh table -} - -uiUnixControlAllDefaultsExceptDestroy(uiTable) - -static void uiTableDestroy(uiControl *c) -{ - uiTable *t = uiTable(c); - - // TODO - g_object_unref(t->widget); - uiFreeControl(uiControl(t)); -} - -uiTableColumn *uiTableAppendColumn(uiTable *t, const char *name) -{ - uiTableColumn *c; - - c = uiNew(uiTableColumn); - c->c = gtk_tree_view_column_new(); - gtk_tree_view_column_set_resizable(c->c, TRUE); - gtk_tree_view_column_set_title(c->c, name); - gtk_tree_view_append_column(t->tv, c->c); - c->tv = t; // TODO rename field to t, cascade - c->parts = g_ptr_array_new(); - return c; -} - -void uiTableSetRowBackgroundColorModelColumn(uiTable *t, int modelColumn) -{ - t->backgroundColumn = modelColumn; - // TODO refresh table -} - -uiTable *uiNewTable(uiTableModel *model) -{ - uiTable *t; - - uiUnixNewControl(uiTable, t); - - t->model = model; - t->backgroundColumn = -1; - - t->widget = gtk_scrolled_window_new(NULL, NULL); - t->scontainer = GTK_CONTAINER(t->widget); - t->sw = GTK_SCROLLED_WINDOW(t->widget); - gtk_scrolled_window_set_shadow_type(t->sw, GTK_SHADOW_IN); - - t->treeWidget = gtk_tree_view_new_with_model(GTK_TREE_MODEL(t->model)); - t->tv = GTK_TREE_VIEW(t->treeWidget); - // TODO set up t->tv - - gtk_container_add(t->scontainer, t->treeWidget); - // and make the tree view visible; only the scrolled window's visibility is controlled by libui - gtk_widget_show(t->treeWidget); - - return t; -} diff --git a/windows/area.cpp b/windows/area.cpp index a86371e9..ab69ff15 100644 --- a/windows/area.cpp +++ b/windows/area.cpp @@ -2,6 +2,8 @@ #include "uipriv_windows.hpp" #include "area.hpp" +// TODO handle WM_DESTROY/WM_NCDESTROY +// TODO same for other Direct2D stuff static LRESULT CALLBACK areaWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { uiArea *a; diff --git a/windows/tablepart.cpp b/windows/tablepart.cpp deleted file mode 100644 index c31932d7..00000000 --- a/windows/tablepart.cpp +++ /dev/null @@ -1,95 +0,0 @@ -// 30 june 2016 -// TODO includes - -typedef struct tablePartDrawParams tablePartDrawParams; -typedef struct tablePartMinimumSizeParams tableDrawMinimumSizeParams; -typedef struct tablePartEditingParams tablePartEditingParams; - -struct tablePartDrawParams { - HWND hwnd; - HDC hdc; - RECT *r; - bool selected; - bool focused; - bool hovering; - uiTableModel *model; - int row; -}; - -struct tablePartMinimumSizeParams { - HWND hwnd; - HDC hdc; - uiTableModel *model; - int row; -}; - -enum { - partEventDoNothing, - partEventRedraw, - partEventEdit, -}; - -struct tablePartEditingParams { - HWND newHWND; -}; - -enum { - partEditContinue, - partEditDone, -}; - -class tablePart { -public: - // needed so we can delete a tablePart - virtual ~tablePart() {} - - virtual HRESULT Draw(tablePartDrawParams *p) = 0; - virtual HRESULT MinimumSize(tablePartMinimumSizeParams *p, int *width, int *height) = 0; - - // returns a partEvent constant - virtual int MouseMove(int x, int y, RECT *cell) = 0; - virtual int MouseLeave(void) = 0; - virtual int LButtonDown(int x, int y, int count, RECT *cell) = 0; - virtual int LButtonUp(int x, int y, RECT *cell) = 0; - virtual int CaptureBroken(void) = 0; - virtual int KeyDown(void) = 0; - virtual int KeyUp(void) = 0; - - // editing; all optional - virtual int StartEditing(tablePartEditingParams *p) { return editDone; } - virtual int EditChildWM_COMMAND(WORD code, LRESULT *lr) { return editDone; } - virtual void FinishEditing(uiTableModel *model, int row) {} - virtual void CancelEditing(void) {} - - // TODO tooltips - // TODO timers and animations - - // optional methods - virtual void SetTextColorColumn(int col) {} - virtual void SetEditable(bool editable) {} -}; - -class tablePartText : public tablePart { - int textColumn; - int colorColumn; -public: - tablePartText(int tc) - { - this->textColumn = tc; - this->colorColumn = -1; - } - - // TODO figure out vertical alignment - virtual HRESULT Draw(tablePartDrawParams *p) - { - } - - virtual HRESULT MinimumSize(tablePartMinimumSizeParams *p, int *width, int *height) - { - } -}; - -tablePart *newTablePartText(int tc) -{ - return new tablePartText(tc); -} From 52d88d3f365c86679640da8b2d4dd3e6db8c92b0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 27 Nov 2016 17:36:11 -0500 Subject: [PATCH 0497/1329] Re-added the table code. Need to patch together everything else, but. --- common/table.c | 22 ++ darwin/table.m | 581 ++++++++++++++++++++++++++++++++++++++ test/images.c | 202 +++++++++++++ test/page16.c | 143 ++++++++++ uitable.h | 65 +++++ unix/table.c | 642 ++++++++++++++++++++++++++++++++++++++++++ windows/tablepart.cpp | 95 +++++++ windows/tableutil.cpp | 32 +++ 8 files changed, 1782 insertions(+) create mode 100644 common/table.c create mode 100644 darwin/table.m create mode 100644 test/images.c create mode 100644 test/page16.c create mode 100644 uitable.h create mode 100644 unix/table.c create mode 100644 windows/tablepart.cpp create mode 100644 windows/tableutil.cpp diff --git a/common/table.c b/common/table.c new file mode 100644 index 00000000..3726883a --- /dev/null +++ b/common/table.c @@ -0,0 +1,22 @@ +// 21 june 2016 +#include "../ui.h" +#include "uipriv.h" + +void *uiTableModelGiveInt(int i) +{ + return (void *) ((intptr_t) i); +} + +int uiTableModelTakeInt(void *v) +{ + return (int) ((intptr_t) v); +} + +uiTableColumn *uiTableAppendTextColumn(uiTable *t, const char *name, int modelColumn) +{ + uiTableColumn *tc; + + tc = uiTableAppendColumn(t, name); + uiTableColumnAppendTextPart(tc, modelColumn, 1); + return tc; +} diff --git a/darwin/table.m b/darwin/table.m new file mode 100644 index 00000000..9e578c85 --- /dev/null +++ b/darwin/table.m @@ -0,0 +1,581 @@ +// 21 june 2016 +#import "uipriv_darwin.h" + +// TODOs +// - initial state of table view is off +// - header cell seems off +// - background color shows up for a line or two below selection +// - editable NSTextFields have no intrinsic width +// - changing a part property does not refresh views +// - is the Y position of checkbox cells correct? +// - progressbars appear ABOVE the table header + +// LONGTERM +// - reuse row views instead of creating a new one each time + +@interface tableModel : NSObject { + uiTableModel *libui_m; +} +- (id)initWithModel:(uiTableModel *)m; +- (IBAction)onAction:(id)sender; +@end + +enum { + partText, + partImage, + partButton, + partCheckbox, + partProgressBar, +}; + +@interface tablePart : NSObject +@property int type; +@property int textColumn; +@property int textColorColumn; +@property int imageColumn; +@property int valueColumn; +@property int expand; +@property int editable; +- (NSView *)mkView:(uiTableModel *)m row:(int)row; +@end + +@interface tableColumn : NSTableColumn +@property uiTableColumn *libui_col; +@end + +@interface tableView : NSTableView +@property uiTable *libui_t; +@end + +struct uiTableModel { + uiTableModelHandler *mh; + tableModel *m; + NSMutableArray *tables; +}; + +struct uiTableColumn { + tableColumn *c; + NSMutableArray *parts; +}; + +struct uiTable { + uiDarwinControl c; + NSScrollView *sv; + tableView *tv; + struct scrollViewData *d; + int backgroundColumn; +}; + +@implementation tableModel + +- (id)initWithModel:(uiTableModel *)m +{ + self = [super init]; + if (self) + self->libui_m = m; + return self; +} + +- (NSInteger)numberOfRowsInTableView:(NSTableView *)tv +{ + uiTableModelHandler *mh = self->libui_m->mh; + + return (*(mh->NumRows))(mh, self->libui_m); +} + +// these are according to Interface Builder +#define xleft 2 +#define xmiddle 7 /* between images and text, anyway; let's just use it for everything to be simpler */ +#define xright 3 + + - (NSView *)tableView:(NSTableView *)tv viewForTableColumn:(NSTableColumn *)cc row:(NSInteger)row +{ + NSTableCellView *v; + tableColumn *c = (tableColumn *) cc; + tablePart *part; + NSMutableArray *views; + NSView *view, *prev; + + v = [[NSTableCellView alloc] initWithFrame:NSZeroRect]; + + views = [NSMutableArray new]; + for (part in c.libui_col->parts) + [views addObject:[part mkView:self->libui_m row:row]]; + if ([views count] == 0) // empty (TODO allow?) + goto done; + + // add to v and arrange horizontally + prev = nil; + for (view in views) { + [v addSubview:view]; + // TODO set [v imageView] and [v textField] as appropriate? + if (prev == nil) { // first view + [v addConstraint:mkConstraint(v, NSLayoutAttributeLeading, + NSLayoutRelationEqual, + view, NSLayoutAttributeLeading, + 1, -xleft, + @"uiTableColumn first part horizontal constraint")]; + prev = view; + continue; + } + [v addConstraint:mkConstraint(prev, NSLayoutAttributeTrailing, + NSLayoutRelationEqual, + view, NSLayoutAttributeLeading, + 1, -xmiddle, + @"uiTableColumn middle horizontal constraint")]; + prev = view; + } + [v addConstraint:mkConstraint(prev, NSLayoutAttributeTrailing, + NSLayoutRelationEqual, + v, NSLayoutAttributeTrailing, + 1, -xright, + @"uiTableColumn last part horizontal constraint")]; + + // and vertically + for (view in views) { + [v addConstraint:mkConstraint(view, NSLayoutAttributeCenterY, + NSLayoutRelationEqual, + v, NSLayoutAttributeCenterY, + 1, 0, + @"uiTableColumn part vertical constraint")]; + // TODO avoid the need for this hack + if ([view isKindOfClass:[NSImageView class]]) + [v addConstraint:mkConstraint(view, NSLayoutAttributeTop, + NSLayoutRelationEqual, + v, NSLayoutAttributeTop, + 1, 0, + @"uiTableColumn part vertical top constraint")]; + } + +done: + [views release]; + [v setTranslatesAutoresizingMaskIntoConstraints:NO]; + // TODO autorelease? + return v; +} + +- (void)tableView:(NSTableView *)nstv didAddRowView:(NSTableRowView *)rv forRow:(NSInteger)row +{ + uiTableModel *m = self->libui_m; + tableView *tv = (tableView *) nstv; + uiTable *t = tv.libui_t; + NSColor *color; + + if (t->backgroundColumn == -1) + return; + color = (NSColor *) ((*(m->mh->CellValue))(m->mh, m, row, t->backgroundColumn)); + if (color == nil) + return; + [rv setBackgroundColor:color]; + // TODO autorelease color? or release it? +} + +- (IBAction)onAction:(id)sender +{ + uiTableModel *m = self->libui_m; + NSView *view = (NSView *) sender; + NSTableView *tv; + NSInteger row; + const void *data; + + row = -1; + for (tv in m->tables) { + row = [tv rowForView:view]; + if (row != -1) + break; + } + if (row == -1) + implbug("table model action triggered on view with no associated table"); + + if ([view isKindOfClass:[NSTextField class]]) + data = [[((NSTextField *) view) stringValue] UTF8String]; + else if ([view isKindOfClass:[NSButton class]]) { + NSButton *b; + + b = (NSButton *) view; +// if ([b buttonType] == NSSwitchButton) + data = uiTableModelGiveInt([b state] == NSOnState); +// TODO there is no buttonType getter +if(1); else + data = NULL; + } else + implbug("table model editing action triggered on non-editable view"); + + // note the use of [view tag] — we need the model column, which we store in the view tag for relevant views below + (*(m->mh->SetCellValue))(m->mh, m, + row, [view tag], + data); + // always refresh the value in case the model rejected it + // TODO only affect tv? + uiTableModelRowChanged(m, row); +} + +@end + +@implementation tablePart + +- (id)init +{ + self = [super init]; + if (self) { + self.textColumn = -1; + self.textColorColumn = -1; + } + return self; +} + +- (NSView *)mkView:(uiTableModel *)m row:(int)row +{ + void *data; + NSString *str; + NSView *view; + NSTextField *tf; + NSImageView *iv; + NSButton *b; + NSProgressIndicator *p; + int value; + + switch (self.type) { + case partText: + data = (*(m->mh->CellValue))(m->mh, m, row, self.textColumn); + str = toNSString((char *) data); + uiFree(data); + tf = newLabel(str); + // TODO set wrap and ellipsize modes? + if (self.textColorColumn != -1) { + NSColor *color; + + color = (NSColor *) ((*(m->mh->CellValue))(m->mh, m, row, self.textColorColumn)); + if (color != nil) + [tf setTextColor:color]; + // TODO release color + } + if (self.editable) { + [tf setEditable:YES]; + [tf setTarget:m->m]; + [tf setAction:@selector(onAction:)]; + } + [tf setTag:self.textColumn]; + view = tf; + break; + case partImage: + data = (*(m->mh->CellValue))(m->mh, m, row, self.imageColumn); + iv = [[NSImageView alloc] initWithFrame:NSZeroRect]; + [iv setImage:imageImage((uiImage *) data)]; + [iv setImageFrameStyle:NSImageFrameNone]; + [iv setImageAlignment:NSImageAlignCenter]; + [iv setImageScaling:NSImageScaleProportionallyDown]; + [iv setAnimates:NO]; + [iv setEditable:NO]; + [iv addConstraint:mkConstraint(iv, NSLayoutAttributeWidth, + NSLayoutRelationEqual, + iv, NSLayoutAttributeHeight, + 1, 0, + @"uiTable image squareness constraint")]; + [iv setTag:self.imageColumn]; + view = iv; + break; + case partButton: + // TODO buttons get clipped + data = (*(m->mh->CellValue))(m->mh, m, row, self.textColumn); + str = toNSString((char *) data); + b = [[NSButton alloc] initWithFrame:NSZeroRect]; + [b setTitle:str]; + [b setButtonType:NSMomentaryPushInButton]; + [b setBordered:YES]; + [b setBezelStyle:NSRoundRectBezelStyle]; + uiDarwinSetControlFont(b, NSRegularControlSize); + if (self.editable) { + [b setTarget:m->m]; + [b setAction:@selector(onAction:)]; + } else + [b setEnabled:NO]; + [b setTag:self.textColumn]; + view = b; + break; + case partCheckbox: + data = (*(m->mh->CellValue))(m->mh, m, row, self.valueColumn); + b = [[NSButton alloc] initWithFrame:NSZeroRect]; + [b setTitle:@""]; + [b setButtonType:NSSwitchButton]; + // doesn't seem to have an associated bezel style + [b setBordered:NO]; + [b setTransparent:NO]; + uiDarwinSetControlFont(b, NSRegularControlSize); + if (uiTableModelTakeInt(data) != 0) + [b setState:NSOnState]; + else + [b setState:NSOffState]; + if (self.editable) { + [b setTarget:m->m]; + [b setAction:@selector(onAction:)]; + } else + [b setEnabled:NO]; + [b setTag:self.valueColumn]; + view = b; + break; + case partProgressBar: + data = (*(m->mh->CellValue))(m->mh, m, row, self.valueColumn); + value = uiTableModelTakeInt(data); + // TODO no intrinsic width + p = [[NSProgressIndicator alloc] initWithFrame:NSZeroRect]; + [p setControlSize:NSRegularControlSize]; + [p setBezeled:YES]; + [p setStyle:NSProgressIndicatorBarStyle]; + if (value == -1) { + [p setIndeterminate:YES]; + [p startAnimation:p]; + } else if (value == 100) { + [p setIndeterminate:NO]; + [p setMaxValue:101]; + [p setDoubleValue:101]; + [p setDoubleValue:100]; + [p setMaxValue:100]; + } else { + [p setIndeterminate:NO]; + [p setDoubleValue:(value + 1)]; + [p setDoubleValue:value]; + } + view = p; + break; + } + + // if stretchy, don't hug, otherwise hug forcibly + if (self.expand) + [view setContentHuggingPriority:NSLayoutPriorityDefaultLow forOrientation:NSLayoutConstraintOrientationHorizontal]; + else + [view setContentHuggingPriority:NSLayoutPriorityRequired forOrientation:NSLayoutConstraintOrientationHorizontal]; + [view setTranslatesAutoresizingMaskIntoConstraints:NO]; + // TODO autorelease? + return view; +} + +@end + +@implementation tableColumn +@end + +@implementation tableView +@end + +void *uiTableModelStrdup(const char *str) +{ + // TODO don't we have this already? + char *dup; + + dup = (char *) uiAlloc((strlen(str) + 1) * sizeof (char), "char[]"); + strcpy(dup, str); + return dup; +} + +uiTableModel *uiNewTableModel(uiTableModelHandler *mh) +{ + uiTableModel *m; + + m = uiNew(uiTableModel); + m->mh = mh; + m->m = [[tableModel alloc] initWithModel:m]; + m->tables = [NSMutableArray new]; + return m; +} + +void *uiTableModelGiveColor(double r, double g, double b, double a) +{ + return [[NSColor colorWithSRGBRed:r green:g blue:b alpha:a] retain]; +} + +void uiFreeTableModel(uiTableModel *m) +{ + if ([m->tables count] != 0) + userbug("You cannot free a uiTableModel while uiTables are using it."); + [m->tables release]; + [m->m release]; + uiFree(m); +} + +void uiTableModelRowInserted(uiTableModel *m, int newIndex) +{ + NSTableView *tv; + NSIndexSet *set; + + set = [NSIndexSet indexSetWithIndex:newIndex]; + for (tv in m->tables) + [tv insertRowsAtIndexes:set withAnimation:NSTableViewAnimationEffectNone]; + // set is autoreleased +} + +void uiTableModelRowChanged(uiTableModel *m, int index) +{ + NSTableView *tv; + NSIndexSet *set, *cols; + + set = [NSIndexSet indexSetWithIndex:index]; + for (tv in m->tables) { + cols = [[NSIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, [[tv tableColumns] count])]; + [tv reloadDataForRowIndexes:set columnIndexes:cols]; + // TODO this isn't enough + [cols release]; + } + // set is autoreleased +} + +void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) +{ + NSTableView *tv; + NSIndexSet *set; + + set = [NSIndexSet indexSetWithIndex:oldIndex]; + for (tv in m->tables) + [tv removeRowsAtIndexes:set withAnimation:NSTableViewAnimationEffectNone]; + // set is autoreleased +} + +void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand) +{ + tablePart *part; + + part = [tablePart new]; + part.type = partText; + part.textColumn = modelColumn; + part.expand = expand; + [c->parts addObject:part]; +} + +void uiTableColumnAppendImagePart(uiTableColumn *c, int modelColumn, int expand) +{ + tablePart *part; + + part = [tablePart new]; + part.type = partImage; + part.imageColumn = modelColumn; + part.expand = expand; + [c->parts addObject:part]; +} + +void uiTableColumnAppendButtonPart(uiTableColumn *c, int modelColumn, int expand) +{ + tablePart *part; + + part = [tablePart new]; + part.type = partButton; + part.textColumn = modelColumn; + part.expand = expand; + part.editable = 1; // editable by default + [c->parts addObject:part]; +} + +void uiTableColumnAppendCheckboxPart(uiTableColumn *c, int modelColumn, int expand) +{ + tablePart *part; + + part = [tablePart new]; + part.type = partCheckbox; + part.valueColumn = modelColumn; + part.expand = expand; + part.editable = 1; // editable by default + [c->parts addObject:part]; +} + +void uiTableColumnAppendProgressBarPart(uiTableColumn *c, int modelColumn, int expand) +{ + tablePart *part; + + part = [tablePart new]; + part.type = partProgressBar; + part.valueColumn = modelColumn; + part.expand = expand; + [c->parts addObject:part]; +} + +void uiTableColumnPartSetEditable(uiTableColumn *c, int part, int editable) +{ + tablePart *p; + + p = (tablePart *) [c->parts objectAtIndex:part]; + p.editable = editable; +} + +void uiTableColumnPartSetTextColor(uiTableColumn *c, int part, int modelColumn) +{ + tablePart *p; + + p = (tablePart *) [c->parts objectAtIndex:part]; + p.textColorColumn = modelColumn; +} + +uiDarwinControlAllDefaultsExceptDestroy(uiTable, sv) + +static void uiTableDestroy(uiControl *c) +{ + uiTable *t = uiTable(c); + + // TODO + [t->sv release]; + uiFreeControl(uiControl(t)); +} + +uiTableColumn *uiTableAppendColumn(uiTable *t, const char *name) +{ + uiTableColumn *c; + + c = uiNew(uiTableColumn); + c->c = [[tableColumn alloc] initWithIdentifier:@""]; + c->c.libui_col = c; + // via Interface Builder + [c->c setResizingMask:(NSTableColumnAutoresizingMask | NSTableColumnUserResizingMask)]; + // 10.10 adds -[NSTableColumn setTitle:]; before then we have to do this + [[c->c headerCell] setStringValue:toNSString(name)]; + // TODO is this sufficient? + [[c->c headerCell] setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]]; + c->parts = [NSMutableArray new]; + [t->tv addTableColumn:c->c]; + return c; +} + +void uiTableSetRowBackgroundColorModelColumn(uiTable *t, int modelColumn) +{ + t->backgroundColumn = modelColumn; +} + +uiTable *uiNewTable(uiTableModel *model) +{ + uiTable *t; + struct scrollViewCreateParams p; + + uiDarwinNewControl(uiTable, t); + + t->tv = [[tableView alloc] initWithFrame:NSZeroRect]; + t->tv.libui_t = t; + + [t->tv setDataSource:model->m]; + [t->tv setDelegate:model->m]; + [t->tv reloadData]; + [model->tables addObject:t->tv]; + + // TODO is this sufficient? + [t->tv setAllowsColumnReordering:NO]; + [t->tv setAllowsColumnResizing:YES]; + [t->tv setAllowsMultipleSelection:NO]; + [t->tv setAllowsEmptySelection:YES]; + [t->tv setAllowsColumnSelection:NO]; + [t->tv setUsesAlternatingRowBackgroundColors:YES]; + [t->tv setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleRegular]; + [t->tv setGridStyleMask:NSTableViewGridNone]; + [t->tv setAllowsTypeSelect:YES]; + // TODO floatsGroupRows — do we even allow group rows? + + memset(&p, 0, sizeof (struct scrollViewCreateParams)); + p.DocumentView = t->tv; + // this is what Interface Builder sets it to + // TODO verify + p.BackgroundColor = [NSColor colorWithCalibratedWhite:1.0 alpha:1.0]; + p.DrawsBackground = YES; + p.Bordered = YES; + p.HScroll = YES; + p.VScroll = YES; + t->sv = mkScrollView(&p, &(t->d)); + + t->backgroundColumn = -1; + + return t; +} diff --git a/test/images.c b/test/images.c new file mode 100644 index 00000000..df21b6eb --- /dev/null +++ b/test/images.c @@ -0,0 +1,202 @@ +// auto-generated by images/gen.go +#include "test.h" + +static const uint32_t dat0[] = { + 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, 0xFF8AC3FF, 0xFF8AC3FF, 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF89C57C, + 0xFF89C57C, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF89C57C, + 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, +}; + +static const uint32_t dat1[] = { + 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, + 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, + 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, + 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, + 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, + 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, + 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFF89C57C, + 0xFF89C57C, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF89C57C, + 0xFF89C57C, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, + 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF89C57C, + 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, + 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, +}; + +static const uint32_t dat2[] = { + 0xAC676767, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0x562B2B2B, 0x00000000, 0x00000000, + 0xFF818181, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, + 0xFF818181, 0xFFFFFFFF, 0xFFB7B7B7, 0xFFB7B7B7, 0xFFB7B7B8, 0xFF9E9E9F, 0xFFB8B8B8, 0xFFB8B8B8, 0xFF9F9F9F, 0xFFB8B8B9, 0xFFB8B8B9, 0xFF9F9F9F, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, + 0xFF818181, 0xFFFFFFFF, 0xFF9E9E9E, 0xFF9F9F9E, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, + 0xFF818181, 0xFFFFFFFF, 0xFFB7B8B7, 0xFFC3C3C3, 0xFFC3C3C3, 0xFF9F9F9F, 0xFFEEEEEE, 0xFFEEEEEE, 0xFFC2C2C2, 0xFFEEEFEE, 0xFFEFEEEF, 0xFFC2C2C2, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, + 0xFF818181, 0xFFFFFFFF, 0xFFB1B1B1, 0xFFB8B8B8, 0xFFB9B8B9, 0xFF9F9F9F, 0xFFE0E0E0, 0xFFE0E1E0, 0xFFC2C2C2, 0xFFE1E0E0, 0xFFE1E1E1, 0xFFC2C2C2, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, + 0xFF818181, 0xFFFFFFFF, 0xFFB9B8B8, 0xFFC4C4C4, 0xFFC3C4C4, 0xFF9F9F9F, 0xFFEEEEEF, 0xFFEFEEEF, 0xFFC2C2C2, 0xFFEFEFEF, 0xFFF0EFEF, 0xFFC2C2C2, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, + 0xFF818181, 0xFFFFFFFF, 0xFFB1B1B1, 0xFFB9B9B8, 0xFFB9B9B9, 0xFF9F9F9F, 0xFFC9A6A5, 0xFFAE3A36, 0xFFA50F0C, 0xFFA40201, 0xFFA40201, 0xFFA40D0A, 0xFFB03B37, 0xFF8D6969, 0x00000000, 0x00000000, + 0xFF818181, 0xFFFFFFFF, 0xFFB9B9B9, 0xFFC4C4C4, 0xFFC4C4C4, 0xFF9E403D, 0xFFA70A07, 0xFFC66453, 0xFFCB715C, 0xFFC9735D, 0xFFC5715B, 0xFFBF6C59, 0xFFC06E61, 0xFFAC201D, 0xC17E2927, 0x19191919, + 0xFF818181, 0xFFFFFFFF, 0xFFB2B2B2, 0xFFB9B9B9, 0xFFA65C5B, 0xFFAF2017, 0xFFCB725D, 0xFFC7654D, 0xFFBB5338, 0xFFB65136, 0xFFB04E35, 0xFFB35D47, 0xFFAE5B45, 0xFFA95743, 0xFFAA2F28, 0xA1641716, + 0xFF818181, 0xFFFFFFFF, 0xFFB9B9B9, 0xFFC4C4C4, 0xFFA51412, 0xFFCA6F5A, 0xFFBF5439, 0xFFBA5237, 0xFFB44F36, 0xFFAF4D34, 0xFF886556, 0xFF765F5A, 0xFF715B56, 0xFF6B5853, 0xFF77605B, 0xF7980C0B, + 0xFF818181, 0xFFFFFFFF, 0xFFB2B2B2, 0xFFB9B9B9, 0xFFA40201, 0xFFC76951, 0xFFB85137, 0xFFB24E36, 0xFFAD4C34, 0xFFAE653A, 0xFF95995D, 0xFF1D84A0, 0xFF177F99, 0xFF3D91A5, 0xFF3B8EA0, 0xFFA20101, + 0xFF818181, 0xFFFFFFFF, 0xFFB9B9B9, 0xFFC4C4C4, 0xFF9F1212, 0xFFCA7E6D, 0xFFB85F48, 0xFFAB4C33, 0xFFA64932, 0xFFB18B44, 0xFFB8BC50, 0xFF358086, 0xFF509CAD, 0xFF278397, 0xFF496E77, 0xF7920808, + 0xFF818181, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFBC7878, 0xFF9C1D1A, 0xFFBD7E6D, 0xFFBE7F70, 0xFFBD8374, 0xFFCDC787, 0xFFCBD089, 0xFFB4B48A, 0xFF5C93A0, 0xFF44656D, 0xFF7B1618, 0xA1661D1D, + 0xB4696969, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF934040, 0xFF9E0605, 0xFF99463D, 0xFF9E624D, 0xFFA6A14C, 0xFF9F9B4A, 0xFF948845, 0xFF4B3C43, 0xFF9A0505, 0xBA771F1F, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x55351616, 0xD37F1717, 0xF99C0B0B, 0xFFA30201, 0xFFA20101, 0xF99C0B0B, 0xD3881E1E, 0x55412222, 0x00000000, 0x00000000, +}; + +static const uint32_t dat3[] = { + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x52303030, 0xF2919191, 0xFF999999, 0xFF9C9C9C, 0xFF9E9E9E, 0xFF9C9C9C, 0xFF999999, 0xFF969696, 0xFF939393, 0xFF8F8F8F, 0xFF8C8C8C, 0xFF888888, + 0xFF848484, 0xFF818181, 0xFF7D7D7D, 0xFF7A7A7A, 0xFF767676, 0xFF727272, 0xFF6F6F6F, 0xFF6B6B6B, 0xFF686868, 0xFF646464, 0xF25F5F5F, 0x521E1E1E, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xEF8D8D8D, 0xFFFDFDFD, 0xFFFDFDFD, 0xFFFEFEFE, 0xFFFEFEFE, 0xFFFEFEFE, 0xFFFDFDFD, 0xFFFDFDFD, 0xFFFDFDFD, 0xFFFDFDFD, 0xFFFCFCFC, 0xFFFCFCFC, + 0xFFFCFCFC, 0xFFFBFBFB, 0xFFFBFBFB, 0xFFFBFBFB, 0xFFFAFAFA, 0xFFFAFAFA, 0xFFFAFAFA, 0xFFF9F9F9, 0xFFF9F9F9, 0xFFF9F9F9, 0xFFF8F8F8, 0xEF5D5D5D, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF939393, 0xFFFDFDFD, 0xFFDCDCDC, 0xFFDDDDDD, 0xFFDEDEDE, 0xFFDEDEDE, 0xFFDFDFDF, 0xFFDFDFDF, 0xFFE0E0E0, 0xFFE1E1E1, 0xFFE1E1E1, 0xFFE1E1E1, + 0xFFE2E2E2, 0xFFE2E2E2, 0xFFE2E2E2, 0xFFE2E2E2, 0xFFE3E3E3, 0xFFE3E3E3, 0xFFE3E3E3, 0xFFE3E3E3, 0xFFE3E3E3, 0xFFE2E2E2, 0xFFF8F8F8, 0xFF5D5D5D, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF929292, 0xFFFEFEFE, 0xFFDDDDDD, 0xFF787878, 0xFF8D8D8D, 0xFF8E8E8E, 0xFF8F8F8F, 0xFF8F8F8F, 0xFF787878, 0xFF8F8F8F, 0xFF909090, 0xFF909090, + 0xFF7A7A7A, 0xFF919191, 0xFF919191, 0xFF919191, 0xFF7B7B7B, 0xFF919191, 0xFF919191, 0xFF919191, 0xFF7D7D7D, 0xFFE3E3E3, 0xFFF8F8F8, 0xFF5D5D5D, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF909090, 0xFFFEFEFE, 0xFFDEDEDE, 0xFF949494, 0xFFB0B0B0, 0xFFB1B1B1, 0xFFB1B1B1, 0xFFB1B1B1, 0xFF969696, 0xFFB2B2B2, 0xFFB3B3B3, 0xFFB3B3B3, + 0xFF989898, 0xFFB4B4B4, 0xFFB4B4B4, 0xFFB5B5B5, 0xFF999999, 0xFFB5B5B5, 0xFFB5B5B5, 0xFFB5B5B5, 0xFF999999, 0xFFE4E4E4, 0xFFF8F8F8, 0xFF5C5C5C, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF8E8E8E, 0xFFFDFDFD, 0xFFDFDFDF, 0xFF949494, 0xFFB1B1B1, 0xFFB1B1B1, 0xFFB2B2B2, 0xFFB2B2B2, 0xFF979797, 0xFFB3B3B3, 0xFFB4B4B4, 0xFFB4B4B4, + 0xFF999999, 0xFFB5B5B5, 0xFFB5B5B5, 0xFFB5B5B5, 0xFF999999, 0xFFB5B5B5, 0xFFB5B5B5, 0xFFB5B5B5, 0xFF999999, 0xFFE5E5E5, 0xFFF8F8F8, 0xFF5C5C5C, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF8B8B8B, 0xFFFDFDFD, 0xFFDFDFDF, 0xFF7A7A7A, 0xFF919191, 0xFF929292, 0xFF929292, 0xFF939393, 0xFF7D7D7D, 0xFF949494, 0xFF949494, 0xFF949494, + 0xFF7D7D7D, 0xFF949494, 0xFF949494, 0xFF959595, 0xFF7E7E7E, 0xFF959595, 0xFF959595, 0xFF959595, 0xFF7E7E7E, 0xFFE6E6E6, 0xFFF8F8F8, 0xFF5B5B5B, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF898989, 0xFFFDFDFD, 0xFFE0E0E0, 0xFF949494, 0xFFB0B0B0, 0xFFB0B0B0, 0xFFB1B1B1, 0xFFB2B2B2, 0xFF979797, 0xFFE2E2E2, 0xFFE3E3E3, 0xFFE3E3E3, + 0xFFC0C0C0, 0xFFE4E4E4, 0xFFE4E4E4, 0xFFE5E5E5, 0xFFC1C1C1, 0xFFE5E5E5, 0xFFE5E5E5, 0xFFE5E5E5, 0xFFC1C1C1, 0xFFE8E8E8, 0xFFF8F8F8, 0xFF5A5A5A, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF868686, 0xFFFDFDFD, 0xFFE1E1E1, 0xFF7B7B7B, 0xFF929292, 0xFF939393, 0xFF949494, 0xFF949494, 0xFF7D7D7D, 0xFFBDBDBD, 0xFFBDBDBD, 0xFFBDBDBD, + 0xFFA0A0A0, 0xFFBEBEBE, 0xFFBEBEBE, 0xFFBFBFBF, 0xFFA1A1A1, 0xFFBFBFBF, 0xFFBFBFBF, 0xFFBFBFBF, 0xFFA1A1A1, 0xFFE9E9E9, 0xFFF8F8F8, 0xFF595959, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF838383, 0xFFFDFDFD, 0xFFE1E1E1, 0xFF949494, 0xFFB1B1B1, 0xFFB2B2B2, 0xFFB3B3B3, 0xFFB3B3B3, 0xFF979797, 0xFFE4E4E4, 0xFFE5E5E5, 0xFFE5E5E5, + 0xFFC2C2C2, 0xFFE6E6E6, 0xFFE6E6E6, 0xFFE7E7E7, 0xFFC3C3C3, 0xFFE7E7E7, 0xFFE7E7E7, 0xFFE7E7E7, 0xFFC3C3C3, 0xFFEAEAEA, 0xFFF8F8F8, 0xFF585858, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF808080, 0xFFFCFCFC, 0xFFE2E2E2, 0xFF7C7C7C, 0xFF949494, 0xFF949494, 0xFF949494, 0xFF949494, 0xFF7E7E7E, 0xFFBEBEBE, 0xFFBEBEBE, 0xFFBFBFBF, + 0xFFA2A2A2, 0xFFC0C0C0, 0xFFC0C0C0, 0xFFC1C1C1, 0xFFA3A3A3, 0xFFC1C1C1, 0xFFC1C1C1, 0xFFC1C1C1, 0xFFA3A3A3, 0xFFEBEBEB, 0xFFF8F8F8, 0xFF575757, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF7D7D7D, 0xFFFCFCFC, 0xFFE3E3E3, 0xFF969696, 0xFFB3B3B3, 0xFFB3B3B3, 0xFFB3B3B3, 0xFFB4B4B4, 0xFF999999, 0xFFE6E6E6, 0xFFE6E6E6, 0xFFE7E7E7, + 0xFFC4C4C4, 0xFFE8E8E8, 0xFFE8E8E8, 0xFFE9E9E9, 0xFFC4C4C4, 0xFFE9E9E9, 0xFFE9E9E9, 0xFFE9E9E9, 0xFFC4C4C4, 0xFFECECEC, 0xFFF8F8F8, 0xFF555555, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF7A7A7A, 0xFFFCFCFC, 0xFFE3E3E3, 0xFF7D7D7D, 0xFF949494, 0xFF949494, 0xFF959595, 0xFF969696, 0xFF7F7F7F, 0xFFBFBFBF, 0xFFC0C0C0, 0xFFC1C1C1, + 0xFFA3A3A3, 0xFFC1C1C1, 0xFFC1C1C1, 0xFFC2C2C2, 0xFFA4A4A4, 0xFFC2C2C2, 0xFFC2C2C2, 0xFFC2C2C2, 0xFFA4A4A4, 0xFFEDEDED, 0xFFF8F8F8, 0xFF545454, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF777777, 0xFFFBFBFB, 0xFFE4E4E4, 0xFF979797, 0xFFB3B3B3, 0xFFB4B4B4, 0xFFB5B5B5, 0xFFB6B6B6, 0xFF999999, 0xFFE7E7E7, 0xFFE8E8E8, 0xFFE9E9E9, + 0xFFC4C4C4, 0xFFEAEAEA, 0xFFEAEAEA, 0xFFEBEBEB, 0xFFC6C6C6, 0xFFEBEBEB, 0xFFEBEBEB, 0xFFEBEBEB, 0xFFC6C6C6, 0xFFEEEEEE, 0xFFF8F8F8, 0xFF525252, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF737373, 0xFFFBFBFB, 0xFFE4E4E4, 0xFF7D7D7D, 0xFF949494, 0xFF959595, 0xFF969696, 0xFF979797, 0xFF7F7F7F, 0xFFC1C1C1, 0xFFC1C1C1, 0xFFC2C2C2, + 0xFFA3A2A2, 0xFFAB9191, 0xFF946060, 0xFF843D3D, 0xFF782525, 0xFF802727, 0xFF7E2B2B, 0xFF843D3D, 0xFF865252, 0xFFCCB2B2, 0xFFF6F5F5, 0xFF505050, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF707070, 0xFFFBFBFB, 0xFFE5E5E5, 0xFF979797, 0xFFB4B4B4, 0xFFB5B5B5, 0xFFB6B6B6, 0xFFB6B6B6, 0xFF9A9A9A, 0xFFE9E9E9, 0xFFE6E4E4, 0xFFB18585, + 0xFF7A1B1B, 0xFFA62F2F, 0xFFD35050, 0xFFF26767, 0xFFFF7070, 0xFFFE6E6E, 0xFFFC6969, 0xFFED5B5B, 0xFFCD4343, 0xFFA22525, 0xFF7E1D1D, 0xFF582C2C, 0x06020000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF6D6D6D, 0xFFFBFBFB, 0xFFE5E5E5, 0xFF7D7D7D, 0xFF959595, 0xFF969696, 0xFF979797, 0xFF979797, 0xFF808080, 0xFFB7ABAB, 0xFF7B2828, 0xFFAC3434, + 0xFFF56A6A, 0xFFFF7070, 0xFFFF7070, 0xFFFE6F6F, 0xFFFC6A6A, 0xFFFA6565, 0xFFF86060, 0xFFF55C5C, 0xFFF35757, 0xFFF15252, 0xFFE64848, 0xFFA11F1F, 0xCB530000, 0x1D0C0000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF6A6A6A, 0xFFFAFAFA, 0xFFE6E6E6, 0xFF989898, 0xFFB5B5B5, 0xFFB6B6B6, 0xFFB6B6B6, 0xFFB7B7B7, 0xFF989292, 0xFF7E2626, 0xFFCB5050, 0xFFFF7474, + 0xFFFF7070, 0xFFFF7070, 0xFFFD6B6B, 0xFFFA6767, 0xFFF86262, 0xFFF65D5D, 0xFFF45858, 0xFFF15353, 0xFFEF4E4E, 0xFFED4949, 0xFFEB4444, 0xFFE93F3F, 0xFFB62424, 0xD7570000, 0x0F060000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF666666, 0xFFFAFAFA, 0xFFE6E6E6, 0xFF7E7E7E, 0xFF969696, 0xFF979797, 0xFF979797, 0xFF989898, 0xFF733D3D, 0xFFA63737, 0xFFFF7979, 0xFFFF7272, + 0xFFFD6D6D, 0xFFFB6868, 0xFFF96363, 0xFFF65E5E, 0xFFF45959, 0xFFF25454, 0xFFF04F4F, 0xFFEE4A4A, 0xFFEB4545, 0xFFE94141, 0xFFE73C3C, 0xFFE53737, 0xFFE23535, 0xFF911313, 0x86360000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF636363, 0xFFFAFAFA, 0xFFE6E6E6, 0xFF989898, 0xFFB5B5B5, 0xFFB6B6B6, 0xFFB7B7B7, 0xFFB8B8B8, 0xFF6E1515, 0xFFDF6D6D, 0xFFFE7575, 0xFFFB6969, + 0xFFF96464, 0xFFF75F5F, 0xFFF55A5A, 0xFFF35555, 0xFFF05050, 0xFFEE4C4C, 0xFFEC4747, 0xFFEA4242, 0xFFE73D3D, 0xFFE53838, 0xFFE33333, 0xFFE12E2E, 0xFFDF2D2D, 0xFFC22828, 0xDE590000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF606060, 0xFFF9F9F9, 0xFFE6E6E6, 0xFF7E7E7E, 0xFF979797, 0xFF979797, 0xFF989898, 0xFF989898, 0xFF670101, 0xFFED7B7B, 0xFFFA6A6A, 0xFFF86060, + 0xFFF55B5B, 0xFFF35656, 0xFFF15252, 0xFFEF4D4D, 0xFFEC4848, 0xFFCD8478, 0xFF777CAE, 0xFF777BAD, 0xFF7275A7, 0xFF6B6FA1, 0xFF67699B, 0xFF606396, 0xFF5E6091, 0xFF7E5E82, 0xFC660000, 0x02000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF5D5D5D, 0xFFF9F9F9, 0xFFE6E6E6, 0xFF999999, 0xFFB6B6B6, 0xFFB6B6B6, 0xFFB7B7B7, 0xFFB8B8B8, 0xFF670000, 0xFFD86262, 0xFFF76D6D, 0xFFF45858, + 0xFFF15353, 0xFFEF4E4E, 0xFFED4949, 0xFFEB4444, 0xFFE84341, 0xFFC5E18C, 0xFF8A939F, 0xFF5889C7, 0xFF5182C1, 0xFF4B7CBA, 0xFF4475B4, 0xFF3D6EAE, 0xFF4572AD, 0xFF6A6087, 0xFF670000, 0x0E000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF595959, 0xFFF9F9F9, 0xFFE7E7E7, 0xFF7E7E7E, 0xFF979797, 0xFF979797, 0xFF989898, 0xFF989898, 0xFF690D0D, 0xFFA32424, 0xFFEF7B7B, 0xFFF15A5A, + 0xFFEE4A4A, 0xFFEB4545, 0xFFE94040, 0xFFE73C3C, 0xFFD86B4D, 0xFFADE773, 0xFFABC35F, 0xFF5585C0, 0xFF4D7EBD, 0xFF4778B7, 0xFF4071B0, 0xFF4372AE, 0xFF6B7AA6, 0xFF544267, 0xE85C0000, 0x16000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF565656, 0xFFF8F8F8, 0xFFE7E7E7, 0xFF999999, 0xFFB6B6B6, 0xFFB6B6B6, 0xFFB7B7B7, 0xFFB8B8B8, 0xFF773939, 0xFF7A0909, 0xFFBB3232, 0xFFE87171, + 0xFFED6161, 0xFFE84242, 0xFFE53838, 0xFFE33333, 0xFFBA9F4E, 0xFF99E053, 0xFF8FDC43, 0xFF848A66, 0xFF497AB9, 0xFF4777B4, 0xFF5882B9, 0xFF797FA6, 0xFF455E90, 0xFF651723, 0xAC3E0000, 0x13000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF535353, 0xFFF8F8F8, 0xFFE6E6E6, 0xFF7E7E7E, 0xFF979797, 0xFF979797, 0xFF989898, 0xFF989898, 0xFF766B6B, 0xFF6D0E0E, 0xFF881111, 0xFFB02323, + 0xFFCA4545, 0xFFE26666, 0xFFE85D5D, 0xFFE24F4B, 0xFFA1D656, 0xFF91DC47, 0xFF8BD93C, 0xFF90C93A, 0xFF7494BB, 0xFF848EB4, 0xFF70688E, 0xFF3B5E92, 0xFF602942, 0xEF5F0000, 0x400B0000, 0x08000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF4F4F4F, 0xFFF8F8F8, 0xFFE6E6E6, 0xFFE5E5E5, 0xFFE6E6E6, 0xFFE7E7E7, 0xFFE8E8E8, 0xFFE9E9E9, 0xFFE6E6E6, 0xFFBDA9A9, 0xFF711515, 0xFF7B0C0C, + 0xFFA21D1D, 0xFFAD1B1B, 0xFFB62727, 0xFFB24F33, 0xFF93A640, 0xFF96A840, 0xFF91A73C, 0xFF869F33, 0xFF6E675B, 0xFF375C93, 0xFF544D75, 0xFF6B1823, 0xEB5C0000, 0x59130000, 0x16000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x0D000000, 0xF14C4C4C, 0xFFF7F7F7, 0xFFF6F6F6, 0xFFF6F6F6, 0xFFF6F6F6, 0xFFF7F7F7, 0xFFF7F7F7, 0xFFF7F7F7, 0xFFF7F7F7, 0xFFF0F0F0, 0xFFD1C9C9, 0xFF905353, + 0xFF6A0505, 0xFF770A0A, 0xFF8E1414, 0xFF91331C, 0xFF728723, 0xFF709024, 0xFF6E8F23, 0xFF737C1F, 0xFF703C31, 0xFF6C141B, 0xFF6A0505, 0xFB531616, 0x52080000, 0x1D000000, 0x01000000, 0x00000000, + 0x00000000, 0x00000000, 0x0D000000, 0x22000000, 0x78191919, 0xF6535353, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF555555, 0xFF4F4F4F, + 0xFF4B4343, 0xFF522828, 0xFF5A1616, 0xFF610A0A, 0xFF650404, 0xFF670000, 0xFF650404, 0xFF600909, 0xFF591515, 0xFF502626, 0xF9453C3C, 0x93151515, 0x3A000000, 0x1B000000, 0x02000000, 0x00000000, + 0x00000000, 0x00000000, 0x06000000, 0x1D000000, 0x30000000, 0x40000000, 0x47000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, + 0x4C000000, 0x54000000, 0x5E000000, 0x65000000, 0x69000000, 0x6B000000, 0x6B000000, 0x69000000, 0x63000000, 0x52000000, 0x4B000000, 0x3B000000, 0x29000000, 0x0C000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x07000000, 0x0F000000, 0x15000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, + 0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x17000000, 0x18000000, 0x17000000, 0x17000000, 0x16000000, 0x14000000, 0x0F000000, 0x08000000, 0x01000000, 0x00000000, 0x00000000, 0x00000000, +}; + +static const struct { + const char *name; + void *data; + int width; + int height; + int stride; +} files[] = { + { "andlabs_16x16test_24june2016.png", dat0, 16, 16, 64 }, + { "andlabs_32x32test_24june2016.png", dat1, 32, 32, 128 }, + { "tango-icon-theme-0.8.90_16x16_x-office-spreadsheet.png", dat2, 16, 16, 64 }, + { "tango-icon-theme-0.8.90_32x32_x-office-spreadsheet.png", dat3, 32, 32, 128 }, +}; + +void appendImageNamed(uiImage *img, const char *name) +{ + int i; + + i = 0; + for (;;) { + if (strcmp(name, files[i].name) == 0) { + uiImageAppend(img, files[i].data, files[i].width, files[i].height, files[i].stride); + return; + } + i++; + } +} + diff --git a/test/page16.c b/test/page16.c new file mode 100644 index 00000000..80ac0139 --- /dev/null +++ b/test/page16.c @@ -0,0 +1,143 @@ +// 21 june 2016 +#include "test.h" + +static uiTableModelHandler mh; + +static int modelNumColumns(uiTableModelHandler *mh, uiTableModel *m) +{ + return 9; +} + +static uiTableModelColumnType modelColumnType(uiTableModelHandler *mh, uiTableModel *m, int column) +{ + if (column == 3 || column == 4) + return uiTableModelColumnColor; + if (column == 5) + return uiTableModelColumnImage; + if (column == 7 || column == 8) + return uiTableModelColumnInt; + return uiTableModelColumnString; +} + +static int modelNumRows(uiTableModelHandler *mh, uiTableModel *m) +{ + return 15; +} + +static uiImage *img[2]; +static char row9text[1024]; +static int yellowRow = -1; +static int checkStates[15]; + +static void *modelCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, int col) +{ + char buf[256]; + + if (col == 3) { + if (row == yellowRow) + return uiTableModelGiveColor(1, 1, 0, 1); + if (row == 3) + return uiTableModelGiveColor(1, 0, 0, 1); + if (row == 11) + return uiTableModelGiveColor(0, 0.5, 1, 0.5); + return NULL; + } + if (col == 4) { + if ((row % 2) == 1) + return uiTableModelGiveColor(0.5, 0, 0.75, 1); + return NULL; + } + if (col == 5) { + if (row < 8) + return img[0]; + return img[1]; + } + if (col == 7) + return uiTableModelGiveInt(checkStates[row]); + if (col == 8) { + if (row == 0) + return uiTableModelGiveInt(0); + if (row == 13) + return uiTableModelGiveInt(100); + if (row == 14) + return uiTableModelGiveInt(-1); + return uiTableModelGiveInt(50); + } + switch (col) { + case 0: + sprintf(buf, "Row %d", row); + break; + case 2: + if (row == 9) + return uiTableModelStrdup(row9text); + // fall through + case 1: + strcpy(buf, "Part"); + break; + case 6: + strcpy(buf, "Make Yellow"); + break; + } + return uiTableModelStrdup(buf); +} + +static void modelSetCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, int col, const void *val) +{ + if (row == 9 && col == 2) + strcpy(row9text, (const char *) val); + if (col == 6) + yellowRow = row; + if (col == 7) + checkStates[row] = uiTableModelTakeInt(val); +} + +uiBox *makePage16(void) +{ + uiBox *page16; + uiTableModel *m; + uiTable *t; + uiTableColumn *tc; + + img[0] = uiNewImage(16, 16); + appendImageNamed(img[0], "andlabs_16x16test_24june2016.png"); + appendImageNamed(img[0], "andlabs_32x32test_24june2016.png"); + img[1] = uiNewImage(16, 16); + appendImageNamed(img[1], "tango-icon-theme-0.8.90_16x16_x-office-spreadsheet.png"); + appendImageNamed(img[1], "tango-icon-theme-0.8.90_32x32_x-office-spreadsheet.png"); + + strcpy(row9text, "Part"); + + memset(checkStates, 0, 15 * sizeof (int)); + + page16 = newVerticalBox(); + + mh.NumColumns = modelNumColumns; + mh.ColumnType = modelColumnType; + mh.NumRows = modelNumRows; + mh.CellValue = modelCellValue; + mh.SetCellValue = modelSetCellValue; + m = uiNewTableModel(&mh); + + t = uiNewTable(m); + uiBoxAppend(page16, uiControl(t), 1); + + uiTableAppendTextColumn(t, "Column 1", 0); + + tc = uiTableAppendColumn(t, "Column 2"); + uiTableColumnAppendImagePart(tc, 5, 0); + uiTableColumnAppendTextPart(tc, 1, 0); + uiTableColumnAppendTextPart(tc, 2, 1); + uiTableColumnPartSetTextColor(tc, 1, 4); + uiTableColumnPartSetEditable(tc, 2, 1); + + uiTableSetRowBackgroundColorModelColumn(t, 3); + + tc = uiTableAppendColumn(t, "Buttons"); + uiTableColumnAppendCheckboxPart(tc, 7, 0); + uiTableColumnAppendButtonPart(tc, 6, 1); + + tc = uiTableAppendColumn(t, "Progress Bar"); + uiTableColumnAppendProgressBarPart(tc, 8, 0); + + return page16; +} diff --git a/uitable.h b/uitable.h new file mode 100644 index 00000000..a54398c5 --- /dev/null +++ b/uitable.h @@ -0,0 +1,65 @@ +// 20 june 2016 +// kept in a separate file for now + +typedef struct uiImage uiImage; + +// TODO use const void * for const correctness +_UI_EXTERN uiImage *uiNewImage(double width, double height); +_UI_EXTERN void uiFreeImage(uiImage *i); +_UI_EXTERN void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int pixelStride); + +typedef struct uiTableModel uiTableModel; +typedef struct uiTableModelHandler uiTableModelHandler; + +// TODO actually validate these +_UI_ENUM(uiTableModelColumnType) { + uiTableModelColumnString, + uiTableModelColumnImage, + uiTableModelColumnInt, + uiTableModelColumnColor, +}; + +// TODO validate ranges; validate types on each getter/setter call (? table columns only?) +struct uiTableModelHandler { + int (*NumColumns)(uiTableModelHandler *, uiTableModel *); + uiTableModelColumnType (*ColumnType)(uiTableModelHandler *, uiTableModel *, int); + int (*NumRows)(uiTableModelHandler *, uiTableModel *); + void *(*CellValue)(uiTableModelHandler *, uiTableModel *, int, int); + void (*SetCellValue)(uiTableModelHandler *, uiTableModel *, int, int, const void *); +}; + +_UI_EXTERN void *uiTableModelStrdup(const char *str); +// TODO rename the strdup one to this too +_UI_EXTERN void *uiTableModelGiveColor(double r, double g, double b, double a); +_UI_EXTERN void *uiTableModelGiveInt(int i); +// TODO TakeString +// TODO add const +_UI_EXTERN int uiTableModelTakeInt(void *v); + +_UI_EXTERN uiTableModel *uiNewTableModel(uiTableModelHandler *mh); +_UI_EXTERN void uiFreeTableModel(uiTableModel *m); +_UI_EXTERN void uiTableModelRowInserted(uiTableModel *m, int newIndex); +_UI_EXTERN void uiTableModelRowChanged(uiTableModel *m, int index); +_UI_EXTERN void uiTableModelRowDeleted(uiTableModel *m, int oldIndex); +// TODO reordering/moving + +typedef struct uiTableColumn uiTableColumn; + +_UI_EXTERN void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand); +// TODO images shouldn't expand... +_UI_EXTERN void uiTableColumnAppendImagePart(uiTableColumn *c, int modelColumn, int expand); +_UI_EXTERN void uiTableColumnAppendButtonPart(uiTableColumn *c, int modelColumn, int expand); +// TODO should these have labels? +_UI_EXTERN void uiTableColumnAppendCheckboxPart(uiTableColumn *c, int modelColumn, int expand); +_UI_EXTERN void uiTableColumnAppendProgressBarPart(uiTableColumn *c, int modelColumn, int expand); +// TODO Editable? +_UI_EXTERN void uiTableColumnPartSetEditable(uiTableColumn *c, int part, int editable); +_UI_EXTERN void uiTableColumnPartSetTextColor(uiTableColumn *c, int part, int modelColumn); + +typedef struct uiTable uiTable; +#define uiTable(this) ((uiTable *) (this)) +_UI_EXTERN uiTableColumn *uiTableAppendColumn(uiTable *t, const char *name); +_UI_EXTERN uiTableColumn *uiTableAppendTextColumn(uiTable *t, const char *name, int modelColumn); +// TODO getter? +_UI_EXTERN void uiTableSetRowBackgroundColorModelColumn(uiTable *t, int modelColumn); +_UI_EXTERN uiTable *uiNewTable(uiTableModel *model); diff --git a/unix/table.c b/unix/table.c new file mode 100644 index 00000000..a11844c2 --- /dev/null +++ b/unix/table.c @@ -0,0 +1,642 @@ +// 26 june 2016 +#include "uipriv_unix.h" + +#define uiTableModelType (uiTableModel_get_type()) +#define uiTableModel(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), uiTableModelType, uiTableModel)) +#define isuiTableModel(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), uiTableModelType)) +#define uiTableModelClass(class) (G_TYPE_CHECK_CLASS_CAST((class), uiTableModelType, uiTableModelClass)) +#define isuiTableModelClass(class) (G_TYPE_CHECK_CLASS_TYPE((class), uiTableModel)) +#define getuiTableModelClass(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), uiTableModelType, uiTableModelClass)) + +typedef struct uiTableModelClass uiTableModelClass; + +struct uiTableModel { + GObject parent_instance; + uiTableModelHandler *mh; +}; + +struct uiTableModelClass { + GObjectClass parent_class; +}; + +static void uiTableModel_gtk_tree_model_interface_init(GtkTreeModelIface *iface); + +G_DEFINE_TYPE_WITH_CODE(uiTableModel, uiTableModel, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE(GTK_TYPE_TREE_MODEL, uiTableModel_gtk_tree_model_interface_init)) + +static void uiTableModel_init(uiTableModel *m) +{ + // nothing to do +} + +static void uiTableModel_dispose(GObject *obj) +{ + G_OBJECT_CLASS(uiTableModel_parent_class)->dispose(obj); +} + +static void uiTableModel_finalize(GObject *obj) +{ + G_OBJECT_CLASS(uiTableModel_parent_class)->finalize(obj); +} + +static GtkTreeModelFlags uiTableModel_get_flags(GtkTreeModel *mm) +{ + return GTK_TREE_MODEL_LIST_ONLY; +} + +static gint uiTableModel_get_n_columns(GtkTreeModel *mm) +{ + uiTableModel *m = uiTableModel(mm); + + return (*(m->mh->NumColumns))(m->mh, m); +} + +static GType uiTableModel_get_column_type(GtkTreeModel *mm, gint index) +{ + uiTableModel *m = uiTableModel(mm); + + switch ((*(m->mh->ColumnType))(m->mh, m, index)) { + case uiTableModelColumnString: + return G_TYPE_STRING; + case uiTableModelColumnImage: + return G_TYPE_POINTER; + case uiTableModelColumnInt: + return G_TYPE_INT; + case uiTableModelColumnColor: + return GDK_TYPE_RGBA; + } + // TODO + return G_TYPE_INVALID; +} + +#define STAMP_GOOD 0x1234 +#define STAMP_BAD 0x5678 + +static gboolean uiTableModel_get_iter(GtkTreeModel *mm, GtkTreeIter *iter, GtkTreePath *path) +{ + uiTableModel *m = uiTableModel(mm); + gint row; + + if (gtk_tree_path_get_depth(path) != 1) + goto bad; + row = gtk_tree_path_get_indices(path)[0]; + if (row < 0) + goto bad; + if (row >= (*(m->mh->NumRows))(m->mh, m)) + goto bad; + iter->stamp = STAMP_GOOD; + iter->user_data = GINT_TO_POINTER(row); + return TRUE; +bad: + iter->stamp = STAMP_BAD; + return FALSE; +} + +// GtkListStore returns NULL on error; let's do that too +static GtkTreePath *uiTableModel_get_path(GtkTreeModel *mm, GtkTreeIter *iter) +{ + gint row; + + if (iter->stamp != STAMP_GOOD) + return NULL; + row = GPOINTER_TO_INT(iter->user_data); + return gtk_tree_path_new_from_indices(row, -1); +} + +// GtkListStore leaves value empty on failure; let's do the same +static void uiTableModel_get_value(GtkTreeModel *mm, GtkTreeIter *iter, gint column, GValue *value) +{ + uiTableModel *m = uiTableModel(mm); + gint row; + void *data; + + if (iter->stamp != STAMP_GOOD) + return; + row = GPOINTER_TO_INT(iter->user_data); + data = (*(m->mh->CellValue))(m->mh, m, row, column); + switch ((*(m->mh->ColumnType))(m->mh, m, column)) { + case uiTableModelColumnString: + g_value_init(value, G_TYPE_STRING); + g_value_take_string(value, (char *) data); + return; + case uiTableModelColumnImage: + g_value_init(value, G_TYPE_POINTER); + g_value_set_pointer(value, data); + return; + case uiTableModelColumnInt: + g_value_init(value, G_TYPE_INT); + g_value_set_int(value, uiTableModelTakeInt(data)); + return; + case uiTableModelColumnColor: + g_value_init(value, GDK_TYPE_RGBA); + g_value_take_boxed(value, data); + return; + } + // TODO +} + +static gboolean uiTableModel_iter_next(GtkTreeModel *mm, GtkTreeIter *iter) +{ + uiTableModel *m = uiTableModel(mm); + gint row; + + if (iter->stamp != STAMP_GOOD) + return FALSE; + row = GPOINTER_TO_INT(iter->user_data); + row++; + if (row >= (*(m->mh->NumRows))(m->mh, m)) { + iter->stamp = STAMP_BAD; + return FALSE; + } + iter->user_data = GINT_TO_POINTER(row); + return TRUE; +} + +static gboolean uiTableModel_iter_previous(GtkTreeModel *mm, GtkTreeIter *iter) +{ + gint row; + + if (iter->stamp != STAMP_GOOD) + return FALSE; + row = GPOINTER_TO_INT(iter->user_data); + row--; + if (row < 0) { + iter->stamp = STAMP_BAD; + return FALSE; + } + iter->user_data = GINT_TO_POINTER(row); + return TRUE; +} + +static gboolean uiTableModel_iter_children(GtkTreeModel *mm, GtkTreeIter *iter, GtkTreeIter *parent) +{ + return gtk_tree_model_iter_nth_child(mm, iter, parent, 0); +} + +static gboolean uiTableModel_iter_has_child(GtkTreeModel *mm, GtkTreeIter *iter) +{ + return FALSE; +} + +static gint uiTableModel_iter_n_children(GtkTreeModel *mm, GtkTreeIter *iter) +{ + uiTableModel *m = uiTableModel(mm); + + if (iter != NULL) + return 0; + return (*(m->mh->NumRows))(m->mh, m); +} + +static gboolean uiTableModel_iter_nth_child(GtkTreeModel *mm, GtkTreeIter *iter, GtkTreeIter *parent, gint n) +{ + uiTableModel *m = uiTableModel(mm); + + if (iter->stamp != STAMP_GOOD) + return FALSE; + if (parent != NULL) + goto bad; + if (n < 0) + goto bad; + if (n >= (*(m->mh->NumRows))(m->mh, m)) + goto bad; + iter->stamp = STAMP_GOOD; + iter->user_data = GINT_TO_POINTER(n); + return TRUE; +bad: + iter->stamp = STAMP_BAD; + return FALSE; +} + +gboolean uiTableModel_iter_parent(GtkTreeModel *mm, GtkTreeIter *iter, GtkTreeIter *child) +{ + iter->stamp = STAMP_BAD; + return FALSE; +} + +static void uiTableModel_class_init(uiTableModelClass *class) +{ + G_OBJECT_CLASS(class)->dispose = uiTableModel_dispose; + G_OBJECT_CLASS(class)->finalize = uiTableModel_finalize; +} + +static void uiTableModel_gtk_tree_model_interface_init(GtkTreeModelIface *iface) +{ + iface->get_flags = uiTableModel_get_flags; + iface->get_n_columns = uiTableModel_get_n_columns; + iface->get_column_type = uiTableModel_get_column_type; + iface->get_iter = uiTableModel_get_iter; + iface->get_path = uiTableModel_get_path; + iface->get_value = uiTableModel_get_value; + iface->iter_next = uiTableModel_iter_next; + iface->iter_previous = uiTableModel_iter_previous; + iface->iter_children = uiTableModel_iter_children; + iface->iter_has_child = uiTableModel_iter_has_child; + iface->iter_n_children = uiTableModel_iter_n_children; + iface->iter_nth_child = uiTableModel_iter_nth_child; + iface->iter_parent = uiTableModel_iter_parent; + // don't specify ref_node() or unref_node() +} + +void *uiTableModelStrdup(const char *str) +{ + return g_strdup(str); +} + +void *uiTableModelGiveColor(double r, double g, double b, double a) +{ + GdkRGBA rgba; + + rgba.red = r; + rgba.green = g; + rgba.blue = b; + rgba.alpha = a; + return gdk_rgba_copy(&rgba); +} + +uiTableModel *uiNewTableModel(uiTableModelHandler *mh) +{ + uiTableModel *m; + + m = uiTableModel(g_object_new(uiTableModelType, NULL)); + m->mh = mh; + return m; +} + +void uiFreeTableModel(uiTableModel *m) +{ + g_object_unref(m); +} + +void uiTableModelRowInserted(uiTableModel *m, int newIndex) +{ + GtkTreePath *path; + GtkTreeIter iter; + + path = gtk_tree_path_new_from_indices(newIndex, -1); + iter.stamp = STAMP_GOOD; + iter.user_data = GINT_TO_POINTER(newIndex); + gtk_tree_model_row_inserted(GTK_TREE_MODEL(m), path, &iter); + gtk_tree_path_free(path); +} + +void uiTableModelRowChanged(uiTableModel *m, int index) +{ + GtkTreePath *path; + GtkTreeIter iter; + + path = gtk_tree_path_new_from_indices(index, -1); + iter.stamp = STAMP_GOOD; + iter.user_data = GINT_TO_POINTER(index); + gtk_tree_model_row_changed(GTK_TREE_MODEL(m), path, &iter); + gtk_tree_path_free(path); +} + +void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) +{ + GtkTreePath *path; + + path = gtk_tree_path_new_from_indices(oldIndex, -1); + gtk_tree_model_row_deleted(GTK_TREE_MODEL(m), path); + gtk_tree_path_free(path); +} + +enum { + partText, + partImage, + partButton, + partCheckbox, + partProgressBar, +}; + +struct tablePart { + int type; + int textColumn; + int imageColumn; + int valueColumn; + int colorColumn; + GtkCellRenderer *r; + uiTable *tv; // for pixbufs and background color +}; + +struct uiTableColumn { + GtkTreeViewColumn *c; + uiTable *tv; // for pixbufs and background color + GPtrArray *parts; +}; + +struct uiTable { + uiUnixControl c; + GtkWidget *widget; + GtkContainer *scontainer; + GtkScrolledWindow *sw; + GtkWidget *treeWidget; + GtkTreeView *tv; + GPtrArray *columns; + uiTableModel *model; + int backgroundColumn; +}; + +// use the same size as GtkFileChooserWidget's treeview +// TODO refresh when icon theme changes +// TODO doesn't work when scaled +// TODO is this even necessary? +static void setImageSize(GtkCellRenderer *r) +{ + gint size; + gint width, height; + gint xpad, ypad; + + size = 16; // fallback used by GtkFileChooserWidget + if (gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height) != FALSE) + size = MAX(width, height); + gtk_cell_renderer_get_padding(r, &xpad, &ypad); + gtk_cell_renderer_set_fixed_size(r, + 2 * xpad + size, + 2 * ypad + size); +} + +static void applyColor(GtkTreeModel *mm, GtkTreeIter *iter, int modelColumn, GtkCellRenderer *r, const char *prop, const char *propSet) +{ + GValue value = G_VALUE_INIT; + GdkRGBA *rgba; + + gtk_tree_model_get_value(mm, iter, modelColumn, &value); + rgba = (GdkRGBA *) g_value_get_boxed(&value); + if (rgba != NULL) + g_object_set(r, prop, rgba, NULL); + else + g_object_set(r, propSet, FALSE, NULL); + g_value_unset(&value); +} + +static void dataFunc(GtkTreeViewColumn *c, GtkCellRenderer *r, GtkTreeModel *mm, GtkTreeIter *iter, gpointer data) +{ + struct tablePart *part = (struct tablePart *) data; + GValue value = G_VALUE_INIT; + const gchar *str; + uiImage *img; + int pval; + + switch (part->type) { + case partText: + gtk_tree_model_get_value(mm, iter, part->textColumn, &value); + str = g_value_get_string(&value); + g_object_set(r, "text", str, NULL); + if (part->colorColumn != -1) + applyColor(mm, iter, + part->colorColumn, + r, "foreground-rgba", "foreground-set"); + break; + case partImage: +//TODO setImageSize(r); + gtk_tree_model_get_value(mm, iter, part->imageColumn, &value); + img = (uiImage *) g_value_get_pointer(&value); + g_object_set(r, "surface", + imageAppropriateSurface(img, part->tv->treeWidget), + NULL); + break; + case partButton: + gtk_tree_model_get_value(mm, iter, part->textColumn, &value); + str = g_value_get_string(&value); + g_object_set(r, "text", str, NULL); + break; + case partCheckbox: + gtk_tree_model_get_value(mm, iter, part->valueColumn, &value); + g_object_set(r, "active", g_value_get_int(&value) != 0, NULL); + break; + case partProgressBar: + gtk_tree_model_get_value(mm, iter, part->valueColumn, &value); + pval = g_value_get_int(&value); + if (pval == -1) { + // TODO + } else + g_object_set(r, + "pulse", -1, + "value", pval, + NULL); + break; + } + g_value_unset(&value); + + if (part->tv->backgroundColumn != -1) + applyColor(mm, iter, + part->tv->backgroundColumn, + r, "cell-background-rgba", "cell-background-set"); +} + +static void onEdited(struct tablePart *part, int column, const char *pathstr, const void *data) +{ + GtkTreePath *path; + int row; + uiTableModel *m; + + path = gtk_tree_path_new_from_string(pathstr); + row = gtk_tree_path_get_indices(path)[0]; + gtk_tree_path_free(path); + m = part->tv->model; + (*(m->mh->SetCellValue))(m->mh, m, row, column, data); + // and update + uiTableModelRowChanged(m, row); +} + +static void appendPart(uiTableColumn *c, struct tablePart *part, GtkCellRenderer *r, int expand) +{ + part->r = r; + gtk_tree_view_column_pack_start(c->c, part->r, expand != 0); + gtk_tree_view_column_set_cell_data_func(c->c, part->r, dataFunc, part, NULL); + g_ptr_array_add(c->parts, part); +} + +static void textEdited(GtkCellRendererText *renderer, gchar *path, gchar *newText, gpointer data) +{ + struct tablePart *part = (struct tablePart *) data; + + onEdited(part, part->textColumn, path, newText); +} + +void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand) +{ + struct tablePart *part; + GtkCellRenderer *r; + + part = uiNew(struct tablePart); + part->type = partText; + part->textColumn = modelColumn; + part->tv = c->tv; + part->colorColumn = -1; + + r = gtk_cell_renderer_text_new(); + g_object_set(r, "editable", FALSE, NULL); + g_signal_connect(r, "edited", G_CALLBACK(textEdited), part); + + appendPart(c, part, r, expand); +} + +void uiTableColumnAppendImagePart(uiTableColumn *c, int modelColumn, int expand) +{ + struct tablePart *part; + + part = uiNew(struct tablePart); + part->type = partImage; + part->imageColumn = modelColumn; + part->tv = c->tv; + appendPart(c, part, + gtk_cell_renderer_pixbuf_new(), + expand); +} + +// TODO wrong type here +static void buttonClicked(GtkCellRenderer *r, gchar *pathstr, gpointer data) +{ + struct tablePart *part = (struct tablePart *) data; + + onEdited(part, part->textColumn, pathstr, NULL); +} + +void uiTableColumnAppendButtonPart(uiTableColumn *c, int modelColumn, int expand) +{ + struct tablePart *part; + GtkCellRenderer *r; + + part = uiNew(struct tablePart); + part->type = partButton; + part->textColumn = modelColumn; + part->tv = c->tv; + + r = newCellRendererButton(); + g_object_set(r, "sensitive", TRUE, NULL); // editable by default + g_signal_connect(r, "clicked", G_CALLBACK(buttonClicked), part); + + appendPart(c, part, r, expand); +} + +// yes, we need to do all this twice :| +static void checkboxToggled(GtkCellRendererToggle *r, gchar *pathstr, gpointer data) +{ + struct tablePart *part = (struct tablePart *) data; + GtkTreePath *path; + int row; + uiTableModel *m; + void *value; + int intval; + + path = gtk_tree_path_new_from_string(pathstr); + row = gtk_tree_path_get_indices(path)[0]; + gtk_tree_path_free(path); + m = part->tv->model; + value = (*(m->mh->CellValue))(m->mh, m, row, part->valueColumn); + intval = !uiTableModelTakeInt(value); + onEdited(part, part->valueColumn, pathstr, uiTableModelGiveInt(intval)); +} + +void uiTableColumnAppendCheckboxPart(uiTableColumn *c, int modelColumn, int expand) +{ + struct tablePart *part; + GtkCellRenderer *r; + + part = uiNew(struct tablePart); + part->type = partCheckbox; + part->valueColumn = modelColumn; + part->tv = c->tv; + + r = gtk_cell_renderer_toggle_new(); + g_object_set(r, "sensitive", TRUE, NULL); // editable by default + g_signal_connect(r, "toggled", G_CALLBACK(checkboxToggled), part); + + appendPart(c, part, r, expand); +} + +void uiTableColumnAppendProgressBarPart(uiTableColumn *c, int modelColumn, int expand) +{ + struct tablePart *part; + + part = uiNew(struct tablePart); + part->type = partProgressBar; + part->valueColumn = modelColumn; + part->tv = c->tv; + appendPart(c, part, + gtk_cell_renderer_progress_new(), + expand); +} + +void uiTableColumnPartSetEditable(uiTableColumn *c, int part, int editable) +{ + struct tablePart *p; + + p = (struct tablePart *) g_ptr_array_index(c->parts, part); + switch (p->type) { + case partImage: + case partProgressBar: + return; + case partButton: + case partCheckbox: + g_object_set(p->r, "sensitive", editable != 0, NULL); + return; + } + g_object_set(p->r, "editable", editable != 0, NULL); +} + +void uiTableColumnPartSetTextColor(uiTableColumn *c, int part, int modelColumn) +{ + struct tablePart *p; + + p = (struct tablePart *) g_ptr_array_index(c->parts, part); + p->colorColumn = modelColumn; + // TODO refresh table +} + +uiUnixControlAllDefaultsExceptDestroy(uiTable) + +static void uiTableDestroy(uiControl *c) +{ + uiTable *t = uiTable(c); + + // TODO + g_object_unref(t->widget); + uiFreeControl(uiControl(t)); +} + +uiTableColumn *uiTableAppendColumn(uiTable *t, const char *name) +{ + uiTableColumn *c; + + c = uiNew(uiTableColumn); + c->c = gtk_tree_view_column_new(); + gtk_tree_view_column_set_resizable(c->c, TRUE); + gtk_tree_view_column_set_title(c->c, name); + gtk_tree_view_append_column(t->tv, c->c); + c->tv = t; // TODO rename field to t, cascade + c->parts = g_ptr_array_new(); + return c; +} + +void uiTableSetRowBackgroundColorModelColumn(uiTable *t, int modelColumn) +{ + t->backgroundColumn = modelColumn; + // TODO refresh table +} + +uiTable *uiNewTable(uiTableModel *model) +{ + uiTable *t; + + uiUnixNewControl(uiTable, t); + + t->model = model; + t->backgroundColumn = -1; + + t->widget = gtk_scrolled_window_new(NULL, NULL); + t->scontainer = GTK_CONTAINER(t->widget); + t->sw = GTK_SCROLLED_WINDOW(t->widget); + gtk_scrolled_window_set_shadow_type(t->sw, GTK_SHADOW_IN); + + t->treeWidget = gtk_tree_view_new_with_model(GTK_TREE_MODEL(t->model)); + t->tv = GTK_TREE_VIEW(t->treeWidget); + // TODO set up t->tv + + gtk_container_add(t->scontainer, t->treeWidget); + // and make the tree view visible; only the scrolled window's visibility is controlled by libui + gtk_widget_show(t->treeWidget); + + return t; +} diff --git a/windows/tablepart.cpp b/windows/tablepart.cpp new file mode 100644 index 00000000..c31932d7 --- /dev/null +++ b/windows/tablepart.cpp @@ -0,0 +1,95 @@ +// 30 june 2016 +// TODO includes + +typedef struct tablePartDrawParams tablePartDrawParams; +typedef struct tablePartMinimumSizeParams tableDrawMinimumSizeParams; +typedef struct tablePartEditingParams tablePartEditingParams; + +struct tablePartDrawParams { + HWND hwnd; + HDC hdc; + RECT *r; + bool selected; + bool focused; + bool hovering; + uiTableModel *model; + int row; +}; + +struct tablePartMinimumSizeParams { + HWND hwnd; + HDC hdc; + uiTableModel *model; + int row; +}; + +enum { + partEventDoNothing, + partEventRedraw, + partEventEdit, +}; + +struct tablePartEditingParams { + HWND newHWND; +}; + +enum { + partEditContinue, + partEditDone, +}; + +class tablePart { +public: + // needed so we can delete a tablePart + virtual ~tablePart() {} + + virtual HRESULT Draw(tablePartDrawParams *p) = 0; + virtual HRESULT MinimumSize(tablePartMinimumSizeParams *p, int *width, int *height) = 0; + + // returns a partEvent constant + virtual int MouseMove(int x, int y, RECT *cell) = 0; + virtual int MouseLeave(void) = 0; + virtual int LButtonDown(int x, int y, int count, RECT *cell) = 0; + virtual int LButtonUp(int x, int y, RECT *cell) = 0; + virtual int CaptureBroken(void) = 0; + virtual int KeyDown(void) = 0; + virtual int KeyUp(void) = 0; + + // editing; all optional + virtual int StartEditing(tablePartEditingParams *p) { return editDone; } + virtual int EditChildWM_COMMAND(WORD code, LRESULT *lr) { return editDone; } + virtual void FinishEditing(uiTableModel *model, int row) {} + virtual void CancelEditing(void) {} + + // TODO tooltips + // TODO timers and animations + + // optional methods + virtual void SetTextColorColumn(int col) {} + virtual void SetEditable(bool editable) {} +}; + +class tablePartText : public tablePart { + int textColumn; + int colorColumn; +public: + tablePartText(int tc) + { + this->textColumn = tc; + this->colorColumn = -1; + } + + // TODO figure out vertical alignment + virtual HRESULT Draw(tablePartDrawParams *p) + { + } + + virtual HRESULT MinimumSize(tablePartMinimumSizeParams *p, int *width, int *height) + { + } +}; + +tablePart *newTablePartText(int tc) +{ + return new tablePartText(tc); +} diff --git a/windows/tableutil.cpp b/windows/tableutil.cpp new file mode 100644 index 00000000..e6e5bb14 --- /dev/null +++ b/windows/tableutil.cpp @@ -0,0 +1,32 @@ +// 1 july 2016 +// TODO includes + +void tableGetClientRect(HWND hwnd, RECT *r) +{ + if (GetClientRect(hwnd, r) == 0) { + r->left = 0; + r->top = 0; + r->right = 0; + r->bottom = 0; + } +} + +void tableGetWindowRect(HWND hwnd, RECT *r) +{ + if (GetWindowRect(hwnd, r) == 0) { + r->left = 0; + r->top = 0; + r->right = 0; + r->bottom = 0; + } +} + +void tableGetTextExtentPoint32W(HDC dc, const WSTR *str, int len, SIZE *s) +{ + if (len == -1) + len = wcslen(str); + if (GetTextExtentPoint32W(dc, str, len, s) == 0) { + s->cx = 0; + s->cy = 0; + } +} From 18b103c988bb057d04c6b0bd930dad315a87d039 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 27 Nov 2016 17:44:52 -0500 Subject: [PATCH 0498/1329] Fully reintegrated the table code. --- common/CMakeLists.txt | 1 + darwin/CMakeLists.txt | 1 + test/CMakeLists.txt | 2 ++ test/main.c | 4 ++-- test/test.h | 6 ++++++ ui.h | 3 +++ unix/CMakeLists.txt | 1 + 7 files changed, 16 insertions(+), 2 deletions(-) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 91d79493..7f704032 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -6,6 +6,7 @@ list(APPEND _LIBUI_SOURCES common/debug.c common/matrix.c common/shouldquit.c + common/table.c common/userbugs.c ) set(_LIBUI_SOURCES ${_LIBUI_SOURCES} PARENT_SCOPE) diff --git a/darwin/CMakeLists.txt b/darwin/CMakeLists.txt index dbef5d43..f0c936b5 100644 --- a/darwin/CMakeLists.txt +++ b/darwin/CMakeLists.txt @@ -35,6 +35,7 @@ list(APPEND _LIBUI_SOURCES darwin/spinbox.m darwin/stddialogs.m darwin/tab.m + darwin/table.m darwin/text.m darwin/util.m darwin/window.m diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e4924bb3..b753a7d4 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -6,6 +6,7 @@ endif() _add_exec(tester drawtests.c + images.c main.c menus.c page1.c @@ -26,6 +27,7 @@ _add_exec(tester page13.c page14.c page15.c + page16.c spaced.c ${_TEST_RESOURCES_RC} ) diff --git a/test/main.c b/test/main.c index 18774dcd..f33f30ab 100644 --- a/test/main.c +++ b/test/main.c @@ -159,8 +159,8 @@ int main(int argc, char *argv[]) innerTab = newTab(); uiTabAppend(outerTab, "Pages 16-?", uiControl(innerTab)); -// page16 = makePage16(); -// uiTabAppend(innerTab, "Page 16", uiControl(page16)); + page16 = makePage16(); + uiTabAppend(innerTab, "Page 16", uiControl(page16)); if (startspaced) setSpaced(1); diff --git a/test/test.h b/test/test.h index 66b1baa7..224ef667 100644 --- a/test/test.h +++ b/test/test.h @@ -89,3 +89,9 @@ extern uiTab *makePage14(void); // page15.c extern uiBox *makePage15(uiWindow *); + +// page16.c +extern uiBox *makePage16(void); + +// images.c +extern void appendImageNamed(uiImage *img, const char *name); diff --git a/ui.h b/ui.h index 5a2069e8..5852bf6d 100644 --- a/ui.h +++ b/ui.h @@ -684,6 +684,9 @@ _UI_EXTERN int uiGridPadded(uiGrid *g); _UI_EXTERN void uiGridSetPadded(uiGrid *g, int padded); _UI_EXTERN uiGrid *uiNewGrid(void); +// TODO merge +#include "uitable.h" + #ifdef __cplusplus } #endif diff --git a/unix/CMakeLists.txt b/unix/CMakeLists.txt index 9300bcb7..e967f29a 100644 --- a/unix/CMakeLists.txt +++ b/unix/CMakeLists.txt @@ -40,6 +40,7 @@ list(APPEND _LIBUI_SOURCES unix/spinbox.c unix/stddialogs.c unix/tab.c + unix/table.c unix/text.c unix/util.c unix/window.c From dd339699cd9bd6aae0279afeeb70557bfb6370a7 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 30 Nov 2016 09:21:37 -0500 Subject: [PATCH 0499/1329] Oops, I forgot to remove the uiImage code from the GTK+ port when splitting uiTable into a branch. Fixed the build for now. Fixes #238. --- unix/uipriv_unix.h | 1 + 1 file changed, 1 insertion(+) diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index d52a83c2..33ff1e35 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -53,6 +53,7 @@ extern PangoFont *pangoDescToPangoFont(PangoFontDescription *pdesc); extern ptrdiff_t *graphemes(const char *text, PangoContext *context); // image.c +/*TODO remove this*/typedef struct uiImage uiImage; extern cairo_surface_t *imageAppropriateSurface(uiImage *i, GtkWidget *w); // cellrendererbutton.c From cb927659faa8f826af490bbfa7ac24d48f9d6bad Mon Sep 17 00:00:00 2001 From: Lailton Fernando Mariano Date: Thu, 1 Dec 2016 14:32:14 -0200 Subject: [PATCH 0500/1329] Update uipriv_darwin.h --- darwin/uipriv_darwin.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 78f31af5..ad1627d0 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -17,6 +17,8 @@ #define NSAppKitVersionNumber10_9 1265 #endif +typedef struct uiImage uiImage; + // menu.m @interface menuManager : NSObject { struct mapTable *items; From 39d1c0565eb58d38716960fb4149180ee2ce5746 Mon Sep 17 00:00:00 2001 From: Lailton Fernando Mariano Date: Thu, 1 Dec 2016 16:26:59 -0200 Subject: [PATCH 0501/1329] Update uipriv_darwin.h --- darwin/uipriv_darwin.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index ad1627d0..6bca87b2 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -17,7 +17,7 @@ #define NSAppKitVersionNumber10_9 1265 #endif -typedef struct uiImage uiImage; +/*TODO remove this*/typedef struct uiImage uiImage; // menu.m @interface menuManager : NSObject { From c0f91058c44e7c2ed449188ae6fda795b824933c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 3 Dec 2016 11:20:22 -0500 Subject: [PATCH 0502/1329] Started the move to my utf library. Imported the library. --- common/utf.c | 347 +++++++++++++++++++++++++++++++++++++++++++++++++++ common/utf.h | 61 +++++++++ 2 files changed, 408 insertions(+) create mode 100644 common/utf.c create mode 100644 common/utf.h diff --git a/common/utf.c b/common/utf.c new file mode 100644 index 00000000..9efb9493 --- /dev/null +++ b/common/utf.c @@ -0,0 +1,347 @@ +// utf by pietro gagliardi (andlabs) — https://github.com/andlabs/utf/ +// 10 november 2016 +#include "utf.h" + +// this code imitates Go's unicode/utf8 and unicode/utf16 +// the biggest difference is that a rune is unsigned instead of signed (because Go guarantees what a right shift on a signed number will do, whereas C does not) +// it is also an imitation so we can license it under looser terms than the Go source +#define badrune 0xFFFD + +// encoded must be at most 4 bytes +// TODO clean this code up somehow +size_t utf8EncodeRune(uint32_t rune, char *encoded) +{ + uint8_t b, c, d, e; + size_t n; + + // not in the valid range for Unicode + if (rune > 0x10FFFF) + rune = badrune; + // surrogate runes cannot be encoded + if (rune >= 0xD800 && rune < 0xE000) + rune = badrune; + + if (rune < 0x80) { // ASCII bytes represent themselves + b = (uint8_t) (rune & 0xFF); + n = 1; + goto done; + } + if (rune < 0x800) { // two-byte encoding + c = (uint8_t) (rune & 0x3F); + c |= 0x80; + rune >>= 6; + b = (uint8_t) (rune & 0x1F); + b |= 0xC0; + n = 2; + goto done; + } + if (rune < 0x10000) { // three-byte encoding + d = (uint8_t) (rune & 0x3F); + d |= 0x80; + rune >>= 6; + c = (uint8_t) (rune & 0x3F); + c |= 0x80; + rune >>= 6; + b = (uint8_t) (rune & 0x0F); + b |= 0xE0; + n = 3; + goto done; + } + // otherwise use a four-byte encoding + e = (uint8_t) (rune & 0x3F); + e |= 0x80; + rune >>= 6; + d = (uint8_t) (rune & 0x3F); + d |= 0x80; + rune >>= 6; + c = (uint8_t) (rune & 0x3F); + c |= 0x80; + rune >>= 6; + b = (uint8_t) (rune & 0x07); + b |= 0xF0; + n = 4; + +done: + encoded[0] = b; + if (n > 1) + encoded[1] = c; + if (n > 2) + encoded[2] = d; + if (n > 3) + encoded[3] = e; + return n; +} + +const char *utf8DecodeRune(const char *s, size_t nElem, uint32_t *rune) +{ + uint8_t b, c; + uint8_t lowestAllowed, highestAllowed; + size_t i, expected; + int bad; + + b = (uint8_t) (*s); + if (b < 0x80) { // ASCII bytes represent themselves + *rune = b; + s++; + return s; + } + // 0xC0 and 0xC1 cover 2-byte overlong equivalents + // 0xF5 to 0xFD cover values > 0x10FFFF + // 0xFE and 0xFF were never defined (always illegal) + if (b < 0xC2 || b > 0xF4) { // invalid + *rune = badrune; + s++; + return s; + } + + // this determines the range of allowed first continuation bytes + lowestAllowed = 0x80; + highestAllowed = 0xBF; + switch (b) { + case 0xE0: + // disallow 3-byte overlong equivalents + lowestAllowed = 0xA0; + break; + case 0xED: + // disallow surrogate characters + highestAllowed = 0x9F; + break; + case 0xF0: + // disallow 4-byte overlong equivalents + lowestAllowed = 0x90; + break; + case 0xF4: + // disallow values > 0x10FFFF + highestAllowed = 0x8F; + break; + } + + // and this determines how many continuation bytes are expected + expected = 1; + if (b >= 0xE0) + expected++; + if (b >= 0xF0) + expected++; + if (nElem != 0) { // are there enough bytes? + nElem--; + if (nElem < expected) { // nope + *rune = badrune; + s++; + return s; + } + } + + // ensure that everything is correct + // if not, **only** consume the initial byte + bad = 0; + for (i = 0; i < expected; i++) { + c = (uint8_t) (s[1 + i]); + if (c < lowestAllowed || c > highestAllowed) { + bad = 1; + break; + } + // the old lowestAllowed and highestAllowed is only for the first continuation byte + lowestAllowed = 0x80; + highestAllowed = 0xBF; + } + if (bad) { + *rune = badrune; + s++; + return s; + } + + // now do the topmost bits + if (b < 0xE0) + *rune = b & 0x1F; + else if (b < 0xF0) + *rune = b & 0x0F; + else + *rune = b & 0x07; + s++; // we can finally move on + + // now do the continuation bytes + for (; expected; expected--) { + c = (uint8_t) (*s); + s++; + c &= 0x3F; // strip continuation bits + *rune <<= 6; + *rune |= c; + } + + return s; +} + +// encoded must have at most 2 elements +size_t utf16EncodeRune(uint32_t rune, uint16_t *encoded) +{ + uint16_t low, high; + + // not in the valid range for Unicode + if (rune > 0x10FFFF) + rune = badrune; + // surrogate runes cannot be encoded + if (rune >= 0xD800 && rune < 0xE000) + rune = badrune; + + if (rune < 0x10000) { + encoded[0] = (uint16_t) rune; + return 1; + } + + rune -= 0x10000; + low = (uint16_t) (rune & 0x3FF); + rune >>= 10; + high = (uint16_t) (rune & 0x3FF); + encoded[0] = high | 0xD800; + encoded[1] = low | 0xDC00; + return 2; +} + +// TODO see if this can be cleaned up somehow +const uint16_t *utf16DecodeRune(const uint16_t *s, size_t nElem, uint32_t *rune) +{ + uint16_t high, low; + + if (*s < 0xD800 || *s >= 0xE000) { + // self-representing character + *rune = *s; + s++; + return s; + } + if (*s >= 0xDC00) { + // out-of-order surrogates + *rune = badrune; + s++; + return s; + } + if (nElem == 1) { // not enough elements + *rune = badrune; + s++; + return s; + } + high = *s; + high &= 0x3FF; + if (s[1] < 0xDC00 || s[1] >= 0xE000) { + // bad surrogate pair + *rune = badrune; + s++; + return s; + } + s++; + low = *s; + s++; + low &= 0x3FF; + *rune = high; + *rune <<= 10; + *rune |= low; + *rune += 0x10000; + return s; +} + +// TODO find a way to reduce the code in all of these somehow +// TODO find a way to remove u as well +size_t utf8RuneCount(const char *s, size_t nElem) +{ + size_t len; + uint32_t rune; + + if (nElem != 0) { + const char *t, *u; + + len = 0; + t = s; + while (nElem != 0) { + u = utf8DecodeRune(t, nElem, &rune); + len++; + nElem -= u - t; + t = u; + } + return len; + } + len = 0; + while (*s) { + s = utf8DecodeRune(s, nElem, &rune); + len++; + } + return len; +} + +size_t utf8UTF16Count(const char *s, size_t nElem) +{ + size_t len; + uint32_t rune; + uint16_t encoded[2]; + + if (nElem != 0) { + const char *t, *u; + + len = 0; + t = s; + while (nElem != 0) { + u = utf8DecodeRune(t, nElem, &rune); + len += utf16EncodeRune(rune, encoded); + nElem -= u - t; + t = u; + } + return len; + } + len = 0; + while (*s) { + s = utf8DecodeRune(s, nElem, &rune); + len += utf16EncodeRune(rune, encoded); + } + return len; +} + +size_t utf16RuneCount(const uint16_t *s, size_t nElem) +{ + size_t len; + uint32_t rune; + + if (nElem != 0) { + const uint16_t *t, *u; + + len = 0; + t = s; + while (nElem != 0) { + u = utf16DecodeRune(t, nElem, &rune); + len++; + nElem -= u - t; + t = u; + } + return len; + } + len = 0; + while (*s) { + s = utf16DecodeRune(s, nElem, &rune); + len++; + } + return len; +} + +size_t utf16UTF8Count(const uint16_t *s, size_t nElem) +{ + size_t len; + uint32_t rune; + char encoded[4]; + + if (nElem != 0) { + const uint16_t *t, *u; + + len = 0; + t = s; + while (nElem != 0) { + u = utf16DecodeRune(t, nElem, &rune); + len += utf8EncodeRune(rune, encoded); + nElem -= u - t; + t = u; + } + return len; + } + len = 0; + while (*s) { + s = utf16DecodeRune(s, nElem, &rune); + len += utf8EncodeRune(rune, encoded); + } + return len; +} diff --git a/common/utf.h b/common/utf.h new file mode 100644 index 00000000..b810a49d --- /dev/null +++ b/common/utf.h @@ -0,0 +1,61 @@ +// utf by pietro gagliardi (andlabs) — https://github.com/andlabs/utf/ +// 10 november 2016 + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +// if nElem == 0, assume the buffer has no upper limit and is '\0' terminated +// otherwise, assume buffer is NOT '\0' terminated but is bounded by nElem *elements* + +extern size_t utf8EncodeRune(uint32_t rune, char *encoded); +extern const char *utf8DecodeRune(const char *s, size_t nElem, uint32_t *rune); +extern size_t utf16EncodeRune(uint32_t rune, uint16_t *encoded); +extern const uint16_t *utf16DecodeRune(const uint16_t *s, size_t nElem, uint32_t *rune); + +extern size_t utf8RuneCount(const char *s, size_t nElem); +extern size_t utf8UTF16Count(const char *s, size_t nElem); +extern size_t utf16RuneCount(const uint16_t *s, size_t nElem); +extern size_t utf16UTF8Count(const uint16_t *s, size_t nElem); + +#ifdef __cplusplus +} + +// Provide overloads on Windows for using these functions with wchar_t and WCHAR when wchar_t is a keyword in C++ mode (the default). +// Otherwise, you'd need to cast to pass a wchar_t pointer, WCHAR pointer, or equivalent to these functions. +// We use __wchar_t to be independent of the setting; see https://blogs.msdn.microsoft.com/oldnewthing/20161201-00/?p=94836 (ironically posted one day after I initially wrote this code!). +// TODO check this on MinGW-w64 +// TODO check this under /Wall +// TODO C-style casts enough? or will that fail in /Wall? +// TODO same for UniChar/unichar on Mac? if both are unsigned then we have nothing to worry about +#if defined(_MSC_VER) + +inline size_t utf16EncodeRune(uint32_t rune, __wchar_t *encoded) +{ + return utf16EncodeRune(rune, reinterpret_cast(encoded)); +} + +inline const __wchar_t *utf16DecodeRune(const __wchar_t *s, size_t nElem, uint32_t *rune) +{ + const uint16_t *ret; + + ret = utf16DecodeRune(reinterpret_cast(s), nElem, rune); + return reinterpret_cast(ret); +} + +inline size_t utf16RuneCount(const __wchar_t *s, size_t nElem) +{ + return utf16RuneCount(reinterpret_cast(s), nElem); +} + +inline size_t utf16UTF8Count(const __wchar_t *s, size_t nElem) +{ + return utf16UTF8Count(reinterpret_cast(s), nElem); +} + +#endif + +#endif From 0d5ff432b379c4ad2d7f59d3fd3d3306d9f35f04 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 3 Dec 2016 11:31:11 -0500 Subject: [PATCH 0503/1329] Rewrote utf16.cpp to use my utf lib. Maybe I should be doing attributed strings safter all, but I might as well optimize too I guess?? --- common/CMakeLists.txt | 1 + common/uipriv.h | 3 +++ windows/utf16.cpp | 48 ++++++++++++++++++++----------------------- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 91d79493..a4008fd1 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -7,6 +7,7 @@ list(APPEND _LIBUI_SOURCES common/matrix.c common/shouldquit.c common/userbugs.c + common/utf.c ) set(_LIBUI_SOURCES ${_LIBUI_SOURCES} PARENT_SCOPE) diff --git a/common/uipriv.h b/common/uipriv.h index d6b54e89..f22a08a6 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -1,4 +1,7 @@ // 6 april 2015 +// TODO can extern "C"s nest? +#include "utf.h" + #ifdef __cplusplus extern "C" { #endif diff --git a/windows/utf16.cpp b/windows/utf16.cpp index 98954d0a..6271fff7 100644 --- a/windows/utf16.cpp +++ b/windows/utf16.cpp @@ -3,48 +3,42 @@ // see http://stackoverflow.com/a/29556509/3408572 -#define MBTWC(str, wstr, bufsiz) MultiByteToWideChar(CP_UTF8, 0, str, -1, wstr, bufsiz) - WCHAR *toUTF16(const char *str) { WCHAR *wstr; - int n; + WCHAR *wp; + size_t n; + uint32_t rune; if (*str == '\0') // empty string return emptyUTF16(); - n = MBTWC(str, NULL, 0); - if (n == 0) { - logLastError(L"error figuring out number of characters to convert to"); - return emptyUTF16(); - } - wstr = (WCHAR *) uiAlloc(n * sizeof (WCHAR), "WCHAR[]"); - if (MBTWC(str, wstr, n) != n) { - logLastError(L"error converting from UTF-8 to UTF-16"); - // and return an empty string - *wstr = L'\0'; + n = utf8UTF16Count(str, 0); + wstr = (WCHAR *) uiAlloc((n + 1) * sizeof (WCHAR), "WCHAR[]"); + wp = wstr; + while (*str) { + str = utf8DecodeRune(str, 0, &rune); + n = utf16EncodeRune(rune, wp); + wp += n; } return wstr; } -#define WCTMB(wstr, str, bufsiz) WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, bufsiz, NULL, NULL) - char *toUTF8(const WCHAR *wstr) { char *str; - int n; + char *sp; + size_t n; + uint32_t rune; if (*wstr == L'\0') // empty string return emptyUTF8(); - n = WCTMB(wstr, NULL, 0); - if (n == 0) { - logLastError(L"error figuring out number of characters to convert to"); - return emptyUTF8(); - } - str = (char *) uiAlloc(n * sizeof (char), "char[]"); - if (WCTMB(wstr, str, n) != n) { - logLastError(L"error converting from UTF-16 to UTF-8"); - // and return an empty string - *str = '\0'; + n = utf16RuneCount(wstr, 0); + str = (char *) uiAlloc((n + 1) * sizeof (char), "char[]"); + sp = str; + while (*wstr) { + wstr = utf16DecodeRune(wstr, &rune); + n = utf8EncodeRune(rune, sp); + sp += n; } return str; } @@ -92,6 +86,8 @@ WCHAR *vstrf(const WCHAR *format, va_list ap) return buf; } +// TODO merge the following two with the toUTF*()s? + // Let's shove these utility routines here too. // Prerequisite: lfonly is UTF-8. char *LFtoCRLF(const char *lfonly) From 526173bf76bb064084fa3f89e249bb40bf8c487b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 3 Dec 2016 18:34:06 -0500 Subject: [PATCH 0504/1329] Started writing the implementation of uiAttributedString. Updated windows/graphemes.cpp for this new implementation. Also fixed up a bunch of subtle errors and other issues with windows/graphemes.cpp. --- common/attrstr.c | 160 ++++++++++++++++++++++++++++++++++++++++++ common/uipriv.h | 12 +++- windows/graphemes.cpp | 80 ++++++++++++++------- 3 files changed, 223 insertions(+), 29 deletions(-) create mode 100644 common/attrstr.c diff --git a/common/attrstr.c b/common/attrstr.c new file mode 100644 index 00000000..03a1dadc --- /dev/null +++ b/common/attrstr.c @@ -0,0 +1,160 @@ +// 3 december 2016 +#include "../ui.h" +#include "uipriv.h" + +struct uiAttributedString { + char *s; + size_t len; + + // TODO attributes + + // indiscriminately keep a UTF-16 copy of the string on all platforms so we can hand this off to the grapheme calculator + // this ensures no one platform has a speed advantage (sorry GTK+) + uint16_t *u16; + size_t u16len; + + size_t *u8tou16; + size_t *u16tou8; + + // this is lazily created to keep things from getting *too* slow + struct graphemes *graphemes; +}; + +static void resize(uiAttributedString *s, size_t u8, size_t u16) +{ + s->len = u8; + s->s = (char *) uiRealloc(s->s, (s->len + 1) * sizeof (char), "char[] (uiAttributedString)"); + s->u8tou16 = (size_t *) uiRealloc(s->u8tou16, (s->len + 1) * sizeof (size_t), "size_t[] (uiAttributedString)"); + s->u16len = u16; + s->u16 = (uint16_t *) uiRealloc(s->u16, (s->u16len + 1) * sizeof (uint16_t), "uint16_t[] (uiAttributedString)"); + s->u16tou8 = (size_t *) uiRealloc(s->u16tou8, (s->u16len + 1) * sizeof (size_t), "size_t[] (uiAttributedString)"); +} + +uiAttributedString *uiNewAttributedString(const char *initialString) +{ + uiAttributedString *s; + + s = uiNew(uiAttributedString); + uiAttributedStringAppendUnattributed(s, initialString); + return s; +} + +static void recomputeGraphemes(uiAttributedString *s) +{ + if (s->graphemes != NULL) + return; + if (graphemesTakesUTF16()) { + s->graphemes = graphemes(s->u16, s->u16len); + return; + } + s->graphemes = graphemes(s->s, s->len); +} + +static void invalidateGraphemes(uiAttributedString *s) +{ + if (s->graphemes == NULL) + return; + uiFree(s->graphemes->pointsToGraphemes); + uiFree(s->graphemes->graphemesToPoints); + uiFree(s->graphemes); + s->graphemes = NULL; +} + +void uiFreeAttributedString(uiAttributedString *s) +{ + invalidateGraphemes(s); + uiFree(s->u16tou8); + uiFree(s->u8tou16); + uiFree(s->u16); + uiFree(s->s); + uiFree(s); +} + +const char *uiAttributedStringString(uiAttributedString *s) +{ + return s->s; +} + +void uiAttributedStringAppendUnattributed(uiAttributedString *s, const char *str) +{ + const char *t; + uint32_t rune; + char buf[4]; + uint16_t u16buf[2]; + size_t n, n16; + size_t old, old; + + // first figure out how much we need to grow by + // this includes post-validated UTF-8 + t = str; + n = 0; + n16 = 0; + while (*t) { + t = utf8DecodeRune(t, 0, &rune); + n += utf8EncodeRune(rune, buf); + n16 += utf16EncodeRune(rune, buf16); + } + + // and resize + old = s->len; + old16 = s->len16; + resize(s, s->len + n, s->u16len + n16); + + // and copy + while (*str) { + str = utf8DecodeRune(str, 0, &rune); + n = utf8EncodeRune(rune, buf); + n16 = utf16EncodeRune(rune, buf16); + s->s[old] = buf[0]; + s->u8tou16[old] = old16; + if (n > 1) { + s->s[old + 1] = buf[1]; + s->u8tou16[old + 1] = old16; + } + if (n > 2) { + s->s[old + 2] = buf[2]; + s->u8tou16[old + 2] = old16; + } + if (n > 3) { + s->s[old + 3] = buf[3]; + s->u8tou16[old + 3] = old16; + } + s->u16[old16] = buf16[0]; + s->u16tou8[old16] = old; + if (n16 > 1) { + s->u16[old16 + 1] = buf16[1]; + s->u16tou8[old16 + 1] = old; + } + old += n; + old16 += n16; + } + // and have an index for the end of the string + s->u8tou16[old] = old16; + s->u16tou8[old16] = old; + + invalidateGraphemes(s); +} + +// TODO figure out if we should count the grapheme past the end +size_t uiAttributedStringNumGraphemes(uiAttributedString *s) +{ + recomputeGraphemes(s); + return s->graphemes->len; +} + +size_t uiAttributedStringByteIndexToGrapheme(uiAttributedString *s, size_t pos) +{ + recomputeGraphemes(s); + if (graphemesTakesUTF16()) + pos = s->u8tou16[pos]; + return s->graphemes->pointsToGraphemes[pos]; +} + +size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos) +{ + recomputeGraphemes(s); + pos = s->graphemes->graphemesToPoints[pos]; + if (graphemesTakesUTF16()) + pos = s->u16tou8[pos]; + return pos; +} diff --git a/common/uipriv.h b/common/uipriv.h index f22a08a6..1fe00b16 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -1,6 +1,4 @@ // 6 april 2015 -// TODO can extern "C"s nest? -#include "utf.h" #ifdef __cplusplus extern "C" { @@ -8,6 +6,7 @@ extern "C" { #include #include "controlsigs.h" +#include "utf.h" extern uiInitOptions options; @@ -56,6 +55,15 @@ extern void fallbackSkew(uiDrawMatrix *, double, double, double, double); extern void scaleCenter(double, double, double *, double *); extern void fallbackTransformSize(uiDrawMatrix *, double *, double *); +// for attrstr.c +struct graphemes { + size_t len; + size_t *pointsToGraphemes; + size_t *graphemesToPoints; +}; +extern int graphemesTakesUTF16(void); +extern struct graphemes *graphemes(void *s, size_t len); + #ifdef __cplusplus } #endif diff --git a/windows/graphemes.cpp b/windows/graphemes.cpp index 355e4037..d4fc1e1b 100644 --- a/windows/graphemes.cpp +++ b/windows/graphemes.cpp @@ -3,9 +3,14 @@ // We could use CharNext() to generate grapheme cluster boundaries, but it doesn't handle surrogate pairs properly (see http://archives.miloush.net/michkap/archive/2008/12/16/9223301.html). // So let's use Uniscribe (see http://archives.miloush.net/michkap/archive/2005/01/14/352802.html) -// See also http://www.catch22.net/tuts/uniscribe-mysteries and http://www.catch22.net/tuts/keyboard-navigation for more details. +// See also http://www.catch22.net/tuts/uniscribe-mysteries, http://www.catch22.net/tuts/keyboard-navigation, and https://maxradi.us/documents/uniscribe/ for more details. -static HRESULT itemize(WCHAR *msg, size_t len, SCRIPT_ITEM **out, int *outn) +int graphemesTakesUTF16(void) +{ + return 1; +} + +static HRESULT itemize(WCHAR *s, size_t len, SCRIPT_ITEM **out, int *outn) { SCRIPT_CONTROL sc; SCRIPT_STATE ss; @@ -20,8 +25,8 @@ static HRESULT itemize(WCHAR *msg, size_t len, SCRIPT_ITEM **out, int *outn) maxItems = len + 2; for (;;) { - items = new SCRIPT_ITEM[maxItems]; - hr = ScriptItemize(msg, len, + items = new SCRIPT_ITEM[maxItems + 1]; + hr = ScriptItemize(s, len, maxItems, &sc, &ss, items, &n); @@ -39,42 +44,63 @@ static HRESULT itemize(WCHAR *msg, size_t len, SCRIPT_ITEM **out, int *outn) return S_OK; } -size_t *graphemes(WCHAR *msg) +struct graphemes *graphemes(void *s, size_t len) { - size_t len; + struct graphemes *g; + WCHAR *str = (WCHAR *) s; SCRIPT_ITEM *items; - int i, n; - size_t *out; - size_t *op; - SCRIPT_LOGATTR *logattr; - int j, nn; + int nItems; + int curItemIndex; + int nCharsInCurItem; + size_t *pPTG, *pGTP; HRESULT hr; - len = wcslen(msg); - hr = itemize(msg, len, &items, &n); + g = uiNew(struct graphemes); + + hr = itemize(str, len, &items, &n); if (hr != S_OK) logHRESULT(L"error itemizing string for finding grapheme cluster boundaries", hr); + g->len = nItems; + g->pointsToGraphemes = (size_t *) uiAlloc((len + 1) * sizeof (size_t), "size_t[] (graphemes)"); + // note that there are actually nItems + 1 elements in items + // items[nItems] is the grapheme one past the end + g->graphemesToPoints = (size_t *) uiAlloc((g->len + 1) * sizeof (size_t), "size_t[] (graphemes)"); - // should be enough; 2 more just to be safe - out = (size_t *) uiAlloc((len + 2) * sizeof (size_t), "size_t[]"); - op = out; + pPTG = g->pointsToGraphemes; + pGTP = g->graphemesToPoints; + for (curItemIndex = 0; curItemIndex < nItems; curItemIndex++) { + SCRIPT_ITEM *curItem, *nextItem; + SCRIPT_LOGATTR *logattr; + size_t *curGTP; - // note that there are actually n + 1 elements in items - for (i = 0; i < n; i++) { - nn = items[i + 1].iCharPos - items[i].iCharPos; - logattr = new SCRIPT_LOGATTR[nn]; - hr = ScriptBreak(msg + items[i].iCharPos, nn, - &(items[i].a), logattr); + curItem = items + curItemIndex; + nextItem = curItem + 1; + + nCharsInCurItem = nextItem->iCharPos - curItem->iCharPos; + + logattr = new SCRIPT_LOGATTR[nCharsInCurItem]; + hr = ScriptBreak(str + curItem->iCharPos, nCharsInCurItem, + &(curItem->a), logattr); if (hr != S_OK) logHRESULT(L"error breaking string for finding grapheme cluster boundaries", hr); - for (j = 0; j < nn; j++) - if (logattr[j].fCharStop != 0) - *op++ = items[i].iCharPos + j; + + // TODO can we merge these loops somehow? + curGTP = pGTP; + for (i = 0; i < nCharsInCurItem; i++) + if (logattr[i].fCharStop != 0) + *pGTP++ = curItem->iCharPos + i; + for (i = 0; i < nCharsInCurItem; i++) { + *pPTG++ = curGTP - g->graphemesToPoints; + if (logattr[i].fCharStop != 0) + curGTP++; + } + delete[] logattr; } // and handle the last item for the end of the string - *op++ = items[i].iCharPos; + *pGTP++ = items[nItems].iCharPos; + *pPTG++ = pGTP - g->graphemesToPoints; delete[] items; - return out; + return g; } From 3218ba2a439120ca1cfa674290ba8b21e4246f2a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 3 Dec 2016 18:53:46 -0500 Subject: [PATCH 0505/1329] And migrated the Unix grapheme code for the new attributed string system. --- unix/graphemes.c | 55 +++++++++++++++++++++++++++++--------- unix/uipriv_unix.h | 3 --- windows/uipriv_windows.hpp | 3 --- 3 files changed, 43 insertions(+), 18 deletions(-) diff --git a/unix/graphemes.c b/unix/graphemes.c index a2c47b72..e489ebff 100644 --- a/unix/graphemes.c +++ b/unix/graphemes.c @@ -1,24 +1,41 @@ // 25 may 2016 #include "uipriv_unix.h" -ptrdiff_t *graphemes(const char *text, PangoContext *context) +int graphemesTakesUTF16(void) { - size_t len, lenchars; - PangoLogAttr *logattrs; - ptrdiff_t *out; - ptrdiff_t *op; - size_t i; + return 0; +} - len = strlen(text); +struct graphemes *graphemes(void *s, size_t len); +{ + struct graphemes *g; + char *text = (char *) s; + size_t lenchars; + PangoLogAttr *logattrs; + size_t i; + size_t *op; + + g = uiNew(struct graphemes); + + // TODO see if we can use the utf routines lenchars = g_utf8_strlen(text, -1); - logattrs = (PangoLogAttr *) uiAlloc((lenchars + 1) * sizeof (PangoLogAttr), "PangoLogAttr[]"); + logattrs = (PangoLogAttr *) uiAlloc((lenchars + 1) * sizeof (PangoLogAttr), "PangoLogAttr[] (graphemes)"); pango_get_log_attrs(text, len, -1, NULL, logattrs, lenchars + 1); - // should be more than enough - out = (ptrdiff_t *) uiAlloc((lenchars + 2) * sizeof (ptrdiff_t), "ptrdiff_t[]"); - op = out; + // first figure out how many graphemes there are + g->len = 0; + for (i = 0; i < lenchars; i++) + if (logattrs[i].is_cursor_position != 0) + g->len++; + + g->pointsToGraphemes = (size_t *) uiAlloc((len + 1) * sizeof (size_t), "size_t[] (graphemes)"); + g->graphemesToPoints = (size_t *) uiAlloc((g->len + 1) * sizeof (size_t), "size_t[] (graphemes)"); + + // compute the graphemesToPoints array + // TODO merge with the next for loop somehow? + op = g->graphemesToPoints; for (i = 0; i < lenchars; i++) if (logattrs[i].is_cursor_position != 0) // TODO optimize this @@ -26,6 +43,20 @@ ptrdiff_t *graphemes(const char *text, PangoContext *context) // and do the last one *op++ = len; + // and finally build the pointsToGraphemes array + op = g->pointsToGraphemes; + for (i = 0; i < g->len; i++) { + size_t j; + size_t first, last; + + first = g->graphemesToPoints[i]; + last = g->graphemesToPoints[i + 1]; + for (j = first; j < last; j++) + *op++ = i; + } + // and do the last one + *op++ = i; + uiFree(logattrs); - return out; + return g; } diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index 33ff1e35..de2c1d3d 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -49,9 +49,6 @@ extern void freeContext(uiDrawContext *); extern uiDrawTextFont *mkTextFont(PangoFont *f, gboolean add); extern PangoFont *pangoDescToPangoFont(PangoFontDescription *pdesc); -// graphemes.c -extern ptrdiff_t *graphemes(const char *text, PangoContext *context); - // image.c /*TODO remove this*/typedef struct uiImage uiImage; extern cairo_surface_t *imageAppropriateSurface(uiImage *i, GtkWidget *w); diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index 6ffe09f1..2354bfd7 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -148,9 +148,6 @@ extern BOOL showColorDialog(HWND parent, struct colorDialogRGBA *c); // sizing.cpp extern void getSizing(HWND hwnd, uiWindowsSizing *sizing, HFONT font); -// graphemes.cpp -extern size_t *graphemes(WCHAR *msg); - // TODO move into a dedicated file abibugs.cpp when we rewrite the drawing code extern D2D1_SIZE_F realGetSize(ID2D1RenderTarget *rt); From da8b8de37141f2774d2b2f4e84ff3340493b90fb Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 3 Dec 2016 20:01:45 -0500 Subject: [PATCH 0506/1329] Wrote the new attributed string system's grapheme code for OS X. --- darwin/graphemes.m | 58 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 darwin/graphemes.m diff --git a/darwin/graphemes.m b/darwin/graphemes.m new file mode 100644 index 00000000..3819f0b4 --- /dev/null +++ b/darwin/graphemes.m @@ -0,0 +1,58 @@ +// 3 december 2016 +#import "uipriv_darwin.h" + +// CFStringGetRangeOfComposedCharactersAtIndex() is the function for grapheme clusters +// https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Strings/Articles/stringsClusters.html says that this does work on all multi-codepoint graphemes (despite the name), and that this is the preferred function for this particular job anyway + +int graphemesTakesUTF16(void) +{ + return 1; +} + +struct graphemes *graphemes(void *s, size_t len) +{ + struct graphemes *g; + UniChar *str = (UniChar *) s; + CFString cfstr; + size_t ppos, gpos; + CFRange range; + size_t i; + + g = uiNew(struct graphemes); + + cfstr = CFStringCreateWithCharactersNoCopy(NULL, str, len, kCFAllocatorNull); + if (cfstr == NULL) { + // TODO + } + + // first figure out how many graphemes there are + g->len = 0; + ppos = 0; + while (ppos < len) { + range = CFStringGetRangeOfComposedCharactersAtIndex(cfstr, ppos); + g->len++; + ppos = range.location + range.length; + } + + g->pointsToGraphemes = (size_t *) uiAlloc((len + 1) * sizeof (size_t), "size_t[] (graphemes)"); + g->graphemesToPoints = (size_t *) uiAlloc((g->len + 1) * sizeof (size_t), "size_t[] (graphemes)"); + + // now calculate everything + // fortunately due to the use of CFRange we can do this in one loop trivially! + ppos = 0; + gpos = 0; + while (ppos < len) { + range = CFStringGetRangeOfComposedCharactersAtIndex(cfstr, ppos); + for (i = 0; i < range.length; i++) + g->pointsToGraphemes[range.location + i] = gpos; + g->graphemesToPoints[gpos] = range.location; + gpos++; + ppos = range.location + range.length; + } + // and set the last one + g->pointsToGraphemes[ppos] = gpos; + g->graphemesToPoints[gpos] = ppos; + + CFRelease(cfstr); + return g; +} From 4f8f94b85ab81c5111a442abddb75851a242362a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 3 Dec 2016 20:02:39 -0500 Subject: [PATCH 0507/1329] Updated the CMakeLists.txt for OS X. --- darwin/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/darwin/CMakeLists.txt b/darwin/CMakeLists.txt index dbef5d43..dc99b864 100644 --- a/darwin/CMakeLists.txt +++ b/darwin/CMakeLists.txt @@ -19,6 +19,7 @@ list(APPEND _LIBUI_SOURCES darwin/entry.m darwin/fontbutton.m darwin/form.m + darwin/graphemes.m darwin/grid.m darwin/group.m darwin/image.m From b45e5f4de2fba1d0ed189529095bcd275da94738 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 4 Dec 2016 16:02:56 -0500 Subject: [PATCH 0508/1329] Extended attrstr.c. Much more complete API now. --- common/attrstr.c | 150 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 138 insertions(+), 12 deletions(-) diff --git a/common/attrstr.c b/common/attrstr.c index 03a1dadc..98e8e90a 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -39,6 +39,7 @@ uiAttributedString *uiNewAttributedString(const char *initialString) return s; } +// TODO make sure that all implementations of graphemes() work fine with empty strings; in particular, the Windows one might not static void recomputeGraphemes(uiAttributedString *s) { if (s->graphemes != NULL) @@ -75,31 +76,95 @@ const char *uiAttributedStringString(uiAttributedString *s) return s->s; } +size_t uiAttributedStringLen(uiAttributedString *s) +{ + return s->len; +} + +static void u8u16len(const char *str, size_t *n8, size_t *n16) +{ + uint32_t rune; + char buf[4]; + uint16_t buf16[2]; + + *n8 = 0; + *n16 = 0; + while (*str) { + str = utf8DecodeRune(str, 0, &rune); + *n8 += utf8EncodeRune(rune, buf); + *n16 += utf16EncodeRune(rune, buf16); + } +} + void uiAttributedStringAppendUnattributed(uiAttributedString *s, const char *str) { - const char *t; + uiAttributedStringInsertAtUnattributed(s, str, s->len); +} + +// this works (and returns true, which is what we want) at s->len too because s->s[s->len] is always going to be 0 due to us allocating s->len + 1 bytes and because uiRealloc() always zero-fills allocated memory +static int onCodepointBoundary(uiAttributedString *s, size_t at) +{ + uint8_t c; + + // for uiNewAttributedString() + if (s->s == NULL && at == 0) + return 1; + c = (uint8_t) (s->s[at]); + return c < 0x80 || c >= 0xC0; +} + +// TODO note that at must be on a codeoint boundary +void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *str, size_t at) +{ uint32_t rune; char buf[4]; uint16_t u16buf[2]; size_t n, n16; - size_t old, old; + size_t old, old16; + size_t oldlen, old16len; + size_t at16; + size_t i; + + if (!onCodepointBoundary(s, at)) { + // TODO + } + + at16 = s->u8tou16[at]; + + // do this first to reclaim memory + invalidateGraphemes(s); // first figure out how much we need to grow by // this includes post-validated UTF-8 - t = str; - n = 0; - n16 = 0; - while (*t) { - t = utf8DecodeRune(t, 0, &rune); - n += utf8EncodeRune(rune, buf); - n16 += utf16EncodeRune(rune, buf16); - } + u8u16len(str, &n, &n16); // and resize - old = s->len; - old16 = s->len16; + old = at; + old16 = at16; + oldlen = s->len; + old16len = s->u16len; resize(s, s->len + n, s->u16len + n16); + // move existing characters out of the way + // note the use of memmove(): https://twitter.com/rob_pike/status/737797688217894912 + memmove( + s->s + at + n8, + s->s + at, + (oldlen - at) * sizeof (char)); + memmove( + s->u16 + at16 + n16, + s->u16 + at16, + (old16len - at16) * sizeof (uint16_t)); + // note the + 1 for these; we want to copy the terminating null too + memmove( + s->u8tou16 + at + n8, + s->u8tou16 + at, + (oldlen - at + 1) * sizeof (size_t)); + memmove( + s->u16tou8 + at16 + n16, + s->u16tou8 + at16, + (old16len - at16 + 1) * sizeof (size_t)); + // and copy while (*str) { str = utf8DecodeRune(str, 0, &rune); @@ -129,10 +194,71 @@ void uiAttributedStringAppendUnattributed(uiAttributedString *s, const char *str old16 += n16; } // and have an index for the end of the string + // TODO is this done by the below? s->u8tou16[old] = old16; s->u16tou8[old16] = old; + // and finally adjust the prior values in the conversion tables + // use <= so the terminating 0 gets updated too + for (i = 0; i <= oldlen - at; i++) + s->u8tou16[at + n + i] += n16; + for (i = 0; i <= old16len - at16; i++) + s->u16tou8[at16 + n16 + i] += n; +} + +// TODO document that end is the first index that will be maintained +void uiAttributedStringDelete(uiAttributedString *s, size_t start, size_t end) +{ + size_t start16, end16; + size_t count, count16; + size_t i; + + if (!onCodepointBoundary(s, start)) { + // TODO + } + if (!onCodepointBoundary(s, end)) { + // TODO + } + + count = end - start; + start16 = s->u8tou16[start]; + end16 = s->u8tou16[end]; + count16 = end16 - start16; + invalidateGraphemes(s); + + // overwrite old characters + memmove( + s->s + start, + s->s + end, + (oldlen - end) * sizeof (char)); + memmove( + s->u16 + start16, + s->u16 + end16, + (old16len - end16) * sizeof (uint16_t)); + // note the + 1 for these; we want to copy the terminating null too + memmove( + s->u8tou16 + start, + s->u8tou16 + end, + (oldlen - end + 1) * sizeof (size_t)); + memmove( + s->u16tou8 + start16, + s->u16tou8 + end16, + (old16len - end16 + 1) * sizeof (size_t)); + + // update the conversion tables + // note the use of <= to include the null terminator + for (i = 0; i <= count; i++) + s->u8tou16[start + i] -= count16; + for (i = 0; i <= count16; i++) + s->u16tou8[start16 + i] -= count; + + // null-terminate the string + s->s[start + count] = 0; + s->u16[start16 + count16] = 0; + + // and finally resize + resize(s, start + count, start16 + count16); } // TODO figure out if we should count the grapheme past the end From cb8d75d43168aea043571b387bda0c3a5ab57af6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 5 Dec 2016 18:32:51 -0500 Subject: [PATCH 0509/1329] Started implementing the attribute handling code itself. --- common/attrstr.c | 260 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 253 insertions(+), 7 deletions(-) diff --git a/common/attrstr.c b/common/attrstr.c index 98e8e90a..bc28242c 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -6,7 +6,9 @@ struct uiAttributedString { char *s; size_t len; - // TODO attributes + size_t nAttrs; // TODO this needs to be maintained; is it necessary? + struct attr *attrs; + struct attr *lastattr; // indiscriminately keep a UTF-16 copy of the string on all platforms so we can hand this off to the grapheme calculator // this ensures no one platform has a speed advantage (sorry GTK+) @@ -20,6 +22,237 @@ struct uiAttributedString { struct graphemes *graphemes; }; +struct attr { + int type; + uintptr_t val; + size_t start; + size_t end; + struct attr *next; +}; + +enum { + // TODO put attr types here + nAttrTypes, +}; + +static int attrHasPos(struct attr *a, size_t pos) +{ + if (pos < a->start) + return 0; + return pos < a->end; +} + +// returns 1 if there was an intersection and 0 otherwise +static int attrRangeIntersect(struct attr *a, size_t *start, size_t *end) +{ + // is the range outside a entirely? + if (*start >= a->end) + return 0; + if (*end < a->start) + return 0; + + // okay, so there is an overlap + // compute the intersection + if (*start < a->start) + *start = a->start; + if (*end > a->end) + *end = a->end; + return 1; +} + +// returns: +// - 0 if no change needed +// - 1 if the attribute was split +static int attrSplitAt(uiAttributedString *s, struct attr *a, size_t at) +{ + struct attr *b; + + // no splittng needed? + if (at == a->start) + return 0; + if ((at + 1) == a->end) + return 0; + + b = uiNew(struct attr); + b->what = a->what; + b->val = a->val; + b->start = at; + b->end = a->end; + b->next = a->next; + + a->end = at; + a->next = b; + if (a == s->lastattr) + s->lastattr = a->next; + return 1; +} + +// returns: +// - 0 if the attribute needs to be deleted +// - 1 if the attribute was changed or no change needed +// - 2 if the attribute was split +static int attrDropRange(uiAttributedString *s, struct attr *a, size_t start, size_t end) +{ + struct attr *b; + + if (!attrRangeIntersect(a, &start, &end)) + // out of range; nothing to do + return 1; + + // just outright delete the attribute? + if (a->start == start && a->end == end) + return 0; + + // only chop off the start or end? + if (a->start == start) { // chop off the end + a->end = end; + return 1; + } + if (a->end == end) { // chop off the start + a->start = start; + return 1; + } + + // we'll need to split the attribute into two + b = uiNew(struct attr); + b->what = a->what; + b->val = a->val; + b->start = end; + b->end = a->end; + b->next = a->next; + + a->end = start; + a->next = b; + if (a == s->lastattr) + s->lastattr = a->next; + return 2; +} + +// returns the attribute to continue with +static struct attr *attrDelete(uiAttributedString *s, struct attr *a, struct attr *prev) +{ + if (a == s->attrs) { + s->attrs = a->next; + uiFree(a); + return s->attrs; + } + if (a == s->lastattr) + s->lastattr = prev; + prev->next = a->next; + uiFree(a); + return prev->next; +} + +static void attrAppend(uiAttributedString *s, int type, uintptr_t val, size_t start, size_t end) +{ + struct attr *a; + + a = uiNew(struct attr); + a->type = type; + a->val = val; + a->start = start; + a->end = end; + if (s->attrs == NULL) { + s->attrs = a; + s->lastattr = a; + return; + } + s->lastattr->next = a; + s->lastattr = a; +} + +// alist should be struct attr *alist[nAttrTypes] +static void attrsGetFor(uiAttributedString *s, struct attr **alist, size_t at) +{ + int i; + struct attr *a; + + // we want the attributes for at + // these are the attributes of at - 1 + // if at == 0. then htese are the attributes at 0 + if (at != 0) + at--; + + // make usre unset attributes are NULL + for (i = 0; i < nAttrTypes; i++) + alist[i] = NULL; + + for (a = s->attrs; a != NULL; a = a->next) { + if (!attrHasPos(a, at)) + continue; + alist[a->type] = a; + } +} + +// TODO have a routine that prunes overridden attributes for a given character from the list? merge it with the above? + +static void attrAdjustPostInsert(uiAttributedString *s, size_t start, size_t n, size_t oldlen) +{ + struct attr *a; + + for (a = s->attrs; a != NULL; a = a->next) { + size_t astart, aend; + int splitNeeded; + + // do we need to adjust this, and where? + astart = start; + aend = oldlen; + if (!attrRangeIntersect(a, &astart, &aend)) + continue; + + // if only part of the attribute falls in the moved area, we need to split at the insertion point and adjust both resultant attributes + // otherwise, we only adjust the original attribute + // split *before* adjusting so that the split is correct + splitNeeded = attrSplit(s, a, astart); + if (a->start >= start) + a->start += n; + if (a->end >= end) + a->end += n; + if (splitNeeded == 1) { + a = a->next; + if (a->start >= start) + a->start += n; + if (a->end >= end) + a->end += n; + } + } +} + +// TODO this is very very wrong +static void attrAdjustPostDelete(uiAttributedString *s, size_t start, size_t end) +{ + struct attr *a, *prev; + + a = s->attrs; + prev = NULL; + while (a != NULL) { + size_t astart, aend; + + // do we need to adjust this, and where? + astart = start; + aend = end; + if (!attrRangeIntersect(a, &astart, &aend)) { + prev = a; + a = a->next; + continue; + } + + switch (attrDropRange(s, a, astart, aend)) { + case 0: // delete + a = attrDelete(s, a, prev); + // keep prev unchanged + break; + case 2: // split + a = a->next; + // fall through + case 1: // existing only needed adjustment + prev = a; + a = a->next; + break; + } + } +} + static void resize(uiAttributedString *s, size_t u8, size_t u16) { s->len = u8; @@ -63,6 +296,14 @@ static void invalidateGraphemes(uiAttributedString *s) void uiFreeAttributedString(uiAttributedString *s) { + struct attr *a, *b; + + a = s->attrs; + while (a != NULL) { + b = a->next; + uiFree(a); + a = b; + } invalidateGraphemes(s); uiFree(s->u16tou8); uiFree(s->u8tou16); @@ -119,7 +360,7 @@ void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *s uint32_t rune; char buf[4]; uint16_t u16buf[2]; - size_t n, n16; + size_t n8, n16; size_t old, old16; size_t oldlen, old16len; size_t at16; @@ -136,14 +377,14 @@ void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *s // first figure out how much we need to grow by // this includes post-validated UTF-8 - u8u16len(str, &n, &n16); + u8u16len(str, &n8, &n16); // and resize old = at; old16 = at16; oldlen = s->len; old16len = s->u16len; - resize(s, s->len + n, s->u16len + n16); + resize(s, s->len + n8, s->u16len + n16); // move existing characters out of the way // note the use of memmove(): https://twitter.com/rob_pike/status/737797688217894912 @@ -167,6 +408,8 @@ void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *s // and copy while (*str) { + size_t n; + str = utf8DecodeRune(str, 0, &rune); n = utf8EncodeRune(rune, buf); n16 = utf16EncodeRune(rune, buf16); @@ -198,12 +441,15 @@ void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *s s->u8tou16[old] = old16; s->u16tou8[old16] = old; - // and finally adjust the prior values in the conversion tables + // and adjust the prior values in the conversion tables // use <= so the terminating 0 gets updated too for (i = 0; i <= oldlen - at; i++) - s->u8tou16[at + n + i] += n16; + s->u8tou16[at + n8 + i] += n16; for (i = 0; i <= old16len - at16; i++) - s->u16tou8[at16 + n16 + i] += n; + s->u16tou8[at16 + n16 + i] += n8; + + // and finally do the attributes + attrAdjustPostInsert(s, at, n8, oldlen); } // TODO document that end is the first index that will be maintained From 0503110ddd5a0a2c629b7adeb389faf72b78fd7e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 6 Dec 2016 10:23:53 -0500 Subject: [PATCH 0510/1329] Fixed the delete attributes code. --- common/attrstr.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/common/attrstr.c b/common/attrstr.c index bc28242c..bcd78052 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -87,6 +87,7 @@ static int attrSplitAt(uiAttributedString *s, struct attr *a, size_t at) return 1; } +// removes attributes without deleting characters // returns: // - 0 if the attribute needs to be deleted // - 1 if the attribute was changed or no change needed @@ -128,6 +129,69 @@ static int attrDropRange(uiAttributedString *s, struct attr *a, size_t start, si return 2; } +// removes attributes while deleting characters +// returns: +// - 0 if the attribute needs to be deleted +// - 1 if the attribute was changed or no change needed +// - 2 if the attribute was split +static int attrDeleteRange(uiAttributedString *s, struct attr *a, size_t start, size_t end) +{ + struct attr *b; + struct attr tmp; + size_t count, acount; + + if (!attrRangeIntersect(a, &start, &end)) + // out of range; nothing to do + return 1; + + // just outright delete the attribute? + if (a->start == start && a->end == end) + return 0; + + acount = a->end - a->start; + count = end - start; + + // only the start or end deleted? + if (a->start == start) { // start deleted + a->end = a->start + (acount - count); + return 1; + } + if (a->end == end) { // end deleted + a->end = a->start + count; + return 1; + } + + // something in the middle deleted + // we ened to split the attribute into *three* + // first, split at the start of he deleted range + tmp.what = a->what; + tmp.val = a->val; + tmp.start = start; + tmp.end = a->end; + tmp.next = a->next; + + a->end = start; + a->next = &tmp; + + // now split at the end + b = uiNew(struct attr); + b->what = a->what; + b->val = a->val; + b->start = end; + b->end = a->end; + b->next = tmp.next; + + tmp.end = end; + tmp.next = b; + + // and now push c back to overwrite the deleted stuff + a->next = b; + b->start -= count; + b->end -= count; + + return 2; +} + // returns the attribute to continue with static struct attr *attrDelete(uiAttributedString *s, struct attr *a, struct attr *prev) { @@ -218,7 +282,6 @@ static void attrAdjustPostInsert(uiAttributedString *s, size_t start, size_t n, } } -// TODO this is very very wrong static void attrAdjustPostDelete(uiAttributedString *s, size_t start, size_t end) { struct attr *a, *prev; @@ -237,7 +300,7 @@ static void attrAdjustPostDelete(uiAttributedString *s, size_t start, size_t end continue; } - switch (attrDropRange(s, a, astart, aend)) { + switch (attrDeleteRange(s, a, astart, aend)) { case 0: // delete a = attrDelete(s, a, prev); // keep prev unchanged From e9fdbf33f3789167e65b9269cf094aaa0255ce99 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 6 Dec 2016 10:35:08 -0500 Subject: [PATCH 0511/1329] And plugged in the deletion stuff into the rest of attrstr.c. We're on a roll here! --- common/attrstr.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/common/attrstr.c b/common/attrstr.c index bcd78052..e1d42bed 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -184,11 +184,12 @@ static int attrDeleteRange(uiAttributedString *s, struct attr *a, size_t start, tmp.end = end; tmp.next = b; - // and now push c back to overwrite the deleted stuff + // and now push b back to overwrite the deleted stuff a->next = b; b->start -= count; b->end -= count; - + if (a == s->lastattr) + s->lastattr = a->next; return 2; } @@ -566,6 +567,9 @@ void uiAttributedStringDelete(uiAttributedString *s, size_t start, size_t end) s->s[start + count] = 0; s->u16[start16 + count16] = 0; + // fix up attributes + attrAdjustPostDelete(s, start, end); + // and finally resize resize(s, start + count, start16 + count16); } From 3f48bddce0fdda4665ab02f2cb1c395c1dbf7a28 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 12 Dec 2016 06:45:49 -0500 Subject: [PATCH 0512/1329] Started the header file for attributed strings, drawing text layouts, and the new text system in general. --- common/ui_attrstr.h | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 common/ui_attrstr.h diff --git a/common/ui_attrstr.h b/common/ui_attrstr.h new file mode 100644 index 00000000..a333fb78 --- /dev/null +++ b/common/ui_attrstr.h @@ -0,0 +1,30 @@ +typedef struct uiAttributedString uiAttributedString; + +_UI_ENUM(uiAttribute) { + // TODO +}; + +// @role uiAttributedString constructor +// uiNewAttributedString() creates a new uiAttributedString from +// initialString. The string will be entirely unattributed. +_UI_EXTERN uiAttributedString *uiNewAttributedString(const char *initialString); + +// @role uiAttributedString destructor +// uiFreeAttributedString() destroys the uiAttributedString s. +_UI_EXTERN void uiFreeAttributedString(uiAttributedString *s); + +// uiAttributedStringString() returns the textual content of s as a +// '\0'-terminated UTF-8 string. The returned pointer is valid until +// the next change to the textual content of s. +_UI_EXTERN const char *uiAttributedStringString(uiAttributedString *s); + +// uiAttributedStringLength() returns the number of UTF-8 bytes in +// the textual content of s, excluding the terminating '\0'. +_UI_EXTERN size_t uiAttributedStringLen(uiAttributedString *s); + +_UI_EXTERN void uiAttributedStringAppendUnattributed(uiAttributedString *s, const char *str); +_UI_EXTERN void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *str, size_t at); +_UI_EXTERN void uiAttributedStringDelete(uiAttributedString *s, size_t start, size_t end); +_UI_EXTERN size_t uiAttributedStringNumGraphemes(uiAttributedString *s); +_UI_EXTERN size_t uiAttributedStringByteIndexToGrapheme(uiAttributedString *s, size_t pos); +_UI_EXTERN size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos); From 6726ab70a9fabdb95509c15e24f9f82d43fad934 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 15 Dec 2016 13:39:19 -0500 Subject: [PATCH 0513/1329] Changed attrstr.c to match ui_attrstr.h. --- common/attrstr.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/common/attrstr.c b/common/attrstr.c index e1d42bed..224b6074 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -23,17 +23,15 @@ struct uiAttributedString { }; struct attr { - int type; + uiAttribute type; uintptr_t val; size_t start; size_t end; struct attr *next; }; -enum { - // TODO put attr types here - nAttrTypes, -}; +// if new entries types are added to the end of the uiAttribute enumeration, this MUST be updated! +#define nAttrTypes (TODO + 1) static int attrHasPos(struct attr *a, size_t pos) { @@ -234,7 +232,7 @@ static void attrsGetFor(uiAttributedString *s, struct attr **alist, size_t at) // we want the attributes for at // these are the attributes of at - 1 - // if at == 0. then htese are the attributes at 0 + // if at == 0. then these are the attributes at 0 if (at != 0) at--; From 98082068f69b75a6ef15c52cbf052c20f77f3ae7 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 16 Dec 2016 23:31:04 -0500 Subject: [PATCH 0514/1329] Started an experimental doubly linked list implementation of attribute lists. --- common/exp_attrdll.c | 52 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 common/exp_attrdll.c diff --git a/common/exp_attrdll.c b/common/exp_attrdll.c new file mode 100644 index 00000000..b4bd5f3c --- /dev/null +++ b/common/exp_attrdll.c @@ -0,0 +1,52 @@ +// 16 december 2016 + +struct attr { + uiAttribute type; + uintptr_t val; + size_t start; + size_t end; + struct attr *prev; + struct attr *next; +}; + +struct attrlist { + struct attr *first; + struct attr *last; +}; + +void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, size_t start, size_t end) +{ + struct attr *a; + struct attr *after; + + a = uiNew(struct attr); + a->type = type; + a->val = val; + a->start = start; + a->end = end; + + if (alist->first == NULL) { // empty list + alist->first = a; + alist->last = a; + return; + } + + // place a before the first element that starts after a does + for (before = alist->first; before != NULL; before = before->next) + if (before->start > a->start) + break; + if (before == NULL) { // if there is none, add a to the end + alist->last->next = a; + a->prev = alist->last; + alist->last = a; + return; + } + + if (before == alist->first) + alist->first = a; + else // not the first; hook up our new prev + before->prev->next = a; + a->next = before; + a->prev = before->prev; + before->prev = a; +} From 722dd03193a32498aa554f447409385e587a86e9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Dec 2016 12:19:33 -0500 Subject: [PATCH 0515/1329] Cleaned up exp_attrdll.c a bit. --- common/exp_attrdll.c | 61 +++++++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/common/exp_attrdll.c b/common/exp_attrdll.c index b4bd5f3c..2dfa7440 100644 --- a/common/exp_attrdll.c +++ b/common/exp_attrdll.c @@ -14,6 +14,45 @@ struct attrlist { struct attr *last; }; +// if before is NULL, add to the end of the list +static void linkInsertBefore(struct attrlist *alist, struct attr *what, struct attr *before) +{ + // if the list is empty, this is the first item + if (alist->first == NULL) { + alist->first = a; + alist->last = a; + return; + } + + // add to the end + if (before == NULL) { + struct attr *oldlast; + + oldlast = alist->last; + alist->last = a; + a->prev = oldlast; + oldlast->next = a; + return; + } + + // add to the beginning + if (before == alist->first) { + struct attr *oldfirst; + + oldfirst = alist->first; + alist->first = a; + oldfirst->prev = a; + a->next = oldfirst; + return; + } + + // add to the middle + a->prev = before->prev; + a->next = before; + before->prev = a; + a->prev->next = a; +} + void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, size_t start, size_t end) { struct attr *a; @@ -25,28 +64,10 @@ void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, s a->start = start; a->end = end; - if (alist->first == NULL) { // empty list - alist->first = a; - alist->last = a; - return; - } - // place a before the first element that starts after a does for (before = alist->first; before != NULL; before = before->next) if (before->start > a->start) break; - if (before == NULL) { // if there is none, add a to the end - alist->last->next = a; - a->prev = alist->last; - alist->last = a; - return; - } - - if (before == alist->first) - alist->first = a; - else // not the first; hook up our new prev - before->prev->next = a; - a->next = before; - a->prev = before->prev; - before->prev = a; + // TODO this does not handle cases where the attribute overwrites an existing attribute + linkInsertBefore(alist, a, before); } From 2f449887d880b28379e5df131d3619fca411abb7 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Dec 2016 12:23:09 -0500 Subject: [PATCH 0516/1329] More TODOs. --- common/exp_attrdll.c | 1 + 1 file changed, 1 insertion(+) diff --git a/common/exp_attrdll.c b/common/exp_attrdll.c index 2dfa7440..45a30a24 100644 --- a/common/exp_attrdll.c +++ b/common/exp_attrdll.c @@ -70,4 +70,5 @@ void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, s break; // TODO this does not handle cases where the attribute overwrites an existing attribute linkInsertBefore(alist, a, before); + // TODO see if adding this attribute leads to a fragmented contiguous run } From 4c99899a1d5baa7d68ff1ade47c5c95f7377138c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Dec 2016 23:07:48 -0500 Subject: [PATCH 0517/1329] More work. Much clearer now... not yet complete though. --- common/exp_attrdll.c | 103 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 100 insertions(+), 3 deletions(-) diff --git a/common/exp_attrdll.c b/common/exp_attrdll.c index 45a30a24..51c1a90c 100644 --- a/common/exp_attrdll.c +++ b/common/exp_attrdll.c @@ -53,10 +53,36 @@ static void linkInsertBefore(struct attrlist *alist, struct attr *what, struct a a->prev->next = a; } +static int attrHasPos(struct attr *a, size_t pos) +{ + if (pos < a->start) + return 0; + return pos < a->end; +} + +// returns 1 if there was an intersection and 0 otherwise +static int attrRangeIntersect(struct attr *a, size_t *start, size_t *end) +{ + // is the range outside a entirely? + if (*start >= a->end) + return 0; + if (*end < a->start) + return 0; + + // okay, so there is an overlap + // compute the intersection + if (*start < a->start) + *start = a->start; + if (*end > a->end) + *end = a->end; + return 1; +} + +#if 0 void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, size_t start, size_t end) { struct attr *a; - struct attr *after; + struct attr *before; a = uiNew(struct attr); a->type = type; @@ -65,10 +91,81 @@ void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, s a->end = end; // place a before the first element that starts after a does - for (before = alist->first; before != NULL; before = before->next) + for (before = alist->first; before != NULL; before = before->next) { + size_t lstart, lend; + if (before->start > a->start) break; - // TODO this does not handle cases where the attribute overwrites an existing attribute + // if this attribute overrides another, we have to split + lstart = start; + lend = end; + if (!attrRangeIntersect(before, &lstart, &lend)) + continue; + if (before->type != type) + continue; + if (before->val == val) + continue; + // TODO now split around start/end and drop the overlap + } linkInsertBefore(alist, a, before); // TODO see if adding this attribute leads to a fragmented contiguous run } +#endif + +void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, size_t start, size_t end) +{ + struct attr *a; + struct attr *before; + struct attr *tail = NULL; + + // first, figure out where in the list this should go + // in addition, if this attribute overrides one that already exists, split that one apart so this one can take over + before = alist->first; + while (before != NULL) { + size_t lstart, lend; + + // once we get to the first point after start, we know where to insert + if (before->start > start) + break; + + // if we have already split a prior instance of this attribute, don't bother doing it again + if (tail != NULL) + goto next; + + // should we split this? + if (before->type != type) + goto next; + lstart = start; + lend = end; + if (!attrRangeIntersects(before, &lstart, &lend)) + goto next; + + // okay so this might conflict; if the val is the same as the one we want, we need to expand the existing attribute, not fragment anything + // TODO this might cause problems with system specific attributes, if we support those; maybe also user-specific? + if (before->val == val) { + // TODO + } + // okay the values are different; we need to split apart + // TODO + + next: + before = before->next; + } + + // if we got here, we know we have to add the attribute before before + a = uiNew(struct attr); + a->type = type; + a->val = val; + a->start = start; + a->end = end; + linkInsertBefore(alist, a, before); + + // and finally, if we split, insert the remainder + if (tail == NULL) + return; + // note we start at before; it won't be inserted before that by the sheer nature of how the code above works + for (; before != NULL; before = before->next) + if (before->start > tail->start) + break; + linkInsertBefore(alist, tail, before); +} From 75525196b114e69b86647c4f5232e7de4e2f5d9b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 18 Dec 2016 11:49:54 -0500 Subject: [PATCH 0518/1329] Even more expansion of the experimental attribute list. --- common/exp_attrdll.c | 134 +++++++++++++++++++++++++++++++++---------- 1 file changed, 103 insertions(+), 31 deletions(-) diff --git a/common/exp_attrdll.c b/common/exp_attrdll.c index 51c1a90c..138b4643 100644 --- a/common/exp_attrdll.c +++ b/common/exp_attrdll.c @@ -15,7 +15,7 @@ struct attrlist { }; // if before is NULL, add to the end of the list -static void linkInsertBefore(struct attrlist *alist, struct attr *what, struct attr *before) +static void linkInsertBefore(struct attrlist *alist, struct attr *a, struct attr *before) { // if the list is empty, this is the first item if (alist->first == NULL) { @@ -78,45 +78,114 @@ static int attrRangeIntersect(struct attr *a, size_t *start, size_t *end) return 1; } -#if 0 -void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, size_t start, size_t end) +static void attrUnlink(struct attrlist *alist, struct attr *a, struct attr **next) { - struct attr *a; - struct attr *before; + struct attr *p, *n; - a = uiNew(struct attr); - a->type = type; - a->val = val; - a->start = start; - a->end = end; + p = a->prev; + n = a->next; + a->prev = NULL; + a->next = NULL; - // place a before the first element that starts after a does - for (before = alist->first; before != NULL; before = before->next) { - size_t lstart, lend; - - if (before->start > a->start) - break; - // if this attribute overrides another, we have to split - lstart = start; - lend = end; - if (!attrRangeIntersect(before, &lstart, &lend)) - continue; - if (before->type != type) - continue; - if (before->val == val) - continue; - // TODO now split around start/end and drop the overlap + // only item in list? + if (p == NULL && n == NULL) { + alist->first = NULL; + alist->last = NULL; + *next = NULL; + return; } - linkInsertBefore(alist, a, before); - // TODO see if adding this attribute leads to a fragmented contiguous run + // start of list? + if (p == NULL) { + n->prev = NULL; + alist->first = n; + *next = n; + return; + } + // end of list? + if (n == NULL) { + p->next = NULL; + alist->last = p; + *next = NULL; + return; + } + // middle of list + p->next = n; + n->prev = p; + *next = n; +} + +static void attrDelete(struct attrlist *alist, struct attr *a, struct attr **next) +{ + attrUnlink(alist, a, next); + uiFree(a); +} + +// attrDropRange() removes attributes without deleting characters. +// +// If the attribute needs no change, 1 is returned. +// +// If the attribute needs to be deleted, it is deleted and 0 is returned. +// +// If the attribute only needs to be resized at the end, it is adjusted and 1 is returned. +// +// If the attribute only needs to be resized at the start, it is adjusted, unlinked, and returned in tail, and 1 is returned. +// +// Otherwise, the attribute needs to be split. The existing attribute is adjusted to make the left half and a new attribute with the right half. This attribute is kept unlinked and returned in tail. Then, 2 is returned. +// +// In all cases, next is set to the next attribute to look at in a forward sequential loop. +// +// TODO is the return value relevant? +static int attrDropRange(struct attrlist *alist, struct attr *a, size_t start, size_t end, struct attr **tail, struct attr **next) +{ + struct attr *b; + + // always pre-initialize tail to NULL + *tail = NULL; + + if (!attrRangeIntersect(a, &start, &end)) { + // out of range; nothing to do + *next = a->next; + return 1; + } + + // just outright delete the attribute? + if (a->start == start && a->end == end) { + attrDelete(alist, a, next); + return 0; + } + + // only chop off the start or end? + if (a->start == start) { // chop off the end + a->end = end; + *next = a->next; + return 1; + } + if (a->end == end) { // chop off the start + a->start = start; + attrUnlink(attr, a, next); + *tail = a; + return 1; + } + + // we'll need to split the attribute into two + b = uiNew(struct attr); + b->type = a->type; + b->val = a->val; + b->start = end; + b->end = a->end; + *tail = b; + + a->end = start; + *next = a->next; + return 2; } -#endif void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, size_t start, size_t end) { struct attr *a; struct attr *before; struct attr *tail = NULL; + int split = 0; // first, figure out where in the list this should go // in addition, if this attribute overrides one that already exists, split that one apart so this one can take over @@ -129,7 +198,7 @@ void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, s break; // if we have already split a prior instance of this attribute, don't bother doing it again - if (tail != NULL) + if (split) goto next; // should we split this? @@ -144,9 +213,12 @@ void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, s // TODO this might cause problems with system specific attributes, if we support those; maybe also user-specific? if (before->val == val) { // TODO + return; } // okay the values are different; we need to split apart - // TODO + attrDropRange(alist, a, start, end, &tail, &before); + split = 1; + continue; next: before = before->next; From 5c96266c7c69219bab7a88dd1488e9b17e679491 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 18 Dec 2016 14:06:37 -0500 Subject: [PATCH 0519/1329] And finished implementing attrlistInsertAt(). Woo! --- common/exp_attrdll.c | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/common/exp_attrdll.c b/common/exp_attrdll.c index 138b4643..531a2f65 100644 --- a/common/exp_attrdll.c +++ b/common/exp_attrdll.c @@ -15,7 +15,7 @@ struct attrlist { }; // if before is NULL, add to the end of the list -static void linkInsertBefore(struct attrlist *alist, struct attr *a, struct attr *before) +static void attrInsertBefore(struct attrlist *alist, struct attr *a, struct attr *before) { // if the list is empty, this is the first item if (alist->first == NULL) { @@ -180,6 +180,29 @@ static int attrDropRange(struct attrlist *alist, struct attr *a, size_t start, s return 2; } +static void attrGrow(struct attrlist *alist, struct attr *a, size_t start, size_t end) +{ + struct attr *before; + struct attr *unused; + + // adjusting the end is simple: if it ends before our new end, just set the new end + if (a->end < end) + a->end = end; + + // adjusting the start is harder + // if the start is before our new start, we are done + // otherwise, we have to move the start back AND reposition the attribute to keep the sorted order + if (a->start <= start) + return; + a->start = start; + // TODO could we just return next? + attrUnlink(alist, a, &unused); + for (before = alist->first; before != NULL; before = before->next) + if (before->start > a->start) + break; + attrInsertBefore(alist, a, before); +} + void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, size_t start, size_t end) { struct attr *a; @@ -211,8 +234,9 @@ void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, s // okay so this might conflict; if the val is the same as the one we want, we need to expand the existing attribute, not fragment anything // TODO this might cause problems with system specific attributes, if we support those; maybe also user-specific? + // TODO will this cause problems with fonts? if (before->val == val) { - // TODO + attrGrow(alist, a, start, end); return; } // okay the values are different; we need to split apart @@ -230,7 +254,7 @@ void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, s a->val = val; a->start = start; a->end = end; - linkInsertBefore(alist, a, before); + attrInsertBefore(alist, a, before); // and finally, if we split, insert the remainder if (tail == NULL) @@ -239,5 +263,5 @@ void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, s for (; before != NULL; before = before->next) if (before->start > tail->start) break; - linkInsertBefore(alist, tail, before); + attrInsertBefore(alist, tail, before); } From 96e15116ba0a8cb62f3ff49a0ce95ea1c221be54 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 18 Dec 2016 14:11:12 -0500 Subject: [PATCH 0520/1329] Added some expository information about attribute lists. --- common/exp_attrdll.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/common/exp_attrdll.c b/common/exp_attrdll.c index 531a2f65..c4083e28 100644 --- a/common/exp_attrdll.c +++ b/common/exp_attrdll.c @@ -1,5 +1,13 @@ // 16 december 2016 +/* +An attribute list is a doubly linked list of attributes. +The list is kept sorted in increasing order by start position. Whether or not the sort is stable is undefined, so no temporal information should be expected to stay. +Overlapping attributes are not allowed; if an attribute is added that conflicts with an existing one, the existing one is removed. +In addition, the linked list tries to reduce fragmentation: if an attribute is added that just expands another, then there will only be one entry in alist, not two. (TODO does it really?) +The linked list is not a ring; alist->fist->prev == NULL and alist->last->next == NULL. +*/ + struct attr { uiAttribute type; uintptr_t val; @@ -235,6 +243,7 @@ void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, s // okay so this might conflict; if the val is the same as the one we want, we need to expand the existing attribute, not fragment anything // TODO this might cause problems with system specific attributes, if we support those; maybe also user-specific? // TODO will this cause problems with fonts? + // TODO will this reduce fragmentation if we first add from 0 to 2 and then from 2 to 4? or do we have to do that separately? if (before->val == val) { attrGrow(alist, a, start, end); return; From 7ebfe73bce8f913319c36894ca3dbb5f9f645f5a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 19 Dec 2016 11:02:33 -0500 Subject: [PATCH 0521/1329] Some minor cleanup. --- common/exp_attrdll.c | 65 +++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 37 deletions(-) diff --git a/common/exp_attrdll.c b/common/exp_attrdll.c index c4083e28..c9cd1c68 100644 --- a/common/exp_attrdll.c +++ b/common/exp_attrdll.c @@ -86,7 +86,8 @@ static int attrRangeIntersect(struct attr *a, size_t *start, size_t *end) return 1; } -static void attrUnlink(struct attrlist *alist, struct attr *a, struct attr **next) +// returns the old a->next, for forward iteration +static struct attr *attrUnlink(struct attrlist *alist, struct attr *a) { struct attr *p, *n; @@ -99,80 +100,73 @@ static void attrUnlink(struct attrlist *alist, struct attr *a, struct attr **nex if (p == NULL && n == NULL) { alist->first = NULL; alist->last = NULL; - *next = NULL; - return; + return NULL; } // start of list? if (p == NULL) { n->prev = NULL; alist->first = n; - *next = n; - return; + return n; } // end of list? if (n == NULL) { p->next = NULL; alist->last = p; - *next = NULL; - return; + return NULL; } // middle of list p->next = n; n->prev = p; - *next = n; + return n; } -static void attrDelete(struct attrlist *alist, struct attr *a, struct attr **next) +// returns the old a->next, for forward iteration +static struct attr *attrDelete(struct attrlist *alist, struct attr *a) { - attrUnlink(alist, a, next); + struct attr *next; + + next = attrUnlink(alist, a); uiFree(a); + return next; } // attrDropRange() removes attributes without deleting characters. // -// If the attribute needs no change, 1 is returned. +// If the attribute needs no change, then nothing is done. // -// If the attribute needs to be deleted, it is deleted and 0 is returned. +// If the attribute needs to be deleted, it is deleted. // -// If the attribute only needs to be resized at the end, it is adjusted and 1 is returned. +// If the attribute only needs to be resized at the end, it is adjusted. // -// If the attribute only needs to be resized at the start, it is adjusted, unlinked, and returned in tail, and 1 is returned. +// If the attribute only needs to be resized at the start, it is adjusted, unlinked, and returned in tail. // -// Otherwise, the attribute needs to be split. The existing attribute is adjusted to make the left half and a new attribute with the right half. This attribute is kept unlinked and returned in tail. Then, 2 is returned. +// Otherwise, the attribute needs to be split. The existing attribute is adjusted to make the left half and a new attribute with the right half. This attribute is kept unlinked and returned in tail. // -// In all cases, next is set to the next attribute to look at in a forward sequential loop. -// -// TODO is the return value relevant? -static int attrDropRange(struct attrlist *alist, struct attr *a, size_t start, size_t end, struct attr **tail, struct attr **next) +// In all cases, the return value is the next attribute to look at in a forward sequential loop. +static struct attr *attrDropRange(struct attrlist *alist, struct attr *a, size_t start, size_t end, struct attr **tail) { struct attr *b; // always pre-initialize tail to NULL *tail = NULL; - if (!attrRangeIntersect(a, &start, &end)) { + if (!attrRangeIntersect(a, &start, &end)) // out of range; nothing to do - *next = a->next; - return 1; - } + return a->next; // just outright delete the attribute? - if (a->start == start && a->end == end) { - attrDelete(alist, a, next); - return 0; - } + if (a->start == start && a->end == end) + return attrDelete(alist, a); // only chop off the start or end? if (a->start == start) { // chop off the end a->end = end; - *next = a->next; - return 1; + return a->next; } if (a->end == end) { // chop off the start a->start = start; - attrUnlink(attr, a, next); *tail = a; - return 1; + return attrUnlink(attr, a); } // we'll need to split the attribute into two @@ -184,14 +178,12 @@ static int attrDropRange(struct attrlist *alist, struct attr *a, size_t start, s *tail = b; a->end = start; - *next = a->next; - return 2; + return a->next; } static void attrGrow(struct attrlist *alist, struct attr *a, size_t start, size_t end) { struct attr *before; - struct attr *unused; // adjusting the end is simple: if it ends before our new end, just set the new end if (a->end < end) @@ -203,8 +195,7 @@ static void attrGrow(struct attrlist *alist, struct attr *a, size_t start, size_ if (a->start <= start) return; a->start = start; - // TODO could we just return next? - attrUnlink(alist, a, &unused); + attrUnlink(alist, a); for (before = alist->first; before != NULL; before = before->next) if (before->start > a->start) break; @@ -249,7 +240,7 @@ void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, s return; } // okay the values are different; we need to split apart - attrDropRange(alist, a, start, end, &tail, &before); + before = attrDropRange(alist, a, start, end, &tail); split = 1; continue; From fee06b95532fb73d52096c17055e21baaddefe27 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 19 Dec 2016 11:07:52 -0500 Subject: [PATCH 0522/1329] Wrote the prototypes for the other functions I will need. --- common/exp_attrdll.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/common/exp_attrdll.c b/common/exp_attrdll.c index c9cd1c68..11e70d32 100644 --- a/common/exp_attrdll.c +++ b/common/exp_attrdll.c @@ -265,3 +265,25 @@ void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, s break; attrInsertBefore(alist, tail, before); } + +void attrlistInsertCharacters(struct attrlist *alist, size_t start, size_t end) +{ +} + +// The attributes are those of character start - 1. +// If start == 0, the attributes are those of character 0. +void attrlistInsertCharactersExtendingAttributes(struct attrlist *alist, size_t start, size_t end) +{ +} + +void attrlistRemoveAttribute(struct attrlist *alist, uiAttribute type, size_t start, size_t end) +{ +} + +void attrlistRemoveAttributes(struct attrlist *alist, size_t start, size_t end) +{ +} + +void attrlistRemoveCharacters(struct attrlist *alist, size_t start, size_t end) +{ +} From 78f5ca5eb5ff0b44693144e160f52b8f88951e93 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 19 Dec 2016 13:46:20 -0500 Subject: [PATCH 0523/1329] Implemented attrlistInsertCharactersUnattributed(). --- common/exp_attrdll.c | 72 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 2 deletions(-) diff --git a/common/exp_attrdll.c b/common/exp_attrdll.c index 11e70d32..0340d744 100644 --- a/common/exp_attrdll.c +++ b/common/exp_attrdll.c @@ -202,6 +202,29 @@ static void attrGrow(struct attrlist *alist, struct attr *a, size_t start, size_ attrInsertBefore(alist, a, before); } +// returns the right side of the split, which is unlinked, or NULL if no split was done +static struct attr *attrSplitAt(struct attrlist *alist, struct attr *a, size_t at) +{ + struct attr *b; + + // no splittng needed? + // note the equality: we don't need to split at start or end + // in the end case, the last split point is at - 1; at itself is outside the range, and at - 1 results in the right hand side having length 1 + if (at <= a->start) + return NULL; + if (at >= a->end) + return NULL; + + b = uiNew(struct attr); + b->type = a->type; + b->val = a->val; + b->start = at; + b->end = a->end; + + a->end = at; + return b; +} + void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, size_t start, size_t end) { struct attr *a; @@ -266,13 +289,58 @@ void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, s attrInsertBefore(alist, tail, before); } -void attrlistInsertCharacters(struct attrlist *alist, size_t start, size_t end) +void attrlistInsertCharactersUnattributed(struct attrlist *alist, size_t start, size_t count) { + struct attr *a; + struct attr *tails = NULL; + + // every attribute before the insertion point can either cross into the insertion point or not + // if it does, we need to split that attribute apart at the insertion point, keeping only the old attribute in place, adjusting the new tail, and preparing it for being re-added later + for (a = alist->first; a != NULL; a = a->next) { + struct attr *tail; + + // stop once we get to the insertion point + if (a->start >= start) + break; + // only do something if overlapping + if (!attrHasPos(a, start)) + continue; + + tail = attrSplitAt(alist, a, start); + // adjust the new tail for the insertion + tail->start += count; + tail->end += count; + // and queue it for re-adding later + // we can safely use tails as if it was singly-linked since it's just a temporary list; we properly merge them back in below and they'll be doubly-linked again then + // TODO actually we could probably save some time by making then doubly-linked now and adding them in one fell swoop, but that would make things a bit more complicated... + tail->next = tails; + tails = tail; + } + + // at this point in the attribute list, we are at or after the insertion point + // all the split-apart attributes will be at the insertion point + // therefore, we can just add them all back now, and the list will still be sorted correctly + while (tails != NULL) { + struct attr *next; + + // make all the links NULL before insertion, just to be safe + next = tails->next; + tails->next = NULL; + attrInsertBefore(alist, tails, a); + tails = next; + } + + // every remaining attribute will be either at or after the insertion point + // we just need to move them ahead + for (; a != NULL; a = a->next) { + a->start += count; + a->end += count; + } } // The attributes are those of character start - 1. // If start == 0, the attributes are those of character 0. -void attrlistInsertCharactersExtendingAttributes(struct attrlist *alist, size_t start, size_t end) +void attrlistInsertCharactersExtendingAttributes(struct attrlist *alist, size_t start, size_t count) { } From 643e2937c42110f35949c3debe191a48666ad36c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 19 Dec 2016 17:15:48 -0500 Subject: [PATCH 0524/1329] More stuff I guess? --- common/exp_attrdll.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/common/exp_attrdll.c b/common/exp_attrdll.c index 0340d744..45717b54 100644 --- a/common/exp_attrdll.c +++ b/common/exp_attrdll.c @@ -342,6 +342,13 @@ void attrlistInsertCharactersUnattributed(struct attrlist *alist, size_t start, // If start == 0, the attributes are those of character 0. void attrlistInsertCharactersExtendingAttributes(struct attrlist *alist, size_t start, size_t count) { + size_t from; + + from = start - 1; + if (start == 0) + from = 0; + + TODO } void attrlistRemoveAttribute(struct attrlist *alist, uiAttribute type, size_t start, size_t end) From 4f1ba0df84f990cb1dfe27869067166cf7465c48 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 20 Dec 2016 14:24:26 -0500 Subject: [PATCH 0525/1329] Plotted out at a high level how insertion with attribute borrowing should work. --- common/exp_attrdll.c | 50 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/common/exp_attrdll.c b/common/exp_attrdll.c index 45717b54..372110ed 100644 --- a/common/exp_attrdll.c +++ b/common/exp_attrdll.c @@ -347,8 +347,54 @@ void attrlistInsertCharactersExtendingAttributes(struct attrlist *alist, size_t from = start - 1; if (start == 0) from = 0; - - TODO +///////// + if start == 0 + for every attribute + if it doesn't start at 0 + move start up by count + move end up by count + return + for every attribute + if the attribute ends at or after the insertion point + advance its end by count + continue + if the attribute starts at or after the insertion point + advance its start and end by count + continue +///// +abcdefghi (012345678 9) +red start 0 end 3 +bold start 2 end 6 +underline start 5 end 8 +inserting qwe -> qweabcdefghi (0123456789AB C) +012345678 9 +rrr------ +--bbbb--- +-----uuu- +before 0, 1, 2 (grow the red part, move everything else down) + red -> start 0 (no change) end 3+3=6 + bold -> start 2+3=5 end 6+3=9 + underline -> start 5+3=8 end 8+3=B +before 3 (grow red and bold, move underline down) + red -> start 0 (no change) end 3+3=6 + bold -> start 2 (no change) end 6+3=9 + underline -> start 5+3=8 end 8+3=B +before 4, 5 (keep red, grow bold, move underline down) + red -> start 0 (no change) end 3 (no change) + bold -> start 2 (no change) end 6+3=9 + underline -> start 5+3=8 end 8+3=B +before 6 (keep red, grow bold and underline) + red -> start 0 (no change) end 3 (no change) + bold -> start 2 (no change) end 6+3=9 + underline -> start 5 (no change) end 8+3=B +before 7, 8 (keep red and bold, grow underline) + red -> start 0 (no change) end 3 (no change) + bold -> start 2 (no change) end 6 (no change) + underline -> start 5 (no change) end 8+3=B +before 9 (keep all three) + red -> start 0 (no change) end 3 (no change) + bold -> start 2 (no change) end 6 (no change) + underline -> start 5 (no change) end 8 (no change) } void attrlistRemoveAttribute(struct attrlist *alist, uiAttribute type, size_t start, size_t end) From 28ca02673b01e035828f3555dc2aa9078dd9e9ce Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 20 Dec 2016 14:47:11 -0500 Subject: [PATCH 0526/1329] Even more trying to reason about the algorithm. --- common/exp_attrdll.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/common/exp_attrdll.c b/common/exp_attrdll.c index 372110ed..e03aef99 100644 --- a/common/exp_attrdll.c +++ b/common/exp_attrdll.c @@ -348,20 +348,13 @@ void attrlistInsertCharactersExtendingAttributes(struct attrlist *alist, size_t if (start == 0) from = 0; ///////// - if start == 0 - for every attribute - if it doesn't start at 0 - move start up by count - move end up by count - return for every attribute - if the attribute ends at or after the insertion point - advance its end by count - continue - if the attribute starts at or after the insertion point - advance its start and end by count - continue -///// + if TODO + adjust start by count + adjust end by count + else if TODO + adjust end by count +///////// abcdefghi (012345678 9) red start 0 end 3 bold start 2 end 6 @@ -395,6 +388,16 @@ before 9 (keep all three) red -> start 0 (no change) end 3 (no change) bold -> start 2 (no change) end 6 (no change) underline -> start 5 (no change) end 8 (no change) +result: + 0 1 2 3 4 5 6 7 8 9 + red E E E e n n n n n n + bold s s S E E E e n n n +underline s s s s s S E E e n +N = none +E = end only +S = start and end +uppercase = in original range, lowercase = not +*/ } void attrlistRemoveAttribute(struct attrlist *alist, uiAttribute type, size_t start, size_t end) From ecc7b70a6bcfd10acaaad1d69ff9784873850326 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 20 Dec 2016 16:16:52 -0500 Subject: [PATCH 0527/1329] And FINALLY implemented that function :D --- common/exp_attrdll.c | 62 ++++++++++++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/common/exp_attrdll.c b/common/exp_attrdll.c index e03aef99..2949e15b 100644 --- a/common/exp_attrdll.c +++ b/common/exp_attrdll.c @@ -340,30 +340,22 @@ void attrlistInsertCharactersUnattributed(struct attrlist *alist, size_t start, // The attributes are those of character start - 1. // If start == 0, the attributes are those of character 0. -void attrlistInsertCharactersExtendingAttributes(struct attrlist *alist, size_t start, size_t count) -{ - size_t from; +/* +This is an obtuse function. Here's some diagrams to help. + +Given the input string + abcdefghi (positions: 012345678 9) +and attribute set + red start 0 end 3 + bold start 2 end 6 + underline start 5 end 8 +or visually: + 012345678 9 + rrr------ + --bbbb--- + -----uuu- +If we insert qwe to result in positions 0123456789AB C: - from = start - 1; - if (start == 0) - from = 0; -///////// - for every attribute - if TODO - adjust start by count - adjust end by count - else if TODO - adjust end by count -///////// -abcdefghi (012345678 9) -red start 0 end 3 -bold start 2 end 6 -underline start 5 end 8 -inserting qwe -> qweabcdefghi (0123456789AB C) -012345678 9 -rrr------ ---bbbb--- ------uuu- before 0, 1, 2 (grow the red part, move everything else down) red -> start 0 (no change) end 3+3=6 bold -> start 2+3=5 end 6+3=9 @@ -388,6 +380,7 @@ before 9 (keep all three) red -> start 0 (no change) end 3 (no change) bold -> start 2 (no change) end 6 (no change) underline -> start 5 (no change) end 8 (no change) + result: 0 1 2 3 4 5 6 7 8 9 red E E E e n n n n n n @@ -397,7 +390,30 @@ N = none E = end only S = start and end uppercase = in original range, lowercase = not + +which results in our algorithm: + for each attribute + if start < insertion point + move start up + else if start == insertion point + if start != 0 + move start up + if end <= insertion point + move end up */ +// TODO does this ensure the list remains sorted? +void attrlistInsertCharactersExtendingAttributes(struct attrlist *alist, size_t start, size_t count) +{ + struct attr *a; + + for (a = alist->first; a != NULL; a = a->next) { + if (a->start < start) + a->start += count; + else if (a->start == start && start != 0) + a->start += count; + if (a->end <= start) + a->end += count; + } } void attrlistRemoveAttribute(struct attrlist *alist, uiAttribute type, size_t start, size_t end) From 0078615662419e9a08acfa3dcb9d959b77f20ac3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 22 Dec 2016 13:05:36 -0500 Subject: [PATCH 0528/1329] More attribute list work. --- common/exp_attrdll.c | 46 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/common/exp_attrdll.c b/common/exp_attrdll.c index 2949e15b..e0a20cc0 100644 --- a/common/exp_attrdll.c +++ b/common/exp_attrdll.c @@ -2,6 +2,7 @@ /* An attribute list is a doubly linked list of attributes. +Attribute start positions are inclusive and attribute end positions are exclusive (or in other words, [start, end)). The list is kept sorted in increasing order by start position. Whether or not the sort is stable is undefined, so no temporal information should be expected to stay. Overlapping attributes are not allowed; if an attribute is added that conflicts with an existing one, the existing one is removed. In addition, the linked list tries to reduce fragmentation: if an attribute is added that just expands another, then there will only be one entry in alist, not two. (TODO does it really?) @@ -251,7 +252,7 @@ void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, s goto next; lstart = start; lend = end; - if (!attrRangeIntersects(before, &lstart, &lend)) + if (!attrRangeIntersect(before, &lstart, &lend)) goto next; // okay so this might conflict; if the val is the same as the one we want, we need to expand the existing attribute, not fragment anything @@ -416,8 +417,51 @@ void attrlistInsertCharactersExtendingAttributes(struct attrlist *alist, size_t } } +// TODO replace at point with — replaces with first character's attributes + void attrlistRemoveAttribute(struct attrlist *alist, uiAttribute type, size_t start, size_t end) { + struct attr *a; + struct attr *tails = NULL; // see attrlistInsertCharactersUnattributed() above + struct attr *tailsAt = NULL; + + a = alist->start; + while (a != NULL) { + size_t lstart, lend; + + // this defines where to re-attach the tails + // (all the tails will have their start at end, so we can just insert them all before tailsAt) + if (a->start >= end) { + tailsAt = a; + // and at this point we're done, so + break; + } + if (a->type != type) + goto next; + lstart = start; + lend = end; + if (!attrRangeIntersect(a, &lstart, &lend)) + goto next; + a = attrDropRange(alist, a, start, end, &tail); + if (tail != NULL) { + tail->next = tails; + tails = tail; + } + continue; + + next: + a = a->next; + } + + while (tails != NULL) { + struct attr *next; + + // make all the links NULL before insertion, just to be safe + next = tails->next; + tails->next = NULL; + attrInsertBefore(alist, tails, a); + tails = next; + } } void attrlistRemoveAttributes(struct attrlist *alist, size_t start, size_t end) From ab8aa9266ec882f6377934411cbe41a658fb44a3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 22 Dec 2016 14:22:01 -0500 Subject: [PATCH 0529/1329] Finished filling in exp_attrdll.c. I think this will stay. --- common/exp_attrdll.c | 118 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 111 insertions(+), 7 deletions(-) diff --git a/common/exp_attrdll.c b/common/exp_attrdll.c index e0a20cc0..d8c9152d 100644 --- a/common/exp_attrdll.c +++ b/common/exp_attrdll.c @@ -156,19 +156,23 @@ static struct attr *attrDropRange(struct attrlist *alist, struct attr *a, size_t return a->next; // just outright delete the attribute? - if (a->start == start && a->end == end) + // the inequalities handle attributes entirely inside the range + // if both are equal, the attribute's range is equal to the range + if (a->start >= start && a->end <= end) return attrDelete(alist, a); // only chop off the start or end? - if (a->start == start) { // chop off the end - a->end = end; - return a->next; - } - if (a->end == end) { // chop off the start - a->start = start; + if (a->start == start) { // chop off the start + // we are dropping the left half, so set a->start and unlink + a->start = end; *tail = a; return attrUnlink(attr, a); } + if (a->end == end) { // chop off the end + // we are dropping the right half, so just set a->end + a->end = start; + return a->next; + } // we'll need to split the attribute into two b = uiNew(struct attr); @@ -226,6 +230,61 @@ static struct attr *attrSplitAt(struct attrlist *alist, struct attr *a, size_t a return b; } +// attrDeleteRange() removes attributes while deleting characters. +// +// If the attribute does not include the deleted range, then nothing is done (though the start and end are adjusted as necessary). +// +// If the attribute needs to be deleted, it is deleted. +// +// Otherwise, the attribute only needs the start or end deleted, and it is adjusted. +// +// In all cases, the return value is the next attribute to look at in a forward sequential loop. +// TODO rewrite this comment +static struct attr *attrDeleteRange(struct attrlist *alist, struct attr *a, size_t start, size_t end) +{ + size_t ostart, oend; + size_t count; + + ostart = start; + oend = end; + count = oend - ostart; + + if (!attrRangeIntersect(a, &start, &end)) { + // out of range + // adjust if necessary + if (a->start >= ostart) + a->start -= count; + if (a->end >= oend) + a->end -= count; + return a->next; + } + + // just outright delete the attribute? + // the inequalities handle attributes entirely inside the range + // if both are equal, the attribute's range is equal to the range + if (a->start >= start && a->end <= end) + return attrDelete(alist, a); + + // only chop off the start or end? + if (a->start == start) { // chop off the start + // if we weren't adjusting positions this would just be setting a->start to end + // but since this is deleting from the start, we need to adjust both by count + a->start = end - count; + a->end -= count; + return a->next; + } + if (a->end == end) { // chop off the end + // a->start is already good + a->end = start; + return a->next; + } + + // in this case, the deleted range is inside the attribute + // we can clear it by just removing count from a->end + a->end -= count; + return a->next; +} + void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, size_t start, size_t end) { struct attr *a; @@ -464,10 +523,55 @@ void attrlistRemoveAttribute(struct attrlist *alist, uiAttribute type, size_t st } } +// TODO merge this with the above void attrlistRemoveAttributes(struct attrlist *alist, size_t start, size_t end) { + struct attr *a; + struct attr *tails = NULL; // see attrlistInsertCharactersUnattributed() above + struct attr *tailsAt = NULL; + + a = alist->start; + while (a != NULL) { + size_t lstart, lend; + + // this defines where to re-attach the tails + // (all the tails will have their start at end, so we can just insert them all before tailsAt) + if (a->start >= end) { + tailsAt = a; + // and at this point we're done, so + break; + } + lstart = start; + lend = end; + if (!attrRangeIntersect(a, &lstart, &lend)) + goto next; + a = attrDropRange(alist, a, start, end, &tail); + if (tail != NULL) { + tail->next = tails; + tails = tail; + } + continue; + + next: + a = a->next; + } + + while (tails != NULL) { + struct attr *next; + + // make all the links NULL before insertion, just to be safe + next = tails->next; + tails->next = NULL; + attrInsertBefore(alist, tails, a); + tails = next; + } } void attrlistRemoveCharacters(struct attrlist *alist, size_t start, size_t end) { + struct attr *a; + + a = alist->first; + while (a != NULL) + a = attrDeleteRange(alist, a, start, end); } From 5c1dfbd86f8ad70653296b0e8fcee1e316fc8597 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 22 Dec 2016 14:33:30 -0500 Subject: [PATCH 0530/1329] Got rid of the old attribute code and integrated exp_attrdll.c into attrstr.c. I'll rename the file to attrlist.c later. --- common/attrstr.c | 311 +------------------------------------------ common/exp_attrdll.c | 20 ++- 2 files changed, 24 insertions(+), 307 deletions(-) diff --git a/common/attrstr.c b/common/attrstr.c index 224b6074..822e55bb 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -6,9 +6,7 @@ struct uiAttributedString { char *s; size_t len; - size_t nAttrs; // TODO this needs to be maintained; is it necessary? - struct attr *attrs; - struct attr *lastattr; + struct attrlist *attrs; // indiscriminately keep a UTF-16 copy of the string on all platforms so we can hand this off to the grapheme calculator // this ensures no one platform has a speed advantage (sorry GTK+) @@ -22,299 +20,6 @@ struct uiAttributedString { struct graphemes *graphemes; }; -struct attr { - uiAttribute type; - uintptr_t val; - size_t start; - size_t end; - struct attr *next; -}; - -// if new entries types are added to the end of the uiAttribute enumeration, this MUST be updated! -#define nAttrTypes (TODO + 1) - -static int attrHasPos(struct attr *a, size_t pos) -{ - if (pos < a->start) - return 0; - return pos < a->end; -} - -// returns 1 if there was an intersection and 0 otherwise -static int attrRangeIntersect(struct attr *a, size_t *start, size_t *end) -{ - // is the range outside a entirely? - if (*start >= a->end) - return 0; - if (*end < a->start) - return 0; - - // okay, so there is an overlap - // compute the intersection - if (*start < a->start) - *start = a->start; - if (*end > a->end) - *end = a->end; - return 1; -} - -// returns: -// - 0 if no change needed -// - 1 if the attribute was split -static int attrSplitAt(uiAttributedString *s, struct attr *a, size_t at) -{ - struct attr *b; - - // no splittng needed? - if (at == a->start) - return 0; - if ((at + 1) == a->end) - return 0; - - b = uiNew(struct attr); - b->what = a->what; - b->val = a->val; - b->start = at; - b->end = a->end; - b->next = a->next; - - a->end = at; - a->next = b; - if (a == s->lastattr) - s->lastattr = a->next; - return 1; -} - -// removes attributes without deleting characters -// returns: -// - 0 if the attribute needs to be deleted -// - 1 if the attribute was changed or no change needed -// - 2 if the attribute was split -static int attrDropRange(uiAttributedString *s, struct attr *a, size_t start, size_t end) -{ - struct attr *b; - - if (!attrRangeIntersect(a, &start, &end)) - // out of range; nothing to do - return 1; - - // just outright delete the attribute? - if (a->start == start && a->end == end) - return 0; - - // only chop off the start or end? - if (a->start == start) { // chop off the end - a->end = end; - return 1; - } - if (a->end == end) { // chop off the start - a->start = start; - return 1; - } - - // we'll need to split the attribute into two - b = uiNew(struct attr); - b->what = a->what; - b->val = a->val; - b->start = end; - b->end = a->end; - b->next = a->next; - - a->end = start; - a->next = b; - if (a == s->lastattr) - s->lastattr = a->next; - return 2; -} - -// removes attributes while deleting characters -// returns: -// - 0 if the attribute needs to be deleted -// - 1 if the attribute was changed or no change needed -// - 2 if the attribute was split -static int attrDeleteRange(uiAttributedString *s, struct attr *a, size_t start, size_t end) -{ - struct attr *b; - struct attr tmp; - size_t count, acount; - - if (!attrRangeIntersect(a, &start, &end)) - // out of range; nothing to do - return 1; - - // just outright delete the attribute? - if (a->start == start && a->end == end) - return 0; - - acount = a->end - a->start; - count = end - start; - - // only the start or end deleted? - if (a->start == start) { // start deleted - a->end = a->start + (acount - count); - return 1; - } - if (a->end == end) { // end deleted - a->end = a->start + count; - return 1; - } - - // something in the middle deleted - // we ened to split the attribute into *three* - // first, split at the start of he deleted range - tmp.what = a->what; - tmp.val = a->val; - tmp.start = start; - tmp.end = a->end; - tmp.next = a->next; - - a->end = start; - a->next = &tmp; - - // now split at the end - b = uiNew(struct attr); - b->what = a->what; - b->val = a->val; - b->start = end; - b->end = a->end; - b->next = tmp.next; - - tmp.end = end; - tmp.next = b; - - // and now push b back to overwrite the deleted stuff - a->next = b; - b->start -= count; - b->end -= count; - if (a == s->lastattr) - s->lastattr = a->next; - return 2; -} - -// returns the attribute to continue with -static struct attr *attrDelete(uiAttributedString *s, struct attr *a, struct attr *prev) -{ - if (a == s->attrs) { - s->attrs = a->next; - uiFree(a); - return s->attrs; - } - if (a == s->lastattr) - s->lastattr = prev; - prev->next = a->next; - uiFree(a); - return prev->next; -} - -static void attrAppend(uiAttributedString *s, int type, uintptr_t val, size_t start, size_t end) -{ - struct attr *a; - - a = uiNew(struct attr); - a->type = type; - a->val = val; - a->start = start; - a->end = end; - if (s->attrs == NULL) { - s->attrs = a; - s->lastattr = a; - return; - } - s->lastattr->next = a; - s->lastattr = a; -} - -// alist should be struct attr *alist[nAttrTypes] -static void attrsGetFor(uiAttributedString *s, struct attr **alist, size_t at) -{ - int i; - struct attr *a; - - // we want the attributes for at - // these are the attributes of at - 1 - // if at == 0. then these are the attributes at 0 - if (at != 0) - at--; - - // make usre unset attributes are NULL - for (i = 0; i < nAttrTypes; i++) - alist[i] = NULL; - - for (a = s->attrs; a != NULL; a = a->next) { - if (!attrHasPos(a, at)) - continue; - alist[a->type] = a; - } -} - -// TODO have a routine that prunes overridden attributes for a given character from the list? merge it with the above? - -static void attrAdjustPostInsert(uiAttributedString *s, size_t start, size_t n, size_t oldlen) -{ - struct attr *a; - - for (a = s->attrs; a != NULL; a = a->next) { - size_t astart, aend; - int splitNeeded; - - // do we need to adjust this, and where? - astart = start; - aend = oldlen; - if (!attrRangeIntersect(a, &astart, &aend)) - continue; - - // if only part of the attribute falls in the moved area, we need to split at the insertion point and adjust both resultant attributes - // otherwise, we only adjust the original attribute - // split *before* adjusting so that the split is correct - splitNeeded = attrSplit(s, a, astart); - if (a->start >= start) - a->start += n; - if (a->end >= end) - a->end += n; - if (splitNeeded == 1) { - a = a->next; - if (a->start >= start) - a->start += n; - if (a->end >= end) - a->end += n; - } - } -} - -static void attrAdjustPostDelete(uiAttributedString *s, size_t start, size_t end) -{ - struct attr *a, *prev; - - a = s->attrs; - prev = NULL; - while (a != NULL) { - size_t astart, aend; - - // do we need to adjust this, and where? - astart = start; - aend = end; - if (!attrRangeIntersect(a, &astart, &aend)) { - prev = a; - a = a->next; - continue; - } - - switch (attrDeleteRange(s, a, astart, aend)) { - case 0: // delete - a = attrDelete(s, a, prev); - // keep prev unchanged - break; - case 2: // split - a = a->next; - // fall through - case 1: // existing only needed adjustment - prev = a; - a = a->next; - break; - } - } -} - static void resize(uiAttributedString *s, size_t u8, size_t u16) { s->len = u8; @@ -330,6 +35,7 @@ uiAttributedString *uiNewAttributedString(const char *initialString) uiAttributedString *s; s = uiNew(uiAttributedString); + s->attrs = attrlistNew(); uiAttributedStringAppendUnattributed(s, initialString); return s; } @@ -358,14 +64,7 @@ static void invalidateGraphemes(uiAttributedString *s) void uiFreeAttributedString(uiAttributedString *s) { - struct attr *a, *b; - - a = s->attrs; - while (a != NULL) { - b = a->next; - uiFree(a); - a = b; - } + attrlistFree(s->attrs); invalidateGraphemes(s); uiFree(s->u16tou8); uiFree(s->u8tou16); @@ -511,7 +210,7 @@ void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *s s->u16tou8[at16 + n16 + i] += n8; // and finally do the attributes - attrAdjustPostInsert(s, at, n8, oldlen); + attrlistInsertCharactersUnattributed(s->attrs, at, n8); } // TODO document that end is the first index that will be maintained @@ -566,7 +265,7 @@ void uiAttributedStringDelete(uiAttributedString *s, size_t start, size_t end) s->u16[start16 + count16] = 0; // fix up attributes - attrAdjustPostDelete(s, start, end); + attrlistRemoveCharacters(s->attrs, start, end); // and finally resize resize(s, start + count, start16 + count16); diff --git a/common/exp_attrdll.c b/common/exp_attrdll.c index d8c9152d..5dc2fc9b 100644 --- a/common/exp_attrdll.c +++ b/common/exp_attrdll.c @@ -285,7 +285,7 @@ static struct attr *attrDeleteRange(struct attrlist *alist, struct attr *a, size return a->next; } -void attrlistInsertAt(struct attrlist *alist, uiAttribute type, uintptr_t val, size_t start, size_t end) +void attrlistInsertAttribute(struct attrlist *alist, uiAttribute type, uintptr_t val, size_t start, size_t end) { struct attr *a; struct attr *before; @@ -575,3 +575,21 @@ void attrlistRemoveCharacters(struct attrlist *alist, size_t start, size_t end) while (a != NULL) a = attrDeleteRange(alist, a, start, end); } + +struct attrlist *attrlistNew(void) +{ + return uiNew(struct attrlist); +} + +void attrlistFree(struct attrlist *alist) +{ + struct attr *a, *next; + + a = alist->first; + while (a != NULL) { + next = a->next; + uiFree(a); + a = next; + } + uiFree(alist); +} From 4f6ed98e40a2aa04d6fdd74a043a3bac751a4dd3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 23 Dec 2016 00:31:11 -0500 Subject: [PATCH 0531/1329] And integrated the new attribute list implementation fully. --- common/{exp_attrdll.c => attrlist.c} | 2 ++ common/uipriv.h | 12 ++++++++++++ 2 files changed, 14 insertions(+) rename common/{exp_attrdll.c => attrlist.c} (99%) diff --git a/common/exp_attrdll.c b/common/attrlist.c similarity index 99% rename from common/exp_attrdll.c rename to common/attrlist.c index 5dc2fc9b..4f3afd24 100644 --- a/common/exp_attrdll.c +++ b/common/attrlist.c @@ -1,4 +1,6 @@ // 16 december 2016 +#include "../ui.h" +#include "uipriv.h" /* An attribute list is a doubly linked list of attributes. diff --git a/common/uipriv.h b/common/uipriv.h index 1fe00b16..f29f4f86 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -64,6 +64,18 @@ struct graphemes { extern int graphemesTakesUTF16(void); extern struct graphemes *graphemes(void *s, size_t len); +// attrlist.c +struct attrlist; +extern void attrlistInsertAttribute(struct attrlist *alist, uiAttribute type, uintptr_t val, size_t start, size_t end); +extern void attrlistInsertCharactersUnattributed(struct attrlist *alist, size_t start, size_t count); +extern void attrlistInsertCharactersExtendingAttributes(struct attrlist *alist, size_t start, size_t count); +extern void attrlistRemoveAttribute(struct attrlist *alist, uiAttribute type, size_t start, size_t end); +extern void attrlistRemoveAttributes(struct attrlist *alist, size_t start, size_t end); +extern; void attrlistRemoveCharacters(struct attrlist *alist, size_t start, size_t end) +// TODO move these to the top like everythng else +extern struct attrlist *attrlistNew(void); +extern void attrlistFree(struct attrlist *alist); + #ifdef __cplusplus } #endif From dfffc4c851db7c39398df7cdc3070ba2e4723ac4 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 23 Dec 2016 12:24:20 -0500 Subject: [PATCH 0532/1329] Started the API definition of the new uiDrawTextLayout. --- common/ui_attrstr.h | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/common/ui_attrstr.h b/common/ui_attrstr.h index a333fb78..f22fb3a4 100644 --- a/common/ui_attrstr.h +++ b/common/ui_attrstr.h @@ -28,3 +28,48 @@ _UI_EXTERN void uiAttributedStringDelete(uiAttributedString *s, size_t start, si _UI_EXTERN size_t uiAttributedStringNumGraphemes(uiAttributedString *s); _UI_EXTERN size_t uiAttributedStringByteIndexToGrapheme(uiAttributedString *s, size_t pos); _UI_EXTERN size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos); + +typedef struct uiDrawTextLayout uiDrawTextLayout; +typedef struct uiDrawTextLayoutLineMetrics uiDrawTextLayoutLineMetrics; +typedef struct uiDrawTextLayoutByteRangeRectangle uiDrawTextLayoutByteRangeRectangle; + +struct uiDrawTextLayoutLineMetrics { + double X; + double Y; + double Width; + double Ascent; + double Descent; + double Leading; +}; + +_UI_ENUM(uiDrawTextLayoutHitTestResult) { + uiDrawTextLayoutHitTestResultNowhere, + uiDrawTextLayoutHitTestResultOnLineTrailingWhitespace, + uiDrawTextLayoutHitTestResultOnCharacter, +}; + +struct uiDrawTextLayoutByteRangeRectangle { + int Line; + double X; + double Y; + double Width; + double Height; + size_t RealStart; + size_t RealEnd; +}; + +_UI_EXTERN uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, /* TODO default font */, double width); +_UI_EXTERN void uiDrawFreeTextLayout(uiDrawTextLayout *tl); +_UI_EXTERN void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y); +_UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height); +_UI_EXTERN int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl); +_UI_EXTERN void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end); +_UI_EXTERN void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m); +// TODO redo this? remove it entirely? +_UI_EXTERN void uiDrawTextLayoutByteIndexToGraphemeRect(uiDrawTextLayout *tl, size_t pos, int *line, double *x, double *y, double *width, double *height); +// TODO partial offset? +_UI_EXTERN uiDrawTextLayoutHitTestResult uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *byteIndex, int *line); +_UI_EXTERN void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r); +// TODO draw only a line? +// TODO other layout-specific attributes (alignment, wrapping, etc.)? +// TODO number of lines visible for clipping rect, range visible for clipping rect? From e409943a50062890c7221103cb226a10d6621c95 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 23 Dec 2016 14:01:09 -0500 Subject: [PATCH 0533/1329] One more support method before I can actually implement uiDrawTextLayout. Still need to figure out how I'm going to deal with fonts... --- common/attrlist.c | 11 +++++++++++ common/attrstr.c | 6 ++++++ common/ui_attrstr.h | 3 +++ common/uipriv.h | 3 ++- 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/common/attrlist.c b/common/attrlist.c index 4f3afd24..bae1203b 100644 --- a/common/attrlist.c +++ b/common/attrlist.c @@ -578,6 +578,17 @@ void attrlistRemoveCharacters(struct attrlist *alist, size_t start, size_t end) a = attrDeleteRange(alist, a, start, end); } +void attrlistForEach(struct attr *alist, uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data) +{ + struct attr *a; + + for (a = alist->first; a != NULL; a = a->next) + // TODO document this + // TODO should this be return 0 to break? + if ((*f)(s, a->type, a->val, a->start, a->end, data)) + break; +} + struct attrlist *attrlistNew(void) { return uiNew(struct attrlist); diff --git a/common/attrstr.c b/common/attrstr.c index 822e55bb..4cd447f6 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -294,3 +294,9 @@ size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos) pos = s->u16tou8[pos]; return pos; } + +// TODO introduce an iterator? +void uiAttributedStringForEachAttribute(uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data) +{ + attrlistForEach(s->attrs, s, f, data); +} diff --git a/common/ui_attrstr.h b/common/ui_attrstr.h index f22fb3a4..f623612d 100644 --- a/common/ui_attrstr.h +++ b/common/ui_attrstr.h @@ -4,6 +4,8 @@ _UI_ENUM(uiAttribute) { // TODO }; +typedef int (*uiAttributedStringForEachAttributeFunc)(uiAttributedString *, uiAttribute type, uintptr_t value, size_t start, size_t end, void *data); + // @role uiAttributedString constructor // uiNewAttributedString() creates a new uiAttributedString from // initialString. The string will be entirely unattributed. @@ -28,6 +30,7 @@ _UI_EXTERN void uiAttributedStringDelete(uiAttributedString *s, size_t start, si _UI_EXTERN size_t uiAttributedStringNumGraphemes(uiAttributedString *s); _UI_EXTERN size_t uiAttributedStringByteIndexToGrapheme(uiAttributedString *s, size_t pos); _UI_EXTERN size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos); +_UI_EXTERN void uiAttributedStringForEachAttribute(uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); typedef struct uiDrawTextLayout uiDrawTextLayout; typedef struct uiDrawTextLayoutLineMetrics uiDrawTextLayoutLineMetrics; diff --git a/common/uipriv.h b/common/uipriv.h index f29f4f86..70615d8f 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -71,7 +71,8 @@ extern void attrlistInsertCharactersUnattributed(struct attrlist *alist, size_t extern void attrlistInsertCharactersExtendingAttributes(struct attrlist *alist, size_t start, size_t count); extern void attrlistRemoveAttribute(struct attrlist *alist, uiAttribute type, size_t start, size_t end); extern void attrlistRemoveAttributes(struct attrlist *alist, size_t start, size_t end); -extern; void attrlistRemoveCharacters(struct attrlist *alist, size_t start, size_t end) +extern; void attrlistRemoveCharacters(struct attrlist *alist, size_t start, size_t end); +extern void attrlistForEach(struct attr *alist, uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); // TODO move these to the top like everythng else extern struct attrlist *attrlistNew(void); extern void attrlistFree(struct attrlist *alist); From 30ca879c14fbdc7a8348d0b08aae9af16d417d4b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 2 Jan 2017 20:11:15 -0500 Subject: [PATCH 0534/1329] More API stuff. --- common/CMakeLists.txt | 2 ++ common/ui_attrstr.h | 58 ++++++++++++++++++++++++++++++++++++++++++- ui.h | 32 ------------------------ 3 files changed, 59 insertions(+), 33 deletions(-) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index a4008fd1..fdace350 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -1,6 +1,8 @@ # 3 june 2016 list(APPEND _LIBUI_SOURCES + common/attrlist.c + common/attrstr.c common/areaevents.c common/control.c common/debug.c diff --git a/common/ui_attrstr.h b/common/ui_attrstr.h index f623612d..7bfa0af9 100644 --- a/common/ui_attrstr.h +++ b/common/ui_attrstr.h @@ -1,7 +1,12 @@ typedef struct uiAttributedString uiAttributedString; _UI_ENUM(uiAttribute) { + // TODO uiAttributeFamily, + // TODO uiAttributeSize, + uiAttributeWeight, // TODO + // TODO uiAttributeSystem, + // TODO uiAttributeCustom, }; typedef int (*uiAttributedStringForEachAttributeFunc)(uiAttributedString *, uiAttribute type, uintptr_t value, size_t start, size_t end, void *data); @@ -32,6 +37,53 @@ _UI_EXTERN size_t uiAttributedStringByteIndexToGrapheme(uiAttributedString *s, s _UI_EXTERN size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos); _UI_EXTERN void uiAttributedStringForEachAttribute(uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); +typedef struct uiDrawFontDescriptor uiDrawFontDescriptor; + +// TODO Minimum == 1? IIRC there is at least one font on OS X that actually has a weight of 0 +// TODO Maximum == 999? IIRC there is at least one font on OS X that actually has a weight of 1000 +_UI_ENUM(uiDrawTextWeight) { + uiDrawTextWeightMinimum = 0, + uiDrawTextWeightThin = 100, + uiDrawTextWeightUltraLight = 200, + uiDrawTextWeightLight = 300, + uiDrawTextWeightBook = 350, + uiDrawTextWeightNormal = 400, + uiDrawTextWeightMedium = 500, + uiDrawTextWeightSemiBold = 600, + uiDrawTextWeightBold = 700, + uiDrawTextWeightUltraBold = 800, + uiDrawTextWeightHeavy = 900, + uiDrawTextWeightUltraHeavy = 950, + uiDrawTextWeightMaximum = 1000, +}; + +_UI_ENUM(uiDrawTextItalic) { + uiDrawTextItalicNormal, + uiDrawTextItalicOblique, + uiDrawTextItalicItalic, +}; + +// TODO realign this so that Normal == 0? +_UI_ENUM(uiDrawTextStretch) { + uiDrawTextStretchUltraCondensed, + uiDrawTextStretchExtraCondensed, + uiDrawTextStretchCondensed, + uiDrawTextStretchSemiCondensed, + uiDrawTextStretchNormal, + uiDrawTextStretchSemiExpanded, + uiDrawTextStretchExpanded, + uiDrawTextStretchExtraExpanded, + uiDrawTextStretchUltraExpanded, +}; + +struct uiDrawFontDescriptor { + char *Family; + double Size; + uiDrawTextWeight Weight; + uiDrawTextItalic Italic; + uiDrawTextStretch Stretch; +}; + typedef struct uiDrawTextLayout uiDrawTextLayout; typedef struct uiDrawTextLayoutLineMetrics uiDrawTextLayoutLineMetrics; typedef struct uiDrawTextLayoutByteRangeRectangle uiDrawTextLayoutByteRangeRectangle; @@ -61,7 +113,11 @@ struct uiDrawTextLayoutByteRangeRectangle { size_t RealEnd; }; -_UI_EXTERN uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, /* TODO default font */, double width); +// TODO +// - allow creating a layout out of a substring +// - allow marking compositon strings +// - allow marking selections, even after creation +_UI_EXTERN uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescriptor *defaultFont, double width); _UI_EXTERN void uiDrawFreeTextLayout(uiDrawTextLayout *tl); _UI_EXTERN void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y); _UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height); diff --git a/ui.h b/ui.h index 5a2069e8..919f43d3 100644 --- a/ui.h +++ b/ui.h @@ -491,38 +491,6 @@ typedef struct uiDrawTextFont uiDrawTextFont; typedef struct uiDrawTextFontDescriptor uiDrawTextFontDescriptor; typedef struct uiDrawTextFontMetrics uiDrawTextFontMetrics; -_UI_ENUM(uiDrawTextWeight) { - uiDrawTextWeightThin, - uiDrawTextWeightUltraLight, - uiDrawTextWeightLight, - uiDrawTextWeightBook, - uiDrawTextWeightNormal, - uiDrawTextWeightMedium, - uiDrawTextWeightSemiBold, - uiDrawTextWeightBold, - uiDrawTextWeightUltraBold, - uiDrawTextWeightHeavy, - uiDrawTextWeightUltraHeavy, -}; - -_UI_ENUM(uiDrawTextItalic) { - uiDrawTextItalicNormal, - uiDrawTextItalicOblique, - uiDrawTextItalicItalic, -}; - -_UI_ENUM(uiDrawTextStretch) { - uiDrawTextStretchUltraCondensed, - uiDrawTextStretchExtraCondensed, - uiDrawTextStretchCondensed, - uiDrawTextStretchSemiCondensed, - uiDrawTextStretchNormal, - uiDrawTextStretchSemiExpanded, - uiDrawTextStretchExpanded, - uiDrawTextStretchExtraExpanded, - uiDrawTextStretchUltraExpanded, -}; - struct uiDrawTextFontDescriptor { const char *Family; double Size; From 31274bcbd2b26a33bcdfde002b3e3a0afbb62980 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 2 Jan 2017 23:53:31 -0500 Subject: [PATCH 0535/1329] Started implementing the new attributed string system on OS X. --- common/attrstr.c | 12 + common/uipriv.h | 4 + darwin/_old_drawtext.m | 655 +++++++++++++++++++++++++++ darwin/drawtext.m | 660 ++-------------------------- ui.h | 40 +- common/ui_attrstr.h => ui_attrstr.h | 1 + 6 files changed, 720 insertions(+), 652 deletions(-) create mode 100644 darwin/_old_drawtext.m rename common/ui_attrstr.h => ui_attrstr.h (98%) diff --git a/common/attrstr.c b/common/attrstr.c index 4cd447f6..102d56eb 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -300,3 +300,15 @@ void uiAttributedStringForEachAttribute(uiAttributedString *s, uiAttributedStrin { attrlistForEach(s->attrs, s, f, data); } + +// helpers for platform-specific code + +const uint16_t *attrstrUTF16(uiAttributedString *s) +{ + return s->u16; +} + +size_t attrstrUTF16LEn(uiAttributedString *s) +{ + return s->u16len; +} diff --git a/common/uipriv.h b/common/uipriv.h index 70615d8f..31d941ff 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -64,6 +64,10 @@ struct graphemes { extern int graphemesTakesUTF16(void); extern struct graphemes *graphemes(void *s, size_t len); +// attrstr.c +extern const uint16_t *attrstrUTF16(uiAttributedString *s); +extern size_t attrstrUTF16LEn(uiAttributedString *s); + // attrlist.c struct attrlist; extern void attrlistInsertAttribute(struct attrlist *alist, uiAttribute type, uintptr_t val, size_t start, size_t end); diff --git a/darwin/_old_drawtext.m b/darwin/_old_drawtext.m new file mode 100644 index 00000000..c376536a --- /dev/null +++ b/darwin/_old_drawtext.m @@ -0,0 +1,655 @@ +// 6 september 2015 +#import "uipriv_darwin.h" + +// TODO +#define complain(...) implbug(__VA_ARGS__) + +// TODO double-check that we are properly handling allocation failures (or just toll free bridge from cocoa) +struct uiDrawFontFamilies { + CFArrayRef fonts; +}; + +uiDrawFontFamilies *uiDrawListFontFamilies(void) +{ + uiDrawFontFamilies *ff; + + ff = uiNew(uiDrawFontFamilies); + ff->fonts = CTFontManagerCopyAvailableFontFamilyNames(); + if (ff->fonts == NULL) + implbug("error getting available font names (no reason specified) (TODO)"); + return ff; +} + +int uiDrawFontFamiliesNumFamilies(uiDrawFontFamilies *ff) +{ + return CFArrayGetCount(ff->fonts); +} + +char *uiDrawFontFamiliesFamily(uiDrawFontFamilies *ff, int n) +{ + CFStringRef familystr; + char *family; + + familystr = (CFStringRef) CFArrayGetValueAtIndex(ff->fonts, n); + // toll-free bridge + family = uiDarwinNSStringToText((NSString *) familystr); + // Get Rule means we do not free familystr + return family; +} + +void uiDrawFreeFontFamilies(uiDrawFontFamilies *ff) +{ + CFRelease(ff->fonts); + uiFree(ff); +} + +struct uiDrawTextFont { + CTFontRef f; +}; + +uiDrawTextFont *mkTextFont(CTFontRef f, BOOL retain) +{ + uiDrawTextFont *font; + + font = uiNew(uiDrawTextFont); + font->f = f; + if (retain) + CFRetain(font->f); + return font; +} + +uiDrawTextFont *mkTextFontFromNSFont(NSFont *f) +{ + // toll-free bridging; we do retain, though + return mkTextFont((CTFontRef) f, YES); +} + +static CFMutableDictionaryRef newAttrList(void) +{ + CFMutableDictionaryRef attr; + + attr = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (attr == NULL) + complain("error creating attribute dictionary in newAttrList()()"); + return attr; +} + +static void addFontFamilyAttr(CFMutableDictionaryRef attr, const char *family) +{ + CFStringRef cfstr; + + cfstr = CFStringCreateWithCString(NULL, family, kCFStringEncodingUTF8); + if (cfstr == NULL) + complain("error creating font family name CFStringRef in addFontFamilyAttr()"); + CFDictionaryAddValue(attr, kCTFontFamilyNameAttribute, cfstr); + CFRelease(cfstr); // dictionary holds its own reference +} + +static void addFontSizeAttr(CFMutableDictionaryRef attr, double size) +{ + CFNumberRef n; + + n = CFNumberCreate(NULL, kCFNumberDoubleType, &size); + CFDictionaryAddValue(attr, kCTFontSizeAttribute, n); + CFRelease(n); +} + +#if 0 +TODO +// See http://stackoverflow.com/questions/4810409/does-coretext-support-small-caps/4811371#4811371 and https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c for what these do +// And fortunately, unlike the traits (see below), unmatched features are simply ignored without affecting the other features :D +static void addFontSmallCapsAttr(CFMutableDictionaryRef attr) +{ + CFMutableArrayRef outerArray; + CFMutableDictionaryRef innerDict; + CFNumberRef numType, numSelector; + int num; + + outerArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + if (outerArray == NULL) + complain("error creating outer CFArray for adding small caps attributes in addFontSmallCapsAttr()"); + + // Apple's headers say these are deprecated, but a few fonts still rely on them + num = kLetterCaseType; + numType = CFNumberCreate(NULL, kCFNumberIntType, &num); + num = kSmallCapsSelector; + numSelector = CFNumberCreate(NULL, kCFNumberIntType, &num); + innerDict = newAttrList(); + CFDictionaryAddValue(innerDict, kCTFontFeatureTypeIdentifierKey, numType); + CFRelease(numType); + CFDictionaryAddValue(innerDict, kCTFontFeatureSelectorIdentifierKey, numSelector); + CFRelease(numSelector); + CFArrayAppendValue(outerArray, innerDict); + CFRelease(innerDict); // and likewise for CFArray + + // these are the non-deprecated versions of the above; some fonts have these instead + num = kLowerCaseType; + numType = CFNumberCreate(NULL, kCFNumberIntType, &num); + num = kLowerCaseSmallCapsSelector; + numSelector = CFNumberCreate(NULL, kCFNumberIntType, &num); + innerDict = newAttrList(); + CFDictionaryAddValue(innerDict, kCTFontFeatureTypeIdentifierKey, numType); + CFRelease(numType); + CFDictionaryAddValue(innerDict, kCTFontFeatureSelectorIdentifierKey, numSelector); + CFRelease(numSelector); + CFArrayAppendValue(outerArray, innerDict); + CFRelease(innerDict); // and likewise for CFArray + + CFDictionaryAddValue(attr, kCTFontFeatureSettingsAttribute, outerArray); + CFRelease(outerArray); +} +#endif + +// Named constants for these were NOT added until 10.11, and even then they were added as external symbols instead of macros, so we can't use them directly :( +// kode54 got these for me before I had access to El Capitan; thanks to him. +#define ourNSFontWeightUltraLight -0.800000 +#define ourNSFontWeightThin -0.600000 +#define ourNSFontWeightLight -0.400000 +#define ourNSFontWeightRegular 0.000000 +#define ourNSFontWeightMedium 0.230000 +#define ourNSFontWeightSemibold 0.300000 +#define ourNSFontWeightBold 0.400000 +#define ourNSFontWeightHeavy 0.560000 +#define ourNSFontWeightBlack 0.620000 +static const CGFloat ctWeights[] = { + // yeah these two have their names swapped; blame Pango + [uiDrawTextWeightThin] = ourNSFontWeightUltraLight, + [uiDrawTextWeightUltraLight] = ourNSFontWeightThin, + [uiDrawTextWeightLight] = ourNSFontWeightLight, + // for this one let's go between Light and Regular + // we're doing nearest so if there happens to be an exact value hopefully it's close enough + [uiDrawTextWeightBook] = ourNSFontWeightLight + ((ourNSFontWeightRegular - ourNSFontWeightLight) / 2), + [uiDrawTextWeightNormal] = ourNSFontWeightRegular, + [uiDrawTextWeightMedium] = ourNSFontWeightMedium, + [uiDrawTextWeightSemiBold] = ourNSFontWeightSemibold, + [uiDrawTextWeightBold] = ourNSFontWeightBold, + // for this one let's go between Bold and Heavy + [uiDrawTextWeightUltraBold] = ourNSFontWeightBold + ((ourNSFontWeightHeavy - ourNSFontWeightBold) / 2), + [uiDrawTextWeightHeavy] = ourNSFontWeightHeavy, + [uiDrawTextWeightUltraHeavy] = ourNSFontWeightBlack, +}; + +// Unfortunately there are still no named constants for these. +// Let's just use normalized widths. +// As far as I can tell (OS X only ships with condensed fonts, not expanded fonts; TODO), regardless of condensed or expanded, negative means condensed and positive means expanded. +// TODO verify this is correct +static const CGFloat ctStretches[] = { + [uiDrawTextStretchUltraCondensed] = -1.0, + [uiDrawTextStretchExtraCondensed] = -0.75, + [uiDrawTextStretchCondensed] = -0.5, + [uiDrawTextStretchSemiCondensed] = -0.25, + [uiDrawTextStretchNormal] = 0.0, + [uiDrawTextStretchSemiExpanded] = 0.25, + [uiDrawTextStretchExpanded] = 0.5, + [uiDrawTextStretchExtraExpanded] = 0.75, + [uiDrawTextStretchUltraExpanded] = 1.0, +}; + +struct closeness { + CFIndex index; + CGFloat weight; + CGFloat italic; + CGFloat stretch; + CGFloat distance; +}; + +// Stupidity: CTFont requires an **exact match for the entire traits dictionary**, otherwise it will **drop ALL the traits**. +// We have to implement the closest match ourselves. +// Also we have to do this before adding the small caps flags, because the matching descriptors won't have those. +CTFontDescriptorRef matchTraits(CTFontDescriptorRef against, uiDrawTextWeight weight, uiDrawTextItalic italic, uiDrawTextStretch stretch) +{ + CGFloat targetWeight; + CGFloat italicCloseness, obliqueCloseness, normalCloseness; + CGFloat targetStretch; + CFArrayRef matching; + CFIndex i, n; + struct closeness *closeness; + CTFontDescriptorRef current; + CTFontDescriptorRef out; + + targetWeight = ctWeights[weight]; + switch (italic) { + case uiDrawTextItalicNormal: + italicCloseness = 1; + obliqueCloseness = 1; + normalCloseness = 0; + break; + case uiDrawTextItalicOblique: + italicCloseness = 0.5; + obliqueCloseness = 0; + normalCloseness = 1; + break; + case uiDrawTextItalicItalic: + italicCloseness = 0; + obliqueCloseness = 0.5; + normalCloseness = 1; + break; + } + targetStretch = ctStretches[stretch]; + + matching = CTFontDescriptorCreateMatchingFontDescriptors(against, NULL); + if (matching == NULL) + // no matches; give the original back and hope for the best + return against; + n = CFArrayGetCount(matching); + if (n == 0) { + // likewise + CFRelease(matching); + return against; + } + + closeness = (struct closeness *) uiAlloc(n * sizeof (struct closeness), "struct closeness[]"); + for (i = 0; i < n; i++) { + CFDictionaryRef traits; + CFNumberRef cfnum; + CTFontSymbolicTraits symbolic; + + closeness[i].index = i; + + current = CFArrayGetValueAtIndex(matching, i); + traits = CTFontDescriptorCopyAttribute(current, kCTFontTraitsAttribute); + if (traits == NULL) { + // couldn't get traits; be safe by ranking it lowest + // LONGTERM figure out what the longest possible distances are + closeness[i].weight = 3; + closeness[i].italic = 2; + closeness[i].stretch = 3; + continue; + } + + symbolic = 0; // assume no symbolic traits if none are listed + cfnum = CFDictionaryGetValue(traits, kCTFontSymbolicTrait); + if (cfnum != NULL) { + SInt32 s; + + if (CFNumberGetValue(cfnum, kCFNumberSInt32Type, &s) == false) + complain("error getting symbolic traits in matchTraits()"); + symbolic = (CTFontSymbolicTraits) s; + // Get rule; do not release cfnum + } + + // now try weight + cfnum = CFDictionaryGetValue(traits, kCTFontWeightTrait); + if (cfnum != NULL) { + CGFloat val; + + // LONGTERM instead of complaining for this and width and possibly also symbolic traits above, should we just fall through to the default? + if (CFNumberGetValue(cfnum, kCFNumberCGFloatType, &val) == false) + complain("error getting weight value in matchTraits()"); + closeness[i].weight = val - targetWeight; + } else + // okay there's no weight key; let's try the literal meaning of the symbolic constant + // LONGTERM is the weight key guaranteed? + if ((symbolic & kCTFontBoldTrait) != 0) + closeness[i].weight = ourNSFontWeightBold - targetWeight; + else + closeness[i].weight = ourNSFontWeightRegular - targetWeight; + + // italics is a bit harder because Core Text doesn't expose a concept of obliqueness + // Pango just does a g_strrstr() (backwards case-sensitive search) for "Oblique" in the font's style name (see https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c); let's do that too I guess + if ((symbolic & kCTFontItalicTrait) != 0) + closeness[i].italic = italicCloseness; + else { + CFStringRef styleName; + BOOL isOblique; + + isOblique = NO; // default value + styleName = CTFontDescriptorCopyAttribute(current, kCTFontStyleNameAttribute); + if (styleName != NULL) { + CFRange range; + + // note the use of the toll-free bridge for the string literal, since CFSTR() *can* return NULL + range = CFStringFind(styleName, (CFStringRef) @"Oblique", kCFCompareBackwards); + if (range.location != kCFNotFound) + isOblique = YES; + CFRelease(styleName); + } + if (isOblique) + closeness[i].italic = obliqueCloseness; + else + closeness[i].italic = normalCloseness; + } + + // now try width + // TODO this does not seem to be enough for Skia's extended variants; the width trait is 0 but the Expanded flag is on + // TODO verify the rest of this matrix (what matrix?) + cfnum = CFDictionaryGetValue(traits, kCTFontWidthTrait); + if (cfnum != NULL) { + CGFloat val; + + if (CFNumberGetValue(cfnum, kCFNumberCGFloatType, &val) == false) + complain("error getting width value in matchTraits()"); + closeness[i].stretch = val - targetStretch; + } else + // okay there's no width key; let's try the literal meaning of the symbolic constant + // LONGTERM is the width key guaranteed? + if ((symbolic & kCTFontExpandedTrait) != 0) + closeness[i].stretch = 1.0 - targetStretch; + else if ((symbolic & kCTFontCondensedTrait) != 0) + closeness[i].stretch = -1.0 - targetStretch; + else + closeness[i].stretch = 0.0 - targetStretch; + + CFRelease(traits); + } + + // now figure out the 3-space difference between the three and sort by that + for (i = 0; i < n; i++) { + CGFloat weight, italic, stretch; + + weight = closeness[i].weight; + weight *= weight; + italic = closeness[i].italic; + italic *= italic; + stretch = closeness[i].stretch; + stretch *= stretch; + closeness[i].distance = sqrt(weight + italic + stretch); + } + qsort_b(closeness, n, sizeof (struct closeness), ^(const void *aa, const void *bb) { + const struct closeness *a = (const struct closeness *) aa; + const struct closeness *b = (const struct closeness *) bb; + + // via http://www.gnu.org/software/libc/manual/html_node/Comparison-Functions.html#Comparison-Functions + // LONGTERM is this really the best way? isn't it the same as if (*a < *b) return -1; if (*a > *b) return 1; return 0; ? + return (a->distance > b->distance) - (a->distance < b->distance); + }); + // and the first element of the sorted array is what we want + out = CFArrayGetValueAtIndex(matching, closeness[0].index); + CFRetain(out); // get rule + + // release everything + uiFree(closeness); + CFRelease(matching); + // and release the original descriptor since we no longer need it + CFRelease(against); + + return out; +} + +// Now remember what I said earlier about having to add the small caps traits after calling the above? This gets a dictionary back so we can do so. +CFMutableDictionaryRef extractAttributes(CTFontDescriptorRef desc) +{ + CFDictionaryRef dict; + CFMutableDictionaryRef mdict; + + dict = CTFontDescriptorCopyAttributes(desc); + // this might not be mutable, so make a mutable copy + mdict = CFDictionaryCreateMutableCopy(NULL, 0, dict); + CFRelease(dict); + return mdict; +} + +uiDrawTextFont *uiDrawLoadClosestFont(const uiDrawTextFontDescriptor *desc) +{ + CTFontRef f; + CFMutableDictionaryRef attr; + CTFontDescriptorRef cfdesc; + + attr = newAttrList(); + addFontFamilyAttr(attr, desc->Family); + addFontSizeAttr(attr, desc->Size); + + // now we have to do the traits matching, so create a descriptor, match the traits, and then get the attributes back + cfdesc = CTFontDescriptorCreateWithAttributes(attr); + // TODO release attr? + cfdesc = matchTraits(cfdesc, desc->Weight, desc->Italic, desc->Stretch); + + // specify the initial size again just to be safe + f = CTFontCreateWithFontDescriptor(cfdesc, desc->Size, NULL); + // TODO release cfdesc? + + return mkTextFont(f, NO); // we hold the initial reference; no need to retain again +} + +void uiDrawFreeTextFont(uiDrawTextFont *font) +{ + CFRelease(font->f); + uiFree(font); +} + +uintptr_t uiDrawTextFontHandle(uiDrawTextFont *font) +{ + return (uintptr_t) (font->f); +} + +void uiDrawTextFontDescribe(uiDrawTextFont *font, uiDrawTextFontDescriptor *desc) +{ + // TODO +} + +// text sizes and user space points are identical: +// - https://developer.apple.com/library/mac/documentation/TextFonts/Conceptual/CocoaTextArchitecture/TypoFeatures/TextSystemFeatures.html#//apple_ref/doc/uid/TP40009459-CH6-51627-BBCCHIFF text points are 72 per inch +// - https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CocoaDrawingGuide/Transforms/Transforms.html#//apple_ref/doc/uid/TP40003290-CH204-SW5 user space points are 72 per inch +void uiDrawTextFontGetMetrics(uiDrawTextFont *font, uiDrawTextFontMetrics *metrics) +{ + metrics->Ascent = CTFontGetAscent(font->f); + metrics->Descent = CTFontGetDescent(font->f); + metrics->Leading = CTFontGetLeading(font->f); + metrics->UnderlinePos = CTFontGetUnderlinePosition(font->f); + metrics->UnderlineThickness = CTFontGetUnderlineThickness(font->f); +} + +struct uiDrawTextLayout { + CFMutableAttributedStringRef mas; + CFRange *charsToRanges; + double width; +}; + +uiDrawTextLayout *uiDrawNewTextLayout(const char *str, uiDrawTextFont *defaultFont, double width) +{ + uiDrawTextLayout *layout; + CFAttributedStringRef immutable; + CFMutableDictionaryRef attr; + CFStringRef backing; + CFIndex i, j, n; + + layout = uiNew(uiDrawTextLayout); + + // TODO docs say we need to use a different set of key callbacks + // TODO see if the font attribute key callbacks need to be the same + attr = newAttrList(); + // this will retain defaultFont->f; no need to worry + CFDictionaryAddValue(attr, kCTFontAttributeName, defaultFont->f); + + immutable = CFAttributedStringCreate(NULL, (CFStringRef) [NSString stringWithUTF8String:str], attr); + if (immutable == NULL) + complain("error creating immutable attributed string in uiDrawNewTextLayout()"); + CFRelease(attr); + + layout->mas = CFAttributedStringCreateMutableCopy(NULL, 0, immutable); + if (layout->mas == NULL) + complain("error creating attributed string in uiDrawNewTextLayout()"); + CFRelease(immutable); + + uiDrawTextLayoutSetWidth(layout, width); + + // unfortunately the CFRanges for attributes expect UTF-16 codepoints + // we want graphemes + // fortunately CFStringGetRangeOfComposedCharactersAtIndex() is here for us + // https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Strings/Articles/stringsClusters.html says that this does work on all multi-codepoint graphemes (despite the name), and that this is the preferred function for this particular job anyway + backing = CFAttributedStringGetString(layout->mas); + n = CFStringGetLength(backing); + // allocate one extra, just to be safe + layout->charsToRanges = (CFRange *) uiAlloc((n + 1) * sizeof (CFRange), "CFRange[]"); + i = 0; + j = 0; + while (i < n) { + CFRange range; + + range = CFStringGetRangeOfComposedCharactersAtIndex(backing, i); + i = range.location + range.length; + layout->charsToRanges[j] = range; + j++; + } + // and set the last one + layout->charsToRanges[j].location = i; + layout->charsToRanges[j].length = 0; + + return layout; +} + +void uiDrawFreeTextLayout(uiDrawTextLayout *layout) +{ + uiFree(layout->charsToRanges); + CFRelease(layout->mas); + uiFree(layout); +} + +void uiDrawTextLayoutSetWidth(uiDrawTextLayout *layout, double width) +{ + layout->width = width; +} + +struct framesetter { + CTFramesetterRef fs; + CFMutableDictionaryRef frameAttrib; + CGSize extents; +}; + +// TODO CTFrameProgression for RTL/LTR +// TODO kCTParagraphStyleSpecifierMaximumLineSpacing, kCTParagraphStyleSpecifierMinimumLineSpacing, kCTParagraphStyleSpecifierLineSpacingAdjustment for line spacing +static void mkFramesetter(uiDrawTextLayout *layout, struct framesetter *fs) +{ + CFRange fitRange; + CGFloat width; + + fs->fs = CTFramesetterCreateWithAttributedString(layout->mas); + if (fs->fs == NULL) + complain("error creating CTFramesetter object in mkFramesetter()"); + + // TODO kCTFramePathWidthAttributeName? + fs->frameAttrib = NULL; + + width = layout->width; + if (layout->width < 0) + width = CGFLOAT_MAX; + // TODO these seem to be floor()'d or truncated? + fs->extents = CTFramesetterSuggestFrameSizeWithConstraints(fs->fs, + CFRangeMake(0, 0), + fs->frameAttrib, + CGSizeMake(width, CGFLOAT_MAX), + &fitRange); // not documented as accepting NULL +} + +static void freeFramesetter(struct framesetter *fs) +{ + if (fs->frameAttrib != NULL) + CFRelease(fs->frameAttrib); + CFRelease(fs->fs); +} + +// LONGTERM allow line separation and leading to be factored into a wrapping text layout + +// TODO reconcile differences in character wrapping on platforms +void uiDrawTextLayoutExtents(uiDrawTextLayout *layout, double *width, double *height) +{ + struct framesetter fs; + + mkFramesetter(layout, &fs); + *width = fs.extents.width; + *height = fs.extents.height; + freeFramesetter(&fs); +} + +// Core Text doesn't draw onto a flipped view correctly; we have to do this +// see the iOS bits of the first example at https://developer.apple.com/library/mac/documentation/StringsTextFonts/Conceptual/CoreText_Programming/LayoutOperations/LayoutOperations.html#//apple_ref/doc/uid/TP40005533-CH12-SW1 (iOS is naturally flipped) +// TODO how is this affected by the CTM? +static void prepareContextForText(CGContextRef c, CGFloat cheight, double *y) +{ + CGContextSaveGState(c); + CGContextTranslateCTM(c, 0, cheight); + CGContextScaleCTM(c, 1.0, -1.0); + CGContextSetTextMatrix(c, CGAffineTransformIdentity); + + // wait, that's not enough; we need to offset y values to account for our new flipping + *y = cheight - *y; +} + +// TODO placement is incorrect for Helvetica +void doDrawText(CGContextRef c, CGFloat cheight, double x, double y, uiDrawTextLayout *layout) +{ + struct framesetter fs; + CGRect rect; + CGPathRef path; + CTFrameRef frame; + + prepareContextForText(c, cheight, &y); + mkFramesetter(layout, &fs); + + // oh, and since we're flipped, y is the bottom-left coordinate of the rectangle, not the top-left + // since we are flipped, we subtract + y -= fs.extents.height; + + rect.origin = CGPointMake(x, y); + rect.size = fs.extents; + path = CGPathCreateWithRect(rect, NULL); + + frame = CTFramesetterCreateFrame(fs.fs, + CFRangeMake(0, 0), + path, + fs.frameAttrib); + if (frame == NULL) + complain("error creating CTFrame object in doDrawText()"); + CTFrameDraw(frame, c); + CFRelease(frame); + + CFRelease(path); + + freeFramesetter(&fs); + CGContextRestoreGState(c); +} + +// LONGTERM provide an equivalent to CTLineGetTypographicBounds() on uiDrawTextLayout? + +// LONGTERM keep this for later features and documentation purposes +#if 0 + w = CTLineGetTypographicBounds(line, &ascent, &descent, NULL); + // though CTLineGetTypographicBounds() returns 0 on error, it also returns 0 on an empty string, so we can't reasonably check for error + CFRelease(line); + + // LONGTERM provide a way to get the image bounds as a separate function later + bounds = CTLineGetImageBounds(line, c); + // though CTLineGetImageBounds() returns CGRectNull on error, it also returns CGRectNull on an empty string, so we can't reasonably check for error + + // CGContextSetTextPosition() positions at the baseline in the case of CTLineDraw(); we need the top-left corner instead + CTLineGetTypographicBounds(line, &yoff, NULL, NULL); + // remember that we're flipped, so we subtract + y -= yoff; + CGContextSetTextPosition(c, x, y); +#endif + +static CFRange charsToRange(uiDrawTextLayout *layout, int startChar, int endChar) +{ + CFRange start, end; + CFRange out; + + start = layout->charsToRanges[startChar]; + end = layout->charsToRanges[endChar]; + out.location = start.location; + out.length = end.location - start.location; + return out; +} + +#define rangeToCFRange() charsToRange(layout, startChar, endChar) + +void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, int startChar, int endChar, double r, double g, double b, double a) +{ + CGColorSpaceRef colorspace; + CGFloat components[4]; + CGColorRef color; + + // for consistency with windows, use sRGB + colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); + components[0] = r; + components[1] = g; + components[2] = b; + components[3] = a; + color = CGColorCreate(colorspace, components); + CGColorSpaceRelease(colorspace); + + CFAttributedStringSetAttribute(layout->mas, + rangeToCFRange(), + kCTForegroundColorAttributeName, + color); + CGColorRelease(color); // TODO safe? +} diff --git a/darwin/drawtext.m b/darwin/drawtext.m index c376536a..64a8233e 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -1,655 +1,81 @@ -// 6 september 2015 +// 2 january 2017 #import "uipriv_darwin.h" -// TODO -#define complain(...) implbug(__VA_ARGS__) - -// TODO double-check that we are properly handling allocation failures (or just toll free bridge from cocoa) -struct uiDrawFontFamilies { - CFArrayRef fonts; -}; - -uiDrawFontFamilies *uiDrawListFontFamilies(void) -{ - uiDrawFontFamilies *ff; - - ff = uiNew(uiDrawFontFamilies); - ff->fonts = CTFontManagerCopyAvailableFontFamilyNames(); - if (ff->fonts == NULL) - implbug("error getting available font names (no reason specified) (TODO)"); - return ff; -} - -int uiDrawFontFamiliesNumFamilies(uiDrawFontFamilies *ff) -{ - return CFArrayGetCount(ff->fonts); -} - -char *uiDrawFontFamiliesFamily(uiDrawFontFamilies *ff, int n) -{ - CFStringRef familystr; - char *family; - - familystr = (CFStringRef) CFArrayGetValueAtIndex(ff->fonts, n); - // toll-free bridge - family = uiDarwinNSStringToText((NSString *) familystr); - // Get Rule means we do not free familystr - return family; -} - -void uiDrawFreeFontFamilies(uiDrawFontFamilies *ff) -{ - CFRelease(ff->fonts); - uiFree(ff); -} - -struct uiDrawTextFont { - CTFontRef f; -}; - -uiDrawTextFont *mkTextFont(CTFontRef f, BOOL retain) -{ - uiDrawTextFont *font; - - font = uiNew(uiDrawTextFont); - font->f = f; - if (retain) - CFRetain(font->f); - return font; -} - -uiDrawTextFont *mkTextFontFromNSFont(NSFont *f) -{ - // toll-free bridging; we do retain, though - return mkTextFont((CTFontRef) f, YES); -} - -static CFMutableDictionaryRef newAttrList(void) -{ - CFMutableDictionaryRef attr; - - attr = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - if (attr == NULL) - complain("error creating attribute dictionary in newAttrList()()"); - return attr; -} - -static void addFontFamilyAttr(CFMutableDictionaryRef attr, const char *family) -{ - CFStringRef cfstr; - - cfstr = CFStringCreateWithCString(NULL, family, kCFStringEncodingUTF8); - if (cfstr == NULL) - complain("error creating font family name CFStringRef in addFontFamilyAttr()"); - CFDictionaryAddValue(attr, kCTFontFamilyNameAttribute, cfstr); - CFRelease(cfstr); // dictionary holds its own reference -} - -static void addFontSizeAttr(CFMutableDictionaryRef attr, double size) -{ - CFNumberRef n; - - n = CFNumberCreate(NULL, kCFNumberDoubleType, &size); - CFDictionaryAddValue(attr, kCTFontSizeAttribute, n); - CFRelease(n); -} - -#if 0 -TODO -// See http://stackoverflow.com/questions/4810409/does-coretext-support-small-caps/4811371#4811371 and https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c for what these do -// And fortunately, unlike the traits (see below), unmatched features are simply ignored without affecting the other features :D -static void addFontSmallCapsAttr(CFMutableDictionaryRef attr) -{ - CFMutableArrayRef outerArray; - CFMutableDictionaryRef innerDict; - CFNumberRef numType, numSelector; - int num; - - outerArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - if (outerArray == NULL) - complain("error creating outer CFArray for adding small caps attributes in addFontSmallCapsAttr()"); - - // Apple's headers say these are deprecated, but a few fonts still rely on them - num = kLetterCaseType; - numType = CFNumberCreate(NULL, kCFNumberIntType, &num); - num = kSmallCapsSelector; - numSelector = CFNumberCreate(NULL, kCFNumberIntType, &num); - innerDict = newAttrList(); - CFDictionaryAddValue(innerDict, kCTFontFeatureTypeIdentifierKey, numType); - CFRelease(numType); - CFDictionaryAddValue(innerDict, kCTFontFeatureSelectorIdentifierKey, numSelector); - CFRelease(numSelector); - CFArrayAppendValue(outerArray, innerDict); - CFRelease(innerDict); // and likewise for CFArray - - // these are the non-deprecated versions of the above; some fonts have these instead - num = kLowerCaseType; - numType = CFNumberCreate(NULL, kCFNumberIntType, &num); - num = kLowerCaseSmallCapsSelector; - numSelector = CFNumberCreate(NULL, kCFNumberIntType, &num); - innerDict = newAttrList(); - CFDictionaryAddValue(innerDict, kCTFontFeatureTypeIdentifierKey, numType); - CFRelease(numType); - CFDictionaryAddValue(innerDict, kCTFontFeatureSelectorIdentifierKey, numSelector); - CFRelease(numSelector); - CFArrayAppendValue(outerArray, innerDict); - CFRelease(innerDict); // and likewise for CFArray - - CFDictionaryAddValue(attr, kCTFontFeatureSettingsAttribute, outerArray); - CFRelease(outerArray); -} -#endif - -// Named constants for these were NOT added until 10.11, and even then they were added as external symbols instead of macros, so we can't use them directly :( -// kode54 got these for me before I had access to El Capitan; thanks to him. -#define ourNSFontWeightUltraLight -0.800000 -#define ourNSFontWeightThin -0.600000 -#define ourNSFontWeightLight -0.400000 -#define ourNSFontWeightRegular 0.000000 -#define ourNSFontWeightMedium 0.230000 -#define ourNSFontWeightSemibold 0.300000 -#define ourNSFontWeightBold 0.400000 -#define ourNSFontWeightHeavy 0.560000 -#define ourNSFontWeightBlack 0.620000 -static const CGFloat ctWeights[] = { - // yeah these two have their names swapped; blame Pango - [uiDrawTextWeightThin] = ourNSFontWeightUltraLight, - [uiDrawTextWeightUltraLight] = ourNSFontWeightThin, - [uiDrawTextWeightLight] = ourNSFontWeightLight, - // for this one let's go between Light and Regular - // we're doing nearest so if there happens to be an exact value hopefully it's close enough - [uiDrawTextWeightBook] = ourNSFontWeightLight + ((ourNSFontWeightRegular - ourNSFontWeightLight) / 2), - [uiDrawTextWeightNormal] = ourNSFontWeightRegular, - [uiDrawTextWeightMedium] = ourNSFontWeightMedium, - [uiDrawTextWeightSemiBold] = ourNSFontWeightSemibold, - [uiDrawTextWeightBold] = ourNSFontWeightBold, - // for this one let's go between Bold and Heavy - [uiDrawTextWeightUltraBold] = ourNSFontWeightBold + ((ourNSFontWeightHeavy - ourNSFontWeightBold) / 2), - [uiDrawTextWeightHeavy] = ourNSFontWeightHeavy, - [uiDrawTextWeightUltraHeavy] = ourNSFontWeightBlack, -}; - -// Unfortunately there are still no named constants for these. -// Let's just use normalized widths. -// As far as I can tell (OS X only ships with condensed fonts, not expanded fonts; TODO), regardless of condensed or expanded, negative means condensed and positive means expanded. -// TODO verify this is correct -static const CGFloat ctStretches[] = { - [uiDrawTextStretchUltraCondensed] = -1.0, - [uiDrawTextStretchExtraCondensed] = -0.75, - [uiDrawTextStretchCondensed] = -0.5, - [uiDrawTextStretchSemiCondensed] = -0.25, - [uiDrawTextStretchNormal] = 0.0, - [uiDrawTextStretchSemiExpanded] = 0.25, - [uiDrawTextStretchExpanded] = 0.5, - [uiDrawTextStretchExtraExpanded] = 0.75, - [uiDrawTextStretchUltraExpanded] = 1.0, -}; - -struct closeness { - CFIndex index; - CGFloat weight; - CGFloat italic; - CGFloat stretch; - CGFloat distance; -}; - -// Stupidity: CTFont requires an **exact match for the entire traits dictionary**, otherwise it will **drop ALL the traits**. -// We have to implement the closest match ourselves. -// Also we have to do this before adding the small caps flags, because the matching descriptors won't have those. -CTFontDescriptorRef matchTraits(CTFontDescriptorRef against, uiDrawTextWeight weight, uiDrawTextItalic italic, uiDrawTextStretch stretch) -{ - CGFloat targetWeight; - CGFloat italicCloseness, obliqueCloseness, normalCloseness; - CGFloat targetStretch; - CFArrayRef matching; - CFIndex i, n; - struct closeness *closeness; - CTFontDescriptorRef current; - CTFontDescriptorRef out; - - targetWeight = ctWeights[weight]; - switch (italic) { - case uiDrawTextItalicNormal: - italicCloseness = 1; - obliqueCloseness = 1; - normalCloseness = 0; - break; - case uiDrawTextItalicOblique: - italicCloseness = 0.5; - obliqueCloseness = 0; - normalCloseness = 1; - break; - case uiDrawTextItalicItalic: - italicCloseness = 0; - obliqueCloseness = 0.5; - normalCloseness = 1; - break; - } - targetStretch = ctStretches[stretch]; - - matching = CTFontDescriptorCreateMatchingFontDescriptors(against, NULL); - if (matching == NULL) - // no matches; give the original back and hope for the best - return against; - n = CFArrayGetCount(matching); - if (n == 0) { - // likewise - CFRelease(matching); - return against; - } - - closeness = (struct closeness *) uiAlloc(n * sizeof (struct closeness), "struct closeness[]"); - for (i = 0; i < n; i++) { - CFDictionaryRef traits; - CFNumberRef cfnum; - CTFontSymbolicTraits symbolic; - - closeness[i].index = i; - - current = CFArrayGetValueAtIndex(matching, i); - traits = CTFontDescriptorCopyAttribute(current, kCTFontTraitsAttribute); - if (traits == NULL) { - // couldn't get traits; be safe by ranking it lowest - // LONGTERM figure out what the longest possible distances are - closeness[i].weight = 3; - closeness[i].italic = 2; - closeness[i].stretch = 3; - continue; - } - - symbolic = 0; // assume no symbolic traits if none are listed - cfnum = CFDictionaryGetValue(traits, kCTFontSymbolicTrait); - if (cfnum != NULL) { - SInt32 s; - - if (CFNumberGetValue(cfnum, kCFNumberSInt32Type, &s) == false) - complain("error getting symbolic traits in matchTraits()"); - symbolic = (CTFontSymbolicTraits) s; - // Get rule; do not release cfnum - } - - // now try weight - cfnum = CFDictionaryGetValue(traits, kCTFontWeightTrait); - if (cfnum != NULL) { - CGFloat val; - - // LONGTERM instead of complaining for this and width and possibly also symbolic traits above, should we just fall through to the default? - if (CFNumberGetValue(cfnum, kCFNumberCGFloatType, &val) == false) - complain("error getting weight value in matchTraits()"); - closeness[i].weight = val - targetWeight; - } else - // okay there's no weight key; let's try the literal meaning of the symbolic constant - // LONGTERM is the weight key guaranteed? - if ((symbolic & kCTFontBoldTrait) != 0) - closeness[i].weight = ourNSFontWeightBold - targetWeight; - else - closeness[i].weight = ourNSFontWeightRegular - targetWeight; - - // italics is a bit harder because Core Text doesn't expose a concept of obliqueness - // Pango just does a g_strrstr() (backwards case-sensitive search) for "Oblique" in the font's style name (see https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c); let's do that too I guess - if ((symbolic & kCTFontItalicTrait) != 0) - closeness[i].italic = italicCloseness; - else { - CFStringRef styleName; - BOOL isOblique; - - isOblique = NO; // default value - styleName = CTFontDescriptorCopyAttribute(current, kCTFontStyleNameAttribute); - if (styleName != NULL) { - CFRange range; - - // note the use of the toll-free bridge for the string literal, since CFSTR() *can* return NULL - range = CFStringFind(styleName, (CFStringRef) @"Oblique", kCFCompareBackwards); - if (range.location != kCFNotFound) - isOblique = YES; - CFRelease(styleName); - } - if (isOblique) - closeness[i].italic = obliqueCloseness; - else - closeness[i].italic = normalCloseness; - } - - // now try width - // TODO this does not seem to be enough for Skia's extended variants; the width trait is 0 but the Expanded flag is on - // TODO verify the rest of this matrix (what matrix?) - cfnum = CFDictionaryGetValue(traits, kCTFontWidthTrait); - if (cfnum != NULL) { - CGFloat val; - - if (CFNumberGetValue(cfnum, kCFNumberCGFloatType, &val) == false) - complain("error getting width value in matchTraits()"); - closeness[i].stretch = val - targetStretch; - } else - // okay there's no width key; let's try the literal meaning of the symbolic constant - // LONGTERM is the width key guaranteed? - if ((symbolic & kCTFontExpandedTrait) != 0) - closeness[i].stretch = 1.0 - targetStretch; - else if ((symbolic & kCTFontCondensedTrait) != 0) - closeness[i].stretch = -1.0 - targetStretch; - else - closeness[i].stretch = 0.0 - targetStretch; - - CFRelease(traits); - } - - // now figure out the 3-space difference between the three and sort by that - for (i = 0; i < n; i++) { - CGFloat weight, italic, stretch; - - weight = closeness[i].weight; - weight *= weight; - italic = closeness[i].italic; - italic *= italic; - stretch = closeness[i].stretch; - stretch *= stretch; - closeness[i].distance = sqrt(weight + italic + stretch); - } - qsort_b(closeness, n, sizeof (struct closeness), ^(const void *aa, const void *bb) { - const struct closeness *a = (const struct closeness *) aa; - const struct closeness *b = (const struct closeness *) bb; - - // via http://www.gnu.org/software/libc/manual/html_node/Comparison-Functions.html#Comparison-Functions - // LONGTERM is this really the best way? isn't it the same as if (*a < *b) return -1; if (*a > *b) return 1; return 0; ? - return (a->distance > b->distance) - (a->distance < b->distance); - }); - // and the first element of the sorted array is what we want - out = CFArrayGetValueAtIndex(matching, closeness[0].index); - CFRetain(out); // get rule - - // release everything - uiFree(closeness); - CFRelease(matching); - // and release the original descriptor since we no longer need it - CFRelease(against); - - return out; -} - -// Now remember what I said earlier about having to add the small caps traits after calling the above? This gets a dictionary back so we can do so. -CFMutableDictionaryRef extractAttributes(CTFontDescriptorRef desc) -{ - CFDictionaryRef dict; - CFMutableDictionaryRef mdict; - - dict = CTFontDescriptorCopyAttributes(desc); - // this might not be mutable, so make a mutable copy - mdict = CFDictionaryCreateMutableCopy(NULL, 0, dict); - CFRelease(dict); - return mdict; -} - -uiDrawTextFont *uiDrawLoadClosestFont(const uiDrawTextFontDescriptor *desc) -{ - CTFontRef f; - CFMutableDictionaryRef attr; - CTFontDescriptorRef cfdesc; - - attr = newAttrList(); - addFontFamilyAttr(attr, desc->Family); - addFontSizeAttr(attr, desc->Size); - - // now we have to do the traits matching, so create a descriptor, match the traits, and then get the attributes back - cfdesc = CTFontDescriptorCreateWithAttributes(attr); - // TODO release attr? - cfdesc = matchTraits(cfdesc, desc->Weight, desc->Italic, desc->Stretch); - - // specify the initial size again just to be safe - f = CTFontCreateWithFontDescriptor(cfdesc, desc->Size, NULL); - // TODO release cfdesc? - - return mkTextFont(f, NO); // we hold the initial reference; no need to retain again -} - -void uiDrawFreeTextFont(uiDrawTextFont *font) -{ - CFRelease(font->f); - uiFree(font); -} - -uintptr_t uiDrawTextFontHandle(uiDrawTextFont *font) -{ - return (uintptr_t) (font->f); -} - -void uiDrawTextFontDescribe(uiDrawTextFont *font, uiDrawTextFontDescriptor *desc) -{ - // TODO -} - -// text sizes and user space points are identical: -// - https://developer.apple.com/library/mac/documentation/TextFonts/Conceptual/CocoaTextArchitecture/TypoFeatures/TextSystemFeatures.html#//apple_ref/doc/uid/TP40009459-CH6-51627-BBCCHIFF text points are 72 per inch -// - https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CocoaDrawingGuide/Transforms/Transforms.html#//apple_ref/doc/uid/TP40003290-CH204-SW5 user space points are 72 per inch -void uiDrawTextFontGetMetrics(uiDrawTextFont *font, uiDrawTextFontMetrics *metrics) -{ - metrics->Ascent = CTFontGetAscent(font->f); - metrics->Descent = CTFontGetDescent(font->f); - metrics->Leading = CTFontGetLeading(font->f); - metrics->UnderlinePos = CTFontGetUnderlinePosition(font->f); - metrics->UnderlineThickness = CTFontGetUnderlineThickness(font->f); -} - struct uiDrawTextLayout { - CFMutableAttributedStringRef mas; - CFRange *charsToRanges; + CFAttributedStringRef attrstr; double width; }; -uiDrawTextLayout *uiDrawNewTextLayout(const char *str, uiDrawTextFont *defaultFont, double width) +CFAttributedStringRef attrstrToCoreFoundation(uiAttributedString *s, uiDrawFontDescriptor *defaultFont) { - uiDrawTextLayout *layout; - CFAttributedStringRef immutable; - CFMutableDictionaryRef attr; - CFStringRef backing; - CFIndex i, j, n; + CFStringRef cfstr; + CFAttributedStringRef base; + CFMutableAttributedStringRef mas; + CFMutableDictionaryRef defaultAttrs; - layout = uiNew(uiDrawTextLayout); - - // TODO docs say we need to use a different set of key callbacks - // TODO see if the font attribute key callbacks need to be the same - attr = newAttrList(); - // this will retain defaultFont->f; no need to worry - CFDictionaryAddValue(attr, kCTFontAttributeName, defaultFont->f); - - immutable = CFAttributedStringCreate(NULL, (CFStringRef) [NSString stringWithUTF8String:str], attr); - if (immutable == NULL) - complain("error creating immutable attributed string in uiDrawNewTextLayout()"); - CFRelease(attr); - - layout->mas = CFAttributedStringCreateMutableCopy(NULL, 0, immutable); - if (layout->mas == NULL) - complain("error creating attributed string in uiDrawNewTextLayout()"); - CFRelease(immutable); - - uiDrawTextLayoutSetWidth(layout, width); - - // unfortunately the CFRanges for attributes expect UTF-16 codepoints - // we want graphemes - // fortunately CFStringGetRangeOfComposedCharactersAtIndex() is here for us - // https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Strings/Articles/stringsClusters.html says that this does work on all multi-codepoint graphemes (despite the name), and that this is the preferred function for this particular job anyway - backing = CFAttributedStringGetString(layout->mas); - n = CFStringGetLength(backing); - // allocate one extra, just to be safe - layout->charsToRanges = (CFRange *) uiAlloc((n + 1) * sizeof (CFRange), "CFRange[]"); - i = 0; - j = 0; - while (i < n) { - CFRange range; - - range = CFStringGetRangeOfComposedCharactersAtIndex(backing, i); - i = range.location + range.length; - layout->charsToRanges[j] = range; - j++; + cfstr = CFStringCreateWithCharacters(NULL, attrstrUTF16(s), attrstrUTF16Len(s)); + if (cfstr == NULL) { + // TODO + } + defaultAttrs = CFDictionaryCreateMutable(NULL, 4, + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (defaultAttrs == NULL) { + // TODO } - // and set the last one - layout->charsToRanges[j].location = i; - layout->charsToRanges[j].length = 0; - return layout; + base = CFAttributedStringCreate(NULL, cfstr, defaultAttrs); + if (base == NULL) { + // TODO + } + CFRelease(cfstr); + CFRelease(defaultAttrs); + mas = CFAttributedStringCreateMutableCopy(NULL, 0, base); + CFRelease(base); + + CFAttributedStringBeginEditing(mas); + // TODO copy in the attributes + CFAttributedStringEndEditing(mas); + + return mas; } -void uiDrawFreeTextLayout(uiDrawTextLayout *layout) +uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescriptor *defaultFont, double width) { - uiFree(layout->charsToRanges); - CFRelease(layout->mas); - uiFree(layout); } -void uiDrawTextLayoutSetWidth(uiDrawTextLayout *layout, double width) +void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { - layout->width = width; } -struct framesetter { - CTFramesetterRef fs; - CFMutableDictionaryRef frameAttrib; - CGSize extents; -}; - -// TODO CTFrameProgression for RTL/LTR -// TODO kCTParagraphStyleSpecifierMaximumLineSpacing, kCTParagraphStyleSpecifierMinimumLineSpacing, kCTParagraphStyleSpecifierLineSpacingAdjustment for line spacing -static void mkFramesetter(uiDrawTextLayout *layout, struct framesetter *fs) +void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) { - CFRange fitRange; - CGFloat width; - - fs->fs = CTFramesetterCreateWithAttributedString(layout->mas); - if (fs->fs == NULL) - complain("error creating CTFramesetter object in mkFramesetter()"); - - // TODO kCTFramePathWidthAttributeName? - fs->frameAttrib = NULL; - - width = layout->width; - if (layout->width < 0) - width = CGFLOAT_MAX; - // TODO these seem to be floor()'d or truncated? - fs->extents = CTFramesetterSuggestFrameSizeWithConstraints(fs->fs, - CFRangeMake(0, 0), - fs->frameAttrib, - CGSizeMake(width, CGFLOAT_MAX), - &fitRange); // not documented as accepting NULL } -static void freeFramesetter(struct framesetter *fs) +void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height) { - if (fs->frameAttrib != NULL) - CFRelease(fs->frameAttrib); - CFRelease(fs->fs); } -// LONGTERM allow line separation and leading to be factored into a wrapping text layout - -// TODO reconcile differences in character wrapping on platforms -void uiDrawTextLayoutExtents(uiDrawTextLayout *layout, double *width, double *height) +int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) { - struct framesetter fs; - - mkFramesetter(layout, &fs); - *width = fs.extents.width; - *height = fs.extents.height; - freeFramesetter(&fs); } -// Core Text doesn't draw onto a flipped view correctly; we have to do this -// see the iOS bits of the first example at https://developer.apple.com/library/mac/documentation/StringsTextFonts/Conceptual/CoreText_Programming/LayoutOperations/LayoutOperations.html#//apple_ref/doc/uid/TP40005533-CH12-SW1 (iOS is naturally flipped) -// TODO how is this affected by the CTM? -static void prepareContextForText(CGContextRef c, CGFloat cheight, double *y) +void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end) { - CGContextSaveGState(c); - CGContextTranslateCTM(c, 0, cheight); - CGContextScaleCTM(c, 1.0, -1.0); - CGContextSetTextMatrix(c, CGAffineTransformIdentity); - - // wait, that's not enough; we need to offset y values to account for our new flipping - *y = cheight - *y; } -// TODO placement is incorrect for Helvetica -void doDrawText(CGContextRef c, CGFloat cheight, double x, double y, uiDrawTextLayout *layout) +void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m) { - struct framesetter fs; - CGRect rect; - CGPathRef path; - CTFrameRef frame; - - prepareContextForText(c, cheight, &y); - mkFramesetter(layout, &fs); - - // oh, and since we're flipped, y is the bottom-left coordinate of the rectangle, not the top-left - // since we are flipped, we subtract - y -= fs.extents.height; - - rect.origin = CGPointMake(x, y); - rect.size = fs.extents; - path = CGPathCreateWithRect(rect, NULL); - - frame = CTFramesetterCreateFrame(fs.fs, - CFRangeMake(0, 0), - path, - fs.frameAttrib); - if (frame == NULL) - complain("error creating CTFrame object in doDrawText()"); - CTFrameDraw(frame, c); - CFRelease(frame); - - CFRelease(path); - - freeFramesetter(&fs); - CGContextRestoreGState(c); } -// LONGTERM provide an equivalent to CTLineGetTypographicBounds() on uiDrawTextLayout? - -// LONGTERM keep this for later features and documentation purposes -#if 0 - w = CTLineGetTypographicBounds(line, &ascent, &descent, NULL); - // though CTLineGetTypographicBounds() returns 0 on error, it also returns 0 on an empty string, so we can't reasonably check for error - CFRelease(line); - - // LONGTERM provide a way to get the image bounds as a separate function later - bounds = CTLineGetImageBounds(line, c); - // though CTLineGetImageBounds() returns CGRectNull on error, it also returns CGRectNull on an empty string, so we can't reasonably check for error - - // CGContextSetTextPosition() positions at the baseline in the case of CTLineDraw(); we need the top-left corner instead - CTLineGetTypographicBounds(line, &yoff, NULL, NULL); - // remember that we're flipped, so we subtract - y -= yoff; - CGContextSetTextPosition(c, x, y); -#endif - -static CFRange charsToRange(uiDrawTextLayout *layout, int startChar, int endChar) +void uiDrawTextLayoutByteIndexToGraphemeRect(uiDrawTextLayout *tl, size_t pos, int *line, double *x, double *y, double *width, double *height) { - CFRange start, end; - CFRange out; - - start = layout->charsToRanges[startChar]; - end = layout->charsToRanges[endChar]; - out.location = start.location; - out.length = end.location - start.location; - return out; } -#define rangeToCFRange() charsToRange(layout, startChar, endChar) - -void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, int startChar, int endChar, double r, double g, double b, double a) +uiDrawTextLayoutHitTestResult uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *byteIndex, int *line) +{ +} + +void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r) { - CGColorSpaceRef colorspace; - CGFloat components[4]; - CGColorRef color; - - // for consistency with windows, use sRGB - colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); - components[0] = r; - components[1] = g; - components[2] = b; - components[3] = a; - color = CGColorCreate(colorspace, components); - CGColorSpaceRelease(colorspace); - - CFAttributedStringSetAttribute(layout->mas, - rangeToCFRange(), - kCTForegroundColorAttributeName, - color); - CGColorRelease(color); // TODO safe? } diff --git a/ui.h b/ui.h index 919f43d3..2a9de749 100644 --- a/ui.h +++ b/ui.h @@ -475,30 +475,11 @@ _UI_EXTERN void uiDrawClip(uiDrawContext *c, uiDrawPath *path); _UI_EXTERN void uiDrawSave(uiDrawContext *c); _UI_EXTERN void uiDrawRestore(uiDrawContext *c); -// TODO manage the use of Text, Font, and TextFont, and of the uiDrawText prefix in general - -///// TODO reconsider this -typedef struct uiDrawFontFamilies uiDrawFontFamilies; - -_UI_EXTERN uiDrawFontFamilies *uiDrawListFontFamilies(void); -_UI_EXTERN int uiDrawFontFamiliesNumFamilies(uiDrawFontFamilies *ff); -_UI_EXTERN char *uiDrawFontFamiliesFamily(uiDrawFontFamilies *ff, int n); -_UI_EXTERN void uiDrawFreeFontFamilies(uiDrawFontFamilies *ff); -///// END TODO - -typedef struct uiDrawTextLayout uiDrawTextLayout; -typedef struct uiDrawTextFont uiDrawTextFont; -typedef struct uiDrawTextFontDescriptor uiDrawTextFontDescriptor; -typedef struct uiDrawTextFontMetrics uiDrawTextFontMetrics; - -struct uiDrawTextFontDescriptor { - const char *Family; - double Size; - uiDrawTextWeight Weight; - uiDrawTextItalic Italic; - uiDrawTextStretch Stretch; -}; +// TODO merge back in +#include "ui_attrstr.h" +// TODO +#if 0 struct uiDrawTextFontMetrics { double Ascent; double Descent; @@ -515,18 +496,7 @@ _UI_EXTERN void uiDrawTextFontDescribe(uiDrawTextFont *font, uiDrawTextFontDescr // TODO make copy with given attributes methods? // TODO yuck this name _UI_EXTERN void uiDrawTextFontGetMetrics(uiDrawTextFont *font, uiDrawTextFontMetrics *metrics); - -// TODO initial line spacing? and what about leading? -_UI_EXTERN uiDrawTextLayout *uiDrawNewTextLayout(const char *text, uiDrawTextFont *defaultFont, double width); -_UI_EXTERN void uiDrawFreeTextLayout(uiDrawTextLayout *layout); -// TODO get width -_UI_EXTERN void uiDrawTextLayoutSetWidth(uiDrawTextLayout *layout, double width); -_UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *layout, double *width, double *height); - -// and the attributes that you can set on a text layout -_UI_EXTERN void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, int startChar, int endChar, double r, double g, double b, double a); - -_UI_EXTERN void uiDrawText(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout); +#endif _UI_ENUM(uiModifiers) { uiModifierCtrl = 1 << 0, diff --git a/common/ui_attrstr.h b/ui_attrstr.h similarity index 98% rename from common/ui_attrstr.h rename to ui_attrstr.h index 7bfa0af9..274d059d 100644 --- a/common/ui_attrstr.h +++ b/ui_attrstr.h @@ -92,6 +92,7 @@ struct uiDrawTextLayoutLineMetrics { double X; double Y; double Width; + // height = ascent + descent + leading (TODO formally document) double Ascent; double Descent; double Leading; From 4318785eb2c99bdf5ac5cfb461620b9d0e188567 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 3 Jan 2017 12:18:17 -0500 Subject: [PATCH 0536/1329] More drawtext.m work. I was wrong; I'll need to do the trait matching anyway. Ugh. --- darwin/drawtext.m | 131 ++++++++++++++++++++++++++++++++++++++++++++-- ui_attrstr.h | 1 + 2 files changed, 129 insertions(+), 3 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 64a8233e..f93111c1 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -6,23 +6,146 @@ struct uiDrawTextLayout { double width; }; -CFAttributedStringRef attrstrToCoreFoundation(uiAttributedString *s, uiDrawFontDescriptor *defaultFont) +// since uiDrawTextWeight effectively corresponds to OS/2 weights (which roughly correspond to GDI, Pango, and DirectWrite weights, and to a lesser(? TODO) degree, CSS weights), let's just do what Core Text does with OS/2 weights +// TODO this will not be correct for system fonts, which use cached values that have no relation to the OS/2 weights; we need to figure out how to reconcile these +// for more information, see https://bugzilla.gnome.org/show_bug.cgi?id=766148 and TODO_put_blog_post_here_once_I_write_it (TODO keep this line when resolving the above TODO) +static const double weightsToCTWeights[] = { + -1.0, // 0..99 + -0.7, // 100..199 + -0.5, // 200..299 + -0.23, // 300..399 + 0.0, // 400..499 + 0.2, // 500..599 + 0.3, // 600..699 + 0.4, // 700..799 + 0.6, // 800..899 + 0.8, // 900..999 + 1.0, // 1000 +}; + +static double weightToCTWeight(uiDrawTextWeight weight) +{ + int weightClass; + double ctclass; + double rest, weightFloor, nextFloor; + + if (weight <= 0) + return -1.0; + if (weight >= 1000) + return 1.0; + + weightClass = weight / 100; + rest = (double) weight; + weightFloor = (double) (weightClass * 100); + nextFloor = (double) ((weightClass + 1) * 100); + rest = (rest - weightFloor) / (nextFloor - weightFloor); + + ctclass = weightsToCTWeights[weightClass]; + return fma(rest, + weightsToCTWeights[weightClass + 1] - ctclass, + ctclass); +} + +// TODO put italics here + +// based on what Core Text says about actual fonts (system fonts, system fonts in another folder to avoid using cached values, Adobe Font Folio 11, Google Fonts archive, fonts in Windows 7/8.1/10) +static const double stretchesToCTWidths[] = { + [uiDrawTextStretchUltraCondensed] = -0.400000, + [uiDrawTextStretchExtraCondensed] = -0.300000, + [uiDrawTextStretchCondensed] = -0.200000, + [uiDrawTextStretchSemiCondensed] = -0.100000, + [uiDrawTextStretchNormal] = 0.000000, + [uiDrawTextStretchSemiExpanded] = 0.100000, + [uiDrawTextStretchExpanded] = 0.200000, + [uiDrawTextStretchExtraExpanded] = 0.300000, + // this one isn't present in any of the fonts I tested, but it follows naturally from the pattern of the rest, so... (TODO verify by checking the font files directly) + [uiDrawTextStretchUltraExpanded] = 0.400000, +}; + +static CFDictionaryRef fontdescToTraits(uiDrawFontDescriptor *fd) +{ + CFMutableDictionaryRef traits; + CFNumberRef num; + double x; + + traits = CFDictionaryCreateMutable(NULL, 2, + // TODO are these correct? + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (traits == NULL) { + // TODO + } + + x = weightToCTWeight(fd->Weight); + num = CFNumberCreate(NULL, kCFNumberDoubleType, &x); + CFDictionaryAddValue(traits, kCTFontWeightTrait, num); + CFRelease(num); + + // TODO italics + + x = stretchesToCTWidths[fd->Stretch]; + num = CFNumberCreate(NULL, kCFNumberDoubleType, &x); + CFDictionaryAddValue(traits, kCTFontWidthTrait, num); + CFRelease(num); + + return traits; +} + +static CTFontRef fontdescToCTFont(uiDrawFontDescriptor *fd) +{ + CFMutableDictionaryRef attrs; + CFStringRef cffamily; + CFDictionaryRef traits; + CTFontDescriptorRef desc; + CTFontRef font; + + attrs = CFDictionaryCreateMutable(NULL, 2, + // TODO are these correct? + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (attrs == NULL) { + // TODO + } + cffamily = CFStringCreateWithCString(NULL, fd.Family, kCFStringEncodingUTF8); + if (cffamily == NULL) { + // TODO + } + CFDictionaryAddValue(attrs, kCTFontFamilyNameAttribute, cffamily); + CFRelease(cffamily); + traits = fontdescToTraits(fd); + CFDictionaryAddValue(attrs, kCTFontTraitsAttribute, traits); + CFRelease(traits); + + desc = CTFontDescriptorCreateWithAttributes(attrs); + CFRelease(attrs); // TODO correct? + // This function DOES return a font with the closest traits that are available, so we don't have to do any manual matching. + // TODO figure out why we had to for other things... + font = CTFontCreateWithFontDescriptor(desc, fd.Size, NULL); + CFRelease(desc); // TODO correct? + return font; +} + +static CFAttributedStringRef attrstrToCoreFoundation(uiAttributedString *s, uiDrawFontDescriptor *defaultFont) { CFStringRef cfstr; + CFMutableDictionaryRef defaultAttrs; + CTFontRef defaultCTFont; CFAttributedStringRef base; CFMutableAttributedStringRef mas; - CFMutableDictionaryRef defaultAttrs; cfstr = CFStringCreateWithCharacters(NULL, attrstrUTF16(s), attrstrUTF16Len(s)); if (cfstr == NULL) { // TODO } - defaultAttrs = CFDictionaryCreateMutable(NULL, 4, + defaultAttrs = CFDictionaryCreateMutable(NULL, 1, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if (defaultAttrs == NULL) { // TODO } + defaultCTFont = fontdescToCTFont(defaultFont); + CFDictionaryAddValue(defaultAttrs, kCTFontAttributeName, defaultCTFont); + CFRelease(defaultCTFont); base = CFAttributedStringCreate(NULL, cfstr, defaultAttrs); if (base == NULL) { @@ -46,6 +169,8 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { + CFRelease(tl->attrstr); + uiFree(tl); } void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) diff --git a/ui_attrstr.h b/ui_attrstr.h index 274d059d..c7e61667 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -78,6 +78,7 @@ _UI_ENUM(uiDrawTextStretch) { struct uiDrawFontDescriptor { char *Family; + // TODO rename to PointSize? double Size; uiDrawTextWeight Weight; uiDrawTextItalic Italic; From bab798543f843beae09b8df829940d4a60f4df90 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 3 Jan 2017 13:42:12 -0500 Subject: [PATCH 0537/1329] Wrote the new font matching code. This is taken from the old code, but cleaned up considerably and updated with new knowledge. --- darwin/_old_drawtext.m | 214 --------------------------------------- darwin/fontmatch.m | 223 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 223 insertions(+), 214 deletions(-) create mode 100644 darwin/fontmatch.m diff --git a/darwin/_old_drawtext.m b/darwin/_old_drawtext.m index c376536a..14d0a64b 100644 --- a/darwin/_old_drawtext.m +++ b/darwin/_old_drawtext.m @@ -151,220 +151,6 @@ static void addFontSmallCapsAttr(CFMutableDictionaryRef attr) #define ourNSFontWeightBold 0.400000 #define ourNSFontWeightHeavy 0.560000 #define ourNSFontWeightBlack 0.620000 -static const CGFloat ctWeights[] = { - // yeah these two have their names swapped; blame Pango - [uiDrawTextWeightThin] = ourNSFontWeightUltraLight, - [uiDrawTextWeightUltraLight] = ourNSFontWeightThin, - [uiDrawTextWeightLight] = ourNSFontWeightLight, - // for this one let's go between Light and Regular - // we're doing nearest so if there happens to be an exact value hopefully it's close enough - [uiDrawTextWeightBook] = ourNSFontWeightLight + ((ourNSFontWeightRegular - ourNSFontWeightLight) / 2), - [uiDrawTextWeightNormal] = ourNSFontWeightRegular, - [uiDrawTextWeightMedium] = ourNSFontWeightMedium, - [uiDrawTextWeightSemiBold] = ourNSFontWeightSemibold, - [uiDrawTextWeightBold] = ourNSFontWeightBold, - // for this one let's go between Bold and Heavy - [uiDrawTextWeightUltraBold] = ourNSFontWeightBold + ((ourNSFontWeightHeavy - ourNSFontWeightBold) / 2), - [uiDrawTextWeightHeavy] = ourNSFontWeightHeavy, - [uiDrawTextWeightUltraHeavy] = ourNSFontWeightBlack, -}; - -// Unfortunately there are still no named constants for these. -// Let's just use normalized widths. -// As far as I can tell (OS X only ships with condensed fonts, not expanded fonts; TODO), regardless of condensed or expanded, negative means condensed and positive means expanded. -// TODO verify this is correct -static const CGFloat ctStretches[] = { - [uiDrawTextStretchUltraCondensed] = -1.0, - [uiDrawTextStretchExtraCondensed] = -0.75, - [uiDrawTextStretchCondensed] = -0.5, - [uiDrawTextStretchSemiCondensed] = -0.25, - [uiDrawTextStretchNormal] = 0.0, - [uiDrawTextStretchSemiExpanded] = 0.25, - [uiDrawTextStretchExpanded] = 0.5, - [uiDrawTextStretchExtraExpanded] = 0.75, - [uiDrawTextStretchUltraExpanded] = 1.0, -}; - -struct closeness { - CFIndex index; - CGFloat weight; - CGFloat italic; - CGFloat stretch; - CGFloat distance; -}; - -// Stupidity: CTFont requires an **exact match for the entire traits dictionary**, otherwise it will **drop ALL the traits**. -// We have to implement the closest match ourselves. -// Also we have to do this before adding the small caps flags, because the matching descriptors won't have those. -CTFontDescriptorRef matchTraits(CTFontDescriptorRef against, uiDrawTextWeight weight, uiDrawTextItalic italic, uiDrawTextStretch stretch) -{ - CGFloat targetWeight; - CGFloat italicCloseness, obliqueCloseness, normalCloseness; - CGFloat targetStretch; - CFArrayRef matching; - CFIndex i, n; - struct closeness *closeness; - CTFontDescriptorRef current; - CTFontDescriptorRef out; - - targetWeight = ctWeights[weight]; - switch (italic) { - case uiDrawTextItalicNormal: - italicCloseness = 1; - obliqueCloseness = 1; - normalCloseness = 0; - break; - case uiDrawTextItalicOblique: - italicCloseness = 0.5; - obliqueCloseness = 0; - normalCloseness = 1; - break; - case uiDrawTextItalicItalic: - italicCloseness = 0; - obliqueCloseness = 0.5; - normalCloseness = 1; - break; - } - targetStretch = ctStretches[stretch]; - - matching = CTFontDescriptorCreateMatchingFontDescriptors(against, NULL); - if (matching == NULL) - // no matches; give the original back and hope for the best - return against; - n = CFArrayGetCount(matching); - if (n == 0) { - // likewise - CFRelease(matching); - return against; - } - - closeness = (struct closeness *) uiAlloc(n * sizeof (struct closeness), "struct closeness[]"); - for (i = 0; i < n; i++) { - CFDictionaryRef traits; - CFNumberRef cfnum; - CTFontSymbolicTraits symbolic; - - closeness[i].index = i; - - current = CFArrayGetValueAtIndex(matching, i); - traits = CTFontDescriptorCopyAttribute(current, kCTFontTraitsAttribute); - if (traits == NULL) { - // couldn't get traits; be safe by ranking it lowest - // LONGTERM figure out what the longest possible distances are - closeness[i].weight = 3; - closeness[i].italic = 2; - closeness[i].stretch = 3; - continue; - } - - symbolic = 0; // assume no symbolic traits if none are listed - cfnum = CFDictionaryGetValue(traits, kCTFontSymbolicTrait); - if (cfnum != NULL) { - SInt32 s; - - if (CFNumberGetValue(cfnum, kCFNumberSInt32Type, &s) == false) - complain("error getting symbolic traits in matchTraits()"); - symbolic = (CTFontSymbolicTraits) s; - // Get rule; do not release cfnum - } - - // now try weight - cfnum = CFDictionaryGetValue(traits, kCTFontWeightTrait); - if (cfnum != NULL) { - CGFloat val; - - // LONGTERM instead of complaining for this and width and possibly also symbolic traits above, should we just fall through to the default? - if (CFNumberGetValue(cfnum, kCFNumberCGFloatType, &val) == false) - complain("error getting weight value in matchTraits()"); - closeness[i].weight = val - targetWeight; - } else - // okay there's no weight key; let's try the literal meaning of the symbolic constant - // LONGTERM is the weight key guaranteed? - if ((symbolic & kCTFontBoldTrait) != 0) - closeness[i].weight = ourNSFontWeightBold - targetWeight; - else - closeness[i].weight = ourNSFontWeightRegular - targetWeight; - - // italics is a bit harder because Core Text doesn't expose a concept of obliqueness - // Pango just does a g_strrstr() (backwards case-sensitive search) for "Oblique" in the font's style name (see https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c); let's do that too I guess - if ((symbolic & kCTFontItalicTrait) != 0) - closeness[i].italic = italicCloseness; - else { - CFStringRef styleName; - BOOL isOblique; - - isOblique = NO; // default value - styleName = CTFontDescriptorCopyAttribute(current, kCTFontStyleNameAttribute); - if (styleName != NULL) { - CFRange range; - - // note the use of the toll-free bridge for the string literal, since CFSTR() *can* return NULL - range = CFStringFind(styleName, (CFStringRef) @"Oblique", kCFCompareBackwards); - if (range.location != kCFNotFound) - isOblique = YES; - CFRelease(styleName); - } - if (isOblique) - closeness[i].italic = obliqueCloseness; - else - closeness[i].italic = normalCloseness; - } - - // now try width - // TODO this does not seem to be enough for Skia's extended variants; the width trait is 0 but the Expanded flag is on - // TODO verify the rest of this matrix (what matrix?) - cfnum = CFDictionaryGetValue(traits, kCTFontWidthTrait); - if (cfnum != NULL) { - CGFloat val; - - if (CFNumberGetValue(cfnum, kCFNumberCGFloatType, &val) == false) - complain("error getting width value in matchTraits()"); - closeness[i].stretch = val - targetStretch; - } else - // okay there's no width key; let's try the literal meaning of the symbolic constant - // LONGTERM is the width key guaranteed? - if ((symbolic & kCTFontExpandedTrait) != 0) - closeness[i].stretch = 1.0 - targetStretch; - else if ((symbolic & kCTFontCondensedTrait) != 0) - closeness[i].stretch = -1.0 - targetStretch; - else - closeness[i].stretch = 0.0 - targetStretch; - - CFRelease(traits); - } - - // now figure out the 3-space difference between the three and sort by that - for (i = 0; i < n; i++) { - CGFloat weight, italic, stretch; - - weight = closeness[i].weight; - weight *= weight; - italic = closeness[i].italic; - italic *= italic; - stretch = closeness[i].stretch; - stretch *= stretch; - closeness[i].distance = sqrt(weight + italic + stretch); - } - qsort_b(closeness, n, sizeof (struct closeness), ^(const void *aa, const void *bb) { - const struct closeness *a = (const struct closeness *) aa; - const struct closeness *b = (const struct closeness *) bb; - - // via http://www.gnu.org/software/libc/manual/html_node/Comparison-Functions.html#Comparison-Functions - // LONGTERM is this really the best way? isn't it the same as if (*a < *b) return -1; if (*a > *b) return 1; return 0; ? - return (a->distance > b->distance) - (a->distance < b->distance); - }); - // and the first element of the sorted array is what we want - out = CFArrayGetValueAtIndex(matching, closeness[0].index); - CFRetain(out); // get rule - - // release everything - uiFree(closeness); - CFRelease(matching); - // and release the original descriptor since we no longer need it - CFRelease(against); - - return out; -} // Now remember what I said earlier about having to add the small caps traits after calling the above? This gets a dictionary back so we can do so. CFMutableDictionaryRef extractAttributes(CTFontDescriptorRef desc) diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m new file mode 100644 index 00000000..c8840d76 --- /dev/null +++ b/darwin/fontmatch.m @@ -0,0 +1,223 @@ +// 3 january 2017 +#import "uipriv_darwin.h" + +// Stupidity: Core Text requires an **exact match for the entire traits dictionary**, otherwise it will **drop ALL the traits**. +// This seems to be true for every function in Core Text that advertises that it performs font matching; I can confirm at the time of writing this is the case for +// - CTFontDescriptorCreateMatchingFontDescriptors() (though the conditions here seem odd) +// - CTFontCreateWithFontDescriptor() +// - CTFontCreateCopyWithAttributes() +// We have to implement the closest match ourselves. +// This needs to be done early in the matching process; in particular, we have to do this before adding any features (such as small caps), because the matching descriptors won't have those. + +struct closeness { + CFIndex index; + double weight; + double italic; + double stretch; + double distance; +}; + +static double doubleAttr(CFDictionaryRef traits, CFStringRef attr) +{ + CFNumberRef cfnum; + double val; + + cfnum = (CFNumberRef) CFDictionaryGetValue(traits, attr); + if (cfnum == NULL) { + // TODO + } + if (CFNumberGetValue(cfnum, kCFNumberDoubleType, &val) == false) { + // TODO + } + // Get Rule; do not release cfnum + return val; +} + +struct italicCloseness { + double normal; + double oblique; + double italic; +}; + +// remember that in closeness, 0 means exact +// in this case, since we define the range, we use 0.5 to mean "close enough" (oblique for italic and italic for oblique) and 1 to mean "not a match" +static const struct italicCloseness italicClosenesses[] = { + [uiDrawTextItalicNormal] = { 0, 1, 1 }, + [uiDrawTextItalicOblique] = { 1, 0, 0.5 }, + [uiDrawTextItalicItalic] = { 1, 0.5, 0 }, +}; + +// Italics are hard because Core Text does NOT distinguish between italic and oblique. +// All Core Text provides is a slant value and the italic bit of the symbolic traits mask. +// However, Core Text does seem to guarantee (from experimentation; see below) that the slant will be nonzero if and only if the italic bit is set, so we don't need to use the slant value. +// Core Text also seems to guarantee that if a font lists itself as Italic or Oblique by name (font subfamily name, font style name, whatever), it will also have that bit set, so testing this bit does cover all fonts that name themselves as Italic and Oblique. (Again, this is from the below experimentation.) +// TODO there is still one catch that might matter from a user's POV: the reverse is not true — the italic bit can be set even if the style of the font face/subfamily/style isn't named as Italic (for example, script typefaces like Adobe's Palace Script MT Std); I don't know what to do about this... I know how to start: find a script font that has an italic form (Adobe's Palace Script MT Std does not; only Regular and Semibold) +static double italicCloseness(CTFontDescriptorRef desc, CFDictionaryRef traits, uiDrawTextItalic italic) +{ + struct italicCloseness *ic = &(italicClosenesses[italic]); + CFNumberRef cfnum; + CTFontSymbolicTraits symbolic; + // there is no kCFNumberUInt32Type, but CTFontSymbolicTraits is uint32_t, so SInt32 should work + SInt32 s; + CFStringRef styleName; + BOOL isOblique; + + cfnum = CFDictionaryGetValue(traits, kCTFontSymbolicTrait); + if (cfnum == NULL) { + // TODO + } + if (CFNumberGetValue(cfnum, kCFNumberSInt32Type, &s) == false) { + // TODO + } + symbolic = (CTFontSymbolicTraits) s; + // Get Rule; do not release cfnum + if ((symbolic & kCTFontItalicTrait) == 0) + return ic->normal; + + // Okay, now we know it's either Italic or Oblique + // Pango's Core Text code just does a g_strrstr() (backwards case-sensitive search) for "Oblique" in the font's style name (see https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c); let's do that too I guess + isOblique = NO; // default value + styleName = (CFStringRef) CTFontDescriptorCopyAttribute(current, kCTFontStyleNameAttribute); + // TODO is styleName guaranteed? + if (styleName != NULL) { + CFRange range; + + // note the use of the toll-free bridge for the string literal, since CFSTR() *can* return NULL + // TODO is this really the case? or is that just a copy-paste error from the other CFStringCreateXxx() functions? and what's this about -fconstant-cfstring? + range = CFStringFind(styleName, (CFStringRef) @"Oblique", kCFCompareBackwards); + if (range.location != kCFNotFound) + isOblique = YES; + CFRelease(styleName); + } + if (isOblique) + return ic->oblique; + return ic->italic; +} + +static CTFontDescriptorRef matchTraits(CTFontDescriptorRef against, double targetWeight, uiDrawTextItalic targetItalic, double targetStretch) +{ + CFArrayRef matching; + CFIndex i, n; + struct closeness *closeness; + CTFontDescriptorRef current; + CTFontDescriptorRef out; + + matching = CTFontDescriptorCreateMatchingFontDescriptors(against, NULL); + if (matching == NULL) + // no matches; give the original back and hope for the best + return against; + n = CFArrayGetCount(matching); + if (n == 0) { + // likewise + CFRelease(matching); + return against; + } + + closeness = (struct closeness *) uiAlloc(n * sizeof (struct closeness), "struct closeness[]"); + for (i = 0; i < n; i++) { + CFDictionaryRef traits; + + closeness[i].index = i; + current = (CTFontDescriptorRef) CFArrayGetValueAtIndex(matching, i); + traits = (CFDictionaryRef) CTFontDescriptorCopyAttribute(current, kCTFontTraitsAttribute); + if (traits == NULL) { + // couldn't get traits; be safe by ranking it lowest + // LONGTERM figure out what the longest possible distances are + closeness[i].weight = 3; + closeness[i].italic = 2; + closeness[i].stretch = 3; + continue; + } + closeness[i].weight = doubleAttr(traits, kCTFontWeightTrait) - targetWeight; + closeness[i].italic = italicCloseness(current, traits, targetItalic); + closeness[i].stretch = doubleAttr(traits, kCTFontWidthTrait) - targetStretch; + CFRelease(traits); + } + + // now figure out the 3-space difference between the three and sort by that + // TODO merge this loop with the previous loop? + for (i = 0; i < n; i++) { + double weight, italic, stretch; + + weight = closeness[i].weight; + weight *= weight; + italic = closeness[i].italic; + italic *= italic; + stretch = closeness[i].stretch; + stretch *= stretch; + closeness[i].distance = sqrt(weight + italic + stretch); + } + qsort_b(closeness, n, sizeof (struct closeness), ^(const void *aa, const void *bb) { + const struct closeness *a = (const struct closeness *) aa; + const struct closeness *b = (const struct closeness *) bb; + + // via http://www.gnu.org/software/libc/manual/html_node/Comparison-Functions.html#Comparison-Functions + // LONGTERM is this really the best way? isn't it the same as if (*a < *b) return -1; if (*a > *b) return 1; return 0; ? + return (a->distance > b->distance) - (a->distance < b->distance); + }); + // and the first element of the sorted array is what we want + out = CFArrayGetValueAtIndex(matching, closeness[0].index); + CFRetain(out); // get rule + + // release everything + uiFree(closeness); + CFRelease(matching); + // and release the original descriptor since we no longer need it + CFRelease(against); + + return out; +} + +// since uiDrawTextWeight effectively corresponds to OS/2 weights (which roughly correspond to GDI, Pango, and DirectWrite weights, and to a lesser(? TODO) degree, CSS weights), let's just do what Core Text does with OS/2 weights +// TODO this will not be correct for system fonts, which use cached values that have no relation to the OS/2 weights; we need to figure out how to reconcile these +// for more information, see https://bugzilla.gnome.org/show_bug.cgi?id=766148 and TODO_put_blog_post_here_once_I_write_it (TODO keep this line when resolving the above TODO) +static const double weightsToCTWeights[] = { + -1.0, // 0..99 + -0.7, // 100..199 + -0.5, // 200..299 + -0.23, // 300..399 + 0.0, // 400..499 + 0.2, // 500..599 + 0.3, // 600..699 + 0.4, // 700..799 + 0.6, // 800..899 + 0.8, // 900..999 + 1.0, // 1000 +}; + +static double weightToCTWeight(uiDrawTextWeight weight) +{ + int weightClass; + double ctclass; + double rest, weightFloor, nextFloor; + + if (weight <= 0) + return -1.0; + if (weight >= 1000) + return 1.0; + + weightClass = weight / 100; + rest = (double) weight; + weightFloor = (double) (weightClass * 100); + nextFloor = (double) ((weightClass + 1) * 100); + rest = (rest - weightFloor) / (nextFloor - weightFloor); + + ctclass = weightsToCTWeights[weightClass]; + return fma(rest, + weightsToCTWeights[weightClass + 1] - ctclass, + ctclass); +} + +// based on what Core Text says about actual fonts (system fonts, system fonts in another folder to avoid using cached values, Adobe Font Folio 11, Google Fonts archive, fonts in Windows 7/8.1/10) +static const double stretchesToCTWidths[] = { + [uiDrawTextStretchUltraCondensed] = -0.400000, + [uiDrawTextStretchExtraCondensed] = -0.300000, + [uiDrawTextStretchCondensed] = -0.200000, + [uiDrawTextStretchSemiCondensed] = -0.100000, + [uiDrawTextStretchNormal] = 0.000000, + [uiDrawTextStretchSemiExpanded] = 0.100000, + [uiDrawTextStretchExpanded] = 0.200000, + [uiDrawTextStretchExtraExpanded] = 0.300000, + // this one isn't present in any of the fonts I tested, but it follows naturally from the pattern of the rest, so... (TODO verify by checking the font files directly) + [uiDrawTextStretchUltraExpanded] = 0.400000, +}; From f147edf94902bfc3de9e44dd26905e9b7fba19b5 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 3 Jan 2017 23:59:23 -0500 Subject: [PATCH 0538/1329] More work. --- darwin/CMakeLists.txt | 1 + darwin/_old_drawtext.m | 32 --------- darwin/drawtext.m | 151 +++++++++++------------------------------ darwin/fontmatch.m | 32 +++++++++ darwin/uipriv_darwin.h | 8 +-- 5 files changed, 77 insertions(+), 147 deletions(-) diff --git a/darwin/CMakeLists.txt b/darwin/CMakeLists.txt index dc99b864..385a4a53 100644 --- a/darwin/CMakeLists.txt +++ b/darwin/CMakeLists.txt @@ -18,6 +18,7 @@ list(APPEND _LIBUI_SOURCES darwin/editablecombo.m darwin/entry.m darwin/fontbutton.m + darwin/fontmatch.m darwin/form.m darwin/graphemes.m darwin/grid.m diff --git a/darwin/_old_drawtext.m b/darwin/_old_drawtext.m index 14d0a64b..96cf696e 100644 --- a/darwin/_old_drawtext.m +++ b/darwin/_old_drawtext.m @@ -292,38 +292,6 @@ struct framesetter { CGSize extents; }; -// TODO CTFrameProgression for RTL/LTR -// TODO kCTParagraphStyleSpecifierMaximumLineSpacing, kCTParagraphStyleSpecifierMinimumLineSpacing, kCTParagraphStyleSpecifierLineSpacingAdjustment for line spacing -static void mkFramesetter(uiDrawTextLayout *layout, struct framesetter *fs) -{ - CFRange fitRange; - CGFloat width; - - fs->fs = CTFramesetterCreateWithAttributedString(layout->mas); - if (fs->fs == NULL) - complain("error creating CTFramesetter object in mkFramesetter()"); - - // TODO kCTFramePathWidthAttributeName? - fs->frameAttrib = NULL; - - width = layout->width; - if (layout->width < 0) - width = CGFLOAT_MAX; - // TODO these seem to be floor()'d or truncated? - fs->extents = CTFramesetterSuggestFrameSizeWithConstraints(fs->fs, - CFRangeMake(0, 0), - fs->frameAttrib, - CGSizeMake(width, CGFLOAT_MAX), - &fitRange); // not documented as accepting NULL -} - -static void freeFramesetter(struct framesetter *fs) -{ - if (fs->frameAttrib != NULL) - CFRelease(fs->frameAttrib); - CFRelease(fs->fs); -} - // LONGTERM allow line separation and leading to be factored into a wrapping text layout // TODO reconcile differences in character wrapping on platforms diff --git a/darwin/drawtext.m b/darwin/drawtext.m index f93111c1..f5e1acdb 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -4,123 +4,18 @@ struct uiDrawTextLayout { CFAttributedStringRef attrstr; double width; + CTFramesetterRef framesetter; + CGSize size; + CGPathRef path; }; -// since uiDrawTextWeight effectively corresponds to OS/2 weights (which roughly correspond to GDI, Pango, and DirectWrite weights, and to a lesser(? TODO) degree, CSS weights), let's just do what Core Text does with OS/2 weights -// TODO this will not be correct for system fonts, which use cached values that have no relation to the OS/2 weights; we need to figure out how to reconcile these -// for more information, see https://bugzilla.gnome.org/show_bug.cgi?id=766148 and TODO_put_blog_post_here_once_I_write_it (TODO keep this line when resolving the above TODO) -static const double weightsToCTWeights[] = { - -1.0, // 0..99 - -0.7, // 100..199 - -0.5, // 200..299 - -0.23, // 300..399 - 0.0, // 400..499 - 0.2, // 500..599 - 0.3, // 600..699 - 0.4, // 700..799 - 0.6, // 800..899 - 0.8, // 900..999 - 1.0, // 1000 -}; - -static double weightToCTWeight(uiDrawTextWeight weight) -{ - int weightClass; - double ctclass; - double rest, weightFloor, nextFloor; - - if (weight <= 0) - return -1.0; - if (weight >= 1000) - return 1.0; - - weightClass = weight / 100; - rest = (double) weight; - weightFloor = (double) (weightClass * 100); - nextFloor = (double) ((weightClass + 1) * 100); - rest = (rest - weightFloor) / (nextFloor - weightFloor); - - ctclass = weightsToCTWeights[weightClass]; - return fma(rest, - weightsToCTWeights[weightClass + 1] - ctclass, - ctclass); -} - -// TODO put italics here - -// based on what Core Text says about actual fonts (system fonts, system fonts in another folder to avoid using cached values, Adobe Font Folio 11, Google Fonts archive, fonts in Windows 7/8.1/10) -static const double stretchesToCTWidths[] = { - [uiDrawTextStretchUltraCondensed] = -0.400000, - [uiDrawTextStretchExtraCondensed] = -0.300000, - [uiDrawTextStretchCondensed] = -0.200000, - [uiDrawTextStretchSemiCondensed] = -0.100000, - [uiDrawTextStretchNormal] = 0.000000, - [uiDrawTextStretchSemiExpanded] = 0.100000, - [uiDrawTextStretchExpanded] = 0.200000, - [uiDrawTextStretchExtraExpanded] = 0.300000, - // this one isn't present in any of the fonts I tested, but it follows naturally from the pattern of the rest, so... (TODO verify by checking the font files directly) - [uiDrawTextStretchUltraExpanded] = 0.400000, -}; - -static CFDictionaryRef fontdescToTraits(uiDrawFontDescriptor *fd) -{ - CFMutableDictionaryRef traits; - CFNumberRef num; - double x; - - traits = CFDictionaryCreateMutable(NULL, 2, - // TODO are these correct? - &kCFCopyStringDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - if (traits == NULL) { - // TODO - } - - x = weightToCTWeight(fd->Weight); - num = CFNumberCreate(NULL, kCFNumberDoubleType, &x); - CFDictionaryAddValue(traits, kCTFontWeightTrait, num); - CFRelease(num); - - // TODO italics - - x = stretchesToCTWidths[fd->Stretch]; - num = CFNumberCreate(NULL, kCFNumberDoubleType, &x); - CFDictionaryAddValue(traits, kCTFontWidthTrait, num); - CFRelease(num); - - return traits; -} - static CTFontRef fontdescToCTFont(uiDrawFontDescriptor *fd) { - CFMutableDictionaryRef attrs; - CFStringRef cffamily; - CFDictionaryRef traits; CTFontDescriptorRef desc; CTFontRef font; - attrs = CFDictionaryCreateMutable(NULL, 2, - // TODO are these correct? - &kCFCopyStringDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - if (attrs == NULL) { - // TODO - } - cffamily = CFStringCreateWithCString(NULL, fd.Family, kCFStringEncodingUTF8); - if (cffamily == NULL) { - // TODO - } - CFDictionaryAddValue(attrs, kCTFontFamilyNameAttribute, cffamily); - CFRelease(cffamily); - traits = fontdescToTraits(fd); - CFDictionaryAddValue(attrs, kCTFontTraitsAttribute, traits); - CFRelease(traits); - - desc = CTFontDescriptorCreateWithAttributes(attrs); - CFRelease(attrs); // TODO correct? - // This function DOES return a font with the closest traits that are available, so we don't have to do any manual matching. - // TODO figure out why we had to for other things... - font = CTFontCreateWithFontDescriptor(desc, fd.Size, NULL); + desc = fontdescToCTFontDescriptor(fd); + font = CTFontCreateWithFontDescriptor(desc, fd->Size, NULL); CFRelease(desc); // TODO correct? return font; } @@ -165,10 +60,46 @@ static CFAttributedStringRef attrstrToCoreFoundation(uiAttributedString *s, uiDr uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescriptor *defaultFont, double width) { + uiDrawTextLayout *tl; + CGFloat cgwidth; + CFRange range, unused; + CGRect rect; + + tl = uiNew(uiDrawTextLayout); + tl->attrstr = attrstrToCoreFoundation(s, defaultFont); + range.location = 0; + range.length = CFAttributedStringGetLength(tl->attrstr); + tl->width = width; + + // TODO CTFrameProgression for RTL/LTR + // TODO kCTParagraphStyleSpecifierMaximumLineSpacing, kCTParagraphStyleSpecifierMinimumLineSpacing, kCTParagraphStyleSpecifierLineSpacingAdjustment for line spacing + tl->framesetter = CTFramesetterCreateWithAttributedString(tl->attrstr); + if (tl->framesetter == NULL) { + // TODO + } + + cgwidth = (CGFloat) width; + if (cgwidth < 0) + cgwidth = CGFLOAT_MAX; + // TODO these seem to be floor()'d or truncated? + // TODO double check to make sure this TODO was right + tl->size = CTFramesetterSuggestFrameSizeWithConstraints(tl->framesetter, + range, + // TODO kCTFramePathWidthAttributeName? + NULL, + CGSizeMake(cgwidth, CGFLOAT_MAX), + &unused); // not documented as accepting NULL + + rect.origin = CGZeroPoint; + rect.size = tl->size; + tl->path = CGPathCreateWithRect(rect, NULL); + return tl; } void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { + CFRelease(tl->path); + CFRelease(tl->framesetter); CFRelease(tl->attrstr); uiFree(tl); } diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index c8840d76..31e2a4d1 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -221,3 +221,35 @@ static const double stretchesToCTWidths[] = { // this one isn't present in any of the fonts I tested, but it follows naturally from the pattern of the rest, so... (TODO verify by checking the font files directly) [uiDrawTextStretchUltraExpanded] = 0.400000, }; + +CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd) +{ + CFMutableDictionaryRef attrs; + CFStringRef cffamily; + CFNumberRef cfsize; + CTFontDescriptorRef basedesc; + + attrs = CFDictionaryCreateMutable(NULL, 2, + // TODO are these correct? + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (attrs == NULL) { + // TODO + } + cffamily = CFStringCreateWithCString(NULL, fd->Family, kCFStringEncodingUTF8); + if (cffamily == NULL) { + // TODO + } + CFDictionaryAddValue(attrs, kCTFontFamilyNameAttribute, cffamily); + CFRelease(cffamily); + cfsize = CFNumberCreate(NULL, kCFNumberDoubleType, &(fd->Size)); + CFDictionaryAddValue(attrs, kCTFontSizeAttribute, cfsize); + CFRelease(cfsize); + + basedesc = CTFontDescriptorCreateWithAttributes(attrs); + CFRelease(attrs); // TODO correct? + return matchTraits(basedesc, + weightToCTWeight(fd->Weight), + fd->Italic, + stretchesToCTWidths[fd->Stretch]); +} diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 6bca87b2..125fd94a 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -108,11 +108,6 @@ extern BOOL keycodeModifier(unsigned short keycode, uiModifiers *mod); extern uiDrawContext *newContext(CGContextRef, CGFloat); extern void freeContext(uiDrawContext *); -// drawtext.m -extern uiDrawTextFont *mkTextFont(CTFontRef f, BOOL retain); -extern uiDrawTextFont *mkTextFontFromNSFont(NSFont *f); -extern void doDrawText(CGContextRef c, CGFloat cheight, double x, double y, uiDrawTextLayout *layout); - // fontbutton.m extern BOOL fontButtonInhibitSendAction(SEL sel, id from, id to); extern BOOL fontButtonOverrideTargetForAction(SEL sel, id from, id to, id *override); @@ -144,3 +139,6 @@ extern NSImage *imageImage(uiImage *); // winmoveresize.m extern void doManualMove(NSWindow *w, NSEvent *initialEvent); extern void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdge edge); + +// fontmatch.m +extern CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd); From dfaf640101c18a022266033dd137b51fd4b811f0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 4 Jan 2017 23:50:08 -0500 Subject: [PATCH 0539/1329] More work. Core Text chaos has resurged... --- darwin/_old_drawtext.m | 101 ++--------------------------------------- darwin/drawtext.m | 45 ++++++++++++++++++ ui_attrstr.h | 1 + 3 files changed, 50 insertions(+), 97 deletions(-) diff --git a/darwin/_old_drawtext.m b/darwin/_old_drawtext.m index 96cf696e..14346784 100644 --- a/darwin/_old_drawtext.m +++ b/darwin/_old_drawtext.m @@ -1,9 +1,6 @@ // 6 september 2015 #import "uipriv_darwin.h" -// TODO -#define complain(...) implbug(__VA_ARGS__) - // TODO double-check that we are properly handling allocation failures (or just toll free bridge from cocoa) struct uiDrawFontFamilies { CFArrayRef fonts; @@ -140,6 +137,7 @@ static void addFontSmallCapsAttr(CFMutableDictionaryRef attr) } #endif +#if 0 // Named constants for these were NOT added until 10.11, and even then they were added as external symbols instead of macros, so we can't use them directly :( // kode54 got these for me before I had access to El Capitan; thanks to him. #define ourNSFontWeightUltraLight -0.800000 @@ -151,6 +149,7 @@ static void addFontSmallCapsAttr(CFMutableDictionaryRef attr) #define ourNSFontWeightBold 0.400000 #define ourNSFontWeightHeavy 0.560000 #define ourNSFontWeightBlack 0.620000 +#endif // Now remember what I said earlier about having to add the small caps traits after calling the above? This gets a dictionary back so we can do so. CFMutableDictionaryRef extractAttributes(CTFontDescriptorRef desc) @@ -215,83 +214,6 @@ void uiDrawTextFontGetMetrics(uiDrawTextFont *font, uiDrawTextFontMetrics *metri metrics->UnderlineThickness = CTFontGetUnderlineThickness(font->f); } -struct uiDrawTextLayout { - CFMutableAttributedStringRef mas; - CFRange *charsToRanges; - double width; -}; - -uiDrawTextLayout *uiDrawNewTextLayout(const char *str, uiDrawTextFont *defaultFont, double width) -{ - uiDrawTextLayout *layout; - CFAttributedStringRef immutable; - CFMutableDictionaryRef attr; - CFStringRef backing; - CFIndex i, j, n; - - layout = uiNew(uiDrawTextLayout); - - // TODO docs say we need to use a different set of key callbacks - // TODO see if the font attribute key callbacks need to be the same - attr = newAttrList(); - // this will retain defaultFont->f; no need to worry - CFDictionaryAddValue(attr, kCTFontAttributeName, defaultFont->f); - - immutable = CFAttributedStringCreate(NULL, (CFStringRef) [NSString stringWithUTF8String:str], attr); - if (immutable == NULL) - complain("error creating immutable attributed string in uiDrawNewTextLayout()"); - CFRelease(attr); - - layout->mas = CFAttributedStringCreateMutableCopy(NULL, 0, immutable); - if (layout->mas == NULL) - complain("error creating attributed string in uiDrawNewTextLayout()"); - CFRelease(immutable); - - uiDrawTextLayoutSetWidth(layout, width); - - // unfortunately the CFRanges for attributes expect UTF-16 codepoints - // we want graphemes - // fortunately CFStringGetRangeOfComposedCharactersAtIndex() is here for us - // https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Strings/Articles/stringsClusters.html says that this does work on all multi-codepoint graphemes (despite the name), and that this is the preferred function for this particular job anyway - backing = CFAttributedStringGetString(layout->mas); - n = CFStringGetLength(backing); - // allocate one extra, just to be safe - layout->charsToRanges = (CFRange *) uiAlloc((n + 1) * sizeof (CFRange), "CFRange[]"); - i = 0; - j = 0; - while (i < n) { - CFRange range; - - range = CFStringGetRangeOfComposedCharactersAtIndex(backing, i); - i = range.location + range.length; - layout->charsToRanges[j] = range; - j++; - } - // and set the last one - layout->charsToRanges[j].location = i; - layout->charsToRanges[j].length = 0; - - return layout; -} - -void uiDrawFreeTextLayout(uiDrawTextLayout *layout) -{ - uiFree(layout->charsToRanges); - CFRelease(layout->mas); - uiFree(layout); -} - -void uiDrawTextLayoutSetWidth(uiDrawTextLayout *layout, double width) -{ - layout->width = width; -} - -struct framesetter { - CTFramesetterRef fs; - CFMutableDictionaryRef frameAttrib; - CGSize extents; -}; - // LONGTERM allow line separation and leading to be factored into a wrapping text layout // TODO reconcile differences in character wrapping on platforms @@ -357,9 +279,6 @@ void doDrawText(CGContextRef c, CGFloat cheight, double x, double y, uiDrawTextL // LONGTERM keep this for later features and documentation purposes #if 0 - w = CTLineGetTypographicBounds(line, &ascent, &descent, NULL); - // though CTLineGetTypographicBounds() returns 0 on error, it also returns 0 on an empty string, so we can't reasonably check for error - CFRelease(line); // LONGTERM provide a way to get the image bounds as a separate function later bounds = CTLineGetImageBounds(line, c); @@ -372,20 +291,7 @@ void doDrawText(CGContextRef c, CGFloat cheight, double x, double y, uiDrawTextL CGContextSetTextPosition(c, x, y); #endif -static CFRange charsToRange(uiDrawTextLayout *layout, int startChar, int endChar) -{ - CFRange start, end; - CFRange out; - - start = layout->charsToRanges[startChar]; - end = layout->charsToRanges[endChar]; - out.location = start.location; - out.length = end.location - start.location; - return out; -} - -#define rangeToCFRange() charsToRange(layout, startChar, endChar) - +#if 0 void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, int startChar, int endChar, double r, double g, double b, double a) { CGColorSpaceRef colorspace; @@ -407,3 +313,4 @@ void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, int startChar, int endCh color); CGColorRelease(color); // TODO safe? } +#endif diff --git a/darwin/drawtext.m b/darwin/drawtext.m index f5e1acdb..1272b1da 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -7,6 +7,8 @@ struct uiDrawTextLayout { CTFramesetterRef framesetter; CGSize size; CGPathRef path; + CTFrameRef frame; + CFArrayRef lines; }; static CTFontRef fontdescToCTFont(uiDrawFontDescriptor *fd) @@ -93,11 +95,24 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto rect.origin = CGZeroPoint; rect.size = tl->size; tl->path = CGPathCreateWithRect(rect, NULL); + tl->frame = CTFramesetterCreateFrame(tl->framesetter, + range, + tl->path, + // TODO kCTFramePathWidthAttributeName? + NULL); + if (tl->frame == NULL) { + // TODO + } + + tl->lines = CTFrameGetLines(tl->frame); + return tl; } void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { + // TODO release tl->lines? + CFRelease(tl->frame); CFRelease(tl->path); CFRelease(tl->framesetter); CFRelease(tl->attrstr); @@ -114,14 +129,44 @@ void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) { + return CFArrayGetCount(tl->lines); } +// TODO release when done? +#define getline(tl, line) ((CTLineRef) CFArrayGetValueAtIndex(tl->lines, line)) + void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end) { + CTLineRef lr; + CFRange range; + + lr = getline(tl, line); + range = CTLineGetStringRange(lr); + // TODO set start and end } void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m) { + CTLineRef lr; + CFRange range; + CGPoint origin; + CGFloat ascent, descent, leading; + + range.location = line; + range.length = 1; + CTFrameGetLineOrigins(tl->frame, range, &origin); + // TODO how exactly do we adjust this by CGPathGetPathBoundingBox(tl->path)? + m->X = origin.x; + m->Y = origin.y; + // TODO is m->Y the baseline position? + // TODO is m->Y flipped? + + lr = getline(tl, line); + // though CTLineGetTypographicBounds() returns 0 on error, it also returns 0 on an empty string, so we can't reasonably check for error + m->Width = CTLineGetTypographicBounds(lr, ascent, descent, leading); + m->Ascent = ascent; + m->Descent = descent; + m->Leading = leading; } void uiDrawTextLayoutByteIndexToGraphemeRect(uiDrawTextLayout *tl, size_t pos, int *line, double *x, double *y, double *width, double *height) diff --git a/ui_attrstr.h b/ui_attrstr.h index c7e61667..4cf7d761 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -97,6 +97,7 @@ struct uiDrawTextLayoutLineMetrics { double Ascent; double Descent; double Leading; + // TODO trailing whitespace? }; _UI_ENUM(uiDrawTextLayoutHitTestResult) { From 3910ff1a13568fc153a42c68625f8473998876bb Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 5 Jan 2017 17:55:05 -0500 Subject: [PATCH 0540/1329] Resolved Core Text pain by not even thinking about lines in terms of boxes. --- darwin/drawtext.m | 11 +++++++---- ui_attrstr.h | 6 ++++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 1272b1da..8d5c400d 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -119,12 +119,16 @@ void uiDrawFreeTextLayout(uiDrawTextLayout *tl) uiFree(tl); } +// TODO what is y, the top-left corner, bottom-left corner, topmost baseline, or bottommost baseline? void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) { } void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height) { + // TODO how exactly do we adjust this by CGPathGetPathBoundingBox(tl->path)? + *width = tl->size.width; + *height = tl->size.height; } int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) @@ -157,13 +161,12 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa CTFrameGetLineOrigins(tl->frame, range, &origin); // TODO how exactly do we adjust this by CGPathGetPathBoundingBox(tl->path)? m->X = origin.x; - m->Y = origin.y; - // TODO is m->Y the baseline position? - // TODO is m->Y flipped? + m->BaselineY = origin.y; + // TODO m->Y is flipped lr = getline(tl, line); // though CTLineGetTypographicBounds() returns 0 on error, it also returns 0 on an empty string, so we can't reasonably check for error - m->Width = CTLineGetTypographicBounds(lr, ascent, descent, leading); + m->Width = CTLineGetTypographicBounds(lr, &ascent, &descent, &leading); m->Ascent = ascent; m->Descent = descent; m->Leading = leading; diff --git a/ui_attrstr.h b/ui_attrstr.h index 4cf7d761..1d0c4696 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -90,10 +90,12 @@ typedef struct uiDrawTextLayoutLineMetrics uiDrawTextLayoutLineMetrics; typedef struct uiDrawTextLayoutByteRangeRectangle uiDrawTextLayoutByteRangeRectangle; struct uiDrawTextLayoutLineMetrics { + // TODO figure out if this is correct regardless of both alignment and writing direction double X; - double Y; + double BaselineY; double Width; - // height = ascent + descent + leading (TODO formally document) + // top-left Y = baseline Y - ascent + // height = ascent + descent + leading (TODO formally document all this) double Ascent; double Descent; double Leading; From 747a0bbfada53e9a8fb4e115d2edcd52ffa063a3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 5 Jan 2017 17:56:47 -0500 Subject: [PATCH 0541/1329] More TODOs. --- darwin/drawtext.m | 1 + 1 file changed, 1 insertion(+) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 8d5c400d..944795c0 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -124,6 +124,7 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) { } +// TODO document that the width and height of a layout is not necessarily the sum of the widths and heights of its constituent lines; this is definitely untrue on OS X, where lines are placed in such a way that the distance between baselines is always integral void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height) { // TODO how exactly do we adjust this by CGPathGetPathBoundingBox(tl->path)? From 6212ac7238f00e3365d154a0e59f88fc849233f2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 5 Jan 2017 21:36:07 -0500 Subject: [PATCH 0542/1329] And integrated the rest of our important tests in. --- darwin/drawtext.m | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 944795c0..cdf766f5 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -5,6 +5,11 @@ struct uiDrawTextLayout { CFAttributedStringRef attrstr; double width; CTFramesetterRef framesetter; + // note: technically, metrics returned from frame are relative to CGPathGetPathBoundingBox(tl->path) + // however, from what I can gather, for a path created by CGPathCreateWithRect(), like we do (with a NULL transform), CGPathGetPathBoundingBox() seems to just return the standardized form of the rect used to create the path + // (this I confirmed through experimentation) + // so we can just use tl->size for adjustments + // we don't need to adjust coordinates by any origin since our rect origin is (0, 0) CGSize size; CGPathRef path; CTFrameRef frame; @@ -127,7 +132,6 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) // TODO document that the width and height of a layout is not necessarily the sum of the widths and heights of its constituent lines; this is definitely untrue on OS X, where lines are placed in such a way that the distance between baselines is always integral void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height) { - // TODO how exactly do we adjust this by CGPathGetPathBoundingBox(tl->path)? *width = tl->size.width; *height = tl->size.height; } @@ -160,10 +164,9 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa range.location = line; range.length = 1; CTFrameGetLineOrigins(tl->frame, range, &origin); - // TODO how exactly do we adjust this by CGPathGetPathBoundingBox(tl->path)? m->X = origin.x; - m->BaselineY = origin.y; - // TODO m->Y is flipped + // and remember that the frame is flipped + m->BaselineY = tl->size.height - origin.y; lr = getline(tl, line); // though CTLineGetTypographicBounds() returns 0 on error, it also returns 0 on an empty string, so we can't reasonably check for error From 1bd2ca22c234b8e6d56cdffd0e741871a4e08364 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 6 Jan 2017 23:53:23 -0500 Subject: [PATCH 0543/1329] Some more work on the new Cocoa text drawing code. --- darwin/CMakeLists.txt | 1 + darwin/_old_drawtext.m | 48 ------------------------------------------ darwin/draw.h | 6 ++++++ darwin/draw.m | 6 +----- darwin/drawtext.m | 25 +++++++++++++++++++++- 5 files changed, 32 insertions(+), 54 deletions(-) create mode 100644 darwin/draw.h diff --git a/darwin/CMakeLists.txt b/darwin/CMakeLists.txt index 385a4a53..32d1b45e 100644 --- a/darwin/CMakeLists.txt +++ b/darwin/CMakeLists.txt @@ -44,6 +44,7 @@ list(APPEND _LIBUI_SOURCES ) set(_LIBUI_SOURCES ${_LIBUI_SOURCES} PARENT_SCOPE) +// TODO is this correct? list(APPEND _LIBUI_INCLUDEDIRS darwin ) diff --git a/darwin/_old_drawtext.m b/darwin/_old_drawtext.m index 14346784..72a8d2e6 100644 --- a/darwin/_old_drawtext.m +++ b/darwin/_old_drawtext.m @@ -227,54 +227,6 @@ void uiDrawTextLayoutExtents(uiDrawTextLayout *layout, double *width, double *he freeFramesetter(&fs); } -// Core Text doesn't draw onto a flipped view correctly; we have to do this -// see the iOS bits of the first example at https://developer.apple.com/library/mac/documentation/StringsTextFonts/Conceptual/CoreText_Programming/LayoutOperations/LayoutOperations.html#//apple_ref/doc/uid/TP40005533-CH12-SW1 (iOS is naturally flipped) -// TODO how is this affected by the CTM? -static void prepareContextForText(CGContextRef c, CGFloat cheight, double *y) -{ - CGContextSaveGState(c); - CGContextTranslateCTM(c, 0, cheight); - CGContextScaleCTM(c, 1.0, -1.0); - CGContextSetTextMatrix(c, CGAffineTransformIdentity); - - // wait, that's not enough; we need to offset y values to account for our new flipping - *y = cheight - *y; -} - -// TODO placement is incorrect for Helvetica -void doDrawText(CGContextRef c, CGFloat cheight, double x, double y, uiDrawTextLayout *layout) -{ - struct framesetter fs; - CGRect rect; - CGPathRef path; - CTFrameRef frame; - - prepareContextForText(c, cheight, &y); - mkFramesetter(layout, &fs); - - // oh, and since we're flipped, y is the bottom-left coordinate of the rectangle, not the top-left - // since we are flipped, we subtract - y -= fs.extents.height; - - rect.origin = CGPointMake(x, y); - rect.size = fs.extents; - path = CGPathCreateWithRect(rect, NULL); - - frame = CTFramesetterCreateFrame(fs.fs, - CFRangeMake(0, 0), - path, - fs.frameAttrib); - if (frame == NULL) - complain("error creating CTFrame object in doDrawText()"); - CTFrameDraw(frame, c); - CFRelease(frame); - - CFRelease(path); - - freeFramesetter(&fs); - CGContextRestoreGState(c); -} - // LONGTERM provide an equivalent to CTLineGetTypographicBounds() on uiDrawTextLayout? // LONGTERM keep this for later features and documentation purposes diff --git a/darwin/draw.h b/darwin/draw.h new file mode 100644 index 00000000..41629809 --- /dev/null +++ b/darwin/draw.h @@ -0,0 +1,6 @@ +// 6 january 2017 + +struct uiDrawContext { + CGContextRef c; + CGFloat height; // needed for text; see below +}; diff --git a/darwin/draw.m b/darwin/draw.m index 262ad3e2..231b4dfd 100644 --- a/darwin/draw.m +++ b/darwin/draw.m @@ -1,5 +1,6 @@ // 6 september 2015 #import "uipriv_darwin.h" +#import "draw.h" struct uiDrawPath { CGMutablePathRef path; @@ -103,11 +104,6 @@ void uiDrawPathEnd(uiDrawPath *p) p->ended = TRUE; } -struct uiDrawContext { - CGContextRef c; - CGFloat height; // needed for text; see below -}; - uiDrawContext *newContext(CGContextRef ctxt, CGFloat height) { uiDrawContext *c; diff --git a/darwin/drawtext.m b/darwin/drawtext.m index cdf766f5..a7a400f7 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -1,5 +1,6 @@ // 2 january 2017 #import "uipriv_darwin.h" +#import "draw.h" struct uiDrawTextLayout { CFAttributedStringRef attrstr; @@ -124,9 +125,31 @@ void uiDrawFreeTextLayout(uiDrawTextLayout *tl) uiFree(tl); } -// TODO what is y, the top-left corner, bottom-left corner, topmost baseline, or bottommost baseline? +// TODO double-check helvetica +// TODO document that (x,y) is the top-left corner of the *entire frame* void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) { + CGContextSaveGState(c->c); + + // Core Text doesn't draw onto a flipped view correctly; we have to pretend it was unflipped + // see the iOS bits of the first example at https://developer.apple.com/library/mac/documentation/StringsTextFonts/Conceptual/CoreText_Programming/LayoutOperations/LayoutOperations.html#//apple_ref/doc/uid/TP40005533-CH12-SW1 (iOS is naturally flipped) + // TODO how is this affected by a non-identity CTM? + CGContextTranslateCTM(c->c, 0, cheight); + CGContextScaleCTM(c->c, 1.0, -1.0); + CGContextSetTextMatrix(c->c, CGAffineTransformIdentity); + + // wait, that's not enough; we need to offset y values to account for our new flipping + y = c->height - y; + + // CTFrameDraw() draws in the path we specified when creating the frame + // this means that in our usage, CTFrameDraw() will draw at (0,0) + // so move the origin to be at (x,y) instead + // TODO are the signs correct? + CGContextTranslateCTM(c->c, x, y); + + CTFrameDraw(tl->frame, c->c); + + CGContextRestoreGState(c->c); } // TODO document that the width and height of a layout is not necessarily the sum of the widths and heights of its constituent lines; this is definitely untrue on OS X, where lines are placed in such a way that the distance between baselines is always integral From e32341b24bf4874be2d8bfc9664fbd13bf96fcf5 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 7 Jan 2017 20:09:44 -0500 Subject: [PATCH 0544/1329] More work. --- common/attrstr.c | 17 ++++++++ common/uipriv.h | 4 ++ darwin/drawtext.m | 99 ++++++++++++++++++++++++++++++++++++++++++++++- ui_attrstr.h | 24 +++++++++--- 4 files changed, 137 insertions(+), 7 deletions(-) diff --git a/common/attrstr.c b/common/attrstr.c index 102d56eb..76a4214b 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -312,3 +312,20 @@ size_t attrstrUTF16LEn(uiAttributedString *s) { return s->u16len; } + +size_t attrstrUTF8ToUTF16(uiAttributedString *s, size_t n) +{ + return s->u8tou16[n]; +} + +size_t *attrstrCopyUTF16ToUTF8(uiAttributedString *s, size_t *n) +{ + size_t *out; + size_t nbytes; + + nbytes = (s->u16len + 1) * sizeof (size_t); + *n = s->u16len; + out = (size_t *) uiAlloc(nbytes, "size_t[] (uiAttributedString)"); + memmove(out, s->u16tou8, nbytes); + return out; +} diff --git a/common/uipriv.h b/common/uipriv.h index 31d941ff..f979e4f6 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -64,9 +64,13 @@ struct graphemes { extern int graphemesTakesUTF16(void); extern struct graphemes *graphemes(void *s, size_t len); +// TODO split these into a separate header file? + // attrstr.c extern const uint16_t *attrstrUTF16(uiAttributedString *s); extern size_t attrstrUTF16LEn(uiAttributedString *s); +extern size_t attrstrUTF8ToUTF16(uiAttributedString *s, size_t n); +extern size_t *attrstrCopyUTF16ToUTF8(uiAttributedString *s, size_t *n); // attrlist.c struct attrlist; diff --git a/darwin/drawtext.m b/darwin/drawtext.m index a7a400f7..45e24470 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -15,6 +15,8 @@ struct uiDrawTextLayout { CGPathRef path; CTFrameRef frame; CFArrayRef lines; + size_t *u16tou8; + size_t nu16tou8; // TODO I don't like the casing of this name }; static CTFontRef fontdescToCTFont(uiDrawFontDescriptor *fd) @@ -112,11 +114,15 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto tl->lines = CTFrameGetLines(tl->frame); + // and finally copy the UTF-16 to UTF-8 index conversion table + tl->u16tou8 = attrstrCopyUTF16ToUTF8(s, &(tl->nu16tou8)); + return tl; } void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { + uiFree(tl->u16tou8); // TODO release tl->lines? CFRelease(tl->frame); CFRelease(tl->path); @@ -174,7 +180,8 @@ void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start lr = getline(tl, line); range = CTLineGetStringRange(lr); - // TODO set start and end + *start = tl->u16tou8[range.location]; + *end = tl->u16tou8[range.location + range.length]; } void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m) @@ -203,8 +210,96 @@ void uiDrawTextLayoutByteIndexToGraphemeRect(uiDrawTextLayout *tl, size_t pos, i { } -uiDrawTextLayoutHitTestResult uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *byteIndex, int *line) +static CGPoint *mkLineOrigins(uiDrawTextLayout *tl) { + CGPoint *origins; + CFRange range; + CFIndex i, n; + CTLine line; + CGFloat ascent; + + n = CFArrayGetCount(tl->lines); + range.location = 0; + range.length = n; + origins = (CGPoint *) uiAlloc(n * sizeof (CGPoint), "CGPoint[]"); + CTFrameGetLineOrigins(tl->frame, range, origins); + for (i = 0; i < n; i++) { + line = getline(tl, i); + CTLineGetTypographicBounds(line, &ascent, NULL, NULL); + origins[i].y = tl->size.height - (origins[i].y + ascent); + } + return origins; +} + +void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) +{ + CGPoint *mkLineOrigins; + CFIndex i, n; + CTLineRef line; + double firstYForLine; + CGFloat width, NULL, descent, leading; + CFRange range; + + n = CFArrayGetCount(tl->lines); + if (n == 0) { + // TODO fill result + return; + } + + origins = mkLineOrigins(tl); + if (y < 0) { + line = getline(tl, 0); + width = CTLineGetTypographicBounds(line, NULL, NULL, NULL); + i = 0; + } else { + firstYForLine = 0; + for (i = 0; i < n; i++) { + line = getline(tl, i); + width = CTLineGetTypographicBounds(line, NULL, &descent, &leading); + if (y < maxYForLine) + break; + firstYForLine = origins[i].y + descent + leading; + } + } + if (i == n) { + i--; + result->Line = i; + result->YPosition = uiDrawTextLayoutHitTestPositionAfter; + } else { + result->Line = i; + result->YPosition = uiDrawTextLayoutHitTestPositionInside; + if (i == 0 && y < 0) + result->YPosition = uiDrawTextLayoutHitTestPositionBefore; + } + result->InTrailingWhitespace = 0; + range = CTLineGetStringRange(line); + if (x < 0) { + result->Start = tl->u16tou8[range.location]; + result->End = result->Start; + result->XPosition = uiDrawTextLayoutHitTestPositionBefore; + } else if (x > tl->size.width) { + result->Start = tl->u16tou8[range.location + range.length]; + result->End = result->Start; + result->XPosition = uiDrawTextLayoutHitTestPositionAfter; + } else { + CGPoint pos; + CFIndex index; + + result->XPosition = uiDrawTextLa +youtHitTestPositionInside; + pos.x = x; + // TODO this isn't set properly in any of the fast-track cases + pos.y = y - firstYForLine; + index = CTLineGetStringIn +dexForPosition(line, pos); + if (index == kCFNotFound) { + // TODO + } + result->Pos = tl->u16tou8[index]; + // TODO compute the fractional offset + result->InTrailingWhitespace = x < origins[i].x || x >= (origins[i].x + width); + } + uiFree(origins); } void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r) diff --git a/ui_attrstr.h b/ui_attrstr.h index 1d0c4696..a9c6ccab 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -87,6 +87,7 @@ struct uiDrawFontDescriptor { typedef struct uiDrawTextLayout uiDrawTextLayout; typedef struct uiDrawTextLayoutLineMetrics uiDrawTextLayoutLineMetrics; +typedef struct uiDrawTextLayoutHitTestResult uiDrawTextLayoutHitTestResult; typedef struct uiDrawTextLayoutByteRangeRectangle uiDrawTextLayoutByteRangeRectangle; struct uiDrawTextLayoutLineMetrics { @@ -102,10 +103,19 @@ struct uiDrawTextLayoutLineMetrics { // TODO trailing whitespace? }; -_UI_ENUM(uiDrawTextLayoutHitTestResult) { - uiDrawTextLayoutHitTestResultNowhere, - uiDrawTextLayoutHitTestResultOnLineTrailingWhitespace, - uiDrawTextLayoutHitTestResultOnCharacter, +_UI_ENUM(uiDrawTextLayoutHitTestPosition) { + uiDrawTextLayoutHitTestPositionBefore, + uiDrawTextLayoutHitTestPositionInside, + uiDrawTextLayoutHitTestPositionAfter, +}; + +struct uiDrawTextLayoutHitTestResult { + size_t Pos; + int Line; + uiDrawTextLayoutHitTestPosition XPosition; + uiDrawTextLayoutHitTestPosition YPosition; + int InTrailingWhitespace; + double XFraction; }; struct uiDrawTextLayoutByteRangeRectangle { @@ -122,6 +132,10 @@ struct uiDrawTextLayoutByteRangeRectangle { // - allow creating a layout out of a substring // - allow marking compositon strings // - allow marking selections, even after creation +// - add the following functions: +// - uiDrawTextLayoutHeightForWidth() (returns the height that a layout would need to be to display the entire string at a given width) +// - uiDrawTextLayoutRangeForSize() (returns what substring would fit in a given size) +// - uiDrawTextLayoutNewWithHeight() (limits amount of string used by the height) _UI_EXTERN uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescriptor *defaultFont, double width); _UI_EXTERN void uiDrawFreeTextLayout(uiDrawTextLayout *tl); _UI_EXTERN void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y); @@ -132,7 +146,7 @@ _UI_EXTERN void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, u // TODO redo this? remove it entirely? _UI_EXTERN void uiDrawTextLayoutByteIndexToGraphemeRect(uiDrawTextLayout *tl, size_t pos, int *line, double *x, double *y, double *width, double *height); // TODO partial offset? -_UI_EXTERN uiDrawTextLayoutHitTestResult uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *byteIndex, int *line); +_UI_EXTERN void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result); _UI_EXTERN void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r); // TODO draw only a line? // TODO other layout-specific attributes (alignment, wrapping, etc.)? From e63a42a2904f1382a89ce77673f464826b1d8a2b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 17 Jan 2017 02:17:12 -0500 Subject: [PATCH 0545/1329] Okay, now that I know what I'm doing, let's make the line metrics struct useful again. --- ui_attrstr.h | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index a9c6ccab..923e3857 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -91,15 +91,33 @@ typedef struct uiDrawTextLayoutHitTestResult uiDrawTextLayoutHitTestResult; typedef struct uiDrawTextLayoutByteRangeRectangle uiDrawTextLayoutByteRangeRectangle; struct uiDrawTextLayoutLineMetrics { - // TODO figure out if this is correct regardless of both alignment and writing direction + // This describes the overall bounding box of the line. + // TODO figure out if X is correct regardless of both alignment and writing direction double X; - double BaselineY; + double Y; double Width; - // top-left Y = baseline Y - ascent - // height = ascent + descent + leading (TODO formally document all this) + double Height; + + // This describes the typographic bounds of the line. + double BaselineY; double Ascent; double Descent; double Leading; + + // This describes any additional whitespace. + // TODO come up with better names for these. + double ParagraphSpacingBefore; + double LineHeightSpace; + double LineSpacing; + double ParagraphSpacing; + + // Height should equal ParagraphSpacingBefore + LineHeightSpace + Ascent + Descent + Leading + LineSpacing + ParagraphSpacing. + // The above values are listed in vertical order, from top to bottom. + // Ascent + Descent + Leading will give you the typographic bounds of the text. + // BaselineY will be the boundary between Ascent and Descent. + // X, Y, and BaselineY are all in the layout's coordinate system, so the start point of the baseline will be at (X, BaselineY). + // All values will be nonnegative. + // TODO trailing whitespace? }; From 794d30154cb8bd4a819d8e4ef822ffcaa4261a42 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 17 Jan 2017 12:02:42 -0500 Subject: [PATCH 0546/1329] And rewrote drawtext.m based around the new Core Text research. --- darwin/drawtext.m | 234 +++++++++++++++++++++++++--------------------- ui_attrstr.h | 6 +- 2 files changed, 133 insertions(+), 107 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 45e24470..6ddeedb9 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -2,19 +2,33 @@ #import "uipriv_darwin.h" #import "draw.h" +// TODO what happens if nLines == 0 in any function? + struct uiDrawTextLayout { CFAttributedStringRef attrstr; + + // the width as passed into uiDrawTextLayout constructors double width; + CTFramesetterRef framesetter; + + // the *actual* size of the frame // note: technically, metrics returned from frame are relative to CGPathGetPathBoundingBox(tl->path) // however, from what I can gather, for a path created by CGPathCreateWithRect(), like we do (with a NULL transform), CGPathGetPathBoundingBox() seems to just return the standardized form of the rect used to create the path // (this I confirmed through experimentation) // so we can just use tl->size for adjustments // we don't need to adjust coordinates by any origin since our rect origin is (0, 0) CGSize size; + CGPathRef path; CTFrameRef frame; + CFArrayRef lines; + CFIndex nLines; + // we compute this once when first creating the layout + uiDrawTextLayoutLineMetrics *lineMetrics; + + // for converting CFAttributedString indices to byte offsets size_t *u16tou8; size_t nu16tou8; // TODO I don't like the casing of this name }; @@ -68,6 +82,77 @@ static CFAttributedStringRef attrstrToCoreFoundation(uiAttributedString *s, uiDr return mas; } +static uiDrawTextLayoutLineMetrics *computeLineMetrics(CTFrame frame, CGSize size) +{ + uiDrawTextLayoutLineMetrics *metrics; + CFArray lines; + CTLineRef line; + CFIndex i, n; + CGFloat ypos; + CGRect bounds, boundsNoLeading; + CGFloat ascent, descent, leading; + CGPoint *origins; + + lines = CTFrameGetLines(frame); + n = CFArrayGetCount(lines); + metrics = (uiDrawTextLay +outLineMetrics *) uiAlloc(n * sizeof (uiDrawTextLayoutLineMetrics), "uiDrawTextLayoutLineMetrics[] (text layout)"); + + origins = (CGFloat *) uiAlloc(n * sizeof (CGFloat), "CGFloat[] (text layout)"); + CTFrameGetLineOrigins(frame, CFRangeMake(0, n), origins); + + ypos = size.height; + for (i = 0; i < n; i++) { + line = (CTLineRef) CFArrayGetValueAtIndex(lines, i); + bounds = CTLineGetBoundsWithOptions(line, 0); + boundsNoLeading = CTLineGetBoundsWithOptions(line, kCTLineBoundsExcludeTypographicLeading); + + // this is equivalent to boundsNoLeading.size.height + boundsNoLeading.origin.y (manually verified) + ascent = bounds.size.height + bounds.origin.y; + descent = -boundsNoLeading.origin.y; + // TODO does this preserve leading sign? + leading = -bounds.origin.y - descent; + + // Core Text always rounds these up for paragraph style calculations; there is a flag to control it but it's inaccessible (and this behavior is turned off for old versions of iPhoto) + ascent = floor(ascent + 0.5); + descent = floor(descent + 0.5); + if (leading > 0) + leading = floor(leading + 0.5); + + metrics[i].X = origins[i].x; + metrics[i].Y = origins[i].y - descent - leading; + metrics[i].Width = bounds.size.width; + metrics[i].Height = ascent + descent + leading; + + metrics[i].BaselineY = origins[i].y; + metrics[i].Ascent = ascent; + metrics[i].Descent = descent; + metrics[i].Leading = leading; + + // TODO + metrics[i].ParagraphSpacingBefore = 0; + metrics[i].LineHeightSpace = 0; + metrics[i].LineSpacing = 0; + metrics[i].ParagraphSpacing = 0; + + // and finally advance to the next line + ypos += metrics[i].Height; + } + + // okay, but now all these metrics are unflipped + // we need to flip them + for (i = 0; i < n; i++) { + metrics[i].Y = size.height - metrics[i].Y; + // go from bottom-left corner to top-left + metrics[i].Y -= metrics[i].Height; + metrics[i].BaselineY = size.height - metrics[i].BaselineY; + // TODO also adjust by metrics[i].Height? + } + + uiFree(origins); + return metrics; +} + uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescriptor *defaultFont, double width) { uiDrawTextLayout *tl; @@ -113,6 +198,8 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto } tl->lines = CTFrameGetLines(tl->frame); + tl->nLines = CFArrayGetCount(tl->lines); + tl->lineMetrics = computeLineMetrics(tl->frame, tl->size); // and finally copy the UTF-16 to UTF-8 index conversion table tl->u16tou8 = attrstrCopyUTF16ToUTF8(s, &(tl->nu16tou8)); @@ -123,6 +210,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { uiFree(tl->u16tou8); + uiFree(tl->lineMetrics); // TODO release tl->lines? CFRelease(tl->frame); CFRelease(tl->path); @@ -131,7 +219,6 @@ void uiDrawFreeTextLayout(uiDrawTextLayout *tl) uiFree(tl); } -// TODO double-check helvetica // TODO document that (x,y) is the top-left corner of the *entire frame* void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) { @@ -167,18 +254,15 @@ void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) { - return CFArrayGetCount(tl->lines); + return tl->nLines; } -// TODO release when done? -#define getline(tl, line) ((CTLineRef) CFArrayGetValueAtIndex(tl->lines, line)) - void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end) { CTLineRef lr; CFRange range; - lr = getline(tl, line); + lr = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, line); range = CTLineGetStringRange(lr); *start = tl->u16tou8[range.location]; *end = tl->u16tou8[range.location + range.length]; @@ -186,120 +270,60 @@ void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m) { - CTLineRef lr; - CFRange range; - CGPoint origin; - CGFloat ascent, descent, leading; - - range.location = line; - range.length = 1; - CTFrameGetLineOrigins(tl->frame, range, &origin); - m->X = origin.x; - // and remember that the frame is flipped - m->BaselineY = tl->size.height - origin.y; - - lr = getline(tl, line); - // though CTLineGetTypographicBounds() returns 0 on error, it also returns 0 on an empty string, so we can't reasonably check for error - m->Width = CTLineGetTypographicBounds(lr, &ascent, &descent, &leading); - m->Ascent = ascent; - m->Descent = descent; - m->Leading = leading; + *m = tl->lineMetrics[line]; } void uiDrawTextLayoutByteIndexToGraphemeRect(uiDrawTextLayout *tl, size_t pos, int *line, double *x, double *y, double *width, double *height) { } -static CGPoint *mkLineOrigins(uiDrawTextLayout *tl) -{ - CGPoint *origins; - CFRange range; - CFIndex i, n; - CTLine line; - CGFloat ascent; - - n = CFArrayGetCount(tl->lines); - range.location = 0; - range.length = n; - origins = (CGPoint *) uiAlloc(n * sizeof (CGPoint), "CGPoint[]"); - CTFrameGetLineOrigins(tl->frame, range, origins); - for (i = 0; i < n; i++) { - line = getline(tl, i); - CTLineGetTypographicBounds(line, &ascent, NULL, NULL); - origins[i].y = tl->size.height - (origins[i].y + ascent); - } - return origins; -} - void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) { - CGPoint *mkLineOrigins; - CFIndex i, n; - CTLineRef line; - double firstYForLine; - CGFloat width, NULL, descent, leading; - CFRange range; + CFIndex i; + CTLine line; + CFIndex pos; + CGFloat charLeft, charRight; - n = CFArrayGetCount(tl->lines); - if (n == 0) { - // TODO fill result - return; - } + if (y >= 0) { + for (i = 0; i < tl->nLines; i++) { + double ltop, lbottom; - origins = mkLineOrigins(tl); - if (y < 0) { - line = getline(tl, 0); - width = CTLineGetTypographicBounds(line, NULL, NULL, NULL); - i = 0; - } else { - firstYForLine = 0; - for (i = 0; i < n; i++) { - line = getline(tl, i); - width = CTLineGetTypographicBounds(line, NULL, &descent, &leading); - if (y < maxYForLine) + ltop = tl->lineMetrics[i].Y; + lbottom = ltop + tl->lineMetrics[i].Height; + if (y >= ltop && y < lbottom) break; - firstYForLine = origins[i].y + descent + leading; } - } - if (i == n) { - i--; - result->Line = i; - result->YPosition = uiDrawTextLayoutHitTestPositionAfter; - } else { - result->Line = i; result->YPosition = uiDrawTextLayoutHitTestPositionInside; - if (i == 0 && y < 0) - result->YPosition = uiDrawTextLayoutHitTestPositionBefore; - } - result->InTrailingWhitespace = 0; - range = CTLineGetStringRange(line); - if (x < 0) { - result->Start = tl->u16tou8[range.location]; - result->End = result->Start; - result->XPosition = uiDrawTextLayoutHitTestPositionBefore; - } else if (x > tl->size.width) { - result->Start = tl->u16tou8[range.location + range.length]; - result->End = result->Start; - result->XPosition = uiDrawTextLayoutHitTestPositionAfter; - } else { - CGPoint pos; - CFIndex index; - - result->XPosition = uiDrawTextLa -youtHitTestPositionInside; - pos.x = x; - // TODO this isn't set properly in any of the fast-track cases - pos.y = y - firstYForLine; - index = CTLineGetStringIn -dexForPosition(line, pos); - if (index == kCFNotFound) { - // TODO + if (i == tl->nLines) { + i--; + result->YPosition = uiDrawTextLayoutHitTestPositionAfter; } - result->Pos = tl->u16tou8[index]; - // TODO compute the fractional offset - result->InTrailingWhitespace = x < origins[i].x || x >= (origins[i].x + width); + } else { + i = 0; + result->YPosition = uiDrawTextLayoutHitTestPositionBefore; } - uiFree(origins); + m->Line = i; + + result->XPosition = uiDrawTextLayoutHitTestPositionInside; + if (x < tl->lineMetrics[i].X) { + result->XPosition = uiDrawTextLay +outHitTestPositionBefore; + // and forcibly return the first character + x = tl->lineMetrics[i].X; + } else if (x > (tl->lineMetrics[i].X + tl->lineMetrics[i].Width)) { + result->XPosition = uiDrawTextLayoutHitTestP +ositionAfter; + // and forcibly return the last character + x = tl->lineMetrics[i].X + tl->lineMetrics[i].Width; + } + + line = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, i); + pos = CTLineGetStringIndexForPosition(line, CGP +ointMake(x, 0)); + if (pos == kCFNotFound) { + // TODO + } + m->Pos = tl->u16tou8[pos]; } void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r) diff --git a/ui_attrstr.h b/ui_attrstr.h index 923e3857..7cfedb10 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -132,8 +132,10 @@ struct uiDrawTextLayoutHitTestResult { int Line; uiDrawTextLayoutHitTestPosition XPosition; uiDrawTextLayoutHitTestPosition YPosition; - int InTrailingWhitespace; - double XFraction; +// TODO? +// int InTrailingWhitespace; +// TODO? +// double XFraction; }; struct uiDrawTextLayoutByteRangeRectangle { From 7bda3baee3e06056eae59801baf1eace6a06b08f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 17 Jan 2017 13:05:40 -0500 Subject: [PATCH 0547/1329] Fixed build errors. DOES IT WORK?!?!?!?!?!?!?!?! --- common/attrlist.c | 14 ++++++++------ common/attrstr.c | 12 ++++++------ common/uipriv.h | 7 ++++--- darwin/CMakeLists.txt | 4 ++-- darwin/draw.m | 5 ----- darwin/drawtext.m | 32 ++++++++++++++------------------ darwin/fontmatch.m | 4 ++-- darwin/graphemes.m | 2 +- darwin/uipriv_darwin.h | 6 ++++++ ui.h | 2 ++ 10 files changed, 45 insertions(+), 43 deletions(-) diff --git a/common/attrlist.c b/common/attrlist.c index bae1203b..f65120f7 100644 --- a/common/attrlist.c +++ b/common/attrlist.c @@ -168,7 +168,7 @@ static struct attr *attrDropRange(struct attrlist *alist, struct attr *a, size_t // we are dropping the left half, so set a->start and unlink a->start = end; *tail = a; - return attrUnlink(attr, a); + return attrUnlink(alist, a); } if (a->end == end) { // chop off the end // we are dropping the right half, so just set a->end @@ -321,11 +321,11 @@ void attrlistInsertAttribute(struct attrlist *alist, uiAttribute type, uintptr_t // TODO will this cause problems with fonts? // TODO will this reduce fragmentation if we first add from 0 to 2 and then from 2 to 4? or do we have to do that separately? if (before->val == val) { - attrGrow(alist, a, start, end); + attrGrow(alist, before, start, end); return; } // okay the values are different; we need to split apart - before = attrDropRange(alist, a, start, end, &tail); + before = attrDropRange(alist, before, start, end, &tail); split = 1; continue; @@ -486,9 +486,10 @@ void attrlistRemoveAttribute(struct attrlist *alist, uiAttribute type, size_t st struct attr *tails = NULL; // see attrlistInsertCharactersUnattributed() above struct attr *tailsAt = NULL; - a = alist->start; + a = alist->first; while (a != NULL) { size_t lstart, lend; + struct attr *tail; // this defines where to re-attach the tails // (all the tails will have their start at end, so we can just insert them all before tailsAt) @@ -532,9 +533,10 @@ void attrlistRemoveAttributes(struct attrlist *alist, size_t start, size_t end) struct attr *tails = NULL; // see attrlistInsertCharactersUnattributed() above struct attr *tailsAt = NULL; - a = alist->start; + a = alist->first; while (a != NULL) { size_t lstart, lend; + struct attr *tail; // this defines where to re-attach the tails // (all the tails will have their start at end, so we can just insert them all before tailsAt) @@ -578,7 +580,7 @@ void attrlistRemoveCharacters(struct attrlist *alist, size_t start, size_t end) a = attrDeleteRange(alist, a, start, end); } -void attrlistForEach(struct attr *alist, uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data) +void attrlistForEach(struct attrlist *alist, uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data) { struct attr *a; diff --git a/common/attrstr.c b/common/attrstr.c index 76a4214b..891453cd 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -120,7 +120,7 @@ void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *s { uint32_t rune; char buf[4]; - uint16_t u16buf[2]; + uint16_t buf16[2]; size_t n8, n16; size_t old, old16; size_t oldlen, old16len; @@ -238,20 +238,20 @@ void uiAttributedStringDelete(uiAttributedString *s, size_t start, size_t end) memmove( s->s + start, s->s + end, - (oldlen - end) * sizeof (char)); + (s->len - end) * sizeof (char)); memmove( s->u16 + start16, s->u16 + end16, - (old16len - end16) * sizeof (uint16_t)); + (s->u16len - end16) * sizeof (uint16_t)); // note the + 1 for these; we want to copy the terminating null too memmove( s->u8tou16 + start, s->u8tou16 + end, - (oldlen - end + 1) * sizeof (size_t)); + (s->len - end + 1) * sizeof (size_t)); memmove( s->u16tou8 + start16, s->u16tou8 + end16, - (old16len - end16 + 1) * sizeof (size_t)); + (s->u16len - end16 + 1) * sizeof (size_t)); // update the conversion tables // note the use of <= to include the null terminator @@ -308,7 +308,7 @@ const uint16_t *attrstrUTF16(uiAttributedString *s) return s->u16; } -size_t attrstrUTF16LEn(uiAttributedString *s) +size_t attrstrUTF16Len(uiAttributedString *s) { return s->u16len; } diff --git a/common/uipriv.h b/common/uipriv.h index f979e4f6..5ed23bf5 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -5,6 +5,7 @@ extern "C" { #endif #include +#include #include "controlsigs.h" #include "utf.h" @@ -68,7 +69,7 @@ extern struct graphemes *graphemes(void *s, size_t len); // attrstr.c extern const uint16_t *attrstrUTF16(uiAttributedString *s); -extern size_t attrstrUTF16LEn(uiAttributedString *s); +extern size_t attrstrUTF16Len(uiAttributedString *s); extern size_t attrstrUTF8ToUTF16(uiAttributedString *s, size_t n); extern size_t *attrstrCopyUTF16ToUTF8(uiAttributedString *s, size_t *n); @@ -79,8 +80,8 @@ extern void attrlistInsertCharactersUnattributed(struct attrlist *alist, size_t extern void attrlistInsertCharactersExtendingAttributes(struct attrlist *alist, size_t start, size_t count); extern void attrlistRemoveAttribute(struct attrlist *alist, uiAttribute type, size_t start, size_t end); extern void attrlistRemoveAttributes(struct attrlist *alist, size_t start, size_t end); -extern; void attrlistRemoveCharacters(struct attrlist *alist, size_t start, size_t end); -extern void attrlistForEach(struct attr *alist, uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); +extern void attrlistRemoveCharacters(struct attrlist *alist, size_t start, size_t end); +extern void attrlistForEach(struct attrlist *alist, uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); // TODO move these to the top like everythng else extern struct attrlist *attrlistNew(void); extern void attrlistFree(struct attrlist *alist); diff --git a/darwin/CMakeLists.txt b/darwin/CMakeLists.txt index 32d1b45e..34368c06 100644 --- a/darwin/CMakeLists.txt +++ b/darwin/CMakeLists.txt @@ -17,7 +17,7 @@ list(APPEND _LIBUI_SOURCES darwin/drawtext.m darwin/editablecombo.m darwin/entry.m - darwin/fontbutton.m +#TODO darwin/fontbutton.m darwin/fontmatch.m darwin/form.m darwin/graphemes.m @@ -44,7 +44,7 @@ list(APPEND _LIBUI_SOURCES ) set(_LIBUI_SOURCES ${_LIBUI_SOURCES} PARENT_SCOPE) -// TODO is this correct? +# TODO is this correct? list(APPEND _LIBUI_INCLUDEDIRS darwin ) diff --git a/darwin/draw.m b/darwin/draw.m index 231b4dfd..d9aadede 100644 --- a/darwin/draw.m +++ b/darwin/draw.m @@ -443,8 +443,3 @@ void uiDrawRestore(uiDrawContext *c) { CGContextRestoreGState(c->c); } - -void uiDrawText(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout) -{ - doDrawText(c->c, c->height, x, y, layout); -} diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 6ddeedb9..4296d35a 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -82,10 +82,10 @@ static CFAttributedStringRef attrstrToCoreFoundation(uiAttributedString *s, uiDr return mas; } -static uiDrawTextLayoutLineMetrics *computeLineMetrics(CTFrame frame, CGSize size) +static uiDrawTextLayoutLineMetrics *computeLineMetrics(CTFrameRef frame, CGSize size) { uiDrawTextLayoutLineMetrics *metrics; - CFArray lines; + CFArrayRef lines; CTLineRef line; CFIndex i, n; CGFloat ypos; @@ -95,10 +95,9 @@ static uiDrawTextLayoutLineMetrics *computeLineMetrics(CTFrame frame, CGSize siz lines = CTFrameGetLines(frame); n = CFArrayGetCount(lines); - metrics = (uiDrawTextLay -outLineMetrics *) uiAlloc(n * sizeof (uiDrawTextLayoutLineMetrics), "uiDrawTextLayoutLineMetrics[] (text layout)"); + metrics = (uiDrawTextLayoutLineMetrics *) uiAlloc(n * sizeof (uiDrawTextLayoutLineMetrics), "uiDrawTextLayoutLineMetrics[] (text layout)"); - origins = (CGFloat *) uiAlloc(n * sizeof (CGFloat), "CGFloat[] (text layout)"); + origins = (CGPoint *) uiAlloc(n * sizeof (CGPoint), "CGPoint[] (text layout)"); CTFrameGetLineOrigins(frame, CFRangeMake(0, n), origins); ypos = size.height; @@ -183,9 +182,9 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto // TODO kCTFramePathWidthAttributeName? NULL, CGSizeMake(cgwidth, CGFLOAT_MAX), - &unused); // not documented as accepting NULL + &unused); // not documented as accepting NULL (TODO really?) - rect.origin = CGZeroPoint; + rect.origin = CGPointZero; rect.size = tl->size; tl->path = CGPathCreateWithRect(rect, NULL); tl->frame = CTFramesetterCreateFrame(tl->framesetter, @@ -227,7 +226,7 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) // Core Text doesn't draw onto a flipped view correctly; we have to pretend it was unflipped // see the iOS bits of the first example at https://developer.apple.com/library/mac/documentation/StringsTextFonts/Conceptual/CoreText_Programming/LayoutOperations/LayoutOperations.html#//apple_ref/doc/uid/TP40005533-CH12-SW1 (iOS is naturally flipped) // TODO how is this affected by a non-identity CTM? - CGContextTranslateCTM(c->c, 0, cheight); + CGContextTranslateCTM(c->c, 0, c->height); CGContextScaleCTM(c->c, 1.0, -1.0); CGContextSetTextMatrix(c->c, CGAffineTransformIdentity); @@ -280,9 +279,8 @@ void uiDrawTextLayoutByteIndexToGraphemeRect(uiDrawTextLayout *tl, size_t pos, i void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) { CFIndex i; - CTLine line; + CTLineRef line; CFIndex pos; - CGFloat charLeft, charRight; if (y >= 0) { for (i = 0; i < tl->nLines; i++) { @@ -302,28 +300,26 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTex i = 0; result->YPosition = uiDrawTextLayoutHitTestPositionBefore; } - m->Line = i; + result->Line = i; result->XPosition = uiDrawTextLayoutHitTestPositionInside; if (x < tl->lineMetrics[i].X) { - result->XPosition = uiDrawTextLay -outHitTestPositionBefore; + result->XPosition = uiDrawTextLayoutHitTestPositionBefore; // and forcibly return the first character x = tl->lineMetrics[i].X; } else if (x > (tl->lineMetrics[i].X + tl->lineMetrics[i].Width)) { - result->XPosition = uiDrawTextLayoutHitTestP -ositionAfter; + result->XPosition = uiDrawTextLayoutHitTestPositionAfter; // and forcibly return the last character x = tl->lineMetrics[i].X + tl->lineMetrics[i].Width; } line = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, i); - pos = CTLineGetStringIndexForPosition(line, CGP -ointMake(x, 0)); + // TODO copy the part from the docs about this point + pos = CTLineGetStringIndexForPosition(line, CGPointMake(x, 0)); if (pos == kCFNotFound) { // TODO } - m->Pos = tl->u16tou8[pos]; + result->Pos = tl->u16tou8[pos]; } void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r) diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index 31e2a4d1..04761ef4 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -54,7 +54,7 @@ static const struct italicCloseness italicClosenesses[] = { // TODO there is still one catch that might matter from a user's POV: the reverse is not true — the italic bit can be set even if the style of the font face/subfamily/style isn't named as Italic (for example, script typefaces like Adobe's Palace Script MT Std); I don't know what to do about this... I know how to start: find a script font that has an italic form (Adobe's Palace Script MT Std does not; only Regular and Semibold) static double italicCloseness(CTFontDescriptorRef desc, CFDictionaryRef traits, uiDrawTextItalic italic) { - struct italicCloseness *ic = &(italicClosenesses[italic]); + const struct italicCloseness *ic = &(italicClosenesses[italic]); CFNumberRef cfnum; CTFontSymbolicTraits symbolic; // there is no kCFNumberUInt32Type, but CTFontSymbolicTraits is uint32_t, so SInt32 should work @@ -77,7 +77,7 @@ static double italicCloseness(CTFontDescriptorRef desc, CFDictionaryRef traits, // Okay, now we know it's either Italic or Oblique // Pango's Core Text code just does a g_strrstr() (backwards case-sensitive search) for "Oblique" in the font's style name (see https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c); let's do that too I guess isOblique = NO; // default value - styleName = (CFStringRef) CTFontDescriptorCopyAttribute(current, kCTFontStyleNameAttribute); + styleName = (CFStringRef) CTFontDescriptorCopyAttribute(desc, kCTFontStyleNameAttribute); // TODO is styleName guaranteed? if (styleName != NULL) { CFRange range; diff --git a/darwin/graphemes.m b/darwin/graphemes.m index 3819f0b4..e5ee816f 100644 --- a/darwin/graphemes.m +++ b/darwin/graphemes.m @@ -13,7 +13,7 @@ struct graphemes *graphemes(void *s, size_t len) { struct graphemes *g; UniChar *str = (UniChar *) s; - CFString cfstr; + CFStringRef cfstr; size_t ppos, gpos; CFRange range; size_t i; diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 125fd94a..c1fde75a 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -109,9 +109,15 @@ extern uiDrawContext *newContext(CGContextRef, CGFloat); extern void freeContext(uiDrawContext *); // fontbutton.m +#if 0 /* TODO */ extern BOOL fontButtonInhibitSendAction(SEL sel, id from, id to); extern BOOL fontButtonOverrideTargetForAction(SEL sel, id from, id to, id *override); extern void setupFontPanel(void); +#else +static inline BOOL fontButtonInhibitSendAction(SEL sel, id from, id to) { return NO; } +static inline BOOL fontButtonOverrideTargetForAction(SEL sel, id from, id to, id *override) { return NO; } +static inline void setupFontPanel(void) {} +#endif // colorbutton.m extern BOOL colorButtonInhibitSendAction(SEL sel, id from, id to); diff --git a/ui.h b/ui.h index 2a9de749..ddc9bb63 100644 --- a/ui.h +++ b/ui.h @@ -577,6 +577,7 @@ struct uiAreaKeyEvent { int Up; }; +#if 0 /* TODO */ typedef struct uiFontButton uiFontButton; #define uiFontButton(this) ((uiFontButton *) (this)) // TODO document this returns a new font @@ -584,6 +585,7 @@ _UI_EXTERN uiDrawTextFont *uiFontButtonFont(uiFontButton *b); // TOOD SetFont, mechanics _UI_EXTERN void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data); _UI_EXTERN uiFontButton *uiNewFontButton(void); +#endif typedef struct uiColorButton uiColorButton; #define uiColorButton(this) ((uiColorButton *) (this)) From 907d7c58300705cc12b1c7b93796053573b52c1b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 17 Jan 2017 13:30:00 -0500 Subject: [PATCH 0548/1329] Added the beginning of a text-drawing example (since I would need to heavily change the tester to test these things; hopefully in the future the example will be much more sophisticated). Time to fix segfaults! --- examples/CMakeLists.txt | 8 ++- examples/drawtext/main.c | 128 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 examples/drawtext/main.c diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 3a9ec4c9..75b7f9d1 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -31,8 +31,14 @@ if(NOT WIN32) target_link_libraries(cpp-multithread pthread) endif() +_add_example(drawtext + drawtext/main.c + ${_EXAMPLE_RESOURCES_RC} +) + add_custom_target(examples DEPENDS controlgallery histogram - cpp-multithread) + cpp-multithread + drawtext) diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c new file mode 100644 index 00000000..90c401ee --- /dev/null +++ b/examples/drawtext/main.c @@ -0,0 +1,128 @@ +// 17 january 2017 +#include +#include +#include "../../ui.h" + +uiWindow *mainwin; +uiArea *area; +uiAreaHandler handler; + +const char *text = + "It is with a kind of fear that I begin to write the history of my life. " + "I have, as it were, a superstitious hesitation in lifting the veil that " + "clings about my childhood like a golden mist. The task of writing an " + "autobiography is a difficult one. When I try to classify my earliest " + "impressions, I find that fact and fancy look alike across the years that " + "link the past with the present. The woman paints the child's experiences " + "in her own fantasy. A few impressions stand out vividly from the first " + "years of my life; but \"the shadows of the prison-house are on the rest.\" " + "Besides, many of the joys and sorrows of childhood have lost their " + "poignancy; and many incidents of vital importance in my early education " + "have been forgotten in the excitement of great discoveries. In order, " + "therefore, not to be tedious I shall try to present in a series of " + "sketches only the episodes that seem to me to be the most interesting " + "and important." + ""; +char fontFamily[] = "Palatino"; +// TODO should be const; look at constructor function +uiDrawFontDescriptor defaultFont = { + .Family = fontFamily, + .Size = 18, + .Weight = uiDrawTextWeightNormal, + .Italic = uiDrawTextItalicNormal, + .Stretch = uiDrawTextStretchNormal, +}; +uiAttributedString *attrstr; + +#define margins 5 + +static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *p) +{ + uiDrawPath *path; + uiDrawTextLayout *layout; + + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathAddRectangle(path, margins, margins, + p->AreaWidth - 2 * margins, + p->AreaHeight - 2 * margins); + uiDrawPathEnd(path); + uiDrawClip(p->Context, path); + uiDrawFreePath(path); + + layout = uiDrawNewTextLayout(attrstr, + &defaultFont, + p->AreaWidth - 2 * margins); + uiDrawText(p->Context, layout, margins, margins); + 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) +{ + uiControlDestroy(uiControl(mainwin)); + uiQuit(); + return 0; +} + +static int shouldQuit(void *data) +{ + uiControlDestroy(uiControl(mainwin)); + return 1; +} + +int main(void) +{ + uiInitOptions o; + const char *err; + + 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); + + attrstr = uiNewAttributedString(text); + + mainwin = uiNewWindow("libui Histogram Example", 640, 480, 1); + uiWindowSetMargined(mainwin, 1); + uiWindowOnClosing(mainwin, onClosing, NULL); + + area = uiNewArea(&handler); + uiWindowSetChild(mainwin, uiControl(area)); + + uiControlShow(uiControl(mainwin)); + uiMain(); + uiFreeAttributedString(attrstr); + uiUninit(); + return 0; +} From 7f270942a7e2988401a831bc01a94a2bd0a27b2a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 17 Jan 2017 13:54:23 -0500 Subject: [PATCH 0549/1329] And fixed errors. Woo, it works!!!!! --- common/attrstr.c | 4 +++- darwin/drawtext.m | 3 ++- examples/drawtext/main.c | 5 ++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/common/attrstr.c b/common/attrstr.c index 891453cd..bfe42842 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -131,7 +131,9 @@ void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *s // TODO } - at16 = s->u8tou16[at]; + at16 = 0; + if (s->u8tou16 != NULL) + at16 = s->u8tou16[at]; // do this first to reclaim memory invalidateGraphemes(s); diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 4296d35a..a8a34542 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -231,7 +231,8 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) CGContextSetTextMatrix(c->c, CGAffineTransformIdentity); // wait, that's not enough; we need to offset y values to account for our new flipping - y = c->height - y; + // TODO explain this calculation + y = c->height - tl->size.height - y; // CTFrameDraw() draws in the path we specified when creating the frame // this means that in our usage, CTFrameDraw() will draw at (0,0) diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c index 90c401ee..f2580504 100644 --- a/examples/drawtext/main.c +++ b/examples/drawtext/main.c @@ -34,7 +34,7 @@ uiDrawFontDescriptor defaultFont = { }; uiAttributedString *attrstr; -#define margins 5 +#define margins 10 static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *p) { @@ -113,8 +113,7 @@ int main(void) attrstr = uiNewAttributedString(text); - mainwin = uiNewWindow("libui Histogram Example", 640, 480, 1); - uiWindowSetMargined(mainwin, 1); + mainwin = uiNewWindow("libui Text-Drawing Example", 640, 480, 1); uiWindowOnClosing(mainwin, onClosing, NULL); area = uiNewArea(&handler); From 5444f76bd35f275f3d4582e6889bb6eb372ffc07 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 17 Jan 2017 21:06:45 -0500 Subject: [PATCH 0550/1329] Started implementing the new text layout stuff on GTK+. The drawtext example works. --- examples/drawtext/main.c | 1 + unix/CMakeLists.txt | 2 +- unix/_old_drawtext.c | 217 +++++++++++++++++++++++++ unix/drawtext.c | 339 +++++++++++---------------------------- unix/graphemes.c | 2 +- unix/uipriv_unix.h | 2 + 6 files changed, 318 insertions(+), 245 deletions(-) create mode 100644 unix/_old_drawtext.c diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c index f2580504..10f41ad5 100644 --- a/examples/drawtext/main.c +++ b/examples/drawtext/main.c @@ -117,6 +117,7 @@ int main(void) uiWindowOnClosing(mainwin, onClosing, NULL); area = uiNewArea(&handler); + // TODO on GTK+ this doesn't get expand properties set properly? uiWindowSetChild(mainwin, uiControl(area)); uiControlShow(uiControl(mainwin)); diff --git a/unix/CMakeLists.txt b/unix/CMakeLists.txt index 9300bcb7..1684e2ce 100644 --- a/unix/CMakeLists.txt +++ b/unix/CMakeLists.txt @@ -22,7 +22,7 @@ list(APPEND _LIBUI_SOURCES unix/drawtext.c unix/editablecombo.c unix/entry.c - unix/fontbutton.c +# unix/fontbutton.c unix/form.c unix/future.c unix/graphemes.c diff --git a/unix/_old_drawtext.c b/unix/_old_drawtext.c new file mode 100644 index 00000000..7677f076 --- /dev/null +++ b/unix/_old_drawtext.c @@ -0,0 +1,217 @@ +// 6 september 2015 +#include "uipriv_unix.h" +#include "draw.h" + +struct uiDrawFontFamilies { + PangoFontFamily **f; + int n; +}; + +uiDrawFontFamilies *uiDrawListFontFamilies(void) +{ + uiDrawFontFamilies *ff; + PangoFontMap *map; + + ff = uiNew(uiDrawFontFamilies); + map = pango_cairo_font_map_get_default(); + pango_font_map_list_families(map, &(ff->f), &(ff->n)); + // do not free map; it's a shared resource + return ff; +} + +int uiDrawFontFamiliesNumFamilies(uiDrawFontFamilies *ff) +{ + return ff->n; +} + +char *uiDrawFontFamiliesFamily(uiDrawFontFamilies *ff, int n) +{ + PangoFontFamily *f; + + f = ff->f[n]; + return uiUnixStrdupText(pango_font_family_get_name(f)); +} + +void uiDrawFreeFontFamilies(uiDrawFontFamilies *ff) +{ + g_free(ff->f); + uiFree(ff); +} + +struct uiDrawTextFont { + PangoFont *f; +}; + +uiDrawTextFont *mkTextFont(PangoFont *f, gboolean ref) +{ + uiDrawTextFont *font; + + font = uiNew(uiDrawTextFont); + font->f = f; + if (ref) + g_object_ref(font->f); + return font; +} + + + +PangoFont *pangoDescToPangoFont(PangoFontDescription *pdesc) +{ + PangoFont *f; + PangoContext *context; + + // in this case, the context is necessary for the metrics to be correct + context = mkGenericPangoCairoContext(); + f = pango_font_map_load_font(pango_cairo_font_map_get_default(), context, pdesc); + if (f == NULL) { + // LONGTERM + g_error("[libui] no match in pangoDescToPangoFont(); report to andlabs"); + } + g_object_unref(context); + return f; +} + +uiDrawTextFont *uiDrawLoadClosestFont(const uiDrawTextFontDescriptor *desc) +{ + PangoFont *f; + PangoFontDescription *pdesc; + + pdesc = pango_font_description_new(); + pango_font_description_set_family(pdesc, + desc->Family); + pango_font_description_set_size(pdesc, + (gint) (desc->Size * PANGO_SCALE)); + pango_font_description_set_weight(pdesc, + pangoWeights[desc->Weight]); + pango_font_description_set_style(pdesc, + pangoItalics[desc->Italic]); + pango_font_description_set_stretch(pdesc, + pangoStretches[desc->Stretch]); + f = pangoDescToPangoFont(pdesc); + pango_font_description_free(pdesc); + return mkTextFont(f, FALSE); // we hold the initial reference; no need to ref +} + +void uiDrawFreeTextFont(uiDrawTextFont *font) +{ + g_object_unref(font->f); + uiFree(font); +} + +uintptr_t uiDrawTextFontHandle(uiDrawTextFont *font) +{ + return (uintptr_t) (font->f); +} + +void uiDrawTextFontDescribe(uiDrawTextFont *font, uiDrawTextFontDescriptor *desc) +{ + PangoFontDescription *pdesc; + + // this creates a copy; we free it later + pdesc = pango_font_describe(font->f); + + // TODO + + pango_font_description_free(pdesc); +} + +// See https://developer.gnome.org/pango/1.30/pango-Cairo-Rendering.html#pango-Cairo-Rendering.description +// Note that we convert to double before dividing to make sure the floating-point stuff is right +#define pangoToCairo(pango) (((double) (pango)) / PANGO_SCALE) +#define cairoToPango(cairo) ((gint) ((cairo) * PANGO_SCALE)) + +void uiDrawTextFontGetMetrics(uiDrawTextFont *font, uiDrawTextFontMetrics *metrics) +{ + PangoFontMetrics *pm; + + pm = pango_font_get_metrics(font->f, NULL); + metrics->Ascent = pangoToCairo(pango_font_metrics_get_ascent(pm)); + metrics->Descent = pangoToCairo(pango_font_metrics_get_descent(pm)); + // Pango doesn't seem to expose this :( Use 0 and hope for the best. + metrics->Leading = 0; + metrics->UnderlinePos = pangoToCairo(pango_font_metrics_get_underline_position(pm)); + metrics->UnderlineThickness = pangoToCairo(pango_font_metrics_get_underline_thickness(pm)); + pango_font_metrics_unref(pm); +} + +// note: PangoCairoLayouts are tied to a given cairo_t, so we can't store one in this device-independent structure +struct uiDrawTextLayout { + char *s; + ptrdiff_t *graphemes; + PangoFont *defaultFont; + double width; + PangoAttrList *attrs; +}; + +uiDrawTextLayout *uiDrawNewTextLayout(const char *text, uiDrawTextFont *defaultFont, double width) +{ + uiDrawTextLayout *layout; + PangoContext *context; + + layout = uiNew(uiDrawTextLayout); + layout->s = g_strdup(text); + context = mkGenericPangoCairoContext(); + layout->graphemes = graphemes(layout->s, context); + g_object_unref(context); + layout->defaultFont = defaultFont->f; + g_object_ref(layout->defaultFont); // retain a copy + uiDrawTextLayoutSetWidth(layout, width); + layout->attrs = pango_attr_list_new(); + return layout; +} + +void uiDrawFreeTextLayout(uiDrawTextLayout *layout) +{ + pango_attr_list_unref(layout->attrs); + g_object_unref(layout->defaultFont); + uiFree(layout->graphemes); + g_free(layout->s); + uiFree(layout); +} + +void uiDrawTextLayoutSetWidth(uiDrawTextLayout *layout, double width) +{ + layout->width = width; +} + +static void prepareLayout(uiDrawTextLayout *layout, PangoLayout *pl) +{ + // again, this makes a copy + desc = pango_font_describe(layout->defaultFont); + + pango_layout_set_attributes(pl, layout->attrs); +} + +void uiDrawText(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout) +{ + PangoLayout *pl; + + pl = pango_cairo_create_layout(c->cr); +} + +static void addAttr(uiDrawTextLayout *layout, PangoAttribute *attr, int startChar, int endChar) +{ + attr->start_index = layout->graphemes[startChar]; + attr->end_index = layout->graphemes[endChar]; + pango_attr_list_insert(layout->attrs, attr); + // pango_attr_list_insert() takes attr; we don't free it +} + +void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, int startChar, int endChar, double r, double g, double b, double a) +{ + PangoAttribute *attr; + guint16 rr, gg, bb, aa; + + rr = (guint16) (r * 65535); + gg = (guint16) (g * 65535); + bb = (guint16) (b * 65535); + aa = (guint16) (a * 65535); + + attr = pango_attr_foreground_new(rr, gg, bb); + addAttr(layout, attr, startChar, endChar); + + // TODO what if aa == 0? + attr = FUTURE_pango_attr_foreground_alpha_new(aa); + if (attr != NULL) + addAttr(layout, attr, startChar, endChar); +} diff --git a/unix/drawtext.c b/unix/drawtext.c index 7078e1ac..64ae9928 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -1,71 +1,21 @@ -// 6 september 2015 +// 17 january 2017 #include "uipriv_unix.h" #include "draw.h" -struct uiDrawFontFamilies { - PangoFontFamily **f; - int n; +struct uiDrawTextLayout { + PangoLayout *layout; }; -uiDrawFontFamilies *uiDrawListFontFamilies(void) -{ - uiDrawFontFamilies *ff; - PangoFontMap *map; +// See https://developer.gnome.org/pango/1.30/pango-Cairo-Rendering.html#pango-Cairo-Rendering.description +// For the conversion, see https://developer.gnome.org/pango/1.30/pango-Glyph-Storage.html#pango-units-to-double and https://developer.gnome.org/pango/1.30/pango-Glyph-Storage.html#pango-units-from-double +#define pangoToCairo(pango) (pango_units_to_double(pango)) +#define cairoToPango(cairo) (pango_units_from_double(cairo)) - ff = uiNew(uiDrawFontFamilies); - map = pango_cairo_font_map_get_default(); - pango_font_map_list_families(map, &(ff->f), &(ff->n)); - // do not free map; it's a shared resource - return ff; -} - -int uiDrawFontFamiliesNumFamilies(uiDrawFontFamilies *ff) -{ - return ff->n; -} - -char *uiDrawFontFamiliesFamily(uiDrawFontFamilies *ff, int n) -{ - PangoFontFamily *f; - - f = ff->f[n]; - return uiUnixStrdupText(pango_font_family_get_name(f)); -} - -void uiDrawFreeFontFamilies(uiDrawFontFamilies *ff) -{ - g_free(ff->f); - uiFree(ff); -} - -struct uiDrawTextFont { - PangoFont *f; -}; - -uiDrawTextFont *mkTextFont(PangoFont *f, gboolean ref) -{ - uiDrawTextFont *font; - - font = uiNew(uiDrawTextFont); - font->f = f; - if (ref) - g_object_ref(font->f); - return font; -} - -static const PangoWeight pangoWeights[] = { - [uiDrawTextWeightThin] = PANGO_WEIGHT_THIN, - [uiDrawTextWeightUltraLight] = PANGO_WEIGHT_ULTRALIGHT, - [uiDrawTextWeightLight] = PANGO_WEIGHT_LIGHT, - [uiDrawTextWeightBook] = PANGO_WEIGHT_BOOK, - [uiDrawTextWeightNormal] = PANGO_WEIGHT_NORMAL, - [uiDrawTextWeightMedium] = PANGO_WEIGHT_MEDIUM, - [uiDrawTextWeightSemiBold] = PANGO_WEIGHT_SEMIBOLD, - [uiDrawTextWeightBold] = PANGO_WEIGHT_BOLD, - [uiDrawTextWeightUltraBold] = PANGO_WEIGHT_ULTRABOLD, - [uiDrawTextWeightHeavy] = PANGO_WEIGHT_HEAVY, - [uiDrawTextWeightUltraHeavy] = PANGO_WEIGHT_ULTRAHEAVY, -}; +// we need a context for a few things +// the documentation suggests creating cairo_t-specific, GdkScreen-specific, or even GtkWidget-specific contexts, but we can't really do that because we want our uiDrawTextFonts and uiDrawTextLayouts to be context-independent +// we could use pango_font_map_create_context(pango_cairo_font_map_get_default()) but that will ignore GDK-specific settings +// so let's use gdk_pango_context_get() instead; even though it's for the default screen only, it's good enough for us +#define mkGenericPangoCairoContext() (gdk_pango_context_get()) static const PangoStyle pangoItalics[] = { [uiDrawTextItalicNormal] = PANGO_STYLE_NORMAL, @@ -85,209 +35,112 @@ static const PangoStretch pangoStretches[] = { [uiDrawTextStretchUltraExpanded] = PANGO_STRETCH_ULTRA_EXPANDED, }; -// we need a context for a few things -// the documentation suggests creating cairo_t-specific, GdkScreen-specific, or even GtkWidget-specific contexts, but we can't really do that because we want our uiDrawTextFonts and uiDrawTextLayouts to be context-independent -// we could use pango_font_map_create_context(pango_cairo_font_map_get_default()) but that will ignore GDK-specific settings -// so let's use gdk_pango_context_get() instead; even though it's for the default screen only, it's good enough for us -#define mkGenericPangoCairoContext() (gdk_pango_context_get()) - -PangoFont *pangoDescToPangoFont(PangoFontDescription *pdesc) +uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescriptor *defaultFont, double width) { - PangoFont *f; + uiDrawTextLayout *tl; PangoContext *context; - - // in this case, the context is necessary for the metrics to be correct - context = mkGenericPangoCairoContext(); - f = pango_font_map_load_font(pango_cairo_font_map_get_default(), context, pdesc); - if (f == NULL) { - // LONGTERM - g_error("[libui] no match in pangoDescToPangoFont(); report to andlabs"); - } - g_object_unref(context); - return f; -} - -uiDrawTextFont *uiDrawLoadClosestFont(const uiDrawTextFontDescriptor *desc) -{ - PangoFont *f; - PangoFontDescription *pdesc; - - pdesc = pango_font_description_new(); - pango_font_description_set_family(pdesc, - desc->Family); - pango_font_description_set_size(pdesc, - (gint) (desc->Size * PANGO_SCALE)); - pango_font_description_set_weight(pdesc, - pangoWeights[desc->Weight]); - pango_font_description_set_style(pdesc, - pangoItalics[desc->Italic]); - pango_font_description_set_stretch(pdesc, - pangoStretches[desc->Stretch]); - f = pangoDescToPangoFont(pdesc); - pango_font_description_free(pdesc); - return mkTextFont(f, FALSE); // we hold the initial reference; no need to ref -} - -void uiDrawFreeTextFont(uiDrawTextFont *font) -{ - g_object_unref(font->f); - uiFree(font); -} - -uintptr_t uiDrawTextFontHandle(uiDrawTextFont *font) -{ - return (uintptr_t) (font->f); -} - -void uiDrawTextFontDescribe(uiDrawTextFont *font, uiDrawTextFontDescriptor *desc) -{ - PangoFontDescription *pdesc; - - // this creates a copy; we free it later - pdesc = pango_font_describe(font->f); - - // TODO - - pango_font_description_free(pdesc); -} - -// See https://developer.gnome.org/pango/1.30/pango-Cairo-Rendering.html#pango-Cairo-Rendering.description -// Note that we convert to double before dividing to make sure the floating-point stuff is right -#define pangoToCairo(pango) (((double) (pango)) / PANGO_SCALE) -#define cairoToPango(cairo) ((gint) ((cairo) * PANGO_SCALE)) - -void uiDrawTextFontGetMetrics(uiDrawTextFont *font, uiDrawTextFontMetrics *metrics) -{ - PangoFontMetrics *pm; - - pm = pango_font_get_metrics(font->f, NULL); - metrics->Ascent = pangoToCairo(pango_font_metrics_get_ascent(pm)); - metrics->Descent = pangoToCairo(pango_font_metrics_get_descent(pm)); - // Pango doesn't seem to expose this :( Use 0 and hope for the best. - metrics->Leading = 0; - metrics->UnderlinePos = pangoToCairo(pango_font_metrics_get_underline_position(pm)); - metrics->UnderlineThickness = pangoToCairo(pango_font_metrics_get_underline_thickness(pm)); - pango_font_metrics_unref(pm); -} - -// note: PangoCairoLayouts are tied to a given cairo_t, so we can't store one in this device-independent structure -struct uiDrawTextLayout { - char *s; - ptrdiff_t *graphemes; - PangoFont *defaultFont; - double width; - PangoAttrList *attrs; -}; - -uiDrawTextLayout *uiDrawNewTextLayout(const char *text, uiDrawTextFont *defaultFont, double width) -{ - uiDrawTextLayout *layout; - PangoContext *context; - - layout = uiNew(uiDrawTextLayout); - layout->s = g_strdup(text); - context = mkGenericPangoCairoContext(); - layout->graphemes = graphemes(layout->s, context); - g_object_unref(context); - layout->defaultFont = defaultFont->f; - g_object_ref(layout->defaultFont); // retain a copy - uiDrawTextLayoutSetWidth(layout, width); - layout->attrs = pango_attr_list_new(); - return layout; -} - -void uiDrawFreeTextLayout(uiDrawTextLayout *layout) -{ - pango_attr_list_unref(layout->attrs); - g_object_unref(layout->defaultFont); - uiFree(layout->graphemes); - g_free(layout->s); - uiFree(layout); -} - -void uiDrawTextLayoutSetWidth(uiDrawTextLayout *layout, double width) -{ - layout->width = width; -} - -static void prepareLayout(uiDrawTextLayout *layout, PangoLayout *pl) -{ PangoFontDescription *desc; - int width; + int pangoWidth; - pango_layout_set_text(pl, layout->s, -1); - - // again, this makes a copy - desc = pango_font_describe(layout->defaultFont); - // this is safe; the description is copied - pango_layout_set_font_description(pl, desc); - pango_font_description_free(desc); - - width = cairoToPango(layout->width); - if (layout->width < 0) - width = -1; - pango_layout_set_width(pl, width); - - pango_layout_set_attributes(pl, layout->attrs); -} - -void uiDrawTextLayoutExtents(uiDrawTextLayout *layout, double *width, double *height) -{ - PangoContext *context; - PangoLayout *pl; - PangoRectangle logical; + tl = uiNew(uiDrawTextLayout); // in this case, the context is necessary to create the layout // the layout takes a ref on the context so we can unref it afterward context = mkGenericPangoCairoContext(); - pl = pango_layout_new(context); + tl->layout = pango_layout_new(context); g_object_unref(context); - prepareLayout(layout, pl); - pango_layout_get_extents(pl, NULL, &logical); + // this is safe; pango_layout_set_text() copies the string + pango_layout_set_text(tl->layout, uiAttributedStringString(s), -1); - g_object_unref(pl); + desc = pango_font_description_new(); + pango_font_description_set_family(desc, defaultFont->Family); + pango_font_description_set_style(desc, pangoItalics[defaultFont->Italic]); + // for the most part, pango weights correlate to ours + // the differences: + // - Book — libui: 350, Pango: 380 + // - Ultra Heavy — libui: 950, Pango: 1000 + // TODO figure out what to do about this misalignment + pango_font_description_set_weight(desc, defaultFont->Weight); + pango_font_description_set_stretch(desc, pangoStretches[defaultFont->Stretch]); + // see https://developer.gnome.org/pango/1.30/pango-Fonts.html#pango-font-description-set-size and https://developer.gnome.org/pango/1.30/pango-Glyph-Storage.html#pango-units-from-double + pango_font_description_set_size(desc, pango_units_from_double(defaultFont->Size)); + pango_layout_set_font_description(tl->layout, desc); + // this is safe; the description is copied + pango_font_description_free(desc); + pangoWidth = cairoToPango(width); + if (width < 0) + pangoWidth = -1; + pango_layout_set_width(tl->layout, pangoWidth); + + // TODO attributes + + return tl; +} + +void uiDrawFreeTextLayout(uiDrawTextLayout *tl) +{ + g_object_unref(tl->layout); + uiFree(tl); +} + +void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) +{ + cairo_move_to(c->cr, x, y); + pango_cairo_show_layout(c->cr, tl->layout); +} + +void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height) +{ + PangoRectangle logical; + + pango_layout_get_extents(tl->layout, NULL, &logical); *width = pangoToCairo(logical.width); *height = pangoToCairo(logical.height); } -void uiDrawText(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout) +int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) { - PangoLayout *pl; - - pl = pango_cairo_create_layout(c->cr); - prepareLayout(layout, pl); - - cairo_move_to(c->cr, x, y); - pango_cairo_show_layout(c->cr, pl); - - g_object_unref(pl); + return pango_layout_get_line_count(tl->layout); } -static void addAttr(uiDrawTextLayout *layout, PangoAttribute *attr, int startChar, int endChar) +void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end) { - attr->start_index = layout->graphemes[startChar]; - attr->end_index = layout->graphemes[endChar]; - pango_attr_list_insert(layout->attrs, attr); - // pango_attr_list_insert() takes attr; we don't free it + PangoLayoutLine *pll; + + pll = pango_layout_get_line_readonly(tl->layout, line); + *start = pll->start_index; + *end = pll->start_index + pll->length; + // TODO unref? } -void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, int startChar, int endChar, double r, double g, double b, double a) +void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m) { - PangoAttribute *attr; - guint16 rr, gg, bb, aa; + PangoLayoutLine *pll; - rr = (guint16) (r * 65535); - gg = (guint16) (g * 65535); - bb = (guint16) (b * 65535); - aa = (guint16) (a * 65535); - - attr = pango_attr_foreground_new(rr, gg, bb); - addAttr(layout, attr, startChar, endChar); - - // TODO what if aa == 0? - attr = FUTURE_pango_attr_foreground_alpha_new(aa); - if (attr != NULL) - addAttr(layout, attr, startChar, endChar); + pll = pango_layout_get_line_readonly(tl->layout, line); + // TODO unref? +} + +// TODO +#if 0 +{ + PangoLayoutLine *pll; + + pll = pango_layout_get_line_readonly(tl->layout, line); + // TODO unref? +} +#endif + +void uiDrawTextLayoutByteIndexToGraphemeRect(uiDrawTextLayout *tl, size_t pos, int *line, double *x, double *y, double *width, double *height) +{ +} + +void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) +{ +} + +void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r) +{ } diff --git a/unix/graphemes.c b/unix/graphemes.c index e489ebff..4f957352 100644 --- a/unix/graphemes.c +++ b/unix/graphemes.c @@ -6,7 +6,7 @@ int graphemesTakesUTF16(void) return 0; } -struct graphemes *graphemes(void *s, size_t len); +struct graphemes *graphemes(void *s, size_t len) { struct graphemes *g; char *text = (char *) s; diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index de2c1d3d..0a477158 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -46,8 +46,10 @@ extern uiDrawContext *newContext(cairo_t *); extern void freeContext(uiDrawContext *); // drawtext.c +#if 0 /* TODO */ extern uiDrawTextFont *mkTextFont(PangoFont *f, gboolean add); extern PangoFont *pangoDescToPangoFont(PangoFontDescription *pdesc); +#endif // image.c /*TODO remove this*/typedef struct uiImage uiImage; From f7121774e175ea3f450eaa97a9dbcd3bfe970601 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 17 Jan 2017 23:25:26 -0500 Subject: [PATCH 0551/1329] Added some TODOs and started the work necessary for the Windows text system migration... which is gonna hurt. --- test/drawtests.c | 1 + windows/CMakeLists.txt | 4 ++-- windows/{drawtext.cpp => _old_drawtext.cpp} | 0 windows/_uipriv_migrate.hpp | 6 ++++++ windows/drawpath.cpp | 1 + windows/dwrite.cpp | 6 +++++- 6 files changed, 15 insertions(+), 3 deletions(-) rename windows/{drawtext.cpp => _old_drawtext.cpp} (100%) diff --git a/test/drawtests.c b/test/drawtests.c index b6de753f..5c409294 100644 --- a/test/drawtests.c +++ b/test/drawtests.c @@ -4,6 +4,7 @@ // TODO // - test multiple clips // - test saving and restoring clips +// - copy tests from https://github.com/Microsoft/WinObjC struct drawtest { const char *name; diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 4695eb4f..5b1232ed 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -26,8 +26,8 @@ list(APPEND _LIBUI_SOURCES windows/editablecombo.cpp windows/entry.cpp windows/events.cpp - windows/fontbutton.cpp - windows/fontdialog.cpp +# windows/fontbutton.cpp +# windows/fontdialog.cpp windows/form.cpp windows/graphemes.cpp windows/grid.cpp diff --git a/windows/drawtext.cpp b/windows/_old_drawtext.cpp similarity index 100% rename from windows/drawtext.cpp rename to windows/_old_drawtext.cpp diff --git a/windows/_uipriv_migrate.hpp b/windows/_uipriv_migrate.hpp index 13d3670e..11b737d1 100644 --- a/windows/_uipriv_migrate.hpp +++ b/windows/_uipriv_migrate.hpp @@ -19,6 +19,7 @@ extern IDWriteFactory *dwfactory; #endif extern HRESULT initDrawText(void); extern void uninitDrawText(void); +#if 0 /* TODO */ #ifdef __cplusplus struct fontCollection { IDWriteFontCollection *fonts; @@ -30,8 +31,10 @@ extern WCHAR *fontCollectionFamilyName(fontCollection *fc, IDWriteFontFamily *fa extern void fontCollectionFree(fontCollection *fc); extern WCHAR *fontCollectionCorrectString(fontCollection *fc, IDWriteLocalizedStrings *names); #endif +#endif // drawtext.cpp +#if 0 /* TODO */ #ifdef __cplusplus extern uiDrawTextFont *mkTextFont(IDWriteFont *df, BOOL addRef, WCHAR *family, BOOL copyFamily, double size); struct dwriteAttr { @@ -45,8 +48,10 @@ struct dwriteAttr { extern void attrToDWriteAttr(struct dwriteAttr *attr); extern void dwriteAttrToAttr(struct dwriteAttr *attr); #endif +#endif // fontdialog.cpp +#if 0 /* TODO */ #ifdef __cplusplus struct fontDialogParams { IDWriteFont *font; @@ -59,3 +64,4 @@ extern void loadInitialFontDialogParams(struct fontDialogParams *params); extern void destroyFontDialogParams(struct fontDialogParams *params); extern WCHAR *fontDialogParamsToString(struct fontDialogParams *params); #endif +#endif diff --git a/windows/drawpath.cpp b/windows/drawpath.cpp index 49855be6..045d11c0 100644 --- a/windows/drawpath.cpp +++ b/windows/drawpath.cpp @@ -66,6 +66,7 @@ void uiDrawPathNewFigure(uiDrawPath *p, double x, double y) // That is to say, it's NOT THE SWEEP. // The sweep is defined by the start and end points and whether the arc is "large". // As a result, this design does not allow for full circles or ellipses with a single arc; they have to be simulated with two. +// TODO https://github.com/Microsoft/WinObjC/blob/develop/Frameworks/CoreGraphics/CGPath.mm#L313 struct arc { double xCenter; diff --git a/windows/dwrite.cpp b/windows/dwrite.cpp index 9156f179..6e9e5835 100644 --- a/windows/dwrite.cpp +++ b/windows/dwrite.cpp @@ -1,6 +1,6 @@ // 14 april 2016 #include "uipriv_windows.hpp" -// TODO really migrate? +// TODO really migrate? (TODO what did I mean by this?) IDWriteFactory *dwfactory = NULL; @@ -17,6 +17,8 @@ void uninitDrawText(void) dwfactory->Release(); } +#if 0 /* TODO */ + fontCollection *loadFontCollection(void) { fontCollection *fc; @@ -86,3 +88,5 @@ void fontCollectionFree(fontCollection *fc) fc->fonts->Release(); uiFree(fc); } + +#endif From cac390a82153fe148b285aa854f96e7212ec7f42 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 19 Jan 2017 21:13:03 -0500 Subject: [PATCH 0552/1329] Wrote much of the new text layout code on Windows. Now to test. --- windows/_old_drawtext.cpp | 200 +------------------------------ windows/areaevents.cpp | 2 + windows/drawtext.cpp | 244 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 248 insertions(+), 198 deletions(-) create mode 100644 windows/drawtext.cpp diff --git a/windows/_old_drawtext.cpp b/windows/_old_drawtext.cpp index 05a24f67..a10e1ad6 100644 --- a/windows/_old_drawtext.cpp +++ b/windows/_old_drawtext.cpp @@ -77,152 +77,8 @@ uiDrawTextFont *mkTextFont(IDWriteFont *df, BOOL addRef, WCHAR *family, BOOL cop return font; } -// TODO consider moving these all to dwrite.cpp - // TODO MinGW-w64 is missing this one #define DWRITE_FONT_WEIGHT_SEMI_LIGHT (DWRITE_FONT_WEIGHT(350)) -static const struct { - bool lastOne; - uiDrawTextWeight uival; - DWRITE_FONT_WEIGHT dwval; -} dwriteWeights[] = { - { false, uiDrawTextWeightThin, DWRITE_FONT_WEIGHT_THIN }, - { false, uiDrawTextWeightUltraLight, DWRITE_FONT_WEIGHT_ULTRA_LIGHT }, - { false, uiDrawTextWeightLight, DWRITE_FONT_WEIGHT_LIGHT }, - { false, uiDrawTextWeightBook, DWRITE_FONT_WEIGHT_SEMI_LIGHT }, - { false, uiDrawTextWeightNormal, DWRITE_FONT_WEIGHT_NORMAL }, - { false, uiDrawTextWeightMedium, DWRITE_FONT_WEIGHT_MEDIUM }, - { false, uiDrawTextWeightSemiBold, DWRITE_FONT_WEIGHT_SEMI_BOLD }, - { false, uiDrawTextWeightBold, DWRITE_FONT_WEIGHT_BOLD }, - { false, uiDrawTextWeightUltraBold, DWRITE_FONT_WEIGHT_ULTRA_BOLD }, - { false, uiDrawTextWeightHeavy, DWRITE_FONT_WEIGHT_HEAVY }, - { true, uiDrawTextWeightUltraHeavy, DWRITE_FONT_WEIGHT_ULTRA_BLACK, }, -}; - -static const struct { - bool lastOne; - uiDrawTextItalic uival; - DWRITE_FONT_STYLE dwval; -} dwriteItalics[] = { - { false, uiDrawTextItalicNormal, DWRITE_FONT_STYLE_NORMAL }, - { false, uiDrawTextItalicOblique, DWRITE_FONT_STYLE_OBLIQUE }, - { true, uiDrawTextItalicItalic, DWRITE_FONT_STYLE_ITALIC }, -}; - -static const struct { - bool lastOne; - uiDrawTextStretch uival; - DWRITE_FONT_STRETCH dwval; -} dwriteStretches[] = { - { false, uiDrawTextStretchUltraCondensed, DWRITE_FONT_STRETCH_ULTRA_CONDENSED }, - { false, uiDrawTextStretchExtraCondensed, DWRITE_FONT_STRETCH_EXTRA_CONDENSED }, - { false, uiDrawTextStretchCondensed, DWRITE_FONT_STRETCH_CONDENSED }, - { false, uiDrawTextStretchSemiCondensed, DWRITE_FONT_STRETCH_SEMI_CONDENSED }, - { false, uiDrawTextStretchNormal, DWRITE_FONT_STRETCH_NORMAL }, - { false, uiDrawTextStretchSemiExpanded, DWRITE_FONT_STRETCH_SEMI_EXPANDED }, - { false, uiDrawTextStretchExpanded, DWRITE_FONT_STRETCH_EXPANDED }, - { false, uiDrawTextStretchExtraExpanded, DWRITE_FONT_STRETCH_EXTRA_EXPANDED }, - { true, uiDrawTextStretchUltraExpanded, DWRITE_FONT_STRETCH_ULTRA_EXPANDED }, -}; - -void attrToDWriteAttr(struct dwriteAttr *attr) -{ - bool found; - int i; - - found = false; - for (i = 0; ; i++) { - if (dwriteWeights[i].uival == attr->weight) { - attr->dweight = dwriteWeights[i].dwval; - found = true; - break; - } - if (dwriteWeights[i].lastOne) - break; - } - if (!found) - userbug("Invalid text weight %d passed to text function.", attr->weight); - - found = false; - for (i = 0; ; i++) { - if (dwriteItalics[i].uival == attr->italic) { - attr->ditalic = dwriteItalics[i].dwval; - found = true; - break; - } - if (dwriteItalics[i].lastOne) - break; - } - if (!found) - userbug("Invalid text italic %d passed to text function.", attr->italic); - - found = false; - for (i = 0; ; i++) { - if (dwriteStretches[i].uival == attr->stretch) { - attr->dstretch = dwriteStretches[i].dwval; - found = true; - break; - } - if (dwriteStretches[i].lastOne) - break; - } - if (!found) - // TODO on other platforms too - userbug("Invalid text stretch %d passed to text function.", attr->stretch); -} - -void dwriteAttrToAttr(struct dwriteAttr *attr) -{ - int weight, against, n; - int curdiff, curindex; - bool found; - int i; - - // weight is scaled; we need to test to see what's nearest - weight = (int) (attr->dweight); - against = (int) (dwriteWeights[0].dwval); - curdiff = abs(against - weight); - curindex = 0; - for (i = 1; ; i++) { - against = (int) (dwriteWeights[i].dwval); - n = abs(against - weight); - if (n < curdiff) { - curdiff = n; - curindex = i; - } - if (dwriteWeights[i].lastOne) - break; - } - attr->weight = dwriteWeights[i].uival; - - // italic and stretch are simple values; we can just do a matching search - found = false; - for (i = 0; ; i++) { - if (dwriteItalics[i].dwval == attr->ditalic) { - attr->italic = dwriteItalics[i].uival; - found = true; - break; - } - if (dwriteItalics[i].lastOne) - break; - } - if (!found) - // these are implbug()s because users shouldn't be able to get here directly; TODO? - implbug("invalid italic %d passed to dwriteAttrToAttr()", attr->ditalic); - - found = false; - for (i = 0; ; i++) { - if (dwriteStretches[i].dwval == attr->dstretch) { - attr->stretch = dwriteStretches[i].uival; - found = true; - break; - } - if (dwriteStretches[i].lastOne) - break; - } - if (!found) - implbug("invalid stretch %d passed to dwriteAttrToAttr()", attr->dstretch); -} uiDrawTextFont *uiDrawLoadClosestFont(const uiDrawTextFontDescriptor *desc) { @@ -357,13 +213,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(const char *text, uiDrawTextFont *defaultF defaultFont->f->GetWeight(), defaultFont->f->GetStyle(), defaultFont->f->GetStretch(), - // typographic points are 1/72 inch; this parameter is 1/96 inch - // fortunately Microsoft does this too, in https://msdn.microsoft.com/en-us/library/windows/desktop/dd371554%28v=vs.85%29.aspx - defaultFont->size * (96.0 / 72.0), - // see http://stackoverflow.com/questions/28397971/idwritefactorycreatetextformat-failing and https://msdn.microsoft.com/en-us/library/windows/desktop/dd368203.aspx - // TODO use the current locale again? - L"", - &(layout->format)); + if (hr != S_OK) logHRESULT(L"error creating IDWriteTextFormat", hr); @@ -417,18 +267,8 @@ IDWriteTextLayout *prepareLayout(uiDrawTextLayout *layout, ID2D1RenderTarget *rt IDWriteTextLayout *dl; DWRITE_TEXT_RANGE range; IUnknown *unkBrush; - DWRITE_WORD_WRAPPING wrap; - FLOAT maxWidth; HRESULT hr; - hr = dwfactory->CreateTextLayout(layout->text, layout->textlen, - layout->format, - // FLOAT is float, not double, so this should work... TODO - FLT_MAX, FLT_MAX, - &dl); - if (hr != S_OK) - logHRESULT(L"error creating IDWriteTextLayout", hr); - for (const struct layoutAttr &attr : *(layout->attrs)) { range.startPosition = layout->graphemes[attr.start]; range.length = layout->graphemes[attr.end] - layout->graphemes[attr.start]; @@ -452,48 +292,12 @@ IDWriteTextLayout *prepareLayout(uiDrawTextLayout *layout, ID2D1RenderTarget *rt logHRESULT(L"error adding attribute to text layout", hr); } - // and set the width - // this is the only wrapping mode (apart from "no wrap") available prior to Windows 8.1 - wrap = DWRITE_WORD_WRAPPING_WRAP; - maxWidth = layout->width; - if (layout->width < 0) { - wrap = DWRITE_WORD_WRAPPING_NO_WRAP; - // setting the max width in this case technically isn't needed since the wrap mode will simply ignore the max width, but let's do it just to be safe - maxWidth = FLT_MAX; // see TODO above - } - hr = dl->SetWordWrapping(wrap); - if (hr != S_OK) - logHRESULT(L"error setting word wrapping mode", hr); - hr = dl->SetMaxWidth(maxWidth); - if (hr != S_OK) - logHRESULT(L"error setting max layout width", hr); + return dl; } -void uiDrawTextLayoutSetWidth(uiDrawTextLayout *layout, double width) -{ - layout->width = width; -} - -// TODO for a single line the height includes the leading; it should not -void uiDrawTextLayoutExtents(uiDrawTextLayout *layout, double *width, double *height) -{ - IDWriteTextLayout *dl; - DWRITE_TEXT_METRICS metrics; - HRESULT hr; - - dl = prepareLayout(layout, NULL); - hr = dl->GetMetrics(&metrics); - if (hr != S_OK) - logHRESULT(L"error getting layout metrics", hr); - *width = metrics.width; - // TODO make sure the behavior of this on empty strings is the same on all platforms - *height = metrics.height; - dl->Release(); -} - void uiDrawText(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout) { IDWriteTextLayout *dl; diff --git a/windows/areaevents.cpp b/windows/areaevents.cpp index 7d391b85..615c06ea 100644 --- a/windows/areaevents.cpp +++ b/windows/areaevents.cpp @@ -2,6 +2,8 @@ #include "uipriv_windows.hpp" #include "area.hpp" +// TODO https://github.com/Microsoft/Windows-classic-samples/blob/master/Samples/Win7Samples/multimedia/DirectWrite/PadWrite/TextEditor.cpp notes on explicit RTL handling under MirrorXCoordinate(); also in areadraw.cpp too? + static uiModifiers getModifiers(void) { uiModifiers m = 0; diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp new file mode 100644 index 00000000..3932af52 --- /dev/null +++ b/windows/drawtext.cpp @@ -0,0 +1,244 @@ +// 17 january 2017 +#include "uipriv_windows.hpp" + +struct uiDrawTextLayout { + IDWriteTextFormat *format; + IDWriteTextLayout *layout; + UINT32 *nLines; + struct lineInfo *lineInfo; + // for converting DirectWrite indices to byte offsets + size_t *u16tou8; + size_t nu16tou8; // TODO I don't like the casing of this name; is it even necessary? +}; + +// typographic points are 1/72 inch; this parameter is 1/96 inch +// fortunately Microsoft does this too, in https://msdn.microsoft.com/en-us/library/windows/desktop/dd371554%28v=vs.85%29.aspx +#define pointSizeToDWriteSize(size) (size * (96.0 / 72.0)) + +static const DWRITE_FONT_STYLE dwriteItalics[] = { + [uiDrawTextItalicNormal] = DWRITE_FONT_STYLE_NORMAL, + [uiDrawTextItalicOblique] = DWRITE_FONT_STYLE_OBLIQUE, + [uiDrawTextItalicItalic] = DWRITE_FONT_STYLE_ITALIC, +}; + +static const DWRITE_FONT_STRETCH dwriteStretches[] = { + [uiDrawTextStretchUltraCondensed] = DWRITE_FONT_STRETCH_ULTRA_CONDENSED, + [uiDrawTextStretchExtraCondensed] = DWRITE_FONT_STRETCH_EXTRA_CONDENSED, + [uiDrawTextStretchCondensed] = DWRITE_FONT_STRETCH_CONDENSED, + [uiDrawTextStretchSemiCondensed] = DWRITE_FONT_STRETCH_SEMI_CONDENSED, + [uiDrawTextStretchNormal] = DWRITE_FONT_STRETCH_NORMAL, + [uiDrawTextStretchSemiExpanded] = DWRITE_FONT_STRETCH_SEMI_EXPANDED, + [uiDrawTextStretchExpanded] = DWRITE_FONT_STRETCH_EXPANDED, + [uiDrawTextStretchExtraExpanded] = DWRITE_FONT_STRETCH_EXTRA_EXPANDED, + [uiDrawTextStretchUltraExpanded] = DWRITE_FONT_STRETCH_ULTRA_EXPANDED, +}; + +struct lineInfo { + size_t startPos; + size_t endPos; + size_t newlineCount; + double x; + double y; + double width; + double height; + double baseline; +}; + +// this function is deeply indebted to the PadWrite sample: https://github.com/Microsoft/Windows-classic-samples/blob/master/Samples/Win7Samples/multimedia/DirectWrite/PadWrite/TextEditor.cpp +static void computeLineInfo(uiDrawTextLayout *tl) +{ + DWRITE_LINE_METRICS *dlm; + size_t nextStart; + UINT32 i; + DWRITE_HIT_TEST_METRICS htm; + UINT32 unused; + HRESULT hr; + + // TODO make sure this is legal; if not, switch to GetMetrics() and use its line count field instead + hr = tl->layout->GetLineMetrics(NULL, 0, &(tl->nLines)); + switch (hr) { + case S_OK: + // TODO what do we do here + case E_NOT_SUFFICIENT_BUFFER: + break; + default: + logHRESULT(L"error getting number of lines in IDWriteTextLayout", hr); + } + tl->lineInfo = (struct lineInfo *) uiAlloc(tl->nLines * sizeof (struct lineInfo), "struct lineInfo[] (text layout)"); + + dlm = new DWRITE_LINE_METRICS[tl->nLines]; + // TODO make sure pasisng NULL here is legal + hr = tl->layout->GetLineMetrics(dlm, tl->nLines, NULL); + if (hr != S_OK) + logHRESULT(L"error getting IDWriteTextLayout line metrics", hr); + + // assume the first line starts at position 0 and the string flow is incremental + nextStart = 0; + for (i = 0; i < tl->nLines; i++) { + tl->lineinfo[i].startPos = nextStart; + tl->lineinfo[i].endPos = nextStart + dlm[i].length; + tl->lineinfo[i].newlineCount = dlm[i].newlineLength; + nextStart = tl->lineinfo[i].endpos; + + hr = layout->HitTestTextRange(line->startPos, (line->endPos - line->newlineCount) - line->startPos, + 0, 0, + &htm, 1, &unused); + if (hr == E_NOT_SUFFICIENT_BUFFER) + logHRESULT(L"TODO CONTACT ANDLABS — IDWriteTextLayout::HitTestTextRange() can return multiple ranges for a single line", hr); + if (hr != S_OK) + logHRESULT(L"error getting IDWriteTextLayout line rect", hr); + // TODO verify htm.textPosition and htm.length? + tl->lineInfo[i].x = htm.left; + tl->lineInfo[i].y = htm.top; + tl->lineInfo[i].width = htm.width; + tl->lineInfo[i].height = htm.height; + // TODO verify dlm[i].height == htm.height + + // TODO on Windows 8.1 and/or 10 we can use DWRITE_LINE_METRICS1 to get specific info about the ascent and descent; do we have an alternative? + // TODO and even on those platforms can we somehow split tyographic leading from spacing? + // TODO and on that note, can we have both line spacing proportionally above and uniformly below? + tl->lineinfo[i].baseline = dlm[i].baseline; + } + + delete[] dlm; +} + +uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescriptor *defaultFont, double width) +{ + uiDrawTextLayout *tl; + WCHAR *wDefaultFamily; + DWRITE_WORD_WRAPPING wrap; + FLOAT maxWidth; + HRESULT hr; + + tl = uiNew(uiDrawTextLayout); + + wDefaultFamily = toUTF16(defaultFont->Family); + hr = dwfactory->CreateTextFormat( + wDefaultFamily, NULL, + // for the most part, DirectWrite weights correlate to ours + // the differences: + // - Minimum — libui: 0, DirectWrite: 1 + // - Maximum — libui: 1000, DirectWrite: 999 + // TODO figure out what to do about this shorter range (the actual major values are the same (but with different names), so it's just a range issue) + (DWRITE_FONT_WEIGHT) (defaultFont->Weight), + dwriteItalics[defaultFont->Italic], + dwriteStrecthes[defaultFont->Stretch], + pointSizeToDWriteSize(defaultFont->Size), + // see http://stackoverflow.com/questions/28397971/idwritefactorycreatetextformat-failing and https://msdn.microsoft.com/en-us/library/windows/desktop/dd368203.aspx + // TODO use the current locale? + L"", + &(tl->format)); + if (hr != S_OK) + logHRESULT(L"error creating IDWriteTextFormat", hr); + + hr = dwfactory->CreateTextLayout( + attrstrUTF16(s), attrstrUTF16Len(s), + tl->format, + // FLOAT is float, not double, so this should work... TODO + FLT_MAX, FLT_MAX, + &(tl->layout)); + if (hr != S_OK) + logHRESULT(L"error creating IDWriteTextLayout", hr); + + // and set the width + // this is the only wrapping mode (apart from "no wrap") available prior to Windows 8.1 (TODO verify this fact) (TODO this should be the default anyway) + wrap = DWRITE_WORD_WRAPPING_WRAP; + maxWidth = (FLOAT) width; + if (width < 0) { + // TODO is this wrapping juggling even necessary? + wrap = DWRITE_WORD_WRAPPING_NO_WRAP; + // setting the max width in this case technically isn't needed since the wrap mode will simply ignore the max width, but let's do it just to be safe + maxWidth = FLT_MAX; // see TODO above + } + hr = tl->layout->SetWordWrapping(wrap); + if (hr != S_OK) + logHRESULT(L"error setting IDWriteTextLayout word wrapping mode", hr); + hr = tl->layout->SetMaxWidth(maxWidth); + if (hr != S_OK) + logHRESULT(L"error setting IDWriteTextLayout max layout width", hr); + + computeLineInfo(tl); + + // and finally copy the UTF-16 to UTF-8 index conversion table + tl->u16tou8 = attrstrCopyUTF16ToUTF8(s, &(tl->nu16tou8)); + + // TODO can/should this be moved elsewhere? + uiFree(wDefaultFamily); + return tl; +} + +void uiDrawFreeTextLayout(uiDrawTextLayout *tl) +{ + uiFree(tl->u16tou8); + uiFree(tl->lineInfo); + tl->layout->Release(); + tl->format->Release(); + uiFree(tl); +} + +void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) +{ + D2D1_POINT_2F pt; + ID2D1Brush *black; + + // TODO document that fully opaque black is the default text color; figure out whether this is upheld in various scenarios on other platforms + // TODO figure out if this needs to be cleaned out + black = mkSolidBrush(c->rt, 0.0, 0.0, 0.0, 1.0); + + pt.x = x; + pt.y = y; + // TODO D2D1_DRAW_TEXT_OPTIONS_NO_SNAP? + // TODO D2D1_DRAW_TEXT_OPTIONS_CLIP? + // TODO when setting 8.1 as minimum (TODO verify), D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT? + // TODO what is our pixel snapping setting related to the OPTIONS enum values? + c->rt->DrawTextLayout(pt, tl->layout, black, D2D1_DRAW_TEXT_OPTIONS_NONE); + + black->Release(); +} + +// TODO for a single line the height includes the leading; should it? TextEdit on OS X always includes the leading and/or paragraph spacing, otherwise Klee won't work... +void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height) +{ + DWRITE_TEXT_METRICS metrics; + HRESULT hr; + + hr = tl->layout->GetMetrics(&metrics); + if (hr != S_OK) + logHRESULT(L"error getting IDWriteTextLayout layout metrics", hr); + *width = metrics.width; + // TODO make sure the behavior of this on empty strings is the same on all platforms (ideally should be 0-width, line height-height; TODO note this in the docs too) + *height = metrics.height; +} + +int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) +{ + return tl->nLines; +} + +// DirectWrite doesn't provide a direct way to do this, so we have to do this manually +void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end) +{ + *start = tl->lineInfo[line].startPos; + *start = tl->u16tou8[*start]; + *end = tl->lineInfo[line].endPos - tl->lineInfo[line].newlineCount; + *end = tl->u16tou8[*end]; +} + +void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m) +{ + // TODO +} + +void uiDrawTextLayoutByteIndexToGraphemeRect(uiDrawTextLayout *tl, size_t pos, int *line, double *x, double *y, double *width, double *height) +{ +} + +void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) +{ + // TODO +} + +void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r) +{ +} From c0781a13ae5839617910c45f79d1297cb89810c9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 20 Jan 2017 03:24:06 -0500 Subject: [PATCH 0553/1329] Fixed compile errors. Jesus. Runtime errors next. --- common/uipriv.h | 4 +- windows/_old_drawtext.cpp | 24 ----------- windows/drawtext.cpp | 85 ++++++++++++++++++++++++++------------- windows/graphemes.cpp | 3 +- windows/utf16.cpp | 2 +- 5 files changed, 62 insertions(+), 56 deletions(-) diff --git a/common/uipriv.h b/common/uipriv.h index 5ed23bf5..e62e86f8 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -1,5 +1,8 @@ // 6 april 2015 +// this must go outside other extern "C" blocks, otherwise we'll get double-declaration errors +#include "utf.h" + #ifdef __cplusplus extern "C" { #endif @@ -7,7 +10,6 @@ extern "C" { #include #include #include "controlsigs.h" -#include "utf.h" extern uiInitOptions options; diff --git a/windows/_old_drawtext.cpp b/windows/_old_drawtext.cpp index a10e1ad6..dec0478d 100644 --- a/windows/_old_drawtext.cpp +++ b/windows/_old_drawtext.cpp @@ -237,30 +237,6 @@ void uiDrawFreeTextLayout(uiDrawTextLayout *layout) uiFree(layout); } -static ID2D1SolidColorBrush *mkSolidBrush(ID2D1RenderTarget *rt, double r, double g, double b, double a) -{ - D2D1_BRUSH_PROPERTIES props; - D2D1_COLOR_F color; - ID2D1SolidColorBrush *brush; - HRESULT hr; - - ZeroMemory(&props, sizeof (D2D1_BRUSH_PROPERTIES)); - props.opacity = 1.0; - // identity matrix - props.transform._11 = 1; - props.transform._22 = 1; - color.r = r; - color.g = g; - color.b = b; - color.a = a; - hr = rt->CreateSolidColorBrush( - &color, - &props, - &brush); - if (hr != S_OK) - logHRESULT(L"error creating solid brush", hr); - return brush; -} IDWriteTextLayout *prepareLayout(uiDrawTextLayout *layout, ID2D1RenderTarget *rt) { diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 3932af52..8877396e 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -1,36 +1,41 @@ // 17 january 2017 #include "uipriv_windows.hpp" +#include "draw.hpp" struct uiDrawTextLayout { IDWriteTextFormat *format; IDWriteTextLayout *layout; - UINT32 *nLines; + UINT32 nLines; struct lineInfo *lineInfo; // for converting DirectWrite indices to byte offsets size_t *u16tou8; size_t nu16tou8; // TODO I don't like the casing of this name; is it even necessary? }; +// TODO copy notes about DirectWrite DIPs being equal to Direct2D DIPs here + // typographic points are 1/72 inch; this parameter is 1/96 inch // fortunately Microsoft does this too, in https://msdn.microsoft.com/en-us/library/windows/desktop/dd371554%28v=vs.85%29.aspx #define pointSizeToDWriteSize(size) (size * (96.0 / 72.0)) -static const DWRITE_FONT_STYLE dwriteItalics[] = { - [uiDrawTextItalicNormal] = DWRITE_FONT_STYLE_NORMAL, - [uiDrawTextItalicOblique] = DWRITE_FONT_STYLE_OBLIQUE, - [uiDrawTextItalicItalic] = DWRITE_FONT_STYLE_ITALIC, +// TODO should be const but then I can't operator[] on it; the real solution is to find a way to do designated array initializers in C++11 but I do not know enough C++ voodoo to make it work (it is possible but no one else has actually done it before) +static std::map dwriteItalics = { + { uiDrawTextItalicNormal, DWRITE_FONT_STYLE_NORMAL }, + { uiDrawTextItalicOblique, DWRITE_FONT_STYLE_OBLIQUE }, + { uiDrawTextItalicItalic, DWRITE_FONT_STYLE_ITALIC }, }; -static const DWRITE_FONT_STRETCH dwriteStretches[] = { - [uiDrawTextStretchUltraCondensed] = DWRITE_FONT_STRETCH_ULTRA_CONDENSED, - [uiDrawTextStretchExtraCondensed] = DWRITE_FONT_STRETCH_EXTRA_CONDENSED, - [uiDrawTextStretchCondensed] = DWRITE_FONT_STRETCH_CONDENSED, - [uiDrawTextStretchSemiCondensed] = DWRITE_FONT_STRETCH_SEMI_CONDENSED, - [uiDrawTextStretchNormal] = DWRITE_FONT_STRETCH_NORMAL, - [uiDrawTextStretchSemiExpanded] = DWRITE_FONT_STRETCH_SEMI_EXPANDED, - [uiDrawTextStretchExpanded] = DWRITE_FONT_STRETCH_EXPANDED, - [uiDrawTextStretchExtraExpanded] = DWRITE_FONT_STRETCH_EXTRA_EXPANDED, - [uiDrawTextStretchUltraExpanded] = DWRITE_FONT_STRETCH_ULTRA_EXPANDED, +// TODO should be const but then I can't operator[] on it; the real solution is to find a way to do designated array initializers in C++11 but I do not know enough C++ voodoo to make it work (it is possible but no one else has actually done it before) +static std::map dwriteStretches = { + { uiDrawTextStretchUltraCondensed, DWRITE_FONT_STRETCH_ULTRA_CONDENSED }, + { uiDrawTextStretchExtraCondensed, DWRITE_FONT_STRETCH_EXTRA_CONDENSED }, + { uiDrawTextStretchCondensed, DWRITE_FONT_STRETCH_CONDENSED }, + { uiDrawTextStretchSemiCondensed, DWRITE_FONT_STRETCH_SEMI_CONDENSED }, + { uiDrawTextStretchNormal, DWRITE_FONT_STRETCH_NORMAL }, + { uiDrawTextStretchSemiExpanded, DWRITE_FONT_STRETCH_SEMI_EXPANDED }, + { uiDrawTextStretchExpanded, DWRITE_FONT_STRETCH_EXPANDED }, + { uiDrawTextStretchExtraExpanded, DWRITE_FONT_STRETCH_EXTRA_EXPANDED }, + { uiDrawTextStretchUltraExpanded, DWRITE_FONT_STRETCH_ULTRA_EXPANDED }, }; struct lineInfo { @@ -56,14 +61,11 @@ static void computeLineInfo(uiDrawTextLayout *tl) // TODO make sure this is legal; if not, switch to GetMetrics() and use its line count field instead hr = tl->layout->GetLineMetrics(NULL, 0, &(tl->nLines)); - switch (hr) { - case S_OK: + // ugh, HRESULT_TO_WIN32() is an inline function and is not constexpr so we can't use switch here + if (hr == S_OK) { // TODO what do we do here - case E_NOT_SUFFICIENT_BUFFER: - break; - default: + } else if (hr != E_NOT_SUFFICIENT_BUFFER) logHRESULT(L"error getting number of lines in IDWriteTextLayout", hr); - } tl->lineInfo = (struct lineInfo *) uiAlloc(tl->nLines * sizeof (struct lineInfo), "struct lineInfo[] (text layout)"); dlm = new DWRITE_LINE_METRICS[tl->nLines]; @@ -75,12 +77,12 @@ static void computeLineInfo(uiDrawTextLayout *tl) // assume the first line starts at position 0 and the string flow is incremental nextStart = 0; for (i = 0; i < tl->nLines; i++) { - tl->lineinfo[i].startPos = nextStart; - tl->lineinfo[i].endPos = nextStart + dlm[i].length; - tl->lineinfo[i].newlineCount = dlm[i].newlineLength; - nextStart = tl->lineinfo[i].endpos; + tl->lineInfo[i].startPos = nextStart; + tl->lineInfo[i].endPos = nextStart + dlm[i].length; + tl->lineInfo[i].newlineCount = dlm[i].newlineLength; + nextStart = tl->lineInfo[i].endPos; - hr = layout->HitTestTextRange(line->startPos, (line->endPos - line->newlineCount) - line->startPos, + hr = tl->layout->HitTestTextRange(tl->lineInfo[i].startPos, (tl->lineInfo[i].endPos - tl->lineInfo[i].newlineCount) - tl->lineInfo[i].startPos, 0, 0, &htm, 1, &unused); if (hr == E_NOT_SUFFICIENT_BUFFER) @@ -97,7 +99,7 @@ static void computeLineInfo(uiDrawTextLayout *tl) // TODO on Windows 8.1 and/or 10 we can use DWRITE_LINE_METRICS1 to get specific info about the ascent and descent; do we have an alternative? // TODO and even on those platforms can we somehow split tyographic leading from spacing? // TODO and on that note, can we have both line spacing proportionally above and uniformly below? - tl->lineinfo[i].baseline = dlm[i].baseline; + tl->lineInfo[i].baseline = dlm[i].baseline; } delete[] dlm; @@ -123,7 +125,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto // TODO figure out what to do about this shorter range (the actual major values are the same (but with different names), so it's just a range issue) (DWRITE_FONT_WEIGHT) (defaultFont->Weight), dwriteItalics[defaultFont->Italic], - dwriteStrecthes[defaultFont->Stretch], + dwriteStretches[defaultFont->Stretch], pointSizeToDWriteSize(defaultFont->Size), // see http://stackoverflow.com/questions/28397971/idwritefactorycreatetextformat-failing and https://msdn.microsoft.com/en-us/library/windows/desktop/dd368203.aspx // TODO use the current locale? @@ -133,7 +135,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto logHRESULT(L"error creating IDWriteTextFormat", hr); hr = dwfactory->CreateTextLayout( - attrstrUTF16(s), attrstrUTF16Len(s), + (const WCHAR *) attrstrUTF16(s), attrstrUTF16Len(s), tl->format, // FLOAT is float, not double, so this should work... TODO FLT_MAX, FLT_MAX, @@ -177,6 +179,31 @@ void uiDrawFreeTextLayout(uiDrawTextLayout *tl) uiFree(tl); } +static ID2D1SolidColorBrush *mkSolidBrush(ID2D1RenderTarget *rt, double r, double g, double b, double a) +{ + D2D1_BRUSH_PROPERTIES props; + D2D1_COLOR_F color; + ID2D1SolidColorBrush *brush; + HRESULT hr; + + ZeroMemory(&props, sizeof (D2D1_BRUSH_PROPERTIES)); + props.opacity = 1.0; + // identity matrix + props.transform._11 = 1; + props.transform._22 = 1; + color.r = r; + color.g = g; + color.b = b; + color.a = a; + hr = rt->CreateSolidColorBrush( + &color, + &props, + &brush); + if (hr != S_OK) + logHRESULT(L"error creating solid brush", hr); + return brush; +} + void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) { D2D1_POINT_2F pt; diff --git a/windows/graphemes.cpp b/windows/graphemes.cpp index d4fc1e1b..a4aff059 100644 --- a/windows/graphemes.cpp +++ b/windows/graphemes.cpp @@ -57,7 +57,7 @@ struct graphemes *graphemes(void *s, size_t len) g = uiNew(struct graphemes); - hr = itemize(str, len, &items, &n); + hr = itemize(str, len, &items, &nItems); if (hr != S_OK) logHRESULT(L"error itemizing string for finding grapheme cluster boundaries", hr); g->len = nItems; @@ -72,6 +72,7 @@ struct graphemes *graphemes(void *s, size_t len) SCRIPT_ITEM *curItem, *nextItem; SCRIPT_LOGATTR *logattr; size_t *curGTP; + int i; curItem = items + curItemIndex; nextItem = curItem + 1; diff --git a/windows/utf16.cpp b/windows/utf16.cpp index 6271fff7..21d2f8a5 100644 --- a/windows/utf16.cpp +++ b/windows/utf16.cpp @@ -36,7 +36,7 @@ char *toUTF8(const WCHAR *wstr) str = (char *) uiAlloc((n + 1) * sizeof (char), "char[]"); sp = str; while (*wstr) { - wstr = utf16DecodeRune(wstr, &rune); + wstr = utf16DecodeRune(wstr, 0, &rune); n = utf8EncodeRune(rune, sp); sp += n; } From 88ea7c4665a661edc2d72da16090ac2f336cfa5e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 20 Jan 2017 04:34:15 -0500 Subject: [PATCH 0554/1329] Fixed runtime errors. Yay it works on all platforms now! Also more TODOs. Now the real work is getting the rest of the functionality in. --- windows/drawtext.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 8877396e..1e35a582 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -69,8 +69,9 @@ static void computeLineInfo(uiDrawTextLayout *tl) tl->lineInfo = (struct lineInfo *) uiAlloc(tl->nLines * sizeof (struct lineInfo), "struct lineInfo[] (text layout)"); dlm = new DWRITE_LINE_METRICS[tl->nLines]; - // TODO make sure pasisng NULL here is legal - hr = tl->layout->GetLineMetrics(dlm, tl->nLines, NULL); + // we can't pass NULL here; it outright crashes if we do + // TODO verify the numbers haven't changed + hr = tl->layout->GetLineMetrics(dlm, tl->nLines, &unused); if (hr != S_OK) logHRESULT(L"error getting IDWriteTextLayout line metrics", hr); @@ -204,6 +205,7 @@ static ID2D1SolidColorBrush *mkSolidBrush(ID2D1RenderTarget *rt, double r, doubl return brush; } +// TODO this ignores clipping? void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) { D2D1_POINT_2F pt; From 2d09f2293295793ca3989be3842a95faf7357e64 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 20 Jan 2017 12:46:00 -0500 Subject: [PATCH 0555/1329] Added a debugging guide for Windows. Will have to investigate this more, because I can't find any information online that suggests Direct2D should not respect clipping when drawing text :S Also more TODOs. --- examples/drawtext/main.c | 15 +++++++++++++++ windows/graphemes.cpp | 2 ++ 2 files changed, 17 insertions(+) diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c index 10f41ad5..d01d96c6 100644 --- a/examples/drawtext/main.c +++ b/examples/drawtext/main.c @@ -49,6 +49,21 @@ static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *p) uiDrawClip(p->Context, path); uiDrawFreePath(path); + // TODO get rid of this later + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathAddRectangle(path, -100, -100, + p->AreaWidth * 2, + p->AreaHeight * 2); + uiDrawPathEnd(path); + uiDrawBrush b; + b.Type = uiDrawBrushTypeSolid; + b.R = 0.0; + b.G = 1.0; + b.B = 0.0; + b.A = 1.0; + uiDrawFill(p->Context, path, &b); + uiDrawFreePath(path); + layout = uiDrawNewTextLayout(attrstr, &defaultFont, p->AreaWidth - 2 * margins); diff --git a/windows/graphemes.cpp b/windows/graphemes.cpp index a4aff059..2b93fab4 100644 --- a/windows/graphemes.cpp +++ b/windows/graphemes.cpp @@ -5,6 +5,8 @@ // So let's use Uniscribe (see http://archives.miloush.net/michkap/archive/2005/01/14/352802.html) // See also http://www.catch22.net/tuts/uniscribe-mysteries, http://www.catch22.net/tuts/keyboard-navigation, and https://maxradi.us/documents/uniscribe/ for more details. +// TODO the DirectWrite equivalent appears to be https://msdn.microsoft.com/en-us/library/windows/desktop/dd316625(v=vs.85).aspx but is somehow somewhat more complicated to use than Uniscribe is! maybe the PadWrite sample uses it? or should we just keep using Uniscribe? + int graphemesTakesUTF16(void) { return 1; From 6ef6ed8cdeb28882c15646a4bc3ff86b310b1cf6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 20 Jan 2017 13:25:21 -0500 Subject: [PATCH 0556/1329] Expanded the drawtext example to allow for multiple examples and options in the examples. Our old makefiles wouldn't allow examples to be spread across multiple files like this, so yay cmake? --- examples/CMakeLists.txt | 3 + examples/drawtext/basic.c | 81 +++++++++++++++++++++++++ examples/drawtext/drawtext.h | 17 ++++++ examples/drawtext/main.c | 113 ++++++++++++++--------------------- 4 files changed, 145 insertions(+), 69 deletions(-) create mode 100644 examples/drawtext/basic.c create mode 100644 examples/drawtext/drawtext.h diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 75b7f9d1..430224d8 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -32,9 +32,12 @@ if(NOT WIN32) endif() _add_example(drawtext + drawtext/basic.c drawtext/main.c ${_EXAMPLE_RESOURCES_RC} ) +target_include_directories(drawtext + PRIVATE drawtext) add_custom_target(examples DEPENDS diff --git a/examples/drawtext/basic.c b/examples/drawtext/basic.c new file mode 100644 index 00000000..2d3d7f3d --- /dev/null +++ b/examples/drawtext/basic.c @@ -0,0 +1,81 @@ +// 17 january 2017 +#include "drawtext.h" + +static const char *text = + "It is with a kind of fear that I begin to write the history of my life. " + "I have, as it were, a superstitious hesitation in lifting the veil that " + "clings about my childhood like a golden mist. The task of writing an " + "autobiography is a difficult one. When I try to classify my earliest " + "impressions, I find that fact and fancy look alike across the years that " + "link the past with the present. The woman paints the child's experiences " + "in her own fantasy. A few impressions stand out vividly from the first " + "years of my life; but \"the shadows of the prison-house are on the rest.\" " + "Besides, many of the joys and sorrows of childhood have lost their " + "poignancy; and many incidents of vital importance in my early education " + "have been forgotten in the excitement of great discoveries. In order, " + "therefore, not to be tedious I shall try to present in a series of " + "sketches only the episodes that seem to me to be the most interesting " + "and important." + ""; +static char fontFamily[] = "Palatino"; +// TODO should be const; look at constructor function +static uiDrawFontDescriptor defaultFont = { + .Family = fontFamily, + .Size = 18, + .Weight = uiDrawTextWeightNormal, + .Italic = uiDrawTextItalicNormal, + .Stretch = uiDrawTextStretchNormal, +}; +static uiAttributedString *attrstr; + +#define margins 10 + +static void draw(uiAreaDrawParams *p) +{ + uiDrawPath *path; + uiDrawTextLayout *layout; + + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathAddRectangle(path, margins, margins, + p->AreaWidth - 2 * margins, + p->AreaHeight - 2 * margins); + uiDrawPathEnd(path); + uiDrawClip(p->Context, path); + uiDrawFreePath(path); + + // TODO get rid of this later + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathAddRectangle(path, -100, -100, + p->AreaWidth * 2, + p->AreaHeight * 2); + uiDrawPathEnd(path); + uiDrawBrush b; + b.Type = uiDrawBrushTypeSolid; + b.R = 0.0; + b.G = 1.0; + b.B = 0.0; + b.A = 1.0; + uiDrawFill(p->Context, path, &b); + uiDrawFreePath(path); + + layout = uiDrawNewTextLayout(attrstr, + &defaultFont, + p->AreaWidth - 2 * margins); + uiDrawText(p->Context, layout, margins, margins); + uiDrawFreeTextLayout(layout); +} + +static struct example basicExample; + +struct example *mkBasicExample(void) +{ + basicExample.name = "Basic Paragraph of Text"; + basicExample.panel = uiControl(uiNewVerticalBox()); + basicExample.draw = draw; + + attrstr = uiNewAttributedString(text); + + return &basicExample; +} + +// TODO on GTK+ an area by itself in a window doesn't get expand properties set properly? diff --git a/examples/drawtext/drawtext.h b/examples/drawtext/drawtext.h new file mode 100644 index 00000000..adbac19b --- /dev/null +++ b/examples/drawtext/drawtext.h @@ -0,0 +1,17 @@ +// 20 january 2017 +#include +#include +#include "../../ui.h" + +struct example { + const char *name; + uiControl *panel; + void (*draw)(uiAreaDrawParams *p); + // TODO mouse and key? +}; + +// main.c +extern void redraw(void); + +// basic.c +extern struct example *mkBasicExample(void); diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c index d01d96c6..ebc9210a 100644 --- a/examples/drawtext/main.c +++ b/examples/drawtext/main.c @@ -1,74 +1,32 @@ // 17 january 2017 -#include -#include -#include "../../ui.h" +#include "drawtext.h" -uiWindow *mainwin; -uiArea *area; -uiAreaHandler handler; +static uiWindow *mainwin; +static uiBox *box; +static uiCombobox *exampleList; +static uiArea *area; +static uiAreaHandler handler; -const char *text = - "It is with a kind of fear that I begin to write the history of my life. " - "I have, as it were, a superstitious hesitation in lifting the veil that " - "clings about my childhood like a golden mist. The task of writing an " - "autobiography is a difficult one. When I try to classify my earliest " - "impressions, I find that fact and fancy look alike across the years that " - "link the past with the present. The woman paints the child's experiences " - "in her own fantasy. A few impressions stand out vividly from the first " - "years of my life; but \"the shadows of the prison-house are on the rest.\" " - "Besides, many of the joys and sorrows of childhood have lost their " - "poignancy; and many incidents of vital importance in my early education " - "have been forgotten in the excitement of great discoveries. In order, " - "therefore, not to be tedious I shall try to present in a series of " - "sketches only the episodes that seem to me to be the most interesting " - "and important." - ""; -char fontFamily[] = "Palatino"; -// TODO should be const; look at constructor function -uiDrawFontDescriptor defaultFont = { - .Family = fontFamily, - .Size = 18, - .Weight = uiDrawTextWeightNormal, - .Italic = uiDrawTextItalicNormal, - .Stretch = uiDrawTextStretchNormal, -}; -uiAttributedString *attrstr; +#define nExamples 20 +static struct example *examples[nExamples]; +static int curExample = 0; -#define margins 10 +static void onExampleChanged(uiCombobox *c, void *data) +{ + uiControlHide(examples[curExample]->panel); + curExample = uiComboboxSelected(exampleList); + uiControlShow(examples[curExample]->panel); + redraw(); +} + +void redraw(void) +{ + uiAreaQueueRedrawAll(area); +} static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *p) { - uiDrawPath *path; - uiDrawTextLayout *layout; - - path = uiDrawNewPath(uiDrawFillModeWinding); - uiDrawPathAddRectangle(path, margins, margins, - p->AreaWidth - 2 * margins, - p->AreaHeight - 2 * margins); - uiDrawPathEnd(path); - uiDrawClip(p->Context, path); - uiDrawFreePath(path); - - // TODO get rid of this later - path = uiDrawNewPath(uiDrawFillModeWinding); - uiDrawPathAddRectangle(path, -100, -100, - p->AreaWidth * 2, - p->AreaHeight * 2); - uiDrawPathEnd(path); - uiDrawBrush b; - b.Type = uiDrawBrushTypeSolid; - b.R = 0.0; - b.G = 1.0; - b.B = 0.0; - b.A = 1.0; - uiDrawFill(p->Context, path, &b); - uiDrawFreePath(path); - - layout = uiDrawNewTextLayout(attrstr, - &defaultFont, - p->AreaWidth - 2 * margins); - uiDrawText(p->Context, layout, margins, margins); - uiDrawFreeTextLayout(layout); + examples[curExample]->draw(p); } static void handlerMouseEvent(uiAreaHandler *a, uiArea *area, uiAreaMouseEvent *e) @@ -109,6 +67,7 @@ int main(void) { uiInitOptions o; const char *err; + int n; handler.Draw = handlerDraw; handler.MouseEvent = handlerMouseEvent; @@ -126,18 +85,34 @@ int main(void) uiOnShouldQuit(shouldQuit, NULL); - attrstr = uiNewAttributedString(text); - mainwin = uiNewWindow("libui Text-Drawing Example", 640, 480, 1); uiWindowOnClosing(mainwin, onClosing, NULL); + box = uiNewVerticalBox(); + uiWindowSetChild(mainwin, uiControl(box)); + + exampleList = uiNewCombobox(); + uiBoxAppend(box, uiControl(exampleList), 0); + area = uiNewArea(&handler); - // TODO on GTK+ this doesn't get expand properties set properly? - uiWindowSetChild(mainwin, uiControl(area)); + uiBoxAppend(box, uiControl(area), 1); + + n = 0; + examples[n] = mkBasicExample(); + uiComboboxAppend(exampleList, examples[n]->name); + uiBoxAppend(box, examples[n]->panel, 0); + n++; + // and set things up for the initial state + uiComboboxSetSelected(exampleList, 0); + uiComboboxOnSelected(exampleList, onExampleChanged, NULL); + // and set up the first one + onExampleChanged(NULL, NULL); uiControlShow(uiControl(mainwin)); uiMain(); - uiFreeAttributedString(attrstr); + + // TODO free examples + uiUninit(); return 0; } From 6ccf436206396a2a3b61400d18d00e1df18b74b6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 20 Jan 2017 16:36:44 -0500 Subject: [PATCH 0557/1329] Implemented Pango text metrics and expanded the drawtext basic page to draw metrics. Works on both Pango and OS X; DirectWrite comes next. --- darwin/drawtext.m | 2 + examples/drawtext/basic.c | 187 +++++++++++++++++++++++++++++++++++++- unix/drawtext.c | 60 +++++++++++- 3 files changed, 241 insertions(+), 8 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index a8a34542..a38c152d 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -246,6 +246,8 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) } // TODO document that the width and height of a layout is not necessarily the sum of the widths and heights of its constituent lines; this is definitely untrue on OS X, where lines are placed in such a way that the distance between baselines is always integral +// TODO width doesn't include trailing whitespace... +// TODO figure out how paragraph spacing should play into this void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height) { *width = tl->size.width; diff --git a/examples/drawtext/basic.c b/examples/drawtext/basic.c index 2d3d7f3d..04ea42e1 100644 --- a/examples/drawtext/basic.c +++ b/examples/drawtext/basic.c @@ -30,10 +30,93 @@ static uiAttributedString *attrstr; #define margins 10 +static uiBox *panel; +static uiCheckbox *showExtents; +static uiCheckbox *showLineBounds; +static uiCheckbox *showLineGuides; + +// TODO should this be const? +static double strokeDashes[] = { 5, 2 }; +// TODO this should be const +static uiDrawStrokeParams strokeParams = { + .Cap = uiDrawLineCapFlat, + .Join = uiDrawLineJoinMiter, + .Thickness = 1, + .MiterLimit = uiDrawDefaultMiterLimit, + .Dashes = strokeDashes, + .NumDashes = 2, + .DashPhase = 0, +}; + +// TODO should be const? +static uiDrawBrush fillBrushes[4] = { + { + .Type = uiDrawBrushTypeSolid, + .R = 1.0, + .G = 0.0, + .B = 0.0, + .A = 0.5, + }, + { + .Type = uiDrawBrushTypeSolid, + .R = 0.0, + .G = 1.0, + .B = 0.0, + .A = 0.5, + }, + { + .Type = uiDrawBrushTypeSolid, + .R = 0.0, + .G = 0.0, + .B = 1.0, + .A = 0.5, + }, + { + .Type = uiDrawBrushTypeSolid, + .R = 0.0, + .G = 1.0, + .B = 1.0, + .A = 0.5, + }, +}; +// TODO come up with better colors +static uiDrawBrush strokeBrushes[3] = { + // baseline + { + .Type = uiDrawBrushTypeSolid, + .R = 0.5, + .G = 0.5, + .B = 0.0, + .A = 0.75, + }, + // ascent + { + .Type = uiDrawBrushTypeSolid, + .R = 1.0, + .G = 0.0, + .B = 1.0, + .A = 0.75, + }, + // descent + { + .Type = uiDrawBrushTypeSolid, + .R = 0.5, + .G = 0.75, + .B = 1.0, + .A = 0.75, + }, +}; + static void draw(uiAreaDrawParams *p) { uiDrawPath *path; uiDrawTextLayout *layout; + uiDrawBrush b; + + b.Type = uiDrawBrushTypeSolid; + + // only clip the text, not the guides + uiDrawSave(p->Context); path = uiDrawNewPath(uiDrawFillModeWinding); uiDrawPathAddRectangle(path, margins, margins, @@ -44,33 +127,131 @@ static void draw(uiAreaDrawParams *p) uiDrawFreePath(path); // TODO get rid of this later +#if 0 path = uiDrawNewPath(uiDrawFillModeWinding); uiDrawPathAddRectangle(path, -100, -100, p->AreaWidth * 2, p->AreaHeight * 2); uiDrawPathEnd(path); - uiDrawBrush b; - b.Type = uiDrawBrushTypeSolid; b.R = 0.0; b.G = 1.0; b.B = 0.0; b.A = 1.0; uiDrawFill(p->Context, path, &b); uiDrawFreePath(path); +#endif layout = uiDrawNewTextLayout(attrstr, &defaultFont, p->AreaWidth - 2 * margins); uiDrawText(p->Context, layout, margins, margins); + + uiDrawRestore(p->Context); + + if (uiCheckboxChecked(showExtents)) { + double width, height; + + uiDrawTextLayoutExtents(layout, &width, &height); + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathAddRectangle(path, margins, margins, + width, height); + uiDrawPathEnd(path); + b.R = 1.0; + b.G = 0.0; + b.B = 1.0; + b.A = 0.5; + uiDrawStroke(p->Context, path, &b, &strokeParams); + uiDrawFreePath(path); + } + + if (uiCheckboxChecked(showLineBounds) || uiCheckboxChecked(showLineGuides)) { + uiDrawTextLayoutLineMetrics m; + int i, n; + int fill = 0; + + n = uiDrawTextLayoutNumLines(layout); + for (i = 0; i < n; i++) { + uiDrawTextLayoutLineGetMetrics(layout, i, &m); + + if (uiCheckboxChecked(showLineBounds)) { + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathAddRectangle(path, margins + m.X, margins + m.Y, + m.Width, m.Height); + uiDrawPathEnd(path); + uiDrawFill(p->Context, path, fillBrushes + fill); + uiDrawFreePath(path); + fill = (fill + 1) % 4; + } + if (uiCheckboxChecked(showLineGuides)) { + // baseline + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathNewFigure(path, + margins + m.X, + margins + m.BaselineY); + uiDrawPathLineTo(path, + margins + m.X + m.Width, + margins + m.BaselineY); + uiDrawPathEnd(path); + uiDrawStroke(p->Context, path, &(strokeBrushes[0]), &strokeParams); + uiDrawFreePath(path); + + // ascent line + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathNewFigure(path, + margins + m.X, + margins + m.BaselineY - m.Ascent); + uiDrawPathLineTo(path, + margins + m.X + m.Width, + margins + m.BaselineY - m.Ascent); + uiDrawPathEnd(path); + uiDrawStroke(p->Context, path, &(strokeBrushes[1]), &strokeParams); + uiDrawFreePath(path); + + // descent line + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathNewFigure(path, + margins + m.X, + margins + m.BaselineY + m.Descent); + uiDrawPathLineTo(path, + margins + m.X + m.Width, + margins + m.BaselineY + m.Descent); + uiDrawPathEnd(path); + uiDrawStroke(p->Context, path, &(strokeBrushes[2]), &strokeParams); + uiDrawFreePath(path); + } + } + } + uiDrawFreeTextLayout(layout); } static struct example basicExample; +// TODO share? +static void checkboxChecked(uiCheckbox *c, void *data) +{ + redraw(); +} + +static uiCheckbox *newCheckbox(const char *text) +{ + uiCheckbox *c; + + c = uiNewCheckbox(text); + uiCheckboxOnToggled(c, checkboxChecked, NULL); + uiBoxAppend(panel, uiControl(c), 0); + return c; +} + struct example *mkBasicExample(void) { + panel = uiNewVerticalBox(); + showExtents = newCheckbox("Show Layout Extents"); + showLineBounds = newCheckbox("Show Line Bounds"); + showLineGuides = newCheckbox("Show Line Guides"); + basicExample.name = "Basic Paragraph of Text"; - basicExample.panel = uiControl(uiNewVerticalBox()); + basicExample.panel = uiControl(panel); basicExample.draw = draw; attrstr = uiNewAttributedString(text); diff --git a/unix/drawtext.c b/unix/drawtext.c index 64ae9928..217b3c48 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -4,6 +4,7 @@ struct uiDrawTextLayout { PangoLayout *layout; + uiDrawTextLayoutLineMetrics *lineMetrics; }; // See https://developer.gnome.org/pango/1.30/pango-Cairo-Rendering.html#pango-Cairo-Rendering.description @@ -35,6 +36,55 @@ static const PangoStretch pangoStretches[] = { [uiDrawTextStretchUltraExpanded] = PANGO_STRETCH_ULTRA_EXPANDED, }; +// TODO neither these nor the overall extents seem to include trailing whitespace... we need to figure that out too +static void computeLineMetrics(uiDrawTextLayout *tl) +{ + PangoLayoutIter *iter; + PangoLayoutLine *pll; + PangoRectangle lineStartPos, lineExtents; + int i, n; + uiDrawTextLayoutLineMetrics *m; + + n = pango_layout_get_line_count(tl->layout); + tl->lineMetrics = (uiDrawTextLayoutLineMetrics *) uiAlloc(n * sizeof (uiDrawTextLayoutLineMetrics), "uiDrawTextLayoutLineMetrics[] (text layout)"); + iter = pango_layout_get_iter(tl->layout); + + m = tl->lineMetrics; + for (i = 0; i < n; i++) { + int baselineY; + + // TODO we use this instead of _get_yrange() because of the block of text in that function's description about how line spacing is distributed in Pango; we have to worry about this when we start adding line spacing... + baselineY = pango_layout_iter_get_baseline(iter); + pll = pango_layout_iter_get_line_readonly(iter); + pango_layout_index_to_pos(tl->layout, pll->start_index, &lineStartPos); + pango_layout_line_get_extents(pll, NULL, &lineExtents); + // TODO unref pll? + + // TODO is this correct for RTL glyphs? + m->X = pangoToCairo(lineStartPos.x); + // TODO fix the whole combined not being updated shenanigans in the static build (here because ugh) + m->Y = pangoToCairo(baselineY - PANGO_ASCENT(lineExtents)); + m->Width = pangoToCairo(lineExtents.width); + m->Height = pangoToCairo(lineExtents.height); + + m->BaselineY = pangoToCairo(baselineY); + m->Ascent = pangoToCairo(PANGO_ASCENT(lineExtents)); + m->Descent = pangoToCairo(PANGO_DESCENT(lineExtents)); + m->Leading = 0; // TODO + + m->ParagraphSpacingBefore = 0; // TODO + m->LineHeightSpace = 0; // TODO + m->LineSpacing = 0; // TODO + m->ParagraphSpacing = 0; // TODO + + // don't worry about the return value; we're not using this after the last line + pango_layout_iter_next_line(iter); + m++; + } + + pango_layout_iter_free(iter); +} + uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescriptor *defaultFont, double width) { uiDrawTextLayout *tl; @@ -76,11 +126,14 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto // TODO attributes + computeLineMetrics(tl); + return tl; } void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { + uiFree(tl->lineMetrics); g_object_unref(tl->layout); uiFree(tl); } @@ -112,15 +165,12 @@ void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start pll = pango_layout_get_line_readonly(tl->layout, line); *start = pll->start_index; *end = pll->start_index + pll->length; - // TODO unref? + // TODO unref pll? } void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m) { - PangoLayoutLine *pll; - - pll = pango_layout_get_line_readonly(tl->layout, line); - // TODO unref? + *m = tl->lineMetrics[line]; } // TODO From fc7fcd9f0596e05d6fc6c5d79fb3100ebcaab8d0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 20 Jan 2017 18:09:06 -0500 Subject: [PATCH 0558/1329] And implemented metrics stuff on Windows. --- windows/drawtext.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 1e35a582..208f02bc 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -227,6 +227,7 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) } // TODO for a single line the height includes the leading; should it? TextEdit on OS X always includes the leading and/or paragraph spacing, otherwise Klee won't work... +// TODO width does not include trailing whitespace void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height) { DWRITE_TEXT_METRICS metrics; @@ -246,6 +247,7 @@ int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) } // DirectWrite doesn't provide a direct way to do this, so we have to do this manually +// TODO does that comment still apply here or to the code at the top of this file? void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end) { *start = tl->lineInfo[line].startPos; @@ -256,7 +258,21 @@ void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m) { - // TODO + m->X = tl->lineInfo[line].x; + m->Y = tl->lineInfo[line].y; + m->Width = tl->lineInfo[line].width; + m->Height = tl->lineInfo[line].height; + + // TODO rename tl->lineInfo[line].baseline to .baselineOffset or something of the sort to make its meaning more clear + m->BaselineY = tl->lineInfo[line].y + tl->lineInfo[line].baseline; + m->Ascent = tl->lineInfo[line].baseline; + m->Descent = tl->lineInfo[line].height - tl->lineInfo[line].baseline; + m->Leading = 0; // TODO + + m->ParagraphSpacingBefore = 0; // TODO + m->LineHeightSpace = 0; // TODO + m->LineSpacing = 0; // TODO + m->ParagraphSpacing = 0; // TODO } void uiDrawTextLayoutByteIndexToGraphemeRect(uiDrawTextLayout *tl, size_t pos, int *line, double *x, double *y, double *width, double *height) From 87b7d5b4b7e9d15e9db1b45a52e1972f948153e3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 20 Jan 2017 19:34:16 -0500 Subject: [PATCH 0559/1329] Decided to remove uiDrawTextLayoutByteIndexToGraphemeRect(); the Range one later on handles it. Now we can do the hit testing functions! --- darwin/drawtext.m | 4 ---- ui_attrstr.h | 6 +++--- unix/drawtext.c | 4 ---- windows/drawtext.cpp | 4 ---- 4 files changed, 3 insertions(+), 15 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index a38c152d..f782d614 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -275,10 +275,6 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa *m = tl->lineMetrics[line]; } -void uiDrawTextLayoutByteIndexToGraphemeRect(uiDrawTextLayout *tl, size_t pos, int *line, double *x, double *y, double *width, double *height) -{ -} - void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) { CFIndex i; diff --git a/ui_attrstr.h b/ui_attrstr.h index 7cfedb10..8ca835ea 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -136,6 +136,9 @@ struct uiDrawTextLayoutHitTestResult { // int InTrailingWhitespace; // TODO? // double XFraction; +// extra TODO? +// double YFraction; +// or just have offsets instead? in addition? }; struct uiDrawTextLayoutByteRangeRectangle { @@ -163,9 +166,6 @@ _UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, dou _UI_EXTERN int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl); _UI_EXTERN void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end); _UI_EXTERN void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m); -// TODO redo this? remove it entirely? -_UI_EXTERN void uiDrawTextLayoutByteIndexToGraphemeRect(uiDrawTextLayout *tl, size_t pos, int *line, double *x, double *y, double *width, double *height); -// TODO partial offset? _UI_EXTERN void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result); _UI_EXTERN void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r); // TODO draw only a line? diff --git a/unix/drawtext.c b/unix/drawtext.c index 217b3c48..f8ec1e67 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -183,10 +183,6 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa } #endif -void uiDrawTextLayoutByteIndexToGraphemeRect(uiDrawTextLayout *tl, size_t pos, int *line, double *x, double *y, double *width, double *height) -{ -} - void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) { } diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 208f02bc..52099e79 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -275,10 +275,6 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa m->ParagraphSpacing = 0; // TODO } -void uiDrawTextLayoutByteIndexToGraphemeRect(uiDrawTextLayout *tl, size_t pos, int *line, double *x, double *y, double *width, double *height) -{ -} - void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) { // TODO From 339bdfc89b9447126f1ddd53717225f945f2e4b6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 20 Jan 2017 20:38:18 -0500 Subject: [PATCH 0560/1329] Started a hit-test and grapheme boundary test. Looks like we're not quite out of the woods with DirectWrite just yet. --- examples/CMakeLists.txt | 1 + examples/drawtext/drawtext.h | 3 ++ examples/drawtext/hittest.c | 69 ++++++++++++++++++++++++++++++++++++ examples/drawtext/main.c | 6 ++++ 4 files changed, 79 insertions(+) create mode 100644 examples/drawtext/hittest.c diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 430224d8..3fda1b82 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -33,6 +33,7 @@ endif() _add_example(drawtext drawtext/basic.c + drawtext/hittest.c drawtext/main.c ${_EXAMPLE_RESOURCES_RC} ) diff --git a/examples/drawtext/drawtext.h b/examples/drawtext/drawtext.h index adbac19b..ec821eb8 100644 --- a/examples/drawtext/drawtext.h +++ b/examples/drawtext/drawtext.h @@ -15,3 +15,6 @@ extern void redraw(void); // basic.c extern struct example *mkBasicExample(void); + +// hittest.c +extern struct example *mkHitTestExample(void); diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c new file mode 100644 index 00000000..37386082 --- /dev/null +++ b/examples/drawtext/hittest.c @@ -0,0 +1,69 @@ +// 20 january 2017 +#include "drawtext.h" + +static const char *text = + "Each of the glyphs an end user interacts with are called graphemes. " + "If you enter a byte range in the text boxes below and click the button, you can see the blue box move to surround that byte range, as well as what the actual byte range necessary is. " + "You'll also see the index of the first grapheme; uiAttributedString has facilities for converting between UTF-8 code points and grapheme indices. " + "Additionally, click on the string to move the caret. Watch the status text at the bottom change too. " + "That being said: " + "\xC3\x93O\xCC\x81 (combining accents) " + "A\xCC\x81\xE0\xAB\x81 (multiple combining accents) " + "\xE2\x80\xAE#\xE2\x80\xAC (RTL glyph) " + "\xF0\x9F\x92\xBB (non-BMP character) " + "\xF0\x9F\x92\xBB\xCC\x80 (combined non-BMP character) " + ""; +static char fontFamily[] = "Helvetica"; +static uiDrawFontDescriptor defaultFont = { + .Family = fontFamily, + .Size = 14, + .Weight = uiDrawTextWeightNormal, + .Italic = uiDrawTextItalicNormal, + .Stretch = uiDrawTextStretchNormal, +}; +static uiAttributedString *attrstr; + +#define margins 10 + +static uiBox *panel; + +static void draw(uiAreaDrawParams *p) +{ + uiDrawPath *path; + uiDrawTextLayout *layout; + + // only clip the text, not the guides + uiDrawSave(p->Context); + + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathAddRectangle(path, margins, margins, + p->AreaWidth - 2 * margins, + p->AreaHeight - 2 * margins); + uiDrawPathEnd(path); + uiDrawClip(p->Context, path); + uiDrawFreePath(path); + + layout = uiDrawNewTextLayout(attrstr, + &defaultFont, + p->AreaWidth - 2 * margins); + uiDrawText(p->Context, layout, margins, margins); + + uiDrawRestore(p->Context); + + uiDrawFreeTextLayout(layout); +} + +static struct example hitTestExample; + +struct example *mkHitTestExample(void) +{ + panel = uiNewVerticalBox(); + + hitTestExample.name = "Hit-Testing and Grapheme Boundaries"; + hitTestExample.panel = uiControl(panel); + hitTestExample.draw = draw; + + attrstr = uiNewAttributedString(text); + + return &hitTestExample; +} diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c index ebc9210a..8e5a93ec 100644 --- a/examples/drawtext/main.c +++ b/examples/drawtext/main.c @@ -100,6 +100,12 @@ int main(void) n = 0; examples[n] = mkBasicExample(); uiComboboxAppend(exampleList, examples[n]->name); + uiControlHide(examples[n]->panel); + uiBoxAppend(box, examples[n]->panel, 0); + n++; + examples[n] = mkHitTestExample(); + uiComboboxAppend(exampleList, examples[n]->name); + uiControlHide(examples[n]->panel); uiBoxAppend(box, examples[n]->panel, 0); n++; // and set things up for the initial state From c720e8147bf4430362ef186bcc3b4caa0c413fc2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 20 Jan 2017 20:57:20 -0500 Subject: [PATCH 0561/1329] Figured out what's going on. No clue how to fix it. --- windows/drawtext.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 52099e79..ae79b1f2 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -86,8 +86,9 @@ static void computeLineInfo(uiDrawTextLayout *tl) hr = tl->layout->HitTestTextRange(tl->lineInfo[i].startPos, (tl->lineInfo[i].endPos - tl->lineInfo[i].newlineCount) - tl->lineInfo[i].startPos, 0, 0, &htm, 1, &unused); + // TODO this happens with the hit test string on the line with the RTL override (presumably the overridden part is its own separate result); see how it affects metrics if (hr == E_NOT_SUFFICIENT_BUFFER) - logHRESULT(L"TODO CONTACT ANDLABS — IDWriteTextLayout::HitTestTextRange() can return multiple ranges for a single line", hr); +;else// logHRESULT(L"TODO CONTACT ANDLABS — IDWriteTextLayout::HitTestTextRange() can return multiple ranges for a single line", hr); if (hr != S_OK) logHRESULT(L"error getting IDWriteTextLayout line rect", hr); // TODO verify htm.textPosition and htm.length? From bbed231324bd252cfb93db90cc67fc8eb00a8f82 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 20 Jan 2017 22:25:37 -0500 Subject: [PATCH 0562/1329] Fixed up misused Unicode. --- examples/drawtext/hittest.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index 37386082..3d4f7677 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -8,10 +8,10 @@ static const char *text = "Additionally, click on the string to move the caret. Watch the status text at the bottom change too. " "That being said: " "\xC3\x93O\xCC\x81 (combining accents) " - "A\xCC\x81\xE0\xAB\x81 (multiple combining accents) " + "A\xCC\xAA\xEF\xB8\xA0 (multiple combining characters) " "\xE2\x80\xAE#\xE2\x80\xAC (RTL glyph) " "\xF0\x9F\x92\xBB (non-BMP character) " - "\xF0\x9F\x92\xBB\xCC\x80 (combined non-BMP character) " + "\xF0\x9F\x92\xBB\xCC\x80 (combined non-BMP character; may render strangely) " ""; static char fontFamily[] = "Helvetica"; static uiDrawFontDescriptor defaultFont = { From 351b3b607708ca9d2b66db14debde4e2ca6b9b66 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 20 Jan 2017 22:39:37 -0500 Subject: [PATCH 0563/1329] Added some debugging for that DirectWrite RTL stuff. --- examples/drawtext/hittest.c | 68 +++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index 3d4f7677..dc1f9dce 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -26,6 +26,39 @@ static uiAttributedString *attrstr; #define margins 10 static uiBox *panel; +static uiCheckbox *showLineBounds; + +// TODO should be const? +static uiDrawBrush fillBrushes[4] = { + { + .Type = uiDrawBrushTypeSolid, + .R = 1.0, + .G = 0.0, + .B = 0.0, + .A = 0.5, + }, + { + .Type = uiDrawBrushTypeSolid, + .R = 0.0, + .G = 1.0, + .B = 0.0, + .A = 0.5, + }, + { + .Type = uiDrawBrushTypeSolid, + .R = 0.0, + .G = 0.0, + .B = 1.0, + .A = 0.5, + }, + { + .Type = uiDrawBrushTypeSolid, + .R = 0.0, + .G = 1.0, + .B = 1.0, + .A = 0.5, + }, +}; static void draw(uiAreaDrawParams *p) { @@ -50,14 +83,49 @@ static void draw(uiAreaDrawParams *p) uiDrawRestore(p->Context); + if (uiCheckboxChecked(showLineBounds)) { + uiDrawTextLayoutLineMetrics m; + int i, n; + int fill = 0; + + n = uiDrawTextLayoutNumLines(layout); + for (i = 0; i < n; i++) { + uiDrawTextLayoutLineGetMetrics(layout, i, &m); + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathAddRectangle(path, margins + m.X, margins + m.Y, + m.Width, m.Height); + uiDrawPathEnd(path); + uiDrawFill(p->Context, path, fillBrushes + fill); + uiDrawFreePath(path); + fill = (fill + 1) % 4; + } + } + uiDrawFreeTextLayout(layout); } static struct example hitTestExample; +// TODO share? +static void checkboxChecked(uiCheckbox *c, void *data) +{ + redraw(); +} + +static uiCheckbox *newCheckbox(const char *text) +{ + uiCheckbox *c; + + c = uiNewCheckbox(text); + uiCheckboxOnToggled(c, checkboxChecked, NULL); + uiBoxAppend(panel, uiControl(c), 0); + return c; +} + struct example *mkHitTestExample(void) { panel = uiNewVerticalBox(); + showLineBounds = newCheckbox("Show Line Bounds (for debugging metrics)"); hitTestExample.name = "Hit-Testing and Grapheme Boundaries"; hitTestExample.panel = uiControl(panel); From 4de8d4402fea5f792922668f78d510f2c97db431 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 20 Jan 2017 23:15:10 -0500 Subject: [PATCH 0564/1329] More TODOs. --- darwin/drawtext.m | 1 + 1 file changed, 1 insertion(+) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index f782d614..2ac104c6 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -82,6 +82,7 @@ static CFAttributedStringRef attrstrToCoreFoundation(uiAttributedString *s, uiDr return mas; } +// TODO this is wrong for our grapheme test; figure out what it should be... static uiDrawTextLayoutLineMetrics *computeLineMetrics(CTFrameRef frame, CGSize size) { uiDrawTextLayoutLineMetrics *metrics; From f0b9ff9aba0df03ce79faa53eb6008237c80467d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 21 Jan 2017 09:39:53 -0500 Subject: [PATCH 0565/1329] Fixed multifragment lines on Windows, again with the help of the PadWrite sample. --- windows/drawtext.cpp | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index ae79b1f2..0caac7f3 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -54,9 +54,9 @@ static void computeLineInfo(uiDrawTextLayout *tl) { DWRITE_LINE_METRICS *dlm; size_t nextStart; - UINT32 i; - DWRITE_HIT_TEST_METRICS htm; - UINT32 unused; + UINT32 i, j; + DWRITE_HIT_TEST_METRICS *htm; + UINT32 nFragments, unused; HRESULT hr; // TODO make sure this is legal; if not, switch to GetMetrics() and use its line count field instead @@ -83,20 +83,34 @@ static void computeLineInfo(uiDrawTextLayout *tl) tl->lineInfo[i].newlineCount = dlm[i].newlineLength; nextStart = tl->lineInfo[i].endPos; + // a line can have multiple fragments; for example, if there's a bidirectional override in the middle of a line hr = tl->layout->HitTestTextRange(tl->lineInfo[i].startPos, (tl->lineInfo[i].endPos - tl->lineInfo[i].newlineCount) - tl->lineInfo[i].startPos, 0, 0, - &htm, 1, &unused); - // TODO this happens with the hit test string on the line with the RTL override (presumably the overridden part is its own separate result); see how it affects metrics - if (hr == E_NOT_SUFFICIENT_BUFFER) -;else// logHRESULT(L"TODO CONTACT ANDLABS — IDWriteTextLayout::HitTestTextRange() can return multiple ranges for a single line", hr); + NULL, 0, &nFragments); + if (hr != S_OK && hr != E_NOT_SUFFICIENT_BUFFER) + logHRESULT(L"error getting IDWriteTextLayout line fragment count", hr); + htm = new DWRITE_HIT_TEST_METRICS[nFragments]; + // TODO verify unused == nFragments? + hr = tl->layout->HitTestTextRange(tl->lineInfo[i].startPos, (tl->lineInfo[i].endPos - tl->lineInfo[i].newlineCount) - tl->lineInfo[i].startPos, + 0, 0, + htm, nFragments, &unused); + // TODO can this return E_NOT_SUFFICIENT_BUFFER again? if (hr != S_OK) - logHRESULT(L"error getting IDWriteTextLayout line rect", hr); - // TODO verify htm.textPosition and htm.length? - tl->lineInfo[i].x = htm.left; - tl->lineInfo[i].y = htm.top; - tl->lineInfo[i].width = htm.width; - tl->lineInfo[i].height = htm.height; - // TODO verify dlm[i].height == htm.height + logHRESULT(L"error getting IDWriteTextLayout line fragment metrics", hr); + // TODO verify htm.textPosition and htm.length against dtm[i]/tl->lineInfo[i]? + tl->lineInfo[i].x = htm[0].left; + tl->lineInfo[i].y = htm[0].top; + tl->lineInfo[i].width = htm[0].width; + tl->lineInfo[i].height = htm[0].height; + for (j = 1; j < nFragments; j++) { + // this is correct even if the leftmost fragment on the line is RTL + if (tl->lineInfo[i].x > htm[j].left) + tl->lineInfo[i].x = htm[j].left; + tl->lineInfo[i].width += htm[j].width; + // TODO verify y and height haven't changed? + } + // TODO verify dlm[i].height == htm.height? + delete[] htm; // TODO on Windows 8.1 and/or 10 we can use DWRITE_LINE_METRICS1 to get specific info about the ascent and descent; do we have an alternative? // TODO and even on those platforms can we somehow split tyographic leading from spacing? From d8316790a00ed1e7838a1f3e10c886c70f084bf1 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 21 Jan 2017 10:21:39 -0500 Subject: [PATCH 0566/1329] More notes. --- darwin/drawtext.m | 2 +- unix/drawtext.c | 3 +++ windows/drawtext.cpp | 4 ++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 2ac104c6..a84b68b5 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -82,7 +82,7 @@ static CFAttributedStringRef attrstrToCoreFoundation(uiAttributedString *s, uiDr return mas; } -// TODO this is wrong for our grapheme test; figure out what it should be... +// TODO this is wrong for our hit-test example's multiple combining character example static uiDrawTextLayoutLineMetrics *computeLineMetrics(CTFrameRef frame, CGSize size) { uiDrawTextLayoutLineMetrics *metrics; diff --git a/unix/drawtext.c b/unix/drawtext.c index f8ec1e67..6c01bc7d 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -2,6 +2,9 @@ #include "uipriv_unix.h" #include "draw.h" +// TODO +// - if the RTL override is at the beginning of a line, the preceding space is included? + struct uiDrawTextLayout { PangoLayout *layout; uiDrawTextLayoutLineMetrics *lineMetrics; diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 0caac7f3..345723ce 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -2,6 +2,10 @@ #include "uipriv_windows.hpp" #include "draw.hpp" +// TODO +// - consider the warnings about antialiasing in the PadWrite sample +// - if that's not a problem, do we have overlapping rects in the hittest sample? I can't tell... + struct uiDrawTextLayout { IDWriteTextFormat *format; IDWriteTextLayout *layout; From 313905230da987e00ba56f2f9c283fd0e8994392 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 21 Jan 2017 15:22:19 -0500 Subject: [PATCH 0567/1329] Ugh, Core Text and NSLayoutManager produce inconsistent output :| --- stats.osxdrawtext | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 stats.osxdrawtext diff --git a/stats.osxdrawtext b/stats.osxdrawtext new file mode 100644 index 00000000..15c9c6c5 --- /dev/null +++ b/stats.osxdrawtext @@ -0,0 +1,43 @@ +diff --git a/darwin/drawtext.m b/darwin/drawtext.m +index a84b68b..c95bbde 100644 +--- a/darwin/drawtext.m ++++ b/darwin/drawtext.m +@@ -108,7 +108,7 @@ static CFAttributedStringRef attrstrToCoreFoundation(uiAttributedString *s, uiDr + boundsNoLeading = CTLineGetBoundsWithOptions(line, kCTLineBoundsExcludeTypographicLeading); + + // this is equivalent to boundsNoLeading.size.height + boundsNoLeading.origin.y (manually verified) +- ascent = bounds.size.height + bounds.origin.y; ++if(i!=5) ascent = bounds.size.height + bounds.origin.y; + descent = -boundsNoLeading.origin.y; + // TODO does this preserve leading sign? + leading = -bounds.origin.y - descent; +@@ -119,11 +119,20 @@ static CFAttributedStringRef attrstrToCoreFoundation(uiAttributedString *s, uiDr + if (leading > 0) + leading = floor(leading + 0.5); + ++NSLog(@"line %d", (int)i); ++NSLog(@"ypos %g", ypos); ++if (i>0) { ++NSLog(@"expected Y: %g", metrics[i - 1].Y - metrics[i - 1].Height); ++} ++ + metrics[i].X = origins[i].x; + metrics[i].Y = origins[i].y - descent - leading; + metrics[i].Width = bounds.size.width; + metrics[i].Height = ascent + descent + leading; + ++NSLog(@"o %g a %g d %g l %g", origins[i].y, ascent, descent, leading); ++NSLog(@"actual Y: %g height: %g", metrics[i].Y, metrics[i].Height); ++ + metrics[i].BaselineY = origins[i].y; + metrics[i].Ascent = ascent; + metrics[i].Descent = descent; +@@ -148,7 +157,7 @@ static CFAttributedStringRef attrstrToCoreFoundation(uiAttributedString *s, uiDr + metrics[i].BaselineY = size.height - metrics[i].BaselineY; + // TODO also adjust by metrics[i].Height? + } +- ++NSLog(@"==="); + uiFree(origins); + return metrics; + } From 7eba767ffb26e91911078813b924671531b5e79b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 21 Jan 2017 18:27:03 -0500 Subject: [PATCH 0568/1329] Temporarily turning off excess text for magnification tests. --- examples/drawtext/hittest.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index dc1f9dce..a2454549 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -3,8 +3,8 @@ static const char *text = "Each of the glyphs an end user interacts with are called graphemes. " - "If you enter a byte range in the text boxes below and click the button, you can see the blue box move to surround that byte range, as well as what the actual byte range necessary is. " - "You'll also see the index of the first grapheme; uiAttributedString has facilities for converting between UTF-8 code points and grapheme indices. " +//TODO "If you enter a byte range in the text boxes below and click the button, you can see the blue box move to surround that byte range, as well as what the actual byte range necessary is. " +//TODO "You'll also see the index of the first grapheme; uiAttributedString has facilities for converting between UTF-8 code points and grapheme indices. " "Additionally, click on the string to move the caret. Watch the status text at the bottom change too. " "That being said: " "\xC3\x93O\xCC\x81 (combining accents) " From b2ce7c879498b0f057ed12f4e69dc2a56114b3e4 Mon Sep 17 00:00:00 2001 From: l0calh05t Date: Sun, 22 Jan 2017 13:46:34 +0100 Subject: [PATCH 0569/1329] Fix release build on MSVC. The /RTC* flags conflict with all optimizations, therefore they should only be added when building the Debug configuration. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index eb1696d9..3d7d8c8e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,7 +77,7 @@ if(MSVC) set(_COMMON_CFLAGS /W4 /wd4100 /bigobj /nologo - /RTC1 /RTCs /RTCu + $<$:/RTC1 /RTCs /RTCu> /EHsc ) From 4e2dc90f4f202dbbfdccdfb428a70f63f463b518 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 23 Jan 2017 01:28:53 -0500 Subject: [PATCH 0570/1329] Started an experimental port from Core Text to Cocoa's typesetting system, since that seems to produce more sensible results (and is somewhat easier to use...). We lose per-line spacing though :/ --- darwin/_coretext_drawtext.m | 268 +++++++++++++++++++++++++++++++++++ darwin/_coretext_fontmatch.m | 255 +++++++++++++++++++++++++++++++++ darwin/fontmatch.m | 131 +++++++---------- darwin/uipriv_darwin.h | 3 +- 4 files changed, 576 insertions(+), 81 deletions(-) create mode 100644 darwin/_coretext_drawtext.m create mode 100644 darwin/_coretext_fontmatch.m diff --git a/darwin/_coretext_drawtext.m b/darwin/_coretext_drawtext.m new file mode 100644 index 00000000..72a8d2e6 --- /dev/null +++ b/darwin/_coretext_drawtext.m @@ -0,0 +1,268 @@ +// 6 september 2015 +#import "uipriv_darwin.h" + +// TODO double-check that we are properly handling allocation failures (or just toll free bridge from cocoa) +struct uiDrawFontFamilies { + CFArrayRef fonts; +}; + +uiDrawFontFamilies *uiDrawListFontFamilies(void) +{ + uiDrawFontFamilies *ff; + + ff = uiNew(uiDrawFontFamilies); + ff->fonts = CTFontManagerCopyAvailableFontFamilyNames(); + if (ff->fonts == NULL) + implbug("error getting available font names (no reason specified) (TODO)"); + return ff; +} + +int uiDrawFontFamiliesNumFamilies(uiDrawFontFamilies *ff) +{ + return CFArrayGetCount(ff->fonts); +} + +char *uiDrawFontFamiliesFamily(uiDrawFontFamilies *ff, int n) +{ + CFStringRef familystr; + char *family; + + familystr = (CFStringRef) CFArrayGetValueAtIndex(ff->fonts, n); + // toll-free bridge + family = uiDarwinNSStringToText((NSString *) familystr); + // Get Rule means we do not free familystr + return family; +} + +void uiDrawFreeFontFamilies(uiDrawFontFamilies *ff) +{ + CFRelease(ff->fonts); + uiFree(ff); +} + +struct uiDrawTextFont { + CTFontRef f; +}; + +uiDrawTextFont *mkTextFont(CTFontRef f, BOOL retain) +{ + uiDrawTextFont *font; + + font = uiNew(uiDrawTextFont); + font->f = f; + if (retain) + CFRetain(font->f); + return font; +} + +uiDrawTextFont *mkTextFontFromNSFont(NSFont *f) +{ + // toll-free bridging; we do retain, though + return mkTextFont((CTFontRef) f, YES); +} + +static CFMutableDictionaryRef newAttrList(void) +{ + CFMutableDictionaryRef attr; + + attr = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + if (attr == NULL) + complain("error creating attribute dictionary in newAttrList()()"); + return attr; +} + +static void addFontFamilyAttr(CFMutableDictionaryRef attr, const char *family) +{ + CFStringRef cfstr; + + cfstr = CFStringCreateWithCString(NULL, family, kCFStringEncodingUTF8); + if (cfstr == NULL) + complain("error creating font family name CFStringRef in addFontFamilyAttr()"); + CFDictionaryAddValue(attr, kCTFontFamilyNameAttribute, cfstr); + CFRelease(cfstr); // dictionary holds its own reference +} + +static void addFontSizeAttr(CFMutableDictionaryRef attr, double size) +{ + CFNumberRef n; + + n = CFNumberCreate(NULL, kCFNumberDoubleType, &size); + CFDictionaryAddValue(attr, kCTFontSizeAttribute, n); + CFRelease(n); +} + +#if 0 +TODO +// See http://stackoverflow.com/questions/4810409/does-coretext-support-small-caps/4811371#4811371 and https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c for what these do +// And fortunately, unlike the traits (see below), unmatched features are simply ignored without affecting the other features :D +static void addFontSmallCapsAttr(CFMutableDictionaryRef attr) +{ + CFMutableArrayRef outerArray; + CFMutableDictionaryRef innerDict; + CFNumberRef numType, numSelector; + int num; + + outerArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + if (outerArray == NULL) + complain("error creating outer CFArray for adding small caps attributes in addFontSmallCapsAttr()"); + + // Apple's headers say these are deprecated, but a few fonts still rely on them + num = kLetterCaseType; + numType = CFNumberCreate(NULL, kCFNumberIntType, &num); + num = kSmallCapsSelector; + numSelector = CFNumberCreate(NULL, kCFNumberIntType, &num); + innerDict = newAttrList(); + CFDictionaryAddValue(innerDict, kCTFontFeatureTypeIdentifierKey, numType); + CFRelease(numType); + CFDictionaryAddValue(innerDict, kCTFontFeatureSelectorIdentifierKey, numSelector); + CFRelease(numSelector); + CFArrayAppendValue(outerArray, innerDict); + CFRelease(innerDict); // and likewise for CFArray + + // these are the non-deprecated versions of the above; some fonts have these instead + num = kLowerCaseType; + numType = CFNumberCreate(NULL, kCFNumberIntType, &num); + num = kLowerCaseSmallCapsSelector; + numSelector = CFNumberCreate(NULL, kCFNumberIntType, &num); + innerDict = newAttrList(); + CFDictionaryAddValue(innerDict, kCTFontFeatureTypeIdentifierKey, numType); + CFRelease(numType); + CFDictionaryAddValue(innerDict, kCTFontFeatureSelectorIdentifierKey, numSelector); + CFRelease(numSelector); + CFArrayAppendValue(outerArray, innerDict); + CFRelease(innerDict); // and likewise for CFArray + + CFDictionaryAddValue(attr, kCTFontFeatureSettingsAttribute, outerArray); + CFRelease(outerArray); +} +#endif + +#if 0 +// Named constants for these were NOT added until 10.11, and even then they were added as external symbols instead of macros, so we can't use them directly :( +// kode54 got these for me before I had access to El Capitan; thanks to him. +#define ourNSFontWeightUltraLight -0.800000 +#define ourNSFontWeightThin -0.600000 +#define ourNSFontWeightLight -0.400000 +#define ourNSFontWeightRegular 0.000000 +#define ourNSFontWeightMedium 0.230000 +#define ourNSFontWeightSemibold 0.300000 +#define ourNSFontWeightBold 0.400000 +#define ourNSFontWeightHeavy 0.560000 +#define ourNSFontWeightBlack 0.620000 +#endif + +// Now remember what I said earlier about having to add the small caps traits after calling the above? This gets a dictionary back so we can do so. +CFMutableDictionaryRef extractAttributes(CTFontDescriptorRef desc) +{ + CFDictionaryRef dict; + CFMutableDictionaryRef mdict; + + dict = CTFontDescriptorCopyAttributes(desc); + // this might not be mutable, so make a mutable copy + mdict = CFDictionaryCreateMutableCopy(NULL, 0, dict); + CFRelease(dict); + return mdict; +} + +uiDrawTextFont *uiDrawLoadClosestFont(const uiDrawTextFontDescriptor *desc) +{ + CTFontRef f; + CFMutableDictionaryRef attr; + CTFontDescriptorRef cfdesc; + + attr = newAttrList(); + addFontFamilyAttr(attr, desc->Family); + addFontSizeAttr(attr, desc->Size); + + // now we have to do the traits matching, so create a descriptor, match the traits, and then get the attributes back + cfdesc = CTFontDescriptorCreateWithAttributes(attr); + // TODO release attr? + cfdesc = matchTraits(cfdesc, desc->Weight, desc->Italic, desc->Stretch); + + // specify the initial size again just to be safe + f = CTFontCreateWithFontDescriptor(cfdesc, desc->Size, NULL); + // TODO release cfdesc? + + return mkTextFont(f, NO); // we hold the initial reference; no need to retain again +} + +void uiDrawFreeTextFont(uiDrawTextFont *font) +{ + CFRelease(font->f); + uiFree(font); +} + +uintptr_t uiDrawTextFontHandle(uiDrawTextFont *font) +{ + return (uintptr_t) (font->f); +} + +void uiDrawTextFontDescribe(uiDrawTextFont *font, uiDrawTextFontDescriptor *desc) +{ + // TODO +} + +// text sizes and user space points are identical: +// - https://developer.apple.com/library/mac/documentation/TextFonts/Conceptual/CocoaTextArchitecture/TypoFeatures/TextSystemFeatures.html#//apple_ref/doc/uid/TP40009459-CH6-51627-BBCCHIFF text points are 72 per inch +// - https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CocoaDrawingGuide/Transforms/Transforms.html#//apple_ref/doc/uid/TP40003290-CH204-SW5 user space points are 72 per inch +void uiDrawTextFontGetMetrics(uiDrawTextFont *font, uiDrawTextFontMetrics *metrics) +{ + metrics->Ascent = CTFontGetAscent(font->f); + metrics->Descent = CTFontGetDescent(font->f); + metrics->Leading = CTFontGetLeading(font->f); + metrics->UnderlinePos = CTFontGetUnderlinePosition(font->f); + metrics->UnderlineThickness = CTFontGetUnderlineThickness(font->f); +} + +// LONGTERM allow line separation and leading to be factored into a wrapping text layout + +// TODO reconcile differences in character wrapping on platforms +void uiDrawTextLayoutExtents(uiDrawTextLayout *layout, double *width, double *height) +{ + struct framesetter fs; + + mkFramesetter(layout, &fs); + *width = fs.extents.width; + *height = fs.extents.height; + freeFramesetter(&fs); +} + +// LONGTERM provide an equivalent to CTLineGetTypographicBounds() on uiDrawTextLayout? + +// LONGTERM keep this for later features and documentation purposes +#if 0 + + // LONGTERM provide a way to get the image bounds as a separate function later + bounds = CTLineGetImageBounds(line, c); + // though CTLineGetImageBounds() returns CGRectNull on error, it also returns CGRectNull on an empty string, so we can't reasonably check for error + + // CGContextSetTextPosition() positions at the baseline in the case of CTLineDraw(); we need the top-left corner instead + CTLineGetTypographicBounds(line, &yoff, NULL, NULL); + // remember that we're flipped, so we subtract + y -= yoff; + CGContextSetTextPosition(c, x, y); +#endif + +#if 0 +void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, int startChar, int endChar, double r, double g, double b, double a) +{ + CGColorSpaceRef colorspace; + CGFloat components[4]; + CGColorRef color; + + // for consistency with windows, use sRGB + colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); + components[0] = r; + components[1] = g; + components[2] = b; + components[3] = a; + color = CGColorCreate(colorspace, components); + CGColorSpaceRelease(colorspace); + + CFAttributedStringSetAttribute(layout->mas, + rangeToCFRange(), + kCTForegroundColorAttributeName, + color); + CGColorRelease(color); // TODO safe? +} +#endif diff --git a/darwin/_coretext_fontmatch.m b/darwin/_coretext_fontmatch.m new file mode 100644 index 00000000..04761ef4 --- /dev/null +++ b/darwin/_coretext_fontmatch.m @@ -0,0 +1,255 @@ +// 3 january 2017 +#import "uipriv_darwin.h" + +// Stupidity: Core Text requires an **exact match for the entire traits dictionary**, otherwise it will **drop ALL the traits**. +// This seems to be true for every function in Core Text that advertises that it performs font matching; I can confirm at the time of writing this is the case for +// - CTFontDescriptorCreateMatchingFontDescriptors() (though the conditions here seem odd) +// - CTFontCreateWithFontDescriptor() +// - CTFontCreateCopyWithAttributes() +// We have to implement the closest match ourselves. +// This needs to be done early in the matching process; in particular, we have to do this before adding any features (such as small caps), because the matching descriptors won't have those. + +struct closeness { + CFIndex index; + double weight; + double italic; + double stretch; + double distance; +}; + +static double doubleAttr(CFDictionaryRef traits, CFStringRef attr) +{ + CFNumberRef cfnum; + double val; + + cfnum = (CFNumberRef) CFDictionaryGetValue(traits, attr); + if (cfnum == NULL) { + // TODO + } + if (CFNumberGetValue(cfnum, kCFNumberDoubleType, &val) == false) { + // TODO + } + // Get Rule; do not release cfnum + return val; +} + +struct italicCloseness { + double normal; + double oblique; + double italic; +}; + +// remember that in closeness, 0 means exact +// in this case, since we define the range, we use 0.5 to mean "close enough" (oblique for italic and italic for oblique) and 1 to mean "not a match" +static const struct italicCloseness italicClosenesses[] = { + [uiDrawTextItalicNormal] = { 0, 1, 1 }, + [uiDrawTextItalicOblique] = { 1, 0, 0.5 }, + [uiDrawTextItalicItalic] = { 1, 0.5, 0 }, +}; + +// Italics are hard because Core Text does NOT distinguish between italic and oblique. +// All Core Text provides is a slant value and the italic bit of the symbolic traits mask. +// However, Core Text does seem to guarantee (from experimentation; see below) that the slant will be nonzero if and only if the italic bit is set, so we don't need to use the slant value. +// Core Text also seems to guarantee that if a font lists itself as Italic or Oblique by name (font subfamily name, font style name, whatever), it will also have that bit set, so testing this bit does cover all fonts that name themselves as Italic and Oblique. (Again, this is from the below experimentation.) +// TODO there is still one catch that might matter from a user's POV: the reverse is not true — the italic bit can be set even if the style of the font face/subfamily/style isn't named as Italic (for example, script typefaces like Adobe's Palace Script MT Std); I don't know what to do about this... I know how to start: find a script font that has an italic form (Adobe's Palace Script MT Std does not; only Regular and Semibold) +static double italicCloseness(CTFontDescriptorRef desc, CFDictionaryRef traits, uiDrawTextItalic italic) +{ + const struct italicCloseness *ic = &(italicClosenesses[italic]); + CFNumberRef cfnum; + CTFontSymbolicTraits symbolic; + // there is no kCFNumberUInt32Type, but CTFontSymbolicTraits is uint32_t, so SInt32 should work + SInt32 s; + CFStringRef styleName; + BOOL isOblique; + + cfnum = CFDictionaryGetValue(traits, kCTFontSymbolicTrait); + if (cfnum == NULL) { + // TODO + } + if (CFNumberGetValue(cfnum, kCFNumberSInt32Type, &s) == false) { + // TODO + } + symbolic = (CTFontSymbolicTraits) s; + // Get Rule; do not release cfnum + if ((symbolic & kCTFontItalicTrait) == 0) + return ic->normal; + + // Okay, now we know it's either Italic or Oblique + // Pango's Core Text code just does a g_strrstr() (backwards case-sensitive search) for "Oblique" in the font's style name (see https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c); let's do that too I guess + isOblique = NO; // default value + styleName = (CFStringRef) CTFontDescriptorCopyAttribute(desc, kCTFontStyleNameAttribute); + // TODO is styleName guaranteed? + if (styleName != NULL) { + CFRange range; + + // note the use of the toll-free bridge for the string literal, since CFSTR() *can* return NULL + // TODO is this really the case? or is that just a copy-paste error from the other CFStringCreateXxx() functions? and what's this about -fconstant-cfstring? + range = CFStringFind(styleName, (CFStringRef) @"Oblique", kCFCompareBackwards); + if (range.location != kCFNotFound) + isOblique = YES; + CFRelease(styleName); + } + if (isOblique) + return ic->oblique; + return ic->italic; +} + +static CTFontDescriptorRef matchTraits(CTFontDescriptorRef against, double targetWeight, uiDrawTextItalic targetItalic, double targetStretch) +{ + CFArrayRef matching; + CFIndex i, n; + struct closeness *closeness; + CTFontDescriptorRef current; + CTFontDescriptorRef out; + + matching = CTFontDescriptorCreateMatchingFontDescriptors(against, NULL); + if (matching == NULL) + // no matches; give the original back and hope for the best + return against; + n = CFArrayGetCount(matching); + if (n == 0) { + // likewise + CFRelease(matching); + return against; + } + + closeness = (struct closeness *) uiAlloc(n * sizeof (struct closeness), "struct closeness[]"); + for (i = 0; i < n; i++) { + CFDictionaryRef traits; + + closeness[i].index = i; + current = (CTFontDescriptorRef) CFArrayGetValueAtIndex(matching, i); + traits = (CFDictionaryRef) CTFontDescriptorCopyAttribute(current, kCTFontTraitsAttribute); + if (traits == NULL) { + // couldn't get traits; be safe by ranking it lowest + // LONGTERM figure out what the longest possible distances are + closeness[i].weight = 3; + closeness[i].italic = 2; + closeness[i].stretch = 3; + continue; + } + closeness[i].weight = doubleAttr(traits, kCTFontWeightTrait) - targetWeight; + closeness[i].italic = italicCloseness(current, traits, targetItalic); + closeness[i].stretch = doubleAttr(traits, kCTFontWidthTrait) - targetStretch; + CFRelease(traits); + } + + // now figure out the 3-space difference between the three and sort by that + // TODO merge this loop with the previous loop? + for (i = 0; i < n; i++) { + double weight, italic, stretch; + + weight = closeness[i].weight; + weight *= weight; + italic = closeness[i].italic; + italic *= italic; + stretch = closeness[i].stretch; + stretch *= stretch; + closeness[i].distance = sqrt(weight + italic + stretch); + } + qsort_b(closeness, n, sizeof (struct closeness), ^(const void *aa, const void *bb) { + const struct closeness *a = (const struct closeness *) aa; + const struct closeness *b = (const struct closeness *) bb; + + // via http://www.gnu.org/software/libc/manual/html_node/Comparison-Functions.html#Comparison-Functions + // LONGTERM is this really the best way? isn't it the same as if (*a < *b) return -1; if (*a > *b) return 1; return 0; ? + return (a->distance > b->distance) - (a->distance < b->distance); + }); + // and the first element of the sorted array is what we want + out = CFArrayGetValueAtIndex(matching, closeness[0].index); + CFRetain(out); // get rule + + // release everything + uiFree(closeness); + CFRelease(matching); + // and release the original descriptor since we no longer need it + CFRelease(against); + + return out; +} + +// since uiDrawTextWeight effectively corresponds to OS/2 weights (which roughly correspond to GDI, Pango, and DirectWrite weights, and to a lesser(? TODO) degree, CSS weights), let's just do what Core Text does with OS/2 weights +// TODO this will not be correct for system fonts, which use cached values that have no relation to the OS/2 weights; we need to figure out how to reconcile these +// for more information, see https://bugzilla.gnome.org/show_bug.cgi?id=766148 and TODO_put_blog_post_here_once_I_write_it (TODO keep this line when resolving the above TODO) +static const double weightsToCTWeights[] = { + -1.0, // 0..99 + -0.7, // 100..199 + -0.5, // 200..299 + -0.23, // 300..399 + 0.0, // 400..499 + 0.2, // 500..599 + 0.3, // 600..699 + 0.4, // 700..799 + 0.6, // 800..899 + 0.8, // 900..999 + 1.0, // 1000 +}; + +static double weightToCTWeight(uiDrawTextWeight weight) +{ + int weightClass; + double ctclass; + double rest, weightFloor, nextFloor; + + if (weight <= 0) + return -1.0; + if (weight >= 1000) + return 1.0; + + weightClass = weight / 100; + rest = (double) weight; + weightFloor = (double) (weightClass * 100); + nextFloor = (double) ((weightClass + 1) * 100); + rest = (rest - weightFloor) / (nextFloor - weightFloor); + + ctclass = weightsToCTWeights[weightClass]; + return fma(rest, + weightsToCTWeights[weightClass + 1] - ctclass, + ctclass); +} + +// based on what Core Text says about actual fonts (system fonts, system fonts in another folder to avoid using cached values, Adobe Font Folio 11, Google Fonts archive, fonts in Windows 7/8.1/10) +static const double stretchesToCTWidths[] = { + [uiDrawTextStretchUltraCondensed] = -0.400000, + [uiDrawTextStretchExtraCondensed] = -0.300000, + [uiDrawTextStretchCondensed] = -0.200000, + [uiDrawTextStretchSemiCondensed] = -0.100000, + [uiDrawTextStretchNormal] = 0.000000, + [uiDrawTextStretchSemiExpanded] = 0.100000, + [uiDrawTextStretchExpanded] = 0.200000, + [uiDrawTextStretchExtraExpanded] = 0.300000, + // this one isn't present in any of the fonts I tested, but it follows naturally from the pattern of the rest, so... (TODO verify by checking the font files directly) + [uiDrawTextStretchUltraExpanded] = 0.400000, +}; + +CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd) +{ + CFMutableDictionaryRef attrs; + CFStringRef cffamily; + CFNumberRef cfsize; + CTFontDescriptorRef basedesc; + + attrs = CFDictionaryCreateMutable(NULL, 2, + // TODO are these correct? + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (attrs == NULL) { + // TODO + } + cffamily = CFStringCreateWithCString(NULL, fd->Family, kCFStringEncodingUTF8); + if (cffamily == NULL) { + // TODO + } + CFDictionaryAddValue(attrs, kCTFontFamilyNameAttribute, cffamily); + CFRelease(cffamily); + cfsize = CFNumberCreate(NULL, kCFNumberDoubleType, &(fd->Size)); + CFDictionaryAddValue(attrs, kCTFontSizeAttribute, cfsize); + CFRelease(cfsize); + + basedesc = CTFontDescriptorCreateWithAttributes(attrs); + CFRelease(attrs); // TODO correct? + return matchTraits(basedesc, + weightToCTWeight(fd->Weight), + fd->Italic, + stretchesToCTWidths[fd->Stretch]); +} diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index 04761ef4..27440841 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -6,31 +6,25 @@ // - CTFontDescriptorCreateMatchingFontDescriptors() (though the conditions here seem odd) // - CTFontCreateWithFontDescriptor() // - CTFontCreateCopyWithAttributes() +// And as a bonus prize, this also applies to Cocoa's NSFontDescriptor methods as well! // We have to implement the closest match ourselves. // This needs to be done early in the matching process; in particular, we have to do this before adding any features (such as small caps), because the matching descriptors won't have those. struct closeness { - CFIndex index; + NSUInteger index; double weight; double italic; double stretch; double distance; }; -static double doubleAttr(CFDictionaryRef traits, CFStringRef attr) +static double doubleAttr(NSDictionary *traits, NSString *attr) { - CFNumberRef cfnum; + NSNumber *n; double val; - cfnum = (CFNumberRef) CFDictionaryGetValue(traits, attr); - if (cfnum == NULL) { - // TODO - } - if (CFNumberGetValue(cfnum, kCFNumberDoubleType, &val) == false) { - // TODO - } - // Get Rule; do not release cfnum - return val; + n = (NSNumber *) [traits objectForKey:attr]; + return [n doubleValue]; } struct italicCloseness { @@ -52,75 +46,62 @@ static const struct italicCloseness italicClosenesses[] = { // However, Core Text does seem to guarantee (from experimentation; see below) that the slant will be nonzero if and only if the italic bit is set, so we don't need to use the slant value. // Core Text also seems to guarantee that if a font lists itself as Italic or Oblique by name (font subfamily name, font style name, whatever), it will also have that bit set, so testing this bit does cover all fonts that name themselves as Italic and Oblique. (Again, this is from the below experimentation.) // TODO there is still one catch that might matter from a user's POV: the reverse is not true — the italic bit can be set even if the style of the font face/subfamily/style isn't named as Italic (for example, script typefaces like Adobe's Palace Script MT Std); I don't know what to do about this... I know how to start: find a script font that has an italic form (Adobe's Palace Script MT Std does not; only Regular and Semibold) -static double italicCloseness(CTFontDescriptorRef desc, CFDictionaryRef traits, uiDrawTextItalic italic) +// TODO see if the above applies to Cocoa as well +static double italicCloseness(NSFontDescriptor *desc, NSDictionary *traits, uiDrawTextItalic italic) { const struct italicCloseness *ic = &(italicClosenesses[italic]); - CFNumberRef cfnum; - CTFontSymbolicTraits symbolic; - // there is no kCFNumberUInt32Type, but CTFontSymbolicTraits is uint32_t, so SInt32 should work - SInt32 s; - CFStringRef styleName; + NSNumber *num; + NSFontSymbolicTraits symbolic; + NSString *styleName; + NSRange range; BOOL isOblique; - cfnum = CFDictionaryGetValue(traits, kCTFontSymbolicTrait); - if (cfnum == NULL) { - // TODO - } - if (CFNumberGetValue(cfnum, kCFNumberSInt32Type, &s) == false) { - // TODO - } - symbolic = (CTFontSymbolicTraits) s; - // Get Rule; do not release cfnum - if ((symbolic & kCTFontItalicTrait) == 0) + num = (NSNumber *) [traits objectForKey:NSFontSymbolicTrait]; + // TODO this should really be a uint32_t-specific one + symbolic = (NSFontSymbolicTraits) [num unsignedIntegerValue]; + if ((symbolic & NSFontItalicTrait) == 0) return ic->normal; // Okay, now we know it's either Italic or Oblique // Pango's Core Text code just does a g_strrstr() (backwards case-sensitive search) for "Oblique" in the font's style name (see https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c); let's do that too I guess + // note that NSFontFaceAttribute is the Cocoa equivalent of the style name isOblique = NO; // default value - styleName = (CFStringRef) CTFontDescriptorCopyAttribute(desc, kCTFontStyleNameAttribute); + styleName = (NSString *) [desc objectForKey:NSFontFaceAttribute]; // TODO is styleName guaranteed? - if (styleName != NULL) { - CFRange range; - - // note the use of the toll-free bridge for the string literal, since CFSTR() *can* return NULL - // TODO is this really the case? or is that just a copy-paste error from the other CFStringCreateXxx() functions? and what's this about -fconstant-cfstring? - range = CFStringFind(styleName, (CFStringRef) @"Oblique", kCFCompareBackwards); - if (range.location != kCFNotFound) - isOblique = YES; - CFRelease(styleName); - } - if (isOblique) + // TODO NSLiteralSearch? + range = [styleName rangeOfString:@"Oblique" options:NSCaseInsensitiveSearch]; + if (range.location != NSNotFound) return ic->oblique; return ic->italic; } -static CTFontDescriptorRef matchTraits(CTFontDescriptorRef against, double targetWeight, uiDrawTextItalic targetItalic, double targetStretch) +static NSFontDescriptor *matchTraits(NSFontDescriptor *against, double targetWeight, uiDrawTextItalic targetItalic, double targetStretch) { - CFArrayRef matching; - CFIndex i, n; + NSArray *matching; + NSUInteger i, n; struct closeness *closeness; - CTFontDescriptorRef current; - CTFontDescriptorRef out; + NSFontDescriptor *current; + NSFontDescriptor *out; - matching = CTFontDescriptorCreateMatchingFontDescriptors(against, NULL); - if (matching == NULL) + matching = [against matchingFontDescriptorsWithMandatoryKeys:nil]; + if (matching == nil) // no matches; give the original back and hope for the best return against; - n = CFArrayGetCount(matching); + n = [matching count]; if (n == 0) { // likewise - CFRelease(matching); + [matching release]; return against; } closeness = (struct closeness *) uiAlloc(n * sizeof (struct closeness), "struct closeness[]"); for (i = 0; i < n; i++) { - CFDictionaryRef traits; + NSDictionary *traits; closeness[i].index = i; - current = (CTFontDescriptorRef) CFArrayGetValueAtIndex(matching, i); - traits = (CFDictionaryRef) CTFontDescriptorCopyAttribute(current, kCTFontTraitsAttribute); - if (traits == NULL) { + current = (NSFontDescriptor *) [matching objectAtIndex:i]; + traits = (NSDictionary *) [current objectAtIndex:NSFontTraitsAttribute]; + if (traits == nil) { // couldn't get traits; be safe by ranking it lowest // LONGTERM figure out what the longest possible distances are closeness[i].weight = 3; @@ -128,10 +109,10 @@ static CTFontDescriptorRef matchTraits(CTFontDescriptorRef against, double targe closeness[i].stretch = 3; continue; } - closeness[i].weight = doubleAttr(traits, kCTFontWeightTrait) - targetWeight; + closeness[i].weight = doubleAttr(traits, NSFontWeightTrait) - targetWeight; closeness[i].italic = italicCloseness(current, traits, targetItalic); - closeness[i].stretch = doubleAttr(traits, kCTFontWidthTrait) - targetStretch; - CFRelease(traits); + closeness[i].stretch = doubleAttr(traits, NSFontWidthTrait) - targetStretch; + // TODO release traits? } // now figure out the 3-space difference between the three and sort by that @@ -156,14 +137,15 @@ static CTFontDescriptorRef matchTraits(CTFontDescriptorRef against, double targe return (a->distance > b->distance) - (a->distance < b->distance); }); // and the first element of the sorted array is what we want - out = CFArrayGetValueAtIndex(matching, closeness[0].index); - CFRetain(out); // get rule + out = (NSFontDescriptor *) [matching objectAtIndex:closeness[0].index]; + // TODO is this correct? + [out retain]; // get rule // release everything uiFree(closeness); - CFRelease(matching); + [matching release]; // and release the original descriptor since we no longer need it - CFRelease(against); + [against release]; return out; } @@ -222,32 +204,21 @@ static const double stretchesToCTWidths[] = { [uiDrawTextStretchUltraExpanded] = 0.400000, }; -CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd) +NSFontDescriptor *fontdescToNSFontDescriptor(uiDrawFontDescriptor *fd) { - CFMutableDictionaryRef attrs; + NSMutableDictionary *attrs; CFStringRef cffamily; CFNumberRef cfsize; CTFontDescriptorRef basedesc; - attrs = CFDictionaryCreateMutable(NULL, 2, - // TODO are these correct? - &kCFCopyStringDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - if (attrs == NULL) { - // TODO - } - cffamily = CFStringCreateWithCString(NULL, fd->Family, kCFStringEncodingUTF8); - if (cffamily == NULL) { - // TODO - } - CFDictionaryAddValue(attrs, kCTFontFamilyNameAttribute, cffamily); - CFRelease(cffamily); - cfsize = CFNumberCreate(NULL, kCFNumberDoubleType, &(fd->Size)); - CFDictionaryAddValue(attrs, kCTFontSizeAttribute, cfsize); - CFRelease(cfsize); + attrs = [NSMutableDictionary new]; + [attrs setObject:[NSString stringWithUTF8String:fd->Family] + forKey:NSFontFamilyAttribute]; + [attrs setObject:[NSNumber numberWithDouble:fd->Size] + forKye:NSFontSizeAttribute]; - basedesc = CTFontDescriptorCreateWithAttributes(attrs); - CFRelease(attrs); // TODO correct? + basedesc = [[NSFontDescriptor alloc] initWithFontAttributes:attrs]; + [attrs release]; return matchTraits(basedesc, weightToCTWeight(fd->Weight), fd->Italic, diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index c1fde75a..bfbf0688 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -147,4 +147,5 @@ extern void doManualMove(NSWindow *w, NSEvent *initialEvent); extern void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdge edge); // fontmatch.m -extern CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd); +//TODO extern CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd); +extern NSFontDescriptor *fontdescToNSFontDescriptor(uiDrawFontDescriptor *fd); From b19f4cf251c6424e95e141ccc87f98b2140bca01 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 23 Jan 2017 11:43:03 -0500 Subject: [PATCH 0571/1329] Finished writing a NSLayoutManager-based text system. Not quite perfect yet, but we're getting somewhere! --- darwin/drawtext.m | 315 ++++++++++++++++++--------------------------- darwin/fontmatch.m | 13 +- 2 files changed, 133 insertions(+), 195 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index a84b68b5..6a16af1e 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -2,16 +2,26 @@ #import "uipriv_darwin.h" #import "draw.h" -// TODO what happens if nLines == 0 in any function? +@interface lineInfo : NSObject +@property NSRange glyphRange; +@property NSRange characterRange; +@property NSRect lineRect; +@property CGFloat baselineOffset; +@end + +@implementation lineInfo +@end struct uiDrawTextLayout { - CFAttributedStringRef attrstr; + // NSTextStorage is subclassed from NSMutableAttributedString + NSTextStorage *attrstr; + NSTextContainer *container; + NSLayoutManager *layoutManager; // the width as passed into uiDrawTextLayout constructors double width; - CTFramesetterRef framesetter; - +#if 0 /* TODO */ // the *actual* size of the frame // note: technically, metrics returned from frame are relative to CGPathGetPathBoundingBox(tl->path) // however, from what I can gather, for a path created by CGPathCreateWithRect(), like we do (with a NULL transform), CGPathGetPathBoundingBox() seems to just return the standardized form of the rect used to create the path @@ -19,188 +29,101 @@ struct uiDrawTextLayout { // so we can just use tl->size for adjustments // we don't need to adjust coordinates by any origin since our rect origin is (0, 0) CGSize size; +#endif - CGPathRef path; - CTFrameRef frame; - - CFArrayRef lines; - CFIndex nLines; - // we compute this once when first creating the layout - uiDrawTextLayoutLineMetrics *lineMetrics; + NSMutableArray *lineInfo; // for converting CFAttributedString indices to byte offsets size_t *u16tou8; size_t nu16tou8; // TODO I don't like the casing of this name }; -static CTFontRef fontdescToCTFont(uiDrawFontDescriptor *fd) +static NSFont *fontdescToNSFont(uiDrawFontDescriptor *fd) { - CTFontDescriptorRef desc; - CTFontRef font; + NSFontDescriptor *desc; + NSFont *font; - desc = fontdescToCTFontDescriptor(fd); - font = CTFontCreateWithFontDescriptor(desc, fd->Size, NULL); - CFRelease(desc); // TODO correct? + desc = fontdescToNSFontDescriptor(fd); + font = [NSFont fontWithDescriptor:desc size:fd->Size]; + [desc release]; return font; } -static CFAttributedStringRef attrstrToCoreFoundation(uiAttributedString *s, uiDrawFontDescriptor *defaultFont) +static NSTextStorage *attrstrToTextStorage(uiAttributedString *s, uiDrawFontDescriptor *defaultFont) { - CFStringRef cfstr; - CFMutableDictionaryRef defaultAttrs; - CTFontRef defaultCTFont; - CFAttributedStringRef base; - CFMutableAttributedStringRef mas; + NSString *nsstr; + NSMutableDictionary *defaultAttrs; + NSTextStorage *attrstr; - cfstr = CFStringCreateWithCharacters(NULL, attrstrUTF16(s), attrstrUTF16Len(s)); - if (cfstr == NULL) { - // TODO - } - defaultAttrs = CFDictionaryCreateMutable(NULL, 1, - &kCFCopyStringDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - if (defaultAttrs == NULL) { - // TODO - } - defaultCTFont = fontdescToCTFont(defaultFont); - CFDictionaryAddValue(defaultAttrs, kCTFontAttributeName, defaultCTFont); - CFRelease(defaultCTFont); + nsstr = [[NSString alloc] initWithCharacters:attrstrUTF16(s) + length:attrstrUTF16Len(s)]; - base = CFAttributedStringCreate(NULL, cfstr, defaultAttrs); - if (base == NULL) { - // TODO - } - CFRelease(cfstr); - CFRelease(defaultAttrs); - mas = CFAttributedStringCreateMutableCopy(NULL, 0, base); - CFRelease(base); + defaultAttrs = [NSMutableDictionary new]; + [defaultAttrs setObject:fontdescToNSFont(defaultFont) + forKey:NSFontAttributeName]; - CFAttributedStringBeginEditing(mas); + attrstr = [[NSTextStorage alloc] initWithString:nsstr + attributes:defaultAttrs]; + [defaultAttrs release]; + [nsstr release]; + + [attrstr beginEditing]; // TODO copy in the attributes - CFAttributedStringEndEditing(mas); + [attrstr endEditing]; - return mas; -} - -// TODO this is wrong for our hit-test example's multiple combining character example -static uiDrawTextLayoutLineMetrics *computeLineMetrics(CTFrameRef frame, CGSize size) -{ - uiDrawTextLayoutLineMetrics *metrics; - CFArrayRef lines; - CTLineRef line; - CFIndex i, n; - CGFloat ypos; - CGRect bounds, boundsNoLeading; - CGFloat ascent, descent, leading; - CGPoint *origins; - - lines = CTFrameGetLines(frame); - n = CFArrayGetCount(lines); - metrics = (uiDrawTextLayoutLineMetrics *) uiAlloc(n * sizeof (uiDrawTextLayoutLineMetrics), "uiDrawTextLayoutLineMetrics[] (text layout)"); - - origins = (CGPoint *) uiAlloc(n * sizeof (CGPoint), "CGPoint[] (text layout)"); - CTFrameGetLineOrigins(frame, CFRangeMake(0, n), origins); - - ypos = size.height; - for (i = 0; i < n; i++) { - line = (CTLineRef) CFArrayGetValueAtIndex(lines, i); - bounds = CTLineGetBoundsWithOptions(line, 0); - boundsNoLeading = CTLineGetBoundsWithOptions(line, kCTLineBoundsExcludeTypographicLeading); - - // this is equivalent to boundsNoLeading.size.height + boundsNoLeading.origin.y (manually verified) - ascent = bounds.size.height + bounds.origin.y; - descent = -boundsNoLeading.origin.y; - // TODO does this preserve leading sign? - leading = -bounds.origin.y - descent; - - // Core Text always rounds these up for paragraph style calculations; there is a flag to control it but it's inaccessible (and this behavior is turned off for old versions of iPhoto) - ascent = floor(ascent + 0.5); - descent = floor(descent + 0.5); - if (leading > 0) - leading = floor(leading + 0.5); - - metrics[i].X = origins[i].x; - metrics[i].Y = origins[i].y - descent - leading; - metrics[i].Width = bounds.size.width; - metrics[i].Height = ascent + descent + leading; - - metrics[i].BaselineY = origins[i].y; - metrics[i].Ascent = ascent; - metrics[i].Descent = descent; - metrics[i].Leading = leading; - - // TODO - metrics[i].ParagraphSpacingBefore = 0; - metrics[i].LineHeightSpace = 0; - metrics[i].LineSpacing = 0; - metrics[i].ParagraphSpacing = 0; - - // and finally advance to the next line - ypos += metrics[i].Height; - } - - // okay, but now all these metrics are unflipped - // we need to flip them - for (i = 0; i < n; i++) { - metrics[i].Y = size.height - metrics[i].Y; - // go from bottom-left corner to top-left - metrics[i].Y -= metrics[i].Height; - metrics[i].BaselineY = size.height - metrics[i].BaselineY; - // TODO also adjust by metrics[i].Height? - } - - uiFree(origins); - return metrics; + return attrstr; } +// TODO fine-tune all the properties uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescriptor *defaultFont, double width) { uiDrawTextLayout *tl; CGFloat cgwidth; - CFRange range, unused; - CGRect rect; + // TODO correct type? + NSUInteger index; tl = uiNew(uiDrawTextLayout); - tl->attrstr = attrstrToCoreFoundation(s, defaultFont); - range.location = 0; - range.length = CFAttributedStringGetLength(tl->attrstr); + tl->attrstr = attrstrToTextStorage(s, defaultFont); tl->width = width; - // TODO CTFrameProgression for RTL/LTR - // TODO kCTParagraphStyleSpecifierMaximumLineSpacing, kCTParagraphStyleSpecifierMinimumLineSpacing, kCTParagraphStyleSpecifierLineSpacingAdjustment for line spacing - tl->framesetter = CTFramesetterCreateWithAttributedString(tl->attrstr); - if (tl->framesetter == NULL) { - // TODO - } - + // TODO the documentation on the size property implies this might not be necessary? cgwidth = (CGFloat) width; if (cgwidth < 0) cgwidth = CGFLOAT_MAX; - // TODO these seem to be floor()'d or truncated? - // TODO double check to make sure this TODO was right - tl->size = CTFramesetterSuggestFrameSizeWithConstraints(tl->framesetter, - range, - // TODO kCTFramePathWidthAttributeName? - NULL, - CGSizeMake(cgwidth, CGFLOAT_MAX), - &unused); // not documented as accepting NULL (TODO really?) + // TODO rename to tl->textContainer + tl->container = [[NSTextContainer alloc] initWithSize:NSMakeSize(cgwidth, CGFLOAT_MAX)]; + // TODO pull the reference for this + [tl->container setLineFragmentPadding:0]; - rect.origin = CGPointZero; - rect.size = tl->size; - tl->path = CGPathCreateWithRect(rect, NULL); - tl->frame = CTFramesetterCreateFrame(tl->framesetter, - range, - tl->path, - // TODO kCTFramePathWidthAttributeName? - NULL); - if (tl->frame == NULL) { - // TODO + tl->layoutManager = [[NSLayoutManager alloc] init]; + + [tl->layoutManager addTextContainer:tl->container]; + [tl->attrstr addLayoutManager:tl->layoutManager]; + // and force a re-layout (TODO get source + [tl->layoutManager glyphRangeForTextContainer:tl->container]; + + // TODO equivalent of CTFrameProgression for RTL/LTR? + + // now collect line information; see https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/TextLayout/Tasks/CountLines.html + tl->lineInfo = [NSMutableArray new]; + index = 0; + while (index < [tl->layoutManager numberOfGlyphs]) { + NSRange glyphRange; + NSRect lineRect; + lineInfo *li; + + lineRect = [tl->layoutManager lineFragmentRectForGlyphAtIndex:index effectiveRange:&glyphRange]; + li = [lineInfo new]; + li.glyphRange = glyphRange; + li.characterRange = [tl->layoutManager characterRangeForGlyphRange:li.glyphRange actualGlyphRange:NULL]; + li.lineRect = lineRect; + // and this is from http://www.cocoabuilder.com/archive/cocoa/308568-how-to-get-baseline-info.html and http://www.cocoabuilder.com/archive/cocoa/199283-height-and-location-of-text-within-line-in-nslayoutmanager-ignoring-spacing.html + li.baselineOffset = [[tl->layoutManager typesetter] baselineOffsetInLayoutManager:tl->layoutManager glyphIndex:index]; + [tl->lineInfo addObject:li]; + [li release]; + index = glyphRange.location + glyphRange.length; } - tl->lines = CTFrameGetLines(tl->frame); - tl->nLines = CFArrayGetCount(tl->lines); - tl->lineMetrics = computeLineMetrics(tl->frame, tl->size); - // and finally copy the UTF-16 to UTF-8 index conversion table tl->u16tou8 = attrstrCopyUTF16ToUTF8(s, &(tl->nu16tou8)); @@ -210,74 +133,91 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { uiFree(tl->u16tou8); - uiFree(tl->lineMetrics); - // TODO release tl->lines? - CFRelease(tl->frame); - CFRelease(tl->path); - CFRelease(tl->framesetter); - CFRelease(tl->attrstr); + [tl->lineInfo release]; + [tl->layoutManager release]; + [tl->container release]; + [tl->attrstr release]; uiFree(tl); } // TODO document that (x,y) is the top-left corner of the *entire frame* void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) { - CGContextSaveGState(c->c); + NSGraphicsContext *gc; - // Core Text doesn't draw onto a flipped view correctly; we have to pretend it was unflipped - // see the iOS bits of the first example at https://developer.apple.com/library/mac/documentation/StringsTextFonts/Conceptual/CoreText_Programming/LayoutOperations/LayoutOperations.html#//apple_ref/doc/uid/TP40005533-CH12-SW1 (iOS is naturally flipped) - // TODO how is this affected by a non-identity CTM? - CGContextTranslateCTM(c->c, 0, c->height); - CGContextScaleCTM(c->c, 1.0, -1.0); - CGContextSetTextMatrix(c->c, CGAffineTransformIdentity); + CGContextFlush(c->c); // just to be safe + [NSGraphicsContext saveGraphicsState]; + gc = [NSGraphicsContext graphicsContextWithGraphicsPort:c->c flipped:YES]; + [NSGraphicsContext setCurrentContext:gc]; - // wait, that's not enough; we need to offset y values to account for our new flipping - // TODO explain this calculation - y = c->height - tl->size.height - y; + // TODO is this the right point? + // TODO does this draw with the proper default styles? + [tl->layoutManager drawGlyphsForGlyphRange:[tl->layoutManager glyphRangeForTextContainer:tl->container] + atPoint:NSMakePoint(x, y)]; - // CTFrameDraw() draws in the path we specified when creating the frame - // this means that in our usage, CTFrameDraw() will draw at (0,0) - // so move the origin to be at (x,y) instead - // TODO are the signs correct? - CGContextTranslateCTM(c->c, x, y); - - CTFrameDraw(tl->frame, c->c); - - CGContextRestoreGState(c->c); + [gc flushGraphics]; // just to be safe + [NSGraphicsContext restoreGraphicsState]; + // TODO release gc? } +// TODO update all of these { // TODO document that the width and height of a layout is not necessarily the sum of the widths and heights of its constituent lines; this is definitely untrue on OS X, where lines are placed in such a way that the distance between baselines is always integral // TODO width doesn't include trailing whitespace... // TODO figure out how paragraph spacing should play into this +// } void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height) { - *width = tl->size.width; - *height = tl->size.height; + NSRect r; + + // see https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/TextLayout/Tasks/StringHeight.html + r = [tl->layoutManager usedRectForTextContainer:tl->container]; + *width = r.size.width; + *height = r.size.height; } int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) { - return tl->nLines; + return [tl->lineInfo count]; } void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end) { - CTLineRef lr; - CFRange range; + lineInfo *li; - lr = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, line); - range = CTLineGetStringRange(lr); - *start = tl->u16tou8[range.location]; - *end = tl->u16tou8[range.location + range.length]; + li = (lineInfo *) [tl->lineInfo objectAtIndex:line]; + *start = tl->u16tou8[li.characterRange.location]; + *end = tl->u16tou8[li.characterRange.location + li.characterRange.length]; } void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m) { - *m = tl->lineMetrics[line]; + lineInfo *li; + + li = (lineInfo *) [tl->lineInfo objectAtIndex:line]; + + m->X = li.lineRect.origin.x; + m->Y = li.lineRect.origin.y; + m->Width = li.lineRect.size.width; + m->Height = li.lineRect.size.height; + + // TODO is this correct? + m->BaselineY = (m->X + m->Height) - li.baselineOffset; + + // TODO + m->Ascent = 10000; + m->Descent = 10000; + m->Leading = 10000; + + // TODO + m->ParagraphSpacingBefore = 0; + m->LineHeightSpace = 0; + m->LineSpacing = 0; + m->ParagraphSpacing = 0; } void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) { +#if 0 /* TODO */ CFIndex i; CTLineRef line; CFIndex pos; @@ -320,6 +260,7 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTex // TODO } result->Pos = tl->u16tou8[pos]; +#endif } void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r) diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index 27440841..8e81400f 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -21,7 +21,6 @@ struct closeness { static double doubleAttr(NSDictionary *traits, NSString *attr) { NSNumber *n; - double val; n = (NSNumber *) [traits objectForKey:attr]; return [n doubleValue]; @@ -90,7 +89,7 @@ static NSFontDescriptor *matchTraits(NSFontDescriptor *against, double targetWei n = [matching count]; if (n == 0) { // likewise - [matching release]; +//TODO [matching release]; return against; } @@ -100,7 +99,7 @@ static NSFontDescriptor *matchTraits(NSFontDescriptor *against, double targetWei closeness[i].index = i; current = (NSFontDescriptor *) [matching objectAtIndex:i]; - traits = (NSDictionary *) [current objectAtIndex:NSFontTraitsAttribute]; + traits = (NSDictionary *) [current objectForKey:NSFontTraitsAttribute]; if (traits == nil) { // couldn't get traits; be safe by ranking it lowest // LONGTERM figure out what the longest possible distances are @@ -143,7 +142,7 @@ static NSFontDescriptor *matchTraits(NSFontDescriptor *against, double targetWei // release everything uiFree(closeness); - [matching release]; +//TODO [matching release]; // and release the original descriptor since we no longer need it [against release]; @@ -207,15 +206,13 @@ static const double stretchesToCTWidths[] = { NSFontDescriptor *fontdescToNSFontDescriptor(uiDrawFontDescriptor *fd) { NSMutableDictionary *attrs; - CFStringRef cffamily; - CFNumberRef cfsize; - CTFontDescriptorRef basedesc; + NSFontDescriptor *basedesc; attrs = [NSMutableDictionary new]; [attrs setObject:[NSString stringWithUTF8String:fd->Family] forKey:NSFontFamilyAttribute]; [attrs setObject:[NSNumber numberWithDouble:fd->Size] - forKye:NSFontSizeAttribute]; + forKey:NSFontSizeAttribute]; basedesc = [[NSFontDescriptor alloc] initWithFontAttributes:attrs]; [attrs release]; From 08f2085f418907ece4fda1123eb59a5bda3534d8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 23 Jan 2017 14:31:39 -0500 Subject: [PATCH 0572/1329] Quick fix. --- darwin/drawtext.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 6a16af1e..08f37ed8 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -201,7 +201,7 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa m->Height = li.lineRect.size.height; // TODO is this correct? - m->BaselineY = (m->X + m->Height) - li.baselineOffset; + m->BaselineY = (m->Y + m->Height) - li.baselineOffset; // TODO m->Ascent = 10000; From 1ca9a28f2d42aa2e45fadb10c7e249f1234bd0ab Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 23 Jan 2017 15:07:28 -0500 Subject: [PATCH 0573/1329] More work. --- darwin/drawtext.m | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 08f37ed8..69166610 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -6,6 +6,8 @@ @property NSRange glyphRange; @property NSRange characterRange; @property NSRect lineRect; +@property NSRect lineUsedRect; +@property NSRect glyphBoundingRect; @property CGFloat baselineOffset; @end @@ -117,9 +119,15 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto li.glyphRange = glyphRange; li.characterRange = [tl->layoutManager characterRangeForGlyphRange:li.glyphRange actualGlyphRange:NULL]; li.lineRect = lineRect; + li.lineUsedRect = [tl->layoutManager lineFragmentUsedRectForGlyphAtIndex:index effectiveRange:NULL]; + li.glyphBoundingRect = [tl->layoutManager boundingRectForGlyphRange:li.glyphRange inTextContainer:tl->container]; // and this is from http://www.cocoabuilder.com/archive/cocoa/308568-how-to-get-baseline-info.html and http://www.cocoabuilder.com/archive/cocoa/199283-height-and-location-of-text-within-line-in-nslayoutmanager-ignoring-spacing.html li.baselineOffset = [[tl->layoutManager typesetter] baselineOffsetInLayoutManager:tl->layoutManager glyphIndex:index]; [tl->lineInfo addObject:li]; +NSLog(@"line %d", (int)[tl->lineInfo count]); +NSLog(@" rect %@", NSStringFromRect(li.lineRect)); +NSLog(@" used rect %@", NSStringFromRect(li.lineUsedRect)); +NSLog(@"glyph rect %@", NSStringFromRect(li.glyphBoundingRect)); [li release]; index = glyphRange.location + glyphRange.length; } @@ -197,7 +205,8 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa m->X = li.lineRect.origin.x; m->Y = li.lineRect.origin.y; - m->Width = li.lineRect.size.width; + // if we use li.lineRect here we get the whole line, not just the part with stuff in it + m->Width = li.lineUsedRect.size.width; m->Height = li.lineRect.size.height; // TODO is this correct? From d1e2b17f6e4a793f7610bae72606a5109f8629f6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 24 Jan 2017 01:04:08 -0500 Subject: [PATCH 0574/1329] Attempted to the layout er I mean attempted to define the ascent, desceitn, er descent, and leading for the NSLayoutManager based code AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA. --- darwin/drawtext.m | 51 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 69166610..79aace57 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -9,6 +9,9 @@ @property NSRect lineUsedRect; @property NSRect glyphBoundingRect; @property CGFloat baselineOffset; +@property double ascent; +@property double descent; +@property double leading; @end @implementation lineInfo @@ -111,23 +114,47 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto index = 0; while (index < [tl->layoutManager numberOfGlyphs]) { NSRange glyphRange; - NSRect lineRect; - lineInfo *li; + __block lineInfo *li; - lineRect = [tl->layoutManager lineFragmentRectForGlyphAtIndex:index effectiveRange:&glyphRange]; li = [lineInfo new]; + li.lineRect = [tl->layoutManager lineFragmentRectForGlyphAtIndex:index effectiveRange:&glyphRange]; li.glyphRange = glyphRange; li.characterRange = [tl->layoutManager characterRangeForGlyphRange:li.glyphRange actualGlyphRange:NULL]; - li.lineRect = lineRect; li.lineUsedRect = [tl->layoutManager lineFragmentUsedRectForGlyphAtIndex:index effectiveRange:NULL]; li.glyphBoundingRect = [tl->layoutManager boundingRectForGlyphRange:li.glyphRange inTextContainer:tl->container]; // and this is from http://www.cocoabuilder.com/archive/cocoa/308568-how-to-get-baseline-info.html and http://www.cocoabuilder.com/archive/cocoa/199283-height-and-location-of-text-within-line-in-nslayoutmanager-ignoring-spacing.html li.baselineOffset = [[tl->layoutManager typesetter] baselineOffsetInLayoutManager:tl->layoutManager glyphIndex:index]; + li.ascent = 0; + li.descent = 0; + li.leading = 0; + // imitate what AppKit actually does (or seems to) + [tl->attrstr enumerateAttributesInRange:li.characterRange options:0 usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) { + NSFont *f; + double v; + + f = (NSFont *) [attrs objectForKey:NSFontAttributeName]; + if (f == nil) { + f = [NSFont fontWithName:@"Helvetica" size:12.0]; + if (f == nil) + f = [NSFont systemFontOfSize:12.0]; + } + +NSLog(@"%@ %g %g %g", NSStringFromRect([f boundingRectForFont]), +[f ascender], [f descender], [f leading]); + + v = floor([f ascender] + 0.5); + if (li.ascent < v) + li.ascent = v; + + v = floor(-[f descender] + 0.5); + if (li.descent < v) + li.descent = v; + + v = floor([f leading] + 0.5); + if (li.leading < v) + li.leading = v; + }]; [tl->lineInfo addObject:li]; -NSLog(@"line %d", (int)[tl->lineInfo count]); -NSLog(@" rect %@", NSStringFromRect(li.lineRect)); -NSLog(@" used rect %@", NSStringFromRect(li.lineUsedRect)); -NSLog(@"glyph rect %@", NSStringFromRect(li.glyphBoundingRect)); [li release]; index = glyphRange.location + glyphRange.length; } @@ -211,11 +238,9 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa // TODO is this correct? m->BaselineY = (m->Y + m->Height) - li.baselineOffset; - - // TODO - m->Ascent = 10000; - m->Descent = 10000; - m->Leading = 10000; + m->Ascent = li.ascent; + m->Descent = li.descent; + m->Leading = li.leading; // TODO m->ParagraphSpacingBefore = 0; From 877ffa5f897c3ad3065ff43ea888aa40ff37e22f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 24 Jan 2017 10:36:13 -0500 Subject: [PATCH 0575/1329] More attempts. Ugggggh. --- darwin/drawtext.m | 56 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 6 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 79aace57..4747e58b 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -130,7 +130,9 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto // imitate what AppKit actually does (or seems to) [tl->attrstr enumerateAttributesInRange:li.characterRange options:0 usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) { NSFont *f; - double v; + NSRect boundingRect; + double v, realAscent, realDescent, realLeading; + BOOL skipAdjust, doAdjust; f = (NSFont *) [attrs objectForKey:NSFontAttributeName]; if (f == nil) { @@ -139,21 +141,63 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto f = [NSFont systemFontOfSize:12.0]; } -NSLog(@"%@ %g %g %g", NSStringFromRect([f boundingRectForFont]), -[f ascender], [f descender], [f leading]); + boundingRect = [f boundingRectForFont]; + realAscent = [f ascender]; + realDescent = -[f descender]; + realLeading = [f leading]; + skipAdjust = NO; + doAdjust = NO; + if (NSMaxY(boundingRect) <= realAscent) { + // ascent entirely within bounding box + // don't do anything if there's leading; I'm guessing it's a combination of both of the reasons to skip below... (least sure of this one) + if (realLeading != 0) + skipAdjust = YES; + // does the descent slip outside the bounding box? + if (-realDescent <= NSMinY(boundingRect)) + // yes — I guess we should assume accents don't collide with the previous line's descent, though I'm not as sure of that as I am about the else clause below... + skipAdjust = YES; + } else { + // ascent outside bounding box — ascent does not include accents + // only skip adjustment if there isn't leading (apparently some fonts use the previous line's leading for accents? :/ ) + if (realLeading != 0) + skipAdjust = YES; + } + if (!skipAdjust) { + UniChar ch = 0xC0; + CGGlyph glyph; - v = floor([f ascender] + 0.5); + // there does not seem to be an AppKit API for this... + if (CTFontGetGlyphsForCharacters((CTFontRef) f, &ch, &glyph, 1) != false) { + NSRect bbox; + + bbox = [f boundingRectForGlyph:glyph]; + if (NSMaxY(bbox) > realAscent) + doAdjust = YES; + if (-realDescent > NSMinY(bbox)) + doAdjust = YES; + } + } + // TODO vertical + + v = [f ascender]; + // TODO get this one back out + if (doAdjust) + v += 0.2 * ([f ascender] + [f descender]); + //v = floor(v + 0.5); if (li.ascent < v) li.ascent = v; - v = floor(-[f descender] + 0.5); + v = -[f descender];// floor(-[f descender] + 0.5); if (li.descent < v) li.descent = v; - v = floor([f leading] + 0.5); + v = [f leading];//floor([f leading] + 0.5); if (li.leading < v) li.leading = v; }]; + li.ascent = floor(li.ascent + 0.5); + li.descent = floor(li.descent + 0.5); + li.leading = floor(li.leading + 0.5); [tl->lineInfo addObject:li]; [li release]; index = glyphRange.location + glyphRange.length; From 8d3c68d7f020990b307d8829bd49b2f93554619e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 24 Jan 2017 17:12:17 -0500 Subject: [PATCH 0576/1329] Moved the AppKit text drawer out of the way for now. One last experiment first; didn't seem to matter :S --- darwin/{drawtext.m => _appkit_drawtext.m} | 1 + darwin/{fontmatch.m => _appkit_fontmatch.m} | 0 darwin/uipriv_darwin.h | 2 +- 3 files changed, 2 insertions(+), 1 deletion(-) rename darwin/{drawtext.m => _appkit_drawtext.m} (99%) rename darwin/{fontmatch.m => _appkit_fontmatch.m} (100%) diff --git a/darwin/drawtext.m b/darwin/_appkit_drawtext.m similarity index 99% rename from darwin/drawtext.m rename to darwin/_appkit_drawtext.m index 4747e58b..644a2bdf 100644 --- a/darwin/drawtext.m +++ b/darwin/_appkit_drawtext.m @@ -101,6 +101,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto [tl->container setLineFragmentPadding:0]; tl->layoutManager = [[NSLayoutManager alloc] init]; + [tl->layoutManager setTypesetterBehavior:NSTypesetterLatestBehavior]; [tl->layoutManager addTextContainer:tl->container]; [tl->attrstr addLayoutManager:tl->layoutManager]; diff --git a/darwin/fontmatch.m b/darwin/_appkit_fontmatch.m similarity index 100% rename from darwin/fontmatch.m rename to darwin/_appkit_fontmatch.m diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index bfbf0688..0a15e3bf 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -147,5 +147,5 @@ extern void doManualMove(NSWindow *w, NSEvent *initialEvent); extern void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdge edge); // fontmatch.m -//TODO extern CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd); +extern CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd); extern NSFontDescriptor *fontdescToNSFontDescriptor(uiDrawFontDescriptor *fd); From 8ff01c5034f8000393466f5e30dc1edca2a584d9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 24 Jan 2017 17:15:57 -0500 Subject: [PATCH 0577/1329] Ugh --- darwin/_coretext_drawtext.m | 489 ++++++++++++++++++++---------------- 1 file changed, 274 insertions(+), 215 deletions(-) diff --git a/darwin/_coretext_drawtext.m b/darwin/_coretext_drawtext.m index 72a8d2e6..a84b68b5 100644 --- a/darwin/_coretext_drawtext.m +++ b/darwin/_coretext_drawtext.m @@ -1,268 +1,327 @@ -// 6 september 2015 +// 2 january 2017 #import "uipriv_darwin.h" +#import "draw.h" -// TODO double-check that we are properly handling allocation failures (or just toll free bridge from cocoa) -struct uiDrawFontFamilies { - CFArrayRef fonts; +// TODO what happens if nLines == 0 in any function? + +struct uiDrawTextLayout { + CFAttributedStringRef attrstr; + + // the width as passed into uiDrawTextLayout constructors + double width; + + CTFramesetterRef framesetter; + + // the *actual* size of the frame + // note: technically, metrics returned from frame are relative to CGPathGetPathBoundingBox(tl->path) + // however, from what I can gather, for a path created by CGPathCreateWithRect(), like we do (with a NULL transform), CGPathGetPathBoundingBox() seems to just return the standardized form of the rect used to create the path + // (this I confirmed through experimentation) + // so we can just use tl->size for adjustments + // we don't need to adjust coordinates by any origin since our rect origin is (0, 0) + CGSize size; + + CGPathRef path; + CTFrameRef frame; + + CFArrayRef lines; + CFIndex nLines; + // we compute this once when first creating the layout + uiDrawTextLayoutLineMetrics *lineMetrics; + + // for converting CFAttributedString indices to byte offsets + size_t *u16tou8; + size_t nu16tou8; // TODO I don't like the casing of this name }; -uiDrawFontFamilies *uiDrawListFontFamilies(void) +static CTFontRef fontdescToCTFont(uiDrawFontDescriptor *fd) { - uiDrawFontFamilies *ff; + CTFontDescriptorRef desc; + CTFontRef font; - ff = uiNew(uiDrawFontFamilies); - ff->fonts = CTFontManagerCopyAvailableFontFamilyNames(); - if (ff->fonts == NULL) - implbug("error getting available font names (no reason specified) (TODO)"); - return ff; -} - -int uiDrawFontFamiliesNumFamilies(uiDrawFontFamilies *ff) -{ - return CFArrayGetCount(ff->fonts); -} - -char *uiDrawFontFamiliesFamily(uiDrawFontFamilies *ff, int n) -{ - CFStringRef familystr; - char *family; - - familystr = (CFStringRef) CFArrayGetValueAtIndex(ff->fonts, n); - // toll-free bridge - family = uiDarwinNSStringToText((NSString *) familystr); - // Get Rule means we do not free familystr - return family; -} - -void uiDrawFreeFontFamilies(uiDrawFontFamilies *ff) -{ - CFRelease(ff->fonts); - uiFree(ff); -} - -struct uiDrawTextFont { - CTFontRef f; -}; - -uiDrawTextFont *mkTextFont(CTFontRef f, BOOL retain) -{ - uiDrawTextFont *font; - - font = uiNew(uiDrawTextFont); - font->f = f; - if (retain) - CFRetain(font->f); + desc = fontdescToCTFontDescriptor(fd); + font = CTFontCreateWithFontDescriptor(desc, fd->Size, NULL); + CFRelease(desc); // TODO correct? return font; } -uiDrawTextFont *mkTextFontFromNSFont(NSFont *f) -{ - // toll-free bridging; we do retain, though - return mkTextFont((CTFontRef) f, YES); -} - -static CFMutableDictionaryRef newAttrList(void) -{ - CFMutableDictionaryRef attr; - - attr = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - if (attr == NULL) - complain("error creating attribute dictionary in newAttrList()()"); - return attr; -} - -static void addFontFamilyAttr(CFMutableDictionaryRef attr, const char *family) +static CFAttributedStringRef attrstrToCoreFoundation(uiAttributedString *s, uiDrawFontDescriptor *defaultFont) { CFStringRef cfstr; + CFMutableDictionaryRef defaultAttrs; + CTFontRef defaultCTFont; + CFAttributedStringRef base; + CFMutableAttributedStringRef mas; - cfstr = CFStringCreateWithCString(NULL, family, kCFStringEncodingUTF8); - if (cfstr == NULL) - complain("error creating font family name CFStringRef in addFontFamilyAttr()"); - CFDictionaryAddValue(attr, kCTFontFamilyNameAttribute, cfstr); - CFRelease(cfstr); // dictionary holds its own reference + cfstr = CFStringCreateWithCharacters(NULL, attrstrUTF16(s), attrstrUTF16Len(s)); + if (cfstr == NULL) { + // TODO + } + defaultAttrs = CFDictionaryCreateMutable(NULL, 1, + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (defaultAttrs == NULL) { + // TODO + } + defaultCTFont = fontdescToCTFont(defaultFont); + CFDictionaryAddValue(defaultAttrs, kCTFontAttributeName, defaultCTFont); + CFRelease(defaultCTFont); + + base = CFAttributedStringCreate(NULL, cfstr, defaultAttrs); + if (base == NULL) { + // TODO + } + CFRelease(cfstr); + CFRelease(defaultAttrs); + mas = CFAttributedStringCreateMutableCopy(NULL, 0, base); + CFRelease(base); + + CFAttributedStringBeginEditing(mas); + // TODO copy in the attributes + CFAttributedStringEndEditing(mas); + + return mas; } -static void addFontSizeAttr(CFMutableDictionaryRef attr, double size) +// TODO this is wrong for our hit-test example's multiple combining character example +static uiDrawTextLayoutLineMetrics *computeLineMetrics(CTFrameRef frame, CGSize size) { - CFNumberRef n; + uiDrawTextLayoutLineMetrics *metrics; + CFArrayRef lines; + CTLineRef line; + CFIndex i, n; + CGFloat ypos; + CGRect bounds, boundsNoLeading; + CGFloat ascent, descent, leading; + CGPoint *origins; - n = CFNumberCreate(NULL, kCFNumberDoubleType, &size); - CFDictionaryAddValue(attr, kCTFontSizeAttribute, n); - CFRelease(n); + lines = CTFrameGetLines(frame); + n = CFArrayGetCount(lines); + metrics = (uiDrawTextLayoutLineMetrics *) uiAlloc(n * sizeof (uiDrawTextLayoutLineMetrics), "uiDrawTextLayoutLineMetrics[] (text layout)"); + + origins = (CGPoint *) uiAlloc(n * sizeof (CGPoint), "CGPoint[] (text layout)"); + CTFrameGetLineOrigins(frame, CFRangeMake(0, n), origins); + + ypos = size.height; + for (i = 0; i < n; i++) { + line = (CTLineRef) CFArrayGetValueAtIndex(lines, i); + bounds = CTLineGetBoundsWithOptions(line, 0); + boundsNoLeading = CTLineGetBoundsWithOptions(line, kCTLineBoundsExcludeTypographicLeading); + + // this is equivalent to boundsNoLeading.size.height + boundsNoLeading.origin.y (manually verified) + ascent = bounds.size.height + bounds.origin.y; + descent = -boundsNoLeading.origin.y; + // TODO does this preserve leading sign? + leading = -bounds.origin.y - descent; + + // Core Text always rounds these up for paragraph style calculations; there is a flag to control it but it's inaccessible (and this behavior is turned off for old versions of iPhoto) + ascent = floor(ascent + 0.5); + descent = floor(descent + 0.5); + if (leading > 0) + leading = floor(leading + 0.5); + + metrics[i].X = origins[i].x; + metrics[i].Y = origins[i].y - descent - leading; + metrics[i].Width = bounds.size.width; + metrics[i].Height = ascent + descent + leading; + + metrics[i].BaselineY = origins[i].y; + metrics[i].Ascent = ascent; + metrics[i].Descent = descent; + metrics[i].Leading = leading; + + // TODO + metrics[i].ParagraphSpacingBefore = 0; + metrics[i].LineHeightSpace = 0; + metrics[i].LineSpacing = 0; + metrics[i].ParagraphSpacing = 0; + + // and finally advance to the next line + ypos += metrics[i].Height; + } + + // okay, but now all these metrics are unflipped + // we need to flip them + for (i = 0; i < n; i++) { + metrics[i].Y = size.height - metrics[i].Y; + // go from bottom-left corner to top-left + metrics[i].Y -= metrics[i].Height; + metrics[i].BaselineY = size.height - metrics[i].BaselineY; + // TODO also adjust by metrics[i].Height? + } + + uiFree(origins); + return metrics; } -#if 0 -TODO -// See http://stackoverflow.com/questions/4810409/does-coretext-support-small-caps/4811371#4811371 and https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c for what these do -// And fortunately, unlike the traits (see below), unmatched features are simply ignored without affecting the other features :D -static void addFontSmallCapsAttr(CFMutableDictionaryRef attr) +uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescriptor *defaultFont, double width) { - CFMutableArrayRef outerArray; - CFMutableDictionaryRef innerDict; - CFNumberRef numType, numSelector; - int num; + uiDrawTextLayout *tl; + CGFloat cgwidth; + CFRange range, unused; + CGRect rect; - outerArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - if (outerArray == NULL) - complain("error creating outer CFArray for adding small caps attributes in addFontSmallCapsAttr()"); + tl = uiNew(uiDrawTextLayout); + tl->attrstr = attrstrToCoreFoundation(s, defaultFont); + range.location = 0; + range.length = CFAttributedStringGetLength(tl->attrstr); + tl->width = width; - // Apple's headers say these are deprecated, but a few fonts still rely on them - num = kLetterCaseType; - numType = CFNumberCreate(NULL, kCFNumberIntType, &num); - num = kSmallCapsSelector; - numSelector = CFNumberCreate(NULL, kCFNumberIntType, &num); - innerDict = newAttrList(); - CFDictionaryAddValue(innerDict, kCTFontFeatureTypeIdentifierKey, numType); - CFRelease(numType); - CFDictionaryAddValue(innerDict, kCTFontFeatureSelectorIdentifierKey, numSelector); - CFRelease(numSelector); - CFArrayAppendValue(outerArray, innerDict); - CFRelease(innerDict); // and likewise for CFArray + // TODO CTFrameProgression for RTL/LTR + // TODO kCTParagraphStyleSpecifierMaximumLineSpacing, kCTParagraphStyleSpecifierMinimumLineSpacing, kCTParagraphStyleSpecifierLineSpacingAdjustment for line spacing + tl->framesetter = CTFramesetterCreateWithAttributedString(tl->attrstr); + if (tl->framesetter == NULL) { + // TODO + } - // these are the non-deprecated versions of the above; some fonts have these instead - num = kLowerCaseType; - numType = CFNumberCreate(NULL, kCFNumberIntType, &num); - num = kLowerCaseSmallCapsSelector; - numSelector = CFNumberCreate(NULL, kCFNumberIntType, &num); - innerDict = newAttrList(); - CFDictionaryAddValue(innerDict, kCTFontFeatureTypeIdentifierKey, numType); - CFRelease(numType); - CFDictionaryAddValue(innerDict, kCTFontFeatureSelectorIdentifierKey, numSelector); - CFRelease(numSelector); - CFArrayAppendValue(outerArray, innerDict); - CFRelease(innerDict); // and likewise for CFArray + cgwidth = (CGFloat) width; + if (cgwidth < 0) + cgwidth = CGFLOAT_MAX; + // TODO these seem to be floor()'d or truncated? + // TODO double check to make sure this TODO was right + tl->size = CTFramesetterSuggestFrameSizeWithConstraints(tl->framesetter, + range, + // TODO kCTFramePathWidthAttributeName? + NULL, + CGSizeMake(cgwidth, CGFLOAT_MAX), + &unused); // not documented as accepting NULL (TODO really?) - CFDictionaryAddValue(attr, kCTFontFeatureSettingsAttribute, outerArray); - CFRelease(outerArray); + rect.origin = CGPointZero; + rect.size = tl->size; + tl->path = CGPathCreateWithRect(rect, NULL); + tl->frame = CTFramesetterCreateFrame(tl->framesetter, + range, + tl->path, + // TODO kCTFramePathWidthAttributeName? + NULL); + if (tl->frame == NULL) { + // TODO + } + + tl->lines = CTFrameGetLines(tl->frame); + tl->nLines = CFArrayGetCount(tl->lines); + tl->lineMetrics = computeLineMetrics(tl->frame, tl->size); + + // and finally copy the UTF-16 to UTF-8 index conversion table + tl->u16tou8 = attrstrCopyUTF16ToUTF8(s, &(tl->nu16tou8)); + + return tl; } -#endif -#if 0 -// Named constants for these were NOT added until 10.11, and even then they were added as external symbols instead of macros, so we can't use them directly :( -// kode54 got these for me before I had access to El Capitan; thanks to him. -#define ourNSFontWeightUltraLight -0.800000 -#define ourNSFontWeightThin -0.600000 -#define ourNSFontWeightLight -0.400000 -#define ourNSFontWeightRegular 0.000000 -#define ourNSFontWeightMedium 0.230000 -#define ourNSFontWeightSemibold 0.300000 -#define ourNSFontWeightBold 0.400000 -#define ourNSFontWeightHeavy 0.560000 -#define ourNSFontWeightBlack 0.620000 -#endif - -// Now remember what I said earlier about having to add the small caps traits after calling the above? This gets a dictionary back so we can do so. -CFMutableDictionaryRef extractAttributes(CTFontDescriptorRef desc) +void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { - CFDictionaryRef dict; - CFMutableDictionaryRef mdict; - - dict = CTFontDescriptorCopyAttributes(desc); - // this might not be mutable, so make a mutable copy - mdict = CFDictionaryCreateMutableCopy(NULL, 0, dict); - CFRelease(dict); - return mdict; + uiFree(tl->u16tou8); + uiFree(tl->lineMetrics); + // TODO release tl->lines? + CFRelease(tl->frame); + CFRelease(tl->path); + CFRelease(tl->framesetter); + CFRelease(tl->attrstr); + uiFree(tl); } -uiDrawTextFont *uiDrawLoadClosestFont(const uiDrawTextFontDescriptor *desc) +// TODO document that (x,y) is the top-left corner of the *entire frame* +void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) { - CTFontRef f; - CFMutableDictionaryRef attr; - CTFontDescriptorRef cfdesc; + CGContextSaveGState(c->c); - attr = newAttrList(); - addFontFamilyAttr(attr, desc->Family); - addFontSizeAttr(attr, desc->Size); + // Core Text doesn't draw onto a flipped view correctly; we have to pretend it was unflipped + // see the iOS bits of the first example at https://developer.apple.com/library/mac/documentation/StringsTextFonts/Conceptual/CoreText_Programming/LayoutOperations/LayoutOperations.html#//apple_ref/doc/uid/TP40005533-CH12-SW1 (iOS is naturally flipped) + // TODO how is this affected by a non-identity CTM? + CGContextTranslateCTM(c->c, 0, c->height); + CGContextScaleCTM(c->c, 1.0, -1.0); + CGContextSetTextMatrix(c->c, CGAffineTransformIdentity); - // now we have to do the traits matching, so create a descriptor, match the traits, and then get the attributes back - cfdesc = CTFontDescriptorCreateWithAttributes(attr); - // TODO release attr? - cfdesc = matchTraits(cfdesc, desc->Weight, desc->Italic, desc->Stretch); + // wait, that's not enough; we need to offset y values to account for our new flipping + // TODO explain this calculation + y = c->height - tl->size.height - y; - // specify the initial size again just to be safe - f = CTFontCreateWithFontDescriptor(cfdesc, desc->Size, NULL); - // TODO release cfdesc? + // CTFrameDraw() draws in the path we specified when creating the frame + // this means that in our usage, CTFrameDraw() will draw at (0,0) + // so move the origin to be at (x,y) instead + // TODO are the signs correct? + CGContextTranslateCTM(c->c, x, y); - return mkTextFont(f, NO); // we hold the initial reference; no need to retain again + CTFrameDraw(tl->frame, c->c); + + CGContextRestoreGState(c->c); } -void uiDrawFreeTextFont(uiDrawTextFont *font) +// TODO document that the width and height of a layout is not necessarily the sum of the widths and heights of its constituent lines; this is definitely untrue on OS X, where lines are placed in such a way that the distance between baselines is always integral +// TODO width doesn't include trailing whitespace... +// TODO figure out how paragraph spacing should play into this +void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height) { - CFRelease(font->f); - uiFree(font); + *width = tl->size.width; + *height = tl->size.height; } -uintptr_t uiDrawTextFontHandle(uiDrawTextFont *font) +int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) { - return (uintptr_t) (font->f); + return tl->nLines; } -void uiDrawTextFontDescribe(uiDrawTextFont *font, uiDrawTextFontDescriptor *desc) +void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end) { - // TODO + CTLineRef lr; + CFRange range; + + lr = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, line); + range = CTLineGetStringRange(lr); + *start = tl->u16tou8[range.location]; + *end = tl->u16tou8[range.location + range.length]; } -// text sizes and user space points are identical: -// - https://developer.apple.com/library/mac/documentation/TextFonts/Conceptual/CocoaTextArchitecture/TypoFeatures/TextSystemFeatures.html#//apple_ref/doc/uid/TP40009459-CH6-51627-BBCCHIFF text points are 72 per inch -// - https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CocoaDrawingGuide/Transforms/Transforms.html#//apple_ref/doc/uid/TP40003290-CH204-SW5 user space points are 72 per inch -void uiDrawTextFontGetMetrics(uiDrawTextFont *font, uiDrawTextFontMetrics *metrics) +void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m) { - metrics->Ascent = CTFontGetAscent(font->f); - metrics->Descent = CTFontGetDescent(font->f); - metrics->Leading = CTFontGetLeading(font->f); - metrics->UnderlinePos = CTFontGetUnderlinePosition(font->f); - metrics->UnderlineThickness = CTFontGetUnderlineThickness(font->f); + *m = tl->lineMetrics[line]; } -// LONGTERM allow line separation and leading to be factored into a wrapping text layout - -// TODO reconcile differences in character wrapping on platforms -void uiDrawTextLayoutExtents(uiDrawTextLayout *layout, double *width, double *height) +void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) { - struct framesetter fs; + CFIndex i; + CTLineRef line; + CFIndex pos; - mkFramesetter(layout, &fs); - *width = fs.extents.width; - *height = fs.extents.height; - freeFramesetter(&fs); + if (y >= 0) { + for (i = 0; i < tl->nLines; i++) { + double ltop, lbottom; + + ltop = tl->lineMetrics[i].Y; + lbottom = ltop + tl->lineMetrics[i].Height; + if (y >= ltop && y < lbottom) + break; + } + result->YPosition = uiDrawTextLayoutHitTestPositionInside; + if (i == tl->nLines) { + i--; + result->YPosition = uiDrawTextLayoutHitTestPositionAfter; + } + } else { + i = 0; + result->YPosition = uiDrawTextLayoutHitTestPositionBefore; + } + result->Line = i; + + result->XPosition = uiDrawTextLayoutHitTestPositionInside; + if (x < tl->lineMetrics[i].X) { + result->XPosition = uiDrawTextLayoutHitTestPositionBefore; + // and forcibly return the first character + x = tl->lineMetrics[i].X; + } else if (x > (tl->lineMetrics[i].X + tl->lineMetrics[i].Width)) { + result->XPosition = uiDrawTextLayoutHitTestPositionAfter; + // and forcibly return the last character + x = tl->lineMetrics[i].X + tl->lineMetrics[i].Width; + } + + line = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, i); + // TODO copy the part from the docs about this point + pos = CTLineGetStringIndexForPosition(line, CGPointMake(x, 0)); + if (pos == kCFNotFound) { + // TODO + } + result->Pos = tl->u16tou8[pos]; } -// LONGTERM provide an equivalent to CTLineGetTypographicBounds() on uiDrawTextLayout? - -// LONGTERM keep this for later features and documentation purposes -#if 0 - - // LONGTERM provide a way to get the image bounds as a separate function later - bounds = CTLineGetImageBounds(line, c); - // though CTLineGetImageBounds() returns CGRectNull on error, it also returns CGRectNull on an empty string, so we can't reasonably check for error - - // CGContextSetTextPosition() positions at the baseline in the case of CTLineDraw(); we need the top-left corner instead - CTLineGetTypographicBounds(line, &yoff, NULL, NULL); - // remember that we're flipped, so we subtract - y -= yoff; - CGContextSetTextPosition(c, x, y); -#endif - -#if 0 -void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, int startChar, int endChar, double r, double g, double b, double a) +void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r) { - CGColorSpaceRef colorspace; - CGFloat components[4]; - CGColorRef color; - - // for consistency with windows, use sRGB - colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); - components[0] = r; - components[1] = g; - components[2] = b; - components[3] = a; - color = CGColorCreate(colorspace, components); - CGColorSpaceRelease(colorspace); - - CFAttributedStringSetAttribute(layout->mas, - rangeToCFRange(), - kCTForegroundColorAttributeName, - color); - CGColorRelease(color); // TODO safe? } -#endif From c9e7ee3a92324bce316a86cd4fa72817bdadd72a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 24 Jan 2017 23:13:44 -0500 Subject: [PATCH 0578/1329] Reactivated the Core Text backend. --- darwin/{_coretext_drawtext.m => drawtext.m} | 0 darwin/{_coretext_fontmatch.m => fontmatch.m} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename darwin/{_coretext_drawtext.m => drawtext.m} (100%) rename darwin/{_coretext_fontmatch.m => fontmatch.m} (100%) diff --git a/darwin/_coretext_drawtext.m b/darwin/drawtext.m similarity index 100% rename from darwin/_coretext_drawtext.m rename to darwin/drawtext.m diff --git a/darwin/_coretext_fontmatch.m b/darwin/fontmatch.m similarity index 100% rename from darwin/_coretext_fontmatch.m rename to darwin/fontmatch.m From 7614d3e0ff53f2ddb704f4d446adaf09ad6b0836 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 27 Jan 2017 15:39:24 -0500 Subject: [PATCH 0579/1329] More TODOs. --- windows/sizing.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/windows/sizing.cpp b/windows/sizing.cpp index a6d25d6e..33cc00b9 100644 --- a/windows/sizing.cpp +++ b/windows/sizing.cpp @@ -51,6 +51,7 @@ void uiWindowsSizingDlgUnitsToPixels(uiWindowsSizing *sizing, int *x, int *y) // from https://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing and https://msdn.microsoft.com/en-us/library/windows/desktop/bb226818%28v=vs.85%29.aspx // this X value is really only for buttons but I don't see a better one :/ #define winXPadding 4 +// TODO is this too much? #define winYPadding 4 void uiWindowsSizingStandardPadding(uiWindowsSizing *sizing, int *x, int *y) From 9063b106910a438b5149e923a3aae3c5b212f3e7 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 28 Jan 2017 15:33:17 -0500 Subject: [PATCH 0580/1329] More notes. --- hidpinotes | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 hidpinotes diff --git a/hidpinotes b/hidpinotes new file mode 100644 index 00000000..66acc0f3 --- /dev/null +++ b/hidpinotes @@ -0,0 +1,2 @@ +https://msdn.microsoft.com/en-us/library/windows/desktop/dn469266(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/mt744321(v=vs.85).aspx From ea93528cbae3de13d6b2886295a062a02e66e62c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 29 Jan 2017 03:21:15 -0500 Subject: [PATCH 0581/1329] IMPORTANT hidpi notes. --- hidpinotes | 1 + 1 file changed, 1 insertion(+) diff --git a/hidpinotes b/hidpinotes index 66acc0f3..74b1b021 100644 --- a/hidpinotes +++ b/hidpinotes @@ -1,2 +1,3 @@ https://msdn.microsoft.com/en-us/library/windows/desktop/dn469266(v=vs.85).aspx https://msdn.microsoft.com/en-us/library/windows/desktop/mt744321(v=vs.85).aspx +!!!! http://stackoverflow.com/questions/41917279/do-child-windows-have-the-same-dpi-as-their-parents-in-a-per-monitor-aware-appli From bb52275686cf2a5269d8b94c20f4bcc9464d66af Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 29 Jan 2017 21:50:38 -0500 Subject: [PATCH 0582/1329] Corrected ambiguous wording in a comment. --- windows/tabpage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/tabpage.cpp b/windows/tabpage.cpp index 5283ce79..77df9a18 100644 --- a/windows/tabpage.cpp +++ b/windows/tabpage.cpp @@ -60,7 +60,7 @@ static INT_PTR CALLBACK dlgproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lPar if (uMsg == WM_WINDOWPOSCHANGED) { tp = (struct tabPage *) GetWindowLongPtrW(hwnd, DWLP_USER); tabPageRelayout(tp); - // pretend the dialog hasn't handled this just in case it needs to do something special + // pretend the dialog hasn't handled this just in case the system needs to do something special return FALSE; } From c336063b65caa390758aa68ddb3e2cafbd414605 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 5 Feb 2017 20:26:59 -0500 Subject: [PATCH 0583/1329] Decided what I need to do. --- darwin/drawtext.m | 8 ++++++-- examples/drawtext/hittest.c | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index a84b68b5..fb9e3133 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -276,6 +276,8 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa *m = tl->lineMetrics[line]; } +// TODO note that in some cases lines can overlap slightly +// in our case, we read lines first to last and use their bottommost point (Y + Height) to determine where the next line should start for hit-testing void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) { CFIndex i; @@ -288,7 +290,8 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTex ltop = tl->lineMetrics[i].Y; lbottom = ltop + tl->lineMetrics[i].Height; - if (y >= ltop && y < lbottom) + // y will already >= ltop at this point since the past lbottom should == (or at least >=, see above) ltop + if (y < lbottom) break; } result->YPosition = uiDrawTextLayoutHitTestPositionInside; @@ -298,6 +301,7 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTex } } else { i = 0; + // TODO what if the first line crosses into the negatives? result->YPosition = uiDrawTextLayoutHitTestPositionBefore; } result->Line = i; @@ -314,7 +318,7 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTex } line = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, i); - // TODO copy the part from the docs about this point + // TODO copy the part from the docs about this point (TODO what point?) pos = CTLineGetStringIndexForPosition(line, CGPointMake(x, 0)); if (pos == kCFNotFound) { // TODO diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index a2454549..dc1f9dce 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -3,8 +3,8 @@ static const char *text = "Each of the glyphs an end user interacts with are called graphemes. " -//TODO "If you enter a byte range in the text boxes below and click the button, you can see the blue box move to surround that byte range, as well as what the actual byte range necessary is. " -//TODO "You'll also see the index of the first grapheme; uiAttributedString has facilities for converting between UTF-8 code points and grapheme indices. " + "If you enter a byte range in the text boxes below and click the button, you can see the blue box move to surround that byte range, as well as what the actual byte range necessary is. " + "You'll also see the index of the first grapheme; uiAttributedString has facilities for converting between UTF-8 code points and grapheme indices. " "Additionally, click on the string to move the caret. Watch the status text at the bottom change too. " "That being said: " "\xC3\x93O\xCC\x81 (combining accents) " From b18cc88dce2c874878dc47fbde437b2fd2ce4bf2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 5 Feb 2017 20:42:52 -0500 Subject: [PATCH 0584/1329] Boilerplate needed for implementing range-to-rect. --- common/attrstr.c | 13 +++++++++++++ common/uipriv.h | 1 + darwin/drawtext.m | 12 ++++++++---- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/common/attrstr.c b/common/attrstr.c index bfe42842..03dab8f9 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -315,11 +315,24 @@ size_t attrstrUTF16Len(uiAttributedString *s) return s->u16len; } +// TODO is this still needed given the below? size_t attrstrUTF8ToUTF16(uiAttributedString *s, size_t n) { return s->u8tou16[n]; } +size_t *attrstrCopyUTF8ToUTF16(uiAttributedString *s, size_t *n) +{ + size_t *out; + size_t nbytes; + + nbytes = (s->len + 1) * sizeof (size_t); + *n = s->len; + out = (size_t *) uiAlloc(nbytes, "size_t[] (uiAttributedString)"); + memmove(out, s->u8tou16, nbytes); + return out; +} + size_t *attrstrCopyUTF16ToUTF8(uiAttributedString *s, size_t *n) { size_t *out; diff --git a/common/uipriv.h b/common/uipriv.h index e62e86f8..0ad18c14 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -73,6 +73,7 @@ extern struct graphemes *graphemes(void *s, size_t len); extern const uint16_t *attrstrUTF16(uiAttributedString *s); extern size_t attrstrUTF16Len(uiAttributedString *s); extern size_t attrstrUTF8ToUTF16(uiAttributedString *s, size_t n); +extern size_t *attrstrCopyUTF8ToUTF16(uiAttributedString *s, size_t *n); extern size_t *attrstrCopyUTF16ToUTF8(uiAttributedString *s, size_t *n); // attrlist.c diff --git a/darwin/drawtext.m b/darwin/drawtext.m index fb9e3133..4db68fad 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -28,9 +28,11 @@ struct uiDrawTextLayout { // we compute this once when first creating the layout uiDrawTextLayoutLineMetrics *lineMetrics; - // for converting CFAttributedString indices to byte offsets + // for converting CFAttributedString indices from/to byte offsets + size_t *u8tou16; + size_t nUTF8; size_t *u16tou8; - size_t nu16tou8; // TODO I don't like the casing of this name + size_t nUTF16; }; static CTFontRef fontdescToCTFont(uiDrawFontDescriptor *fd) @@ -201,8 +203,9 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto tl->nLines = CFArrayGetCount(tl->lines); tl->lineMetrics = computeLineMetrics(tl->frame, tl->size); - // and finally copy the UTF-16 to UTF-8 index conversion table - tl->u16tou8 = attrstrCopyUTF16ToUTF8(s, &(tl->nu16tou8)); + // and finally copy the UTF-8/UTF-16 conversion tables + tl->u8tou16 = attrstrCopyUTF8ToUTF16(s, &(tl->nUTF8)); + tl->u16tou8 = attrstrCopyUTF16ToUTF8(s, &(tl->nUTF16)); return tl; } @@ -210,6 +213,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { uiFree(tl->u16tou8); + uiFree(tl->u8tou16); uiFree(tl->lineMetrics); // TODO release tl->lines? CFRelease(tl->frame); From 0ae25c62ed02951abd215ffe3f648432fe88720a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 5 Feb 2017 21:17:48 -0500 Subject: [PATCH 0585/1329] Implemented the range-to-rect function on OS X. --- darwin/drawtext.m | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 4db68fad..f3c8e518 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -330,6 +330,46 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTex result->Pos = tl->u16tou8[pos]; } +// TODO document this is appropriate for a caret +// TODO what happens if we select across a wrapped line? void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r) { + CFIndex i; + CTLineRef line; + CFRange range; + CGFloat x, x2; // TODO rename x to x1 + + if (start > tl->nUTF8) + start = tl->nUTF8; + if (end > tl->nUTF8) + end = tl->nUTF8; + start = tl->u8tou16[start]; + end = tl->u8tou16[end]; + + for (i = 0; i < tl->nLines; i++) { + line = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, i); + range = CTLineGetStringRange(line); + if (start >= range.location) + break; + } + if (i == tl->nLines) + i--; + r->Line = i; + if (end > (range.location + range.length)) + end = range.location + range.length; + + x = CTLineGetOffsetForStringIndex(line, start, NULL); + x2 = CTLineGetOffsetForStringIndex(line, end, NULL); + + r->X = tl->lineMetrics[i].X + x; + r->Y = tl->lineMetrics[i].Y; + r->Width = (tl->lineMetrics[i].X + x2) - r->X; + r->Height = tl->lineMetrics[i].Height; + + // and use x and x2 to get the actual start and end positions + // TODO error check? + r->RealStart = CTLineGetStringIndexForPosition(line, CGPointMake(x, 0)); + r->RealEnd = CTLineGetStringIndexForPosition(line, CGPointMake(x2, 0)); + r->RealStart = tl->u16tou8[r->RealStart]; + r->RealEnd = tl->u16tou8[r->RealEnd]; } From 64a1167e5f042708718fc0f655563abf2e3e0bc3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 5 Feb 2017 21:44:48 -0500 Subject: [PATCH 0586/1329] Added the blue caret to the drawtext example. Phew! --- darwin/drawtext.m | 3 ++- examples/drawtext/hittest.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index f3c8e518..218c06d4 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -349,7 +349,8 @@ void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, si for (i = 0; i < tl->nLines; i++) { line = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, i); range = CTLineGetStringRange(line); - if (start >= range.location) + // TODO explain this check + if (range.location >= start) break; } if (i == tl->nLines) diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index dc1f9dce..554e4a8e 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -4,6 +4,7 @@ static const char *text = "Each of the glyphs an end user interacts with are called graphemes. " "If you enter a byte range in the text boxes below and click the button, you can see the blue box move to surround that byte range, as well as what the actual byte range necessary is. " + // TODO rephrase this; I don't think this code will use those grapheme functions... "You'll also see the index of the first grapheme; uiAttributedString has facilities for converting between UTF-8 code points and grapheme indices. " "Additionally, click on the string to move the caret. Watch the status text at the bottom change too. " "That being said: " @@ -28,6 +29,8 @@ static uiAttributedString *attrstr; static uiBox *panel; static uiCheckbox *showLineBounds; +static size_t cursorPos; + // TODO should be const? static uiDrawBrush fillBrushes[4] = { { @@ -60,10 +63,23 @@ static uiDrawBrush fillBrushes[4] = { }, }; +// TODO this should be const +static uiDrawStrokeParams strokeParams = { + .Cap = uiDrawLineCapFlat, + .Join = uiDrawLineJoinMiter, + .Thickness = 2, + .MiterLimit = uiDrawDefaultMiterLimit, + .Dashes = NULL, + .NumDashes = 0, + .DashPhase = 0, +}; + static void draw(uiAreaDrawParams *p) { uiDrawPath *path; uiDrawTextLayout *layout; + uiDrawTextLayoutByteRangeRectangle r; + uiDrawBrush brush; // only clip the text, not the guides uiDrawSave(p->Context); @@ -83,6 +99,19 @@ static void draw(uiAreaDrawParams *p) uiDrawRestore(p->Context); + uiDrawTextLayoutByteRangeToRectangle(layout, cursorPos, cursorPos, &r); + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathNewFigure(path, margins + r.X, margins + r.Y); + uiDrawPathLineTo(path, margins + r.X, margins + r.Y + r.Height); + uiDrawPathEnd(path); + brush.Type = uiDrawBrushTypeSolid; + brush.R = 0.0; + brush.G = 0.0; + brush.B = 1.0; + brush.A = 1.0; + uiDrawStroke(p->Context, path, &brush, &strokeParams); + uiDrawFreePath(path); + if (uiCheckboxChecked(showLineBounds)) { uiDrawTextLayoutLineMetrics m; int i, n; @@ -132,6 +161,7 @@ struct example *mkHitTestExample(void) hitTestExample.draw = draw; attrstr = uiNewAttributedString(text); + cursorPos = uiAttributedStringLen(attrstr); return &hitTestExample; } From a1bebc82d858f73ad08d9ac06b2f5eba82ee8c08 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 6 Feb 2017 00:26:22 -0500 Subject: [PATCH 0587/1329] And implemented caret motions in the hit test examples. --- darwin/drawtext.m | 3 +-- examples/drawtext/basic.c | 1 + examples/drawtext/drawtext.h | 3 ++- examples/drawtext/hittest.c | 23 +++++++++++++++++++++++ examples/drawtext/main.c | 3 ++- 5 files changed, 29 insertions(+), 4 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 218c06d4..452619f5 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -349,8 +349,7 @@ void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, si for (i = 0; i < tl->nLines; i++) { line = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, i); range = CTLineGetStringRange(line); - // TODO explain this check - if (range.location >= start) + if (start >= range.location && start < (range.location + range.length)) break; } if (i == tl->nLines) diff --git a/examples/drawtext/basic.c b/examples/drawtext/basic.c index 04ea42e1..c40cf36c 100644 --- a/examples/drawtext/basic.c +++ b/examples/drawtext/basic.c @@ -253,6 +253,7 @@ struct example *mkBasicExample(void) basicExample.name = "Basic Paragraph of Text"; basicExample.panel = uiControl(panel); basicExample.draw = draw; + basicExample.mouse = NULL; attrstr = uiNewAttributedString(text); diff --git a/examples/drawtext/drawtext.h b/examples/drawtext/drawtext.h index ec821eb8..c82383eb 100644 --- a/examples/drawtext/drawtext.h +++ b/examples/drawtext/drawtext.h @@ -7,7 +7,8 @@ struct example { const char *name; uiControl *panel; void (*draw)(uiAreaDrawParams *p); - // TODO mouse and key? + void (*mouse)(uiAreaMouseEvent *e); + // TODO key? }; // main.c diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index 554e4a8e..1896fc2a 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -133,6 +133,28 @@ static void draw(uiAreaDrawParams *p) uiDrawFreeTextLayout(layout); } +static void mouse(uiAreaMouseEvent *e) +{ + uiDrawTextLayout *layout; + uiDrawTextLayoutHitTestResult res; + + if (e->Down != 1) + return; + + layout = uiDrawNewTextLayout(attrstr, + &defaultFont, + e->AreaWidth - 2 * margins); + uiDrawTextLayoutHitTest(layout, + e->X - margins, e->Y - margins, + &res); + uiDrawFreeTextLayout(layout); + + // TODO make a label and set it + + cursorPos = res.Pos; + redraw(); +} + static struct example hitTestExample; // TODO share? @@ -159,6 +181,7 @@ struct example *mkHitTestExample(void) hitTestExample.name = "Hit-Testing and Grapheme Boundaries"; hitTestExample.panel = uiControl(panel); hitTestExample.draw = draw; + hitTestExample.mouse = mouse; attrstr = uiNewAttributedString(text); cursorPos = uiAttributedStringLen(attrstr); diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c index 8e5a93ec..552b2f6b 100644 --- a/examples/drawtext/main.c +++ b/examples/drawtext/main.c @@ -31,7 +31,8 @@ static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *p) static void handlerMouseEvent(uiAreaHandler *a, uiArea *area, uiAreaMouseEvent *e) { - // do nothing + if (examples[curExample]->mouse != NULL) + examples[curExample]->mouse(e); } static void handlerMouseCrossed(uiAreaHandler *ah, uiArea *a, int left) From 675e7594dae7b670a9b02f5cdb00b4c0f6f81854 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 6 Feb 2017 00:26:47 -0500 Subject: [PATCH 0588/1329] More TODOs. --- examples/drawtext/hittest.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index 1896fc2a..8e85436a 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -1,6 +1,8 @@ // 20 january 2017 #include "drawtext.h" +// TODO have a ligature + static const char *text = "Each of the glyphs an end user interacts with are called graphemes. " "If you enter a byte range in the text boxes below and click the button, you can see the blue box move to surround that byte range, as well as what the actual byte range necessary is. " From 71176c2e331ce34bdcc6019bac99d81855cb3e75 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 6 Feb 2017 01:46:06 -0500 Subject: [PATCH 0589/1329] Added the descriptive label to the caret. --- examples/drawtext/hittest.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index 8e85436a..efa80907 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -2,6 +2,7 @@ #include "drawtext.h" // TODO have a ligature +// TODO allow clicking on the end of a line to put the caret there static const char *text = "Each of the glyphs an end user interacts with are called graphemes. " @@ -29,6 +30,7 @@ static uiAttributedString *attrstr; #define margins 10 static uiBox *panel; +static uiLabel *caretLabel; static uiCheckbox *showLineBounds; static size_t cursorPos; @@ -135,10 +137,17 @@ static void draw(uiAreaDrawParams *p) uiDrawFreeTextLayout(layout); } +static const char *positions[] = { + [uiDrawTextLayoutHitTestPositionBefore] = "before", + [uiDrawTextLayoutHitTestPositionInside] = "inside", + [uiDrawTextLayoutHitTestPositionAfter] = "after", +}; + static void mouse(uiAreaMouseEvent *e) { uiDrawTextLayout *layout; uiDrawTextLayoutHitTestResult res; + char labelText[128]; if (e->Down != 1) return; @@ -151,7 +160,11 @@ static void mouse(uiAreaMouseEvent *e) &res); uiDrawFreeTextLayout(layout); - // TODO make a label and set it + sprintf(labelText, "pos %zd line %d x position %s y position %s", + res.Pos, res.Line, + positions[res.XPosition], + positions[res.YPosition]); + uiLabelSetText(caretLabel, labelText); cursorPos = res.Pos; redraw(); @@ -178,6 +191,8 @@ static uiCheckbox *newCheckbox(const char *text) struct example *mkHitTestExample(void) { panel = uiNewVerticalBox(); + caretLabel = uiNewLabel("Caret information is shown here"); + uiBoxAppend(panel, uiControl(caretLabel), 0); showLineBounds = newCheckbox("Show Line Bounds (for debugging metrics)"); hitTestExample.name = "Hit-Testing and Grapheme Boundaries"; From c539362c1545adfcca6856ad00601f8487efa6ce Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 6 Feb 2017 10:11:45 -0500 Subject: [PATCH 0590/1329] Implemented the Pango hit test functions. Now to test. --- unix/drawtext.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/unix/drawtext.c b/unix/drawtext.c index 6c01bc7d..7c1f13e4 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -188,8 +188,65 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) { + int index; + int trailing; + int line; + double width, height; + + // disregard the return value; we do our own bounds checking + // TODO see if there's a way we can use some other function (possibly this one again) to extrapolate more precise bounds information + pango_layout_xy_to_index(tl->layout, + cairoToPango(x), cairoToPango(y), + &index, &trailing); + // figure out what line that was + pango_layout_index_to_line_x(tl->layout, index, trailing, + &line, NULL); + result->Pos = index; + result->Line = line; + + uiDrawTextLayoutExtents(tl, &width, &height); + result->XPosition = uiDrawTextLayoutHitTestPositionInside; + if (x < 0) + result->XPosition = uiDrawTextLayoutHitTestPositionBefore; + else if (x >= width) + result->XPosition = uiDrawTextLayoutHitTestPositionAfter; + result->YPosition = uiDrawTextLayoutHitTestPositionInside; + if (y < 0) + result->YPosition = uiDrawTextLayoutHitTestPositionBefore; + else if (y >= height) + result->YPosition = uiDrawTextLayoutHitTestPositionAfter; } +// TODO consider providing a uiDrawTextLayoutByteIndexToCursorPos() function +// TODO is this correct for RTL? void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r) { + PangoRectangle startRect, endRect; + int line; + PangoLayoutLine *pll; + + pango_layout_index_to_pos(tl->layout, start, &startRect); + + // figure out what line that was + // TODO talk about leading edge here + pango_layout_index_to_line_x(tl->layout, start, 1, + &line, NULL); + pll = pango_layout_get_line_readonly(tl->layout, line); + + if (end > (pll->start_index + pll->length)) + end = pll->start_index + pll->length; + pango_layout_index_to_pos(tl->layout, end, &endRect); + + r->X = pangoToCairo(startRect.x); + r->Y = tl->lineMetrics[line].Y; + r->Width = pangoToCairo(endRect.x) - r->X; + r->Height = tl->lineMetrics[line].Height; + + // and figure out the correct start pos + pango_layout_line_x_to_index(pll, startRect.x, + &index, NULL); + r->RealStart = index; + r->RealEnd = end; + + // TODO unref pll? } From 3d8bf019726d76a53116f7a5a17cce4cab782f0d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 6 Feb 2017 10:30:26 -0500 Subject: [PATCH 0591/1329] And implemented the hit-testing functions on GTK+. --- unix/drawtext.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/unix/drawtext.c b/unix/drawtext.c index 7c1f13e4..b0d44833 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -4,6 +4,7 @@ // TODO // - if the RTL override is at the beginning of a line, the preceding space is included? +// - the space at the end of a line seems to capture the entire end of that line, especially in hit-testing :S it should work like it does on other platforms struct uiDrawTextLayout { PangoLayout *layout; @@ -222,7 +223,7 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTex void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r) { PangoRectangle startRect, endRect; - int line; + int line, index; PangoLayoutLine *pll; pango_layout_index_to_pos(tl->layout, start, &startRect); From e4ed1c337ba62a99e8eb3438c57ffba818bbe8f6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 6 Feb 2017 18:38:44 -0500 Subject: [PATCH 0592/1329] And implemented the functions on Windows. Yeah I think I'll need cursor functions, perhaps. --- examples/drawtext/hittest.c | 6 ++- windows/drawtext.cpp | 100 +++++++++++++++++++++++++++++++++--- 2 files changed, 98 insertions(+), 8 deletions(-) diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index efa80907..cacde315 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -160,8 +160,10 @@ static void mouse(uiAreaMouseEvent *e) &res); uiDrawFreeTextLayout(layout); - sprintf(labelText, "pos %zd line %d x position %s y position %s", - res.Pos, res.Line, + // urgh %zd is not supported by MSVC with sprintf() + // TODO get that part in test/ about having no other option + sprintf(labelText, "pos %d line %d x position %s y position %s", + (int) (res.Pos), res.Line, positions[res.XPosition], positions[res.YPosition]); uiLabelSetText(caretLabel, labelText); diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 345723ce..ab63c0e1 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -5,15 +5,18 @@ // TODO // - consider the warnings about antialiasing in the PadWrite sample // - if that's not a problem, do we have overlapping rects in the hittest sample? I can't tell... +// - what happens if any nLines == 0? struct uiDrawTextLayout { IDWriteTextFormat *format; IDWriteTextLayout *layout; UINT32 nLines; struct lineInfo *lineInfo; - // for converting DirectWrite indices to byte offsets + // for converting DirectWrite indices from/to byte offsets + size_t *u8tou16; + size_t nUTF8; size_t *u16tou8; - size_t nu16tou8; // TODO I don't like the casing of this name; is it even necessary? + size_t nUTF16; }; // TODO copy notes about DirectWrite DIPs being equal to Direct2D DIPs here @@ -43,7 +46,7 @@ static std::map dwriteStretches = { }; struct lineInfo { - size_t startPos; + size_t startPos; // in UTF-16 points size_t endPos; size_t newlineCount; double x; @@ -182,8 +185,9 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto computeLineInfo(tl); - // and finally copy the UTF-16 to UTF-8 index conversion table - tl->u16tou8 = attrstrCopyUTF16ToUTF8(s, &(tl->nu16tou8)); + // and finally copy the UTF-8/UTF-16 index conversion tables + tl->u8tou16 = attrstrCopyUTF8ToUTF16(s, &(tl->nUTF8)); + tl->u16tou8 = attrstrCopyUTF16ToUTF8(s, &(tl->nUTF16)); // TODO can/should this be moved elsewhere? uiFree(wDefaultFamily); @@ -193,6 +197,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { uiFree(tl->u16tou8); + uiFree(tl->u8tou16); uiFree(tl->lineInfo); tl->layout->Release(); tl->format->Release(); @@ -294,11 +299,94 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa m->ParagraphSpacing = 0; // TODO } +// TODO stretches space at the end of a line to the whole line void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) { - // TODO + DWRITE_HIT_TEST_METRICS m; + size_t line; + double width, height; + BOOL trailing, inside; // crashes if I skip these :/ + HRESULT hr; + + hr = tl->layout->HitTestPoint(x, y, + &trailing, &inside, + &m); + if (hr != S_OK) + logHRESULT(L"error hit-testing IDWriteTextLayout", hr); + + // figure out what line this is + if (y < 0) + line = 0; + else + for (line = 0; line < tl->nLines; line++) + if (y >= tl->lineInfo[line].y && y < (tl->lineInfo[line].y + tl->lineInfo[line].height)) + break; + if (line == tl->nLines) // last position + line--; + + result->Pos = tl->u16tou8[m.textPosition]; + result->Line = line; + + uiDrawTextLayoutExtents(tl, &width, &height); + result->XPosition = uiDrawTextLayoutHitTestPositionInside; + if (x < 0) + result->XPosition = uiDrawTextLayoutHitTestPositionBefore; + else if (x >= width) + result->XPosition = uiDrawTextLayoutHitTestPositionAfter; + result->YPosition = uiDrawTextLayoutHitTestPositionInside; + if (y < 0) + result->YPosition = uiDrawTextLayoutHitTestPositionBefore; + else if (y >= height) + result->YPosition = uiDrawTextLayoutHitTestPositionAfter; } void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r) { + DWRITE_HIT_TEST_METRICS mstart, mend; + size_t line; + FLOAT x, y; // crashes if I skip these :/ + BOOL trailing, inside; // crashes if I skip these :/ + HRESULT hr; + + start = tl->u8tou16[start]; + end = tl->u8tou16[end]; + + // TODO explain why this is a leading hit + hr = tl->layout->HitTestTextPosition(start, FALSE, + &x, &y, + &mstart); + if (hr != S_OK) + logHRESULT(L"error getting rect of start position", hr); + + // figure out what line this is + for (line = 0; line < tl->nLines; line++) + if (start >= tl->lineInfo[line].startPos && start < tl->lineInfo[line].endPos) + break; + if (line == tl->nLines) // last position + line--; + if (end > tl->lineInfo[line].endPos) + end = tl->lineInfo[line].endPos; + + hr = tl->layout->HitTestTextPosition(end, FALSE, + &x, &y, + &mend); + if (hr != S_OK) + logHRESULT(L"error getting rect of end position", hr); + + r->X = mstart.left; + r->Y = tl->lineInfo[line].y; + r->Width = mend.left - mstart.left; + r->Height = tl->lineInfo[line].height; + + hr = tl->layout->HitTestPoint(r->X, r->Y, + &trailing, &inside, + &mstart); + if (hr != S_OK) + logHRESULT(L"TODO write this", hr); + // TODO also get the end pos just so we can have an accurate r->RealEnd + r->RealStart = mstart.textPosition; + r->RealEnd = end; + + r->RealStart = tl->u16tou8[r->RealStart]; + r->RealEnd = tl->u16tou8[r->RealEnd]; } From ac9aefc43a0c84885f5704db6bac1e426900386c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 7 Feb 2017 15:29:12 -0500 Subject: [PATCH 0593/1329] Set up a new API specifically for caret positioning when hit-testing a point. Not yet implemented, just planned out. --- ui_attrstr.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ui_attrstr.h b/ui_attrstr.h index 8ca835ea..9d71f7d9 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -132,6 +132,12 @@ struct uiDrawTextLayoutHitTestResult { int Line; uiDrawTextLayoutHitTestPosition XPosition; uiDrawTextLayoutHitTestPosition YPosition; + + int CaretLine; + double CaretX; + double CaretY; + // CaretWidth is decided by uiDrawCaret(). + double CaretHeight; // TODO? // int InTrailingWhitespace; // TODO? From a5dac818555e454eff1d335c89b174d6f6e30a2c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 7 Feb 2017 16:21:18 -0500 Subject: [PATCH 0594/1329] Refined the hit-test API some more. --- ui_attrstr.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index 9d71f7d9..f82a0a63 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -128,12 +128,20 @@ _UI_ENUM(uiDrawTextLayoutHitTestPosition) { }; struct uiDrawTextLayoutHitTestResult { + // The byte position of the character at the given point. size_t Pos; + // Line is the line at the given point. If the point is on the space + // after the end of a line, Pos will be Line's end index. In this case, + // the rectangle for that character will actually be on the *next* + // line. This disparity is only relevant for caret positioning when + // using the mouse; in that case, to ensure proper behavior, use + // the Caret fields below. In all other cases (including keyboard + // caret movement), use the actual rectangle for the grapheme + // at Pos (via uiDrawTextLayoutByteRangeToRectangle()). int Line; uiDrawTextLayoutHitTestPosition XPosition; uiDrawTextLayoutHitTestPosition YPosition; - int CaretLine; double CaretX; double CaretY; // CaretWidth is decided by uiDrawCaret(). From bbc03a489edd108d73c7a43cb22ebbb44260d76f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 7 Feb 2017 16:43:49 -0500 Subject: [PATCH 0595/1329] Implemented the system on the hit-test example and on OS X. --- darwin/drawtext.m | 8 ++++++++ examples/drawtext/hittest.c | 28 +++++++++++++++++++++------- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 452619f5..7364088e 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -328,6 +328,14 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTex // TODO } result->Pos = tl->u16tou8[pos]; + + // desired caret behavior: clicking on the right trailing space places inserted text at the start of the next line but the caret is on the clicked line at the trailing edge of the last grapheme + // hitting Right moves to the second point of the next line, then Left to go to the first, then Left to go to the start of the last character of the original line (so the keyboard can't move the caret back to that clicked space) + // this happens regardless of word-wrapping on whitespace, hyphens, or very long words + // use CTLineGetOffsetForStringIndex() here instead of x directly as that isn't always adjusted properly + result->CaretX = CTLineGetOffsetForStringIndex(line, pos, NULL); + result->CaretY = tl->lineMetrics[i].Y; + result->CaretHeight = tl->lineMetrics[i].Height; } // TODO document this is appropriate for a caret diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index cacde315..364dd1ca 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -33,7 +33,9 @@ static uiBox *panel; static uiLabel *caretLabel; static uiCheckbox *showLineBounds; -static size_t cursorPos; +static int caretInit = 0; +// TODO rename all this to caret, as well as in the text above +static double cursorX, cursorY, cursorHeight; // TODO should be const? static uiDrawBrush fillBrushes[4] = { @@ -82,7 +84,6 @@ static void draw(uiAreaDrawParams *p) { uiDrawPath *path; uiDrawTextLayout *layout; - uiDrawTextLayoutByteRangeRectangle r; uiDrawBrush brush; // only clip the text, not the guides @@ -103,10 +104,21 @@ static void draw(uiAreaDrawParams *p) uiDrawRestore(p->Context); - uiDrawTextLayoutByteRangeToRectangle(layout, cursorPos, cursorPos, &r); + if (!caretInit) { + uiDrawTextLayoutByteRangeRectangle r; + + uiDrawTextLayoutByteRangeToRectangle(layout, + uiAttributedStringLen(attrstr), + uiAttributedStringLen(attrstr), + &r); + cursorX = r.X; + cursorY = r.Y; + cursorHeight = r.Height; + caretInit = 1; + } path = uiDrawNewPath(uiDrawFillModeWinding); - uiDrawPathNewFigure(path, margins + r.X, margins + r.Y); - uiDrawPathLineTo(path, margins + r.X, margins + r.Y + r.Height); + uiDrawPathNewFigure(path, margins + cursorX, margins + cursorY); + uiDrawPathLineTo(path, margins + cursorX, margins + cursorY + cursorHeight); uiDrawPathEnd(path); brush.Type = uiDrawBrushTypeSolid; brush.R = 0.0; @@ -162,13 +174,16 @@ static void mouse(uiAreaMouseEvent *e) // urgh %zd is not supported by MSVC with sprintf() // TODO get that part in test/ about having no other option + // TODO byte 1 is actually byte 684?! sprintf(labelText, "pos %d line %d x position %s y position %s", (int) (res.Pos), res.Line, positions[res.XPosition], positions[res.YPosition]); uiLabelSetText(caretLabel, labelText); - cursorPos = res.Pos; + cursorX = res.CaretX; + cursorY = res.CaretY; + cursorHeight = res.CaretHeight; redraw(); } @@ -203,7 +218,6 @@ struct example *mkHitTestExample(void) hitTestExample.mouse = mouse; attrstr = uiNewAttributedString(text); - cursorPos = uiAttributedStringLen(attrstr); return &hitTestExample; } From 01b6a16af6bda2436090f83b06610e6e864d4ea4 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 7 Feb 2017 19:14:51 -0500 Subject: [PATCH 0596/1329] And handled caret behavior on GTK+. --- unix/drawtext.c | 21 +++++++++++++++++---- unix/multilineentry.c | 2 ++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/unix/drawtext.c b/unix/drawtext.c index b0d44833..88cc8726 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -4,7 +4,6 @@ // TODO // - if the RTL override is at the beginning of a line, the preceding space is included? -// - the space at the end of a line seems to capture the entire end of that line, especially in hit-testing :S it should work like it does on other platforms struct uiDrawTextLayout { PangoLayout *layout; @@ -68,6 +67,7 @@ static void computeLineMetrics(uiDrawTextLayout *tl) m->X = pangoToCairo(lineStartPos.x); // TODO fix the whole combined not being updated shenanigans in the static build (here because ugh) m->Y = pangoToCairo(baselineY - PANGO_ASCENT(lineExtents)); + // TODO this does not include the last space if any m->Width = pangoToCairo(lineExtents.width); m->Height = pangoToCairo(lineExtents.height); @@ -187,11 +187,19 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa } #endif +// caret behavior, based on GtkTextView (both 2 and 3) and Qt's text views (both 4 and 5), which are all based on TkTextView: +// - clicking on the right side of a line places the cursor at the beginning of the LAST grapheme on the line +// - pressing Right goes to the first grapheme of the next line, pressing Left goes back to the last grapheme of the original line +// - as such, there is *no* way to get the cursor on the end of the line +// - this includes whitespace, hyphens, and character wraps +// - spaces get special treatment (it seems): you don't see them there and they don't show up if you select one and only one! +// - and the best part is that word processors don't typically do this; they do what other platforms do! void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) { int index; int trailing; int line; + int caretX; double width, height; // disregard the return value; we do our own bounds checking @@ -199,9 +207,10 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTex pango_layout_xy_to_index(tl->layout, cairoToPango(x), cairoToPango(y), &index, &trailing); - // figure out what line that was + // figure out what line that was, and also the caret X position since we can get that here too + // TODO should we use the dedicated function instead? pango_layout_index_to_line_x(tl->layout, index, trailing, - &line, NULL); + &line, &caretX); result->Pos = index; result->Line = line; @@ -216,9 +225,13 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTex result->YPosition = uiDrawTextLayoutHitTestPositionBefore; else if (y >= height) result->YPosition = uiDrawTextLayoutHitTestPositionAfter; + + result->CaretX = pangoToCairo(caretX); + result->CaretY = tl->lineMetrics[line].Y; + result->CaretHeight = tl->lineMetrics[line].Height; } -// TODO consider providing a uiDrawTextLayoutByteIndexToCursorPos() function +// TODO consider providing a uiDrawTextLayoutByteIndexToCursorPos() function for manual positions by byte index only? // TODO is this correct for RTL? void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r) { diff --git a/unix/multilineentry.c b/unix/multilineentry.c index 09ffd460..228dc80f 100644 --- a/unix/multilineentry.c +++ b/unix/multilineentry.c @@ -1,6 +1,8 @@ // 6 december 2015 #include "uipriv_unix.h" +// TODO GTK_WRAP_WORD_CHAR to avoid spurious resizes? + struct uiMultilineEntry { uiUnixControl c; GtkWidget *widget; From 3e4f99e0dd87ad4de90e2b3cc562ab6e9a20fa5b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 7 Feb 2017 19:42:00 -0500 Subject: [PATCH 0597/1329] Notes for Windows equivalent of previous commits. Not actually done yet. --- windows/drawtext.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index ab63c0e1..f87acd4b 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -107,6 +107,7 @@ static void computeLineInfo(uiDrawTextLayout *tl) // TODO verify htm.textPosition and htm.length against dtm[i]/tl->lineInfo[i]? tl->lineInfo[i].x = htm[0].left; tl->lineInfo[i].y = htm[0].top; + // TODO does this not include trailing whitespace? I forget tl->lineInfo[i].width = htm[0].width; tl->lineInfo[i].height = htm[0].height; for (j = 1; j < nFragments; j++) { @@ -299,7 +300,10 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa m->ParagraphSpacing = 0; // TODO } -// TODO stretches space at the end of a line to the whole line +// expected behavior on end of line: +// - same as OS X +// - TODO RETEST THIS ON OTHER PLATFORMS: EXCEPT: if you type something, then backspace, the cursor will move back to where you clicked! +// TODO this function does NOT yet work like that; it works like the Unix equivalent right now void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) { DWRITE_HIT_TEST_METRICS m; From 012ce92b713d5fb1662db3417f0274a88754eb53 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 7 Feb 2017 19:55:21 -0500 Subject: [PATCH 0598/1329] And implemented the caret changes on Windows. --- windows/drawtext.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index f87acd4b..30114432 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -342,6 +342,10 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTex result->YPosition = uiDrawTextLayoutHitTestPositionBefore; else if (y >= height) result->YPosition = uiDrawTextLayoutHitTestPositionAfter; + + result->CaretX = m.left; // TODO is this correct? + result->CaretY = tl->lineInfo[line].y; + result->CaretHeight = tl->lineInfo[line].height; } void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r) From 93537ebb83b64062ef531994cac62abf422c6b1b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 8 Feb 2017 11:38:52 -0500 Subject: [PATCH 0599/1329] Simpler hit-testing APIs based on Core Text's. Microsoft's PadWrite sample shows us how to implement these on both DirectWrite and the similarly-interfaced Pango. --- ui_attrstr.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index f82a0a63..1dd04435 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -180,8 +180,12 @@ _UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, dou _UI_EXTERN int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl); _UI_EXTERN void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end); _UI_EXTERN void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m); -_UI_EXTERN void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result); -_UI_EXTERN void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r); +//TODO _UI_EXTERN void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result); +//TODO _UI_EXTERN void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r); // TODO draw only a line? // TODO other layout-specific attributes (alignment, wrapping, etc.)? // TODO number of lines visible for clipping rect, range visible for clipping rect? + +_UI_EXTERN void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *pos, int *line); +// TODO line first? +_UI_EXTERN void uiDrawTextLayoutByteLocation(uiDrawTextLayout *tl, size_t pos, double *x, int *line); From 749a0cddaf4d16e17301948b9f6f6dcb1156bc86 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 8 Feb 2017 19:00:14 -0500 Subject: [PATCH 0600/1329] Wrote the new hit-testing API on OS X, the easiest target. Also updated the example. Had to slightly modify one function for this all to work. --- darwin/drawtext.m | 47 +++++++++++++++++++++++++++++++++++++ examples/drawtext/hittest.c | 40 +++++++++++++++---------------- ui_attrstr.h | 3 +-- 3 files changed, 67 insertions(+), 23 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 7364088e..53763ce3 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -280,6 +280,8 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa *m = tl->lineMetrics[line]; } +#if 0 /* TODO */ + // TODO note that in some cases lines can overlap slightly // in our case, we read lines first to last and use their bottommost point (Y + Height) to determine where the next line should start for hit-testing void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) @@ -381,3 +383,48 @@ void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, si r->RealStart = tl->u16tou8[r->RealStart]; r->RealEnd = tl->u16tou8[r->RealEnd]; } + +#endif + +// TODO note that in some cases lines can overlap slightly +// in our case, we read lines first to last and use their bottommost point (Y + Height) to determine where the next line should start for hit-testing +void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *pos, int *line) +{ + int i; + CTLineRef ln; + CFIndex p; + + for (i = 0; i < tl->nLines; i++) { + double ltop, lbottom; + + ltop = tl->lineMetrics[i].Y; + lbottom = ltop + tl->lineMetrics[i].Height; + // y will already >= ltop at this point since the past lbottom should == (or at least >=, see above) ltop + if (y < lbottom) + break; + } + if (i == tl->nLines) + i--; + if (line != NULL) + *line = i; + + // TODO do the hit-test unconditionally? + if (pos != NULL) { + ln = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, i); + p = CTLineGetStringIndexForPosition(ln, CGPointMake(x, 0)); + if (p == kCFNotFound) { + // TODO + } + *pos = tl->u16tou8[p]; + } +} + +double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line) +{ + CTLineRef ln; + + ln = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, line); + // TODO what happens if the byte location is not in this line? a return of 0? + // TODO check return? see if this can return 0, anyway, and if so make a note I guess + return CTLineGetOffsetForStringIndex(ln, tl->u8tou16[pos], NULL); +} diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index 364dd1ca..4d7098d2 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -33,9 +33,8 @@ static uiBox *panel; static uiLabel *caretLabel; static uiCheckbox *showLineBounds; -static int caretInit = 0; -// TODO rename all this to caret, as well as in the text above -static double cursorX, cursorY, cursorHeight; +static int caretLine = -1; +static double caretX; // TODO should be const? static uiDrawBrush fillBrushes[4] = { @@ -84,6 +83,7 @@ static void draw(uiAreaDrawParams *p) { uiDrawPath *path; uiDrawTextLayout *layout; + uiDrawTextLayoutLineMetrics m; uiDrawBrush brush; // only clip the text, not the guides @@ -104,21 +104,16 @@ static void draw(uiAreaDrawParams *p) uiDrawRestore(p->Context); - if (!caretInit) { - uiDrawTextLayoutByteRangeRectangle r; - - uiDrawTextLayoutByteRangeToRectangle(layout, + if (caretLine == -1) { + caretLine = uiDrawTextLayoutNumLines(layout) - 1; + caretX = uiDrawTextLayoutByteLocationInLine(layout, uiAttributedStringLen(attrstr), - uiAttributedStringLen(attrstr), - &r); - cursorX = r.X; - cursorY = r.Y; - cursorHeight = r.Height; - caretInit = 1; + caretLine); } + uiDrawTextLayoutLineGetMetrics(layout, caretLine, &m); path = uiDrawNewPath(uiDrawFillModeWinding); - uiDrawPathNewFigure(path, margins + cursorX, margins + cursorY); - uiDrawPathLineTo(path, margins + cursorX, margins + cursorY + cursorHeight); + uiDrawPathNewFigure(path, margins + caretX, margins + m.Y); + uiDrawPathLineTo(path, margins + caretX, margins + m.Y + m.Height); uiDrawPathEnd(path); brush.Type = uiDrawBrushTypeSolid; brush.R = 0.0; @@ -129,7 +124,6 @@ static void draw(uiAreaDrawParams *p) uiDrawFreePath(path); if (uiCheckboxChecked(showLineBounds)) { - uiDrawTextLayoutLineMetrics m; int i, n; int fill = 0; @@ -158,7 +152,8 @@ static const char *positions[] = { static void mouse(uiAreaMouseEvent *e) { uiDrawTextLayout *layout; - uiDrawTextLayoutHitTestResult res; +// uiDrawTextLayoutHitTestResult res; + size_t pos; char labelText[128]; if (e->Down != 1) @@ -169,22 +164,25 @@ static void mouse(uiAreaMouseEvent *e) e->AreaWidth - 2 * margins); uiDrawTextLayoutHitTest(layout, e->X - margins, e->Y - margins, - &res); +// &res); + &pos, &caretLine); + caretX = uiDrawTextLayoutByteLocationInLine(layout, pos, caretLine); uiDrawFreeTextLayout(layout); // urgh %zd is not supported by MSVC with sprintf() // TODO get that part in test/ about having no other option // TODO byte 1 is actually byte 684?! - sprintf(labelText, "pos %d line %d x position %s y position %s", +/* sprintf(labelText, "pos %d line %d x position %s y position %s", (int) (res.Pos), res.Line, positions[res.XPosition], positions[res.YPosition]); +*/ sprintf(labelText, "TODO\n"); uiLabelSetText(caretLabel, labelText); - cursorX = res.CaretX; +/* cursorX = res.CaretX; cursorY = res.CaretY; cursorHeight = res.CaretHeight; - redraw(); +*/ redraw(); } static struct example hitTestExample; diff --git a/ui_attrstr.h b/ui_attrstr.h index 1dd04435..e96a1019 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -187,5 +187,4 @@ _UI_EXTERN void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, u // TODO number of lines visible for clipping rect, range visible for clipping rect? _UI_EXTERN void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *pos, int *line); -// TODO line first? -_UI_EXTERN void uiDrawTextLayoutByteLocation(uiDrawTextLayout *tl, size_t pos, double *x, int *line); +_UI_EXTERN double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line); From 5458e10134e82ff8c51a009ae2ae305990316ecb Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 8 Feb 2017 20:10:34 -0500 Subject: [PATCH 0601/1329] Implemented the new hit-test functions on Windows. --- windows/drawtext.cpp | 75 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 30114432..29117923 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -300,6 +300,8 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa m->ParagraphSpacing = 0; // TODO } +#if 0 /* TODO */ + // expected behavior on end of line: // - same as OS X // - TODO RETEST THIS ON OTHER PLATFORMS: EXCEPT: if you type something, then backspace, the cursor will move back to where you clicked! @@ -398,3 +400,76 @@ void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, si r->RealStart = tl->u16tou8[r->RealStart]; r->RealEnd = tl->u16tou8[r->RealEnd]; } + +#endif + +// this algorithm comes from Microsoft's PadWrite sample, following TextEditor::SetSelectionFromPoint() +void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *pos, int *line) +{ + DWRITE_HIT_TEST_METRICS m; + BOOL trailing, inside; + size_t p; + HRESULT hr; + + hr = tl->layout->HitTestPoint(x, y, + &trailing, &inside, + &m); + if (hr != S_OK) + logHRESULT(L"error hit-testing IDWriteTextLayout", hr); + p = m.textPosition; + // on a trailing hit, align to the nearest cluster + if (trailing) { + DWRITE_HIT_TEST_METRICS m2; + FLOAT x, y; // crashes if I skip these :/ + + hr = tl->layout->HitTestTextPosition(m.textPosition, trailing, + &x, &y, &m2); + if (hr != S_OK) + logHRESULT(L"error aligning trailing hit to nearest cluster", hr); + p = m2.textPosition + m2.length; + } + // TODO should we just make the pointers required? + if (pos != NULL) + *pos = tl->u16tou8[p]; + + // TODO do the line detection unconditionally? + if (line != NULL) { + UINT32 i; + + for (i = 0; i < tl->nLines; i++) { + double ltop, lbottom; + + ltop = tl->lineInfo[i].y; + lbottom = ltop + tl->lineInfo[i].height; + // y will already >= ltop at this point since the past lbottom should == ltop + if (y < lbottom) + break; + } + if (i == tl->nLines) + i--; + *line = i; + } +} + +double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line) +{ + BOOL trailing; + DWRITE_HIT_TEST_METRICS m; + FLOAT x, y; + HRESULT hr; + + pos = tl->u8tou16[pos]; + // this behavior seems correct + // there's also PadWrite's TextEditor::GetCaretRect() but that requires state... + // TODO where does this fail? + trailing = FALSE; + if (pos != 0 && pos != tl->nUTF16 && pos == tl->lineInfo[line].endPos) { + pos--; + trailing = TRUE; + } + hr = tl->layout->HitTestTextPosition(pos, trailing, + &x, &y, &m); + if (hr != S_OK) + logHRESULT(L"error calling IDWriteTextLayout::HitTestTextPosition()", hr); + return x; +} From d53bc88f50c088c55a0b0f7eee1b17303f696686 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 8 Feb 2017 21:19:49 -0500 Subject: [PATCH 0602/1329] And filled in the new functions on GTK+. Not quite right yet... --- examples/drawtext/hittest.c | 9 +++--- unix/drawtext.c | 62 +++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 4 deletions(-) diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index 4d7098d2..04daaa33 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -3,6 +3,7 @@ // TODO have a ligature // TODO allow clicking on the end of a line to put the caret there +// TODO the hiding and showing does not work properly on GTK+ static const char *text = "Each of the glyphs an end user interacts with are called graphemes. " @@ -172,12 +173,12 @@ static void mouse(uiAreaMouseEvent *e) // urgh %zd is not supported by MSVC with sprintf() // TODO get that part in test/ about having no other option // TODO byte 1 is actually byte 684?! -/* sprintf(labelText, "pos %d line %d x position %s y position %s", - (int) (res.Pos), res.Line, + sprintf(labelText, "pos %d line %d x %g",// x position %s y position %s", + (int) pos, caretLine, caretX); +/* (int) (res.Pos), res.Line, positions[res.XPosition], positions[res.YPosition]); -*/ sprintf(labelText, "TODO\n"); - uiLabelSetText(caretLabel, labelText); +*/ uiLabelSetText(caretLabel, labelText); /* cursorX = res.CaretX; cursorY = res.CaretY; diff --git a/unix/drawtext.c b/unix/drawtext.c index 88cc8726..879cace9 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -187,6 +187,8 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa } #endif +#if 0 /* TODO */ + // caret behavior, based on GtkTextView (both 2 and 3) and Qt's text views (both 4 and 5), which are all based on TkTextView: // - clicking on the right side of a line places the cursor at the beginning of the LAST grapheme on the line // - pressing Right goes to the first grapheme of the next line, pressing Left goes back to the last grapheme of the original line @@ -264,3 +266,63 @@ void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, si // TODO unref pll? } + +#endif + +// this algorithm comes from Microsoft's PadWrite sample, following TextEditor::SetSelectionFromPoint() +// TODO this or the next one doesn't work right for the end of a line? it still behaves like TkTextView... +void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *pos, int *line) +{ + int p, trailing; + + pango_layout_xy_to_index(tl->layout, + cairoToPango(x), cairoToPango(y), + &p, &trailing); + // on a trailing hit, align to the nearest cluster + // fortunately Pango provides that info directly + if (trailing != 0) + p += trailing; + if (pos != NULL) + *pos = p; + + // TODO do the line detection unconditionally? + // TODO optimize the call to pango_layout_get_line_count() + if (line != NULL) { + int i; + + for (i = 0; i < pango_layout_get_line_count(tl->layout); i++) { + double ltop, lbottom; + + ltop = tl->lineMetrics[i].Y; + lbottom = ltop + tl->lineMetrics[i].Height; + // y will already >= ltop at this point since the past lbottom should == ltop + if (y < lbottom) + break; + } + if (i == pango_layout_get_line_count(tl->layout)) + i--; + *line = i; + } +} + +// TODO find a good API for indicating that this character isn't on the line for when the layout is resized and doing that recalculation... +double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line) +{ + PangoLayoutLine *pll; + gboolean trailing; + int pangox; + + pll = pango_layout_get_line_readonly(tl->layout, line); + // this behavior seems correct + // there's also PadWrite's TextEditor::GetCaretRect() but that requires state... + // TODO where does this fail? + // TODO optimize everything to avoid calling strlen() + trailing = 0; + if (pos != 0 && pos != strlen(pango_layout_get_text(tl->layout)) && pos == (pll->start_index + pll->length)) { + pos--; + trailing = 1; + } + pango_layout_line_index_to_x(pll, pos, trailing, &pangox); + // TODO unref pll? + return pangoToCairo(pangox); +} From b96114e02d51d1e84b732e1cad9da4a41f4d19a6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 8 Feb 2017 21:52:52 -0500 Subject: [PATCH 0603/1329] Fixed the hit-test example to manage the cursor more sanely. --- examples/drawtext/hittest.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index 04daaa33..f655fb74 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -35,7 +35,8 @@ static uiLabel *caretLabel; static uiCheckbox *showLineBounds; static int caretLine = -1; -static double caretX; +static size_t caretPos; +//static double caretX; // TODO should be const? static uiDrawBrush fillBrushes[4] = { @@ -86,6 +87,7 @@ static void draw(uiAreaDrawParams *p) uiDrawTextLayout *layout; uiDrawTextLayoutLineMetrics m; uiDrawBrush brush; + double caretX; // only clip the text, not the guides uiDrawSave(p->Context); @@ -107,10 +109,10 @@ static void draw(uiAreaDrawParams *p) if (caretLine == -1) { caretLine = uiDrawTextLayoutNumLines(layout) - 1; - caretX = uiDrawTextLayoutByteLocationInLine(layout, - uiAttributedStringLen(attrstr), - caretLine); + caretPos = uiAttributedStringLen(attrstr); } + caretX = uiDrawTextLayoutByteLocationInLine(layout, + caretPos, caretLine); uiDrawTextLayoutLineGetMetrics(layout, caretLine, &m); path = uiDrawNewPath(uiDrawFillModeWinding); uiDrawPathNewFigure(path, margins + caretX, margins + m.Y); @@ -154,7 +156,6 @@ static void mouse(uiAreaMouseEvent *e) { uiDrawTextLayout *layout; // uiDrawTextLayoutHitTestResult res; - size_t pos; char labelText[128]; if (e->Down != 1) @@ -166,15 +167,15 @@ static void mouse(uiAreaMouseEvent *e) uiDrawTextLayoutHitTest(layout, e->X - margins, e->Y - margins, // &res); - &pos, &caretLine); - caretX = uiDrawTextLayoutByteLocationInLine(layout, pos, caretLine); + &caretPos, &caretLine); +// caretX = uiDrawTextLayoutByteLocationInLine(layout, pos, caretLine); uiDrawFreeTextLayout(layout); // urgh %zd is not supported by MSVC with sprintf() // TODO get that part in test/ about having no other option // TODO byte 1 is actually byte 684?! - sprintf(labelText, "pos %d line %d x %g",// x position %s y position %s", - (int) pos, caretLine, caretX); + sprintf(labelText, "pos %d line %d",// x %g",// x position %s y position %s", + (int) caretPos, caretLine);//, caretX); /* (int) (res.Pos), res.Line, positions[res.XPosition], positions[res.YPosition]); From 92d996f1486e71e19356fc304e0b73d677abe2bc Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 9 Feb 2017 19:20:35 -0500 Subject: [PATCH 0604/1329] Okay so I can't fix Pango as the behavior is hardcoded in (https://git.gnome.org/browse/pango/tree/pango/pango-layout.c?id=f4cbd27f4e5bf8490ea411190d41813e14f12165#n4204); just write some documentation and get rid of the old APIs entirely. --- ui_attrstr.h | 46 ++++++++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index e96a1019..c2ded0d0 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -121,12 +121,6 @@ struct uiDrawTextLayoutLineMetrics { // TODO trailing whitespace? }; -_UI_ENUM(uiDrawTextLayoutHitTestPosition) { - uiDrawTextLayoutHitTestPositionBefore, - uiDrawTextLayoutHitTestPositionInside, - uiDrawTextLayoutHitTestPositionAfter, -}; - struct uiDrawTextLayoutHitTestResult { // The byte position of the character at the given point. size_t Pos; @@ -142,10 +136,6 @@ struct uiDrawTextLayoutHitTestResult { uiDrawTextLayoutHitTestPosition XPosition; uiDrawTextLayoutHitTestPosition YPosition; - double CaretX; - double CaretY; - // CaretWidth is decided by uiDrawCaret(). - double CaretHeight; // TODO? // int InTrailingWhitespace; // TODO? @@ -155,16 +145,6 @@ struct uiDrawTextLayoutHitTestResult { // or just have offsets instead? in addition? }; -struct uiDrawTextLayoutByteRangeRectangle { - int Line; - double X; - double Y; - double Width; - double Height; - size_t RealStart; - size_t RealEnd; -}; - // TODO // - allow creating a layout out of a substring // - allow marking compositon strings @@ -173,6 +153,7 @@ struct uiDrawTextLayoutByteRangeRectangle { // - uiDrawTextLayoutHeightForWidth() (returns the height that a layout would need to be to display the entire string at a given width) // - uiDrawTextLayoutRangeForSize() (returns what substring would fit in a given size) // - uiDrawTextLayoutNewWithHeight() (limits amount of string used by the height) +// - some function to fix up a range (for text editing) _UI_EXTERN uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescriptor *defaultFont, double width); _UI_EXTERN void uiDrawFreeTextLayout(uiDrawTextLayout *tl); _UI_EXTERN void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y); @@ -180,11 +161,28 @@ _UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, dou _UI_EXTERN int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl); _UI_EXTERN void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end); _UI_EXTERN void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m); -//TODO _UI_EXTERN void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result); -//TODO _UI_EXTERN void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r); -// TODO draw only a line? -// TODO other layout-specific attributes (alignment, wrapping, etc.)? // TODO number of lines visible for clipping rect, range visible for clipping rect? +// TODO rewrite all this documentation + +// uiDrawTextLayoutHitTest() returns the byte offset and line closest +// to the given point. The point is relative to the top-left of the layout. +// If the point is outside the layout itself, the closest point is chosen; +// this allows the function to be used for cursor positioning with the +// mouse. Do keep the returned line in mind if used in this way; the +// user might click on the end of a line, at which point the cursor +// might be at the trailing edge of the last grapheme on the line +// (subject to the operating system's APIs). _UI_EXTERN void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *pos, int *line); + +// uiDrawTextLayoutByteLocationInLine() returns the point offset +// into the given line that the given byte position stands. This is +// relative to the line's X position (as returned by +// uiDrawTextLayoutLineGetMetrics()), which in turn is relative to +// the top-left of the layout. This function can be used for cursor +// positioning: if start and end are the start and end of the line +// (as returned by uiDrawTextLayoutLineByteRange()), you will get +// the correct offset, even if pos is at the end of the line. If pos is not +// in the range [start, end], a negative value will be returned, +// indicating you need to move the cursor to another line. _UI_EXTERN double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line); From 49d36b340c4ece8e9cf5dd1b073bbf98a4e0eb2c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Feb 2017 10:54:37 -0500 Subject: [PATCH 0605/1329] Started adjusting all the implementations to the new API's formal definition. There's bugs in uiAttributedString... --- darwin/drawtext.m | 20 +++++++++++++++----- examples/drawtext/hittest.c | 7 +------ ui_attrstr.h | 27 +-------------------------- 3 files changed, 17 insertions(+), 37 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 53763ce3..82b99ac3 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -421,10 +421,20 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *p double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line) { - CTLineRef ln; + CTLineRef lr; + CFRange range; - ln = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, line); - // TODO what happens if the byte location is not in this line? a return of 0? - // TODO check return? see if this can return 0, anyway, and if so make a note I guess - return CTLineGetOffsetForStringIndex(ln, tl->u8tou16[pos], NULL); +printf("= %zd %zd ", pos, tl->nUTF8); + pos = tl->u8tou16[pos]; +printf("-> %zd %zd\n", pos, tl->nUTF16); + if (line < 0 || line >= tl->nLines) + return -1; + lr = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, line); + range = CTLineGetStringRange(lr); + // note: >, not >=, because the position at end is valid! +printf("%zd %zd\n", pos, (size_t)(range.location+range.length)); + if (pos < range.location || pos > (range.location + range.length)) + return -1; + // no point in checking the return; we already validated everything and 0 is a valid return for the first index :/ + return CTLineGetOffsetForStringIndex(lr, pos, NULL); } diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index f655fb74..0ae2df5d 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -113,6 +113,7 @@ static void draw(uiAreaDrawParams *p) } caretX = uiDrawTextLayoutByteLocationInLine(layout, caretPos, caretLine); +printf("%g\n", caretX); uiDrawTextLayoutLineGetMetrics(layout, caretLine, &m); path = uiDrawNewPath(uiDrawFillModeWinding); uiDrawPathNewFigure(path, margins + caretX, margins + m.Y); @@ -146,12 +147,6 @@ static void draw(uiAreaDrawParams *p) uiDrawFreeTextLayout(layout); } -static const char *positions[] = { - [uiDrawTextLayoutHitTestPositionBefore] = "before", - [uiDrawTextLayoutHitTestPositionInside] = "inside", - [uiDrawTextLayoutHitTestPositionAfter] = "after", -}; - static void mouse(uiAreaMouseEvent *e) { uiDrawTextLayout *layout; diff --git a/ui_attrstr.h b/ui_attrstr.h index c2ded0d0..a6e7f896 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -87,8 +87,6 @@ struct uiDrawFontDescriptor { typedef struct uiDrawTextLayout uiDrawTextLayout; typedef struct uiDrawTextLayoutLineMetrics uiDrawTextLayoutLineMetrics; -typedef struct uiDrawTextLayoutHitTestResult uiDrawTextLayoutHitTestResult; -typedef struct uiDrawTextLayoutByteRangeRectangle uiDrawTextLayoutByteRangeRectangle; struct uiDrawTextLayoutLineMetrics { // This describes the overall bounding box of the line. @@ -121,30 +119,6 @@ struct uiDrawTextLayoutLineMetrics { // TODO trailing whitespace? }; -struct uiDrawTextLayoutHitTestResult { - // The byte position of the character at the given point. - size_t Pos; - // Line is the line at the given point. If the point is on the space - // after the end of a line, Pos will be Line's end index. In this case, - // the rectangle for that character will actually be on the *next* - // line. This disparity is only relevant for caret positioning when - // using the mouse; in that case, to ensure proper behavior, use - // the Caret fields below. In all other cases (including keyboard - // caret movement), use the actual rectangle for the grapheme - // at Pos (via uiDrawTextLayoutByteRangeToRectangle()). - int Line; - uiDrawTextLayoutHitTestPosition XPosition; - uiDrawTextLayoutHitTestPosition YPosition; - -// TODO? -// int InTrailingWhitespace; -// TODO? -// double XFraction; -// extra TODO? -// double YFraction; -// or just have offsets instead? in addition? -}; - // TODO // - allow creating a layout out of a substring // - allow marking compositon strings @@ -185,4 +159,5 @@ _UI_EXTERN void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y // the correct offset, even if pos is at the end of the line. If pos is not // in the range [start, end], a negative value will be returned, // indicating you need to move the cursor to another line. +// TODO make sure this works right for right-aligned and center-aligned lines and justified lines and RTL text _UI_EXTERN double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line); From 6fa009a7dd31415faeaa7151d72e64d3451e2e9c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Feb 2017 11:06:37 -0500 Subject: [PATCH 0606/1329] Fixed uiAttributedString not putting the right position for the last character. Simple case of using the wrong variables. --- common/attrstr.c | 4 ++-- darwin/drawtext.m | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/common/attrstr.c b/common/attrstr.c index 03dab8f9..249c086f 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -207,9 +207,9 @@ void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *s // and adjust the prior values in the conversion tables // use <= so the terminating 0 gets updated too for (i = 0; i <= oldlen - at; i++) - s->u8tou16[at + n8 + i] += n16; + s->u8tou16[at + oldlen + i] += old16len; for (i = 0; i <= old16len - at16; i++) - s->u16tou8[at16 + n16 + i] += n8; + s->u16tou8[at16 + old16len + i] += oldlen; // and finally do the attributes attrlistInsertCharactersUnattributed(s->attrs, at, n8); diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 82b99ac3..510164bd 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -424,15 +424,12 @@ double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int CTLineRef lr; CFRange range; -printf("= %zd %zd ", pos, tl->nUTF8); pos = tl->u8tou16[pos]; -printf("-> %zd %zd\n", pos, tl->nUTF16); if (line < 0 || line >= tl->nLines) return -1; lr = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, line); range = CTLineGetStringRange(lr); // note: >, not >=, because the position at end is valid! -printf("%zd %zd\n", pos, (size_t)(range.location+range.length)); if (pos < range.location || pos > (range.location + range.length)) return -1; // no point in checking the return; we already validated everything and 0 is a valid return for the first index :/ From 8ac9e386b569d86632beb1bf97d349368957c161 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Feb 2017 11:07:44 -0500 Subject: [PATCH 0607/1329] More TODOs. --- common/attrstr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/attrstr.c b/common/attrstr.c index 249c086f..f62c42f2 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -121,7 +121,7 @@ void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *s uint32_t rune; char buf[4]; uint16_t buf16[2]; - size_t n8, n16; + size_t n8, n16; // TODO make loop-local? to avoid using them in the wrong place again size_t old, old16; size_t oldlen, old16len; size_t at16; From 4b46dab775c872745a40789f1459d39d8f2e7a82 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Feb 2017 11:09:36 -0500 Subject: [PATCH 0608/1329] And that last fix also fixed the issue about byte 1 being wrong. --- examples/drawtext/hittest.c | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index 0ae2df5d..ab0bc5f5 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -168,7 +168,6 @@ static void mouse(uiAreaMouseEvent *e) // urgh %zd is not supported by MSVC with sprintf() // TODO get that part in test/ about having no other option - // TODO byte 1 is actually byte 684?! sprintf(labelText, "pos %d line %d",// x %g",// x position %s y position %s", (int) caretPos, caretLine);//, caretX); /* (int) (res.Pos), res.Line, From bb50440e559335b22667e6c8f26ff6239caabcc3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Feb 2017 11:14:24 -0500 Subject: [PATCH 0609/1329] Started cleaning up the old API. --- darwin/drawtext.m | 124 +++------------------------------------------- 1 file changed, 8 insertions(+), 116 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 510164bd..c0da5c2f 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -280,112 +280,6 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa *m = tl->lineMetrics[line]; } -#if 0 /* TODO */ - -// TODO note that in some cases lines can overlap slightly -// in our case, we read lines first to last and use their bottommost point (Y + Height) to determine where the next line should start for hit-testing -void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) -{ - CFIndex i; - CTLineRef line; - CFIndex pos; - - if (y >= 0) { - for (i = 0; i < tl->nLines; i++) { - double ltop, lbottom; - - ltop = tl->lineMetrics[i].Y; - lbottom = ltop + tl->lineMetrics[i].Height; - // y will already >= ltop at this point since the past lbottom should == (or at least >=, see above) ltop - if (y < lbottom) - break; - } - result->YPosition = uiDrawTextLayoutHitTestPositionInside; - if (i == tl->nLines) { - i--; - result->YPosition = uiDrawTextLayoutHitTestPositionAfter; - } - } else { - i = 0; - // TODO what if the first line crosses into the negatives? - result->YPosition = uiDrawTextLayoutHitTestPositionBefore; - } - result->Line = i; - - result->XPosition = uiDrawTextLayoutHitTestPositionInside; - if (x < tl->lineMetrics[i].X) { - result->XPosition = uiDrawTextLayoutHitTestPositionBefore; - // and forcibly return the first character - x = tl->lineMetrics[i].X; - } else if (x > (tl->lineMetrics[i].X + tl->lineMetrics[i].Width)) { - result->XPosition = uiDrawTextLayoutHitTestPositionAfter; - // and forcibly return the last character - x = tl->lineMetrics[i].X + tl->lineMetrics[i].Width; - } - - line = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, i); - // TODO copy the part from the docs about this point (TODO what point?) - pos = CTLineGetStringIndexForPosition(line, CGPointMake(x, 0)); - if (pos == kCFNotFound) { - // TODO - } - result->Pos = tl->u16tou8[pos]; - - // desired caret behavior: clicking on the right trailing space places inserted text at the start of the next line but the caret is on the clicked line at the trailing edge of the last grapheme - // hitting Right moves to the second point of the next line, then Left to go to the first, then Left to go to the start of the last character of the original line (so the keyboard can't move the caret back to that clicked space) - // this happens regardless of word-wrapping on whitespace, hyphens, or very long words - // use CTLineGetOffsetForStringIndex() here instead of x directly as that isn't always adjusted properly - result->CaretX = CTLineGetOffsetForStringIndex(line, pos, NULL); - result->CaretY = tl->lineMetrics[i].Y; - result->CaretHeight = tl->lineMetrics[i].Height; -} - -// TODO document this is appropriate for a caret -// TODO what happens if we select across a wrapped line? -void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r) -{ - CFIndex i; - CTLineRef line; - CFRange range; - CGFloat x, x2; // TODO rename x to x1 - - if (start > tl->nUTF8) - start = tl->nUTF8; - if (end > tl->nUTF8) - end = tl->nUTF8; - start = tl->u8tou16[start]; - end = tl->u8tou16[end]; - - for (i = 0; i < tl->nLines; i++) { - line = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, i); - range = CTLineGetStringRange(line); - if (start >= range.location && start < (range.location + range.length)) - break; - } - if (i == tl->nLines) - i--; - r->Line = i; - if (end > (range.location + range.length)) - end = range.location + range.length; - - x = CTLineGetOffsetForStringIndex(line, start, NULL); - x2 = CTLineGetOffsetForStringIndex(line, end, NULL); - - r->X = tl->lineMetrics[i].X + x; - r->Y = tl->lineMetrics[i].Y; - r->Width = (tl->lineMetrics[i].X + x2) - r->X; - r->Height = tl->lineMetrics[i].Height; - - // and use x and x2 to get the actual start and end positions - // TODO error check? - r->RealStart = CTLineGetStringIndexForPosition(line, CGPointMake(x, 0)); - r->RealEnd = CTLineGetStringIndexForPosition(line, CGPointMake(x2, 0)); - r->RealStart = tl->u16tou8[r->RealStart]; - r->RealEnd = tl->u16tou8[r->RealEnd]; -} - -#endif - // TODO note that in some cases lines can overlap slightly // in our case, we read lines first to last and use their bottommost point (Y + Height) to determine where the next line should start for hit-testing void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *pos, int *line) @@ -405,18 +299,16 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *p } if (i == tl->nLines) i--; - if (line != NULL) - *line = i; + *line = i; - // TODO do the hit-test unconditionally? - if (pos != NULL) { - ln = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, i); - p = CTLineGetStringIndexForPosition(ln, CGPointMake(x, 0)); - if (p == kCFNotFound) { - // TODO - } - *pos = tl->u16tou8[p]; + ln = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, i); + // note: according to the docs, we pass a y of 0 for this since the is the baseline of that line (the point is relative to the line) + // TODO is x relative to the line origin? + p = CTLineGetStringIndexForPosition(ln, CGPointMake(x, 0)); + if (p == kCFNotFound) { + // TODO } + *pos = tl->u16tou8[p]; } double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line) From 5823c3a53e0098db7415b2f2c669d5dee7ca4470 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Feb 2017 11:52:26 -0500 Subject: [PATCH 0610/1329] More cleanup. --- windows/drawtext.cpp | 140 ++++++------------------------------------- 1 file changed, 19 insertions(+), 121 deletions(-) diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 29117923..1b525657 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -300,115 +300,15 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa m->ParagraphSpacing = 0; // TODO } -#if 0 /* TODO */ - -// expected behavior on end of line: -// - same as OS X -// - TODO RETEST THIS ON OTHER PLATFORMS: EXCEPT: if you type something, then backspace, the cursor will move back to where you clicked! -// TODO this function does NOT yet work like that; it works like the Unix equivalent right now -void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) -{ - DWRITE_HIT_TEST_METRICS m; - size_t line; - double width, height; - BOOL trailing, inside; // crashes if I skip these :/ - HRESULT hr; - - hr = tl->layout->HitTestPoint(x, y, - &trailing, &inside, - &m); - if (hr != S_OK) - logHRESULT(L"error hit-testing IDWriteTextLayout", hr); - - // figure out what line this is - if (y < 0) - line = 0; - else - for (line = 0; line < tl->nLines; line++) - if (y >= tl->lineInfo[line].y && y < (tl->lineInfo[line].y + tl->lineInfo[line].height)) - break; - if (line == tl->nLines) // last position - line--; - - result->Pos = tl->u16tou8[m.textPosition]; - result->Line = line; - - uiDrawTextLayoutExtents(tl, &width, &height); - result->XPosition = uiDrawTextLayoutHitTestPositionInside; - if (x < 0) - result->XPosition = uiDrawTextLayoutHitTestPositionBefore; - else if (x >= width) - result->XPosition = uiDrawTextLayoutHitTestPositionAfter; - result->YPosition = uiDrawTextLayoutHitTestPositionInside; - if (y < 0) - result->YPosition = uiDrawTextLayoutHitTestPositionBefore; - else if (y >= height) - result->YPosition = uiDrawTextLayoutHitTestPositionAfter; - - result->CaretX = m.left; // TODO is this correct? - result->CaretY = tl->lineInfo[line].y; - result->CaretHeight = tl->lineInfo[line].height; -} - -void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r) -{ - DWRITE_HIT_TEST_METRICS mstart, mend; - size_t line; - FLOAT x, y; // crashes if I skip these :/ - BOOL trailing, inside; // crashes if I skip these :/ - HRESULT hr; - - start = tl->u8tou16[start]; - end = tl->u8tou16[end]; - - // TODO explain why this is a leading hit - hr = tl->layout->HitTestTextPosition(start, FALSE, - &x, &y, - &mstart); - if (hr != S_OK) - logHRESULT(L"error getting rect of start position", hr); - - // figure out what line this is - for (line = 0; line < tl->nLines; line++) - if (start >= tl->lineInfo[line].startPos && start < tl->lineInfo[line].endPos) - break; - if (line == tl->nLines) // last position - line--; - if (end > tl->lineInfo[line].endPos) - end = tl->lineInfo[line].endPos; - - hr = tl->layout->HitTestTextPosition(end, FALSE, - &x, &y, - &mend); - if (hr != S_OK) - logHRESULT(L"error getting rect of end position", hr); - - r->X = mstart.left; - r->Y = tl->lineInfo[line].y; - r->Width = mend.left - mstart.left; - r->Height = tl->lineInfo[line].height; - - hr = tl->layout->HitTestPoint(r->X, r->Y, - &trailing, &inside, - &mstart); - if (hr != S_OK) - logHRESULT(L"TODO write this", hr); - // TODO also get the end pos just so we can have an accurate r->RealEnd - r->RealStart = mstart.textPosition; - r->RealEnd = end; - - r->RealStart = tl->u16tou8[r->RealStart]; - r->RealEnd = tl->u16tou8[r->RealEnd]; -} - -#endif - // this algorithm comes from Microsoft's PadWrite sample, following TextEditor::SetSelectionFromPoint() +// TODO go back through all of these and make sure we convert coordinates properly +// TODO same for OS X void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *pos, int *line) { DWRITE_HIT_TEST_METRICS m; BOOL trailing, inside; size_t p; + UINT32 i; HRESULT hr; hr = tl->layout->HitTestPoint(x, y, @@ -428,27 +328,20 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *p logHRESULT(L"error aligning trailing hit to nearest cluster", hr); p = m2.textPosition + m2.length; } - // TODO should we just make the pointers required? - if (pos != NULL) - *pos = tl->u16tou8[p]; + *pos = tl->u16tou8[p]; - // TODO do the line detection unconditionally? - if (line != NULL) { - UINT32 i; + for (i = 0; i < tl->nLines; i++) { + double ltop, lbottom; - for (i = 0; i < tl->nLines; i++) { - double ltop, lbottom; - - ltop = tl->lineInfo[i].y; - lbottom = ltop + tl->lineInfo[i].height; - // y will already >= ltop at this point since the past lbottom should == ltop - if (y < lbottom) - break; - } - if (i == tl->nLines) - i--; - *line = i; + ltop = tl->lineInfo[i].y; + lbottom = ltop + tl->lineInfo[i].height; + // y will already >= ltop at this point since the past lbottom should == ltop + if (y < lbottom) + break; } + if (i == tl->nLines) + i--; + *line = i; } double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line) @@ -458,7 +351,12 @@ double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int FLOAT x, y; HRESULT hr; + if (line < 0 || line >= tl->nLines) + return -1; pos = tl->u8tou16[pos]; + // note: >, not >=, because the position at endPos is valid! + if (pos < tl->lineInfo[line].startPos || pos > tl->lineInfo[line].endPos) + return -1; // this behavior seems correct // there's also PadWrite's TextEditor::GetCaretRect() but that requires state... // TODO where does this fail? From 0e5be3229949035713c5f973d73b4767dad9719f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Feb 2017 15:49:36 -0500 Subject: [PATCH 0611/1329] And cleaned up the GTK+ code. --- unix/drawtext.c | 126 +++++++++--------------------------------------- 1 file changed, 22 insertions(+), 104 deletions(-) diff --git a/unix/drawtext.c b/unix/drawtext.c index 879cace9..49165ae3 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -8,6 +8,7 @@ struct uiDrawTextLayout { PangoLayout *layout; uiDrawTextLayoutLineMetrics *lineMetrics; + int nLines; }; // See https://developer.gnome.org/pango/1.30/pango-Cairo-Rendering.html#pango-Cairo-Rendering.description @@ -48,7 +49,7 @@ static void computeLineMetrics(uiDrawTextLayout *tl) int i, n; uiDrawTextLayoutLineMetrics *m; - n = pango_layout_get_line_count(tl->layout); + n = tl->nLines; // TODO remove this variable tl->lineMetrics = (uiDrawTextLayoutLineMetrics *) uiAlloc(n * sizeof (uiDrawTextLayoutLineMetrics), "uiDrawTextLayoutLineMetrics[] (text layout)"); iter = pango_layout_get_iter(tl->layout); @@ -130,6 +131,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto // TODO attributes + tl->nLines = pango_layout_get_line_count(tl->layout); computeLineMetrics(tl); return tl; @@ -187,93 +189,12 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa } #endif -#if 0 /* TODO */ - -// caret behavior, based on GtkTextView (both 2 and 3) and Qt's text views (both 4 and 5), which are all based on TkTextView: -// - clicking on the right side of a line places the cursor at the beginning of the LAST grapheme on the line -// - pressing Right goes to the first grapheme of the next line, pressing Left goes back to the last grapheme of the original line -// - as such, there is *no* way to get the cursor on the end of the line -// - this includes whitespace, hyphens, and character wraps -// - spaces get special treatment (it seems): you don't see them there and they don't show up if you select one and only one! -// - and the best part is that word processors don't typically do this; they do what other platforms do! -void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result) -{ - int index; - int trailing; - int line; - int caretX; - double width, height; - - // disregard the return value; we do our own bounds checking - // TODO see if there's a way we can use some other function (possibly this one again) to extrapolate more precise bounds information - pango_layout_xy_to_index(tl->layout, - cairoToPango(x), cairoToPango(y), - &index, &trailing); - // figure out what line that was, and also the caret X position since we can get that here too - // TODO should we use the dedicated function instead? - pango_layout_index_to_line_x(tl->layout, index, trailing, - &line, &caretX); - result->Pos = index; - result->Line = line; - - uiDrawTextLayoutExtents(tl, &width, &height); - result->XPosition = uiDrawTextLayoutHitTestPositionInside; - if (x < 0) - result->XPosition = uiDrawTextLayoutHitTestPositionBefore; - else if (x >= width) - result->XPosition = uiDrawTextLayoutHitTestPositionAfter; - result->YPosition = uiDrawTextLayoutHitTestPositionInside; - if (y < 0) - result->YPosition = uiDrawTextLayoutHitTestPositionBefore; - else if (y >= height) - result->YPosition = uiDrawTextLayoutHitTestPositionAfter; - - result->CaretX = pangoToCairo(caretX); - result->CaretY = tl->lineMetrics[line].Y; - result->CaretHeight = tl->lineMetrics[line].Height; -} - -// TODO consider providing a uiDrawTextLayoutByteIndexToCursorPos() function for manual positions by byte index only? -// TODO is this correct for RTL? -void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r) -{ - PangoRectangle startRect, endRect; - int line, index; - PangoLayoutLine *pll; - - pango_layout_index_to_pos(tl->layout, start, &startRect); - - // figure out what line that was - // TODO talk about leading edge here - pango_layout_index_to_line_x(tl->layout, start, 1, - &line, NULL); - pll = pango_layout_get_line_readonly(tl->layout, line); - - if (end > (pll->start_index + pll->length)) - end = pll->start_index + pll->length; - pango_layout_index_to_pos(tl->layout, end, &endRect); - - r->X = pangoToCairo(startRect.x); - r->Y = tl->lineMetrics[line].Y; - r->Width = pangoToCairo(endRect.x) - r->X; - r->Height = tl->lineMetrics[line].Height; - - // and figure out the correct start pos - pango_layout_line_x_to_index(pll, startRect.x, - &index, NULL); - r->RealStart = index; - r->RealEnd = end; - - // TODO unref pll? -} - -#endif - -// this algorithm comes from Microsoft's PadWrite sample, following TextEditor::SetSelectionFromPoint() -// TODO this or the next one doesn't work right for the end of a line? it still behaves like TkTextView... +// note: Pango will not let us place the cursor at the end of a line the same way other OSs do; see https://git.gnome.org/browse/pango/tree/pango/pango-layout.c?id=f4cbd27f4e5bf8490ea411190d41813e14f12165#n4204 +// ideally there'd be a way to say "I don't need this hack; I'm well behaved" but GTK+ 2 and 3 AND Qt 4 and 5 all behave like this, with the behavior seeming to date back to TkTextView, so... void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *pos, int *line) { int p, trailing; + int i; pango_layout_xy_to_index(tl->layout, cairoToPango(x), cairoToPango(y), @@ -282,37 +203,34 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *p // fortunately Pango provides that info directly if (trailing != 0) p += trailing; - if (pos != NULL) - *pos = p; + *pos = p; - // TODO do the line detection unconditionally? - // TODO optimize the call to pango_layout_get_line_count() - if (line != NULL) { - int i; + for (i = 0; i < tl->nLines; i++) { + double ltop, lbottom; - for (i = 0; i < pango_layout_get_line_count(tl->layout); i++) { - double ltop, lbottom; - - ltop = tl->lineMetrics[i].Y; - lbottom = ltop + tl->lineMetrics[i].Height; - // y will already >= ltop at this point since the past lbottom should == ltop - if (y < lbottom) - break; - } - if (i == pango_layout_get_line_count(tl->layout)) - i--; - *line = i; + ltop = tl->lineMetrics[i].Y; + lbottom = ltop + tl->lineMetrics[i].Height; + // y will already >= ltop at this point since the past lbottom should == ltop + if (y < lbottom) + break; } + if (i == pango_layout_get_line_count(tl->layout)) + i--; + *line = i; } -// TODO find a good API for indicating that this character isn't on the line for when the layout is resized and doing that recalculation... double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line) { PangoLayoutLine *pll; gboolean trailing; int pangox; + if (line < 0 || line >= tl->nLines) + return -1; pll = pango_layout_get_line_readonly(tl->layout, line); + // note: >, not >=, because the position at end is valid! + if (pos < pll->start_index || pos > (pll->start_index + pll->length)) + return -1; // this behavior seems correct // there's also PadWrite's TextEditor::GetCaretRect() but that requires state... // TODO where does this fail? From b52600d9cd8ff9e7828488417d5013efb05776f6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Feb 2017 16:16:43 -0500 Subject: [PATCH 0612/1329] Preparation for adding uiDrawCaret(). --- unix/area.c | 3 ++- unix/draw.c | 4 +++- unix/draw.h | 1 + unix/uipriv_unix.h | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/unix/area.c b/unix/area.c index c46447cc..ca99e602 100644 --- a/unix/area.c +++ b/unix/area.c @@ -119,7 +119,8 @@ static gboolean areaWidget_draw(GtkWidget *w, cairo_t *cr) uiAreaDrawParams dp; double clipX0, clipY0, clipX1, clipY1; - dp.Context = newContext(cr); + dp.Context = newContext(cr, + gtk_widget_get_style_context(a->widget)); loadAreaSize(a, &(dp.AreaWidth), &(dp.AreaHeight)); diff --git a/unix/draw.c b/unix/draw.c index 2d7a6367..b4d18049 100644 --- a/unix/draw.c +++ b/unix/draw.c @@ -2,17 +2,19 @@ #include "uipriv_unix.h" #include "draw.h" -uiDrawContext *newContext(cairo_t *cr) +uiDrawContext *newContext(cairo_t *cr, GtkStyleContext *style) { uiDrawContext *c; c = uiNew(uiDrawContext); c->cr = cr; + c->style = style; return c; } void freeContext(uiDrawContext *c) { + // free neither cr nor style; we own neither uiFree(c); } diff --git a/unix/draw.h b/unix/draw.h index dbd82ff5..9eec2846 100644 --- a/unix/draw.h +++ b/unix/draw.h @@ -3,6 +3,7 @@ // draw.c struct uiDrawContext { cairo_t *cr; + GtkStyleContext *style; }; // drawpath.c diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index 0a477158..3764f8ef 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -42,7 +42,7 @@ extern GtkWidget *childBox(struct child *c); extern void childSetMargined(struct child *c, int margined); // draw.c -extern uiDrawContext *newContext(cairo_t *); +extern uiDrawContext *newContext(cairo_t *cr, GtkStyleContext *style); extern void freeContext(uiDrawContext *); // drawtext.c From 642363ccaea3c93b49e656ce28ca5143a3647cad Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Feb 2017 16:29:36 -0500 Subject: [PATCH 0613/1329] Added the uiDrawCaret() function for drawing text carets. Now to write the backend-specific code and test it out. --- common/CMakeLists.txt | 1 + common/drawtext.c | 53 +++++++++++++++++++++++++++++++++++++++++++ common/uipriv.h | 10 ++++++++ ui_attrstr.h | 2 ++ 4 files changed, 66 insertions(+) create mode 100644 common/drawtext.c diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index fdace350..8ecff8b3 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -6,6 +6,7 @@ list(APPEND _LIBUI_SOURCES common/areaevents.c common/control.c common/debug.c + common/drawtext.c common/matrix.c common/shouldquit.c common/userbugs.c diff --git a/common/drawtext.c b/common/drawtext.c new file mode 100644 index 00000000..41aadb2e --- /dev/null +++ b/common/drawtext.c @@ -0,0 +1,53 @@ +// 10 february 2017 +#include "../ui.h" +#include "uipriv.h" + +void uiDrawCaret(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t pos, int *line) +{ + double xoff; + uiDrawTextLayoutLineMetrics m; + struct caretDrawParams cdp; + uiDrawPath *path; + uiDrawBrush brush; + + caretDrawParams(c, &cdp); + + xoff = uiDrawTextLayoutByteLocationInLine(layout, pos, *line); + if (xoff < 0) { + size_t start, end; + int incr; + + if (*line > (uiDrawTextLayoutNumLines(layout) - 1)) { + *line = (uiDrawTextLayoutNumLines(layout) - 1); + incr = -1; + } else { + uiDrawTextLayoutLineByteRange(layout, *line, &start, &end); + incr = 1; + if (end < pos) + incr = -1; + } + while (xoff < 0) { + *line += incr; + xoff = uiDrawTextLayoutByteLocationInLine(layout, pos, *line); + } + } + uiDrawTextLayoutLineGetMetrics(layout, *line, &m); + + uiDrawSave(c); + + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathAddRectangle(path, + // TODO add m.X? + x + xoff, y + m.Y, + cdp.width, m.Height); + uiDrawPathEnd(path); + brush.Type = uiDrawBrushTypeSolid; + brush.R = cdp.r; + brush.G = cdp.g; + brush.B = cdp.b; + brush.A = cdp.a; + uiDrawFill(c, path, &brush); + uiDrawFreePath(path); + + uiDrawRestore(c); +} diff --git a/common/uipriv.h b/common/uipriv.h index 0ad18c14..64954c38 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -89,6 +89,16 @@ extern void attrlistForEach(struct attrlist *alist, uiAttributedString *s, uiAtt extern struct attrlist *attrlistNew(void); extern void attrlistFree(struct attrlist *alist); +// drawtext.c +struct caretDrawParams { + double r; + double g; + double b; + double a; + double width; +}; +extern void caretDrawParams(uiDrawContext *c, struct caretDrawParams *p); + #ifdef __cplusplus } #endif diff --git a/ui_attrstr.h b/ui_attrstr.h index a6e7f896..6254fb02 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -161,3 +161,5 @@ _UI_EXTERN void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y // indicating you need to move the cursor to another line. // TODO make sure this works right for right-aligned and center-aligned lines and justified lines and RTL text _UI_EXTERN double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line); + +_UI_EXTERN void uiDrawCaret(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t pos, int *line); From b5b0fae052ca8cf5e1c1a72b3dec7d2f10e85ed6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Feb 2017 16:53:08 -0500 Subject: [PATCH 0614/1329] And implemented uiDrawCaret() on GTK+. --- common/drawtext.c | 6 +++--- common/uipriv.h | 3 ++- examples/drawtext/hittest.c | 6 +++++- ui_attrstr.h | 1 + unix/drawtext.c | 36 ++++++++++++++++++++++++++++++++++++ 5 files changed, 47 insertions(+), 5 deletions(-) diff --git a/common/drawtext.c b/common/drawtext.c index 41aadb2e..4c9dd200 100644 --- a/common/drawtext.c +++ b/common/drawtext.c @@ -10,8 +10,6 @@ void uiDrawCaret(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, uiDrawPath *path; uiDrawBrush brush; - caretDrawParams(c, &cdp); - xoff = uiDrawTextLayoutByteLocationInLine(layout, pos, *line); if (xoff < 0) { size_t start, end; @@ -33,12 +31,14 @@ void uiDrawCaret(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, } uiDrawTextLayoutLineGetMetrics(layout, *line, &m); + caretDrawParams(c, m.Height, &cdp); + uiDrawSave(c); path = uiDrawNewPath(uiDrawFillModeWinding); uiDrawPathAddRectangle(path, // TODO add m.X? - x + xoff, y + m.Y, + x + xoff - cdp.xoff, y + m.Y, cdp.width, m.Height); uiDrawPathEnd(path); brush.Type = uiDrawBrushTypeSolid; diff --git a/common/uipriv.h b/common/uipriv.h index 64954c38..73c7eeb0 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -95,9 +95,10 @@ struct caretDrawParams { double g; double b; double a; + double xoff; double width; }; -extern void caretDrawParams(uiDrawContext *c, struct caretDrawParams *p); +extern void caretDrawParams(uiDrawContext *c, double height, struct caretDrawParams *p); #ifdef __cplusplus } diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index ab0bc5f5..1d836efa 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -111,9 +111,9 @@ static void draw(uiAreaDrawParams *p) caretLine = uiDrawTextLayoutNumLines(layout) - 1; caretPos = uiAttributedStringLen(attrstr); } +#if 0 caretX = uiDrawTextLayoutByteLocationInLine(layout, caretPos, caretLine); -printf("%g\n", caretX); uiDrawTextLayoutLineGetMetrics(layout, caretLine, &m); path = uiDrawNewPath(uiDrawFillModeWinding); uiDrawPathNewFigure(path, margins + caretX, margins + m.Y); @@ -126,6 +126,10 @@ printf("%g\n", caretX); brush.A = 1.0; uiDrawStroke(p->Context, path, &brush, &strokeParams); uiDrawFreePath(path); +#else + uiDrawCaret(p->Context, margins, margins, + layout, caretPos, &caretLine); +#endif if (uiCheckboxChecked(showLineBounds)) { int i, n; diff --git a/ui_attrstr.h b/ui_attrstr.h index 6254fb02..052f067d 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -163,3 +163,4 @@ _UI_EXTERN void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y _UI_EXTERN double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line); _UI_EXTERN void uiDrawCaret(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t pos, int *line); +// TODO allow blinking diff --git a/unix/drawtext.c b/unix/drawtext.c index 49165ae3..175403ad 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -244,3 +244,39 @@ double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int // TODO unref pll? return pangoToCairo(pangox); } + +// note: we can't use gtk_render_insertion_cursor() because that doesn't take information about what line to render on +// we'll just recreate what it does +void caretDrawParams(uiDrawContext *c, double height, struct caretDrawParams *p) +{ + GdkColor *color; + GdkRGBA rgba; + gfloat aspectRatio; + gint width, xoff; + + gtk_style_context_get_style(c->style, + "cursor-color", &color, + "cursor-aspect-ratio", &aspectRatio, + NULL); + if (color != NULL) { + p->r = ((double) (color->red)) / 65535.0; + p->g = ((double) (color->green)) / 65535.0; + p->b = ((double) (color->blue)) / 65535.0; + p->a = 1.0; + gdk_color_free(color); + } else { + gtk_style_context_get_color(c->style, GTK_STATE_FLAG_NORMAL, &rgba); + p->r = rgba.red; + p->g = rgba.green; + p->b = rgba.blue; + p->a = rgba.alpha; + } + + // GTK+ itself uses integer arithmetic here; let's do the same + width = height * aspectRatio + 1; + // TODO this is for LTR + xoff = width / 2; + + p->xoff = xoff; + p->width = width; +} From 746e4091cb6a14b0d5117d9811e3944cef3b0329 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Feb 2017 17:38:17 -0500 Subject: [PATCH 0615/1329] Implemented uiDrawCaret() on Windows. --- windows/drawtext.cpp | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 1b525657..b7a5ccdf 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -371,3 +371,33 @@ double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int logHRESULT(L"error calling IDWriteTextLayout::HitTestTextPosition()", hr); return x; } + +void caretDrawParams(uiDrawContext *c, double height, struct caretDrawParams *p) +{ + DWORD caretWidth; + + // there seems to be no defined caret color + // the best I can come up with is "inverts colors underneath" (according to https://msdn.microsoft.com/en-us/library/windows/desktop/ms648397(v=vs.85).aspx) which I have no idea how to do (TODO) + // just return black for now + p->r = 0.0; + p->g = 0.0; + p->b = 0.0; + p->a = 1.0; + + if (SystemParametersInfoW(SPI_GETCARETWIDTH, 0, &caretWidth, 0) == 0) + // don't log the failure, fall back gracefully + // the instruction to use this comes from https://msdn.microsoft.com/en-us/library/windows/desktop/ms648399(v=vs.85).aspx + // and we have to assume GetSystemMetrics() always succeeds, so + caretWidth = GetSystemMetrics(SM_CXBORDER); + // TODO make this a function and split it out of areautil.cpp + { + FLOAT dpix, dpiy; + + // TODO can we pass NULL for dpiy? + c->rt->GetDpi(&dpix, &dpiy); + // see https://msdn.microsoft.com/en-us/library/windows/desktop/dd756649%28v=vs.85%29.aspx (and others; search "direct2d mouse") + p->width = ((double) (caretWidth * 96)) / dpix; + } + // and there doesn't seem to be this either... (TODO check what PadWrite does?) + p->xoff = 0; +} From 5d18d477e36eb41c4f3088a0652a6bec76eec630 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Feb 2017 18:19:32 -0500 Subject: [PATCH 0616/1329] Implemented uiDrawCaret() on OS X. --- darwin/drawtext.m | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index c0da5c2f..3fb12e54 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -327,3 +327,25 @@ double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int // no point in checking the return; we already validated everything and 0 is a valid return for the first index :/ return CTLineGetOffsetForStringIndex(lr, pos, NULL); } + +void caretDrawParams(uiDrawContext *c, double height, struct caretDrawParams *p) +{ + NSColor *cc; + CGFloat cr, cg, cb, ca; + + // Interface Builder sets this as the insertion point color for a NSTextView by default + cc = [NSColor controlTextColor]; + // the given color may not be an RGBA color, which will cause the -getRed:green:blue:alpha: call to throw an exception + cc = [cc colorUsingColorSpace:[NSColorSpace sRGBColorSpace]]; + [cc getRed:&cr green:&cg blue:&cb alpha:&ca]; + p->r = cr; + p->g = cg; + p->b = cb; + p->a = ca; + // both cc and the controlTextColor it was made from will be autoreleased since they aren't new or init calls + // TODO disabled carets have some blending applied... + + // okay there doesn't seem to be any defined metrics for these, argh... + p->width = 1; + p->xoff = 0; +} From 5354aa52624b0b6d43bd333a9f6875858de45a53 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Feb 2017 19:08:31 -0500 Subject: [PATCH 0617/1329] And cleaned hittest.c up. --- examples/drawtext/hittest.c | 48 ++++--------------------------------- 1 file changed, 4 insertions(+), 44 deletions(-) diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index 1d836efa..8fe78ab7 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -2,7 +2,6 @@ #include "drawtext.h" // TODO have a ligature -// TODO allow clicking on the end of a line to put the caret there // TODO the hiding and showing does not work properly on GTK+ static const char *text = @@ -70,24 +69,11 @@ static uiDrawBrush fillBrushes[4] = { }, }; -// TODO this should be const -static uiDrawStrokeParams strokeParams = { - .Cap = uiDrawLineCapFlat, - .Join = uiDrawLineJoinMiter, - .Thickness = 2, - .MiterLimit = uiDrawDefaultMiterLimit, - .Dashes = NULL, - .NumDashes = 0, - .DashPhase = 0, -}; - static void draw(uiAreaDrawParams *p) { uiDrawPath *path; uiDrawTextLayout *layout; uiDrawTextLayoutLineMetrics m; - uiDrawBrush brush; - double caretX; // only clip the text, not the guides uiDrawSave(p->Context); @@ -111,25 +97,8 @@ static void draw(uiAreaDrawParams *p) caretLine = uiDrawTextLayoutNumLines(layout) - 1; caretPos = uiAttributedStringLen(attrstr); } -#if 0 - caretX = uiDrawTextLayoutByteLocationInLine(layout, - caretPos, caretLine); - uiDrawTextLayoutLineGetMetrics(layout, caretLine, &m); - path = uiDrawNewPath(uiDrawFillModeWinding); - uiDrawPathNewFigure(path, margins + caretX, margins + m.Y); - uiDrawPathLineTo(path, margins + caretX, margins + m.Y + m.Height); - uiDrawPathEnd(path); - brush.Type = uiDrawBrushTypeSolid; - brush.R = 0.0; - brush.G = 0.0; - brush.B = 1.0; - brush.A = 1.0; - uiDrawStroke(p->Context, path, &brush, &strokeParams); - uiDrawFreePath(path); -#else uiDrawCaret(p->Context, margins, margins, layout, caretPos, &caretLine); -#endif if (uiCheckboxChecked(showLineBounds)) { int i, n; @@ -154,7 +123,6 @@ static void draw(uiAreaDrawParams *p) static void mouse(uiAreaMouseEvent *e) { uiDrawTextLayout *layout; -// uiDrawTextLayoutHitTestResult res; char labelText[128]; if (e->Down != 1) @@ -165,24 +133,16 @@ static void mouse(uiAreaMouseEvent *e) e->AreaWidth - 2 * margins); uiDrawTextLayoutHitTest(layout, e->X - margins, e->Y - margins, -// &res); &caretPos, &caretLine); -// caretX = uiDrawTextLayoutByteLocationInLine(layout, pos, caretLine); uiDrawFreeTextLayout(layout); // urgh %zd is not supported by MSVC with sprintf() // TODO get that part in test/ about having no other option - sprintf(labelText, "pos %d line %d",// x %g",// x position %s y position %s", - (int) caretPos, caretLine);//, caretX); -/* (int) (res.Pos), res.Line, - positions[res.XPosition], - positions[res.YPosition]); -*/ uiLabelSetText(caretLabel, labelText); + sprintf(labelText, "pos %d line %d", + (int) caretPos, caretLine); + uiLabelSetText(caretLabel, labelText); -/* cursorX = res.CaretX; - cursorY = res.CaretY; - cursorHeight = res.CaretHeight; -*/ redraw(); + redraw(); } static struct example hitTestExample; From 5f05ebbffe7cc5bdff988f4be8e6277b1eedf8dd Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Feb 2017 19:22:25 -0500 Subject: [PATCH 0618/1329] Expanded the hittest example by handling keyboard input. I should probably rename it to The Caret and Graphemes. --- examples/drawtext/basic.c | 1 + examples/drawtext/drawtext.h | 1 + examples/drawtext/hittest.c | 38 +++++++++++++++++++++++++++++++++++- examples/drawtext/main.c | 3 ++- 4 files changed, 41 insertions(+), 2 deletions(-) diff --git a/examples/drawtext/basic.c b/examples/drawtext/basic.c index c40cf36c..e1aaca02 100644 --- a/examples/drawtext/basic.c +++ b/examples/drawtext/basic.c @@ -254,6 +254,7 @@ struct example *mkBasicExample(void) basicExample.panel = uiControl(panel); basicExample.draw = draw; basicExample.mouse = NULL; + basicExample.key = NULL; attrstr = uiNewAttributedString(text); diff --git a/examples/drawtext/drawtext.h b/examples/drawtext/drawtext.h index c82383eb..f14ee485 100644 --- a/examples/drawtext/drawtext.h +++ b/examples/drawtext/drawtext.h @@ -8,6 +8,7 @@ struct example { uiControl *panel; void (*draw)(uiAreaDrawParams *p); void (*mouse)(uiAreaMouseEvent *e); + int (*key)(uiAreaKeyEvent *e); // TODO key? }; diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index 8fe78ab7..cbd0bd90 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -35,7 +35,6 @@ static uiCheckbox *showLineBounds; static int caretLine = -1; static size_t caretPos; -//static double caretX; // TODO should be const? static uiDrawBrush fillBrushes[4] = { @@ -136,6 +135,7 @@ static void mouse(uiAreaMouseEvent *e) &caretPos, &caretLine); uiDrawFreeTextLayout(layout); + // TODO move this into the draw handler so it is reflected by keyboard-based position changes // urgh %zd is not supported by MSVC with sprintf() // TODO get that part in test/ about having no other option sprintf(labelText, "pos %d line %d", @@ -145,6 +145,41 @@ static void mouse(uiAreaMouseEvent *e) redraw(); } +static int key(uiAreaKeyEvent *e) +{ + size_t grapheme; + + if (e->Up) + return 0; + if (e->Key != 0) + return 0; + switch (e->ExtKey) { + case uiExtKeyUp: + // TODO + return 1; + case uiExtKeyDown: + // TODO + return 1; + case uiExtKeyLeft: + grapheme = uiAttributedStringByteIndexToGrapheme(attrstr, caretPos); + if (grapheme == 0) + return 0; + grapheme--; + caretPos = uiAttributedStringGraphemeToByteIndex(attrstr, grapheme); + redraw(); + return 1; + case uiExtKeyRight: + grapheme = uiAttributedStringByteIndexToGrapheme(attrstr, caretPos); + if (grapheme == uiAttributedStringNumGraphemes(attrstr)) + return 0; + grapheme++; + caretPos = uiAttributedStringGraphemeToByteIndex(attrstr, grapheme); + redraw(); + return 1; + } + return 0; +} + static struct example hitTestExample; // TODO share? @@ -174,6 +209,7 @@ struct example *mkHitTestExample(void) hitTestExample.panel = uiControl(panel); hitTestExample.draw = draw; hitTestExample.mouse = mouse; + hitTestExample.key = key; attrstr = uiNewAttributedString(text); diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c index 552b2f6b..cc28acd3 100644 --- a/examples/drawtext/main.c +++ b/examples/drawtext/main.c @@ -47,7 +47,8 @@ static void handlerDragBroken(uiAreaHandler *ah, uiArea *a) static int handlerKeyEvent(uiAreaHandler *ah, uiArea *a, uiAreaKeyEvent *e) { - // reject all keys + if (examples[curExample]->key != NULL) + return examples[curExample]->key(e); return 0; } From 6acb10a4fcfee465a4a74ba5f28311017da28369 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Feb 2017 19:29:07 -0500 Subject: [PATCH 0619/1329] Fixed hanging errors in uiDrawCaret(). --- common/drawtext.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/common/drawtext.c b/common/drawtext.c index 4c9dd200..c55227f1 100644 --- a/common/drawtext.c +++ b/common/drawtext.c @@ -10,24 +10,21 @@ void uiDrawCaret(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, uiDrawPath *path; uiDrawBrush brush; + if (*line < 0) + *line = 0; + if (*line > (uiDrawTextLayoutNumLines(layout) - 1)) + *line = (uiDrawTextLayoutNumLines(layout) - 1); + // TODO cap pos xoff = uiDrawTextLayoutByteLocationInLine(layout, pos, *line); - if (xoff < 0) { + while (xoff < 0) { size_t start, end; - int incr; - if (*line > (uiDrawTextLayoutNumLines(layout) - 1)) { - *line = (uiDrawTextLayoutNumLines(layout) - 1); - incr = -1; - } else { - uiDrawTextLayoutLineByteRange(layout, *line, &start, &end); - incr = 1; - if (end < pos) - incr = -1; - } - while (xoff < 0) { - *line += incr; - xoff = uiDrawTextLayoutByteLocationInLine(layout, pos, *line); - } + uiDrawTextLayoutLineByteRange(layout, *line, &start, &end); + if (end < pos) // too far up + (*line)++; + else + (*line)--; + xoff = uiDrawTextLayoutByteLocationInLine(layout, pos, *line); } uiDrawTextLayoutLineGetMetrics(layout, *line, &m); From 5a1c7338412a003a6bd0238be7bee84d040e3561 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Feb 2017 20:37:05 -0500 Subject: [PATCH 0620/1329] Something is wrong with the Windows code... --- common/drawtext.c | 1 + windows/graphemes.cpp | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/common/drawtext.c b/common/drawtext.c index c55227f1..cbfe3ccb 100644 --- a/common/drawtext.c +++ b/common/drawtext.c @@ -2,6 +2,7 @@ #include "../ui.h" #include "uipriv.h" +// TODO figure out how to make this work on GTK+ void uiDrawCaret(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t pos, int *line) { double xoff; diff --git a/windows/graphemes.cpp b/windows/graphemes.cpp index 2b93fab4..16f2fd82 100644 --- a/windows/graphemes.cpp +++ b/windows/graphemes.cpp @@ -21,12 +21,11 @@ static HRESULT itemize(WCHAR *s, size_t len, SCRIPT_ITEM **out, int *outn) int n; HRESULT hr; - // make sure these are zero-initialized to avoid mangling the text - ZeroMemory(&sc, sizeof (SCRIPT_CONTROL)); - ZeroMemory(&ss, sizeof (SCRIPT_STATE)); - maxItems = len + 2; for (;;) { + // make sure these are zero-initialized to avoid mangling the text + ZeroMemory(&sc, sizeof (SCRIPT_CONTROL)); + ZeroMemory(&ss, sizeof (SCRIPT_STATE)); items = new SCRIPT_ITEM[maxItems + 1]; hr = ScriptItemize(s, len, maxItems, @@ -101,8 +100,9 @@ struct graphemes *graphemes(void *s, size_t len) delete[] logattr; } // and handle the last item for the end of the string - *pGTP++ = items[nItems].iCharPos; - *pPTG++ = pGTP - g->graphemesToPoints; + *pGTP = items[nItems].iCharPos; + *pPTG = pGTP - g->graphemesToPoints; + // TODO is any of the above broken?... also for all platforms, are the last few bytes not covered? delete[] items; return g; From c4b6149ec25b7c66beb43d14116a3a58c8e91f62 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Feb 2017 22:12:37 -0500 Subject: [PATCH 0621/1329] Gave up and rewrote Windows graphemes.cpp to use CharNextW() instead of Uniscribe, since I can't manage Uniscribe memory properly, it seems. The CharNextW() bug is relatively painless to work around anyway. Next commit will drop Uniscribe from the headers and build system. --- windows/graphemes.cpp | 108 ++++++++++++------------------------------ 1 file changed, 29 insertions(+), 79 deletions(-) diff --git a/windows/graphemes.cpp b/windows/graphemes.cpp index 16f2fd82..256c3a07 100644 --- a/windows/graphemes.cpp +++ b/windows/graphemes.cpp @@ -1,109 +1,59 @@ // 25 may 2016 #include "uipriv_windows.hpp" -// We could use CharNext() to generate grapheme cluster boundaries, but it doesn't handle surrogate pairs properly (see http://archives.miloush.net/michkap/archive/2008/12/16/9223301.html). -// So let's use Uniscribe (see http://archives.miloush.net/michkap/archive/2005/01/14/352802.html) -// See also http://www.catch22.net/tuts/uniscribe-mysteries, http://www.catch22.net/tuts/keyboard-navigation, and https://maxradi.us/documents/uniscribe/ for more details. - -// TODO the DirectWrite equivalent appears to be https://msdn.microsoft.com/en-us/library/windows/desktop/dd316625(v=vs.85).aspx but is somehow somewhat more complicated to use than Uniscribe is! maybe the PadWrite sample uses it? or should we just keep using Uniscribe? +// We could use CharNextW() to generate grapheme cluster boundaries, but it doesn't handle surrogate pairs properly (see http://archives.miloush.net/michkap/archive/2008/12/16/9223301.html). +// We could also use Uniscribe (see http://archives.miloush.net/michkap/archive/2005/01/14/352802.html, http://www.catch22.net/tuts/uniscribe-mysteries, http://www.catch22.net/tuts/keyboard-navigation, and https://maxradi.us/documents/uniscribe/), but its rules for buffer sizes is convoluted. +// Let's just deal with the CharNextW() bug. int graphemesTakesUTF16(void) { return 1; } -static HRESULT itemize(WCHAR *s, size_t len, SCRIPT_ITEM **out, int *outn) -{ - SCRIPT_CONTROL sc; - SCRIPT_STATE ss; - SCRIPT_ITEM *items; - size_t maxItems; - int n; - HRESULT hr; - - maxItems = len + 2; - for (;;) { - // make sure these are zero-initialized to avoid mangling the text - ZeroMemory(&sc, sizeof (SCRIPT_CONTROL)); - ZeroMemory(&ss, sizeof (SCRIPT_STATE)); - items = new SCRIPT_ITEM[maxItems + 1]; - hr = ScriptItemize(s, len, - maxItems, - &sc, &ss, - items, &n); - if (hr == S_OK) - break; - // otherwise either an error or not enough room - delete[] items; - if (hr != E_OUTOFMEMORY) - return hr; - maxItems *= 2; // add some more and try again - } - - *out = items; - *outn = n; - return S_OK; -} - struct graphemes *graphemes(void *s, size_t len) { struct graphemes *g; - WCHAR *str = (WCHAR *) s; - SCRIPT_ITEM *items; - int nItems; - int curItemIndex; - int nCharsInCurItem; + WCHAR *str; size_t *pPTG, *pGTP; - HRESULT hr; g = uiNew(struct graphemes); - hr = itemize(str, len, &items, &nItems); - if (hr != S_OK) - logHRESULT(L"error itemizing string for finding grapheme cluster boundaries", hr); - g->len = nItems; + g->len = 0; + str = (WCHAR *) s; + while (*str != L'\0') { + g->len++; + str = CharNextW(str); + // no need to worry about surrogates if we're just counting + } + g->pointsToGraphemes = (size_t *) uiAlloc((len + 1) * sizeof (size_t), "size_t[] (graphemes)"); - // note that there are actually nItems + 1 elements in items - // items[nItems] is the grapheme one past the end g->graphemesToPoints = (size_t *) uiAlloc((g->len + 1) * sizeof (size_t), "size_t[] (graphemes)"); pPTG = g->pointsToGraphemes; pGTP = g->graphemesToPoints; - for (curItemIndex = 0; curItemIndex < nItems; curItemIndex++) { - SCRIPT_ITEM *curItem, *nextItem; - SCRIPT_LOGATTR *logattr; - size_t *curGTP; - int i; + str = (WCHAR *) s; + while (*str != L'\0') { + WCHAR *next, *p; + ptrdiff_t nextoff; - curItem = items + curItemIndex; - nextItem = curItem + 1; + // as part of the bug, we need to make sure we only call CharNextW() on low halves, otherwise it'll return the same low half forever + nextoff = 0; + if (IS_HIGH_SURROGATE(*str)) + nextoff = 1; + next = CharNextW(str + nextoff); + if (IS_LOW_SURROGATE(*next)) + next--; - nCharsInCurItem = nextItem->iCharPos - curItem->iCharPos; + *pGTP = pPTG - g->pointsToGraphemes; + for (p = str; p < next; p++) + *pPTG++ = pGTP - g->graphemesToPoints; + pGTP++; - logattr = new SCRIPT_LOGATTR[nCharsInCurItem]; - hr = ScriptBreak(str + curItem->iCharPos, nCharsInCurItem, - &(curItem->a), logattr); - if (hr != S_OK) - logHRESULT(L"error breaking string for finding grapheme cluster boundaries", hr); - - // TODO can we merge these loops somehow? - curGTP = pGTP; - for (i = 0; i < nCharsInCurItem; i++) - if (logattr[i].fCharStop != 0) - *pGTP++ = curItem->iCharPos + i; - for (i = 0; i < nCharsInCurItem; i++) { - *pPTG++ = curGTP - g->graphemesToPoints; - if (logattr[i].fCharStop != 0) - curGTP++; - } - - delete[] logattr; + str = next; } // and handle the last item for the end of the string - *pGTP = items[nItems].iCharPos; + *pGTP = pPTG - g->pointsToGraphemes; *pPTG = pGTP - g->graphemesToPoints; - // TODO is any of the above broken?... also for all platforms, are the last few bytes not covered? - delete[] items; return g; } From 230d23765a3a76f068e222c8be45bd44b095ba27 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Feb 2017 22:22:47 -0500 Subject: [PATCH 0622/1329] Removed Uniscribe from the Windows build process and header file list. Also more TODOs. Next major goal: reinstating the font button. --- examples/drawtext/hittest.c | 1 + windows/CMakeLists.txt | 3 +-- windows/winapi.hpp | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index cbd0bd90..2393e65b 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -3,6 +3,7 @@ // TODO have a ligature // TODO the hiding and showing does not work properly on GTK+ +// TODO using the arrow keys allows us to walk back to the end of the line; IIRC arrow keys shouldn't do that static const char *text = "Each of the glyphs an end user interacts with are called graphemes. " diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 5b1232ed..a9551776 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -76,10 +76,9 @@ macro(_handle_static) COMMENT "Copying libui.res") endmacro() -# notice that usp10 comes before gdi32 # TODO prune this list set(_LIBUI_LIBS - user32 kernel32 usp10 gdi32 comctl32 uxtheme msimg32 comdlg32 d2d1 dwrite ole32 oleaut32 oleacc uuid + user32 kernel32 gdi32 comctl32 uxtheme msimg32 comdlg32 d2d1 dwrite ole32 oleaut32 oleacc uuid PARENT_SCOPE) if(NOT MSVC) diff --git a/windows/winapi.hpp b/windows/winapi.hpp index 86aba5d7..a540809c 100644 --- a/windows/winapi.hpp +++ b/windows/winapi.hpp @@ -34,7 +34,6 @@ #include #include #include -#include #include #include From 49094bb340c710d0dabe7a08abd9711d509943c9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 11 Feb 2017 00:25:07 -0500 Subject: [PATCH 0623/1329] Merged uiFontButton back in, with an updated API that works directly with uiDrawFontDescriptors. --- ui.h | 30 ------------------------------ ui_attrstr.h | 8 ++++++++ 2 files changed, 8 insertions(+), 30 deletions(-) diff --git a/ui.h b/ui.h index ddc9bb63..922d0625 100644 --- a/ui.h +++ b/ui.h @@ -478,26 +478,6 @@ _UI_EXTERN void uiDrawRestore(uiDrawContext *c); // TODO merge back in #include "ui_attrstr.h" -// TODO -#if 0 -struct uiDrawTextFontMetrics { - double Ascent; - double Descent; - double Leading; - // TODO do these two mean the same across all platforms? - double UnderlinePos; - double UnderlineThickness; -}; - -_UI_EXTERN uiDrawTextFont *uiDrawLoadClosestFont(const uiDrawTextFontDescriptor *desc); -_UI_EXTERN void uiDrawFreeTextFont(uiDrawTextFont *font); -_UI_EXTERN uintptr_t uiDrawTextFontHandle(uiDrawTextFont *font); -_UI_EXTERN void uiDrawTextFontDescribe(uiDrawTextFont *font, uiDrawTextFontDescriptor *desc); -// TODO make copy with given attributes methods? -// TODO yuck this name -_UI_EXTERN void uiDrawTextFontGetMetrics(uiDrawTextFont *font, uiDrawTextFontMetrics *metrics); -#endif - _UI_ENUM(uiModifiers) { uiModifierCtrl = 1 << 0, uiModifierAlt = 1 << 1, @@ -577,16 +557,6 @@ struct uiAreaKeyEvent { int Up; }; -#if 0 /* TODO */ -typedef struct uiFontButton uiFontButton; -#define uiFontButton(this) ((uiFontButton *) (this)) -// TODO document this returns a new font -_UI_EXTERN uiDrawTextFont *uiFontButtonFont(uiFontButton *b); -// TOOD SetFont, mechanics -_UI_EXTERN void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data); -_UI_EXTERN uiFontButton *uiNewFontButton(void); -#endif - typedef struct uiColorButton uiColorButton; #define uiColorButton(this) ((uiColorButton *) (this)) _UI_EXTERN void uiColorButtonColor(uiColorButton *b, double *r, double *g, double *bl, double *a); diff --git a/ui_attrstr.h b/ui_attrstr.h index 052f067d..9fb81513 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -164,3 +164,11 @@ _UI_EXTERN double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_ _UI_EXTERN void uiDrawCaret(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t pos, int *line); // TODO allow blinking + +typedef struct uiFontButton uiFontButton; +#define uiFontButton(this) ((uiFontButton *) (this)) +// TODO document this returns a new font +_UI_EXTERN void uiFontButtonFont(uiFontButton *b, uiDrawFontDescriptor *desc); +// TOOD SetFont, mechanics +_UI_EXTERN void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data); +_UI_EXTERN uiFontButton *uiNewFontButton(void); From bebaf72de3694877e29ece8aa2197aba2be0b39f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 11 Feb 2017 00:53:52 -0500 Subject: [PATCH 0624/1329] Started putting uiFontButton back in on OS X. Just a bunch of boilerplate for now, namely the function to take a CTFontDesciptorRef and convert it into a uiDrawFontDescriptor. --- darwin/CMakeLists.txt | 2 +- darwin/fontmatch.m | 82 ++++++++++++++++++++++++++++++++++++++++++ darwin/uipriv_darwin.h | 8 +---- 3 files changed, 84 insertions(+), 8 deletions(-) diff --git a/darwin/CMakeLists.txt b/darwin/CMakeLists.txt index 34368c06..0fce3838 100644 --- a/darwin/CMakeLists.txt +++ b/darwin/CMakeLists.txt @@ -17,7 +17,7 @@ list(APPEND _LIBUI_SOURCES darwin/drawtext.m darwin/editablecombo.m darwin/entry.m -#TODO darwin/fontbutton.m + darwin/fontbutton.m darwin/fontmatch.m darwin/form.m darwin/graphemes.m diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index 04761ef4..821855a7 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -253,3 +253,85 @@ CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd) fd->Italic, stretchesToCTWidths[fd->Stretch]); } + +// TODO deduplicate this from italicCloseness() +static uiDrawTextItalic italicFromCTItalic(CTFontDescriptorRef desc, CFDictionaryRef traits) +{ + CFNumberRef cfnum; + CTFontSymbolicTraits symbolic; + // there is no kCFNumberUInt32Type, but CTFontSymbolicTraits is uint32_t, so SInt32 should work + SInt32 s; + CFStringRef styleName; + BOOL isOblique; + + cfnum = CFDictionaryGetValue(traits, kCTFontSymbolicTrait); + if (cfnum == NULL) { + // TODO + } + if (CFNumberGetValue(cfnum, kCFNumberSInt32Type, &s) == false) { + // TODO + } + symbolic = (CTFontSymbolicTraits) s; + // Get Rule; do not release cfnum + if ((symbolic & kCTFontItalicTrait) == 0) + return uiDrawTextItalicNormal; + + // Okay, now we know it's either Italic or Oblique + // Pango's Core Text code just does a g_strrstr() (backwards case-sensitive search) for "Oblique" in the font's style name (see https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c); let's do that too I guess + isOblique = NO; // default value + styleName = (CFStringRef) CTFontDescriptorCopyAttribute(desc, kCTFontStyleNameAttribute); + // TODO is styleName guaranteed? + if (styleName != NULL) { + CFRange range; + + // note the use of the toll-free bridge for the string literal, since CFSTR() *can* return NULL + // TODO is this really the case? or is that just a copy-paste error from the other CFStringCreateXxx() functions? and what's this about -fconstant-cfstring? + range = CFStringFind(styleName, (CFStringRef) @"Oblique", kCFCompareBackwards); + if (range.location != kCFNotFound) + isOblique = YES; + CFRelease(styleName); + } + if (isOblique) + return uiDrawTextItalicOblique; + return uiDrawTextItalicItalic; +} + +void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescriptor *uidesc) +{ + CFStringRef cffamily; + CFDictionaryRef traits; + double ctweight, ctstretch; + int wc; + uiDrawTextStretch stretch; + + cffamily = (CFStringRef) CTFontDescriptorCopyAttribute(cfdesc, kCTFontFamilyNameAttribute); + if (cffamily == NULL) { + // TODO + } + // TODO normalize this by adding a uiDarwinCFStringToText() + uidesc->Family = uiDarwinNSStringToText((NSString *) cffamily); + CFRelease(cffamily); + + traits = (CFDictionaryRef) CTFontDescriptorCopyAttribute(cfdesc, kCTFontTraitsAttribute); + if (traits == NULL) { + // TODO + } + ctweight = doubleAttr(traits, kCTFontWeightTrait); + uidesc->Italic = italicFromCTItalic(ctdesc, traits); + ctstretch = doubleAttr(traits, kCTFontWidthTrait); + CFRelease(traits); + + // TODO make sure this is correct + for (wc = 0; wc < 10; wc++) + if (ctweight >= weightsToCTWeights[wc]) + if (ctweight < weightsToCTWeights[wc + 1]) + break; + uidesc->Weight = ((ctweight - weightsToCTWeights[wc]) / (weightsToCTWeights[wc + 1] - weightsToCTWeights[wc])) * 100; + uidesc->Weight += wc * 100; + + // TODO is this correct? + for (stretch = uiDrawTextStretchUltraCondensed; stretch < uiDrawTextStretchUltraExpanded; stretch++) + if (ctstretch <= stretchesToCTWidth[stretch]) + break; + uidesc->Stretch = stretch; +} diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 0a15e3bf..336f5bcf 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -109,15 +109,9 @@ extern uiDrawContext *newContext(CGContextRef, CGFloat); extern void freeContext(uiDrawContext *); // fontbutton.m -#if 0 /* TODO */ extern BOOL fontButtonInhibitSendAction(SEL sel, id from, id to); extern BOOL fontButtonOverrideTargetForAction(SEL sel, id from, id to, id *override); extern void setupFontPanel(void); -#else -static inline BOOL fontButtonInhibitSendAction(SEL sel, id from, id to) { return NO; } -static inline BOOL fontButtonOverrideTargetForAction(SEL sel, id from, id to, id *override) { return NO; } -static inline void setupFontPanel(void) {} -#endif // colorbutton.m extern BOOL colorButtonInhibitSendAction(SEL sel, id from, id to); @@ -148,4 +142,4 @@ extern void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdg // fontmatch.m extern CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd); -extern NSFontDescriptor *fontdescToNSFontDescriptor(uiDrawFontDescriptor *fd); +extern void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescriptor *uidesc); From 67949d79aa71a2710861d3c2d6cbb65de15f163a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 11 Feb 2017 01:13:07 -0500 Subject: [PATCH 0625/1329] And re-added the uiFontButton on OS X and added one to the hit-test example. --- darwin/fontbutton.m | 17 ++++++++++++----- darwin/fontmatch.m | 6 +++--- examples/drawtext/hittest.c | 28 +++++++++++++++++++++++----- 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/darwin/fontbutton.m b/darwin/fontbutton.m index 22bc6465..433f261f 100644 --- a/darwin/fontbutton.m +++ b/darwin/fontbutton.m @@ -11,7 +11,7 @@ - (void)activateFontButton; - (void)deactivateFontButton:(BOOL)activatingAnother; - (void)deactivateOnClose:(NSNotification *)note; -- (uiDrawTextFont *)libuiFont; +- (void)getfontdesc:(uiDrawFontDescriptor *)uidesc; @end // only one may be active at one time @@ -138,9 +138,16 @@ struct uiFontButton { NSFontPanelCollectionModeMask; } -- (uiDrawTextFont *)libuiFont +- (void)getfontdesc:(uiDrawFontDescriptor *)uidesc { - return mkTextFontFromNSFont(self->libui_font); + CTFontRef ctfont; + CTFontDescriptorRef ctdesc; + + ctfont = (CTFontRef) (self->libui_font); + ctdesc = CTFontCopyFontDescriptor(ctfont); + fontdescFromCTFontDescriptor(ctdesc, uidesc); + CFRelease(ctdesc); + uidesc->Size = CTFontGetSize(ctfont); } @end @@ -192,9 +199,9 @@ static void defaultOnChanged(uiFontButton *b, void *data) // do nothing } -uiDrawTextFont *uiFontButtonFont(uiFontButton *b) +void uiFontButtonFont(uiFontButton *b, uiDrawFontDescriptor *desc) { - return [b->button libuiFont]; + [b->button getfontdesc:desc]; } void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data) diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index 821855a7..e1660c8b 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -304,7 +304,7 @@ void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescript int wc; uiDrawTextStretch stretch; - cffamily = (CFStringRef) CTFontDescriptorCopyAttribute(cfdesc, kCTFontFamilyNameAttribute); + cffamily = (CFStringRef) CTFontDescriptorCopyAttribute(ctdesc, kCTFontFamilyNameAttribute); if (cffamily == NULL) { // TODO } @@ -312,7 +312,7 @@ void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescript uidesc->Family = uiDarwinNSStringToText((NSString *) cffamily); CFRelease(cffamily); - traits = (CFDictionaryRef) CTFontDescriptorCopyAttribute(cfdesc, kCTFontTraitsAttribute); + traits = (CFDictionaryRef) CTFontDescriptorCopyAttribute(ctdesc, kCTFontTraitsAttribute); if (traits == NULL) { // TODO } @@ -331,7 +331,7 @@ void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescript // TODO is this correct? for (stretch = uiDrawTextStretchUltraCondensed; stretch < uiDrawTextStretchUltraExpanded; stretch++) - if (ctstretch <= stretchesToCTWidth[stretch]) + if (ctstretch <= stretchesToCTWidths[stretch]) break; uidesc->Stretch = stretch; } diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index 2393e65b..2b80be5e 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -31,8 +31,10 @@ static uiAttributedString *attrstr; #define margins 10 static uiBox *panel; +static uiBox *vbox; static uiLabel *caretLabel; static uiCheckbox *showLineBounds; +static uiFontButton *fontButton; static int caretLine = -1; static size_t caretPos; @@ -189,22 +191,38 @@ static void checkboxChecked(uiCheckbox *c, void *data) redraw(); } -static uiCheckbox *newCheckbox(const char *text) +static void changeFont(uiFontButton *b, void *data) +{ + // TODO free old font name + // TODO rename defaultFont + uiFontButtonFont(fontButton, &defaultFont); + // TODO dump the new font + redraw(); +} + +// TODO share? +static uiCheckbox *newCheckbox(uiBox *box, const char *text) { uiCheckbox *c; c = uiNewCheckbox(text); uiCheckboxOnToggled(c, checkboxChecked, NULL); - uiBoxAppend(panel, uiControl(c), 0); + uiBoxAppend(box, uiControl(c), 0); return c; } struct example *mkHitTestExample(void) { - panel = uiNewVerticalBox(); + panel = uiNewHorizontalBox(); + vbox = uiNewVerticalBox(); + uiBoxAppend(panel, uiControl(vbox), 1); caretLabel = uiNewLabel("Caret information is shown here"); - uiBoxAppend(panel, uiControl(caretLabel), 0); - showLineBounds = newCheckbox("Show Line Bounds (for debugging metrics)"); + uiBoxAppend(vbox, uiControl(caretLabel), 0); + showLineBounds = newCheckbox(vbox, "Show Line Bounds (for debugging metrics)"); + fontButton = uiNewFontButton(); + uiFontButtonOnChanged(fontButton, changeFont, NULL); + // TODO set the font button to the current defaultFont + uiBoxAppend(panel, uiControl(fontButton), 0); hitTestExample.name = "Hit-Testing and Grapheme Boundaries"; hitTestExample.panel = uiControl(panel); From a014eb27e65ec3555f4d82c2a7207722cbee79e4 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 11 Feb 2017 14:22:23 -0500 Subject: [PATCH 0626/1329] More TODO completion in the drawtext example. --- examples/drawtext/hittest.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index 2b80be5e..8e44e5c0 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -193,10 +193,16 @@ static void checkboxChecked(uiCheckbox *c, void *data) static void changeFont(uiFontButton *b, void *data) { - // TODO free old font name + if (defaultFont.Family != fontFamily) + uiFreeText(defaultFont.Family); // TODO rename defaultFont uiFontButtonFont(fontButton, &defaultFont); - // TODO dump the new font + printf("{\n\tfamily: %s\n\tsize: %g\n\tweight: %d\n\titalic: %d\n\tstretch: %d\n}\n", + defaultFont.Family, + defaultFont.Size, + (int) (defaultFont.Weight), + (int) (defaultFont.Italic), + (int) (defaultFont.Stretch)); redraw(); } From 1a8f7ad405024bc507d761e5c5d3c46bb6d78dcc Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 11 Feb 2017 14:45:58 -0500 Subject: [PATCH 0627/1329] Reimplemented uiFontButton on GTK+. --- unix/CMakeLists.txt | 2 +- unix/drawtext.c | 22 ++++++++++++++++++++++ unix/fontbutton.c | 12 +++++------- unix/uipriv_unix.h | 9 +++------ 4 files changed, 31 insertions(+), 14 deletions(-) diff --git a/unix/CMakeLists.txt b/unix/CMakeLists.txt index 1684e2ce..9300bcb7 100644 --- a/unix/CMakeLists.txt +++ b/unix/CMakeLists.txt @@ -22,7 +22,7 @@ list(APPEND _LIBUI_SOURCES unix/drawtext.c unix/editablecombo.c unix/entry.c -# unix/fontbutton.c + unix/fontbutton.c unix/form.c unix/future.c unix/graphemes.c diff --git a/unix/drawtext.c b/unix/drawtext.c index 175403ad..4cbe3c71 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -280,3 +280,25 @@ void caretDrawParams(uiDrawContext *c, double height, struct caretDrawParams *p) p->xoff = xoff; p->width = width; } + +// TODO split this and the other font description stuff into their own file? +void fontdescFromPangoFontDescription(PangoFontDescription *pdesc, uiDrawFontDescriptor *uidesc) +{ + PangoStyle pitalic; + PangoStretch pstretch; + + uidesc->Family = uiUnixStrdupText(pango_font_description_get_family(pdesc)); + pitalic = pango_font_description_get_style(pdesc); + // TODO reverse the above misalignment if it is corrected + uidesc->Weight = pango_font_description_get_weight(pdesc); + pstretch = pango_font_description_get_stretch(pdesc); + // absolute size does not matter because, as above, 1 device unit == 1 cairo point + uidesc->Size = pango_units_to_double(pango_font_description_get_size(pdesc)); + + for (uidesc->Italic = uiDrawTextItalicNormal; uidesc->Italic < uiDrawTextItalicItalic; uidesc->Italic++) + if (pangoItalics[uidesc->Italic] == pitalic) + break; + for (uidesc->Stretch = uiDrawTextStretchUltraCondensed; uidesc->Stretch < uiDrawTextStretchUltraExpanded; uidesc->Stretch++) + if (pangoStretches[uidesc->Stretch] == pstretch) + break; +} diff --git a/unix/fontbutton.c b/unix/fontbutton.c index f8047e08..9a2552b1 100644 --- a/unix/fontbutton.c +++ b/unix/fontbutton.c @@ -26,16 +26,14 @@ static void defaultOnChanged(uiFontButton *b, void *data) // do nothing } -uiDrawTextFont *uiFontButtonFont(uiFontButton *b) +void uiFontButtonFont(uiFontButton *b, uiDrawFontDescriptor *desc) { - PangoFont *f; - PangoFontDescription *desc; + PangoFontDescription *pdesc; - desc = gtk_font_chooser_get_font_desc(b->fc); - f = pangoDescToPangoFont(desc); + pdesc = gtk_font_chooser_get_font_desc(b->fc); + fontdescFromPangoFontDescription(pdesc, desc); // desc is transfer-full and thus is a copy - pango_font_description_free(desc); - return mkTextFont(f, FALSE); // we hold the initial reference; no need to ref + pango_font_description_free(pdesc); } void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data) diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index 3764f8ef..aaae8b34 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -45,12 +45,6 @@ extern void childSetMargined(struct child *c, int margined); extern uiDrawContext *newContext(cairo_t *cr, GtkStyleContext *style); extern void freeContext(uiDrawContext *); -// drawtext.c -#if 0 /* TODO */ -extern uiDrawTextFont *mkTextFont(PangoFont *f, gboolean add); -extern PangoFont *pangoDescToPangoFont(PangoFontDescription *pdesc); -#endif - // image.c /*TODO remove this*/typedef struct uiImage uiImage; extern cairo_surface_t *imageAppropriateSurface(uiImage *i, GtkWidget *w); @@ -62,3 +56,6 @@ extern GtkCellRenderer *newCellRendererButton(void); extern void loadFutures(void); extern PangoAttribute *FUTURE_pango_attr_foreground_alpha_new(guint16 alpha); extern gboolean FUTURE_gtk_widget_path_iter_set_object_name(GtkWidgetPath *path, gint pos, const char *name); + +// drawtext.c +extern void fontdescFromPangoFontDescription(PangoFontDescription *pdesc, uiDrawFontDescriptor *uidesc); From dca92d507ee2e99d779d3f828070c47a0db1a121 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 11 Feb 2017 16:55:30 -0500 Subject: [PATCH 0628/1329] And readded uiFontButton on Windows. Woo! Now we have to deal with styles. --- windows/CMakeLists.txt | 4 ++-- windows/_uipriv_migrate.hpp | 27 --------------------------- windows/drawtext.cpp | 19 +++++++++++++++++++ windows/dwrite.cpp | 4 ---- windows/fontbutton.cpp | 8 ++++---- windows/fontdialog.cpp | 1 + windows/uipriv_windows.hpp | 3 +++ 7 files changed, 29 insertions(+), 37 deletions(-) diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index a9551776..5c401d06 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -26,8 +26,8 @@ list(APPEND _LIBUI_SOURCES windows/editablecombo.cpp windows/entry.cpp windows/events.cpp -# windows/fontbutton.cpp -# windows/fontdialog.cpp + windows/fontbutton.cpp + windows/fontdialog.cpp windows/form.cpp windows/graphemes.cpp windows/grid.cpp diff --git a/windows/_uipriv_migrate.hpp b/windows/_uipriv_migrate.hpp index 11b737d1..b9c365cc 100644 --- a/windows/_uipriv_migrate.hpp +++ b/windows/_uipriv_migrate.hpp @@ -14,13 +14,9 @@ extern uiDrawContext *newContext(ID2D1RenderTarget *); extern void freeContext(uiDrawContext *); // dwrite.cpp -#ifdef __cplusplus extern IDWriteFactory *dwfactory; -#endif extern HRESULT initDrawText(void); extern void uninitDrawText(void); -#if 0 /* TODO */ -#ifdef __cplusplus struct fontCollection { IDWriteFontCollection *fonts; WCHAR userLocale[LOCALE_NAME_MAX_LENGTH]; @@ -30,29 +26,8 @@ extern fontCollection *loadFontCollection(void); extern WCHAR *fontCollectionFamilyName(fontCollection *fc, IDWriteFontFamily *family); extern void fontCollectionFree(fontCollection *fc); extern WCHAR *fontCollectionCorrectString(fontCollection *fc, IDWriteLocalizedStrings *names); -#endif -#endif - -// drawtext.cpp -#if 0 /* TODO */ -#ifdef __cplusplus -extern uiDrawTextFont *mkTextFont(IDWriteFont *df, BOOL addRef, WCHAR *family, BOOL copyFamily, double size); -struct dwriteAttr { - uiDrawTextWeight weight; - uiDrawTextItalic italic; - uiDrawTextStretch stretch; - DWRITE_FONT_WEIGHT dweight; - DWRITE_FONT_STYLE ditalic; - DWRITE_FONT_STRETCH dstretch; -}; -extern void attrToDWriteAttr(struct dwriteAttr *attr); -extern void dwriteAttrToAttr(struct dwriteAttr *attr); -#endif -#endif // fontdialog.cpp -#if 0 /* TODO */ -#ifdef __cplusplus struct fontDialogParams { IDWriteFont *font; double size; @@ -63,5 +38,3 @@ extern BOOL showFontDialog(HWND parent, struct fontDialogParams *params); extern void loadInitialFontDialogParams(struct fontDialogParams *params); extern void destroyFontDialogParams(struct fontDialogParams *params); extern WCHAR *fontDialogParamsToString(struct fontDialogParams *params); -#endif -#endif diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index b7a5ccdf..4049bbb1 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -401,3 +401,22 @@ void caretDrawParams(uiDrawContext *c, double height, struct caretDrawParams *p) // and there doesn't seem to be this either... (TODO check what PadWrite does?) p->xoff = 0; } + +// TODO split this and the above related font matching code into a separate file? +void fontdescFromIDWriteFont(IDWriteFont *font, uiDrawFontDescriptor *uidesc) +{ + DWRITE_FONT_STYLE dwitalic; + DWRITE_FONT_STRETCH dwstretch; + + dwitalic = font->GetStyle(); + // TODO reverse the above misalignment if it is corrected + uidesc->Weight = (uiDrawTextWeight) (font->GetWeight()); + dwstretch = font->GetStretch(); + + for (uidesc->Italic = uiDrawTextItalicNormal; uidesc->Italic < uiDrawTextItalicItalic; uidesc->Italic++) + if (dwriteItalics[uidesc->Italic] == dwitalic) + break; + for (uidesc->Stretch = uiDrawTextStretchUltraCondensed; uidesc->Stretch < uiDrawTextStretchUltraExpanded; uidesc->Stretch++) + if (dwriteStretches[uidesc->Stretch] == dwstretch) + break; +} diff --git a/windows/dwrite.cpp b/windows/dwrite.cpp index 6e9e5835..2e9ce928 100644 --- a/windows/dwrite.cpp +++ b/windows/dwrite.cpp @@ -17,8 +17,6 @@ void uninitDrawText(void) dwfactory->Release(); } -#if 0 /* TODO */ - fontCollection *loadFontCollection(void) { fontCollection *fc; @@ -88,5 +86,3 @@ void fontCollectionFree(fontCollection *fc) fc->fonts->Release(); uiFree(fc); } - -#endif diff --git a/windows/fontbutton.cpp b/windows/fontbutton.cpp index d2d4dabf..ab9fbe73 100644 --- a/windows/fontbutton.cpp +++ b/windows/fontbutton.cpp @@ -86,11 +86,11 @@ static void defaultOnChanged(uiFontButton *b, void *data) // do nothing } -uiDrawTextFont *uiFontButtonFont(uiFontButton *b) +void uiFontButtonFont(uiFontButton *b, uiDrawFontDescriptor *desc) { - // we don't own b->params.font; we have to add a reference - // we don't own b->params.familyName either; we have to copy it - return mkTextFont(b->params.font, TRUE, b->params.familyName, TRUE, b->params.size); + fontdescFromIDWriteFont(b->params.font, desc); + desc->Family = toUTF8(b->params.familyName); + desc->Size = b->params.size; } void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data) diff --git a/windows/fontdialog.cpp b/windows/fontdialog.cpp index 603a17db..d7fa91e9 100644 --- a/windows/fontdialog.cpp +++ b/windows/fontdialog.cpp @@ -320,6 +320,7 @@ static void sizeEdited(struct fontDialog *f) wsize = windowText(f->sizeCombobox); // this is what the Choose Font dialog does; it swallows errors while the real ChooseFont() is not lenient (and only checks on OK) size = wcstod(wsize, NULL); + // TODO free wsize? I forget already if (size <= 0) // don't change on invalid size return; f->curSize = size; diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index 2354bfd7..f1de95cf 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -159,3 +159,6 @@ extern D2D1_SIZE_F realGetSize(ID2D1RenderTarget *rt); // draw.cpp extern ID2D1DCRenderTarget *makeHDCRenderTarget(HDC dc, RECT *r); + +// drawtext.cpp +extern void fontdescFromIDWriteFont(IDWriteFont *font, uiDrawFontDescriptor *uidesc); From 491ec3ae499df0b1f48941ae5fb651195718725a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 11 Feb 2017 17:10:59 -0500 Subject: [PATCH 0629/1329] Actually let's do paragraph alignment first. Haven't tested hit-test with that just yet. --- ui_attrstr.h | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index 9fb81513..8e95be98 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -86,8 +86,22 @@ struct uiDrawFontDescriptor { }; typedef struct uiDrawTextLayout uiDrawTextLayout; +typedef struct uiDrawTextLayoutParams uiDrawTextLayoutParams; typedef struct uiDrawTextLayoutLineMetrics uiDrawTextLayoutLineMetrics; +_UI_ENUM(uiDrawTextLayoutAlign) { + uiDrawTextLayoutAlignLeft, + uiDrawTextLayoutAlignCenter, + uiDrawTextLayoutAlignRight, +}; + +struct uiDrawTextLayoutParams { + uiAttributedString *String; + uiDrawFontDescriptor *DefaultFont; + double Width; + uiDrawTextLayoutAlign Align; +}; + struct uiDrawTextLayoutLineMetrics { // This describes the overall bounding box of the line. // TODO figure out if X is correct regardless of both alignment and writing direction @@ -128,7 +142,7 @@ struct uiDrawTextLayoutLineMetrics { // - uiDrawTextLayoutRangeForSize() (returns what substring would fit in a given size) // - uiDrawTextLayoutNewWithHeight() (limits amount of string used by the height) // - some function to fix up a range (for text editing) -_UI_EXTERN uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescriptor *defaultFont, double width); +_UI_EXTERN uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *params); _UI_EXTERN void uiDrawFreeTextLayout(uiDrawTextLayout *tl); _UI_EXTERN void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y); _UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height); From 8a64a1dfb01ed386819b8a11dc62ad6fbd2ca17d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 11 Feb 2017 19:47:20 -0500 Subject: [PATCH 0630/1329] Made the OS X code and the example program use the new layout stuff. --- darwin/drawtext.m | 12 ++++++------ examples/drawtext/basic.c | 11 +++++++---- examples/drawtext/hittest.c | 14 ++++++++------ ui_attrstr.h | 1 + 4 files changed, 22 insertions(+), 16 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 3fb12e54..e7a59164 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -155,7 +155,7 @@ static uiDrawTextLayoutLineMetrics *computeLineMetrics(CTFrameRef frame, CGSize return metrics; } -uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescriptor *defaultFont, double width) +uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) { uiDrawTextLayout *tl; CGFloat cgwidth; @@ -163,10 +163,10 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto CGRect rect; tl = uiNew(uiDrawTextLayout); - tl->attrstr = attrstrToCoreFoundation(s, defaultFont); + tl->attrstr = attrstrToCoreFoundation(p->String, p->DefaultFont); range.location = 0; range.length = CFAttributedStringGetLength(tl->attrstr); - tl->width = width; + tl->width = p->Width; // TODO CTFrameProgression for RTL/LTR // TODO kCTParagraphStyleSpecifierMaximumLineSpacing, kCTParagraphStyleSpecifierMinimumLineSpacing, kCTParagraphStyleSpecifierLineSpacingAdjustment for line spacing @@ -175,7 +175,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto // TODO } - cgwidth = (CGFloat) width; + cgwidth = (CGFloat) (tl->width); if (cgwidth < 0) cgwidth = CGFLOAT_MAX; // TODO these seem to be floor()'d or truncated? @@ -204,8 +204,8 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto tl->lineMetrics = computeLineMetrics(tl->frame, tl->size); // and finally copy the UTF-8/UTF-16 conversion tables - tl->u8tou16 = attrstrCopyUTF8ToUTF16(s, &(tl->nUTF8)); - tl->u16tou8 = attrstrCopyUTF16ToUTF8(s, &(tl->nUTF16)); + tl->u8tou16 = attrstrCopyUTF8ToUTF16(p->String, &(tl->nUTF8)); + tl->u16tou8 = attrstrCopyUTF16ToUTF8(p->String, &(tl->nUTF16)); return tl; } diff --git a/examples/drawtext/basic.c b/examples/drawtext/basic.c index e1aaca02..1ddbbe6c 100644 --- a/examples/drawtext/basic.c +++ b/examples/drawtext/basic.c @@ -18,7 +18,7 @@ static const char *text = "and important." ""; static char fontFamily[] = "Palatino"; -// TODO should be const; look at constructor function +// TODO should be const; look at constructor function? static uiDrawFontDescriptor defaultFont = { .Family = fontFamily, .Size = 18, @@ -27,6 +27,7 @@ static uiDrawFontDescriptor defaultFont = { .Stretch = uiDrawTextStretchNormal, }; static uiAttributedString *attrstr; +static uiDrawTextLayoutParams params; #define margins 10 @@ -141,9 +142,8 @@ static void draw(uiAreaDrawParams *p) uiDrawFreePath(path); #endif - layout = uiDrawNewTextLayout(attrstr, - &defaultFont, - p->AreaWidth - 2 * margins); + params.Width = p->AreaWidth - 2 * margins; + layout = uiDrawNewTextLayout(¶ms); uiDrawText(p->Context, layout, margins, margins); uiDrawRestore(p->Context); @@ -257,6 +257,9 @@ struct example *mkBasicExample(void) basicExample.key = NULL; attrstr = uiNewAttributedString(text); + params.String = attrstr; + params.DefaultFont = &defaultFont; + params.Align = uiDrawTextLayoutAlignLeft; return &basicExample; } diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index 8e44e5c0..3b6125b0 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -27,6 +27,7 @@ static uiDrawFontDescriptor defaultFont = { .Stretch = uiDrawTextStretchNormal, }; static uiAttributedString *attrstr; +static uiDrawTextLayoutParams params; #define margins 10 @@ -88,9 +89,8 @@ static void draw(uiAreaDrawParams *p) uiDrawClip(p->Context, path); uiDrawFreePath(path); - layout = uiDrawNewTextLayout(attrstr, - &defaultFont, - p->AreaWidth - 2 * margins); + params.Width = p->AreaWidth - 2 * margins; + layout = uiDrawNewTextLayout(¶ms); uiDrawText(p->Context, layout, margins, margins); uiDrawRestore(p->Context); @@ -130,9 +130,8 @@ static void mouse(uiAreaMouseEvent *e) if (e->Down != 1) return; - layout = uiDrawNewTextLayout(attrstr, - &defaultFont, - e->AreaWidth - 2 * margins); + params.Width = e->AreaWidth - 2 * margins; + layout = uiDrawNewTextLayout(¶ms); uiDrawTextLayoutHitTest(layout, e->X - margins, e->Y - margins, &caretPos, &caretLine); @@ -237,6 +236,9 @@ struct example *mkHitTestExample(void) hitTestExample.key = key; attrstr = uiNewAttributedString(text); + params.String = attrstr; + params.DefaultFont = &defaultFont; + params.Align = uiDrawTextLayoutAlignLeft; return &hitTestExample; } diff --git a/ui_attrstr.h b/ui_attrstr.h index 8e95be98..63aed4c0 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -89,6 +89,7 @@ typedef struct uiDrawTextLayout uiDrawTextLayout; typedef struct uiDrawTextLayoutParams uiDrawTextLayoutParams; typedef struct uiDrawTextLayoutLineMetrics uiDrawTextLayoutLineMetrics; +// TODO drop the Layout from this? _UI_ENUM(uiDrawTextLayoutAlign) { uiDrawTextLayoutAlignLeft, uiDrawTextLayoutAlignCenter, From bd66e70452224c4528b27f9b6a79122b063987ff Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 11 Feb 2017 19:56:26 -0500 Subject: [PATCH 0631/1329] And added the alignment flag to the example program. --- examples/drawtext/hittest.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index 3b6125b0..03012891 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -36,6 +36,7 @@ static uiBox *vbox; static uiLabel *caretLabel; static uiCheckbox *showLineBounds; static uiFontButton *fontButton; +static uiCombobox *textAlign; static int caretLine = -1; static size_t caretPos; @@ -205,6 +206,13 @@ static void changeFont(uiFontButton *b, void *data) redraw(); } +static void changeTextAlign(uiCombobox *c, void *data) +{ + // note the order of the items added below + params.Align = (uiDrawTextLayoutAlign) uiComboboxSelected(textAlign); + redraw(); +} + // TODO share? static uiCheckbox *newCheckbox(uiBox *box, const char *text) { @@ -220,14 +228,24 @@ struct example *mkHitTestExample(void) { panel = uiNewHorizontalBox(); vbox = uiNewVerticalBox(); + // TODO the second vbox causes this not to stretch at least on OS X uiBoxAppend(panel, uiControl(vbox), 1); caretLabel = uiNewLabel("Caret information is shown here"); uiBoxAppend(vbox, uiControl(caretLabel), 0); showLineBounds = newCheckbox(vbox, "Show Line Bounds (for debugging metrics)"); + vbox = uiNewVerticalBox(); + uiBoxAppend(panel, uiControl(vbox), 0); fontButton = uiNewFontButton(); uiFontButtonOnChanged(fontButton, changeFont, NULL); // TODO set the font button to the current defaultFont - uiBoxAppend(panel, uiControl(fontButton), 0); + uiBoxAppend(vbox, uiControl(fontButton), 0); + textAlign = uiNewCombobox(); + // note that these are in the order in the enum + uiComboboxAppend(textAlign, "Left"); + uiComboboxAppend(textAlign, "Center"); + uiComboboxAppend(textAlign, "Right"); + uiComboboxOnSelected(textAlign, changeTextAlign, NULL); + uiBoxAppend(vbox, uiControl(textAlign), 0); hitTestExample.name = "Hit-Testing and Grapheme Boundaries"; hitTestExample.panel = uiControl(panel); From 44f8409b8c4b5f3b0bc0810da604923df7cc3436 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 11 Feb 2017 21:10:16 -0500 Subject: [PATCH 0632/1329] And implemented the alignment stuff on OS X. --- darwin/drawtext.m | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index e7a59164..bae7326d 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -46,27 +46,55 @@ static CTFontRef fontdescToCTFont(uiDrawFontDescriptor *fd) return font; } -static CFAttributedStringRef attrstrToCoreFoundation(uiAttributedString *s, uiDrawFontDescriptor *defaultFont) +static const CTTextAlignment ctaligns[] = { + [uiDrawTextLayoutAlignLeft] = kCTTextAlignmentLeft, + [uiDrawTextLayoutAlignCenter] = kCTTextAlignmentCenter, + [uiDrawTextLayoutAlignRight] = kCTTextAlignmentRight, +}; + +static CTParagraphStyleRef mkParagraphStyle(uiDrawTextLayoutParams *p) +{ + CTParagraphStyleRef ps; + CTParagraphStyleSetting settings[16]; + size_t nSettings = 0; + + settings[nSettings].spec = kCTParagraphStyleSpecifierAlignment; + settings[nSettings].valueSize = sizeof (CTTextAlignment); + settings[nSettings].value = ctaligns + p->Align; + nSettings++; + + ps = CTParagraphStyleCreate(settings, nSettings); + if (ps == NULL) { + // TODO + } + return ps; +} + +static CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p) { CFStringRef cfstr; CFMutableDictionaryRef defaultAttrs; CTFontRef defaultCTFont; + CTParagraphStyleRef ps; CFAttributedStringRef base; CFMutableAttributedStringRef mas; - cfstr = CFStringCreateWithCharacters(NULL, attrstrUTF16(s), attrstrUTF16Len(s)); + cfstr = CFStringCreateWithCharacters(NULL, attrstrUTF16(p->String), attrstrUTF16Len(p->String)); if (cfstr == NULL) { // TODO } - defaultAttrs = CFDictionaryCreateMutable(NULL, 1, + defaultAttrs = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); if (defaultAttrs == NULL) { // TODO } - defaultCTFont = fontdescToCTFont(defaultFont); + defaultCTFont = fontdescToCTFont(p->DefaultFont); CFDictionaryAddValue(defaultAttrs, kCTFontAttributeName, defaultCTFont); CFRelease(defaultCTFont); + ps = mkParagraphStyle(p); + CFDictionaryAddValue(defaultAttrs, kCTParagraphStyleAttributeName, ps); + CFRelease(ps); base = CFAttributedStringCreate(NULL, cfstr, defaultAttrs); if (base == NULL) { @@ -163,7 +191,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) CGRect rect; tl = uiNew(uiDrawTextLayout); - tl->attrstr = attrstrToCoreFoundation(p->String, p->DefaultFont); + tl->attrstr = attrstrToCoreFoundation(p); range.location = 0; range.length = CFAttributedStringGetLength(tl->attrstr); tl->width = p->Width; From 210c4507cadf81f9e5521ef6674cd2f323b41756 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 11 Feb 2017 21:25:41 -0500 Subject: [PATCH 0633/1329] Implemented uiDrawTextLayoutParams and alignment on GTK+. --- unix/drawtext.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/unix/drawtext.c b/unix/drawtext.c index 4cbe3c71..abc9aa65 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -90,7 +90,13 @@ static void computeLineMetrics(uiDrawTextLayout *tl) pango_layout_iter_free(iter); } -uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescriptor *defaultFont, double width) +static const PangoAlignment pangoAligns[] = { + [uiDrawTextLayoutAlignLeft] = PANGO_ALIGN_LEFT, + [uiDrawTextLayoutAlignCenter] = PANGO_ALIGN_CENTER, + [uiDrawTextLayoutAlignRight] = PANGO_ALIGN_RIGHT, +}; + +uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) { uiDrawTextLayout *tl; PangoContext *context; @@ -106,29 +112,31 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto g_object_unref(context); // this is safe; pango_layout_set_text() copies the string - pango_layout_set_text(tl->layout, uiAttributedStringString(s), -1); + pango_layout_set_text(tl->layout, uiAttributedStringString(p->String), -1); desc = pango_font_description_new(); - pango_font_description_set_family(desc, defaultFont->Family); - pango_font_description_set_style(desc, pangoItalics[defaultFont->Italic]); + pango_font_description_set_family(desc, p->DefaultFont->Family); + pango_font_description_set_style(desc, pangoItalics[p->DefaultFont->Italic]); // for the most part, pango weights correlate to ours // the differences: // - Book — libui: 350, Pango: 380 // - Ultra Heavy — libui: 950, Pango: 1000 // TODO figure out what to do about this misalignment - pango_font_description_set_weight(desc, defaultFont->Weight); - pango_font_description_set_stretch(desc, pangoStretches[defaultFont->Stretch]); + pango_font_description_set_weight(desc, p->DefaultFont->Weight); + pango_font_description_set_stretch(desc, pangoStretches[p->DefaultFont->Stretch]); // see https://developer.gnome.org/pango/1.30/pango-Fonts.html#pango-font-description-set-size and https://developer.gnome.org/pango/1.30/pango-Glyph-Storage.html#pango-units-from-double - pango_font_description_set_size(desc, pango_units_from_double(defaultFont->Size)); + pango_font_description_set_size(desc, pango_units_from_double(p->DefaultFont->Size)); pango_layout_set_font_description(tl->layout, desc); // this is safe; the description is copied pango_font_description_free(desc); - pangoWidth = cairoToPango(width); - if (width < 0) + pangoWidth = cairoToPango(p->Width); + if (p->Width < 0) pangoWidth = -1; pango_layout_set_width(tl->layout, pangoWidth); + pango_layout_set_alignment(tl->layout, pangoAligns[p->Align]); + // TODO attributes tl->nLines = pango_layout_get_line_count(tl->layout); From c4a97792ea347d167de13eb273c5242d03ab5cc8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 11 Feb 2017 21:54:06 -0500 Subject: [PATCH 0634/1329] And implemented the new stuff on Windows. --- examples/drawtext/hittest.c | 2 +- windows/drawtext.cpp | 32 +++++++++++++++++++++----------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index 03012891..48d2c409 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -1,7 +1,7 @@ // 20 january 2017 #include "drawtext.h" -// TODO have a ligature +// TODO double-check ligatures // TODO the hiding and showing does not work properly on GTK+ // TODO using the arrow keys allows us to walk back to the end of the line; IIRC arrow keys shouldn't do that diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 4049bbb1..da512fd9 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -129,7 +129,14 @@ static void computeLineInfo(uiDrawTextLayout *tl) delete[] dlm; } -uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescriptor *defaultFont, double width) +// TODO should be const but then I can't operator[] on it; the real solution is to find a way to do designated array initializers in C++11 but I do not know enough C++ voodoo to make it work (it is possible but no one else has actually done it before) +static std::map dwriteAligns = { + { uiDrawTextLayoutAlignLeft, DWRITE_TEXT_ALIGNMENT_LEADING }, + { uiDrawTextLayoutAlignCenter, DWRITE_TEXT_ALIGNMENT_CENTER }, + { uiDrawTextLayoutAlignRight, DWRITE_TEXT_ALIGNMENT_TRAILING }, +}; + +uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) { uiDrawTextLayout *tl; WCHAR *wDefaultFamily; @@ -139,7 +146,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto tl = uiNew(uiDrawTextLayout); - wDefaultFamily = toUTF16(defaultFont->Family); + wDefaultFamily = toUTF16(p->DefaultFont->Family); hr = dwfactory->CreateTextFormat( wDefaultFamily, NULL, // for the most part, DirectWrite weights correlate to ours @@ -147,19 +154,22 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto // - Minimum — libui: 0, DirectWrite: 1 // - Maximum — libui: 1000, DirectWrite: 999 // TODO figure out what to do about this shorter range (the actual major values are the same (but with different names), so it's just a range issue) - (DWRITE_FONT_WEIGHT) (defaultFont->Weight), - dwriteItalics[defaultFont->Italic], - dwriteStretches[defaultFont->Stretch], - pointSizeToDWriteSize(defaultFont->Size), + (DWRITE_FONT_WEIGHT) (p->DefaultFont->Weight), + dwriteItalics[p->DefaultFont->Italic], + dwriteStretches[p->DefaultFont->Stretch], + pointSizeToDWriteSize(p->DefaultFont->Size), // see http://stackoverflow.com/questions/28397971/idwritefactorycreatetextformat-failing and https://msdn.microsoft.com/en-us/library/windows/desktop/dd368203.aspx // TODO use the current locale? L"", &(tl->format)); if (hr != S_OK) logHRESULT(L"error creating IDWriteTextFormat", hr); + hr = tl->format->SetTextAlignment(dwriteAligns[p->Align]); + if (hr != S_OK) + logHRESULT(L"error applying text layout alignment", hr); hr = dwfactory->CreateTextLayout( - (const WCHAR *) attrstrUTF16(s), attrstrUTF16Len(s), + (const WCHAR *) attrstrUTF16(p->String), attrstrUTF16Len(p->String), tl->format, // FLOAT is float, not double, so this should work... TODO FLT_MAX, FLT_MAX, @@ -170,8 +180,8 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto // and set the width // this is the only wrapping mode (apart from "no wrap") available prior to Windows 8.1 (TODO verify this fact) (TODO this should be the default anyway) wrap = DWRITE_WORD_WRAPPING_WRAP; - maxWidth = (FLOAT) width; - if (width < 0) { + maxWidth = (FLOAT) (p->Width); + if (p->Width < 0) { // TODO is this wrapping juggling even necessary? wrap = DWRITE_WORD_WRAPPING_NO_WRAP; // setting the max width in this case technically isn't needed since the wrap mode will simply ignore the max width, but let's do it just to be safe @@ -187,8 +197,8 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescripto computeLineInfo(tl); // and finally copy the UTF-8/UTF-16 index conversion tables - tl->u8tou16 = attrstrCopyUTF8ToUTF16(s, &(tl->nUTF8)); - tl->u16tou8 = attrstrCopyUTF16ToUTF8(s, &(tl->nUTF16)); + tl->u8tou16 = attrstrCopyUTF8ToUTF16(p->String, &(tl->nUTF8)); + tl->u16tou8 = attrstrCopyUTF16ToUTF8(p->String, &(tl->nUTF16)); // TODO can/should this be moved elsewhere? uiFree(wDefaultFamily); From de638f3e76d64c882f94754f93c4e211b922e607 Mon Sep 17 00:00:00 2001 From: nopara73 Date: Sun, 12 Feb 2017 12:55:43 +0900 Subject: [PATCH 0635/1329] Add .NET Core bindings --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 01275c0e..a9ddce2e 100644 --- a/README.md +++ b/README.md @@ -147,7 +147,8 @@ Other people have made bindings to other languages: Language | Bindings --- | --- -C#/.net | [LibUI.Binding](https://github.com/NattyNarwhal/LibUI.Binding), [SharpUI](https://github.com/benpye/sharpui/) +C# / .NET Framework | [LibUI.Binding](https://github.com/NattyNarwhal/LibUI.Binding), [SharpUI](https://github.com/benpye/sharpui/) +C# / .NET Core | [DevZH.UI](https://github.com/noliar/DevZH.UI) CHICKEN Scheme | [wasamasa/libui](https://github.com/wasamasa/libui) Crystal | [libui.cr](https://github.com/Fusion/libui.cr) D | [DerelictLibui (flat API)](https://github.com/Extrawurst/DerelictLibui), [libuid (object-oriented)](https://github.com/mogud/libuid) From 32a0284edcb3bedfd31f19d9184ac06137611a3b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 11 Feb 2017 23:19:30 -0500 Subject: [PATCH 0636/1329] Started work on actual attributed text. This includes the beginnings of an attributed text example. Now to implement. --- common/attrstr.c | 5 ++ examples/CMakeLists.txt | 1 + examples/drawtext/attributes.c | 159 +++++++++++++++++++++++++++++++++ examples/drawtext/drawtext.h | 3 + examples/drawtext/main.c | 5 ++ ui_attrstr.h | 11 ++- 6 files changed, 181 insertions(+), 3 deletions(-) create mode 100644 examples/drawtext/attributes.c diff --git a/common/attrstr.c b/common/attrstr.c index f62c42f2..93e4e8f4 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -297,6 +297,11 @@ size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos) return pos; } +void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttribute type, uintptr_t value, size_t start, size_t end) +{ + attrlistInsertAttribute(s->attrs, type, value, start, end); +} + // TODO introduce an iterator? void uiAttributedStringForEachAttribute(uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data) { diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 3fda1b82..64c7098c 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -32,6 +32,7 @@ if(NOT WIN32) endif() _add_example(drawtext + drawtext/attributes.c drawtext/basic.c drawtext/hittest.c drawtext/main.c diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c new file mode 100644 index 00000000..b66635df --- /dev/null +++ b/examples/drawtext/attributes.c @@ -0,0 +1,159 @@ +// 11 february 2017 +#include "drawtext.h" + +static uiAttributedString *attrstr; + +static void setupAttributedString(void) +{ + size_t start, end; + const char *next; + + attrstr = uiNewAttributedString("uiAttributedString isn't just for plain text! It supports "); + + next = "multiple fonts"; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + uiAttributedStringSetAttribute(attrstr, + uiAttributeFamily, + (uintptr_t) "Courier New", + start, end); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "multiple sizes"; +} + +static char fontFamily[] = "Times New Roman"; +// TODO should be const; look at constructor function? +static uiDrawFontDescriptor defaultFont = { + .Family = fontFamily, + .Size = 12, + .Weight = uiDrawTextWeightNormal, + .Italic = uiDrawTextItalicNormal, + .Stretch = uiDrawTextStretchNormal, +}; +static uiDrawTextLayoutParams params; + +#define margins 10 + +static uiBox *panel; +static uiCheckbox *showExtents; +static uiCheckbox *showLineBounds; +static uiCheckbox *showLineGuides; + +// TODO should be const? +static uiDrawBrush fillBrushes[4] = { + { + .Type = uiDrawBrushTypeSolid, + .R = 1.0, + .G = 0.0, + .B = 0.0, + .A = 0.5, + }, + { + .Type = uiDrawBrushTypeSolid, + .R = 0.0, + .G = 1.0, + .B = 0.0, + .A = 0.5, + }, + { + .Type = uiDrawBrushTypeSolid, + .R = 0.0, + .G = 0.0, + .B = 1.0, + .A = 0.5, + }, + { + .Type = uiDrawBrushTypeSolid, + .R = 0.0, + .G = 1.0, + .B = 1.0, + .A = 0.5, + }, +}; + +static void draw(uiAreaDrawParams *p) +{ + uiDrawPath *path; + uiDrawTextLayout *layout; + uiDrawBrush b; + + b.Type = uiDrawBrushTypeSolid; + + // only clip the text, not the guides + uiDrawSave(p->Context); + + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathAddRectangle(path, margins, margins, + p->AreaWidth - 2 * margins, + p->AreaHeight - 2 * margins); + uiDrawPathEnd(path); + uiDrawClip(p->Context, path); + uiDrawFreePath(path); + + params.Width = p->AreaWidth - 2 * margins; + layout = uiDrawNewTextLayout(¶ms); + uiDrawText(p->Context, layout, margins, margins); + + uiDrawRestore(p->Context); + + if (uiCheckboxChecked(showLineBounds)) { + uiDrawTextLayoutLineMetrics m; + int i, n; + int fill = 0; + + n = uiDrawTextLayoutNumLines(layout); + for (i = 0; i < n; i++) { + uiDrawTextLayoutLineGetMetrics(layout, i, &m); + + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathAddRectangle(path, margins + m.X, margins + m.Y, + m.Width, m.Height); + uiDrawPathEnd(path); + uiDrawFill(p->Context, path, fillBrushes + fill); + uiDrawFreePath(path); + fill = (fill + 1) % 4; + } + } + + uiDrawFreeTextLayout(layout); +} + +static struct example attributesExample; + +// TODO share? +static void checkboxChecked(uiCheckbox *c, void *data) +{ + redraw(); +} + +static uiCheckbox *newCheckbox(const char *text) +{ + uiCheckbox *c; + + c = uiNewCheckbox(text); + uiCheckboxOnToggled(c, checkboxChecked, NULL); + uiBoxAppend(panel, uiControl(c), 0); + return c; +} + +struct example *mkAttributesExample(void) +{ + panel = uiNewVerticalBox(); + showLineBounds = newCheckbox("Show Line Bounds"); + + attributesExample.name = "Attributed Text"; + attributesExample.panel = uiControl(panel); + attributesExample.draw = draw; + attributesExample.mouse = NULL; + attributesExample.key = NULL; + + setupAttributedString(); + params.String = attrstr; + params.DefaultFont = &defaultFont; + params.Align = uiDrawTextLayoutAlignLeft; + + return &attributesExample; +} diff --git a/examples/drawtext/drawtext.h b/examples/drawtext/drawtext.h index f14ee485..0ac4cb36 100644 --- a/examples/drawtext/drawtext.h +++ b/examples/drawtext/drawtext.h @@ -20,3 +20,6 @@ extern struct example *mkBasicExample(void); // hittest.c extern struct example *mkHitTestExample(void); + +// attributes.c +extern struct example *mkAttributesExample(void); diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c index cc28acd3..822382d0 100644 --- a/examples/drawtext/main.c +++ b/examples/drawtext/main.c @@ -110,6 +110,11 @@ int main(void) uiControlHide(examples[n]->panel); uiBoxAppend(box, examples[n]->panel, 0); n++; + examples[n] = mkAttributesExample(); + uiComboboxAppend(exampleList, examples[n]->name); + uiControlHide(examples[n]->panel); + uiBoxAppend(box, examples[n]->panel, 0); + n++; // and set things up for the initial state uiComboboxSetSelected(exampleList, 0); uiComboboxOnSelected(exampleList, onExampleChanged, NULL); diff --git a/ui_attrstr.h b/ui_attrstr.h index 63aed4c0..87eac1cf 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -1,15 +1,19 @@ typedef struct uiAttributedString uiAttributedString; _UI_ENUM(uiAttribute) { - // TODO uiAttributeFamily, - // TODO uiAttributeSize, + // TODO once we allow loading fonts in memory we can't use just one pointer anymore + uiAttributeFamily, + // TODO no guarantee this can fit in a uintptr_t; we need a better way to store these... + uiAttributeSize, uiAttributeWeight, + uiAttributeItalic, + uiAttributeStretch, // TODO // TODO uiAttributeSystem, // TODO uiAttributeCustom, }; -typedef int (*uiAttributedStringForEachAttributeFunc)(uiAttributedString *, uiAttribute type, uintptr_t value, size_t start, size_t end, void *data); +typedef int (*uiAttributedStringForEachAttributeFunc)(uiAttributedString *s, uiAttribute type, uintptr_t value, size_t start, size_t end, void *data); // @role uiAttributedString constructor // uiNewAttributedString() creates a new uiAttributedString from @@ -35,6 +39,7 @@ _UI_EXTERN void uiAttributedStringDelete(uiAttributedString *s, size_t start, si _UI_EXTERN size_t uiAttributedStringNumGraphemes(uiAttributedString *s); _UI_EXTERN size_t uiAttributedStringByteIndexToGrapheme(uiAttributedString *s, size_t pos); _UI_EXTERN size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos); +_UI_EXTERN void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttribute type, uintptr_t value, size_t start, size_t end); _UI_EXTERN void uiAttributedStringForEachAttribute(uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); typedef struct uiDrawFontDescriptor uiDrawFontDescriptor; From 5aaac84d5537dfe7f521825fdd3f2c45f90b4de3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 12 Feb 2017 01:05:27 -0500 Subject: [PATCH 0637/1329] Implemented uiAttributeFamily on OS X. ATTRIBUTES WORK!!! --- darwin/CMakeLists.txt | 1 + darwin/attrstr.m | 158 +++++++++++++++++++++++++++++++++++++++++ darwin/drawtext.m | 77 -------------------- darwin/uipriv_darwin.h | 3 + 4 files changed, 162 insertions(+), 77 deletions(-) create mode 100644 darwin/attrstr.m diff --git a/darwin/CMakeLists.txt b/darwin/CMakeLists.txt index 0fce3838..7d9ead75 100644 --- a/darwin/CMakeLists.txt +++ b/darwin/CMakeLists.txt @@ -4,6 +4,7 @@ list(APPEND _LIBUI_SOURCES darwin/alloc.m darwin/area.m darwin/areaevents.m + darwin/attrstr.m darwin/autolayout.m darwin/box.m darwin/button.m diff --git a/darwin/attrstr.m b/darwin/attrstr.m new file mode 100644 index 00000000..49f0d20a --- /dev/null +++ b/darwin/attrstr.m @@ -0,0 +1,158 @@ +// 12 february 2017 +#import "uipriv_darwin.h" + +// unlike the other systems, Core Text rolls family, size, weight, italic, width, AND opentype features into the "font" attribute +// TODO see if we could use NSAttributedString? +// TODO consider renaming this struct and the fep variable(s) +struct foreachParams { + CFMutableAttributedStringRef mas; + NSMutableDictionary *converted; + uiDrawFontDescriptor *defaultFont; +}; + +static void ensureFontInRange(struct foreachParams *p, size_t start, size_t end) +{ + size_t i; + NSNumber *n; + uiDrawFontDescriptor *new; + + for (i = start; i < end; i++) { + n = [NSNumber numberWithInteger:i]; + if ([p->converted objectForKey:n] != nil) + continue; + new = uiNew(uiDrawFontDescriptor); + *new = *(p->defaultFont); + [p->converted setObject:[NSValue valueWithPointer:new] forKey:n]; + } +} + +static void adjustFontInRange(struct foreachParams *p, size_t start, size_t end, void (^adj)(uiDrawFontDescriptor *desc)) +{ + size_t i; + NSNumber *n; + NSValue *v; + + for (i = start; i < end; i++) { + n = [NSNumber numberWithInteger:i]; + v = (NSValue *) [p->converted objectForKey:n]; + adj((uiDrawFontDescriptor *) [v pointerValue]); + } +} + +static int processAttribute(uiAttributedString *s, uiAttribute type, uintptr_t value, size_t start, size_t end, void *data) +{ + struct foreachParams *p = (struct foreachParams *) data; + + start = attrstrUTF8ToUTF16(s, start); + end = attrstrUTF8ToUTF16(s, end); + switch (type) { + case uiAttributeFamily: + ensureFontInRange(p, start, end); + adjustFontInRange(p, start, end, ^(uiDrawFontDescriptor *desc) { + desc->Family = (char *) value; + }); + break; + // TODO + } + return 0; +} + +static CTFontRef fontdescToCTFont(uiDrawFontDescriptor *fd) +{ + CTFontDescriptorRef desc; + CTFontRef font; + + desc = fontdescToCTFontDescriptor(fd); + font = CTFontCreateWithFontDescriptor(desc, fd->Size, NULL); + CFRelease(desc); // TODO correct? + return font; +} + +static void applyAndFreeFontAttributes(struct foreachParams *p) +{ + [p->converted enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, NSValue *val, BOOL *stop) { + uiDrawFontDescriptor *desc; + CTFontRef font; + CFRange range; + + desc = (uiDrawFontDescriptor *) [val pointerValue]; + font = fontdescToCTFont(desc); + range.location = [key integerValue]; + range.length = 1; + CFAttributedStringSetAttribute(p->mas, range, kCTFontAttributeName, font); + CFRelease(font); + uiFree(desc); + }]; +} + +static const CTTextAlignment ctaligns[] = { + [uiDrawTextLayoutAlignLeft] = kCTTextAlignmentLeft, + [uiDrawTextLayoutAlignCenter] = kCTTextAlignmentCenter, + [uiDrawTextLayoutAlignRight] = kCTTextAlignmentRight, +}; + +static CTParagraphStyleRef mkParagraphStyle(uiDrawTextLayoutParams *p) +{ + CTParagraphStyleRef ps; + CTParagraphStyleSetting settings[16]; + size_t nSettings = 0; + + settings[nSettings].spec = kCTParagraphStyleSpecifierAlignment; + settings[nSettings].valueSize = sizeof (CTTextAlignment); + settings[nSettings].value = ctaligns + p->Align; + nSettings++; + + ps = CTParagraphStyleCreate(settings, nSettings); + if (ps == NULL) { + // TODO + } + return ps; +} + +CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p) +{ + CFStringRef cfstr; + CFMutableDictionaryRef defaultAttrs; + CTFontRef defaultCTFont; + CTParagraphStyleRef ps; + CFAttributedStringRef base; + CFMutableAttributedStringRef mas; + struct foreachParams fep; + + cfstr = CFStringCreateWithCharacters(NULL, attrstrUTF16(p->String), attrstrUTF16Len(p->String)); + if (cfstr == NULL) { + // TODO + } + defaultAttrs = CFDictionaryCreateMutable(NULL, 0, + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (defaultAttrs == NULL) { + // TODO + } + defaultCTFont = fontdescToCTFont(p->DefaultFont); + CFDictionaryAddValue(defaultAttrs, kCTFontAttributeName, defaultCTFont); + CFRelease(defaultCTFont); + ps = mkParagraphStyle(p); + CFDictionaryAddValue(defaultAttrs, kCTParagraphStyleAttributeName, ps); + CFRelease(ps); + + base = CFAttributedStringCreate(NULL, cfstr, defaultAttrs); + if (base == NULL) { + // TODO + } + CFRelease(cfstr); + CFRelease(defaultAttrs); + mas = CFAttributedStringCreateMutableCopy(NULL, 0, base); + CFRelease(base); + + CFAttributedStringBeginEditing(mas); + fep.mas = mas; + fep.converted = [NSMutableDictionary new]; + fep.defaultFont = p->DefaultFont; + uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); + applyAndFreeFontAttributes(&fep); + [fep.converted release]; + CFAttributedStringEndEditing(mas); + + return mas; +} diff --git a/darwin/drawtext.m b/darwin/drawtext.m index bae7326d..9fbe5bed 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -35,83 +35,6 @@ struct uiDrawTextLayout { size_t nUTF16; }; -static CTFontRef fontdescToCTFont(uiDrawFontDescriptor *fd) -{ - CTFontDescriptorRef desc; - CTFontRef font; - - desc = fontdescToCTFontDescriptor(fd); - font = CTFontCreateWithFontDescriptor(desc, fd->Size, NULL); - CFRelease(desc); // TODO correct? - return font; -} - -static const CTTextAlignment ctaligns[] = { - [uiDrawTextLayoutAlignLeft] = kCTTextAlignmentLeft, - [uiDrawTextLayoutAlignCenter] = kCTTextAlignmentCenter, - [uiDrawTextLayoutAlignRight] = kCTTextAlignmentRight, -}; - -static CTParagraphStyleRef mkParagraphStyle(uiDrawTextLayoutParams *p) -{ - CTParagraphStyleRef ps; - CTParagraphStyleSetting settings[16]; - size_t nSettings = 0; - - settings[nSettings].spec = kCTParagraphStyleSpecifierAlignment; - settings[nSettings].valueSize = sizeof (CTTextAlignment); - settings[nSettings].value = ctaligns + p->Align; - nSettings++; - - ps = CTParagraphStyleCreate(settings, nSettings); - if (ps == NULL) { - // TODO - } - return ps; -} - -static CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p) -{ - CFStringRef cfstr; - CFMutableDictionaryRef defaultAttrs; - CTFontRef defaultCTFont; - CTParagraphStyleRef ps; - CFAttributedStringRef base; - CFMutableAttributedStringRef mas; - - cfstr = CFStringCreateWithCharacters(NULL, attrstrUTF16(p->String), attrstrUTF16Len(p->String)); - if (cfstr == NULL) { - // TODO - } - defaultAttrs = CFDictionaryCreateMutable(NULL, 0, - &kCFCopyStringDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - if (defaultAttrs == NULL) { - // TODO - } - defaultCTFont = fontdescToCTFont(p->DefaultFont); - CFDictionaryAddValue(defaultAttrs, kCTFontAttributeName, defaultCTFont); - CFRelease(defaultCTFont); - ps = mkParagraphStyle(p); - CFDictionaryAddValue(defaultAttrs, kCTParagraphStyleAttributeName, ps); - CFRelease(ps); - - base = CFAttributedStringCreate(NULL, cfstr, defaultAttrs); - if (base == NULL) { - // TODO - } - CFRelease(cfstr); - CFRelease(defaultAttrs); - mas = CFAttributedStringCreateMutableCopy(NULL, 0, base); - CFRelease(base); - - CFAttributedStringBeginEditing(mas); - // TODO copy in the attributes - CFAttributedStringEndEditing(mas); - - return mas; -} - // TODO this is wrong for our hit-test example's multiple combining character example static uiDrawTextLayoutLineMetrics *computeLineMetrics(CTFrameRef frame, CGSize size) { diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 336f5bcf..c9ab1c34 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -143,3 +143,6 @@ extern void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdg // fontmatch.m extern CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd); extern void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescriptor *uidesc); + +// attrstr.m +extern CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p); From 1c1b16a20658e8cb8f7ca1a20c4961ca204f686b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 12 Feb 2017 13:41:52 -0500 Subject: [PATCH 0638/1329] More attributes. Beyond this point I'd need to either redefine the way attributes are specified or make more header macros. --- darwin/attrstr.m | 25 +++++++++++++++++++ examples/drawtext/attributes.c | 45 ++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 49f0d20a..d1298944 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -2,6 +2,7 @@ #import "uipriv_darwin.h" // unlike the other systems, Core Text rolls family, size, weight, italic, width, AND opentype features into the "font" attribute +// TODO opentype features and AAT fvar table info is lost, so a handful of fonts in the font panel ("Titling" variants of some fonts and Skia and possibly others but those are the examples I know about) cannot be represented by uiDrawFontDescriptor; what *can* we do about this since this info is NOT part of the font on other platforms? // TODO see if we could use NSAttributedString? // TODO consider renaming this struct and the fep variable(s) struct foreachParams { @@ -52,6 +53,30 @@ static int processAttribute(uiAttributedString *s, uiAttribute type, uintptr_t v desc->Family = (char *) value; }); break; + case uiAttributeSize: + ensureFontInRange(p, start, end); + adjustFontInRange(p, start, end, ^(uiDrawFontDescriptor *desc) { + desc->Size = *((double *) value); + }); + break; + case uiAttributeWeight: + ensureFontInRange(p, start, end); + adjustFontInRange(p, start, end, ^(uiDrawFontDescriptor *desc) { + desc->Weight = (uiDrawTextWeight) value; + }); + break; + case uiAttributeItalic: + ensureFontInRange(p, start, end); + adjustFontInRange(p, start, end, ^(uiDrawFontDescriptor *desc) { + desc->Italic = (uiDrawTextItalic) value; + }); + break; + case uiAttributeStretch: + ensureFontInRange(p, start, end); + adjustFontInRange(p, start, end, ^(uiDrawFontDescriptor *desc) { + desc->Stretch = (uiDrawTextStretch) value; + }); + break; // TODO } return 0; diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index b66635df..428c1275 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -22,6 +22,51 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, ", "); next = "multiple sizes"; + static double eighteen = 18; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + uiAttributedStringSetAttribute(attrstr, + uiAttributeSize, + (uintptr_t) (&eighteen), + start, end); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "multiple weights"; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + uiAttributedStringSetAttribute(attrstr, + uiAttributeWeight, + (uintptr_t) uiDrawTextWeightBold, + start, end); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "multiple italics"; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + uiAttributedStringSetAttribute(attrstr, + uiAttributeItalic, + (uintptr_t) uiDrawTextItalicItalic, + start, end); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "multiple stretches"; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + uiAttributedStringSetAttribute(attrstr, + uiAttributeStretch, + (uintptr_t) uiDrawTextStretchCondensed, + start, end); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "multiple TODO"; } static char fontFamily[] = "Times New Roman"; From 261dd4851afbaa099935552851ec1c68f5ea9eb6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 12 Feb 2017 14:11:25 -0500 Subject: [PATCH 0639/1329] Changed the representation of an attribute type/value pair to allow more type safety and expressability. --- common/attrlist.c | 41 +++++++++++++++++++++------------- common/attrstr.c | 4 ++-- common/uipriv.h | 2 +- darwin/attrstr.m | 14 ++++++------ examples/drawtext/attributes.c | 36 +++++++++++++---------------- ui_attrstr.h | 11 +++++++-- 6 files changed, 61 insertions(+), 47 deletions(-) diff --git a/common/attrlist.c b/common/attrlist.c index f65120f7..5afb862f 100644 --- a/common/attrlist.c +++ b/common/attrlist.c @@ -12,8 +12,7 @@ The linked list is not a ring; alist->fist->prev == NULL and alist->last->next = */ struct attr { - uiAttribute type; - uintptr_t val; + uiAttributeSpec spec; size_t start; size_t end; struct attr *prev; @@ -178,8 +177,7 @@ static struct attr *attrDropRange(struct attrlist *alist, struct attr *a, size_t // we'll need to split the attribute into two b = uiNew(struct attr); - b->type = a->type; - b->val = a->val; + b->spec = a->spec; b->start = end; b->end = a->end; *tail = b; @@ -223,8 +221,7 @@ static struct attr *attrSplitAt(struct attrlist *alist, struct attr *a, size_t a return NULL; b = uiNew(struct attr); - b->type = a->type; - b->val = a->val; + b->spec = a->spec; b->start = at; b->end = a->end; @@ -287,7 +284,24 @@ static struct attr *attrDeleteRange(struct attrlist *alist, struct attr *a, size return a->next; } -void attrlistInsertAttribute(struct attrlist *alist, uiAttribute type, uintptr_t val, size_t start, size_t end) +static int specsIdentical(struct attr *attr, uiAttributeSpec *spec) +{ + if (attr->spec.Type != spec->Type) + return 0; + switch (attr->spec.Type) { + case uiAttributeFamily: + // TODO should we start copying these strings? + return strcmp((char *) (attr->spec.Value), (char *) (spec->Value)) == 0; + case uiAttributeSize: + // TODO use a closest match? + return attr->spec.Double == spec->Double; + // TODO + } + // handles the rest + return attr->spec.Value == spec->Value; +} + +void attrlistInsertAttribute(struct attrlist *alist, uiAttributeSpec *spec, size_t start, size_t end) { struct attr *a; struct attr *before; @@ -309,7 +323,7 @@ void attrlistInsertAttribute(struct attrlist *alist, uiAttribute type, uintptr_t goto next; // should we split this? - if (before->type != type) + if (before->spec.Type != spec->Type) goto next; lstart = start; lend = end; @@ -317,10 +331,8 @@ void attrlistInsertAttribute(struct attrlist *alist, uiAttribute type, uintptr_t goto next; // okay so this might conflict; if the val is the same as the one we want, we need to expand the existing attribute, not fragment anything - // TODO this might cause problems with system specific attributes, if we support those; maybe also user-specific? - // TODO will this cause problems with fonts? // TODO will this reduce fragmentation if we first add from 0 to 2 and then from 2 to 4? or do we have to do that separately? - if (before->val == val) { + if (specsIdentical(before, spec)) { attrGrow(alist, before, start, end); return; } @@ -335,8 +347,7 @@ void attrlistInsertAttribute(struct attrlist *alist, uiAttribute type, uintptr_t // if we got here, we know we have to add the attribute before before a = uiNew(struct attr); - a->type = type; - a->val = val; + a->spec = *spec; a->start = start; a->end = end; attrInsertBefore(alist, a, before); @@ -498,7 +509,7 @@ void attrlistRemoveAttribute(struct attrlist *alist, uiAttribute type, size_t st // and at this point we're done, so break; } - if (a->type != type) + if (a->spec.Type != type) goto next; lstart = start; lend = end; @@ -587,7 +598,7 @@ void attrlistForEach(struct attrlist *alist, uiAttributedString *s, uiAttributed for (a = alist->first; a != NULL; a = a->next) // TODO document this // TODO should this be return 0 to break? - if ((*f)(s, a->type, a->val, a->start, a->end, data)) + if ((*f)(s, &(a->spec), a->start, a->end, data)) break; } diff --git a/common/attrstr.c b/common/attrstr.c index 93e4e8f4..9c12a532 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -297,9 +297,9 @@ size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos) return pos; } -void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttribute type, uintptr_t value, size_t start, size_t end) +void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end) { - attrlistInsertAttribute(s->attrs, type, value, start, end); + attrlistInsertAttribute(s->attrs, spec, start, end); } // TODO introduce an iterator? diff --git a/common/uipriv.h b/common/uipriv.h index 73c7eeb0..9d0dc8e6 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -78,7 +78,7 @@ extern size_t *attrstrCopyUTF16ToUTF8(uiAttributedString *s, size_t *n); // attrlist.c struct attrlist; -extern void attrlistInsertAttribute(struct attrlist *alist, uiAttribute type, uintptr_t val, size_t start, size_t end); +extern void attrlistInsertAttribute(struct attrlist *alist, uiAttributeSpec *spec, size_t start, size_t end); extern void attrlistInsertCharactersUnattributed(struct attrlist *alist, size_t start, size_t count); extern void attrlistInsertCharactersExtendingAttributes(struct attrlist *alist, size_t start, size_t count); extern void attrlistRemoveAttribute(struct attrlist *alist, uiAttribute type, size_t start, size_t end); diff --git a/darwin/attrstr.m b/darwin/attrstr.m index d1298944..16f7fb92 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -40,41 +40,41 @@ static void adjustFontInRange(struct foreachParams *p, size_t start, size_t end, } } -static int processAttribute(uiAttributedString *s, uiAttribute type, uintptr_t value, size_t start, size_t end, void *data) +static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) { struct foreachParams *p = (struct foreachParams *) data; start = attrstrUTF8ToUTF16(s, start); end = attrstrUTF8ToUTF16(s, end); - switch (type) { + switch (spec->Type) { case uiAttributeFamily: ensureFontInRange(p, start, end); adjustFontInRange(p, start, end, ^(uiDrawFontDescriptor *desc) { - desc->Family = (char *) value; + desc->Family = (char *) (spec->Value); }); break; case uiAttributeSize: ensureFontInRange(p, start, end); adjustFontInRange(p, start, end, ^(uiDrawFontDescriptor *desc) { - desc->Size = *((double *) value); + desc->Size = spec->Double; }); break; case uiAttributeWeight: ensureFontInRange(p, start, end); adjustFontInRange(p, start, end, ^(uiDrawFontDescriptor *desc) { - desc->Weight = (uiDrawTextWeight) value; + desc->Weight = (uiDrawTextWeight) (spec->Value); }); break; case uiAttributeItalic: ensureFontInRange(p, start, end); adjustFontInRange(p, start, end, ^(uiDrawFontDescriptor *desc) { - desc->Italic = (uiDrawTextItalic) value; + desc->Italic = (uiDrawTextItalic) (spec->Value); }); break; case uiAttributeStretch: ensureFontInRange(p, start, end); adjustFontInRange(p, start, end, ^(uiDrawFontDescriptor *desc) { - desc->Stretch = (uiDrawTextStretch) value; + desc->Stretch = (uiDrawTextStretch) (spec->Value); }); break; // TODO diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index 428c1275..c2a91889 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -5,6 +5,7 @@ static uiAttributedString *attrstr; static void setupAttributedString(void) { + uiAttributeSpec spec; size_t start, end; const char *next; @@ -14,22 +15,20 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - uiAttributedStringSetAttribute(attrstr, - uiAttributeFamily, - (uintptr_t) "Courier New", - start, end); + spec.Type = uiAttributeFamily; + spec.Value = (uintptr_t) "Courier New"; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ", "); next = "multiple sizes"; - static double eighteen = 18; start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeSize; + spec.Double = 18; uiAttributedStringSetAttribute(attrstr, - uiAttributeSize, - (uintptr_t) (&eighteen), - start, end); + &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ", "); @@ -37,10 +36,9 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - uiAttributedStringSetAttribute(attrstr, - uiAttributeWeight, - (uintptr_t) uiDrawTextWeightBold, - start, end); + spec.Type = uiAttributeWeight; + spec.Value = (uintptr_t) uiDrawTextWeightBold; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ", "); @@ -48,10 +46,9 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - uiAttributedStringSetAttribute(attrstr, - uiAttributeItalic, - (uintptr_t) uiDrawTextItalicItalic, - start, end); + spec.Type = uiAttributeItalic; + spec.Value = (uintptr_t) uiDrawTextItalicItalic; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ", "); @@ -59,10 +56,9 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - uiAttributedStringSetAttribute(attrstr, - uiAttributeStretch, - (uintptr_t) uiDrawTextStretchCondensed, - start, end); + spec.Type = uiAttributeStretch; + spec.Value = (uintptr_t) uiDrawTextStretchCondensed; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ", "); diff --git a/ui_attrstr.h b/ui_attrstr.h index 87eac1cf..096bcc4e 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -1,4 +1,5 @@ typedef struct uiAttributedString uiAttributedString; +typedef struct uiAttributeSpec uiAttributeSpec; _UI_ENUM(uiAttribute) { // TODO once we allow loading fonts in memory we can't use just one pointer anymore @@ -13,7 +14,13 @@ _UI_ENUM(uiAttribute) { // TODO uiAttributeCustom, }; -typedef int (*uiAttributedStringForEachAttributeFunc)(uiAttributedString *s, uiAttribute type, uintptr_t value, size_t start, size_t end, void *data); +struct uiAttributeSpec { + uiAttribute Type; + uintptr_t Value; + double Double; +}; + +typedef int (*uiAttributedStringForEachAttributeFunc)(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data); // @role uiAttributedString constructor // uiNewAttributedString() creates a new uiAttributedString from @@ -39,7 +46,7 @@ _UI_EXTERN void uiAttributedStringDelete(uiAttributedString *s, size_t start, si _UI_EXTERN size_t uiAttributedStringNumGraphemes(uiAttributedString *s); _UI_EXTERN size_t uiAttributedStringByteIndexToGrapheme(uiAttributedString *s, size_t pos); _UI_EXTERN size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos); -_UI_EXTERN void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttribute type, uintptr_t value, size_t start, size_t end); +_UI_EXTERN void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end); _UI_EXTERN void uiAttributedStringForEachAttribute(uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); typedef struct uiDrawFontDescriptor uiDrawFontDescriptor; From 44f24fc90063653183235b48e14de3d4ec0b3671 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 12 Feb 2017 20:27:47 -0500 Subject: [PATCH 0640/1329] Added the foreground color attribute. Considering making the background color a background BRUSH attribute instead... --- common/attrlist.c | 6 ++++++ darwin/attrstr.m | 20 ++++++++++++++++++++ darwin/draw.m | 4 ++++ examples/drawtext/attributes.c | 14 ++++++++++++++ ui_attrstr.h | 9 ++++++--- 5 files changed, 50 insertions(+), 3 deletions(-) diff --git a/common/attrlist.c b/common/attrlist.c index 5afb862f..5c75da58 100644 --- a/common/attrlist.c +++ b/common/attrlist.c @@ -295,6 +295,12 @@ static int specsIdentical(struct attr *attr, uiAttributeSpec *spec) case uiAttributeSize: // TODO use a closest match? return attr->spec.Double == spec->Double; + case uiAttributeColor: + // TODO use a closest match? + return attr->spec.R == spec->R && + attr->spec.G == spec->G && + attr->spec.B == spec->B && + attr->spec.A == spec->A; // TODO } // handles the rest diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 16f7fb92..e5d3c84e 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -43,9 +43,15 @@ static void adjustFontInRange(struct foreachParams *p, size_t start, size_t end, static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) { struct foreachParams *p = (struct foreachParams *) data; + CFRange range; + CGColorSpaceRef colorspace; + CGColorRef color; + CGFloat components[4]; start = attrstrUTF8ToUTF16(s, start); end = attrstrUTF8ToUTF16(s, end); + range.location = start; + range.length = end - start; switch (spec->Type) { case uiAttributeFamily: ensureFontInRange(p, start, end); @@ -77,6 +83,20 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t desc->Stretch = (uiDrawTextStretch) (spec->Value); }); break; + case uiAttributeColor: + colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); + if (colorspace == NULL) { + // TODO + } + components[0] = spec->R; + components[1] = spec->G; + components[2] = spec->B; + components[3] = spec->A; + color = CGColorCreate(colorspace, components); + CFRelease(colorspace); + CFAttributedStringSetAttribute(p->mas, range, kCTForegroundColorAttributeName, color); + CFRelease(color); + break; // TODO } return 0; diff --git a/darwin/draw.m b/darwin/draw.m index d9aadede..b4be6e7f 100644 --- a/darwin/draw.m +++ b/darwin/draw.m @@ -218,6 +218,10 @@ static void fillGradient(CGContextRef ctxt, uiDrawPath *p, uiDrawBrush *b) // gradients need a color space // for consistency with windows, use sRGB colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); + if (colorspace == NULL) { + // TODO + } + // TODO add NULL check to other uses of CGColorSpace // make the gradient colors = uiAlloc(b->NumStops * 4 * sizeof (CGFloat), "CGFloat[]"); diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index c2a91889..d5c47058 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -62,6 +62,20 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, ", "); + next = "multiple colors"; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeColor; + // Direct2D "Crimson" (#DC143C) + spec.R = 0.8627450980392156; + spec.G = 0.0784313725490196; + spec.B = 0.2352941176470588; + spec.A = 0.75; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + next = "multiple TODO"; } diff --git a/ui_attrstr.h b/ui_attrstr.h index 096bcc4e..62a82f77 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -2,13 +2,12 @@ typedef struct uiAttributedString uiAttributedString; typedef struct uiAttributeSpec uiAttributeSpec; _UI_ENUM(uiAttribute) { - // TODO once we allow loading fonts in memory we can't use just one pointer anymore uiAttributeFamily, - // TODO no guarantee this can fit in a uintptr_t; we need a better way to store these... - uiAttributeSize, + uiAttributeSize, // use Double uiAttributeWeight, uiAttributeItalic, uiAttributeStretch, + uiAttributeColor, // use R, G, B, A // TODO // TODO uiAttributeSystem, // TODO uiAttributeCustom, @@ -18,6 +17,10 @@ struct uiAttributeSpec { uiAttribute Type; uintptr_t Value; double Double; + double R; + double G; + double B; + double A; }; typedef int (*uiAttributedStringForEachAttributeFunc)(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data); From b2cd5ef851a7101f13fe3dff438ddbbdbd4d009f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 13 Feb 2017 01:22:59 -0500 Subject: [PATCH 0641/1329] Wrote code to draw the background of text. --- common/drawtext.c | 38 ++++++++++++++++++++++++++++++++++++++ common/uipriv.h | 1 + 2 files changed, 39 insertions(+) diff --git a/common/drawtext.c b/common/drawtext.c index cbfe3ccb..9cc04966 100644 --- a/common/drawtext.c +++ b/common/drawtext.c @@ -49,3 +49,41 @@ void uiDrawCaret(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, uiDrawRestore(c); } + +void drawTextBackground(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t start, size_t end, uiDrawBrush *brush, int isSelection) +{ + int line, nLines; + size_t lstart, lend; + double layoutwid, layoutht; + + uiDrawTextLayoutExtents(layout, &layoutwid, &layoutht); + nLines = uiDrawTextLayoutNumLines(layout); + for (line = 0; line < nLines; line++) { + uiDrawTextLayoutLineByteRange(layout, line, &lstart, &lend); + if (start >= lstart && start < lend) + break; + } + while (end - start > 0) { + uiDrawTextLayoutLineMetrics m; + double startx, endx; + uiDrawPath *path; + + if (lend > end) // don't cross lines + lend = end; + startx = uiDrawTextLayoutByteLocationInLine(layout, line, start); + // TODO explain this + endx = layoutwid; + if (!isSelection || end <= lend) + endx = uiDrawTextLayoutByteLocationInLine(layout, line, end); + uiDrawTextLayoutLineGetMetrics(layout, line, &m); + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathAddRectangle(path, + x + startx, y + m.Y, + endx - startx, m.Height); + uiDrawPathEnd(path); + uiDrawFill(c, path, brush); + uiDrawFreePath(path); + line++; + start += lend - start; + } +} diff --git a/common/uipriv.h b/common/uipriv.h index 9d0dc8e6..553073f5 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -99,6 +99,7 @@ struct caretDrawParams { double width; }; extern void caretDrawParams(uiDrawContext *c, double height, struct caretDrawParams *p); +extern void drawTextBackground(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t start, size_t end, uiDrawBrush *brush, int isSelection); #ifdef __cplusplus } From 40c388e01d53bca38bfd4201209c53d3c40dbfa6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 13 Feb 2017 01:28:46 -0500 Subject: [PATCH 0642/1329] Whoops, just realized I typo'd. --- common/drawtext.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/common/drawtext.c b/common/drawtext.c index 9cc04966..3e657a7d 100644 --- a/common/drawtext.c +++ b/common/drawtext.c @@ -71,10 +71,10 @@ void drawTextBackground(uiDrawContext *c, double x, double y, uiDrawTextLayout * if (lend > end) // don't cross lines lend = end; startx = uiDrawTextLayoutByteLocationInLine(layout, line, start); - // TODO explain this + // TODO explain this; note the use of start with lend endx = layoutwid; if (!isSelection || end <= lend) - endx = uiDrawTextLayoutByteLocationInLine(layout, line, end); + endx = uiDrawTextLayoutByteLocationInLine(layout, line, lend); uiDrawTextLayoutLineGetMetrics(layout, line, &m); path = uiDrawNewPath(uiDrawFillModeWinding); uiDrawPathAddRectangle(path, @@ -84,6 +84,6 @@ void drawTextBackground(uiDrawContext *c, double x, double y, uiDrawTextLayout * uiDrawFill(c, path, brush); uiDrawFreePath(path); line++; - start += lend - start; + start = lend; } } From 9eba197fd19872f92123a0ad660396720836149a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 13 Feb 2017 01:44:18 -0500 Subject: [PATCH 0643/1329] Adjusted the example for backgrounds. Nope brushes isn't gonna work; absolute positioning of gradients is a problem. --- common/attrlist.c | 1 + examples/drawtext/attributes.c | 14 ++++++++++++++ ui_attrstr.h | 1 + 3 files changed, 16 insertions(+) diff --git a/common/attrlist.c b/common/attrlist.c index 5c75da58..9a785dd3 100644 --- a/common/attrlist.c +++ b/common/attrlist.c @@ -296,6 +296,7 @@ static int specsIdentical(struct attr *attr, uiAttributeSpec *spec) // TODO use a closest match? return attr->spec.Double == spec->Double; case uiAttributeColor: + case uiAttributeBackground: // TODO use a closest match? return attr->spec.R == spec->R && attr->spec.G == spec->G && diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index d5c47058..63e2e762 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -76,6 +76,20 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, ", "); + next = "multiple backgrounds"; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeBackground; + // Direct2D "Peach Puff" (#FFDAB9) + spec.R = 1.0; + spec.G = 0.85490196078431372; + spec.B = 0.7254901960784313; + spec.A = 0.5; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + next = "multiple TODO"; } diff --git a/ui_attrstr.h b/ui_attrstr.h index 62a82f77..4fa90aea 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -8,6 +8,7 @@ _UI_ENUM(uiAttribute) { uiAttributeItalic, uiAttributeStretch, uiAttributeColor, // use R, G, B, A + uiAttributeBackground, // use R, G, B, A // TODO // TODO uiAttributeSystem, // TODO uiAttributeCustom, From 1c238bf85ba677b7b0a0a059c437902d81ba8b2f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 13 Feb 2017 02:10:39 -0500 Subject: [PATCH 0644/1329] And implemented uiAttributeBackground on OS X. Not sure what else to add besides the feature variants... --- common/drawtext.c | 5 +++-- darwin/attrstr.m | 29 ++++++++++++++++++++++++++++- darwin/drawtext.m | 10 +++++++++- darwin/uipriv_darwin.h | 3 ++- examples/drawtext/attributes.c | 1 + 5 files changed, 43 insertions(+), 5 deletions(-) diff --git a/common/drawtext.c b/common/drawtext.c index 3e657a7d..938e1aeb 100644 --- a/common/drawtext.c +++ b/common/drawtext.c @@ -68,13 +68,14 @@ void drawTextBackground(uiDrawContext *c, double x, double y, uiDrawTextLayout * double startx, endx; uiDrawPath *path; + uiDrawTextLayoutLineByteRange(layout, line, &lstart, &lend); if (lend > end) // don't cross lines lend = end; - startx = uiDrawTextLayoutByteLocationInLine(layout, line, start); + startx = uiDrawTextLayoutByteLocationInLine(layout, start, line); // TODO explain this; note the use of start with lend endx = layoutwid; if (!isSelection || end <= lend) - endx = uiDrawTextLayoutByteLocationInLine(layout, line, lend); + endx = uiDrawTextLayoutByteLocationInLine(layout, lend, line); uiDrawTextLayoutLineGetMetrics(layout, line, &m); path = uiDrawNewPath(uiDrawFillModeWinding); uiDrawPathAddRectangle(path, diff --git a/darwin/attrstr.m b/darwin/attrstr.m index e5d3c84e..c383ab59 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -9,6 +9,7 @@ struct foreachParams { CFMutableAttributedStringRef mas; NSMutableDictionary *converted; uiDrawFontDescriptor *defaultFont; + NSMutableArray *backgroundBlocks; }; static void ensureFontInRange(struct foreachParams *p, size_t start, size_t end) @@ -40,6 +41,20 @@ static void adjustFontInRange(struct foreachParams *p, size_t start, size_t end, } } +static backgroundBlock mkBackgroundBlock(size_t start, size_t end, double r, double g, double b, double a) +{ + return Block_copy(^(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y) { + uiDrawBrush brush; + + brush.Type = uiDrawBrushTypeSolid; + brush.R = r; + brush.G = g; + brush.B = b; + brush.A = a; + drawTextBackground(c, x, y, layout, start, end, &brush, 0); + }); +} + static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) { struct foreachParams *p = (struct foreachParams *) data; @@ -47,7 +62,11 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t CGColorSpaceRef colorspace; CGColorRef color; CGFloat components[4]; + size_t ostart, oend; + backgroundBlock block; + ostart = start; + oend = end; start = attrstrUTF8ToUTF16(s, start); end = attrstrUTF8ToUTF16(s, end); range.location = start; @@ -97,6 +116,12 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t CFAttributedStringSetAttribute(p->mas, range, kCTForegroundColorAttributeName, color); CFRelease(color); break; + case uiAttributeBackground: + block = mkBackgroundBlock(ostart, oend, + spec->R, spec->G, spec->B, spec->A); + [p->backgroundBlocks addObject:block]; + Block_release(block); + break; // TODO } return 0; @@ -154,7 +179,7 @@ static CTParagraphStyleRef mkParagraphStyle(uiDrawTextLayoutParams *p) return ps; } -CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p) +CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks) { CFStringRef cfstr; CFMutableDictionaryRef defaultAttrs; @@ -194,10 +219,12 @@ CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p) fep.mas = mas; fep.converted = [NSMutableDictionary new]; fep.defaultFont = p->DefaultFont; + fep.backgroundBlocks = [NSMutableArray new]; uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); applyAndFreeFontAttributes(&fep); [fep.converted release]; CFAttributedStringEndEditing(mas); + *backgroundBlocks = fep.backgroundBlocks; return mas; } diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 9fbe5bed..c5736bf9 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -28,6 +28,8 @@ struct uiDrawTextLayout { // we compute this once when first creating the layout uiDrawTextLayoutLineMetrics *lineMetrics; + NSArray *backgroundBlocks; + // for converting CFAttributedString indices from/to byte offsets size_t *u8tou16; size_t nUTF8; @@ -114,7 +116,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) CGRect rect; tl = uiNew(uiDrawTextLayout); - tl->attrstr = attrstrToCoreFoundation(p); + tl->attrstr = attrstrToCoreFoundation(p, &(tl->backgroundBlocks)); range.location = 0; range.length = CFAttributedStringGetLength(tl->attrstr); tl->width = p->Width; @@ -165,6 +167,7 @@ void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { uiFree(tl->u16tou8); uiFree(tl->u8tou16); + [tl->backgroundBlocks release]; uiFree(tl->lineMetrics); // TODO release tl->lines? CFRelease(tl->frame); @@ -177,8 +180,13 @@ void uiDrawFreeTextLayout(uiDrawTextLayout *tl) // TODO document that (x,y) is the top-left corner of the *entire frame* void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) { + backgroundBlock b; + CGContextSaveGState(c->c); + for (b in tl->backgroundBlocks) + b(c, tl, x, y); + // Core Text doesn't draw onto a flipped view correctly; we have to pretend it was unflipped // see the iOS bits of the first example at https://developer.apple.com/library/mac/documentation/StringsTextFonts/Conceptual/CoreText_Programming/LayoutOperations/LayoutOperations.html#//apple_ref/doc/uid/TP40005533-CH12-SW1 (iOS is naturally flipped) // TODO how is this affected by a non-identity CTM? diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index c9ab1c34..bf009522 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -145,4 +145,5 @@ extern CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd); extern void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescriptor *uidesc); // attrstr.m -extern CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p); +typedef void (^backgroundBlock)(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y); +extern CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks); diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index 63e2e762..543ba94c 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -82,6 +82,7 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeBackground; // Direct2D "Peach Puff" (#FFDAB9) + // TODO choose a darker color spec.R = 1.0; spec.G = 0.85490196078431372; spec.B = 0.7254901960784313; From ff986858f14cbe83317a1dd2c68d0121ab6afa7e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 13 Feb 2017 10:24:12 -0500 Subject: [PATCH 0645/1329] Added vertical glyph forms. Next up is the rest of the various font features. --- common/attrlist.c | 2 ++ darwin/attrstr.m | 3 +++ examples/drawtext/attributes.c | 10 ++++++++++ ui_attrstr.h | 7 ++++--- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/common/attrlist.c b/common/attrlist.c index 9a785dd3..e4c8ad67 100644 --- a/common/attrlist.c +++ b/common/attrlist.c @@ -302,6 +302,8 @@ static int specsIdentical(struct attr *attr, uiAttributeSpec *spec) attr->spec.G == spec->G && attr->spec.B == spec->B && attr->spec.A == spec->A; + case uiAttributeVerticalForms: + return 1; // TODO } // handles the rest diff --git a/darwin/attrstr.m b/darwin/attrstr.m index c383ab59..cf6fde17 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -122,6 +122,9 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t [p->backgroundBlocks addObject:block]; Block_release(block); break; + case uiAttributeVerticalForms: + CFAttributedStringSetAttribute(p->mas, range, kCTVerticalFormsAttributeName, kCFBooleanTrue); + break; // TODO } return 0; diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index 543ba94c..7599dcfc 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -91,6 +91,16 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, ", "); + next = "vertical glyph forms"; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeVerticalForms; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + + uiAttributedStringAppendUnattributed(attrstr, " (which you can draw rotated for proper vertical text)"); + uiAttributedStringAppendUnattributed(attrstr, ", "); + next = "multiple TODO"; } diff --git a/ui_attrstr.h b/ui_attrstr.h index 4fa90aea..6d6021d2 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -3,12 +3,13 @@ typedef struct uiAttributeSpec uiAttributeSpec; _UI_ENUM(uiAttribute) { uiAttributeFamily, - uiAttributeSize, // use Double + uiAttributeSize, // use Double uiAttributeWeight, uiAttributeItalic, uiAttributeStretch, - uiAttributeColor, // use R, G, B, A - uiAttributeBackground, // use R, G, B, A + uiAttributeColor, // use R, G, B, A + uiAttributeBackground, // use R, G, B, A + uiAttributeVerticalForms, // no parameter // TODO // TODO uiAttributeSystem, // TODO uiAttributeCustom, From 9a19c65323a5d76fa02d4f1c17f2923d7fc4abff Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 13 Feb 2017 10:34:25 -0500 Subject: [PATCH 0646/1329] Prepared the OS X backend for adding feature attributes. Each feature will be a separate attribute for better composability. --- darwin/attrstr.m | 47 ++++++++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index cf6fde17..07663838 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -12,23 +12,32 @@ struct foreachParams { NSMutableArray *backgroundBlocks; }; +#define maxFeatures 32 + +struct fontParams { + uiDrawFontDescriptor desc; + uint16_t featureTypes[maxFeatures]; + uint16_t featureSpecifiers[maxFeatures]; + size_t nFeatures; +}; + static void ensureFontInRange(struct foreachParams *p, size_t start, size_t end) { size_t i; NSNumber *n; - uiDrawFontDescriptor *new; + struct fontParams *new; for (i = start; i < end; i++) { n = [NSNumber numberWithInteger:i]; if ([p->converted objectForKey:n] != nil) continue; - new = uiNew(uiDrawFontDescriptor); - *new = *(p->defaultFont); + new = uiNew(struct fontParams); + new->desc = *(p->defaultFont); [p->converted setObject:[NSValue valueWithPointer:new] forKey:n]; } } -static void adjustFontInRange(struct foreachParams *p, size_t start, size_t end, void (^adj)(uiDrawFontDescriptor *desc)) +static void adjustFontInRange(struct foreachParams *p, size_t start, size_t end, void (^adj)(struct fontParams *fp)) { size_t i; NSNumber *n; @@ -37,7 +46,7 @@ static void adjustFontInRange(struct foreachParams *p, size_t start, size_t end, for (i = start; i < end; i++) { n = [NSNumber numberWithInteger:i]; v = (NSValue *) [p->converted objectForKey:n]; - adj((uiDrawFontDescriptor *) [v pointerValue]); + adj((struct fontParams *) [v pointerValue]); } } @@ -74,32 +83,32 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t switch (spec->Type) { case uiAttributeFamily: ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(uiDrawFontDescriptor *desc) { - desc->Family = (char *) (spec->Value); + adjustFontInRange(p, start, end, ^(struct fontParams *fp) { + fp->desc.Family = (char *) (spec->Value); }); break; case uiAttributeSize: ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(uiDrawFontDescriptor *desc) { - desc->Size = spec->Double; + adjustFontInRange(p, start, end, ^(struct fontParams *fp) { + fp->desc.Size = spec->Double; }); break; case uiAttributeWeight: ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(uiDrawFontDescriptor *desc) { - desc->Weight = (uiDrawTextWeight) (spec->Value); + adjustFontInRange(p, start, end, ^(struct fontParams *fp) { + fp->desc.Weight = (uiDrawTextWeight) (spec->Value); }); break; case uiAttributeItalic: ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(uiDrawFontDescriptor *desc) { - desc->Italic = (uiDrawTextItalic) (spec->Value); + adjustFontInRange(p, start, end, ^(struct fontParams *fp) { + fp->desc.Italic = (uiDrawTextItalic) (spec->Value); }); break; case uiAttributeStretch: ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(uiDrawFontDescriptor *desc) { - desc->Stretch = (uiDrawTextStretch) (spec->Value); + adjustFontInRange(p, start, end, ^(struct fontParams *fp) { + fp->desc.Stretch = (uiDrawTextStretch) (spec->Value); }); break; case uiAttributeColor: @@ -144,17 +153,17 @@ static CTFontRef fontdescToCTFont(uiDrawFontDescriptor *fd) static void applyAndFreeFontAttributes(struct foreachParams *p) { [p->converted enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, NSValue *val, BOOL *stop) { - uiDrawFontDescriptor *desc; + struct fontParams *fp; CTFontRef font; CFRange range; - desc = (uiDrawFontDescriptor *) [val pointerValue]; - font = fontdescToCTFont(desc); + fp = (struct fontParams *) [val pointerValue]; + font = fontdescToCTFont(&(fp->desc)); range.location = [key integerValue]; range.length = 1; CFAttributedStringSetAttribute(p->mas, range, kCTFontAttributeName, font); CFRelease(font); - uiFree(desc); + uiFree(fp); }]; } From 35a06e8540f076a4c4ed26cd685356e525cf6907 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 13 Feb 2017 22:42:18 -0500 Subject: [PATCH 0647/1329] Changed uiAttributeVerticalForms into a boolean. This is how the other features might need to be implemented :/ --- common/attrlist.c | 9 ++++++++- darwin/attrstr.m | 5 ++++- examples/drawtext/attributes.c | 1 + ui_attrstr.h | 2 +- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/common/attrlist.c b/common/attrlist.c index e4c8ad67..1ca52537 100644 --- a/common/attrlist.c +++ b/common/attrlist.c @@ -284,6 +284,13 @@ static struct attr *attrDeleteRange(struct attrlist *alist, struct attr *a, size return a->next; } +static int boolsEqual(struct attr *attr, uiAttributeSpec *spec) +{ + if (attr->spec.Value == 0 && spec->Value == 0) + return 1; + return attr->spec.Value != 0 && spec->Value != 0; +} + static int specsIdentical(struct attr *attr, uiAttributeSpec *spec) { if (attr->spec.Type != spec->Type) @@ -303,7 +310,7 @@ static int specsIdentical(struct attr *attr, uiAttributeSpec *spec) attr->spec.B == spec->B && attr->spec.A == spec->A; case uiAttributeVerticalForms: - return 1; + return boolsEqual(attr, spec); // TODO } // handles the rest diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 07663838..051bec16 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -132,7 +132,10 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t Block_release(block); break; case uiAttributeVerticalForms: - CFAttributedStringSetAttribute(p->mas, range, kCTVerticalFormsAttributeName, kCFBooleanTrue); + if (spec->Value != 0) + CFAttributedStringSetAttribute(p->mas, range, kCTVerticalFormsAttributeName, kCFBooleanTrue); + else + CFAttributedStringSetAttribute(p->mas, range, kCTVerticalFormsAttributeName, kCFBooleanFalse); break; // TODO } diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index 7599dcfc..1aa74a72 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -96,6 +96,7 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeVerticalForms; + spec.Value = 1; uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " (which you can draw rotated for proper vertical text)"); diff --git a/ui_attrstr.h b/ui_attrstr.h index 6d6021d2..f5506fa0 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -9,7 +9,7 @@ _UI_ENUM(uiAttribute) { uiAttributeStretch, uiAttributeColor, // use R, G, B, A uiAttributeBackground, // use R, G, B, A - uiAttributeVerticalForms, // no parameter + uiAttributeVerticalForms, // 0 = off, nonzero = 1 // TODO // TODO uiAttributeSystem, // TODO uiAttributeCustom, From e3b1e033b2622c5dc0a8f3e9410090000896462c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 13 Feb 2017 23:20:38 -0500 Subject: [PATCH 0648/1329] More TODOs. --- ui_attrstr.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ui_attrstr.h b/ui_attrstr.h index f5506fa0..897c738b 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -9,6 +9,7 @@ _UI_ENUM(uiAttribute) { uiAttributeStretch, uiAttributeColor, // use R, G, B, A uiAttributeBackground, // use R, G, B, A + // TODO rename to uiAttributeVertical? uiAttributeVerticalForms, // 0 = off, nonzero = 1 // TODO // TODO uiAttributeSystem, From fbae38ce2eb7ff69758256484b00fbe041d4d0ab Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 14 Feb 2017 02:26:26 -0500 Subject: [PATCH 0649/1329] Started mapping out attributes for typographic features. I FORGOT THE OTHER ATTRIBUTES I NEED TO DO THOSE FIRST. --- ui_attrstr.h | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 157 insertions(+), 2 deletions(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index 897c738b..05d92857 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -1,6 +1,7 @@ typedef struct uiAttributedString uiAttributedString; typedef struct uiAttributeSpec uiAttributeSpec; +// Note: where you say "1 = on", any nonzero value means "on". (TODO) _UI_ENUM(uiAttribute) { uiAttributeFamily, uiAttributeSize, // use Double @@ -10,12 +11,166 @@ _UI_ENUM(uiAttribute) { uiAttributeColor, // use R, G, B, A uiAttributeBackground, // use R, G, B, A // TODO rename to uiAttributeVertical? - uiAttributeVerticalForms, // 0 = off, nonzero = 1 - // TODO + uiAttributeVerticalForms, // 0 = off, 1 = on + + // TODO underline and others + +#if 0 + + // These attributes represent typographic features. Each feature + // is a separate attribute, to make composition easier. The + // availability of for each attribute are defined by the font; the + // default values are defined by the font and/or by the OS. + // + // A note about features whose parameter is an enumeration: + // OS X defines typographic features using the AAT specification + // and converts to OpenType internally when needed, whereas + // other platforms use OpenType directly. OpenType is less + // precise about what each enumeration value means than AAT + // is, so enumeration values do not necessarily represent what + // OS X expects with all fonts. In cases where they do, libui + // provides an enumeration type to use. Otherwise, the AAT + // enumeration values are provided in comments for + // documentation purposes. + + // TODO kAllTypographicFeaturesType + + uiAttributeCommonLigatures, // 0 = off, 1 = on + uiAttributeRequiredLigatures, // 0 = off, 1 = on + uiAttributeRareLigatures, // 0 = off, 1 = on + uiAttributeLogoLigatures, // 0 = off, 1 = on + uiAttributeRebusPictureLigatures, // 0 = off, 1 = on + uiAttributeDipthrongLigatures, // 0 = off, 1 = on + uiAttributeSquaredLigatures, // 0 = off, 1 = on + // TODO rename + uiAttributeAbbreviatedSquared, // 0 = off, 1 = on + uiAttributeSymbolLigatures, // 0 = off, 1 = on + uiAttributeContextualLigatures, // 0 = off, 1 = on + uiAttributeHistoricalLigatures, // 0 = off, 1 = on + + uiAttributeCursiveConnection, // 0 = none, 1 = some, 2 = all + + uiAttributeLetterCasing, // TODO + // kLetterCaseType + + uiAttributeLinguisticRearrangement, // 0 = off, 1 = on + + uiAttributeNumberSpacing, // TODO + // kNumberSpacingType + + // TODO kSmartSwashType + + // TODO kDiacriticsType + + // TODO kVerticalPositionType + + uiAttributeFractionForms, // enum uiAttributeFractionForm + + // TODO kOverlappingCharactersType + + // TODO kTypographicExtrasType + uiAttributeSlashedZero, // 0 = off, 1 = on + + // TODO kMathematicalExtrasType + uiAttributeMathematicalGreek, // 0 = off, 1 = on + + // TODO kOrnamentSetsType + + // TODO kCharacterAlternativesType + + // TODO kDesignComplexityType + + // TODO kStyleOptionsType + // TODO titling? + + // TODO kCharacterShapeType + // we could probably make the enum now + + uiAttributeNumberCase, // TODO + + // TODO kTextSpacingType + + // TODO kTransliterationType + + // AAT defines the following values: + // 0 = none + // 1 = box + // 2 = rounded box + // 3 = circle + // 4 = inverted circle + // 5 = parentheses + // 6 = period + // 7 = roman numeral + // 8 = diamond + // 9 = inverted box + // 10 = inverted rounded box + uiAttributeGlyphAnnotations, // an integer from 0 to a font-specified upper bound + + // TODO kKanaSpacingType + + // TODO kIdeographicSpacingType + + // TODO kUnicodeDecompositionType + + // TODO kRubyKanaType + + // TODO kCJKSymbolAlternativesType + + // TODO kIdeographicAlternativesType + + // TODO kCJKVerticalRomanPlacementType + + // TODO kItalicCJKRomanType + + // TODO kCaseSensitiveLayoutType + + // TODO kAlternateKanaType + + uiAttributeStylisticAlternative1, // 0 = off, 1 = on + uiAttributeStylisticAlternative2, // 0 = off, 1 = on + uiAttributeStylisticAlternative3, // 0 = off, 1 = on + uiAttributeStylisticAlternative4, // 0 = off, 1 = on + uiAttributeStylisticAlternative5, // 0 = off, 1 = on + uiAttributeStylisticAlternative6, // 0 = off, 1 = on + uiAttributeStylisticAlternative7, // 0 = off, 1 = on + uiAttributeStylisticAlternative8, // 0 = off, 1 = on + uiAttributeStylisticAlternative9, // 0 = off, 1 = on + uiAttributeStylisticAlternative10, // 0 = off, 1 = on + uiAttributeStylisticAlternative11, // 0 = off, 1 = on + uiAttributeStylisticAlternative12, // 0 = off, 1 = on + uiAttributeStylisticAlternative13, // 0 = off, 1 = on + uiAttributeStylisticAlternative14, // 0 = off, 1 = on + uiAttributeStylisticAlternative15, // 0 = off, 1 = on + uiAttributeStylisticAlternative16, // 0 = off, 1 = on + uiAttributeStylisticAlternative17, // 0 = off, 1 = on + uiAttributeStylisticAlternative18, // 0 = off, 1 = on + uiAttributeStylisticAlternative19, // 0 = off, 1 = on + uiAttributeStylisticAlternative20, // 0 = off, 1 = on + + // TODO kContextualAlternatesType + + // TODO kLowerCaseType + + // TODO kUpperCaseType + + // TODO kLanguageTagType? + + // TODO kCJKRomanSpacingType + +#endif + // TODO uiAttributeSystem, // TODO uiAttributeCustom, }; +_UI_ENUM(uiAttributeFractionForm) { + uiAttributeFractionFormNone, + uiAttributeFractionFormVertical, + uiAttributeFractionFormDiagonal, +}; + +// TODO number case + struct uiAttributeSpec { uiAttribute Type; uintptr_t Value; From 8aa82592509d859840166a94a8cf5b3c1232d649 Mon Sep 17 00:00:00 2001 From: nopara73 Date: Tue, 14 Feb 2017 16:35:19 +0900 Subject: [PATCH 0650/1329] SharpUI is .NET Core, too. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a9ddce2e..3e5a3a58 100644 --- a/README.md +++ b/README.md @@ -147,8 +147,8 @@ Other people have made bindings to other languages: Language | Bindings --- | --- -C# / .NET Framework | [LibUI.Binding](https://github.com/NattyNarwhal/LibUI.Binding), [SharpUI](https://github.com/benpye/sharpui/) -C# / .NET Core | [DevZH.UI](https://github.com/noliar/DevZH.UI) +C# / .NET Framework | [LibUI.Binding](https://github.com/NattyNarwhal/LibUI.Binding) +C# / .NET Core | [DevZH.UI](https://github.com/noliar/DevZH.UI), [SharpUI](https://github.com/benpye/sharpui/) CHICKEN Scheme | [wasamasa/libui](https://github.com/wasamasa/libui) Crystal | [libui.cr](https://github.com/Fusion/libui.cr) D | [DerelictLibui (flat API)](https://github.com/Extrawurst/DerelictLibui), [libuid (object-oriented)](https://github.com/mogud/libuid) From 9c1293736bb95d81e52a5587815b47eae6be2d60 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 14 Feb 2017 02:53:21 -0500 Subject: [PATCH 0651/1329] Prepared the non-feature attributes based on the OS X list. --- ui_attrstr.h | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index 05d92857..4c967254 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -13,7 +13,54 @@ _UI_ENUM(uiAttribute) { // TODO rename to uiAttributeVertical? uiAttributeVerticalForms, // 0 = off, 1 = on - // TODO underline and others + // TODO kerning amount + // OS X: kCTKernAttributeName + // > 0: farther (TODO from advance or standard kerning?) + // == 0: no kerning + // < 0: closer (TODO same) + // undefined: standard kerning + // Pango: pango_attr_letter_spacing_new() + // parameter meaning unspecified + // Windows: requires Platform Update, SetLetterSpacing() + // parameter meaning unspecified + + // TODO kCTLigatureAttributeName vs below + + // TODO kCTStrokeWidthAttributeName/kCTStrokeColorAttributeName? doesn't seem to be present anywhere else? + + // TODO underline styles + // OS X: kCTUnderlineStyleAttributeName + // none, single, thick, or double + // styles: solid, dot, dash, dash dot, dash dot dot + // Pango: pango_attr_underline_new() + // none, single, double, low single, or wavy/error + // DirectWrite: only ever defined in version 1 + // none or single only + // we could do this with a custom renderer (see Petzold's articles; he does it there) + + // TODO kCTUnderlineColorAttributeName + // OS X: RGBA + // Pango: RGB(? TODO check for A in newer versions) + // DirectWrite: none; unsure if this means "same as text color" or "black only" (I assume Direct2D decides) + // we could do this with a custom renderer (see Petzold's articles; he does it there) + + // TODO kCTSuperscriptAttributeName vs below + + // TODO compare kCTVerticalFormsAttributeName to below? + + // TODO kCTGlyphInfoAttributeName + + // TODO kCTCharacterShapeAttributeName (seems to be provided by pango as well) + + // TODO kCTLanguageAttributeName? + + // TODO kCTRunDelegateAttributeName? + + // TODO kCTBaselineClassAttributeName, kCTBaselineInfoAttributeName, kCTBaselineReferenceInfoAttributeName + + // TODO kCTWritingDirectionAttributeName? + + // TODO kCTRubyAnnotationAttributeName vs below #if 0 From aa7d1e28ead53fd2209ec670375343a7bfca487a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 14 Feb 2017 02:55:42 -0500 Subject: [PATCH 0652/1329] More TODOs. --- ui_attrstr.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ui_attrstr.h b/ui_attrstr.h index 4c967254..dda453ce 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -62,6 +62,9 @@ _UI_ENUM(uiAttribute) { // TODO kCTRubyAnnotationAttributeName vs below + // TODO strikethroughs? + // TODO see what Pango and DirectWrite provide that Core Text doesn't seem to + #if 0 // These attributes represent typographic features. Each feature From 250cba7c6733de6b03c0e7a35304fc06c0770da2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 14 Feb 2017 13:21:47 -0500 Subject: [PATCH 0653/1329] Added more potential attributes. --- ui_attrstr.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index dda453ce..666113e1 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -62,8 +62,10 @@ _UI_ENUM(uiAttribute) { // TODO kCTRubyAnnotationAttributeName vs below - // TODO strikethroughs? - // TODO see what Pango and DirectWrite provide that Core Text doesn't seem to + // TODO strikethroughs? (pango yes, directwrite yes, os x no) + // TODO baseline offsets? (pango yes) + // TODO size scales? (pango yes) + // TODO fallbacks (pango: enable or disable) #if 0 From eb7dfa5cc4a325f4c39251e5b85f6c6c9c657dcb Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 14 Feb 2017 14:21:58 -0500 Subject: [PATCH 0654/1329] More attribute work, including trimming unsupported or nonsensical attributes. --- ui_attrstr.h | 44 +++++++++++++++++--------------------------- 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index 666113e1..8152c8d5 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -28,38 +28,13 @@ _UI_ENUM(uiAttribute) { // TODO kCTStrokeWidthAttributeName/kCTStrokeColorAttributeName? doesn't seem to be present anywhere else? - // TODO underline styles - // OS X: kCTUnderlineStyleAttributeName - // none, single, thick, or double - // styles: solid, dot, dash, dash dot, dash dot dot - // Pango: pango_attr_underline_new() - // none, single, double, low single, or wavy/error - // DirectWrite: only ever defined in version 1 - // none or single only - // we could do this with a custom renderer (see Petzold's articles; he does it there) - - // TODO kCTUnderlineColorAttributeName - // OS X: RGBA - // Pango: RGB(? TODO check for A in newer versions) - // DirectWrite: none; unsure if this means "same as text color" or "black only" (I assume Direct2D decides) - // we could do this with a custom renderer (see Petzold's articles; he does it there) + uiAttributeUnderline, // enum uiDrawUnderlineStyle + uiAttributeUnderlineColor, // enum uiDrawUnderlineColor // TODO kCTSuperscriptAttributeName vs below - // TODO compare kCTVerticalFormsAttributeName to below? - - // TODO kCTGlyphInfoAttributeName - - // TODO kCTCharacterShapeAttributeName (seems to be provided by pango as well) - - // TODO kCTLanguageAttributeName? - - // TODO kCTRunDelegateAttributeName? - // TODO kCTBaselineClassAttributeName, kCTBaselineInfoAttributeName, kCTBaselineReferenceInfoAttributeName - // TODO kCTWritingDirectionAttributeName? - // TODO kCTRubyAnnotationAttributeName vs below // TODO strikethroughs? (pango yes, directwrite yes, os x no) @@ -215,6 +190,20 @@ _UI_ENUM(uiAttribute) { // TODO uiAttributeCustom, }; +_UI_ENUM(uiDrawUnderlineStyle) { + uiDrawUnderlineStyleNone, + uiDrawUnderlineStyleSingle, + uiDrawUnderlineStyleDouble, + uiDrawUnderlineStyleSuggestion, // wavy or dotted underlines used for spelling/grammar checkers +}; + +_UI_ENUM(uiDrawUnderlineColor) { + uiDrawUnderlineColorCustom, // also use R/G/B/A fields + uiDrawUnderlineColorSpelling, + uiDrawUnderlineColorGrammar, + uiDrawUnderlineColorAuxiliary, // for instance, the color used by smart replacements on OS X +}; + _UI_ENUM(uiAttributeFractionForm) { uiAttributeFractionFormNone, uiAttributeFractionFormVertical, @@ -402,6 +391,7 @@ _UI_EXTERN void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y // TODO make sure this works right for right-aligned and center-aligned lines and justified lines and RTL text _UI_EXTERN double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line); +// TODO vertical carets _UI_EXTERN void uiDrawCaret(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t pos, int *line); // TODO allow blinking From 4195bc3b4b16620cc95d64f1b3e3689dc3a9ed3d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 14 Feb 2017 14:57:56 -0500 Subject: [PATCH 0655/1329] Finalized and implemented underlines on OS X. --- common/attrlist.c | 6 ++ darwin/attrstr.m | 126 +++++++++++++++++++++++++++++---- darwin/main.m | 3 + darwin/uipriv_darwin.h | 2 + examples/drawtext/attributes.c | 39 +++++++++- ui_attrstr.h | 1 + 6 files changed, 164 insertions(+), 13 deletions(-) diff --git a/common/attrlist.c b/common/attrlist.c index 1ca52537..746b1e76 100644 --- a/common/attrlist.c +++ b/common/attrlist.c @@ -302,6 +302,12 @@ static int specsIdentical(struct attr *attr, uiAttributeSpec *spec) case uiAttributeSize: // TODO use a closest match? return attr->spec.Double == spec->Double; + case uiAttributeUnderlineColor: + if (attr->spec.Value != spec->Value) + return 0; + if (attr->spec.Value != uiDrawUnderlineColorCustom) + return 1; + // otherwise fall through case uiAttributeColor: case uiAttributeBackground: // TODO use a closest match? diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 051bec16..0a5dd8ac 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -1,6 +1,59 @@ // 12 february 2017 #import "uipriv_darwin.h" +// this is what AppKit does internally +// WebKit does this too; see https://github.com/adobe/webkit/blob/master/Source/WebCore/platform/graphics/mac/GraphicsContextMac.mm +static NSColor *spellingColor = nil; +static NSColor *grammarColor = nil; +static NSColor *auxiliaryColor = nil; + +static NSColor *tryColorNamed(NSString *name) +{ + NSImage *img; + + img = [NSImage imageNamed:name]; + if (img == nil) + return nil; + return [NSColor colorWithPatternImage:img]; +} + +void initUnderlineColors(void) +{ + spellingColor = tryColorNamed(@"NSSpellingDot"); + if (spellingColor == nil) { + // WebKit says this is needed for "older systems"; not sure how old, but 10.11 AppKit doesn't look for this + spellingColor = tryColorNamed(@"SpellingDot"); + if (spellingColor == nil) + spellingColor = [NSColor redColor]; + } + [spellingColor retain]; // override autoreleasing + + grammarColor = tryColorNamed(@"NSGrammarDot"); + if (grammarColor == nil) { + // WebKit says this is needed for "older systems"; not sure how old, but 10.11 AppKit doesn't look for this + grammarColor = tryColorNamed(@"GrammarDot"); + if (grammarColor == nil) + grammarColor = [NSColor greenColor]; + } + [grammarColor retain]; // override autoreleasing + + auxiliaryColor = tryColorNamed(@"NSCorrectionDot"); + if (auxiliaryColor == nil) { + // WebKit says this is needed for "older systems"; not sure how old, but 10.11 AppKit doesn't look for this + auxiliaryColor = tryColorNamed(@"CorrectionDot"); + if (auxiliaryColor == nil) + auxiliaryColor = [NSColor blueColor]; + } + [auxiliaryColor retain]; // override autoreleasing +} + +void uninitUnderlineColors(void) +{ + [auxiliaryColor release]; + [grammarColor release]; + [spellingColor release]; +} + // unlike the other systems, Core Text rolls family, size, weight, italic, width, AND opentype features into the "font" attribute // TODO opentype features and AAT fvar table info is lost, so a handful of fonts in the font panel ("Titling" variants of some fonts and Skia and possibly others but those are the examples I know about) cannot be represented by uiDrawFontDescriptor; what *can* we do about this since this info is NOT part of the font on other platforms? // TODO see if we could use NSAttributedString? @@ -64,15 +117,34 @@ static backgroundBlock mkBackgroundBlock(size_t start, size_t end, double r, dou }); } +static CGColorRef mkcolor(uiAttributeSpec *spec) +{ + CGColorSpaceRef colorspace; + CGColorRef color; + CGFloat components[4]; + + colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); + if (colorspace == NULL) { + // TODO + } + components[0] = spec->R; + components[1] = spec->G; + components[2] = spec->B; + components[3] = spec->A; + color = CGColorCreate(colorspace, components); + CFRelease(colorspace); + return color; +} + static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) { struct foreachParams *p = (struct foreachParams *) data; CFRange range; - CGColorSpaceRef colorspace; CGColorRef color; - CGFloat components[4]; size_t ostart, oend; backgroundBlock block; + int32_t us; + CFNumberRef num; ostart = start; oend = end; @@ -112,16 +184,7 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t }); break; case uiAttributeColor: - colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); - if (colorspace == NULL) { - // TODO - } - components[0] = spec->R; - components[1] = spec->G; - components[2] = spec->B; - components[3] = spec->A; - color = CGColorCreate(colorspace, components); - CFRelease(colorspace); + color = mkcolor(spec); CFAttributedStringSetAttribute(p->mas, range, kCTForegroundColorAttributeName, color); CFRelease(color); break; @@ -137,6 +200,45 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t else CFAttributedStringSetAttribute(p->mas, range, kCTVerticalFormsAttributeName, kCFBooleanFalse); break; + case uiAttributeUnderline: + switch (spec->Value) { + case uiDrawUnderlineStyleNone: + us = kCTUnderlineStyleNone; + break; + case uiDrawUnderlineStyleSingle: + us = kCTUnderlineStyleSingle; + break; + case uiDrawUnderlineStyleDouble: + us = kCTUnderlineStyleDouble; + break; + case uiDrawUnderlineStyleSuggestion: + // TODO incorrect if a solid color + us = kCTUnderlineStyleThick; + break; + } + num = CFNumberCreate(NULL, kCFNumberSInt32Type, &us); + CFAttributedStringSetAttribute(p->mas, range, kCTUnderlineStyleAttributeName, num); + CFRelease(num); + break; + case uiAttributeUnderlineColor: + switch (spec->Value) { + case uiDrawUnderlineColorCustom: + color = mkcolor(spec); + break; + case uiDrawUnderlineColorSpelling: + color = [spellingColor CGColor]; + break; + case uiDrawUnderlineColorGrammar: + color = [grammarColor CGColor]; + break; + case uiDrawUnderlineColorAuxiliary: + color = [auxiliaryColor CGColor]; + break; + } + CFAttributedStringSetAttribute(p->mas, range, kCTUnderlineColorAttributeName, color); + if (spec->Value == uiDrawUnderlineColorCustom) + CFRelease(color); + break; // TODO } return 0; diff --git a/darwin/main.m b/darwin/main.m index 59a8683b..440343bc 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -125,6 +125,8 @@ const char *uiInit(uiInitOptions *o) [realNSApp() setMainMenu:[appDelegate().menuManager makeMenubar]]; setupFontPanel(); + + initUnderlineColors(); } globalPool = [[NSAutoreleasePool alloc] init]; @@ -140,6 +142,7 @@ void uiUninit(void) [globalPool release]; @autoreleasepool { + uninitUnderlineColors(); [delegate release]; [realNSApp() setDelegate:nil]; [app release]; diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index bf009522..32ae8688 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -145,5 +145,7 @@ extern CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd); extern void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescriptor *uidesc); // attrstr.m +extern void initUnderlineColors(void); +extern void uninitUnderlineColors(void); typedef void (^backgroundBlock)(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y); extern CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks); diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index 1aa74a72..6e54dfcb 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -98,8 +98,45 @@ static void setupAttributedString(void) spec.Type = uiAttributeVerticalForms; spec.Value = 1; uiAttributedStringSetAttribute(attrstr, &spec, start, end); - uiAttributedStringAppendUnattributed(attrstr, " (which you can draw rotated for proper vertical text)"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "multiple"; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeUnderline; + spec.Value = uiDrawUnderlineStyleSingle; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " "); + next = "underlines"; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeUnderline; + spec.Value = uiDrawUnderlineStyleDouble; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + spec.Type = uiAttributeUnderlineColor; + spec.Value = uiDrawUnderlineColorCustom; + spec.R = 0.5; + spec.G = 0.0; + spec.B = 1.0; + spec.A = 1.0; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " ("); + next = "including underlines for spelling correction and the like"; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeUnderline; + spec.Value = uiDrawUnderlineStyleSuggestion; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + spec.Type = uiAttributeUnderlineColor; + spec.Value = uiDrawUnderlineColorSpelling; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + uiAttributedStringAppendUnattributed(attrstr, ", "); next = "multiple TODO"; diff --git a/ui_attrstr.h b/ui_attrstr.h index 8152c8d5..c3738a42 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -29,6 +29,7 @@ _UI_ENUM(uiAttribute) { // TODO kCTStrokeWidthAttributeName/kCTStrokeColorAttributeName? doesn't seem to be present anywhere else? uiAttributeUnderline, // enum uiDrawUnderlineStyle + // TODO what is the color in the case we don't specify it, black or the text color? uiAttributeUnderlineColor, // enum uiDrawUnderlineColor // TODO kCTSuperscriptAttributeName vs below From e4b761d611c5bc2e1a999a52a7dc9bdd7570c5b6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 14 Feb 2017 17:15:19 -0500 Subject: [PATCH 0656/1329] Defined more of the OpenType attributes. --- ui_attrstr.h | 113 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 71 insertions(+), 42 deletions(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index c3738a42..a0b8a2d6 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -26,13 +26,12 @@ _UI_ENUM(uiAttribute) { // TODO kCTLigatureAttributeName vs below - // TODO kCTStrokeWidthAttributeName/kCTStrokeColorAttributeName? doesn't seem to be present anywhere else? - uiAttributeUnderline, // enum uiDrawUnderlineStyle // TODO what is the color in the case we don't specify it, black or the text color? uiAttributeUnderlineColor, // enum uiDrawUnderlineColor // TODO kCTSuperscriptAttributeName vs below + // all it does is set the below attribute so // TODO kCTBaselineClassAttributeName, kCTBaselineInfoAttributeName, kCTBaselineReferenceInfoAttributeName @@ -65,58 +64,59 @@ _UI_ENUM(uiAttribute) { uiAttributeCommonLigatures, // 0 = off, 1 = on uiAttributeRequiredLigatures, // 0 = off, 1 = on - uiAttributeRareLigatures, // 0 = off, 1 = on - uiAttributeLogoLigatures, // 0 = off, 1 = on - uiAttributeRebusPictureLigatures, // 0 = off, 1 = on - uiAttributeDipthrongLigatures, // 0 = off, 1 = on - uiAttributeSquaredLigatures, // 0 = off, 1 = on - // TODO rename - uiAttributeAbbreviatedSquared, // 0 = off, 1 = on - uiAttributeSymbolLigatures, // 0 = off, 1 = on + // AAT calls these "rare ligatures" + uiAttributeDiscretionaryLigatures, // 0 = off, 1 = on uiAttributeContextualLigatures, // 0 = off, 1 = on uiAttributeHistoricalLigatures, // 0 = off, 1 = on - uiAttributeCursiveConnection, // 0 = none, 1 = some, 2 = all + // TODO uiAttributeCursiveConnection, // 0 = none, 1 = some, 2 = all - uiAttributeLetterCasing, // TODO - // kLetterCaseType + uiAttributeUnicase, // 0 = off, 1 = on - uiAttributeLinguisticRearrangement, // 0 = off, 1 = on + // TODO uiAttributeLinguisticRearrangement, // 0 = off, 1 = on - uiAttributeNumberSpacing, // TODO - // kNumberSpacingType + // AAT: if off, "monospaced number spacing"; if on, "proportional number spacing" + // OpenType: non-proportional numbers are called "tabular" + // TODO uiAttributeProportionalNumbers, // 0 = off, 1 = on + // TODO kNumberSpacingType 0 or 1 + // TODO really? // TODO kSmartSwashType // TODO kDiacriticsType - // TODO kVerticalPositionType + uiAttributeSuperscripts, // enum uiAttributeSuperscript uiAttributeFractionForms, // enum uiAttributeFractionForm - // TODO kOverlappingCharactersType - - // TODO kTypographicExtrasType uiAttributeSlashedZero, // 0 = off, 1 = on - // TODO kMathematicalExtrasType uiAttributeMathematicalGreek, // 0 = off, 1 = on - // TODO kOrnamentSetsType + // AAT defines the following values: + // 0 = none + // 1 = dingbats + // 2 = pi characters + // 3 = fleurons + // 4 = decorative borders + // 5 = international symbols + // 6 = mathematical symbols + // OpenType says alphanumeric characters must(? TODO) have one form each and the bullet character U+2022 (•) can have many + uiAttributeOrnamentalForms, // an integer from 0 to a font-specified upper bound + // TODO provide a function to get the upper bound? // TODO kCharacterAlternativesType - // TODO kDesignComplexityType + uiAttributeTitlingCapitalForms, // 0 = off, 1 = on - // TODO kStyleOptionsType - // TODO titling? + // AAT calls these "character shapes" + uiAttributeHanCharacterForms, // enum uiAttributeHanCharacterForm - // TODO kCharacterShapeType - // we could probably make the enum now - - uiAttributeNumberCase, // TODO + // OpenType calls these "old-style" + uiAttributeLowercaseNumbers, // 0 = off, 1 = on // TODO kTextSpacingType + // see kKanaSpacingType below // TODO kTransliterationType @@ -133,26 +133,27 @@ _UI_ENUM(uiAttribute) { // 9 = inverted box // 10 = inverted rounded box uiAttributeGlyphAnnotations, // an integer from 0 to a font-specified upper bound + // TODO provide a function to get the upper bound? // TODO kKanaSpacingType - // TODO kIdeographicSpacingType + // should these be provided? CAN they be provided? // TODO kUnicodeDecompositionType // TODO kRubyKanaType - // TODO kCJKSymbolAlternativesType - - // TODO kIdeographicAlternativesType - // TODO kCJKVerticalRomanPlacementType + // this is 'valt' in OpenType but I don't know if I want to make it selectable or not - // TODO kItalicCJKRomanType + uiAttributeCJKRomansToItalics, // 0 = off, 1 = on - // TODO kCaseSensitiveLayoutType + uiAttributeCaseSensitiveLayout, // 0 = off, 1 = on + // AAT: this is called "case-sensitive spacing" + uiAttributeCapitalSpacing, // 0 = off, 1 = on - // TODO kAlternateKanaType + uiAttributeAlternateHorizontalKana, // 0 = off, 1 = on + uiAttributeAlternateVerticalKana, // 0 = off, 1 = on uiAttributeStylisticAlternative1, // 0 = off, 1 = on uiAttributeStylisticAlternative2, // 0 = off, 1 = on @@ -175,11 +176,12 @@ _UI_ENUM(uiAttribute) { uiAttributeStylisticAlternative19, // 0 = off, 1 = on uiAttributeStylisticAlternative20, // 0 = off, 1 = on - // TODO kContextualAlternatesType + uiAttributeContextualAlternates, // 0 = off, 1 = on + uiAttributeSwashes, // 0 = off, 1 = on + uiAttributeContextualSwashes, // 0 = off, 1 = on - // TODO kLowerCaseType - - // TODO kUpperCaseType + uiAttributeLowercaseCapForms, // enum uiAttributeCapForm + uiAttributeUppercaseCapForms, // enum uiAttributeCapForm // TODO kLanguageTagType? @@ -187,7 +189,7 @@ _UI_ENUM(uiAttribute) { #endif - // TODO uiAttributeSystem, + // TODO uiAttributeSystem, (this might not be doable with DirectWrite) // TODO uiAttributeCustom, }; @@ -205,12 +207,39 @@ _UI_ENUM(uiDrawUnderlineColor) { uiDrawUnderlineColorAuxiliary, // for instance, the color used by smart replacements on OS X }; +_UI_ENUM(uiAttributeSuperscript) { + uiAttributeSuperscriptNone, + uiAttributeSuperscriptSuperscript, // AAT: "superior" + uiAttributeSuperscriptSubscript, // AAT: "inferior" + uiAttributeSuperscriptOrdinal, + uiAttributeSuperscriptScientificInferior, +}; + _UI_ENUM(uiAttributeFractionForm) { uiAttributeFractionFormNone, uiAttributeFractionFormVertical, uiAttributeFractionFormDiagonal, }; +_UI_ENUM(uiAttributeHanCharacterForm) { + uiAttributeHanCharacterFormTraditional, + uiAttributeHanCharacterFormSimplified, + uiAttributeHanCharacterFormJIS1978, + uiAttributeHanCharacterFormJIS1983, + uiAttributeHanCharacterFormJIS1990, + uiAttributeHanCharacterFormExpert, + uiAttributeHanCharacterFormJIS2004, + uiAttributeHanCharacterFormHojo, + uiAttributeHanCharacterFormNLC, + uiAttributeHanCharacterFormTraditionalNames, +}; + +_UI_ENUM(uiAttributeCapForm) { + uiAttributeCapFormNormal, + uiAttributeCapFormSmallCaps, + uiAttributeCapFormPetiteCaps, +}; + // TODO number case struct uiAttributeSpec { From 2ffcd192c063871c82002d72c70f2dfb7d368352 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 14 Feb 2017 19:00:35 -0500 Subject: [PATCH 0657/1329] Wrote the common code (for DirectWrite and Pango) to deal with OpenType features. Now to merge with Core Text's internal AAT-to-OpenType mapping. --- common/opentype.c | 227 ++++++++++++++++++++++++++++++++++++++++++++++ ui_attrstr.h | 8 +- 2 files changed, 233 insertions(+), 2 deletions(-) create mode 100644 common/opentype.c diff --git a/common/opentype.c b/common/opentype.c new file mode 100644 index 00000000..12d14699 --- /dev/null +++ b/common/opentype.c @@ -0,0 +1,227 @@ +// 14 february 2017 +#include "../ui.h" +#include "uipriv.h" + +// note: each tag should only appear in quotes once; this allows automated tools to determine what we cover and don't cover + +typedef void (*specToOpenTypeEnumFunc)(const char *featureTag, uint32_t param, void *data); + +static void boolspec(uiAttributeSpec *spec, const char *featureTag, specToOpenTypeEnumFunc f, void *data) +{ + if (spec->Value != 0) { + (*f)(featureTag, 1, data); + return; + } + (*f)(featureTag, 0, data); +} + +void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) +{ + switch (spec->Type) { + case uiAttributeStandardLigatures: + boolspec(spec, "liga", f, data); + return; + case uiAttributeRequiredLigatures: + boolspec(spec, "rlig", f, data); + return; + case uiAttributeDiscretionaryLigatures: + boolspec(spec, "dlig", f, data); + return; + case uiAttributeContextualLigatures: + boolspec(spec, "clig", f, data); + return; + case uiAttributeHistoricalLigatures: + boolspec(spec, "hlig", f, data); + return; + case uiAttributeUnicase: + boolspec(spec, "unic", f, data); + return; + // TODO is this correct or should we explicitly switch the rest off too? + case uiAttributeSuperscripts: + switch (spec->Value) { + case uiAttributeSuperscriptSuperscript: + (*f)("sups", 1, data); + break; + case uiAttributeSuperscriptSubscript: + (*f)("subs", 1, data); + break; + case uiAttributeSuperscriptOrdinal: + (*f)("ordn", 1, data); + break; + case uiAttributeSuperscriptScientificInferior: + (*f)("sinf", 1, data); + break; + } + return; + // TODO is this correct or should we explicitly switch the rest off too? + case uiAttributeFractionForms: + switch (spec->Value) { + case uiAttributeFractionFormVertical: + (*f)("afrc", 1, data); + break; + case uiAttributeFractionFormDiagonal: + (*f)("frac", 1, data); + break; + } + return; + case uiAttributeSlashedZero: + boolspec(spec, "zero", data); + return; + case uiAttributeMathematicalGreek: + boolspec(spec, "mgrk", data); + return; + case uiAttributeOrnamentalForms: + (*f)("ornm", (uint32_t) (spec->Value), data); + return; + case uiAttributeTitlingCapitalForms: + boolspec(spec, "titl", data); + return; + // TODO is this correct or should we explicitly switch the rest off too? + case uiAttributeHanCharacterForms: + switch (spec->Value) { + case uiAttributeHanCharacterFormTraditional: + (*f)("trad", 1, data); + break; + case uiAttributeHanCharacterFormSimplified: + (*f)("smpl", 1, data); + break; + case uiAttributeHanCharacterFormJIS1978: + (*f)("jp78", 1, data); + break; + case uiAttributeHanCharacterFormJIS1983: + (*f)("jp83", 1, data); + break; + case uiAttributeHanCharacterFormJIS1990: + (*f)("jp90", 1, data); + break; + case uiAttributeHanCharacterFormExpert: + (*f)("expt", 1, data); + break; + case uiAttributeHanCharacterFormJIS2004: + (*f)("jp04", 1, data); + break; + case uiAttributeHanCharacterFormHojo: + (*f)("hojo", 1, data); + break; + case uiAttributeHanCharacterFormNLC: + (*f)("nlck", 1, data); + break; + case uiAttributeHanCharacterFormTraditionalNames: + (*f)("tnam", 1, data); + break; + } + return; + case uiAttributeLowercaseNumbers: + boolspec(spec, "onum", data); + return; + case uiAttributeGlyphAnnotations: + (*f)("nalt", (uint32_t) (spec->Value), data); + return; + case uiAttributeCJKRomanToitalics: + boolspec(spec, "ital", data); + return; + case uiAttributeCaseSensitiveForms: + boolspec(spec, "case", data); + return; + case uiAttributeCapitalSpacing: + boolspec(spec, "cpsp", data); + return; + case uiAttributeAlternateHorizontalKana: + boolspec(spec, "hkna", data); + return; + case uiAttributeAlternateVerticalKana: + boolspec(spec, "vkna", data); + return; + case uiAttributeStylisticAlternative1: + boolspec(spec, "ss01", data); + return; + case uiAttributeStylisticAlternative2: + boolspec(spec, "ss02", data); + return; + case uiAttributeStylisticAlternative3: + boolspec(spec, "ss03", data); + return; + case uiAttributeStylisticAlternative4: + boolspec(spec, "ss04", data); + return; + case uiAttributeStylisticAlternative5: + boolspec(spec, "ss05", data); + return; + case uiAttributeStylisticAlternative6: + boolspec(spec, "ss06", data); + return; + case uiAttributeStylisticAlternative7: + boolspec(spec, "ss07", data); + return; + case uiAttributeStylisticAlternative8: + boolspec(spec, "ss08", data); + return; + case uiAttributeStylisticAlternative9: + boolspec(spec, "ss09", data); + return; + case uiAttributeStylisticAlternative10: + boolspec(spec, "ss10", data); + return; + case uiAttributeStylisticAlternative11: + boolspec(spec, "ss11", data); + return; + case uiAttributeStylisticAlternative12: + boolspec(spec, "ss12", data); + return; + case uiAttributeStylisticAlternative13: + boolspec(spec, "ss13", data); + return; + case uiAttributeStylisticAlternative14: + boolspec(spec, "ss14", data); + return; + case uiAttributeStylisticAlternative15: + boolspec(spec, "ss15", data); + return; + case uiAttributeStylisticAlternative16: + boolspec(spec, "ss16", data); + return; + case uiAttributeStylisticAlternative17: + boolspec(spec, "ss17", data); + return; + case uiAttributeStylisticAlternative18: + boolspec(spec, "ss18", data); + return; + case uiAttributeStylisticAlternative19: + boolspec(spec, "ss19", data); + return; + case uiAttributeStylisticAlternative20: + boolspec(spec, "ss20", data); + return; + case uiAttributeContextualAlternates: + boolspec(spec, "calt", data); + return; + case uiAttributeSwashes: + boolspec(spec, "swsh", data); + return; + case uiAttributeContextualSwashes: + boolspec(spec, "cswh", data); + return; + // TODO is this correct or should we explicitly switch the rest off too? + case uiAttributeLowercaseCapForms: + switch (spec->Value) { + case uiAttributeCapFormSmallCaps: + (*f)("smcp", 1, data); + break; + case uiAttributeCapFormPetiteCaps: + (*f)("pcap", 1, data); + break; + } + return; + // TODO is this correct or should we explicitly switch the rest off too? + case uiAttributeUppercaseCapForms: + switch (spec->Value) { + case uiAttributeCapFormSmallCaps: + (*f)("c2sc", 1, data); + break; + case uiAttributeCapFormPetiteCaps: + (*f)("c2pc", 1, data); + break; + } + return; + } +} diff --git a/ui_attrstr.h b/ui_attrstr.h index a0b8a2d6..3da40fe1 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -62,7 +62,8 @@ _UI_ENUM(uiAttribute) { // TODO kAllTypographicFeaturesType - uiAttributeCommonLigatures, // 0 = off, 1 = on + // AAT calls these "common ligatures" + uiAttributeStandardLigatures, // 0 = off, 1 = on uiAttributeRequiredLigatures, // 0 = off, 1 = on // AAT calls these "rare ligatures" uiAttributeDiscretionaryLigatures, // 0 = off, 1 = on @@ -132,6 +133,7 @@ _UI_ENUM(uiAttribute) { // 8 = diamond // 9 = inverted box // 10 = inverted rounded box + // TODO rename to AnnotatedForms? uiAttributeGlyphAnnotations, // an integer from 0 to a font-specified upper bound // TODO provide a function to get the upper bound? @@ -148,13 +150,15 @@ _UI_ENUM(uiAttribute) { uiAttributeCJKRomansToItalics, // 0 = off, 1 = on - uiAttributeCaseSensitiveLayout, // 0 = off, 1 = on + // AAT calls this "case-sensitive layout" + uiAttributeCaseSensitiveForms, // 0 = off, 1 = on // AAT: this is called "case-sensitive spacing" uiAttributeCapitalSpacing, // 0 = off, 1 = on uiAttributeAlternateHorizontalKana, // 0 = off, 1 = on uiAttributeAlternateVerticalKana, // 0 = off, 1 = on + // TODO "Alternate"? unify all this uiAttributeStylisticAlternative1, // 0 = off, 1 = on uiAttributeStylisticAlternative2, // 0 = off, 1 = on uiAttributeStylisticAlternative3, // 0 = off, 1 = on From 16b0ca518e57cbd8bd1def4f65e068768deaee75 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 14 Feb 2017 21:18:56 -0500 Subject: [PATCH 0658/1329] Synced opentype.c to AAT and more TODOs. --- common/opentype.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++- ui_attrstr.h | 25 +++++++++++++------- 2 files changed, 74 insertions(+), 9 deletions(-) diff --git a/common/opentype.c b/common/opentype.c index 12d14699..f561eeab 100644 --- a/common/opentype.c +++ b/common/opentype.c @@ -2,7 +2,8 @@ #include "../ui.h" #include "uipriv.h" -// note: each tag should only appear in quotes once; this allows automated tools to determine what we cover and don't cover +// Notes: +// - Each tag should only appear in quotes once; this allows automated tools to determine what we cover and don't cover typedef void (*specToOpenTypeEnumFunc)(const char *featureTag, uint32_t param, void *data); @@ -15,6 +16,15 @@ static void boolspec(uiAttributeSpec *spec, const char *featureTag, specToOpenTy (*f)(featureTag, 0, data); } +static void boolspecnot(uiAttributeSpec *spec, const char *featureTag, specToOpenTypeEnumFunc f, void *data) +{ + if (spec->Value == 0) { + (*f)(featureTag, 1, data); + return; + } + (*f)(featureTag, 0, data); +} + void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) { switch (spec->Type) { @@ -32,11 +42,25 @@ void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) return; case uiAttributeHistoricalLigatures: boolspec(spec, "hlig", f, data); + // This technically isn't what is meant by "historical ligatures", but Core Text's internal AAT-to-OpenType mapping says to include it, so we include it too + boolspec(spec, "hist", f, data); return; case uiAttributeUnicase: boolspec(spec, "unic", f, data); return; // TODO is this correct or should we explicitly switch the rest off too? + case uiAttributeNumberSpacings: + // TODO does Core Text set both? do we? + switch (spec->Value) { + case uiAttributeNumberSpacingProportional: + (*f)("pnum", 1, data); + break; + case uiAttributeNumberSpacingTitling: + (*f)("tnum", 1, data); + break; + } + return; + // TODO is this correct or should we explicitly switch the rest off too? case uiAttributeSuperscripts: switch (spec->Value) { case uiAttributeSuperscriptSuperscript: @@ -73,6 +97,9 @@ void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) case uiAttributeOrnamentalForms: (*f)("ornm", (uint32_t) (spec->Value), data); return; + case uiAttributeSpecificCharacterForm: + (*f)("aalt", (uint32_t) (spec->Value), data); + return; case uiAttributeTitlingCapitalForms: boolspec(spec, "titl", data); return; @@ -113,6 +140,12 @@ void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) return; case uiAttributeLowercaseNumbers: boolspec(spec, "onum", data); + // Core Text's internal AAT-to-OpenType mapping says to include this, so we include it too + // TODO is it always set? + boolspecnot(spec, "lnum", data); + return; + case uiAttributeHanjaToHangul: + boolspec(spec, "hngl", data); return; case uiAttributeGlyphAnnotations: (*f)("nalt", (uint32_t) (spec->Value), data); @@ -225,3 +258,26 @@ void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) return; } } + +// TODO missing that AAT uses directly: +// - pkna, pwid, fwid, hwid, twid, qwid, palt, valt, vpal, halt, vhal, kern, vkrn (CJK width control) +// - ruby +// missing that AAT knows about: +// - abvf, abvm, abvs, blwf, blwm, blws (baselines) +// - akhn, dist, half, haln, nukt, rkrf, rphf, vatu (Devanagari support) +// - ccmp (compositions) +// - cjct, pres, pstf, psts (Indic script support) +// - curs (cursive positioning) +// - dnom, numr (fraction parts) +// - falt, fina, init, isol, jalt, medi, mset (Arabic support) +// - rclt (required contextual alternates) +// - fin2, fin3, med2 (Syriac script support) +// - lfbd, opbd, rtbd (optical bounds support) +// - ljmo, tjmo, vjmo (Hangul Jamo support) +// - locl (Cyrillic support) +// - ltra, ltrm, rtla, rtlm (bidi support) +// - mark, mkmk (mark positioning) +// - pref (Khmer support) +// - rand (random glyph selection candidates) +// - salt (stylistic alternatives) +// - size (sizing info) diff --git a/ui_attrstr.h b/ui_attrstr.h index 3da40fe1..c33e3fb6 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -76,11 +76,8 @@ _UI_ENUM(uiAttribute) { // TODO uiAttributeLinguisticRearrangement, // 0 = off, 1 = on - // AAT: if off, "monospaced number spacing"; if on, "proportional number spacing" - // OpenType: non-proportional numbers are called "tabular" - // TODO uiAttributeProportionalNumbers, // 0 = off, 1 = on - // TODO kNumberSpacingType 0 or 1 - // TODO really? + // TODO rename this + uiAttributeNumberSpacings, // enum uiAttributeNumberSpacing // TODO kSmartSwashType @@ -106,7 +103,13 @@ _UI_ENUM(uiAttribute) { uiAttributeOrnamentalForms, // an integer from 0 to a font-specified upper bound // TODO provide a function to get the upper bound? - // TODO kCharacterAlternativesType + // AAT calls this "character alternatives" and defines the + // following values: + // 0 = none + // OpenType calls this "access all alternates". + // TODO doesn't OpenType do the same about 0? + uiAttributeSpecificCharacterForm, // an integer from 0 to a font-specified upper bound + // TODO provide a function to get the upper bound? uiAttributeTitlingCapitalForms, // 0 = off, 1 = on @@ -119,7 +122,7 @@ _UI_ENUM(uiAttribute) { // TODO kTextSpacingType // see kKanaSpacingType below - // TODO kTransliterationType + uiAttributeHanjaToHangul, // 0 = off, 1 = on // AAT defines the following values: // 0 = none @@ -139,7 +142,7 @@ _UI_ENUM(uiAttribute) { // TODO kKanaSpacingType // TODO kIdeographicSpacingType - // should these be provided? CAN they be provided? + // can they be provided independently of kTextSpacingType? Core Text doesn't seem to // TODO kUnicodeDecompositionType @@ -211,6 +214,12 @@ _UI_ENUM(uiDrawUnderlineColor) { uiDrawUnderlineColorAuxiliary, // for instance, the color used by smart replacements on OS X }; +_UI_ENUM(uiAttributeNumberSpacing) { + uiAttributeNumberSpacingProportional, + // AAT calls this "monospaced" + uiAttributeNumberSpacingTabular, +}; + _UI_ENUM(uiAttributeSuperscript) { uiAttributeSuperscriptNone, uiAttributeSuperscriptSuperscript, // AAT: "superior" From da22adac7ffefac4737dd7865eb9994d502ca5d0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 14 Feb 2017 22:37:01 -0500 Subject: [PATCH 0659/1329] More feature list completeness work. --- common/opentype.c | 42 +++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/common/opentype.c b/common/opentype.c index f561eeab..56c2f572 100644 --- a/common/opentype.c +++ b/common/opentype.c @@ -3,7 +3,7 @@ #include "uipriv.h" // Notes: -// - Each tag should only appear in quotes once; this allows automated tools to determine what we cover and don't cover +// - Each tag should only appear in quotes once (including within comments); this allows automated tools to determine what we cover and don't cover typedef void (*specToOpenTypeEnumFunc)(const char *featureTag, uint32_t param, void *data); @@ -264,20 +264,48 @@ void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) // - ruby // missing that AAT knows about: // - abvf, abvm, abvs, blwf, blwm, blws (baselines) -// - akhn, dist, half, haln, nukt, rkrf, rphf, vatu (Devanagari support) +// - harfbuzz says tibetan uses this // - ccmp (compositions) -// - cjct, pres, pstf, psts (Indic script support) // - curs (cursive positioning) // - dnom, numr (fraction parts) -// - falt, fina, init, isol, jalt, medi, mset (Arabic support) +// - falt, jalt (Arabic support) // - rclt (required contextual alternates) -// - fin2, fin3, med2 (Syriac script support) // - lfbd, opbd, rtbd (optical bounds support) -// - ljmo, tjmo, vjmo (Hangul Jamo support) // - locl (Cyrillic support) // - ltra, ltrm, rtla, rtlm (bidi support) // - mark, mkmk (mark positioning) -// - pref (Khmer support) // - rand (random glyph selection candidates) // - salt (stylistic alternatives) // - size (sizing info) +// +// script-specific; core text and pango/harfbuzz use these automatically based on the language +// TODO if DirectWrite does too we can ignore them and just provide a language attribute (they all use BCP 47 syntax for language names) +// Tag Core Text? Harfbuzz? +// akhn TODO yes +// cjct TODO yes +// dist TODO yes +// falt TODO TODO +// fin2 TODO yes +// fin3 TODO yes +// fina TODO yes +// half TODO yes +// haln TODO yes +// init TODO yes +// isol TODO yes +// jalt TODO TODO +// ljmo TODO yes +// locl TODO all horz(!) +// med2 TODO yes +// medi TODO yes +// mset TODO yes +// nukt TODO yes +// pref TODO yes +// pres TODO yes +// pstf TODO yes +// psts TODO yes +// rclt TODO all horz(!) +// rkrf TODO yes +// rphf TODO yes +// tjmo TODO yes +// vatu TODO yes +// vjmo TODO yes From bda35b40bdd45dcdb4a3d1c7607e5451ff78a63e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 14 Feb 2017 23:56:20 -0500 Subject: [PATCH 0660/1329] Filled in Core Text language details. --- common/opentype.c | 55 ++++++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/common/opentype.c b/common/opentype.c index 56c2f572..282df9ba 100644 --- a/common/opentype.c +++ b/common/opentype.c @@ -263,10 +263,9 @@ void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) // - pkna, pwid, fwid, hwid, twid, qwid, palt, valt, vpal, halt, vhal, kern, vkrn (CJK width control) // - ruby // missing that AAT knows about: -// - abvf, abvm, abvs, blwf, blwm, blws (baselines) -// - harfbuzz says tibetan uses this // - ccmp (compositions) // - curs (cursive positioning) +// - Core Text uses this in language-specific stuff // - dnom, numr (fraction parts) // - falt, jalt (Arabic support) // - rclt (required contextual alternates) @@ -281,31 +280,37 @@ void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) // script-specific; core text and pango/harfbuzz use these automatically based on the language // TODO if DirectWrite does too we can ignore them and just provide a language attribute (they all use BCP 47 syntax for language names) // Tag Core Text? Harfbuzz? -// akhn TODO yes -// cjct TODO yes -// dist TODO yes +// abvf yes yes +// abvm yes yes +// abvs yes TODO +// akhn yes yes +// blwf yes yes +// blwm yes yes +// blws yes TODO +// cjct yes yes +// dist yes yes // falt TODO TODO -// fin2 TODO yes -// fin3 TODO yes -// fina TODO yes -// half TODO yes -// haln TODO yes -// init TODO yes -// isol TODO yes +// fin2 yes yes +// fin3 yes yes +// fina yes yes +// half yes yes +// haln yes yes +// init yes yes +// isol yes yes // jalt TODO TODO -// ljmo TODO yes +// ljmo yes yes // locl TODO all horz(!) -// med2 TODO yes -// medi TODO yes +// med2 yes yes +// medi yes yes // mset TODO yes -// nukt TODO yes -// pref TODO yes -// pres TODO yes -// pstf TODO yes -// psts TODO yes +// nukt yes yes +// pref yes yes +// pres yes yes +// pstf yes yes +// psts yes yes // rclt TODO all horz(!) -// rkrf TODO yes -// rphf TODO yes -// tjmo TODO yes -// vatu TODO yes -// vjmo TODO yes +// rkrf yes yes +// rphf yes yes +// tjmo yes yes +// vatu yes yes +// vjmo yes yes From 81b520249b213fffcbd3c785da6d8beb7e4eee80 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 15 Feb 2017 01:34:26 -0500 Subject: [PATCH 0661/1329] More attribute work. Getting a clearer idea. --- TODO.md | 43 +++++++++++++++++++++++++++++++++++++++++++ common/opentype.c | 10 +++++----- ui_attrstr.h | 13 +++++-------- 3 files changed, 53 insertions(+), 13 deletions(-) diff --git a/TODO.md b/TODO.md index 33ac95af..8bf48004 100644 --- a/TODO.md +++ b/TODO.md @@ -127,3 +127,46 @@ label shortcut keys [02:15:00] 1.40.3 [02:20:46] I'll ahve to keep this in mind then, thanks [02:20:59] if only there was a cairo-specific attribute for alpha... + +FONT LOADING + +[00:10:08] andlabs: is there API yet to load from memory? last i checked i only found from file (which we use in builder). https://git.gnome.org/browse/gnome-builder/tree/libide/editor/ide-editor-map-bin.c#n115 +[00:13:12] mrmcq2u_ (mrmcq2u@109.79.53.90) joined the channel +[00:14:59] mrmcq2u (mrmcq2u@109.79.73.102) left IRC (Ping timeout: 181 seconds) +[00:15:19] hergertme: no, which is why I was asking =P +[00:15:30] I would have dug down if I could ensure at least something about the backends a GTK+ 3 program uses +[00:15:39] on all platforms except windows and os x +[00:16:11] to the best of my (partially outdated, given pace of foss) knowledge there isn't an api to load from memory +[00:16:28] you can possibly make a tmpdir and put a temp file in there +[00:16:52] and load that as your font dir in your FcConfig, so any PangoFontDescription would point to that one font, no matter what +[00:17:18] (using the API layed out in that link) +[00:18:18] dsr1014__ (dsr1014@c-73-72-102-18.hsd1.il.comcast.net) joined the channel +[00:35:18] simukis_ (simukis@78-60-58-6.static.zebra.lt) left IRC (Quit: simukis_) +[00:35:48] dreamon_ (dreamon@ppp-188-174-49-41.dynamic.mnet-online.de) joined the channel +[00:40:09] samtoday_ (samtoday@114-198-116-132.dyn.iinet.net.au) joined the channel +[00:40:32] mjog (mjog@120.18.225.46) joined the channel +[00:40:38] hergertme: not necessarily fontconfig +[00:40:45] it can be with ft2 or xft I guess +[00:40:55] especially since I want the API NOT to make the font part of the font panel +[00:42:07] what sort of deprecated code are you trying to support? +[00:42:35] both of those are deprecated in pango fwiw +[00:43:06] on Linux im pretty sure we use FC everywhere these days +[00:44:46] (and gtk_widget_set_font_map() is how you get your custom font into a widget without affecting the global font lists, as layed out in that link) +[00:49:14] vasaikar (vasaikar@125.16.97.121) joined the channel +[00:50:14] karlt (karl@2400:e780:801:224:f121:e611:d139:e70e) left IRC (Client exited) +[00:50:49] karlt (karl@2400:e780:801:224:f121:e611:d139:e70e) joined the channel +[00:51:43] PioneerAxon (PioneerAxo@122.171.61.146) left IRC (Ping timeout: 180 seconds) +[00:57:47] PioneerAxon (PioneerAxo@106.201.37.181) joined the channel +[01:03:01] karlt (karl@2400:e780:801:224:f121:e611:d139:e70e) left IRC (Ping timeout: 181 seconds) +[01:05:49] muhannad (muhannad@95.218.26.152) left IRC (Quit: muhannad) +[01:07:51] hergertme: hm +[01:07:54] all right, thanks +[01:08:05] hergertme: fwiw right now my requirement is 3.10 +[01:10:47] ah, well you'll probably be missing the neccesary font API on gtk_widget +[01:11:04] but pango should be fine even back as far as https://developer.gnome.org/pango/1.28/PangoFcFontMap.html +[01:11:56] good +[01:12:04] because this is for custom drawing into a DrawingArea +[01:14:12] presumably just create your PangoContext as normal, but call pango_context_set_font_map() with the map you've setup. now, the load a font from a file i dont think was added to FontConfig until later though (not sure what release) +[01:15:53] FcConfigAppFontAddFile() <-- that API +[01:16:30] great, and they don't say what version the API was added in teh docs +function: ide_editor_map_bin_add() diff --git a/common/opentype.c b/common/opentype.c index 282df9ba..f874752b 100644 --- a/common/opentype.c +++ b/common/opentype.c @@ -60,7 +60,6 @@ void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) break; } return; - // TODO is this correct or should we explicitly switch the rest off too? case uiAttributeSuperscripts: switch (spec->Value) { case uiAttributeSuperscriptSuperscript: @@ -150,6 +149,9 @@ void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) case uiAttributeGlyphAnnotations: (*f)("nalt", (uint32_t) (spec->Value), data); return; + case uiAttributeRubyKanaForms: + boolspec(spec, "ruby", data); + return; case uiAttributeCJKRomanToitalics: boolspec(spec, "ital", data); return; @@ -261,12 +263,9 @@ void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) // TODO missing that AAT uses directly: // - pkna, pwid, fwid, hwid, twid, qwid, palt, valt, vpal, halt, vhal, kern, vkrn (CJK width control) -// - ruby // missing that AAT knows about: // - ccmp (compositions) -// - curs (cursive positioning) -// - Core Text uses this in language-specific stuff -// - dnom, numr (fraction parts) +// - dnom, numr (fraction parts) — no AAT equivalent... // - falt, jalt (Arabic support) // - rclt (required contextual alternates) // - lfbd, opbd, rtbd (optical bounds support) @@ -288,6 +287,7 @@ void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) // blwm yes yes // blws yes TODO // cjct yes yes +// curs yes yes // dist yes yes // falt TODO TODO // fin2 yes yes diff --git a/ui_attrstr.h b/ui_attrstr.h index c33e3fb6..8707a375 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -24,8 +24,6 @@ _UI_ENUM(uiAttribute) { // Windows: requires Platform Update, SetLetterSpacing() // parameter meaning unspecified - // TODO kCTLigatureAttributeName vs below - uiAttributeUnderline, // enum uiDrawUnderlineStyle // TODO what is the color in the case we don't specify it, black or the text color? uiAttributeUnderlineColor, // enum uiDrawUnderlineColor @@ -35,13 +33,14 @@ _UI_ENUM(uiAttribute) { // TODO kCTBaselineClassAttributeName, kCTBaselineInfoAttributeName, kCTBaselineReferenceInfoAttributeName - // TODO kCTRubyAnnotationAttributeName vs below - // TODO strikethroughs? (pango yes, directwrite yes, os x no) // TODO baseline offsets? (pango yes) // TODO size scales? (pango yes) // TODO fallbacks (pango: enable or disable) + // TODO document that this will also enable language-specific font features (TODO on DirectWrite too?) + uiAttributeLanguage, // BCP 47 string + #if 0 // These attributes represent typographic features. Each feature @@ -79,7 +78,7 @@ _UI_ENUM(uiAttribute) { // TODO rename this uiAttributeNumberSpacings, // enum uiAttributeNumberSpacing - // TODO kSmartSwashType + // TODO kSmartSwashType, falt and jalt // TODO kDiacriticsType @@ -146,7 +145,7 @@ _UI_ENUM(uiAttribute) { // TODO kUnicodeDecompositionType - // TODO kRubyKanaType + uiAttributeRubyKanaForms, // 0 = off, 1 = on // TODO kCJKVerticalRomanPlacementType // this is 'valt' in OpenType but I don't know if I want to make it selectable or not @@ -190,8 +189,6 @@ _UI_ENUM(uiAttribute) { uiAttributeLowercaseCapForms, // enum uiAttributeCapForm uiAttributeUppercaseCapForms, // enum uiAttributeCapForm - // TODO kLanguageTagType? - // TODO kCJKRomanSpacingType #endif From 85fd3b72af943e8753e0f28ca6963f11b522adcf Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 15 Feb 2017 09:44:57 -0500 Subject: [PATCH 0662/1329] More support for uiAttributeLanguage. --- common/attrlist.c | 29 +++++++++++++++++++++++++++++ ui_attrstr.h | 1 + 2 files changed, 30 insertions(+) diff --git a/common/attrlist.c b/common/attrlist.c index 746b1e76..3bf8caff 100644 --- a/common/attrlist.c +++ b/common/attrlist.c @@ -291,6 +291,32 @@ static int boolsEqual(struct attr *attr, uiAttributeSpec *spec) return attr->spec.Value != 0 && spec->Value != 0; } +// BCP 47 is ASCII-only +static int asciiStringsEqualCaseFold(const char *a, const char *b) +{ + char c, d; + + for (;;) { + if (*a == *b) { + if (*a == '\0') + return 1; + a++; + b++; + continue; + } + c = *a; + if (c >= 'A' && c <= 'Z') + c += 'a' - 'A'; + d = *b; + if (d >= 'A' && d <= 'Z') + d += 'a' - 'A'; + if (c != d) + return 0; + a++; + b++; + } +} + static int specsIdentical(struct attr *attr, uiAttributeSpec *spec) { if (attr->spec.Type != spec->Type) @@ -298,6 +324,7 @@ static int specsIdentical(struct attr *attr, uiAttributeSpec *spec) switch (attr->spec.Type) { case uiAttributeFamily: // TODO should we start copying these strings? + // TODO should this be case-insensitive? return strcmp((char *) (attr->spec.Value), (char *) (spec->Value)) == 0; case uiAttributeSize: // TODO use a closest match? @@ -317,6 +344,8 @@ static int specsIdentical(struct attr *attr, uiAttributeSpec *spec) attr->spec.A == spec->A; case uiAttributeVerticalForms: return boolsEqual(attr, spec); + case uiAttributeLanguage: + return asciiStringsEqualCaseFold((char *) (attr->spec.Value), (char *) (spec->Value)); // TODO } // handles the rest diff --git a/ui_attrstr.h b/ui_attrstr.h index 8707a375..fac03b0f 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -39,6 +39,7 @@ _UI_ENUM(uiAttribute) { // TODO fallbacks (pango: enable or disable) // TODO document that this will also enable language-specific font features (TODO on DirectWrite too?) + // TODO document that this should be strict BCP 47 form (A-Z, a-z, 0-9, and -) for maximum compatibility uiAttributeLanguage, // BCP 47 string #if 0 From 669538e9ceebbeb147b3ca315353b0639974c658 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 15 Feb 2017 16:22:42 -0500 Subject: [PATCH 0663/1329] Implemented uiAttributeLanguage on OS X. Untested. --- darwin/attrstr.m | 27 +++++++--- darwin/fontmatch.m | 109 +++++++++++++++++++++++++++++++++++++++++ darwin/uipriv_darwin.h | 1 + 3 files changed, 131 insertions(+), 6 deletions(-) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 0a5dd8ac..b46872ce 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -1,6 +1,8 @@ // 12 february 2017 #import "uipriv_darwin.h" +// LONGTERM FUTURE for typographic features, on 10.10 we can use OpenType tags directly! + // this is what AppKit does internally // WebKit does this too; see https://github.com/adobe/webkit/blob/master/Source/WebCore/platform/graphics/mac/GraphicsContextMac.mm static NSColor *spellingColor = nil; @@ -70,8 +72,9 @@ struct foreachParams { struct fontParams { uiDrawFontDescriptor desc; uint16_t featureTypes[maxFeatures]; - uint16_t featureSpecifiers[maxFeatures]; + uint16_t featureSelectors[maxFeatures]; size_t nFeatures; + const char *language; }; static void ensureFontInRange(struct foreachParams *p, size_t start, size_t end) @@ -239,18 +242,27 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t if (spec->Value == uiDrawUnderlineColorCustom) CFRelease(color); break; + // locale identifiers are specified as BCP 47: https://developer.apple.com/reference/corefoundation/cflocale?language=objc + case uiAttributeLanguage: + // LONGTERM FUTURE when we move to 10.9, switch to using kCTLanguageAttributeName + ensureFontInRange(p, start, end); + adjustFontInRange(p, start, end, ^(struct fontParams *fp) { + fp->language = (const char *) (spec->Value); + }); + break; // TODO } return 0; } -static CTFontRef fontdescToCTFont(uiDrawFontDescriptor *fd) +static CTFontRef fontdescToCTFont(struct fontParams *fp) { CTFontDescriptorRef desc; CTFontRef font; - desc = fontdescToCTFontDescriptor(fd); - font = CTFontCreateWithFontDescriptor(desc, fd->Size, NULL); + desc = fontdescToCTFontDescriptor(&(fp->desc)); + desc = fontdescAppendFeatures(desc, fp->featureTypes, fp->featureSelectors, fp->nFeatures, fp->language); + font = CTFontCreateWithFontDescriptor(desc, fp->desc.Size, NULL); CFRelease(desc); // TODO correct? return font; } @@ -263,7 +275,7 @@ static void applyAndFreeFontAttributes(struct foreachParams *p) CFRange range; fp = (struct fontParams *) [val pointerValue]; - font = fontdescToCTFont(&(fp->desc)); + font = fontdescToCTFont(fp); range.location = [key integerValue]; range.length = 1; CFAttributedStringSetAttribute(p->mas, range, kCTFontAttributeName, font); @@ -305,6 +317,7 @@ CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray CFAttributedStringRef base; CFMutableAttributedStringRef mas; struct foreachParams fep; + struct fontParams ffp; cfstr = CFStringCreateWithCharacters(NULL, attrstrUTF16(p->String), attrstrUTF16Len(p->String)); if (cfstr == NULL) { @@ -316,7 +329,9 @@ CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray if (defaultAttrs == NULL) { // TODO } - defaultCTFont = fontdescToCTFont(p->DefaultFont); + memset(&ffp, 0, sizeof (struct fontParams)); + ffp.desc = *(p->DefaultFont); + defaultCTFont = fontdescToCTFont(&ffp); CFDictionaryAddValue(defaultAttrs, kCTFontAttributeName, defaultCTFont); CFRelease(defaultCTFont); ps = mkParagraphStyle(p); diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index e1660c8b..184f1c6e 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -254,6 +254,115 @@ CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd) stretchesToCTWidths[fd->Stretch]); } +// fortunately features that aren't supported are simply ignored, so we can copy them all in +// LONGTERM FUTURE when we switch to 10.9, the language parameter won't be needed anymore +// LONGTERM FUTURE and on 10.10 we can use OpenType tags directly! +CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uint16_t *types, const uint16_t *selectors, size_t n, const char *language) +{ + CTFontDescriptorRef new; + CFMutableArrayRef outerArray; + CFDictionaryRef innerDict; + CFNumberRef numType, numSelector; + const void *keys[2], *values[2]; + size_t i; + CFArrayRef languages; + CFIndex il, nl; + CFStringRef curlang; + char d[2]; + + outerArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + if (outerArray == NULL) { + // TODO + } + keys[0] = kCTFontFeatureTypeIdentifierKey; + keys[1] = kCTFontFeatureSelectorIdentifierKey; + for (i = 0; i < n; i++) { + numType = CFNumberCreate(NULL, kCFNumberSInt16Type, + (const SInt16 *) (types + i)); + numSelector = CFNumberCreate(NULL, kCFNumberSInt16Type, + (const SInt16 *) (selectors + i)); + values[0] = numType; + values[1] = numSelector; + innerDict = CFDictionaryCreate(NULL, + keys, values, 2, + // TODO are these correct? + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (innerDict == NULL) { + // TODO + } + CFArrayAppendValue(outerArray, innerDict); + CFRelease(innerDict); + CFRelease(numSelector); + CFRelease(numType); + } + + // now we have to take care of the language + // TODO can we assume this is present? + if (language != NULL) { + languages = CTFontDescriptorCopyAttribute(desc, kCTFontLanguagesAttribute); + nl = CFArrayGetCount(languages); + d[0] = language[0]; + if (d[0] >= 'A' && d[0] <= 'Z') + d[0] += 'a' - 'A'; + d[1] = language[1]; + if (d[1] >= 'A' && d[1] <= 'Z') + d[1] += 'a' - 'A'; + for (il = 0; il < nl; il++) { + char c[2]; + + curlang = (CFStringRef) CFArrayGetValueAtIndex(languages, il); + // TODO check for failure + CFStringGetBytes(curlang, CFRangeMake(0, 2), + kCFStringEncodingUTF8, 0, false, + (UInt8 *) c, 2, NULL); + if (c[0] >= 'A' && c[0] <= 'Z') + c[0] += 'a' - 'A'; + if (c[1] >= 'A' && c[1] <= 'Z') + c[1] += 'a' - 'A'; + if (c[0] == d[0] && c[1] == d[1]) + break; + } + if (il != nl) { + uint16_t typ; + + typ = kLanguageTagType; + numType = CFNumberCreate(NULL, kCFNumberSInt16Type, + (const SInt16 *) (&typ)); + numSelector = CFNumberCreate(NULL, kCFNumberCFIndexType, + &il); + values[0] = numType; + values[1] = numSelector; + innerDict = CFDictionaryCreate(NULL, + keys, values, 2, + // TODO are these correct? + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (innerDict == NULL) { + // TODO + } + CFArrayAppendValue(outerArray, innerDict); + CFRelease(innerDict); + CFRelease(numSelector); + CFRelease(numType); + } + CFRelease(languages); + } + + keys[0] = kCTFontFeatureSettingsAttribute; + values[0] = outerArray; + innerDict = CFDictionaryCreate(NULL, + keys, values, 1, + // TODO are these correct? + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFRelease(outerArray); + new = CTFontDescriptorCreateCopyWithAttributes(desc, innerDict); + CFRelease(desc); + CFRelease(innerDict); + return new; +} + // TODO deduplicate this from italicCloseness() static uiDrawTextItalic italicFromCTItalic(CTFontDescriptorRef desc, CFDictionaryRef traits) { diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 32ae8688..9d662cd5 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -142,6 +142,7 @@ extern void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdg // fontmatch.m extern CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd); +extern CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uint16_t *types, const uint16_t *selectors, size_t n, const char *language); extern void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescriptor *uidesc); // attrstr.m From 6761c0a9f72c3c9b6a81ae4eb094c9c4e8ccafb9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 15 Feb 2017 16:40:04 -0500 Subject: [PATCH 0664/1329] Added the language tag stuff to the example program. More TODOs. --- examples/drawtext/attributes.c | 25 +++++++++++++++++++++++++ examples/drawtext/main.c | 5 +++++ 2 files changed, 30 insertions(+) diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index 6e54dfcb..c3da5581 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -139,6 +139,31 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, ", "); + next = "\xD0\xB1\xD0\xB3\xD0\xB4\xD0\xBF\xD1\x82"; + uiAttributedStringAppendUnattributed(attrstr, "multiple languages (compare "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeItalic; + spec.Value = uiDrawTextItalicItalic; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + spec.Type = uiAttributeLanguage; + spec.Value = (uintptr_t) "ru"; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " to "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeItalic; + spec.Value = uiDrawTextItalicItalic; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + spec.Type = uiAttributeLanguage; + spec.Value = (uintptr_t) "sr"; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " \xE2\x80\x94 may require changing the font)"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + next = "multiple TODO"; } diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c index 822382d0..848def89 100644 --- a/examples/drawtext/main.c +++ b/examples/drawtext/main.c @@ -1,6 +1,11 @@ // 17 january 2017 #include "drawtext.h" +// okay everything is definitely bugged in the OS X code +// - occasional segfaults on startup +// - very rare size attributes in the attributed string example don't terminate for a while, making everything big +// - very very rare trace/bpt faults on startup + static uiWindow *mainwin; static uiBox *box; static uiCombobox *exampleList; From ddffce6d44906c1e1a3e67bfd81b6a43c15de5ce Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 15 Feb 2017 19:42:32 -0500 Subject: [PATCH 0665/1329] More work. This is annoying, stupid 10.9. --- darwin/fontmatch.m | 1 + examples/drawtext/attributes.c | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index 184f1c6e..8d5094a7 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -327,6 +327,7 @@ CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uint1 uint16_t typ; typ = kLanguageTagType; + il++; numType = CFNumberCreate(NULL, kCFNumberSInt16Type, (const SInt16 *) (&typ)); numSelector = CFNumberCreate(NULL, kCFNumberCFIndexType, diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index c3da5581..9d0c2310 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -139,6 +139,7 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, ", "); + // thanks to https://twitter.com/codeman38/status/831924064012886017 next = "\xD0\xB1\xD0\xB3\xD0\xB4\xD0\xBF\xD1\x82"; uiAttributedStringAppendUnattributed(attrstr, "multiple languages (compare "); start = uiAttributedStringLen(attrstr); @@ -181,9 +182,8 @@ static uiDrawTextLayoutParams params; #define margins 10 static uiBox *panel; -static uiCheckbox *showExtents; static uiCheckbox *showLineBounds; -static uiCheckbox *showLineGuides; +static uiFontButton *fontButton; // TODO should be const? static uiDrawBrush fillBrushes[4] = { @@ -266,6 +266,15 @@ static void draw(uiAreaDrawParams *p) static struct example attributesExample; +static void changeFont(uiFontButton *b, void *data) +{ + if (defaultFont.Family != fontFamily) + uiFreeText(defaultFont.Family); + // TODO rename defaultFont + uiFontButtonFont(fontButton, &defaultFont); + redraw(); +} + // TODO share? static void checkboxChecked(uiCheckbox *c, void *data) { @@ -286,6 +295,10 @@ struct example *mkAttributesExample(void) { panel = uiNewVerticalBox(); showLineBounds = newCheckbox("Show Line Bounds"); + fontButton = uiNewFontButton(); + uiFontButtonOnChanged(fontButton, changeFont, NULL); + // TODO set the font button to the current defaultFont + uiBoxAppend(panel, uiControl(fontButton), 0); attributesExample.name = "Attributed Text"; attributesExample.panel = uiControl(panel); From 4f5328ae652ca56f2214442bfa77631b28be5f7a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 15 Feb 2017 21:11:44 -0500 Subject: [PATCH 0666/1329] And added the typographical features for AAT. --- darwin/aat.m | 389 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 389 insertions(+) create mode 100644 darwin/aat.m diff --git a/darwin/aat.m b/darwin/aat.m new file mode 100644 index 00000000..05622f3c --- /dev/null +++ b/darwin/aat.m @@ -0,0 +1,389 @@ +// 14 february 2017 +#import "uipriv_darwin.h" + +typedef void (*specToAATEnumFunc)(uint16_t type, uint16_t selector, void *data); + +static void boolspec(uiAttributeSpec *spec, uint16_t type, uint16_t ifTrue, uint16_t ifFalse, specToAATEnumFunc f, void *data) +{ + if (spec->Value != 0) { + (*f)(type, ifTrue, data); + return; + } + (*f)(type, ifFalse, data); +} + +void specToAAT(uiAttributeSpec *spec, specToAATEnumFunc f, void *data) +{ + switch (spec->Type) { + case uiAttributeStandardLigatures: + boolspec(spec, kLigaturesType, + kCommonLigaturesOnSelector, + kCommonLigaturesOffSelector, + f, data); + return; + case uiAttributeRequiredLigatures: + boolspec(spec, kLigaturesType, + kRequiredLigaturesOnSelector, + kRequiredLigaturesOffSelector, + f, data); + return; + case uiAttributeDiscretionaryLigatures: + boolspec(spec, kLigaturesType, + kRareLigaturesOnSelector, + kRareLigaturesOffSelector, + f, data); + return; + case uiAttributeContextualLigatures: + boolspec(spec, kLigaturesType, + kContextualLigaturesOnSelector, + kContextualLigaturesOffSelector, + f, data); + return; + case uiAttributeHistoricalLigatures: + boolspec(spec, kLigaturesType, + kHistoricalLigaturesOnSelector, + kHistoricalLigaturesOffSelector, + f, data); + return; + case uiAttributeUnicase: + // TODO is this correct, or should we provide an else case? + if (spec->Value != 0) + // this is undocumented; it comes from Core Text's internal AAT-to-OpenType conversion table + (*f)(kLetterCaseType, 14, data); + return; + // TODO make an array? + case uiAttributeNumberSpacings: + switch (spec->Value) { + case uiAttributeNumberSpacingProportional: + (*f)(kNumberSpacingType, kProportionalNumbersSelector, data); + break; + case uiAttributeNumberSpacingTitling: + (*f)(kNumberSpacingType, kMonospacedNumbersSelector, data); + break; + } + return; + // TODO make an array? + case uiAttributeSuperscripts: + switch (spec->Value) { + case uiAttributeSuperscriptNone: + (*f)(kVerticalPositionType, kNormalPositionSelector, data); + break; + case uiAttributeSuperscriptSuperscript: + (*f)(kVerticalPositionType, kSuperiorsSelector, data); + break; + case uiAttributeSuperscriptSubscript: + (*f)(kVerticalPositionType, kInferiorsSelector, data); + break; + case uiAttributeSuperscriptOrdinal: + (*f)(kVerticalPositionType, kOrdinalsSelector, data); + break; + case uiAttributeSuperscriptScientificInferior: + (*f)(kVerticalPositionType, kScientificInferiorsSelector, data); + break; + } + return; + // TODO make an array? + case uiAttributeFractionForms: + switch (spec->Value) { + case uiAttributeFractionFormNone: + (*f)(kFractionsType, kNoFractionsSelector, data); + break; + case uiAttributeFractionFormVertical: + (*f)(kFractionsType, kVerticalFractionsSelector, data); + break; + case uiAttributeFractionFormDiagonal: + (*f)(kFractionsType, kDiagonalFractionsSelector, data); + break; + } + return; + case uiAttributeSlashedZero: + boolspec(spec, kTypographicExtrasType, + kSlashedZeroOnSelector, + kSlashedZeroOffSelector, + f, data); + return; + case uiAttributeMathematicalGreek: + boolspec(spec, kMathematicalExtrasType, + kMathematicalGreekOnSelector, + kMathematicalGreekOffSelector, + f, data); + return; + case uiAttributeOrnamentalForms: + (*f)(kOrnamentSetsType, (uint16_t) (spec->Value), data); + return; + case uiAttributeSpecificCharacterForm: + (*f)(kCharacterAlternativesType, (uint16_t) (spec->Value), data); + return; + case uiAttributeTitlingCapitalForms: + // TODO is this correct, or should we provide an else case? + if (spec->Value != 0) + (*f)(kStyleOptionsType, kTitlingCapsSelector, data); + return; + // TODO make an array? + case uiAttributeHanCharacterForms: + switch (spec->Value) { + case uiAttributeHanCharacterFormTraditional: + (*f)(kCharacterShapeType, kTraditionalCharactersSelector, data); + break; + case uiAttributeHanCharacterFormSimplified: + (*f)(kCharacterShapeType, kSimplifiedCharactersSelector, data); + break; + case uiAttributeHanCharacterFormJIS1978: + (*f)(kCharacterShapeType, kJIS1978CharactersSelector, data); + break; + case uiAttributeHanCharacterFormJIS1983: + (*f)(kCharacterShapeType, kJIS1983CharactersSelector, data); + break; + case uiAttributeHanCharacterFormJIS1990: + (*f)(kCharacterShapeType, kJIS1990CharactersSelector, data); + break; + case uiAttributeHanCharacterFormExpert: + (*f)(kCharacterShapeType, kExpertCharactersSelector, data); + break; + case uiAttributeHanCharacterFormJIS2004: + (*f)(kCharacterShapeType, kJIS2004CharactersSelector, data); + break; + case uiAttributeHanCharacterFormHojo: + (*f)(kCharacterShapeType, kHojoCharactersSelector, data); + break; + case uiAttributeHanCharacterFormNLC: + (*f)(kCharacterShapeType, kNLCCharactersSelector, data); + break; + case uiAttributeHanCharacterFormTraditionalNames: + (*f)(kCharacterShapeType, kTraditionalNamesCharactersSelector, data); + break; + } + return; + case uiAttributeLowercaseNumbers: + // TODO is this correct, or should we provide an else case? + if (spec->Value != 0) + (*f)(kNumberCaseType, kLowerCaseNumbersSelector, data); + return; + case uiAttributeHanjaToHangul: + // TODO is this correct, or should we provide an else case? + if (spec->Value != 0) + (*f)(kTransliterationType, kHanjaToHangulSelector, data); + return; + case uiAttributeGlyphAnnotations: + (*f)(kAnnotationType, (uint16_t) (spec->Value), data); + return; + case uiAttributeRubyKanaForms: + // include this for completeness + boolspec(spec, kRubyKanaType, + kRubyKanaSelector, + kNoRubyKanaSelector, + f, data); + // this is the current one + boolspec(spec, kRubyKanaType, + kRubyKanaOnSelector, + kRubyKanaOffSelector, + f, data); + return; + case uiAttributeCJKRomanToitalics: + // include this for completeness + boolspec(spec, kItalicCJKRomanType, + kCJKItalicRomanSelector, + kNoCJKItalicRomanSelector, + f, data); + // this is the current one + boolspec(spec, kItalicCJKRomanType, + kCJKItalicRomanOnSelector, + kCJKItalicRomanOffSelector, + f, data); + return; + case uiAttributeCaseSensitiveForms: + boolspec(spec, kCaseSensitiveLayoutType, + kCaseSensitiveLayoutOnSelector, + kCaseSensitiveLayoutOffSelector, + f, data); + return; + case uiAttributeCapitalSpacing: + boolspec(spec, kCaseSensitiveLayoutType, + kCaseSensitiveSpacingOnSelector, + kCaseSensitiveSpacingOffSelector, + f, data); + return; + case uiAttributeAlternateHorizontalKana: + boolspec(spec, kAlternateKanaType, + kAlternateHorizKanaOnSelector, + kAlternateHorizKanaOffSelector, + f, data); + return; + case uiAttributeAlternateVerticalKana: + boolspec(spec, kAlternateKanaType, + kAlternateVertKanaOnSelector, + kAlternateVertKanaOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative1: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltOneOnSelector, + kStylisticAltOneOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative2: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltTwoOnSelector, + kStylisticAltTwoOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative3: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltThreeOnSelector, + kStylisticAltThreeOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative4: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltFourOnSelector, + kStylisticAltFourOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative5: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltFiveOnSelector, + kStylisticAltFiveOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative6: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltSixOnSelector, + kStylisticAltSixOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative7: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltSevenOnSelector, + kStylisticAltSevenOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative8: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltEightOnSelector, + kStylisticAltEightOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative9: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltNineOnSelector, + kStylisticAltNineOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative10: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltTenOnSelector, + kStylisticAltTenOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative11: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltElevenOnSelector, + kStylisticAltElevenOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative12: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltTwelveOnSelector, + kStylisticAltTwelveOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative13: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltThirteenOnSelector, + kStylisticAltThirteenOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative14: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltFourteenOnSelector, + kStylisticAltFourteenOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative15: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltFifteenOnSelector, + kStylisticAltFifteenOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative16: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltSixteenOnSelector, + kStylisticAltSixteenOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative17: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltSeventeenOnSelector, + kStylisticAltSeventeenOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative18: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltEighteenOnSelector, + kStylisticAltEighteenOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative19: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltNineteenOnSelector, + kStylisticAltNineteenOffSelector, + f, data); + return; + case uiAttributeStylisticAlternative20: + boolspec(spec, kStylisticAlternativesType, + kStylisticAltTwentyOnSelector, + kStylisticAltTwentyOffSelector, + f, data); + return; + case uiAttributeContextualAlternates: + boolspec(spec, kContextualAlternatesType, + kContextualAlternatesOnSelector, + kContextualAlternatesOffSelector, + f, data); + return; + case uiAttributeSwashes: + boolspec(spec, kContextualAlternatesType, + kSwashAlternatesOnSelector, + kSwashAlternatesOffSelector, + f, data); + return; + case uiAttributeContextualSwashes: + boolspec(spec, kContextualAlternatesType, + kContextualSwashAlternatesOnSelector, + kContextualSwashAlternatesOffSelector, + f, data); + return; + // TODO use arrays? + case uiAttributeLowercaseCapForms: + switch (spec->Value) { + case uiAttributeCapFormNone: + (*f)(kLowerCaseType, kDefaultLowerCaseSelector, data); + break; + case uiAttributeCapFormSmallCaps: + // include this for compatibility (some fonts that come with OS X still use this!) + // TODO make it boolean? + (*f)(kLetterCaseType, kSmallCapsSelector, data); + // this is the current one + (*f)(kLowerCaseType, kLowerCaseSmallCapsSelector, data); + break; + case uiAttributeCapFormPetiteCaps: + (*f)(kLowerCaseType, kLowerCasePetiteCapsSelector, data); + break; + } + return; + // TODO use arrays? + case uiAttributeUppercaseCapForms: + switch (spec->Value) { + case uiAttributeCapFormNone: + (*f)(kUpperCaseType, kDefaultUpperCaseSelector, data); + break; + case uiAttributeCapFormSmallCaps: + (*f)(kUpperCaseType, kUpperCaseSmallCapsSelector, data); + break; + case uiAttributeCapFormPetiteCaps: + (*f)(kUpperCaseType, kUpperCasePetiteCapsSelector, data); + break; + } + return; + } +} From 1eb2ffaf828f091cbd92d61fa1b580fcac5bd813 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 15 Feb 2017 21:30:21 -0500 Subject: [PATCH 0667/1329] FLIPPED THE SWITCH --- ui_attrstr.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index fac03b0f..31ac8f6b 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -42,8 +42,6 @@ _UI_ENUM(uiAttribute) { // TODO document that this should be strict BCP 47 form (A-Z, a-z, 0-9, and -) for maximum compatibility uiAttributeLanguage, // BCP 47 string -#if 0 - // These attributes represent typographic features. Each feature // is a separate attribute, to make composition easier. The // availability of for each attribute are defined by the font; the @@ -192,8 +190,6 @@ _UI_ENUM(uiAttribute) { // TODO kCJKRomanSpacingType -#endif - // TODO uiAttributeSystem, (this might not be doable with DirectWrite) // TODO uiAttributeCustom, }; From 3e941d008eee6261db5e32a2d167ff4d8538120c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 15 Feb 2017 23:10:23 -0500 Subject: [PATCH 0668/1329] Integrated aat.m into the build. --- common/opentype.c | 4 +- darwin/CMakeLists.txt | 1 + darwin/aat.m | 111 ++++++++++++++++++++--------------------- darwin/attrstr.m | 30 +++++++++++ darwin/uipriv_darwin.h | 4 ++ 5 files changed, 92 insertions(+), 58 deletions(-) diff --git a/common/opentype.c b/common/opentype.c index f874752b..4c5e2070 100644 --- a/common/opentype.c +++ b/common/opentype.c @@ -55,7 +55,7 @@ void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) case uiAttributeNumberSpacingProportional: (*f)("pnum", 1, data); break; - case uiAttributeNumberSpacingTitling: + case uiAttributeNumberSpacingTabular: (*f)("tnum", 1, data); break; } @@ -152,7 +152,7 @@ void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) case uiAttributeRubyKanaForms: boolspec(spec, "ruby", data); return; - case uiAttributeCJKRomanToitalics: + case uiAttributeCJKRomansToItalics: boolspec(spec, "ital", data); return; case uiAttributeCaseSensitiveForms: diff --git a/darwin/CMakeLists.txt b/darwin/CMakeLists.txt index 7d9ead75..96db9c09 100644 --- a/darwin/CMakeLists.txt +++ b/darwin/CMakeLists.txt @@ -1,6 +1,7 @@ # 3 june 2016 list(APPEND _LIBUI_SOURCES + darwin/aat.m darwin/alloc.m darwin/area.m darwin/areaevents.m diff --git a/darwin/aat.m b/darwin/aat.m index 05622f3c..9aab5f89 100644 --- a/darwin/aat.m +++ b/darwin/aat.m @@ -1,8 +1,6 @@ // 14 february 2017 #import "uipriv_darwin.h" -typedef void (*specToAATEnumFunc)(uint16_t type, uint16_t selector, void *data); - static void boolspec(uiAttributeSpec *spec, uint16_t type, uint16_t ifTrue, uint16_t ifFalse, specToAATEnumFunc f, void *data) { if (spec->Value != 0) { @@ -12,7 +10,7 @@ static void boolspec(uiAttributeSpec *spec, uint16_t type, uint16_t ifTrue, uint (*f)(type, ifFalse, data); } -void specToAAT(uiAttributeSpec *spec, specToAATEnumFunc f, void *data) +int specToAAT(uiAttributeSpec *spec, specToAATEnumFunc f, void *data) { switch (spec->Type) { case uiAttributeStandardLigatures: @@ -20,48 +18,48 @@ void specToAAT(uiAttributeSpec *spec, specToAATEnumFunc f, void *data) kCommonLigaturesOnSelector, kCommonLigaturesOffSelector, f, data); - return; + return 1; case uiAttributeRequiredLigatures: boolspec(spec, kLigaturesType, kRequiredLigaturesOnSelector, kRequiredLigaturesOffSelector, f, data); - return; + return 1; case uiAttributeDiscretionaryLigatures: boolspec(spec, kLigaturesType, kRareLigaturesOnSelector, kRareLigaturesOffSelector, f, data); - return; + return 1; case uiAttributeContextualLigatures: boolspec(spec, kLigaturesType, kContextualLigaturesOnSelector, kContextualLigaturesOffSelector, f, data); - return; + return 1; case uiAttributeHistoricalLigatures: boolspec(spec, kLigaturesType, kHistoricalLigaturesOnSelector, kHistoricalLigaturesOffSelector, f, data); - return; + return 1; case uiAttributeUnicase: // TODO is this correct, or should we provide an else case? if (spec->Value != 0) // this is undocumented; it comes from Core Text's internal AAT-to-OpenType conversion table (*f)(kLetterCaseType, 14, data); - return; + return 1; // TODO make an array? case uiAttributeNumberSpacings: switch (spec->Value) { case uiAttributeNumberSpacingProportional: (*f)(kNumberSpacingType, kProportionalNumbersSelector, data); break; - case uiAttributeNumberSpacingTitling: + case uiAttributeNumberSpacingTabular: (*f)(kNumberSpacingType, kMonospacedNumbersSelector, data); break; } - return; + return 1; // TODO make an array? case uiAttributeSuperscripts: switch (spec->Value) { @@ -81,7 +79,7 @@ void specToAAT(uiAttributeSpec *spec, specToAATEnumFunc f, void *data) (*f)(kVerticalPositionType, kScientificInferiorsSelector, data); break; } - return; + return 1; // TODO make an array? case uiAttributeFractionForms: switch (spec->Value) { @@ -95,30 +93,30 @@ void specToAAT(uiAttributeSpec *spec, specToAATEnumFunc f, void *data) (*f)(kFractionsType, kDiagonalFractionsSelector, data); break; } - return; + return 1; case uiAttributeSlashedZero: boolspec(spec, kTypographicExtrasType, kSlashedZeroOnSelector, kSlashedZeroOffSelector, f, data); - return; + return 1; case uiAttributeMathematicalGreek: boolspec(spec, kMathematicalExtrasType, kMathematicalGreekOnSelector, kMathematicalGreekOffSelector, f, data); - return; + return 1; case uiAttributeOrnamentalForms: (*f)(kOrnamentSetsType, (uint16_t) (spec->Value), data); - return; + return 1; case uiAttributeSpecificCharacterForm: (*f)(kCharacterAlternativesType, (uint16_t) (spec->Value), data); - return; + return 1; case uiAttributeTitlingCapitalForms: // TODO is this correct, or should we provide an else case? if (spec->Value != 0) (*f)(kStyleOptionsType, kTitlingCapsSelector, data); - return; + return 1; // TODO make an array? case uiAttributeHanCharacterForms: switch (spec->Value) { @@ -153,20 +151,20 @@ void specToAAT(uiAttributeSpec *spec, specToAATEnumFunc f, void *data) (*f)(kCharacterShapeType, kTraditionalNamesCharactersSelector, data); break; } - return; + return 1; case uiAttributeLowercaseNumbers: // TODO is this correct, or should we provide an else case? if (spec->Value != 0) (*f)(kNumberCaseType, kLowerCaseNumbersSelector, data); - return; + return 1; case uiAttributeHanjaToHangul: // TODO is this correct, or should we provide an else case? if (spec->Value != 0) (*f)(kTransliterationType, kHanjaToHangulSelector, data); - return; + return 1; case uiAttributeGlyphAnnotations: (*f)(kAnnotationType, (uint16_t) (spec->Value), data); - return; + return 1; case uiAttributeRubyKanaForms: // include this for completeness boolspec(spec, kRubyKanaType, @@ -178,8 +176,8 @@ void specToAAT(uiAttributeSpec *spec, specToAATEnumFunc f, void *data) kRubyKanaOnSelector, kRubyKanaOffSelector, f, data); - return; - case uiAttributeCJKRomanToitalics: + return 1; + case uiAttributeCJKRomansToItalics: // include this for completeness boolspec(spec, kItalicCJKRomanType, kCJKItalicRomanSelector, @@ -190,173 +188,173 @@ void specToAAT(uiAttributeSpec *spec, specToAATEnumFunc f, void *data) kCJKItalicRomanOnSelector, kCJKItalicRomanOffSelector, f, data); - return; + return 1; case uiAttributeCaseSensitiveForms: boolspec(spec, kCaseSensitiveLayoutType, kCaseSensitiveLayoutOnSelector, kCaseSensitiveLayoutOffSelector, f, data); - return; + return 1; case uiAttributeCapitalSpacing: boolspec(spec, kCaseSensitiveLayoutType, kCaseSensitiveSpacingOnSelector, kCaseSensitiveSpacingOffSelector, f, data); - return; + return 1; case uiAttributeAlternateHorizontalKana: boolspec(spec, kAlternateKanaType, kAlternateHorizKanaOnSelector, kAlternateHorizKanaOffSelector, f, data); - return; + return 1; case uiAttributeAlternateVerticalKana: boolspec(spec, kAlternateKanaType, kAlternateVertKanaOnSelector, kAlternateVertKanaOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative1: boolspec(spec, kStylisticAlternativesType, kStylisticAltOneOnSelector, kStylisticAltOneOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative2: boolspec(spec, kStylisticAlternativesType, kStylisticAltTwoOnSelector, kStylisticAltTwoOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative3: boolspec(spec, kStylisticAlternativesType, kStylisticAltThreeOnSelector, kStylisticAltThreeOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative4: boolspec(spec, kStylisticAlternativesType, kStylisticAltFourOnSelector, kStylisticAltFourOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative5: boolspec(spec, kStylisticAlternativesType, kStylisticAltFiveOnSelector, kStylisticAltFiveOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative6: boolspec(spec, kStylisticAlternativesType, kStylisticAltSixOnSelector, kStylisticAltSixOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative7: boolspec(spec, kStylisticAlternativesType, kStylisticAltSevenOnSelector, kStylisticAltSevenOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative8: boolspec(spec, kStylisticAlternativesType, kStylisticAltEightOnSelector, kStylisticAltEightOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative9: boolspec(spec, kStylisticAlternativesType, kStylisticAltNineOnSelector, kStylisticAltNineOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative10: boolspec(spec, kStylisticAlternativesType, kStylisticAltTenOnSelector, kStylisticAltTenOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative11: boolspec(spec, kStylisticAlternativesType, kStylisticAltElevenOnSelector, kStylisticAltElevenOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative12: boolspec(spec, kStylisticAlternativesType, kStylisticAltTwelveOnSelector, kStylisticAltTwelveOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative13: boolspec(spec, kStylisticAlternativesType, kStylisticAltThirteenOnSelector, kStylisticAltThirteenOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative14: boolspec(spec, kStylisticAlternativesType, kStylisticAltFourteenOnSelector, kStylisticAltFourteenOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative15: boolspec(spec, kStylisticAlternativesType, kStylisticAltFifteenOnSelector, kStylisticAltFifteenOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative16: boolspec(spec, kStylisticAlternativesType, kStylisticAltSixteenOnSelector, kStylisticAltSixteenOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative17: boolspec(spec, kStylisticAlternativesType, kStylisticAltSeventeenOnSelector, kStylisticAltSeventeenOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative18: boolspec(spec, kStylisticAlternativesType, kStylisticAltEighteenOnSelector, kStylisticAltEighteenOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative19: boolspec(spec, kStylisticAlternativesType, kStylisticAltNineteenOnSelector, kStylisticAltNineteenOffSelector, f, data); - return; + return 1; case uiAttributeStylisticAlternative20: boolspec(spec, kStylisticAlternativesType, kStylisticAltTwentyOnSelector, kStylisticAltTwentyOffSelector, f, data); - return; + return 1; case uiAttributeContextualAlternates: boolspec(spec, kContextualAlternatesType, kContextualAlternatesOnSelector, kContextualAlternatesOffSelector, f, data); - return; + return 1; case uiAttributeSwashes: boolspec(spec, kContextualAlternatesType, kSwashAlternatesOnSelector, kSwashAlternatesOffSelector, f, data); - return; + return 1; case uiAttributeContextualSwashes: boolspec(spec, kContextualAlternatesType, kContextualSwashAlternatesOnSelector, kContextualSwashAlternatesOffSelector, f, data); - return; + return 1; // TODO use arrays? case uiAttributeLowercaseCapForms: switch (spec->Value) { - case uiAttributeCapFormNone: + case uiAttributeCapFormNormal: (*f)(kLowerCaseType, kDefaultLowerCaseSelector, data); break; case uiAttributeCapFormSmallCaps: @@ -370,11 +368,11 @@ void specToAAT(uiAttributeSpec *spec, specToAATEnumFunc f, void *data) (*f)(kLowerCaseType, kLowerCasePetiteCapsSelector, data); break; } - return; + return 1; // TODO use arrays? case uiAttributeUppercaseCapForms: switch (spec->Value) { - case uiAttributeCapFormNone: + case uiAttributeCapFormNormal: (*f)(kUpperCaseType, kDefaultUpperCaseSelector, data); break; case uiAttributeCapFormSmallCaps: @@ -384,6 +382,7 @@ void specToAAT(uiAttributeSpec *spec, specToAATEnumFunc f, void *data) (*f)(kUpperCaseType, kUpperCasePetiteCapsSelector, data); break; } - return; + return 1; } + return 0; } diff --git a/darwin/attrstr.m b/darwin/attrstr.m index b46872ce..c7e227cf 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -139,6 +139,27 @@ static CGColorRef mkcolor(uiAttributeSpec *spec) return color; } +struct aatParam { + struct foreachParams *p; + size_t start; + size_t end; +}; + +static void doAAT(uint16_t type, uint16_t selector, void *data) +{ + struct aatParam *p = (struct aatParam *) data; + + ensureFontInRange(p->p, p->start, p->end); + adjustFontInRange(p->p, p->start, p->end, ^(struct fontParams *fp) { + fp->featureTypes[fp->nFeatures] = type; + fp->featureSelectors[fp->nFeatures] = selector; + fp->nFeatures++; + if (fp->nFeatures == maxFeatures) { + // TODO + } + }); +} + static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) { struct foreachParams *p = (struct foreachParams *) data; @@ -148,6 +169,7 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t backgroundBlock block; int32_t us; CFNumberRef num; + struct aatParam ap; ostart = start; oend = end; @@ -251,6 +273,14 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t }); break; // TODO + default: + // handle typographic features + ap.p = p; + ap.start = start; + ap.end = end; + // TODO check if unhandled and complain + specToAAT(spec, doAAT, &ap); + break; } return 0; } diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 9d662cd5..281a4b57 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -150,3 +150,7 @@ extern void initUnderlineColors(void); extern void uninitUnderlineColors(void); typedef void (^backgroundBlock)(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y); extern CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks); + +// aat.m +typedef void (*specToAATEnumFunc)(uint16_t type, uint16_t selector, void *data); +extern int specToAAT(uiAttributeSpec *spec, specToAATEnumFunc f, void *data); From dcc01f5b01401d454a851b363578141442bf6dda Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 15 Feb 2017 23:52:16 -0500 Subject: [PATCH 0669/1329] Started putting the typographical features attributes into the example program. --- examples/drawtext/attributes.c | 69 +++++++++++++++++++++++++++++++++- examples/drawtext/main.c | 7 ++++ 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index 9d0c2310..47601933 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -165,7 +165,74 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, ", "); - next = "multiple TODO"; + next = "or any combination of the above"; + // TODO + + uiAttributedStringAppendUnattributed(attrstr, ". In addition, a variety of typographical features are available (depending on the chosen font) that can be switched on (or off, if the font enables them by default): "); + + next = "fi"; + uiAttributedStringAppendUnattributed(attrstr, "standard ligatures like f+i ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeStandardLigatures; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + // note the use of RTL embeds to make sure the bidi algorithm doesn't kick in for our demonstration (it will produce incorrect results) + // see also: https://www.w3.org/International/articles/inline-bidi-markup/#nomarkup + next = "\xD9\x84\xD8\xA7"; + uiAttributedStringAppendUnattributed(attrstr, "required ligatures like \xE2\x80\xAB\xD9\x84\xE2\x80\xAC+\xE2\x80\xAB\xD8\xA7\xE2\x80\xAC (\xE2\x80\xAB"); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeRequiredLigatures; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, "\xE2\x80\xAC)"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "ct"; + uiAttributedStringAppendUnattributed(attrstr, "discretionary/rare ligatures like c+t ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeDiscretionaryLigatures; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "the"; + uiAttributedStringAppendUnattributed(attrstr, "contextual ligatures like h+e in the ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeContextualLigatures; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "\xC3\x9F"; + uiAttributedStringAppendUnattributed(attrstr, "historical ligatures like the decomposition of \xC3\x9F ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeHistoricalLigatures; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + // TODO unicase } static char fontFamily[] = "Times New Roman"; diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c index 848def89..22b532fe 100644 --- a/examples/drawtext/main.c +++ b/examples/drawtext/main.c @@ -5,6 +5,13 @@ // - occasional segfaults on startup // - very rare size attributes in the attributed string example don't terminate for a while, making everything big // - very very rare trace/bpt faults on startup +/* +objc[14827]: autorelease pool page 0x7feeab88b000 corrupted + magic 0xe000007f 0xeea9f2df 0x0000007f 0xeea9f2e0 + should be 0xa1a1a1a1 0x4f545541 0x454c4552 0x21455341 + pthread 0x0 + should be 0x7fff727a1000 +*/ static uiWindow *mainwin; static uiBox *box; From 4262d893bc4923eebb8541f28a20d72fcc5ba22b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 16 Feb 2017 00:13:09 -0500 Subject: [PATCH 0670/1329] Fixed the Arabic embeds that demonstrate required ligatures. --- examples/drawtext/attributes.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index 47601933..c7ecf880 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -182,10 +182,10 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, ", "); - // note the use of RTL embeds to make sure the bidi algorithm doesn't kick in for our demonstration (it will produce incorrect results) + // note the use of LTR marks and RTL embeds to make sure the bidi algorithm doesn't kick in for our demonstration (it will produce incorrect results) // see also: https://www.w3.org/International/articles/inline-bidi-markup/#nomarkup next = "\xD9\x84\xD8\xA7"; - uiAttributedStringAppendUnattributed(attrstr, "required ligatures like \xE2\x80\xAB\xD9\x84\xE2\x80\xAC+\xE2\x80\xAB\xD8\xA7\xE2\x80\xAC (\xE2\x80\xAB"); + uiAttributedStringAppendUnattributed(attrstr, "required ligatures like \xE2\x80\xAB\xD9\x84\xE2\x80\xAC+\xE2\x80\xAB\xD8\xA7\xE2\x80\xAC (\xE2\x80\x8E\xE2\x80\xAB"); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); From 768aba6614e7fa4a0916414689c6bf2afc9ae8fc Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 16 Feb 2017 03:05:29 -0500 Subject: [PATCH 0671/1329] Added test of Unicase. Had to use the Arial that comes with Windows 10, which seems to be the only font file I have out of **everything** that comes with Unicase. :S --- examples/drawtext/attributes.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index c7ecf880..51c6cbfa 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -232,7 +232,17 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, ", "); - // TODO unicase + next = "UnICasE wRITInG"; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeUnicase; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "TODO"; } static char fontFamily[] = "Times New Roman"; From 685e17fc0e73cc22c326a91c57d29d2e3b398d43 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 16 Feb 2017 12:49:06 -0500 Subject: [PATCH 0672/1329] More typographical features examples. This crashing thing is getting really annoying. --- examples/drawtext/attributes.c | 115 ++++++++++++++++++++++++++++++++- 1 file changed, 114 insertions(+), 1 deletion(-) diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index 51c6cbfa..8afbb2d7 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -232,6 +232,7 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, ", "); + // TODO a different word than "writing"? next = "UnICasE wRITInG"; start = uiAttributedStringLen(attrstr); end = start + strlen(next); @@ -242,7 +243,119 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, ", "); - next = "TODO"; + next = "316"; + uiAttributedStringAppendUnattributed(attrstr, "proportional ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeNumberSpacings; + spec.Value = uiAttributeNumberSpacingProportional; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ") and tabular/monospaced ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeNumberSpacings; + spec.Value = uiAttributeNumberSpacingTabular; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ") numbers"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "123"; + uiAttributedStringAppendUnattributed(attrstr, "superscipts ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeSuperscripts; + spec.Value = uiAttributeSuperscriptSuperscript; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "123"; + uiAttributedStringAppendUnattributed(attrstr, "subscripts ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeSuperscripts; + spec.Value = uiAttributeSuperscriptSubscript; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "1st"; + uiAttributedStringAppendUnattributed(attrstr, "ordinals ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeSuperscripts; + spec.Value = uiAttributeSuperscriptOrdinal; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "H2O"; + uiAttributedStringAppendUnattributed(attrstr, "scientific inferiors ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeSuperscripts; + spec.Value = uiAttributeSuperscriptScientificInferior; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "2/3"; + uiAttributedStringAppendUnattributed(attrstr, "fraction forms ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeFractionForms; + spec.Value = uiAttributeFractionFormNone; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ", "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeFractionForms; + spec.Value = uiAttributeFractionFormVertical; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ", "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeFractionForms; + spec.Value = uiAttributeFractionFormDiagonal; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "0"; + uiAttributedStringAppendUnattributed(attrstr, "slashed zeroes ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeSlashedZero; + spec.Value = 0; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " vs. "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeSlashedZero; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "TODO mathematical greek"; } static char fontFamily[] = "Times New Roman"; From 92b860c8f4091407ae02ac71a1df5ff5613f37d2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 16 Feb 2017 13:00:57 -0500 Subject: [PATCH 0673/1329] Added Address Sanitizer as a cmake option. This will help us figure out the crash. --- CMakeLists.txt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index eb1696d9..f4a01049 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -118,6 +118,18 @@ else() -static-libstdc++ ) endif() + + # TODO document this + if(ADDRESS_SANITIZER) + set(_COMMON_CFLAGS ${_COMMON_CFLAGS} + -fsanitize=address + -fno-omit-frame-pointer + ) + set(_COMMON_LDFLAGS ${_COMMON_LDFLAGS} + -fsanitize=address + -fno-omit-frame-pointer + ) + endif() endif() # problem: From e27e51c4b889e4ec536a1d091e27c4b77b083a26 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 16 Feb 2017 14:25:04 -0500 Subject: [PATCH 0674/1329] Seemed to fix crashing issues for now. Character insertion is borked :| --- common/attrstr.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/common/attrstr.c b/common/attrstr.c index 9c12a532..c5466edf 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -93,6 +93,7 @@ static void u8u16len(const char *str, size_t *n8, size_t *n16) *n16 = 0; while (*str) { str = utf8DecodeRune(str, 0, &rune); + // TODO document the use of the function vs a pointer subtract here *n8 += utf8EncodeRune(rune, buf); *n16 += utf16EncodeRune(rune, buf16); } @@ -123,6 +124,7 @@ void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *s uint16_t buf16[2]; size_t n8, n16; // TODO make loop-local? to avoid using them in the wrong place again size_t old, old16; + size_t oldn8, oldn16; size_t oldlen, old16len; size_t at16; size_t i; @@ -168,6 +170,8 @@ void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *s s->u16tou8 + at16 + n16, s->u16tou8 + at16, (old16len - at16 + 1) * sizeof (size_t)); + oldn8 = n8; + oldn16 = n16; // and copy while (*str) { @@ -201,15 +205,15 @@ void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *s } // and have an index for the end of the string // TODO is this done by the below? - s->u8tou16[old] = old16; - s->u16tou8[old16] = old; +//TODO s->u8tou16[old] = old16; +//TODO s->u16tou8[old16] = old; // and adjust the prior values in the conversion tables // use <= so the terminating 0 gets updated too for (i = 0; i <= oldlen - at; i++) - s->u8tou16[at + oldlen + i] += old16len; + s->u8tou16[at + oldn8 + i] += s->u16len - old16len; for (i = 0; i <= old16len - at16; i++) - s->u16tou8[at16 + old16len + i] += oldlen; + s->u16tou8[at16 + oldn16 + i] += s->len - oldlen; // and finally do the attributes attrlistInsertCharactersUnattributed(s->attrs, at, n8); From 025dd16d7600b37d0ecf4ec1f14871c38ecaeecc Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 16 Feb 2017 15:02:19 -0500 Subject: [PATCH 0675/1329] More examples and crash fixes. --- darwin/fontmatch.m | 93 +++++++++++++++++----------------- examples/drawtext/attributes.c | 56 +++++++++++++++++++- 2 files changed, 102 insertions(+), 47 deletions(-) diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index 8d5094a7..9acb2edf 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -298,56 +298,57 @@ CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uint1 } // now we have to take care of the language - // TODO can we assume this is present? if (language != NULL) { languages = CTFontDescriptorCopyAttribute(desc, kCTFontLanguagesAttribute); - nl = CFArrayGetCount(languages); - d[0] = language[0]; - if (d[0] >= 'A' && d[0] <= 'Z') - d[0] += 'a' - 'A'; - d[1] = language[1]; - if (d[1] >= 'A' && d[1] <= 'Z') - d[1] += 'a' - 'A'; - for (il = 0; il < nl; il++) { - char c[2]; - - curlang = (CFStringRef) CFArrayGetValueAtIndex(languages, il); - // TODO check for failure - CFStringGetBytes(curlang, CFRangeMake(0, 2), - kCFStringEncodingUTF8, 0, false, - (UInt8 *) c, 2, NULL); - if (c[0] >= 'A' && c[0] <= 'Z') - c[0] += 'a' - 'A'; - if (c[1] >= 'A' && c[1] <= 'Z') - c[1] += 'a' - 'A'; - if (c[0] == d[0] && c[1] == d[1]) - break; - } - if (il != nl) { - uint16_t typ; - - typ = kLanguageTagType; - il++; - numType = CFNumberCreate(NULL, kCFNumberSInt16Type, - (const SInt16 *) (&typ)); - numSelector = CFNumberCreate(NULL, kCFNumberCFIndexType, - &il); - values[0] = numType; - values[1] = numSelector; - innerDict = CFDictionaryCreate(NULL, - keys, values, 2, - // TODO are these correct? - &kCFCopyStringDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - if (innerDict == NULL) { - // TODO + if (languages != NULL) { + nl = CFArrayGetCount(languages); + d[0] = language[0]; + if (d[0] >= 'A' && d[0] <= 'Z') + d[0] += 'a' - 'A'; + d[1] = language[1]; + if (d[1] >= 'A' && d[1] <= 'Z') + d[1] += 'a' - 'A'; + for (il = 0; il < nl; il++) { + char c[2]; + + curlang = (CFStringRef) CFArrayGetValueAtIndex(languages, il); + // TODO check for failure + CFStringGetBytes(curlang, CFRangeMake(0, 2), + kCFStringEncodingUTF8, 0, false, + (UInt8 *) c, 2, NULL); + if (c[0] >= 'A' && c[0] <= 'Z') + c[0] += 'a' - 'A'; + if (c[1] >= 'A' && c[1] <= 'Z') + c[1] += 'a' - 'A'; + if (c[0] == d[0] && c[1] == d[1]) + break; } - CFArrayAppendValue(outerArray, innerDict); - CFRelease(innerDict); - CFRelease(numSelector); - CFRelease(numType); + if (il != nl) { + uint16_t typ; + + typ = kLanguageTagType; + il++; + numType = CFNumberCreate(NULL, kCFNumberSInt16Type, + (const SInt16 *) (&typ)); + numSelector = CFNumberCreate(NULL, kCFNumberCFIndexType, + &il); + values[0] = numType; + values[1] = numSelector; + innerDict = CFDictionaryCreate(NULL, + keys, values, 2, + // TODO are these correct? + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (innerDict == NULL) { + // TODO + } + CFArrayAppendValue(outerArray, innerDict); + CFRelease(innerDict); + CFRelease(numSelector); + CFRelease(numType); + } + CFRelease(languages); } - CFRelease(languages); } keys[0] = kCTFontFeatureSettingsAttribute; diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index 8afbb2d7..625991c1 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -8,6 +8,7 @@ static void setupAttributedString(void) uiAttributeSpec spec; size_t start, end; const char *next; + int i; attrstr = uiNewAttributedString("uiAttributedString isn't just for plain text! It supports "); @@ -355,7 +356,60 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, ", "); - next = "TODO mathematical greek"; + next = "\xCE\xA0\xCE\xA3"; + uiAttributedStringAppendUnattributed(attrstr, "mathematical greek ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeMathematicalGreek; + spec.Value = 0; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " vs. "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeMathematicalGreek; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "qwertyuiop\xE2\x80\xA2"; + uiAttributedStringAppendUnattributed(attrstr, "ornamental forms ("); + for (i = 1; i < 11; i++) { + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeOrnamentalForms; + spec.Value = (uintptr_t) i; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + next = "\xE2\x80\xA2"; + } + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "g"; + uiAttributedStringAppendUnattributed(attrstr, "specific forms/alternates ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeSpecificCharacterForm; + spec.Value = 0; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " vs. "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeSpecificCharacterForm; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "TODO titling capitals"; } static char fontFamily[] = "Times New Roman"; From fbd294c089ce147d64a862250e3a939c6dd887d8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 20 Feb 2017 12:38:39 -0500 Subject: [PATCH 0676/1329] Filled in the rest of the typographic features. Now to write the any combination example. --- examples/drawtext/attributes.c | 325 ++++++++++++++++++++++++++++++++- ui_attrstr.h | 1 + 2 files changed, 324 insertions(+), 2 deletions(-) diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index 625991c1..ef77a29d 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -3,6 +3,7 @@ static uiAttributedString *attrstr; +// some of these examples come from Microsoft's and Apple's lists of typographic features and also https://www.fontfont.com/staticcontent/downloads/FF_OT_User_Guide.pdf static void setupAttributedString(void) { uiAttributeSpec spec; @@ -167,7 +168,7 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, ", "); next = "or any combination of the above"; - // TODO + // TODOTODO uiAttributedStringAppendUnattributed(attrstr, ". In addition, a variety of typographical features are available (depending on the chosen font) that can be switched on (or off, if the font enables them by default): "); @@ -409,7 +410,327 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, ", "); - next = "TODO titling capitals"; + next = "ABCDEFGQWERTY"; + uiAttributedStringAppendUnattributed(attrstr, "titling capital forms ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeTitlingCapitalForms; + spec.Value = 0; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " vs. "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeTitlingCapitalForms; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "\xE7\x80\x86"; + uiAttributedStringAppendUnattributed(attrstr, "alternate Han character forms ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeHanCharacterForms; + spec.Value = uiAttributeHanCharacterFormJIS1978; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " vs. "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeHanCharacterForms; + spec.Value = uiAttributeHanCharacterFormJIS1983; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "0123456789"; + uiAttributedStringAppendUnattributed(attrstr, "lowercase numbers ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeLowercaseNumbers; + spec.Value = 0; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " vs. "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeLowercaseNumbers; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "\xE4\xBC\xBD"; + uiAttributedStringAppendUnattributed(attrstr, "hanja to hangul translation ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeHanjaToHangul; + spec.Value = 0; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " vs. "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeHanjaToHangul; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "\xE3\x81\x82"; + uiAttributedStringAppendUnattributed(attrstr, "annotated glyph forms ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeGlyphAnnotations; + spec.Value = 0; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " vs. "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeGlyphAnnotations; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " vs. "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeGlyphAnnotations; + spec.Value = 4; // AAT inverted circle + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "\xE3\x81\x82"; + uiAttributedStringAppendUnattributed(attrstr, "ruby forms of kana ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeRubyKanaForms; + spec.Value = 0; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " vs. "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeRubyKanaForms; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "now is the time"; + uiAttributedStringAppendUnattributed(attrstr, "italic forms of Latin letters in CJK fonts ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeCJKRomansToItalics; + spec.Value = 0; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " vs. "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeCJKRomansToItalics; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "{I} > {J}"; + uiAttributedStringAppendUnattributed(attrstr, "case-sensitive character forms, such as punctuation ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeCaseSensitiveForms; + spec.Value = 0; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " vs. "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeCaseSensitiveForms; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "ABC"; + uiAttributedStringAppendUnattributed(attrstr, "specialized spacing between uppercase letters ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeCapitalSpacing; + spec.Value = 0; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " vs. "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeCapitalSpacing; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "\xE3\x82\xB9\xE3\x83\x98\xE3\x83\x88"; + uiAttributedStringAppendUnattributed(attrstr, "alternate horizontal ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeAlternateHorizontalKana; + spec.Value = 0; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " vs. "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeAlternateHorizontalKana; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ") and vertical ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeAlternateVerticalKana; + spec.Value = 0; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " vs. "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeAlternateVerticalKana; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ") kana forms"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "g"; + uiAttributedStringAppendUnattributed(attrstr, "stylistic alternates ("); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeStylisticAlternative1; + spec.Value = 1; + for (i = 0; i < 20; i++) { + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type++; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + } + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "first"; + uiAttributedStringAppendUnattributed(attrstr, "contextual alternates ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeContextualAlternates; + spec.Value = 0; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " vs. "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeContextualAlternates; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "FONT"; + uiAttributedStringAppendUnattributed(attrstr, "swashes ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeSwashes; + spec.Value = 0; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " vs. "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeSwashes; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "Font"; + uiAttributedStringAppendUnattributed(attrstr, "contextual swashes ("); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeContextualSwashes; + spec.Value = 0; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " vs. "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeContextualSwashes; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "Small Caps"; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeLowercaseCapForms; + spec.Value = uiAttributeCapFormSmallCaps; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ", "); + next = "Petite Caps"; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeLowercaseCapForms; + spec.Value = uiAttributeCapFormPetiteCaps; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + + uiAttributedStringAppendUnattributed(attrstr, ", "); + + next = "SMALL UPPERCASES"; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeUppercaseCapForms; + spec.Value = uiAttributeCapFormSmallCaps; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ", and "); + next = "PETITE UPPERCASES"; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeUppercaseCapForms; + spec.Value = uiAttributeCapFormPetiteCaps; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + + uiAttributedStringAppendUnattributed(attrstr, "."); + + // TODO write a dedicated example for experimenting with typographic features like the one in gtk3-demo } static char fontFamily[] = "Times New Roman"; diff --git a/ui_attrstr.h b/ui_attrstr.h index 31ac8f6b..5e4fe8a8 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -160,6 +160,7 @@ _UI_ENUM(uiAttribute) { uiAttributeAlternateVerticalKana, // 0 = off, 1 = on // TODO "Alternate"? unify all this + // TODO document that these are guaranteed to be consecutive uiAttributeStylisticAlternative1, // 0 = off, 1 = on uiAttributeStylisticAlternative2, // 0 = off, 1 = on uiAttributeStylisticAlternative3, // 0 = off, 1 = on From f65d4592d0507597c65f9620d83a8c744bea869f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 20 Feb 2017 12:49:20 -0500 Subject: [PATCH 0677/1329] Combined attributes. We're good I guess. --- examples/drawtext/attributes.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index ef77a29d..c0a4e5e9 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -167,8 +167,36 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, ", "); + // TODO randomize these ranges better + // TODO also change colors to light foreground dark background next = "or any combination of the above"; - // TODOTODO + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeWeight; + spec.Value = (uintptr_t) uiDrawTextWeightBold; + uiAttributedStringSetAttribute(attrstr, &spec, start, end - 8); + spec.Type = uiAttributeItalic; + spec.Value = (uintptr_t) uiDrawTextItalicItalic; + uiAttributedStringSetAttribute(attrstr, &spec, start + 3, end - 4); + spec.Type = uiAttributeColor; + spec.R = 0.8627450980392156; + spec.G = 0.0784313725490196; + spec.B = 0.2352941176470588; + spec.A = 0.75; + uiAttributedStringSetAttribute(attrstr, &spec, start + 12, end); + spec.Type = uiAttributeFamily; + spec.Value = (uintptr_t) "Helvetica"; + uiAttributedStringSetAttribute(attrstr, &spec, start + 8, end - 1); + spec.Type = uiAttributeBackground; + spec.R = 1.0; + spec.G = 0.85490196078431372; + spec.B = 0.7254901960784313; + spec.A = 0.5; + uiAttributedStringSetAttribute(attrstr, &spec, start + 5, end - 7); + spec.Type = uiAttributeUnderline; + spec.Value = uiDrawUnderlineStyleSingle; + uiAttributedStringSetAttribute(attrstr, &spec, start + 9, end - 1); uiAttributedStringAppendUnattributed(attrstr, ". In addition, a variety of typographical features are available (depending on the chosen font) that can be switched on (or off, if the font enables them by default): "); From 7920559aa5a3279f16a5ecf2510ca8622a8dcf05 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 20 Feb 2017 12:50:00 -0500 Subject: [PATCH 0678/1329] More TODOs. --- examples/drawtext/attributes.c | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index c0a4e5e9..f0868a8b 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -168,6 +168,7 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, ", "); // TODO randomize these ranges better + // TODO make some overlap to test that // TODO also change colors to light foreground dark background next = "or any combination of the above"; start = uiAttributedStringLen(attrstr); From 6ae6e91238757d222fd120d77b5c200e2942351d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 20 Feb 2017 14:59:43 -0500 Subject: [PATCH 0679/1329] Integrated opentype.c into the build. Finally. --- common/CMakeLists.txt | 1 + common/opentype.c | 70 +++++++++++++++++++++---------------------- 2 files changed, 36 insertions(+), 35 deletions(-) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 8ecff8b3..c0542c82 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -8,6 +8,7 @@ list(APPEND _LIBUI_SOURCES common/debug.c common/drawtext.c common/matrix.c + common/opentype.c common/shouldquit.c common/userbugs.c common/utf.c diff --git a/common/opentype.c b/common/opentype.c index 4c5e2070..aa0a389d 100644 --- a/common/opentype.c +++ b/common/opentype.c @@ -88,10 +88,10 @@ void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) } return; case uiAttributeSlashedZero: - boolspec(spec, "zero", data); + boolspec(spec, "zero", f, data); return; case uiAttributeMathematicalGreek: - boolspec(spec, "mgrk", data); + boolspec(spec, "mgrk", f, data); return; case uiAttributeOrnamentalForms: (*f)("ornm", (uint32_t) (spec->Value), data); @@ -100,7 +100,7 @@ void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) (*f)("aalt", (uint32_t) (spec->Value), data); return; case uiAttributeTitlingCapitalForms: - boolspec(spec, "titl", data); + boolspec(spec, "titl", f, data); return; // TODO is this correct or should we explicitly switch the rest off too? case uiAttributeHanCharacterForms: @@ -138,103 +138,103 @@ void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) } return; case uiAttributeLowercaseNumbers: - boolspec(spec, "onum", data); + boolspec(spec, "onum", f, data); // Core Text's internal AAT-to-OpenType mapping says to include this, so we include it too // TODO is it always set? - boolspecnot(spec, "lnum", data); + boolspecnot(spec, "lnum", f, data); return; case uiAttributeHanjaToHangul: - boolspec(spec, "hngl", data); + boolspec(spec, "hngl", f, data); return; case uiAttributeGlyphAnnotations: (*f)("nalt", (uint32_t) (spec->Value), data); return; case uiAttributeRubyKanaForms: - boolspec(spec, "ruby", data); + boolspec(spec, "ruby", f, data); return; case uiAttributeCJKRomansToItalics: - boolspec(spec, "ital", data); + boolspec(spec, "ital", f, data); return; case uiAttributeCaseSensitiveForms: - boolspec(spec, "case", data); + boolspec(spec, "case", f, data); return; case uiAttributeCapitalSpacing: - boolspec(spec, "cpsp", data); + boolspec(spec, "cpsp", f, data); return; case uiAttributeAlternateHorizontalKana: - boolspec(spec, "hkna", data); + boolspec(spec, "hkna", f, data); return; case uiAttributeAlternateVerticalKana: - boolspec(spec, "vkna", data); + boolspec(spec, "vkna", f, data); return; case uiAttributeStylisticAlternative1: - boolspec(spec, "ss01", data); + boolspec(spec, "ss01", f, data); return; case uiAttributeStylisticAlternative2: - boolspec(spec, "ss02", data); + boolspec(spec, "ss02", f, data); return; case uiAttributeStylisticAlternative3: - boolspec(spec, "ss03", data); + boolspec(spec, "ss03", f, data); return; case uiAttributeStylisticAlternative4: - boolspec(spec, "ss04", data); + boolspec(spec, "ss04", f, data); return; case uiAttributeStylisticAlternative5: - boolspec(spec, "ss05", data); + boolspec(spec, "ss05", f, data); return; case uiAttributeStylisticAlternative6: - boolspec(spec, "ss06", data); + boolspec(spec, "ss06", f, data); return; case uiAttributeStylisticAlternative7: - boolspec(spec, "ss07", data); + boolspec(spec, "ss07", f, data); return; case uiAttributeStylisticAlternative8: - boolspec(spec, "ss08", data); + boolspec(spec, "ss08", f, data); return; case uiAttributeStylisticAlternative9: - boolspec(spec, "ss09", data); + boolspec(spec, "ss09", f, data); return; case uiAttributeStylisticAlternative10: - boolspec(spec, "ss10", data); + boolspec(spec, "ss10", f, data); return; case uiAttributeStylisticAlternative11: - boolspec(spec, "ss11", data); + boolspec(spec, "ss11", f, data); return; case uiAttributeStylisticAlternative12: - boolspec(spec, "ss12", data); + boolspec(spec, "ss12", f, data); return; case uiAttributeStylisticAlternative13: - boolspec(spec, "ss13", data); + boolspec(spec, "ss13", f, data); return; case uiAttributeStylisticAlternative14: - boolspec(spec, "ss14", data); + boolspec(spec, "ss14", f, data); return; case uiAttributeStylisticAlternative15: - boolspec(spec, "ss15", data); + boolspec(spec, "ss15", f, data); return; case uiAttributeStylisticAlternative16: - boolspec(spec, "ss16", data); + boolspec(spec, "ss16", f, data); return; case uiAttributeStylisticAlternative17: - boolspec(spec, "ss17", data); + boolspec(spec, "ss17", f, data); return; case uiAttributeStylisticAlternative18: - boolspec(spec, "ss18", data); + boolspec(spec, "ss18", f, data); return; case uiAttributeStylisticAlternative19: - boolspec(spec, "ss19", data); + boolspec(spec, "ss19", f, data); return; case uiAttributeStylisticAlternative20: - boolspec(spec, "ss20", data); + boolspec(spec, "ss20", f, data); return; case uiAttributeContextualAlternates: - boolspec(spec, "calt", data); + boolspec(spec, "calt", f, data); return; case uiAttributeSwashes: - boolspec(spec, "swsh", data); + boolspec(spec, "swsh", f, data); return; case uiAttributeContextualSwashes: - boolspec(spec, "cswh", data); + boolspec(spec, "cswh", f, data); return; // TODO is this correct or should we explicitly switch the rest off too? case uiAttributeLowercaseCapForms: From ea473a341187c71fbe2685f7e6009be041f7c5f6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 20 Feb 2017 15:14:53 -0500 Subject: [PATCH 0680/1329] Ugh of course I screwed up the malloc() test. Fixed a crash on GTK+ since I guess OS X malloc() autofills to 0? --- darwin/alloc.m | 2 +- unix/alloc.c | 2 +- windows/alloc.cpp | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/darwin/alloc.m b/darwin/alloc.m index e271b90e..0bbb8996 100644 --- a/darwin/alloc.m +++ b/darwin/alloc.m @@ -71,7 +71,7 @@ void *uiRealloc(void *p, size_t new, const char *type) abort(); } s = SIZE(out); - if (new <= *s) + if (new > *s) memset(((uint8_t *) DATA(out)) + *s, 0, new - *s); *s = new; [allocations removeObject:[NSValue valueWithPointer:p]]; diff --git a/unix/alloc.c b/unix/alloc.c index 2561efa6..0291bd49 100644 --- a/unix/alloc.c +++ b/unix/alloc.c @@ -64,7 +64,7 @@ void *uiRealloc(void *p, size_t new, const char *type) p = BASE(p); out = g_realloc(p, EXTRA + new); s = SIZE(out); - if (new <= *s) + if (new > *s) memset(((uint8_t *) DATA(out)) + *s, 0, new - *s); *s = new; if (g_ptr_array_remove(allocations, p) == FALSE) diff --git a/windows/alloc.cpp b/windows/alloc.cpp index eeee3ad4..23201f75 100644 --- a/windows/alloc.cpp +++ b/windows/alloc.cpp @@ -45,6 +45,7 @@ void *uiRealloc(void *_p, size_t size, const char *type) if (p == NULL) return uiAlloc(size, type); arr = heap[p]; + // TODO does this fill in? arr->resize(size, 0); heap.erase(p); heap[rawBytes(arr)] = arr; From d7a44a51688936ba7e6a8b08ee33b5de003d9952 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 20 Feb 2017 17:11:52 -0500 Subject: [PATCH 0681/1329] Added uiAttribute handling code to the GTK+ backend. Not finished yet; not used yet. --- common/opentype.c | 2 - common/uipriv.h | 4 + unix/CMakeLists.txt | 1 + unix/attrstr.c | 254 ++++++++++++++++++++++++++++++++++++++++++++ unix/future.c | 9 ++ unix/uipriv_unix.h | 5 + 6 files changed, 273 insertions(+), 2 deletions(-) create mode 100644 unix/attrstr.c diff --git a/common/opentype.c b/common/opentype.c index aa0a389d..a3e4f852 100644 --- a/common/opentype.c +++ b/common/opentype.c @@ -5,8 +5,6 @@ // Notes: // - Each tag should only appear in quotes once (including within comments); this allows automated tools to determine what we cover and don't cover -typedef void (*specToOpenTypeEnumFunc)(const char *featureTag, uint32_t param, void *data); - static void boolspec(uiAttributeSpec *spec, const char *featureTag, specToOpenTypeEnumFunc f, void *data) { if (spec->Value != 0) { diff --git a/common/uipriv.h b/common/uipriv.h index 553073f5..1c61f9c7 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -101,6 +101,10 @@ struct caretDrawParams { extern void caretDrawParams(uiDrawContext *c, double height, struct caretDrawParams *p); extern void drawTextBackground(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t start, size_t end, uiDrawBrush *brush, int isSelection); +// opentype.c +typedef void (*specToOpenTypeEnumFunc)(const char *featureTag, uint32_t param, void *data); +extern void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data); + #ifdef __cplusplus } #endif diff --git a/unix/CMakeLists.txt b/unix/CMakeLists.txt index 9300bcb7..949c4a11 100644 --- a/unix/CMakeLists.txt +++ b/unix/CMakeLists.txt @@ -6,6 +6,7 @@ pkg_check_modules(GTK REQUIRED gtk+-3.0) list(APPEND _LIBUI_SOURCES unix/alloc.c unix/area.c + unix/attrstr.c unix/box.c unix/button.c unix/cellrendererbutton.c diff --git a/unix/attrstr.c b/unix/attrstr.c new file mode 100644 index 00000000..12cbb12d --- /dev/null +++ b/unix/attrstr.c @@ -0,0 +1,254 @@ +// 12 february 2017 +#include "uipriv_unix.h" + +// we need to collect all the OpenType features and background blocks and add them all at once +// TODO rename this struct to something that isn't exclusively foreach-ing? +struct foreachParams { + PangoAttrList *attrs; + // keys are pointers to size_t maintained by g_new0()/g_free() + // values are GStrings + GHashTable *features; +//TODO GArray *backgroundBlocks; +}; + +static gboolean featurePosEqual(gconstpointer a, gconstpointer b) +{ + size_t *sa = (size_t *) a; + size_t *sb = (size_t *) b; + + return *sa == *sb; +} + +static guint featurePosHash(gconstpointer n) +{ + size_t *sn = (size_t *) n; + + return (guint) (*sn); +} + +static void freeFeatureString(gpointer s) +{ + g_string_free((GString *) s, TRUE); +} + +static void ensureFeaturesInRange(struct foreachParams *p, size_t start, size_t end) +{ + size_t i; + size_t *key; + GString *new; + + for (i = start; i < end; i++) { + new = (GString *) g_hash_table_lookup(p->features, &i); + if (new != NULL) + continue; + new = g_string_new(""); + key = g_new0(size_t, 1); + *key = i; + g_hash_table_replace(p->features, key, new); + } +} + +#if 0 /* TODO */ +static backgroundBlock mkBackgroundBlock(size_t start, size_t end, double r, double g, double b, double a) +{ + return Block_copy(^(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y) { + uiDrawBrush brush; + + brush.Type = uiDrawBrushTypeSolid; + brush.R = r; + brush.G = g; + brush.B = b; + brush.A = a; + drawTextBackground(c, x, y, layout, start, end, &brush, 0); + }); +} +#endif + +struct otParam { + struct foreachParams *p; + size_t start; + size_t end; +}; + +// see https://developer.mozilla.org/en/docs/Web/CSS/font-feature-settings +static void doOpenType(const char *featureTag, uint32_t param, void *data) +{ + struct otParam *p = (struct otParam *) data; + size_t i; + GString *s; + + ensureFeaturesInRange(p->p, p->start, p->end); + for (i = p->start; i < p->end; i++) { + s = (GString *) g_hash_table_lookup(p->p->features, &i); + g_string_append_printf(s, "\"%s\" %" PRIu32 ", ", + featureTag, param); + } +} + +static void addattr(struct foreachParams *p, size_t start, size_t end, PangoAttribute *attr) +{ + if (attr == NULL) // in case of a future attribute + return; + attr->start_index = start; + attr->end_index = end; + pango_attr_list_insert(p->attrs, attr); +} + +static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) +{ + struct foreachParams *p = (struct foreachParams *) data; +//TODO backgroundBlock block; + PangoGravity gravity; + PangoUnderline underline; + PangoLanguage *lang; + struct otParam op; + + switch (spec->Type) { + case uiAttributeFamily: + addattr(p, start, end, + pango_attr_family_new((const char *) (spec->Value))); + break; +#if 0 /* TODO */ + case uiAttributeSize: + addattr(p, start, end, + pango_attr_size_new(cairoToPango(spec->Double))); + break; + case uiAttributeWeight: + ensureFontInRange(p, start, end); + adjustFontInRange(p, start, end, ^(struct fontParams *fp) { + fp->desc.Weight = (uiDrawTextWeight) (spec->Value); + }); + break; + case uiAttributeItalic: + ensureFontInRange(p, start, end); + adjustFontInRange(p, start, end, ^(struct fontParams *fp) { + fp->desc.Italic = (uiDrawTextItalic) (spec->Value); + }); + break; + case uiAttributeStretch: + ensureFontInRange(p, start, end); + adjustFontInRange(p, start, end, ^(struct fontParams *fp) { + fp->desc.Stretch = (uiDrawTextStretch) (spec->Value); + }); + break; +#endif + case uiAttributeColor: + addattr(p, start, end, + pango_attr_foreground_new( + (guint16) (spec->R * 65535.0), + (guint16) (spec->G * 65535.0), + (guint16) (spec->B * 65535.0))); + addattr(p, start, end, + FUTURE_pango_attr_foreground_alpha_new( + (guint16) (spec->A * 65535.0))); + break; +#if 0 /* TODO */ + case uiAttributeBackground: + block = mkBackgroundBlock(ostart, oend, + spec->R, spec->G, spec->B, spec->A); + [p->backgroundBlocks addObject:block]; + Block_release(block); + break; +#endif + case uiAttributeVerticalForms: + gravity = PANGO_GRAVITY_SOUTH; + if (spec->Value != 0) + gravity = PANGO_GRAVITY_EAST; + addattr(p, start, end, + pango_attr_gravity_new(gravity)); + break; + case uiAttributeUnderline: + switch (spec->Value) { + case uiDrawUnderlineStyleNone: + underline = PANGO_UNDERLINE_NONE; + break; + case uiDrawUnderlineStyleSingle: + underline = PANGO_UNDERLINE_SINGLE; + break; + case uiDrawUnderlineStyleDouble: + underline = PANGO_UNDERLINE_DOUBLE; + break; + case uiDrawUnderlineStyleSuggestion: + underline = PANGO_UNDERLINE_ERROR; + break; + } + addattr(p, start, end, + pango_attr_underline_new(underline)); + break; + case uiAttributeUnderlineColor: + switch (spec->Value) { + case uiDrawUnderlineColorCustom: + addattr(p, start, end, + pango_attr_underline_color_new( + (guint16) (spec->R * 65535.0), + (guint16) (spec->G * 65535.0), + (guint16) (spec->B * 65535.0))); + break; + case uiDrawUnderlineColorSpelling: + // TODO GtkTextView style property error-underline-color + addattr(p, start, end, + pango_attr_underline_color_new(65535, 0, 0)); + break; + case uiDrawUnderlineColorGrammar: + // TODO find a more appropriate color + addattr(p, start, end, + pango_attr_underline_color_new(0, 65535, 0)); + break; + case uiDrawUnderlineColorAuxiliary: + // TODO find a more appropriate color + addattr(p, start, end, + pango_attr_underline_color_new(0, 0, 65535)); + break; + } + break; + // language strings are specified as BCP 47: https://developer.gnome.org/pango/1.30/pango-Scripts-and-Languages.html#pango-language-from-string https://www.ietf.org/rfc/rfc3066.txt + case uiAttributeLanguage: + lang = pango_language_from_string((const char *) (spec->Value)); + addattr(p, start, end, + pango_attr_language_new(lang)); + // lang *cannot* be freed + break; + // TODO + default: + // handle typographic features + op.p = p; + op.start = start; + op.end = end; + // TODO check if unhandled and complain + specToOpenType(spec, doOpenType, &op); + break; + } + return 0; +} + +static gboolean applyFeatures(gpointer key, gpointer value, gpointer data) +{ + struct foreachParams *p = (struct foreachParams *) data; + size_t *pos = (size_t *) key; + GString *s = (GString *) value; + + // remove the trailing comma/space + g_string_truncate(s, s->len - 2); + addattr(p, *pos, 1, + FUTURE_pango_attr_font_features_new(s->str)); + return TRUE; // always delete; we're emptying the map +} + +static void applyAndFreeFeatureAttributes(struct foreachParams *p) +{ + g_hash_table_foreach_remove(p->features, applyFeatures, p); + g_hash_table_destroy(p->features); +} + +PangoAttrList *attrstrToPangoAttrList(uiDrawTextLayoutParams *p/*TODO, NSArray **backgroundBlocks*/) +{ + struct foreachParams fep; + + fep.attrs = pango_attr_list_new(); + fep.features = g_hash_table_new_full( + featurePosHash, featurePosEqual, + g_free, freeFeatureString); + uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); + applyAndFreeFeatureAttributes(&fep); + return fep.attrs; +} diff --git a/unix/future.c b/unix/future.c index 1f9f532b..3d63e9e0 100644 --- a/unix/future.c +++ b/unix/future.c @@ -6,6 +6,7 @@ // in others, because parts of GTK+ being unstable until recently also sucks :/ // added in pango 1.38; we need 1.36 +static PangoAttribute *(*newFeaturesAttr)(const gchar *features) = NULL; static PangoAttribute *(*newFGAlphaAttr)(guint16 alpha) = NULL; // added in GTK+ 3.20; we need 3.10 @@ -21,11 +22,19 @@ void loadFutures(void) if (handle == NULL) return; #define GET(var, fn) *((void **) (&var)) = dlsym(handle, #fn) + GET(newFeaturesAttr, pango_attr_font_features_new); GET(newFGAlphaAttr, pango_attr_foreground_alpha_new); GET(gwpIterSetObjectName, gtk_widget_path_iter_set_object_name); dlclose(handle); } +PangoAttribute *FUTURE_pango_attr_font_features_new(const gchar *features) +{ + if (newFeaturesAttr == NULL) + return NULL; + return (*newFeaturesAttr)(features); +} + PangoAttribute *FUTURE_pango_attr_foreground_alpha_new(guint16 alpha) { if (newFGAlphaAttr == NULL) diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index aaae8b34..d2ef5bf0 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -9,6 +9,7 @@ #include #include #include +#include #include "../ui.h" #include "../ui_unix.h" #include "../common/uipriv.h" @@ -54,8 +55,12 @@ extern GtkCellRenderer *newCellRendererButton(void); // future.c extern void loadFutures(void); +extern PangoAttribute *FUTURE_pango_attr_font_features_new(const gchar *features); extern PangoAttribute *FUTURE_pango_attr_foreground_alpha_new(guint16 alpha); extern gboolean FUTURE_gtk_widget_path_iter_set_object_name(GtkWidgetPath *path, gint pos, const char *name); // drawtext.c extern void fontdescFromPangoFontDescription(PangoFontDescription *pdesc, uiDrawFontDescriptor *uidesc); + +// attrstr.c +extern PangoAttrList *attrstrToPangoAttrList(uiDrawTextLayoutParams *p/*TODO, NSArray **backgroundBlocks*/); From d4b38cd3b7f68fceb32d4d633faea9219ae4cfef Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 20 Feb 2017 19:28:19 -0500 Subject: [PATCH 0682/1329] And switched attributes on on GTK+. We have a problem with attributes that span bytes. --- unix/attrstr.c | 2 +- unix/drawtext.c | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/unix/attrstr.c b/unix/attrstr.c index 12cbb12d..e17ea75d 100644 --- a/unix/attrstr.c +++ b/unix/attrstr.c @@ -229,7 +229,7 @@ static gboolean applyFeatures(gpointer key, gpointer value, gpointer data) // remove the trailing comma/space g_string_truncate(s, s->len - 2); - addattr(p, *pos, 1, + addattr(p, *pos, *pos + 1, FUTURE_pango_attr_font_features_new(s->str)); return TRUE; // always delete; we're emptying the map } diff --git a/unix/drawtext.c b/unix/drawtext.c index abc9aa65..16ede458 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -101,6 +101,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) uiDrawTextLayout *tl; PangoContext *context; PangoFontDescription *desc; + PangoAttrList *attrs; int pangoWidth; tl = uiNew(uiDrawTextLayout); @@ -137,7 +138,9 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) pango_layout_set_alignment(tl->layout, pangoAligns[p->Align]); - // TODO attributes + attrs = attrstrToPangoAttrList(p); + pango_layout_set_attributes(tl->layout, attrs); + pango_attr_list_unref(attrs); tl->nLines = pango_layout_get_line_count(tl->layout); computeLineMetrics(tl); From 4a1642cea241cac2c15ebfba3cf4353a2b39032d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 20 Feb 2017 19:51:00 -0500 Subject: [PATCH 0683/1329] Fixed improper breaks in Unix attribute handling. --- darwin/attrstr.m | 1 + unix/attrstr.c | 21 ++++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index c7e227cf..154adbe4 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -307,6 +307,7 @@ static void applyAndFreeFontAttributes(struct foreachParams *p) fp = (struct fontParams *) [val pointerValue]; font = fontdescToCTFont(fp); range.location = [key integerValue]; + // TODO this is wrong for surrogate pairs range.length = 1; CFAttributedStringSetAttribute(p->mas, range, kCTFontAttributeName, font); CFRelease(font); diff --git a/unix/attrstr.c b/unix/attrstr.c index e17ea75d..f627d6c1 100644 --- a/unix/attrstr.c +++ b/unix/attrstr.c @@ -1,9 +1,13 @@ // 12 february 2017 #include "uipriv_unix.h" +// TODO ligatures items turn ligatures *OFF*?! +// TODO actually does not specifying a feature revert to default? + // we need to collect all the OpenType features and background blocks and add them all at once // TODO rename this struct to something that isn't exclusively foreach-ing? struct foreachParams { + const char *s; PangoAttrList *attrs; // keys are pointers to size_t maintained by g_new0()/g_free() // values are GStrings @@ -31,6 +35,8 @@ static void freeFeatureString(gpointer s) g_string_free((GString *) s, TRUE); } +#define isCodepointStart(b) (((uint8_t) (b)) <= 0x7F || ((uint8_t) (b)) >= 0xC0) + static void ensureFeaturesInRange(struct foreachParams *p, size_t start, size_t end) { size_t i; @@ -38,6 +44,9 @@ static void ensureFeaturesInRange(struct foreachParams *p, size_t start, size_t GString *new; for (i = start; i < end; i++) { + // don't create redundant entries; see below + if (!isCodepointStart(p->s[i])) + continue; new = (GString *) g_hash_table_lookup(p->features, &i); if (new != NULL) continue; @@ -79,6 +88,9 @@ static void doOpenType(const char *featureTag, uint32_t param, void *data) ensureFeaturesInRange(p->p, p->start, p->end); for (i = p->start; i < p->end; i++) { + // don't use redundant entries; see below + if (!isCodepointStart(p->s[i])) + continue; s = (GString *) g_hash_table_lookup(p->p->features, &i); g_string_append_printf(s, "\"%s\" %" PRIu32 ", ", featureTag, param); @@ -226,10 +238,16 @@ static gboolean applyFeatures(gpointer key, gpointer value, gpointer data) struct foreachParams *p = (struct foreachParams *) data; size_t *pos = (size_t *) key; GString *s = (GString *) value; + size_t n; // remove the trailing comma/space g_string_truncate(s, s->len - 2); - addattr(p, *pos, *pos + 1, + // make sure we cover an entire code point + // otherwise Pango will break apart multi-byte characters, spitting out U+FFFD characters at the invalid points + n = 1; + while (!isCodepointStart(p->s[*pos + n])) + n++; + addattr(p, *pos, *pos + n, FUTURE_pango_attr_font_features_new(s->str)); return TRUE; // always delete; we're emptying the map } @@ -244,6 +262,7 @@ PangoAttrList *attrstrToPangoAttrList(uiDrawTextLayoutParams *p/*TODO, NSArray * { struct foreachParams fep; + fep.s = uiAttributedStringString(p->String); fep.attrs = pango_attr_list_new(); fep.features = g_hash_table_new_full( featurePosHash, featurePosEqual, From 4ba4e4ba23a162ceac308a5ca1d4c3736701b1b6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 20 Feb 2017 20:41:14 -0500 Subject: [PATCH 0684/1329] More attribute implementation. --- unix/attrstr.c | 27 +++++++++------------------ unix/drawtext.c | 6 +++--- unix/uipriv_unix.h | 6 ++++++ 3 files changed, 18 insertions(+), 21 deletions(-) diff --git a/unix/attrstr.c b/unix/attrstr.c index f627d6c1..cdc72da5 100644 --- a/unix/attrstr.c +++ b/unix/attrstr.c @@ -1,10 +1,8 @@ // 12 february 2017 #include "uipriv_unix.h" -// TODO ligatures items turn ligatures *OFF*?! -// TODO actually does not specifying a feature revert to default? - // we need to collect all the OpenType features and background blocks and add them all at once +// TODO this is the wrong approach; it causes Pango to end runs early, meaning attributes like the ligature attributes never get applied properly // TODO rename this struct to something that isn't exclusively foreach-ing? struct foreachParams { const char *s; @@ -89,7 +87,7 @@ static void doOpenType(const char *featureTag, uint32_t param, void *data) ensureFeaturesInRange(p->p, p->start, p->end); for (i = p->start; i < p->end; i++) { // don't use redundant entries; see below - if (!isCodepointStart(p->s[i])) + if (!isCodepointStart(p->p->s[i])) continue; s = (GString *) g_hash_table_lookup(p->p->features, &i); g_string_append_printf(s, "\"%s\" %" PRIu32 ", ", @@ -120,30 +118,23 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t addattr(p, start, end, pango_attr_family_new((const char *) (spec->Value))); break; -#if 0 /* TODO */ case uiAttributeSize: addattr(p, start, end, pango_attr_size_new(cairoToPango(spec->Double))); break; case uiAttributeWeight: - ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(struct fontParams *fp) { - fp->desc.Weight = (uiDrawTextWeight) (spec->Value); - }); + // TODO reverse the misalignment from drawtext.c if it is corrected + addattr(p, start, end, + pango_attr_weight_new((PangoWeight) (spec->Value))); break; case uiAttributeItalic: - ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(struct fontParams *fp) { - fp->desc.Italic = (uiDrawTextItalic) (spec->Value); - }); + addattr(p, start, end, + pango_attr_style_new(pangoItalics[(uiDrawTextItalic) (spec->Value)])); break; case uiAttributeStretch: - ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(struct fontParams *fp) { - fp->desc.Stretch = (uiDrawTextStretch) (spec->Value); - }); + addattr(p, start, end, + pango_attr_stretch_new(pangoStretches[(uiDrawTextStretch) (spec->Value)])); break; -#endif case uiAttributeColor: addattr(p, start, end, pango_attr_foreground_new( diff --git a/unix/drawtext.c b/unix/drawtext.c index 16ede458..f0e86b86 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -14,7 +14,7 @@ struct uiDrawTextLayout { // See https://developer.gnome.org/pango/1.30/pango-Cairo-Rendering.html#pango-Cairo-Rendering.description // For the conversion, see https://developer.gnome.org/pango/1.30/pango-Glyph-Storage.html#pango-units-to-double and https://developer.gnome.org/pango/1.30/pango-Glyph-Storage.html#pango-units-from-double #define pangoToCairo(pango) (pango_units_to_double(pango)) -#define cairoToPango(cairo) (pango_units_from_double(cairo)) +// cairoToPango() is in uipriv_unix.h because attrstr.c needs it // we need a context for a few things // the documentation suggests creating cairo_t-specific, GdkScreen-specific, or even GtkWidget-specific contexts, but we can't really do that because we want our uiDrawTextFonts and uiDrawTextLayouts to be context-independent @@ -22,13 +22,13 @@ struct uiDrawTextLayout { // so let's use gdk_pango_context_get() instead; even though it's for the default screen only, it's good enough for us #define mkGenericPangoCairoContext() (gdk_pango_context_get()) -static const PangoStyle pangoItalics[] = { +const PangoStyle pangoItalics[] = { [uiDrawTextItalicNormal] = PANGO_STYLE_NORMAL, [uiDrawTextItalicOblique] = PANGO_STYLE_OBLIQUE, [uiDrawTextItalicItalic] = PANGO_STYLE_ITALIC, }; -static const PangoStretch pangoStretches[] = { +const PangoStretch pangoStretches[] = { [uiDrawTextStretchUltraCondensed] = PANGO_STRETCH_ULTRA_CONDENSED, [uiDrawTextStretchExtraCondensed] = PANGO_STRETCH_EXTRA_CONDENSED, [uiDrawTextStretchCondensed] = PANGO_STRETCH_CONDENSED, diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index d2ef5bf0..78e94ad8 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -64,3 +64,9 @@ extern void fontdescFromPangoFontDescription(PangoFontDescription *pdesc, uiDraw // attrstr.c extern PangoAttrList *attrstrToPangoAttrList(uiDrawTextLayoutParams *p/*TODO, NSArray **backgroundBlocks*/); + +// drawtext.c +// TODO get rid of these (for attrstr.c) +#define cairoToPango(cairo) (pango_units_from_double(cairo)) +extern const PangoStyle pangoItalics[]; +extern const PangoStretch pangoStretches[]; From 75c2c805343ebdd0b38dac25c1791a80d053fbf9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 20 Feb 2017 22:24:02 -0500 Subject: [PATCH 0685/1329] And adding the background attributes. We're done with the Pango one! Now for the Windows one... yay. --- unix/attrstr.c | 101 +++++++++++++++++++++++++++++++++++---------- unix/drawtext.c | 13 +++++- unix/uipriv_unix.h | 3 +- 3 files changed, 94 insertions(+), 23 deletions(-) diff --git a/unix/attrstr.c b/unix/attrstr.c index cdc72da5..115b611e 100644 --- a/unix/attrstr.c +++ b/unix/attrstr.c @@ -10,7 +10,8 @@ struct foreachParams { // keys are pointers to size_t maintained by g_new0()/g_free() // values are GStrings GHashTable *features; -//TODO GArray *backgroundBlocks; + // TODO use pango's built-in background attribute? + GPtrArray *backgroundClosures; }; static gboolean featurePosEqual(gconstpointer a, gconstpointer b) @@ -55,21 +56,51 @@ static void ensureFeaturesInRange(struct foreachParams *p, size_t start, size_t } } -#if 0 /* TODO */ -static backgroundBlock mkBackgroundBlock(size_t start, size_t end, double r, double g, double b, double a) -{ - return Block_copy(^(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y) { - uiDrawBrush brush; +struct closureParams { + size_t start; + size_t end; + double r; + double g; + double b; + double a; +}; - brush.Type = uiDrawBrushTypeSolid; - brush.R = r; - brush.G = g; - brush.B = b; - brush.A = a; - drawTextBackground(c, x, y, layout, start, end, &brush, 0); - }); +static void backgroundClosure(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y, gpointer data) +{ + struct closureParams *p = (struct closureParams *) data; + uiDrawBrush brush; + + brush.Type = uiDrawBrushTypeSolid; + brush.R = p->r; + brush.G = p->g; + brush.B = p->b; + brush.A = p->a; + drawTextBackground(c, x, y, layout, p->start, p->end, &brush, 0); +} + +static void freeClosureParams(gpointer data, GClosure *closure) +{ + uiFree((struct closureParams *) data); +} + +static GClosure *mkBackgroundClosure(size_t start, size_t end, double r, double g, double b, double a) +{ + struct closureParams *p; + GClosure *closure; + + p = uiNew(struct closureParams); + p->start = start; + p->end = end; + p->r = r; + p->g = g; + p->b = b; + p->a = a; + closure = (GClosure *) g_cclosure_new(G_CALLBACK(backgroundClosure), p, freeClosureParams); + // TODO write a specific marshaler + // TODO or drop the closure stuff entirely + g_closure_set_marshal(closure, g_cclosure_marshal_generic); + return closure; } -#endif struct otParam { struct foreachParams *p; @@ -107,7 +138,7 @@ static void addattr(struct foreachParams *p, size_t start, size_t end, PangoAttr static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) { struct foreachParams *p = (struct foreachParams *) data; -//TODO backgroundBlock block; + GClosure *closure; PangoGravity gravity; PangoUnderline underline; PangoLanguage *lang; @@ -145,14 +176,11 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t FUTURE_pango_attr_foreground_alpha_new( (guint16) (spec->A * 65535.0))); break; -#if 0 /* TODO */ case uiAttributeBackground: - block = mkBackgroundBlock(ostart, oend, + closure = mkBackgroundClosure(start, end, spec->R, spec->G, spec->B, spec->A); - [p->backgroundBlocks addObject:block]; - Block_release(block); + g_ptr_array_add(p->backgroundClosures, closure); break; -#endif case uiAttributeVerticalForms: gravity = PANGO_GRAVITY_SOUTH; if (spec->Value != 0) @@ -249,7 +277,12 @@ static void applyAndFreeFeatureAttributes(struct foreachParams *p) g_hash_table_destroy(p->features); } -PangoAttrList *attrstrToPangoAttrList(uiDrawTextLayoutParams *p/*TODO, NSArray **backgroundBlocks*/) +static void unrefClosure(gpointer data) +{ + g_closure_unref((GClosure *) data); +} + +PangoAttrList *attrstrToPangoAttrList(uiDrawTextLayoutParams *p, GPtrArray **backgroundClosures) { struct foreachParams fep; @@ -258,7 +291,33 @@ PangoAttrList *attrstrToPangoAttrList(uiDrawTextLayoutParams *p/*TODO, NSArray * fep.features = g_hash_table_new_full( featurePosHash, featurePosEqual, g_free, freeFeatureString); + fep.backgroundClosures = g_ptr_array_new_with_free_func(unrefClosure); uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); applyAndFreeFeatureAttributes(&fep); + *backgroundClosures = fep.backgroundClosures; return fep.attrs; } + +void invokeBackgroundClosure(GClosure *closure, uiDrawContext *c, uiDrawTextLayout *layout, double x, double y) +{ + GValue values[4] = { + // the zero-initialization is needed for g_value_init() to work + G_VALUE_INIT, + G_VALUE_INIT, + G_VALUE_INIT, + G_VALUE_INIT, + }; + + g_value_init(values + 0, G_TYPE_POINTER); + g_value_set_pointer(values + 0, c); + g_value_init(values + 1, G_TYPE_POINTER); + g_value_set_pointer(values + 1, layout); + g_value_init(values + 2, G_TYPE_DOUBLE); + g_value_set_double(values + 2, x); + g_value_init(values + 3, G_TYPE_DOUBLE); + g_value_set_double(values + 3, y); + g_closure_invoke(closure, + NULL, + 4, values, + NULL); +} diff --git a/unix/drawtext.c b/unix/drawtext.c index f0e86b86..8b45530a 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -7,6 +7,7 @@ struct uiDrawTextLayout { PangoLayout *layout; + GPtrArray *backgroundClosures; uiDrawTextLayoutLineMetrics *lineMetrics; int nLines; }; @@ -138,7 +139,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) pango_layout_set_alignment(tl->layout, pangoAligns[p->Align]); - attrs = attrstrToPangoAttrList(p); + attrs = attrstrToPangoAttrList(p, &(tl->backgroundClosures)); pango_layout_set_attributes(tl->layout, attrs); pango_attr_list_unref(attrs); @@ -151,12 +152,22 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { uiFree(tl->lineMetrics); + g_ptr_array_unref(tl->backgroundClosures); g_object_unref(tl->layout); uiFree(tl); } void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) { + guint i; + GClosure *closure; + + for (i = 0; i < tl->backgroundClosures->len; i++) { + closure = (GClosure *) g_ptr_array_index(tl->backgroundClosures, i); + invokeBackgroundClosure(closure, c, tl, x, y); + } + // TODO have an implicit save/restore on each drawing functions instead? and is this correct? + cairo_set_source_rgb(c->cr, 0.0, 0.0, 0.0); cairo_move_to(c->cr, x, y); pango_cairo_show_layout(c->cr, tl->layout); } diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index 78e94ad8..bde6502f 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -63,7 +63,8 @@ extern gboolean FUTURE_gtk_widget_path_iter_set_object_name(GtkWidgetPath *path, extern void fontdescFromPangoFontDescription(PangoFontDescription *pdesc, uiDrawFontDescriptor *uidesc); // attrstr.c -extern PangoAttrList *attrstrToPangoAttrList(uiDrawTextLayoutParams *p/*TODO, NSArray **backgroundBlocks*/); +extern PangoAttrList *attrstrToPangoAttrList(uiDrawTextLayoutParams *p, GPtrArray **backgroundClosures); +extern void invokeBackgroundClosure(GClosure *closure, uiDrawContext *c, uiDrawTextLayout *layout, double x, double y); // drawtext.c // TODO get rid of these (for attrstr.c) From c111239b0d9a63154a81da6c53c6e23d67ccf145 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 20 Feb 2017 22:25:49 -0500 Subject: [PATCH 0686/1329] More TODOs. --- unix/attrstr.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/unix/attrstr.c b/unix/attrstr.c index 115b611e..8882f3ac 100644 --- a/unix/attrstr.c +++ b/unix/attrstr.c @@ -1,6 +1,8 @@ // 12 february 2017 #include "uipriv_unix.h" +// TODO pango alpha attributes turn 0 into 65535 :| + // we need to collect all the OpenType features and background blocks and add them all at once // TODO this is the wrong approach; it causes Pango to end runs early, meaning attributes like the ligature attributes never get applied properly // TODO rename this struct to something that isn't exclusively foreach-ing? From efb7d5b21dc10b52795d1a2f9b971bd6a060506f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 21 Feb 2017 16:06:29 -0500 Subject: [PATCH 0687/1329] More TODOs. --- windows/spinbox.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/windows/spinbox.cpp b/windows/spinbox.cpp index 2b6af66d..8c4b666a 100644 --- a/windows/spinbox.cpp +++ b/windows/spinbox.cpp @@ -32,6 +32,7 @@ static int value(uiSpinbox *s) // control implementation +// TODO assign lResult static BOOL onWM_COMMAND(uiControl *c, HWND hwnd, WORD code, LRESULT *lResult) { uiSpinbox *s = (uiSpinbox *) c; From 9cc8b03516bc6da537d8fb60fdec0ec1f48af7d6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 22 Feb 2017 03:43:43 -0500 Subject: [PATCH 0688/1329] Switched to a custom IDWriteTextRenderer, which will be necessary for some of our text attributes. More TODO. --- CMakeLists.txt | 3 +- examples/drawtext/hittest.c | 5 +- windows/drawtext.cpp | 142 +++++++++++++++++++++++++++++++++++- 3 files changed, 146 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f4a01049..f189e2b9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -202,8 +202,9 @@ if(BUILD_SHARED_LIBS) endif() macro(_add_exec _name) + # TODOTODO re-add WIN32 when merging back add_executable(${_name} - WIN32 EXCLUDE_FROM_ALL + EXCLUDE_FROM_ALL ${ARGN}) target_link_libraries(${_name} libui ${_LIBUI_STATIC_RES}) _target_link_options_private(${_name} diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index 48d2c409..f5252388 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -1,9 +1,10 @@ // 20 january 2017 #include "drawtext.h" -// TODO double-check ligatures +// TODO double-check ligatures on all platforms to make sure we can place the cursor at the right place // TODO the hiding and showing does not work properly on GTK+ -// TODO using the arrow keys allows us to walk back to the end of the line; IIRC arrow keys shouldn't do that +// TODO using the arrow keys allows us to walk back to the end of the line on some platforms (TODO which?); IIRC arrow keys shouldn't do that +// TODO make sure to check the cursor positions of RTL on all platforms static const char *text = "Each of the glyphs an end user interacts with are called graphemes. " diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index da512fd9..26b6a6ab 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -7,6 +7,8 @@ // - if that's not a problem, do we have overlapping rects in the hittest sample? I can't tell... // - what happens if any nLines == 0? +// TODO verify our renderer is correct, especially with regards to snapping + struct uiDrawTextLayout { IDWriteTextFormat *format; IDWriteTextLayout *layout; @@ -240,23 +242,161 @@ static ID2D1SolidColorBrush *mkSolidBrush(ID2D1RenderTarget *rt, double r, doubl return brush; } +// some of the stuff we want to do isn't possible with what DirectWrite provides itself; we need to do it ourselves +// this is based on http://www.charlespetzold.com/blog/2014/01/Character-Formatting-Extensions-with-DirectWrite.html +class textRenderer : public IDWriteTextRenderer { + ULONG refcount; + ID2D1RenderTarget *rt; + BOOL snap; + IUnknown *black; +public: + textRenderer(ID2D1RenderTarget *rt, BOOL snap, IUnknown *black) + { + this->refcount = 1; + this->rt = rt; + this->snap = snap; + this->black = black; + } + + // IUnknown + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) + { + if (ppvObject == NULL) + return E_POINTER; + if (riid == IID_IUnknown || + riid == __uuidof (IDWritePixelSnapping) || + riid == __uuidof (IDWriteTextRenderer)) { + this->AddRef(); + *ppvObject = this; + return S_OK; + } + *ppvObject = NULL; + return E_NOINTERFACE; + } + + virtual ULONG STDMETHODCALLTYPE AddRef(void) + { + this->refcount++; + return this->refcount; + } + + virtual ULONG STDMETHODCALLTYPE Release(void) + { + this->refcount--; + if (this->refcount == 0) { + delete this; + return 0; + } + return this->refcount; + } + + // IDWritePixelSnapping + virtual HRESULT STDMETHODCALLTYPE GetCurrentTransform(void *clientDrawingContext, DWRITE_MATRIX *transform) + { + D2D1_MATRIX_3X2_F d2dtf; + + if (transform == NULL) + return E_POINTER; + this->rt->GetTransform(&d2dtf); + transform->m11 = d2dtf._11; + transform->m12 = d2dtf._12; + transform->m21 = d2dtf._21; + transform->m22 = d2dtf._22; + transform->dx = d2dtf._31; + transform->dy = d2dtf._32; + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE GetPixelsPerDip(void *clientDrawingContext, FLOAT *pixelsPerDip) + { + FLOAT dpix, dpiy; + + if (pixelsPerDip == NULL) + return E_POINTER; + this->rt->GetDpi(&dpix, &dpiy); + *pixelsPerDip = dpix / 96; + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE IsPixelSnappingDisabled(void *clientDrawingContext, BOOL *isDisabled) + { + if (isDisabled == NULL) + return E_POINTER; + *isDisabled = !this->snap; + return S_OK; + } + + // IDWriteTextRenderer + virtual HRESULT STDMETHODCALLTYPE DrawGlyphRun(void *clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, DWRITE_MEASURING_MODE measuringMode, const DWRITE_GLYPH_RUN *glyphRun, const DWRITE_GLYPH_RUN_DESCRIPTION *glyphRunDescription, IUnknown *clientDrawingEffect) + { + D2D1_POINT_2F baseline; + + baseline.x = baselineOriginX; + baseline.y = baselineOriginY; + if (clientDrawingEffect == NULL) + clientDrawingEffect = black; + this->rt->DrawGlyphRun( + baseline, + glyphRun, + (ID2D1Brush *) clientDrawingEffect, + measuringMode); + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE DrawInlineObject(void *clientDrawingContext, FLOAT originX, FLOAT originY, IDWriteInlineObject *inlineObject, BOOL isSideways, BOOL isRightToLeft, IUnknown *clientDrawingEffect) + { + if (inlineObject == NULL) + return E_POINTER; + return inlineObject->Draw(clientDrawingContext, this, + originX, originY, + isSideways, isRightToLeft, + clientDrawingEffect); + } + + virtual HRESULT STDMETHODCALLTYPE DrawStrikethrough(void *clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, const DWRITE_STRIKETHROUGH *strikethrough, IUnknown *clientDrawingEffect) + { + // TODO + return S_OK; + } + + virtual HRESULT DrawUnderline(void *clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, const DWRITE_UNDERLINE *underline, IUnknown *clientDrawingEffect) + { + // TODO + return S_OK; + } +}; + // TODO this ignores clipping? void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) { D2D1_POINT_2F pt; ID2D1Brush *black; + textRenderer *renderer; + HRESULT hr; // TODO document that fully opaque black is the default text color; figure out whether this is upheld in various scenarios on other platforms // TODO figure out if this needs to be cleaned out black = mkSolidBrush(c->rt, 0.0, 0.0, 0.0, 1.0); +#if 0 pt.x = x; pt.y = y; // TODO D2D1_DRAW_TEXT_OPTIONS_NO_SNAP? // TODO D2D1_DRAW_TEXT_OPTIONS_CLIP? - // TODO when setting 8.1 as minimum (TODO verify), D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT? + // TODO LONGTERM when setting 8.1 as minimum (TODO verify), D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT? // TODO what is our pixel snapping setting related to the OPTIONS enum values? c->rt->DrawTextLayout(pt, tl->layout, black, D2D1_DRAW_TEXT_OPTIONS_NONE); +#else + renderer = new textRenderer(c->rt, + TRUE, // TODO FALSE for no-snap? + black); + hr = tl->layout->Draw(NULL, + renderer, + x, y); + if (hr != S_OK) + logHRESULT(L"error drawing IDWriteTextLayout", hr); + renderer->Release(); +#endif black->Release(); } From ae8105c234c6675c02cf6185108de90595080285 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 22 Feb 2017 11:23:26 -0500 Subject: [PATCH 0689/1329] Added debugging to our custom IDWriteTextRenderer. --- windows/drawtext.cpp | 13 +++++++++++-- windows/fontdialog.cpp | 2 ++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 26b6a6ab..91c5a0c4 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -378,7 +378,9 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) // TODO figure out if this needs to be cleaned out black = mkSolidBrush(c->rt, 0.0, 0.0, 0.0, 1.0); -#if 0 +#define renderD2D 0 +#define renderOur 1 +#if renderD2D pt.x = x; pt.y = y; // TODO D2D1_DRAW_TEXT_OPTIONS_NO_SNAP? @@ -386,7 +388,14 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) // TODO LONGTERM when setting 8.1 as minimum (TODO verify), D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT? // TODO what is our pixel snapping setting related to the OPTIONS enum values? c->rt->DrawTextLayout(pt, tl->layout, black, D2D1_DRAW_TEXT_OPTIONS_NONE); -#else +#endif +#if renderD2D && renderOur + // draw ours semitransparent so we can check + // TODO get the actual color Charles Petzold uses and use that + black->Release(); + black = mkSolidBrush(c->rt, 1.0, 0.0, 0.0, 0.75); +#endif +#if renderOur renderer = new textRenderer(c->rt, TRUE, // TODO FALSE for no-snap? black); diff --git a/windows/fontdialog.cpp b/windows/fontdialog.cpp index d7fa91e9..6096d442 100644 --- a/windows/fontdialog.cpp +++ b/windows/fontdialog.cpp @@ -6,6 +6,8 @@ // - the Choose Font sample defaults to Regular/Italic/Bold/Bold Italic in some case (no styles?); do we? find out what the case is // - do we set initial family and style topmost as well? // - this should probably just handle IDWriteFonts +// - localization? +// - the Sample window overlaps the groupbox in a weird way (compare to the real ChooseFont() dialog) struct fontDialog { HWND hwnd; From fb04feaebb828e1520eeb2e6389f787d98c5f9a2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 22 Feb 2017 12:49:55 -0500 Subject: [PATCH 0690/1329] Started Windows attribute handling. --- windows/CMakeLists.txt | 1 + windows/attrstr.cpp | 318 +++++++++++++++++++++++++++++++++++++++++ windows/draw.hpp | 3 + windows/drawtext.cpp | 2 + 4 files changed, 324 insertions(+) create mode 100644 windows/attrstr.cpp diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 5c401d06..f4a10998 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -7,6 +7,7 @@ list(APPEND _LIBUI_SOURCES windows/areaevents.cpp windows/areascroll.cpp windows/areautil.cpp + windows/attrstr.cpp windows/box.cpp windows/button.cpp windows/checkbox.cpp diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp new file mode 100644 index 00000000..10e9002a --- /dev/null +++ b/windows/attrstr.cpp @@ -0,0 +1,318 @@ +// 12 february 2017 +#include "uipriv_windows.hpp" +#include "draw.hpp" + +// we need to collect all the OpenType features and background blocks and add them all at once +// TODO(TODO does not seem to apply here) this is the wrong approach; it causes Pango to end runs early, meaning attributes like the ligature attributes never get applied properly +// TODO contextual alternates override ligatures? +// TODO rename this struct to something that isn't exclusively foreach-ing? +struct foreachParams { + const uint16_t *s; + IDWriteTextLayout *layout; + std::map *features; +//TODO GPtrArray *backgroundClosures; +}; + +#define isCodepointStart(w) ((((uint16_t) (w)) < 0xDC00) || (((uint16_t) (w)) >= 0xE000)) + +static void ensureFeaturesInRange(struct foreachParams *p, size_t start, size_t end) +{ + size_t i; + size_t *key; + IDWriteTypography *t; + HRESULT hr; + + for (i = start; i < end; i++) { + // don't create redundant entries; see below + if (!isCodepointStart(p->s[i])) + continue; + t = (*(p->features))[i]; + if (t != NULL) + continue; + hr = dwfactory->CreateTypography(&t); + if (hr != S_OK) + logHRESULT(L"error creating IDWriteTypography", hr); + (*(p->features))[i] = t; + } +} + +#if 0 /* TODO */ +struct closureParams { + size_t start; + size_t end; + double r; + double g; + double b; + double a; +}; + +static void backgroundClosure(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y, gpointer data) +{ + struct closureParams *p = (struct closureParams *) data; + uiDrawBrush brush; + + brush.Type = uiDrawBrushTypeSolid; + brush.R = p->r; + brush.G = p->g; + brush.B = p->b; + brush.A = p->a; + drawTextBackground(c, x, y, layout, p->start, p->end, &brush, 0); +} + +static void freeClosureParams(gpointer data, GClosure *closure) +{ + uiFree((struct closureParams *) data); +} + +static GClosure *mkBackgroundClosure(size_t start, size_t end, double r, double g, double b, double a) +{ + struct closureParams *p; + GClosure *closure; + + p = uiNew(struct closureParams); + p->start = start; + p->end = end; + p->r = r; + p->g = g; + p->b = b; + p->a = a; + closure = (GClosure *) g_cclosure_new(G_CALLBACK(backgroundClosure), p, freeClosureParams); + // TODO write a specific marshaler + // TODO or drop the closure stuff entirely + g_closure_set_marshal(closure, g_cclosure_marshal_generic); + return closure; +} +#endif + +struct otParam { + struct foreachParams *p; + size_t start; + size_t end; +}; + +static void doOpenType(const char *featureTag, uint32_t param, void *data) +{ + struct otParam *p = (struct otParam *) data; + size_t i; + IDWriteTypography *t; + DWRITE_FONT_FEATURE feature; + HRESULT hr; + + feature.nameTag = (DWRITE_FONT_FEATURE_TAG) DWRITE_MAKE_OPENTYPE_TAG( + featureTag[0], + featureTag[1], + featureTag[2], + featureTag[3]); + feature.parameter = param; + ensureFeaturesInRange(p->p, p->start, p->end); + for (i = p->start; i < p->end; i++) { + // don't use redundant entries; see below + if (!isCodepointStart(p->p->s[i])) + continue; + t = (*(p->p->features))[i]; + hr = t->AddFontFeature(feature); + if (hr != S_OK) + logHRESULT(L"error adding feature to IDWriteTypography", hr); + } +} + +static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) +{ + struct foreachParams *p = (struct foreachParams *) data; + DWRITE_TEXT_RANGE range; + WCHAR *wfamily; +#if 0 /* TODO */ + GClosure *closure; + PangoGravity gravity; + PangoUnderline underline; + PangoLanguage *lang; +#endif + struct otParam op; + HRESULT hr; + + start = attrstrUTF8ToUTF16(s, start); + end = attrstrUTF8ToUTF16(s, end); + range.startPosition = start; + range.length = end - start; + switch (spec->Type) { + case uiAttributeFamily: + wfamily = toUTF16((char *) (spec->Value)); + hr = p->layout->SetFontFamilyName(wfamily, range); + if (hr != S_OK) + logHRESULT(L"error applying family name attribute", hr); + uiFree(wfamily); + break; + case uiAttributeSize: + hr = p->layout->SetFontSize( +// TODO unify with drawtext.cpp +#define pointSizeToDWriteSize(size) (size * (96.0 / 72.0)) + pointSizeToDWriteSize(spec->Double), + range); + if (hr != S_OK) + logHRESULT(L"error applying size attribute", hr); + break; +#if 0 /* TODO */ + case uiAttributeWeight: + // TODO reverse the misalignment from drawtext.c if it is corrected + addattr(p, start, end, + pango_attr_weight_new((PangoWeight) (spec->Value))); + break; + case uiAttributeItalic: + addattr(p, start, end, + pango_attr_style_new(pangoItalics[(uiDrawTextItalic) (spec->Value)])); + break; + case uiAttributeStretch: + addattr(p, start, end, + pango_attr_stretch_new(pangoStretches[(uiDrawTextStretch) (spec->Value)])); + break; + case uiAttributeColor: + addattr(p, start, end, + pango_attr_foreground_new( + (guint16) (spec->R * 65535.0), + (guint16) (spec->G * 65535.0), + (guint16) (spec->B * 65535.0))); + addattr(p, start, end, + FUTURE_pango_attr_foreground_alpha_new( + (guint16) (spec->A * 65535.0))); + break; + case uiAttributeBackground: + closure = mkBackgroundClosure(start, end, + spec->R, spec->G, spec->B, spec->A); + g_ptr_array_add(p->backgroundClosures, closure); + break; + case uiAttributeVerticalForms: + gravity = PANGO_GRAVITY_SOUTH; + if (spec->Value != 0) + gravity = PANGO_GRAVITY_EAST; + addattr(p, start, end, + pango_attr_gravity_new(gravity)); + break; + case uiAttributeUnderline: + switch (spec->Value) { + case uiDrawUnderlineStyleNone: + underline = PANGO_UNDERLINE_NONE; + break; + case uiDrawUnderlineStyleSingle: + underline = PANGO_UNDERLINE_SINGLE; + break; + case uiDrawUnderlineStyleDouble: + underline = PANGO_UNDERLINE_DOUBLE; + break; + case uiDrawUnderlineStyleSuggestion: + underline = PANGO_UNDERLINE_ERROR; + break; + } + addattr(p, start, end, + pango_attr_underline_new(underline)); + break; + case uiAttributeUnderlineColor: + switch (spec->Value) { + case uiDrawUnderlineColorCustom: + addattr(p, start, end, + pango_attr_underline_color_new( + (guint16) (spec->R * 65535.0), + (guint16) (spec->G * 65535.0), + (guint16) (spec->B * 65535.0))); + break; + case uiDrawUnderlineColorSpelling: + // TODO GtkTextView style property error-underline-color + addattr(p, start, end, + pango_attr_underline_color_new(65535, 0, 0)); + break; + case uiDrawUnderlineColorGrammar: + // TODO find a more appropriate color + addattr(p, start, end, + pango_attr_underline_color_new(0, 65535, 0)); + break; + case uiDrawUnderlineColorAuxiliary: + // TODO find a more appropriate color + addattr(p, start, end, + pango_attr_underline_color_new(0, 0, 65535)); + break; + } + break; + // language strings are specified as BCP 47: https://developer.gnome.org/pango/1.30/pango-Scripts-and-Languages.html#pango-language-from-string https://www.ietf.org/rfc/rfc3066.txt + case uiAttributeLanguage: + lang = pango_language_from_string((const char *) (spec->Value)); + addattr(p, start, end, + pango_attr_language_new(lang)); + // lang *cannot* be freed + break; +#endif + // TODO + default: + // handle typographic features + op.p = p; + op.start = start; + op.end = end; + // TODO check if unhandled and complain + specToOpenType(spec, doOpenType, &op); + break; + } + return 0; +} + +static void applyAndFreeFeatureAttributes(struct foreachParams *p) +{ + DWRITE_TEXT_RANGE range; + HRESULT hr; + + for (auto iter = p->features->begin(); iter != p->features->end(); iter++) { + // make sure we cover an entire code point + range.startPosition = iter->first; + range.length = 1; + if (!isCodepointStart(p->s[iter->first])) + range.length = 2; + hr = p->layout->SetTypography(iter->second, range); + if (hr != S_OK) + logHRESULT(L"error applying typographic features", hr); + iter->second->Release(); + } + delete p->features; +} + +#if 0 /* TODO */ +static void unrefClosure(gpointer data) +{ + g_closure_unref((GClosure *) data); +} +#endif + +void attrstrToIDWriteTextLayoutAttrs(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout/*TODO, GPtrArray **backgroundClosures*/) +{ + struct foreachParams fep; + + fep.s = attrstrUTF16(p->String); + fep.layout = layout; + fep.features = new std::map; +//TODO fep.backgroundClosures = g_ptr_array_new_with_free_func(unrefClosure); + uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); + applyAndFreeFeatureAttributes(&fep); +//TODO *backgroundClosures = fep.backgroundClosures; +} + +#if 0 +void invokeBackgroundClosure(GClosure *closure, uiDrawContext *c, uiDrawTextLayout *layout, double x, double y) +{ + GValue values[4] = { + // the zero-initialization is needed for g_value_init() to work + G_VALUE_INIT, + G_VALUE_INIT, + G_VALUE_INIT, + G_VALUE_INIT, + }; + + g_value_init(values + 0, G_TYPE_POINTER); + g_value_set_pointer(values + 0, c); + g_value_init(values + 1, G_TYPE_POINTER); + g_value_set_pointer(values + 1, layout); + g_value_init(values + 2, G_TYPE_DOUBLE); + g_value_set_double(values + 2, x); + g_value_init(values + 3, G_TYPE_DOUBLE); + g_value_set_double(values + 3, y); + g_closure_invoke(closure, + NULL, + 4, values, + NULL); +} +#endif diff --git a/windows/draw.hpp b/windows/draw.hpp index b015791f..ce177523 100644 --- a/windows/draw.hpp +++ b/windows/draw.hpp @@ -14,3 +14,6 @@ extern ID2D1PathGeometry *pathGeometry(uiDrawPath *p); // drawmatrix.cpp extern void m2d(uiDrawMatrix *m, D2D1_MATRIX_3X2_F *d); + +// attrstr.cpp +extern void attrstrToIDWriteTextLayoutAttrs(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout/*TODO, GPtrArray **backgroundClosures*/); diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 91c5a0c4..9f321f12 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -196,6 +196,8 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) if (hr != S_OK) logHRESULT(L"error setting IDWriteTextLayout max layout width", hr); + attrstrToIDWriteTextLayoutAttrs(p, tl->layout); + computeLineInfo(tl); // and finally copy the UTF-8/UTF-16 index conversion tables From b42250e3a9e344de9e1a86873e462a684be320d8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 22 Feb 2017 15:19:11 -0500 Subject: [PATCH 0691/1329] More text attributes on Windows, including the beginning of drawing effects for colors and underlines. --- windows/attrstr.cpp | 106 ++++++++++++++++++++++++++++++++----------- windows/draw.hpp | 62 +++++++++++++++++++++++++ windows/drawtext.cpp | 50 ++++++++++++++------ windows/winapi.hpp | 1 + 4 files changed, 177 insertions(+), 42 deletions(-) diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index 10e9002a..31fefcae 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -2,19 +2,42 @@ #include "uipriv_windows.hpp" #include "draw.hpp" -// we need to collect all the OpenType features and background blocks and add them all at once +// we need to combine color and underline style into one unit for IDWriteLayout::SetDrawingEffect() +// we also need to collect all the OpenType features and background blocks and add them all at once // TODO(TODO does not seem to apply here) this is the wrong approach; it causes Pango to end runs early, meaning attributes like the ligature attributes never get applied properly // TODO contextual alternates override ligatures? // TODO rename this struct to something that isn't exclusively foreach-ing? struct foreachParams { const uint16_t *s; IDWriteTextLayout *layout; + std::map *effects; std::map *features; //TODO GPtrArray *backgroundClosures; }; #define isCodepointStart(w) ((((uint16_t) (w)) < 0xDC00) || (((uint16_t) (w)) >= 0xE000)) +static void ensureEffectsInRange(struct foreachParams *p, size_t start, size_t end, std::function f) +{ + size_t i; + size_t *key; + textDrawingEffect *t; + + for (i = start; i < end; i++) { + // don't create redundant entries; see below + if (!isCodepointStart(p->s[i])) + continue; + t = (*(p->effects))[i]; + if (t != NULL) { + f(t); + continue; + } + t = new textDrawingEffect; + f(t); + (*(p->effects))[i] = t; + } +} + static void ensureFeaturesInRange(struct foreachParams *p, size_t start, size_t end) { size_t i; @@ -124,9 +147,8 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t #if 0 /* TODO */ GClosure *closure; PangoGravity gravity; - PangoUnderline underline; - PangoLanguage *lang; #endif + WCHAR *localeName; struct otParam op; HRESULT hr; @@ -151,30 +173,38 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t if (hr != S_OK) logHRESULT(L"error applying size attribute", hr); break; -#if 0 /* TODO */ case uiAttributeWeight: - // TODO reverse the misalignment from drawtext.c if it is corrected - addattr(p, start, end, - pango_attr_weight_new((PangoWeight) (spec->Value))); + // TODO reverse the misalignment from drawtext.cpp if it is corrected + hr = p->layout->SetFontWeight( + (DWRITE_FONT_WEIGHT) (spec->Value), + range); + if (hr != S_OK) + logHRESULT(L"error applying weight attribute", hr); break; case uiAttributeItalic: - addattr(p, start, end, - pango_attr_style_new(pangoItalics[(uiDrawTextItalic) (spec->Value)])); + hr = p->layout->SetFontStyle( + dwriteItalics[(uiDrawTextItalic) (spec->Value)], + range); + if (hr != S_OK) + logHRESULT(L"error applying italic attribute", hr); break; case uiAttributeStretch: - addattr(p, start, end, - pango_attr_stretch_new(pangoStretches[(uiDrawTextStretch) (spec->Value)])); + hr = p->layout->SetFontStretch( + dwriteStretches[(uiDrawTextStretch) (spec->Value)], + range); + if (hr != S_OK) + logHRESULT(L"error applying stretch attribute", hr); break; case uiAttributeColor: - addattr(p, start, end, - pango_attr_foreground_new( - (guint16) (spec->R * 65535.0), - (guint16) (spec->G * 65535.0), - (guint16) (spec->B * 65535.0))); - addattr(p, start, end, - FUTURE_pango_attr_foreground_alpha_new( - (guint16) (spec->A * 65535.0))); + ensureEffectsInRange(p, start, end, [=](textDrawingEffect *t) { + t->hasColor = true; + t->r = spec->R; + t->g = spec->G; + t->b = spec->B; + t->a = spec->A; + }); break; +#if 0 case uiAttributeBackground: closure = mkBackgroundClosure(start, end, spec->R, spec->G, spec->B, spec->A); @@ -231,14 +261,15 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t break; } break; - // language strings are specified as BCP 47: https://developer.gnome.org/pango/1.30/pango-Scripts-and-Languages.html#pango-language-from-string https://www.ietf.org/rfc/rfc3066.txt - case uiAttributeLanguage: - lang = pango_language_from_string((const char *) (spec->Value)); - addattr(p, start, end, - pango_attr_language_new(lang)); - // lang *cannot* be freed - break; #endif + // locale names are specified as BCP 47: https://msdn.microsoft.com/en-us/library/windows/desktop/dd373814(v=vs.85).aspx https://www.ietf.org/rfc/rfc4646.txt + case uiAttributeLanguage: + localeName = toUTF16((char *) (spec->Value)); + hr = p->layout->SetLocaleName(localeName, range); + if (hr != S_OK) + logHRESULT(L"error applying locale name attribute", hr); + uiFree(localeName); + break; // TODO default: // handle typographic features @@ -252,6 +283,25 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t return 0; } +static void applyAndFreeEffectsAttributes(struct foreachParams *p) +{ + DWRITE_TEXT_RANGE range; + HRESULT hr; + + for (auto iter = p->effects->begin(); iter != p->effects->end(); iter++) { + // make sure we cover an entire code point + range.startPosition = iter->first; + range.length = 1; + if (!isCodepointStart(p->s[iter->first])) + range.length = 2; + hr = p->layout->SetDrawingEffect(iter->second, range); + if (hr != S_OK) + logHRESULT(L"error applying drawing effects attributes", hr); + iter->second->Release(); + } + delete p->effects; +} + static void applyAndFreeFeatureAttributes(struct foreachParams *p) { DWRITE_TEXT_RANGE range; @@ -265,7 +315,7 @@ static void applyAndFreeFeatureAttributes(struct foreachParams *p) range.length = 2; hr = p->layout->SetTypography(iter->second, range); if (hr != S_OK) - logHRESULT(L"error applying typographic features", hr); + logHRESULT(L"error applying typographic features attributes", hr); iter->second->Release(); } delete p->features; @@ -284,9 +334,11 @@ void attrstrToIDWriteTextLayoutAttrs(uiDrawTextLayoutParams *p, IDWriteTextLayou fep.s = attrstrUTF16(p->String); fep.layout = layout; + fep.effects = new std::map; fep.features = new std::map; //TODO fep.backgroundClosures = g_ptr_array_new_with_free_func(unrefClosure); uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); + applyAndFreeEffectsAttributes(&fep); applyAndFreeFeatureAttributes(&fep); //TODO *backgroundClosures = fep.backgroundClosures; } diff --git a/windows/draw.hpp b/windows/draw.hpp index ce177523..adc68294 100644 --- a/windows/draw.hpp +++ b/windows/draw.hpp @@ -17,3 +17,65 @@ extern void m2d(uiDrawMatrix *m, D2D1_MATRIX_3X2_F *d); // attrstr.cpp extern void attrstrToIDWriteTextLayoutAttrs(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout/*TODO, GPtrArray **backgroundClosures*/); + +// drawtext.cpp +class textDrawingEffect : public IUnknown { + ULONG refcount; +public: + bool hasColor; + double r; + double g; + double b; + double a; + + bool hasUnderline; + uiDrawUnderlineStyle u; + + bool hasUnderlineColor; + double ur; + double ug; + double ub; + double ua; + + textDrawingEffect() + { + this->refcount = 1; + this->hasColor = false; + this->hasUnderline = false; + this->hasUnderlineColor = false; + } + + // IUnknown + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) + { + if (ppvObject == NULL) + return E_POINTER; + if (riid == IID_IUnknown) { + this->AddRef(); + *ppvObject = this; + return S_OK; + } + *ppvObject = NULL; + return E_NOINTERFACE; + } + + virtual ULONG STDMETHODCALLTYPE AddRef(void) + { + this->refcount++; + return this->refcount; + } + + virtual ULONG STDMETHODCALLTYPE Release(void) + { + this->refcount--; + if (this->refcount == 0) { + delete this; + return 0; + } + return this->refcount; + } + +}; +// TODO these should not be exported +extern std::map dwriteItalics; +extern std::map dwriteStretches; diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 9f321f12..31d6daee 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -28,14 +28,14 @@ struct uiDrawTextLayout { #define pointSizeToDWriteSize(size) (size * (96.0 / 72.0)) // TODO should be const but then I can't operator[] on it; the real solution is to find a way to do designated array initializers in C++11 but I do not know enough C++ voodoo to make it work (it is possible but no one else has actually done it before) -static std::map dwriteItalics = { +std::map dwriteItalics = { { uiDrawTextItalicNormal, DWRITE_FONT_STYLE_NORMAL }, { uiDrawTextItalicOblique, DWRITE_FONT_STYLE_OBLIQUE }, { uiDrawTextItalicItalic, DWRITE_FONT_STYLE_ITALIC }, }; // TODO should be const but then I can't operator[] on it; the real solution is to find a way to do designated array initializers in C++11 but I do not know enough C++ voodoo to make it work (it is possible but no one else has actually done it before) -static std::map dwriteStretches = { +std::map dwriteStretches = { { uiDrawTextStretchUltraCondensed, DWRITE_FONT_STRETCH_ULTRA_CONDENSED }, { uiDrawTextStretchExtraCondensed, DWRITE_FONT_STRETCH_EXTRA_CONDENSED }, { uiDrawTextStretchCondensed, DWRITE_FONT_STRETCH_CONDENSED }, @@ -219,12 +219,10 @@ void uiDrawFreeTextLayout(uiDrawTextLayout *tl) uiFree(tl); } -static ID2D1SolidColorBrush *mkSolidBrush(ID2D1RenderTarget *rt, double r, double g, double b, double a) +static HRESULT mkSolidBrush(ID2D1RenderTarget *rt, double r, double g, double b, double a, ID2D1SolidColorBrush **brush) { D2D1_BRUSH_PROPERTIES props; D2D1_COLOR_F color; - ID2D1SolidColorBrush *brush; - HRESULT hr; ZeroMemory(&props, sizeof (D2D1_BRUSH_PROPERTIES)); props.opacity = 1.0; @@ -235,10 +233,18 @@ static ID2D1SolidColorBrush *mkSolidBrush(ID2D1RenderTarget *rt, double r, doubl color.g = g; color.b = b; color.a = a; - hr = rt->CreateSolidColorBrush( + return rt->CreateSolidColorBrush( &color, &props, - &brush); + brush); +} + +static ID2D1SolidColorBrush *mustMakeSolidBrush(ID2D1RenderTarget *rt, double r, double g, double b, double a) +{ + ID2D1SolidColorBrush *brush; + HRESULT hr; + + hr = mkSolidBrush(rt, r, g, b, a, &brush); if (hr != S_OK) logHRESULT(L"error creating solid brush", hr); return brush; @@ -250,9 +256,9 @@ class textRenderer : public IDWriteTextRenderer { ULONG refcount; ID2D1RenderTarget *rt; BOOL snap; - IUnknown *black; + ID2D1SolidColorBrush *black; public: - textRenderer(ID2D1RenderTarget *rt, BOOL snap, IUnknown *black) + textRenderer(ID2D1RenderTarget *rt, BOOL snap, ID2D1SolidColorBrush *black) { this->refcount = 1; this->rt = rt; @@ -332,16 +338,26 @@ public: virtual HRESULT STDMETHODCALLTYPE DrawGlyphRun(void *clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, DWRITE_MEASURING_MODE measuringMode, const DWRITE_GLYPH_RUN *glyphRun, const DWRITE_GLYPH_RUN_DESCRIPTION *glyphRunDescription, IUnknown *clientDrawingEffect) { D2D1_POINT_2F baseline; + textDrawingEffect *t = (textDrawingEffect *) clientDrawingEffect; + ID2D1SolidColorBrush *brush; baseline.x = baselineOriginX; baseline.y = baselineOriginY; - if (clientDrawingEffect == NULL) - clientDrawingEffect = black; + brush = this->black; + if (t != NULL && t->hasColor) { + HRESULT hr; + + hr = mkSolidBrush(this->rt, t->r, t->g, t->b, t->a, &brush); + if (hr != S_OK) + return hr; + } this->rt->DrawGlyphRun( baseline, glyphRun, - (ID2D1Brush *) clientDrawingEffect, + brush, measuringMode); + if (t != NULL && t->hasColor) + brush->Release(); return S_OK; } @@ -357,12 +373,16 @@ public: virtual HRESULT STDMETHODCALLTYPE DrawStrikethrough(void *clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, const DWRITE_STRIKETHROUGH *strikethrough, IUnknown *clientDrawingEffect) { + textDrawingEffect *t = (textDrawingEffect *) clientDrawingEffect; + // TODO return S_OK; } virtual HRESULT DrawUnderline(void *clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, const DWRITE_UNDERLINE *underline, IUnknown *clientDrawingEffect) { + textDrawingEffect *t = (textDrawingEffect *) clientDrawingEffect; + // TODO return S_OK; } @@ -372,13 +392,13 @@ public: void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) { D2D1_POINT_2F pt; - ID2D1Brush *black; + ID2D1SolidColorBrush *black; textRenderer *renderer; HRESULT hr; // TODO document that fully opaque black is the default text color; figure out whether this is upheld in various scenarios on other platforms // TODO figure out if this needs to be cleaned out - black = mkSolidBrush(c->rt, 0.0, 0.0, 0.0, 1.0); + black = mustMakeSolidBrush(c->rt, 0.0, 0.0, 0.0, 1.0); #define renderD2D 0 #define renderOur 1 @@ -395,7 +415,7 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) // draw ours semitransparent so we can check // TODO get the actual color Charles Petzold uses and use that black->Release(); - black = mkSolidBrush(c->rt, 1.0, 0.0, 0.0, 0.75); + black = mustMakeSolidBrush(c->rt, 1.0, 0.0, 0.0, 0.75); #endif #if renderOur renderer = new textRenderer(c->rt, diff --git a/windows/winapi.hpp b/windows/winapi.hpp index a540809c..c5796faf 100644 --- a/windows/winapi.hpp +++ b/windows/winapi.hpp @@ -47,4 +47,5 @@ #include #include #include +#include #endif From f2b158b529e521ecac2bf0a4d233a6f44b839ae8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 22 Feb 2017 19:13:36 -0500 Subject: [PATCH 0692/1329] And finished implementing attributes on Windows. --- examples/drawtext/attributes.c | 10 +- windows/attrstr.cpp | 192 ++++++++++++--------------------- windows/draw.hpp | 3 +- windows/drawtext.cpp | 120 +++++++++++++++++++-- 4 files changed, 194 insertions(+), 131 deletions(-) diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index f0868a8b..24dd4a19 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -100,7 +100,15 @@ static void setupAttributedString(void) spec.Type = uiAttributeVerticalForms; spec.Value = 1; uiAttributedStringSetAttribute(attrstr, &spec, start, end); - uiAttributedStringAppendUnattributed(attrstr, " (which you can draw rotated for proper vertical text)"); + uiAttributedStringAppendUnattributed(attrstr, " (which you can draw rotated for proper vertical text; for instance, "); + next = "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A"; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeVerticalForms; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); uiAttributedStringAppendUnattributed(attrstr, ", "); diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index 31fefcae..c6d366c5 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -12,7 +12,7 @@ struct foreachParams { IDWriteTextLayout *layout; std::map *effects; std::map *features; -//TODO GPtrArray *backgroundClosures; + std::vector *backgroundFuncs; }; #define isCodepointStart(w) ((((uint16_t) (w)) < 0xDC00) || (((uint16_t) (w)) >= 0xE000)) @@ -59,54 +59,20 @@ static void ensureFeaturesInRange(struct foreachParams *p, size_t start, size_t } } -#if 0 /* TODO */ -struct closureParams { - size_t start; - size_t end; - double r; - double g; - double b; - double a; -}; - -static void backgroundClosure(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y, gpointer data) +static backgroundFunc mkBackgroundFunc(size_t start, size_t end, double r, double g, double b, double a) { - struct closureParams *p = (struct closureParams *) data; - uiDrawBrush brush; + return [=](uiDrawContext *c, uiDrawTextLayout *layout, double x, double y) { + uiDrawBrush brush; - brush.Type = uiDrawBrushTypeSolid; - brush.R = p->r; - brush.G = p->g; - brush.B = p->b; - brush.A = p->a; - drawTextBackground(c, x, y, layout, p->start, p->end, &brush, 0); + brush.Type = uiDrawBrushTypeSolid; + brush.R = r; + brush.G = g; + brush.B = b; + brush.A = a; + drawTextBackground(c, x, y, layout, start, end, &brush, 0); + }; } -static void freeClosureParams(gpointer data, GClosure *closure) -{ - uiFree((struct closureParams *) data); -} - -static GClosure *mkBackgroundClosure(size_t start, size_t end, double r, double g, double b, double a) -{ - struct closureParams *p; - GClosure *closure; - - p = uiNew(struct closureParams); - p->start = start; - p->end = end; - p->r = r; - p->g = g; - p->b = b; - p->a = a; - closure = (GClosure *) g_cclosure_new(G_CALLBACK(backgroundClosure), p, freeClosureParams); - // TODO write a specific marshaler - // TODO or drop the closure stuff entirely - g_closure_set_marshal(closure, g_cclosure_marshal_generic); - return closure; -} -#endif - struct otParam { struct foreachParams *p; size_t start; @@ -144,10 +110,8 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t struct foreachParams *p = (struct foreachParams *) data; DWRITE_TEXT_RANGE range; WCHAR *wfamily; -#if 0 /* TODO */ - GClosure *closure; - PangoGravity gravity; -#endif + BOOL hasUnderline; + uint32_t vertval; WCHAR *localeName; struct otParam op; HRESULT hr; @@ -204,64 +168,79 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t t->a = spec->A; }); break; -#if 0 case uiAttributeBackground: - closure = mkBackgroundClosure(start, end, - spec->R, spec->G, spec->B, spec->A); - g_ptr_array_add(p->backgroundClosures, closure); + p->backgroundFuncs->push_back( + mkBackgroundFunc(start, end, + spec->R, spec->G, spec->B, spec->A)); break; case uiAttributeVerticalForms: - gravity = PANGO_GRAVITY_SOUTH; + // LONGTERM 8 and/or 8.1 add other methods for vertical text + op.p = p; + op.start = start; + op.end = end; + vertval = 0; if (spec->Value != 0) - gravity = PANGO_GRAVITY_EAST; - addattr(p, start, end, - pango_attr_gravity_new(gravity)); + vertval = 1; + doOpenType("vert", vertval, &op); + doOpenType("vrt2", vertval, &op); + doOpenType("vkrn", vertval, &op); + doOpenType("vrtr", vertval, &op); break; case uiAttributeUnderline: - switch (spec->Value) { - case uiDrawUnderlineStyleNone: - underline = PANGO_UNDERLINE_NONE; - break; - case uiDrawUnderlineStyleSingle: - underline = PANGO_UNDERLINE_SINGLE; - break; - case uiDrawUnderlineStyleDouble: - underline = PANGO_UNDERLINE_DOUBLE; - break; - case uiDrawUnderlineStyleSuggestion: - underline = PANGO_UNDERLINE_ERROR; - break; - } - addattr(p, start, end, - pango_attr_underline_new(underline)); + ensureEffectsInRange(p, start, end, [=](textDrawingEffect *t) { + t->hasUnderline = true; + t->u = (uiDrawUnderlineStyle) (spec->Value); + }); + // mark that we have an underline; otherwise, DirectWrite will never call our custom renderer's DrawUnderline() method + hasUnderline = FALSE; + if ((uiDrawUnderlineStyle) (spec->Value) != uiDrawUnderlineStyleNone) + hasUnderline = TRUE; + hr = p->layout->SetUnderline(hasUnderline, range); + if (hr != S_OK) + logHRESULT(L"error applying underline attribute", hr); break; case uiAttributeUnderlineColor: switch (spec->Value) { case uiDrawUnderlineColorCustom: - addattr(p, start, end, - pango_attr_underline_color_new( - (guint16) (spec->R * 65535.0), - (guint16) (spec->G * 65535.0), - (guint16) (spec->B * 65535.0))); + ensureEffectsInRange(p, start, end, [=](textDrawingEffect *t) { + t->hasUnderlineColor = true; + t->ur = spec->R; + t->ug = spec->G; + t->ub = spec->B; + t->ua = spec->A; + }); break; + // TODO see if Microsoft has any standard colors for this case uiDrawUnderlineColorSpelling: // TODO GtkTextView style property error-underline-color - addattr(p, start, end, - pango_attr_underline_color_new(65535, 0, 0)); + ensureEffectsInRange(p, start, end, [=](textDrawingEffect *t) { + t->hasUnderlineColor = true; + t->ur = 1.0; + t->ug = 0.0; + t->ub = 0.0; + t->ua = 1.0; + }); break; case uiDrawUnderlineColorGrammar: - // TODO find a more appropriate color - addattr(p, start, end, - pango_attr_underline_color_new(0, 65535, 0)); + ensureEffectsInRange(p, start, end, [=](textDrawingEffect *t) { + t->hasUnderlineColor = true; + t->ur = 0.0; + t->ug = 1.0; + t->ub = 0.0; + t->ua = 1.0; + }); break; case uiDrawUnderlineColorAuxiliary: - // TODO find a more appropriate color - addattr(p, start, end, - pango_attr_underline_color_new(0, 0, 65535)); + ensureEffectsInRange(p, start, end, [=](textDrawingEffect *t) { + t->hasUnderlineColor = true; + t->ur = 0.0; + t->ug = 0.0; + t->ub = 1.0; + t->ua = 1.0; + }); break; } break; -#endif // locale names are specified as BCP 47: https://msdn.microsoft.com/en-us/library/windows/desktop/dd373814(v=vs.85).aspx https://www.ietf.org/rfc/rfc4646.txt case uiAttributeLanguage: localeName = toUTF16((char *) (spec->Value)); @@ -321,14 +300,7 @@ static void applyAndFreeFeatureAttributes(struct foreachParams *p) delete p->features; } -#if 0 /* TODO */ -static void unrefClosure(gpointer data) -{ - g_closure_unref((GClosure *) data); -} -#endif - -void attrstrToIDWriteTextLayoutAttrs(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout/*TODO, GPtrArray **backgroundClosures*/) +void attrstrToIDWriteTextLayoutAttrs(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout, std::vector **backgroundFuncs) { struct foreachParams fep; @@ -336,35 +308,9 @@ void attrstrToIDWriteTextLayoutAttrs(uiDrawTextLayoutParams *p, IDWriteTextLayou fep.layout = layout; fep.effects = new std::map; fep.features = new std::map; -//TODO fep.backgroundClosures = g_ptr_array_new_with_free_func(unrefClosure); + fep.backgroundFuncs = new std::vector; uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); applyAndFreeEffectsAttributes(&fep); applyAndFreeFeatureAttributes(&fep); -//TODO *backgroundClosures = fep.backgroundClosures; + *backgroundFuncs = fep.backgroundFuncs; } - -#if 0 -void invokeBackgroundClosure(GClosure *closure, uiDrawContext *c, uiDrawTextLayout *layout, double x, double y) -{ - GValue values[4] = { - // the zero-initialization is needed for g_value_init() to work - G_VALUE_INIT, - G_VALUE_INIT, - G_VALUE_INIT, - G_VALUE_INIT, - }; - - g_value_init(values + 0, G_TYPE_POINTER); - g_value_set_pointer(values + 0, c); - g_value_init(values + 1, G_TYPE_POINTER); - g_value_set_pointer(values + 1, layout); - g_value_init(values + 2, G_TYPE_DOUBLE); - g_value_set_double(values + 2, x); - g_value_init(values + 3, G_TYPE_DOUBLE); - g_value_set_double(values + 3, y); - g_closure_invoke(closure, - NULL, - 4, values, - NULL); -} -#endif diff --git a/windows/draw.hpp b/windows/draw.hpp index adc68294..f3a8d387 100644 --- a/windows/draw.hpp +++ b/windows/draw.hpp @@ -16,7 +16,8 @@ extern ID2D1PathGeometry *pathGeometry(uiDrawPath *p); extern void m2d(uiDrawMatrix *m, D2D1_MATRIX_3X2_F *d); // attrstr.cpp -extern void attrstrToIDWriteTextLayoutAttrs(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout/*TODO, GPtrArray **backgroundClosures*/); +typedef std::function backgroundFunc; +extern void attrstrToIDWriteTextLayoutAttrs(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout, std::vector **backgroundFuncs); // drawtext.cpp class textDrawingEffect : public IUnknown { diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 31d6daee..3510a379 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -6,12 +6,14 @@ // - consider the warnings about antialiasing in the PadWrite sample // - if that's not a problem, do we have overlapping rects in the hittest sample? I can't tell... // - what happens if any nLines == 0? +// - paragraph alignment is subject to RTL mirroring; see if it is on other platforms // TODO verify our renderer is correct, especially with regards to snapping struct uiDrawTextLayout { IDWriteTextFormat *format; IDWriteTextLayout *layout; + std::vector *backgroundFuncs; UINT32 nLines; struct lineInfo *lineInfo; // for converting DirectWrite indices from/to byte offsets @@ -196,7 +198,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) if (hr != S_OK) logHRESULT(L"error setting IDWriteTextLayout max layout width", hr); - attrstrToIDWriteTextLayoutAttrs(p, tl->layout); + attrstrToIDWriteTextLayoutAttrs(p, tl->layout, &(tl->backgroundFuncs)); computeLineInfo(tl); @@ -214,6 +216,7 @@ void uiDrawFreeTextLayout(uiDrawTextLayout *tl) uiFree(tl->u16tou8); uiFree(tl->u8tou16); uiFree(tl->lineInfo); + delete tl->backgroundFuncs; tl->layout->Release(); tl->format->Release(); uiFree(tl); @@ -373,17 +376,118 @@ public: virtual HRESULT STDMETHODCALLTYPE DrawStrikethrough(void *clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, const DWRITE_STRIKETHROUGH *strikethrough, IUnknown *clientDrawingEffect) { - textDrawingEffect *t = (textDrawingEffect *) clientDrawingEffect; - - // TODO - return S_OK; + // we don't support strikethrough + return E_UNEXPECTED; } virtual HRESULT DrawUnderline(void *clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, const DWRITE_UNDERLINE *underline, IUnknown *clientDrawingEffect) { textDrawingEffect *t = (textDrawingEffect *) clientDrawingEffect; + ID2D1SolidColorBrush *brush; + D2D1_RECT_F rect; + D2D1::Matrix3x2F pixeltf; + FLOAT dpix, dpiy; + D2D1_POINT_2F pt; - // TODO + if (underline == NULL) + return E_POINTER; + if (t == NULL) // we can only get here through an underline + return E_UNEXPECTED; + brush = this->black; + if (t->hasUnderlineColor) { + HRESULT hr; + + hr = mkSolidBrush(this->rt, t->ur, t->ug, t->ub, t->ua, &brush); + if (hr != S_OK) + return hr; + } else if (t->hasColor) { + // TODO formalize this rule + HRESULT hr; + + hr = mkSolidBrush(this->rt, t->r, t->g, t->b, t->a, &brush); + if (hr != S_OK) + return hr; + } + rect.left = baselineOriginX; + rect.top = baselineOriginY + underline->offset; + rect.right = rect.left + underline->width; + rect.bottom = rect.top + underline->thickness; + switch (t->u) { + case uiDrawUnderlineStyleSingle: + this->rt->FillRectangle(&rect, brush); + break; + case uiDrawUnderlineStyleDouble: + // TODO do any of the matrix methods return errors? + // TODO standardize double-underline shape across platforms? wavy underline shape? + this->rt->GetTransform(&pixeltf); + this->rt->GetDpi(&dpix, &dpiy); + pixeltf = pixeltf * D2D1::Matrix3x2F::Scale(dpix / 96, dpiy / 96); + pt.x = 0; + pt.y = rect.top; + pt = pixeltf.TransformPoint(pt); + rect.top = (FLOAT) ((int) (pt.y + 0.5)); + pixeltf.Invert(); + pt = pixeltf.TransformPoint(pt); + rect.top = pt.y; + // first line + rect.top -= underline->thickness; + // and it seems we need to recompute this + rect.bottom = rect.top + underline->thickness; + this->rt->FillRectangle(&rect, brush); + // second line + rect.top += 2 * underline->thickness; + rect.bottom = rect.top + underline->thickness; + this->rt->FillRectangle(&rect, brush); + break; + case uiDrawUnderlineStyleSuggestion: + { // TODO get rid of the extra block + // TODO properly clean resources on failure + // TODO use fully qualified C overloads for all methods + // TODO ensure all methods properly have errors handled + ID2D1PathGeometry *path; + ID2D1GeometrySink *sink; + double amplitude, period, xOffset, yOffset; + double t; + bool first = true; + HRESULT hr; + + hr = d2dfactory->CreatePathGeometry(&path); + if (hr != S_OK) + return hr; + hr = path->Open(&sink); + if (hr != S_OK) + return hr; + amplitude = underline->thickness; + period = 5 * underline->thickness; + xOffset = baselineOriginX; + yOffset = baselineOriginY + underline->offset; + for (t = 0; t < underline->width; t++) { + double x, angle, y; + D2D1_POINT_2F pt; + + x = t + xOffset; + angle = 2 * uiPi * fmod(x, period) / period; + y = amplitude * sin(angle) + yOffset; + pt.x = x; + pt.y = y; + if (first) { + sink->BeginFigure(pt, D2D1_FIGURE_BEGIN_HOLLOW); + first = false; + } else + sink->AddLine(pt); + } + sink->EndFigure(D2D1_FIGURE_END_OPEN); + hr = sink->Close(); + if (hr != S_OK) + return hr; + sink->Release(); + this->rt->DrawGeometry(path, brush, underline->thickness); + path->Release(); + } + break; + } + if (t->hasUnderlineColor || t->hasColor) + brush->Release(); return S_OK; } }; @@ -396,6 +500,10 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) textRenderer *renderer; HRESULT hr; + // TODO the "any combination of the above" one isn't drawn in the right place but the "multiple backgrounds" one is (at least for when there's a line break; TODO) + for (const auto &f : *(tl->backgroundFuncs)) + f(c, tl, x, y); + // TODO document that fully opaque black is the default text color; figure out whether this is upheld in various scenarios on other platforms // TODO figure out if this needs to be cleaned out black = mustMakeSolidBrush(c->rt, 0.0, 0.0, 0.0, 1.0); From 0f2b2b1fe5d427582f210f40a3e2140ab9dd2f70 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 22 Feb 2017 21:46:15 -0500 Subject: [PATCH 0693/1329] Fixed background drawing on Windows. --- windows/attrstr.cpp | 5 ++++- windows/drawtext.cpp | 1 - 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index c6d366c5..6c96a8a4 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -110,12 +110,15 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t struct foreachParams *p = (struct foreachParams *) data; DWRITE_TEXT_RANGE range; WCHAR *wfamily; + size_t ostart, oend; BOOL hasUnderline; uint32_t vertval; WCHAR *localeName; struct otParam op; HRESULT hr; + ostart = start; + oend = end; start = attrstrUTF8ToUTF16(s, start); end = attrstrUTF8ToUTF16(s, end); range.startPosition = start; @@ -170,7 +173,7 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t break; case uiAttributeBackground: p->backgroundFuncs->push_back( - mkBackgroundFunc(start, end, + mkBackgroundFunc(ostart, oend, spec->R, spec->G, spec->B, spec->A)); break; case uiAttributeVerticalForms: diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 3510a379..67ec0a47 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -500,7 +500,6 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) textRenderer *renderer; HRESULT hr; - // TODO the "any combination of the above" one isn't drawn in the right place but the "multiple backgrounds" one is (at least for when there's a line break; TODO) for (const auto &f : *(tl->backgroundFuncs)) f(c, tl, x, y); From e5f3646fcf9605b6ac5fbdba1001e8981e00df39 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 23 Feb 2017 21:37:41 -0500 Subject: [PATCH 0694/1329] Attempts to align the vertical glyphs with the orizontal baseline on OS X with Core Text. This is gonna be harder... --- darwin/attrstr.m | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 154adbe4..8b17da15 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -220,10 +220,23 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t Block_release(block); break; case uiAttributeVerticalForms: - if (spec->Value != 0) + if (spec->Value != 0) { CFAttributedStringSetAttribute(p->mas, range, kCTVerticalFormsAttributeName, kCFBooleanTrue); - else +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassRoman); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicCentered); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicLow); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicHigh); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassHanging); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassMath); + } else { CFAttributedStringSetAttribute(p->mas, range, kCTVerticalFormsAttributeName, kCFBooleanFalse); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassRoman); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicCentered); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicLow); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicHigh); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassHanging); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassMath); + } break; case uiAttributeUnderline: switch (spec->Value) { From be56ec3626a1937c74cb36f390023d4999611e6b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 24 Feb 2017 01:23:47 -0500 Subject: [PATCH 0695/1329] Removed the vertical forms attribute. This is a compatiblity nightmare. --- _future/verticaltext/README | 13 +++++++++++++ _future/verticaltext/attrstr_darwin.m | 19 +++++++++++++++++++ _future/verticaltext/attrstr_unix.c | 9 +++++++++ _future/verticaltext/attrstr_windows.cpp | 15 +++++++++++++++ _future/verticaltext/common_attrlist.c | 2 ++ _future/verticaltext/drawtext_example.c | 18 ++++++++++++++++++ _future/verticaltext/ui.h | 2 ++ common/attrlist.c | 3 +-- darwin/attrstr.m | 19 ------------------- examples/drawtext/attributes.c | 19 ------------------- examples/drawtext/main.c | 12 ------------ ui_attrstr.h | 2 -- unix/attrstr.c | 8 -------- windows/attrstr.cpp | 14 -------------- 14 files changed, 79 insertions(+), 76 deletions(-) create mode 100644 _future/verticaltext/README create mode 100644 _future/verticaltext/attrstr_darwin.m create mode 100644 _future/verticaltext/attrstr_unix.c create mode 100644 _future/verticaltext/attrstr_windows.cpp create mode 100644 _future/verticaltext/common_attrlist.c create mode 100644 _future/verticaltext/drawtext_example.c create mode 100644 _future/verticaltext/ui.h diff --git a/_future/verticaltext/README b/_future/verticaltext/README new file mode 100644 index 00000000..d60f3629 --- /dev/null +++ b/_future/verticaltext/README @@ -0,0 +1,13 @@ +Proper vertical text support in uiDrawTextLayout was removed because DirectWrite doesn't add this until Windows 8.1 (unless I drop IDWriteTextLayout and do the script analysis myself; TODO consider this possibility). + +On OS X, setting the vertical forms attribute stacks non-vertical scripts in vertical text (rotates each individual glyph) with Core Text, whereas everything else — including Cocoa's text system — rotates entire non-vertical strings. Not sure what to do about this except manually detect which characters to apply the attribute to: +http://www.unicode.org/notes/tn22/RobustVerticalLayout.pdf +http://www.unicode.org/Public/vertical/revision-17/VerticalOrientation-17.txt + +In addition, with Core Text, the vertical forms attribute vertically centers the vertical glyphs on the bhorizontal baseline, rather than flush with the text. Using the baseline class attribute doesn't seem to work. + +If readded, this will need to be a layout-wide setting, not a per-character setting. Pango works right this way; the current Pango code doesn't seem to work. + +More links: +https://www.w3.org/TR/2012/NOTE-jlreq-20120403/#line-composition +https://www.w3.org/TR/REC-CSS2/notes.html diff --git a/_future/verticaltext/attrstr_darwin.m b/_future/verticaltext/attrstr_darwin.m new file mode 100644 index 00000000..f56adc7f --- /dev/null +++ b/_future/verticaltext/attrstr_darwin.m @@ -0,0 +1,19 @@ + case uiAttributeVerticalForms: + if (spec->Value != 0) { + CFAttributedStringSetAttribute(p->mas, range, kCTVerticalFormsAttributeName, kCFBooleanTrue); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassRoman); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicCentered); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicLow); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicHigh); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassHanging); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassMath); + } else { + CFAttributedStringSetAttribute(p->mas, range, kCTVerticalFormsAttributeName, kCFBooleanFalse); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassRoman); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicCentered); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicLow); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicHigh); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassHanging); +// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassMath); + } + break; diff --git a/_future/verticaltext/attrstr_unix.c b/_future/verticaltext/attrstr_unix.c new file mode 100644 index 00000000..e5e13617 --- /dev/null +++ b/_future/verticaltext/attrstr_unix.c @@ -0,0 +1,9 @@ + PangoGravity gravity; + + case uiAttributeVerticalForms: + gravity = PANGO_GRAVITY_SOUTH; + if (spec->Value != 0) + gravity = PANGO_GRAVITY_EAST; + addattr(p, start, end, + pango_attr_gravity_new(gravity)); + break; diff --git a/_future/verticaltext/attrstr_windows.cpp b/_future/verticaltext/attrstr_windows.cpp new file mode 100644 index 00000000..88369217 --- /dev/null +++ b/_future/verticaltext/attrstr_windows.cpp @@ -0,0 +1,15 @@ + uint32_t vertval; + + case uiAttributeVerticalForms: + // LONGTERM 8 and/or 8.1 add other methods for vertical text + op.p = p; + op.start = start; + op.end = end; + vertval = 0; + if (spec->Value != 0) + vertval = 1; + doOpenType("vert", vertval, &op); + doOpenType("vrt2", vertval, &op); + doOpenType("vkrn", vertval, &op); + doOpenType("vrtr", vertval, &op); + break; diff --git a/_future/verticaltext/common_attrlist.c b/_future/verticaltext/common_attrlist.c new file mode 100644 index 00000000..0b9ea921 --- /dev/null +++ b/_future/verticaltext/common_attrlist.c @@ -0,0 +1,2 @@ + case uiAttributeVerticalForms: + return boolsEqual(attr, spec); diff --git a/_future/verticaltext/drawtext_example.c b/_future/verticaltext/drawtext_example.c new file mode 100644 index 00000000..669bdaa8 --- /dev/null +++ b/_future/verticaltext/drawtext_example.c @@ -0,0 +1,18 @@ + next = "vertical glyph forms"; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeVerticalForms; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " (which you can draw rotated for proper vertical text; for instance, "); + next = "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A"; + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeVerticalForms; + spec.Value = 1; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, ")"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); diff --git a/_future/verticaltext/ui.h b/_future/verticaltext/ui.h new file mode 100644 index 00000000..98903ee9 --- /dev/null +++ b/_future/verticaltext/ui.h @@ -0,0 +1,2 @@ + // TODO rename to uiAttributeVertical? + uiAttributeVerticalForms, // 0 = off, 1 = on diff --git a/common/attrlist.c b/common/attrlist.c index 3bf8caff..20aaf97f 100644 --- a/common/attrlist.c +++ b/common/attrlist.c @@ -342,11 +342,10 @@ static int specsIdentical(struct attr *attr, uiAttributeSpec *spec) attr->spec.G == spec->G && attr->spec.B == spec->B && attr->spec.A == spec->A; - case uiAttributeVerticalForms: - return boolsEqual(attr, spec); case uiAttributeLanguage: return asciiStringsEqualCaseFold((char *) (attr->spec.Value), (char *) (spec->Value)); // TODO + // TODO use boolsEqual() on boolean features } // handles the rest return attr->spec.Value == spec->Value; diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 8b17da15..f4644b37 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -219,25 +219,6 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t [p->backgroundBlocks addObject:block]; Block_release(block); break; - case uiAttributeVerticalForms: - if (spec->Value != 0) { - CFAttributedStringSetAttribute(p->mas, range, kCTVerticalFormsAttributeName, kCFBooleanTrue); -// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassRoman); -// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicCentered); -// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicLow); -// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicHigh); -// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassHanging); -// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassMath); - } else { - CFAttributedStringSetAttribute(p->mas, range, kCTVerticalFormsAttributeName, kCFBooleanFalse); -// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassRoman); -// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicCentered); -// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicLow); -// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicHigh); -// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassHanging); -// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassMath); - } - break; case uiAttributeUnderline: switch (spec->Value) { case uiDrawUnderlineStyleNone: diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index 24dd4a19..b4a697a8 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -93,25 +93,6 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, ", "); - next = "vertical glyph forms"; - start = uiAttributedStringLen(attrstr); - end = start + strlen(next); - uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeVerticalForms; - spec.Value = 1; - uiAttributedStringSetAttribute(attrstr, &spec, start, end); - uiAttributedStringAppendUnattributed(attrstr, " (which you can draw rotated for proper vertical text; for instance, "); - next = "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A"; - start = uiAttributedStringLen(attrstr); - end = start + strlen(next); - uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeVerticalForms; - spec.Value = 1; - uiAttributedStringSetAttribute(attrstr, &spec, start, end); - uiAttributedStringAppendUnattributed(attrstr, ")"); - - uiAttributedStringAppendUnattributed(attrstr, ", "); - next = "multiple"; start = uiAttributedStringLen(attrstr); end = start + strlen(next); diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c index 22b532fe..822382d0 100644 --- a/examples/drawtext/main.c +++ b/examples/drawtext/main.c @@ -1,18 +1,6 @@ // 17 january 2017 #include "drawtext.h" -// okay everything is definitely bugged in the OS X code -// - occasional segfaults on startup -// - very rare size attributes in the attributed string example don't terminate for a while, making everything big -// - very very rare trace/bpt faults on startup -/* -objc[14827]: autorelease pool page 0x7feeab88b000 corrupted - magic 0xe000007f 0xeea9f2df 0x0000007f 0xeea9f2e0 - should be 0xa1a1a1a1 0x4f545541 0x454c4552 0x21455341 - pthread 0x0 - should be 0x7fff727a1000 -*/ - static uiWindow *mainwin; static uiBox *box; static uiCombobox *exampleList; diff --git a/ui_attrstr.h b/ui_attrstr.h index 5e4fe8a8..7bc7cd1a 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -10,8 +10,6 @@ _UI_ENUM(uiAttribute) { uiAttributeStretch, uiAttributeColor, // use R, G, B, A uiAttributeBackground, // use R, G, B, A - // TODO rename to uiAttributeVertical? - uiAttributeVerticalForms, // 0 = off, 1 = on // TODO kerning amount // OS X: kCTKernAttributeName diff --git a/unix/attrstr.c b/unix/attrstr.c index 8882f3ac..cf5bc509 100644 --- a/unix/attrstr.c +++ b/unix/attrstr.c @@ -141,7 +141,6 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t { struct foreachParams *p = (struct foreachParams *) data; GClosure *closure; - PangoGravity gravity; PangoUnderline underline; PangoLanguage *lang; struct otParam op; @@ -183,13 +182,6 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t spec->R, spec->G, spec->B, spec->A); g_ptr_array_add(p->backgroundClosures, closure); break; - case uiAttributeVerticalForms: - gravity = PANGO_GRAVITY_SOUTH; - if (spec->Value != 0) - gravity = PANGO_GRAVITY_EAST; - addattr(p, start, end, - pango_attr_gravity_new(gravity)); - break; case uiAttributeUnderline: switch (spec->Value) { case uiDrawUnderlineStyleNone: diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index 6c96a8a4..d0a4b3cc 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -112,7 +112,6 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t WCHAR *wfamily; size_t ostart, oend; BOOL hasUnderline; - uint32_t vertval; WCHAR *localeName; struct otParam op; HRESULT hr; @@ -176,19 +175,6 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t mkBackgroundFunc(ostart, oend, spec->R, spec->G, spec->B, spec->A)); break; - case uiAttributeVerticalForms: - // LONGTERM 8 and/or 8.1 add other methods for vertical text - op.p = p; - op.start = start; - op.end = end; - vertval = 0; - if (spec->Value != 0) - vertval = 1; - doOpenType("vert", vertval, &op); - doOpenType("vrt2", vertval, &op); - doOpenType("vkrn", vertval, &op); - doOpenType("vrtr", vertval, &op); - break; case uiAttributeUnderline: ensureEffectsInRange(p, start, end, [=](textDrawingEffect *t) { t->hasUnderline = true; From f65fc1f25eb1916d3003fde0c8cb702dc13e30d4 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 24 Feb 2017 10:29:08 -0500 Subject: [PATCH 0696/1329] Fixed text hit-testing on OS X. --- darwin/drawtext.m | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index c5736bf9..c0123117 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -262,7 +262,8 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *p ln = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, i); // note: according to the docs, we pass a y of 0 for this since the is the baseline of that line (the point is relative to the line) - // TODO is x relative to the line origin? + // note: x is relative to the line origin + x -= tl->lineMetrics[*line].X; p = CTLineGetStringIndexForPosition(ln, CGPointMake(x, 0)); if (p == kCFNotFound) { // TODO @@ -284,7 +285,9 @@ double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int if (pos < range.location || pos > (range.location + range.length)) return -1; // no point in checking the return; we already validated everything and 0 is a valid return for the first index :/ - return CTLineGetOffsetForStringIndex(lr, pos, NULL); + // note: the result is relative to the line origin (TODO find documentation to support this) + // TODO document that these functions do this + return CTLineGetOffsetForStringIndex(lr, pos, NULL) + tl->lineMetrics[line].X; } void caretDrawParams(uiDrawContext *c, double height, struct caretDrawParams *p) From 074350bf99dec38ad2b2e36b3ed1596b5a88e043 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 24 Feb 2017 11:49:32 -0500 Subject: [PATCH 0697/1329] Removed the TODOs from uiAttributes for attributes we don't yet support. That'll all come later. --- _future/otherattributes/ui.h | 190 +++++++++++++++++++++++++++++++++++ _future/verticaltext/README | 1 + ui_attrstr.h | 50 +-------- 3 files changed, 192 insertions(+), 49 deletions(-) create mode 100644 _future/otherattributes/ui.h diff --git a/_future/otherattributes/ui.h b/_future/otherattributes/ui.h new file mode 100644 index 00000000..89a0c5a1 --- /dev/null +++ b/_future/otherattributes/ui.h @@ -0,0 +1,190 @@ +_UI_ENUM(uiAttribute) { + uiAttributeFamily, + uiAttributeSize, // use Double + uiAttributeWeight, + uiAttributeItalic, + uiAttributeStretch, + uiAttributeColor, // use R, G, B, A + uiAttributeBackground, // use R, G, B, A + + // TODO kerning amount + // OS X: kCTKernAttributeName + // > 0: farther (TODO from advance or standard kerning?) + // == 0: no kerning + // < 0: closer (TODO same) + // undefined: standard kerning + // Pango: pango_attr_letter_spacing_new() + // parameter meaning unspecified + // Windows: requires Platform Update, SetLetterSpacing() + // parameter meaning unspecified + + uiAttributeUnderline, // enum uiDrawUnderlineStyle + // TODO what is the color in the case we don't specify it, black or the text color? + uiAttributeUnderlineColor, // enum uiDrawUnderlineColor + + // TODO kCTSuperscriptAttributeName vs below + // all it does is set the below attribute so + + // TODO kCTBaselineClassAttributeName, kCTBaselineInfoAttributeName, kCTBaselineReferenceInfoAttributeName + + // TODO strikethroughs? (pango yes, directwrite yes, os x no) + // TODO baseline offsets? (pango yes) + // TODO size scales? (pango yes) + // TODO fallbacks (pango: enable or disable) + + // TODO document that this will also enable language-specific font features (TODO on DirectWrite too?) + // TODO document that this should be strict BCP 47 form (A-Z, a-z, 0-9, and -) for maximum compatibility + uiAttributeLanguage, // BCP 47 string + + // These attributes represent typographic features. Each feature + // is a separate attribute, to make composition easier. The + // availability of for each attribute are defined by the font; the + // default values are defined by the font and/or by the OS. + // + // A note about features whose parameter is an enumeration: + // OS X defines typographic features using the AAT specification + // and converts to OpenType internally when needed, whereas + // other platforms use OpenType directly. OpenType is less + // precise about what each enumeration value means than AAT + // is, so enumeration values do not necessarily represent what + // OS X expects with all fonts. In cases where they do, libui + // provides an enumeration type to use. Otherwise, the AAT + // enumeration values are provided in comments for + // documentation purposes. + + // TODO kAllTypographicFeaturesType + + // AAT calls these "common ligatures" + uiAttributeStandardLigatures, // 0 = off, 1 = on + uiAttributeRequiredLigatures, // 0 = off, 1 = on + // AAT calls these "rare ligatures" + uiAttributeDiscretionaryLigatures, // 0 = off, 1 = on + uiAttributeContextualLigatures, // 0 = off, 1 = on + uiAttributeHistoricalLigatures, // 0 = off, 1 = on + + // TODO uiAttributeCursiveConnection, // 0 = none, 1 = some, 2 = all + + uiAttributeUnicase, // 0 = off, 1 = on + + // TODO uiAttributeLinguisticRearrangement, // 0 = off, 1 = on + + // TODO rename this + uiAttributeNumberSpacings, // enum uiAttributeNumberSpacing + + // TODO kSmartSwashType, falt and jalt + + // TODO kDiacriticsType + + uiAttributeSuperscripts, // enum uiAttributeSuperscript + + uiAttributeFractionForms, // enum uiAttributeFractionForm + + uiAttributeSlashedZero, // 0 = off, 1 = on + + uiAttributeMathematicalGreek, // 0 = off, 1 = on + + // AAT defines the following values: + // 0 = none + // 1 = dingbats + // 2 = pi characters + // 3 = fleurons + // 4 = decorative borders + // 5 = international symbols + // 6 = mathematical symbols + // OpenType says alphanumeric characters must(? TODO) have one form each and the bullet character U+2022 (•) can have many + uiAttributeOrnamentalForms, // an integer from 0 to a font-specified upper bound + // TODO provide a function to get the upper bound? + + // AAT calls this "character alternatives" and defines the + // following values: + // 0 = none + // OpenType calls this "access all alternates". + // TODO doesn't OpenType do the same about 0? + uiAttributeSpecificCharacterForm, // an integer from 0 to a font-specified upper bound + // TODO provide a function to get the upper bound? + + uiAttributeTitlingCapitalForms, // 0 = off, 1 = on + + // AAT calls these "character shapes" + uiAttributeHanCharacterForms, // enum uiAttributeHanCharacterForm + + // OpenType calls these "old-style" + uiAttributeLowercaseNumbers, // 0 = off, 1 = on + + // TODO kTextSpacingType + // see kKanaSpacingType below + + uiAttributeHanjaToHangul, // 0 = off, 1 = on + + // AAT defines the following values: + // 0 = none + // 1 = box + // 2 = rounded box + // 3 = circle + // 4 = inverted circle + // 5 = parentheses + // 6 = period + // 7 = roman numeral + // 8 = diamond + // 9 = inverted box + // 10 = inverted rounded box + // TODO rename to AnnotatedForms? + uiAttributeGlyphAnnotations, // an integer from 0 to a font-specified upper bound + // TODO provide a function to get the upper bound? + + // TODO kKanaSpacingType + // TODO kIdeographicSpacingType + // can they be provided independently of kTextSpacingType? Core Text doesn't seem to + + // TODO kUnicodeDecompositionType + + uiAttributeRubyKanaForms, // 0 = off, 1 = on + + // TODO kCJKVerticalRomanPlacementType + // this is 'valt' in OpenType but I don't know if I want to make it selectable or not + + uiAttributeCJKRomansToItalics, // 0 = off, 1 = on + + // AAT calls this "case-sensitive layout" + uiAttributeCaseSensitiveForms, // 0 = off, 1 = on + // AAT: this is called "case-sensitive spacing" + uiAttributeCapitalSpacing, // 0 = off, 1 = on + + uiAttributeAlternateHorizontalKana, // 0 = off, 1 = on + uiAttributeAlternateVerticalKana, // 0 = off, 1 = on + + // TODO "Alternate"? unify all this + // TODO document that these are guaranteed to be consecutive + uiAttributeStylisticAlternative1, // 0 = off, 1 = on + uiAttributeStylisticAlternative2, // 0 = off, 1 = on + uiAttributeStylisticAlternative3, // 0 = off, 1 = on + uiAttributeStylisticAlternative4, // 0 = off, 1 = on + uiAttributeStylisticAlternative5, // 0 = off, 1 = on + uiAttributeStylisticAlternative6, // 0 = off, 1 = on + uiAttributeStylisticAlternative7, // 0 = off, 1 = on + uiAttributeStylisticAlternative8, // 0 = off, 1 = on + uiAttributeStylisticAlternative9, // 0 = off, 1 = on + uiAttributeStylisticAlternative10, // 0 = off, 1 = on + uiAttributeStylisticAlternative11, // 0 = off, 1 = on + uiAttributeStylisticAlternative12, // 0 = off, 1 = on + uiAttributeStylisticAlternative13, // 0 = off, 1 = on + uiAttributeStylisticAlternative14, // 0 = off, 1 = on + uiAttributeStylisticAlternative15, // 0 = off, 1 = on + uiAttributeStylisticAlternative16, // 0 = off, 1 = on + uiAttributeStylisticAlternative17, // 0 = off, 1 = on + uiAttributeStylisticAlternative18, // 0 = off, 1 = on + uiAttributeStylisticAlternative19, // 0 = off, 1 = on + uiAttributeStylisticAlternative20, // 0 = off, 1 = on + + uiAttributeContextualAlternates, // 0 = off, 1 = on + uiAttributeSwashes, // 0 = off, 1 = on + uiAttributeContextualSwashes, // 0 = off, 1 = on + + uiAttributeLowercaseCapForms, // enum uiAttributeCapForm + uiAttributeUppercaseCapForms, // enum uiAttributeCapForm + + // TODO kCJKRomanSpacingType + + // TODO uiAttributeSystem, (this might not be doable with DirectWrite) + // TODO uiAttributeCustom, +}; diff --git a/_future/verticaltext/README b/_future/verticaltext/README index d60f3629..1bac02e5 100644 --- a/_future/verticaltext/README +++ b/_future/verticaltext/README @@ -5,6 +5,7 @@ http://www.unicode.org/notes/tn22/RobustVerticalLayout.pdf http://www.unicode.org/Public/vertical/revision-17/VerticalOrientation-17.txt In addition, with Core Text, the vertical forms attribute vertically centers the vertical glyphs on the bhorizontal baseline, rather than flush with the text. Using the baseline class attribute doesn't seem to work. +TODO investigate kCJKVerticalRomanPlacementType If readded, this will need to be a layout-wide setting, not a per-character setting. Pango works right this way; the current Pango code doesn't seem to work. diff --git a/ui_attrstr.h b/ui_attrstr.h index 7bc7cd1a..3206efe4 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -11,31 +11,10 @@ _UI_ENUM(uiAttribute) { uiAttributeColor, // use R, G, B, A uiAttributeBackground, // use R, G, B, A - // TODO kerning amount - // OS X: kCTKernAttributeName - // > 0: farther (TODO from advance or standard kerning?) - // == 0: no kerning - // < 0: closer (TODO same) - // undefined: standard kerning - // Pango: pango_attr_letter_spacing_new() - // parameter meaning unspecified - // Windows: requires Platform Update, SetLetterSpacing() - // parameter meaning unspecified - uiAttributeUnderline, // enum uiDrawUnderlineStyle - // TODO what is the color in the case we don't specify it, black or the text color? + // TODO ensure the color in the case we don't specify it is the text color? uiAttributeUnderlineColor, // enum uiDrawUnderlineColor - // TODO kCTSuperscriptAttributeName vs below - // all it does is set the below attribute so - - // TODO kCTBaselineClassAttributeName, kCTBaselineInfoAttributeName, kCTBaselineReferenceInfoAttributeName - - // TODO strikethroughs? (pango yes, directwrite yes, os x no) - // TODO baseline offsets? (pango yes) - // TODO size scales? (pango yes) - // TODO fallbacks (pango: enable or disable) - // TODO document that this will also enable language-specific font features (TODO on DirectWrite too?) // TODO document that this should be strict BCP 47 form (A-Z, a-z, 0-9, and -) for maximum compatibility uiAttributeLanguage, // BCP 47 string @@ -56,8 +35,6 @@ _UI_ENUM(uiAttribute) { // enumeration values are provided in comments for // documentation purposes. - // TODO kAllTypographicFeaturesType - // AAT calls these "common ligatures" uiAttributeStandardLigatures, // 0 = off, 1 = on uiAttributeRequiredLigatures, // 0 = off, 1 = on @@ -66,19 +43,11 @@ _UI_ENUM(uiAttribute) { uiAttributeContextualLigatures, // 0 = off, 1 = on uiAttributeHistoricalLigatures, // 0 = off, 1 = on - // TODO uiAttributeCursiveConnection, // 0 = none, 1 = some, 2 = all - uiAttributeUnicase, // 0 = off, 1 = on - // TODO uiAttributeLinguisticRearrangement, // 0 = off, 1 = on - // TODO rename this uiAttributeNumberSpacings, // enum uiAttributeNumberSpacing - // TODO kSmartSwashType, falt and jalt - - // TODO kDiacriticsType - uiAttributeSuperscripts, // enum uiAttributeSuperscript uiAttributeFractionForms, // enum uiAttributeFractionForm @@ -115,9 +84,6 @@ _UI_ENUM(uiAttribute) { // OpenType calls these "old-style" uiAttributeLowercaseNumbers, // 0 = off, 1 = on - // TODO kTextSpacingType - // see kKanaSpacingType below - uiAttributeHanjaToHangul, // 0 = off, 1 = on // AAT defines the following values: @@ -136,17 +102,8 @@ _UI_ENUM(uiAttribute) { uiAttributeGlyphAnnotations, // an integer from 0 to a font-specified upper bound // TODO provide a function to get the upper bound? - // TODO kKanaSpacingType - // TODO kIdeographicSpacingType - // can they be provided independently of kTextSpacingType? Core Text doesn't seem to - - // TODO kUnicodeDecompositionType - uiAttributeRubyKanaForms, // 0 = off, 1 = on - // TODO kCJKVerticalRomanPlacementType - // this is 'valt' in OpenType but I don't know if I want to make it selectable or not - uiAttributeCJKRomansToItalics, // 0 = off, 1 = on // AAT calls this "case-sensitive layout" @@ -186,11 +143,6 @@ _UI_ENUM(uiAttribute) { uiAttributeLowercaseCapForms, // enum uiAttributeCapForm uiAttributeUppercaseCapForms, // enum uiAttributeCapForm - - // TODO kCJKRomanSpacingType - - // TODO uiAttributeSystem, (this might not be doable with DirectWrite) - // TODO uiAttributeCustom, }; _UI_ENUM(uiDrawUnderlineStyle) { From 0df8346bffd970be166d4004878cad3311dd1573 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 24 Feb 2017 12:12:03 -0500 Subject: [PATCH 0698/1329] Also removed uiAttributeLangauge for compatibility reasons too. Let's settle all the TODOs now. --- _future/textlanguageattr/README | 1 + _future/textlanguageattr/attrstr_darwin.m | 19 ++++ _future/textlanguageattr/attrstr_unix.c | 9 ++ _future/textlanguageattr/attrstr_windows.cpp | 10 ++ _future/textlanguageattr/common_attrlist.c | 2 + _future/textlanguageattr/drawtext_example.c | 27 +++++ _future/textlanguageattr/fontmatch_darwin.m | 112 +++++++++++++++++++ _future/textlanguageattr/ui.h | 5 + _future/verticaltext/README | 3 + common/attrlist.c | 3 - darwin/attrstr.m | 12 +- darwin/fontmatch.m | 60 +--------- darwin/uipriv_darwin.h | 2 +- examples/drawtext/attributes.c | 26 ----- ui_attrstr.h | 4 - unix/attrstr.c | 9 -- windows/attrstr.cpp | 10 -- 17 files changed, 191 insertions(+), 123 deletions(-) create mode 100644 _future/textlanguageattr/README create mode 100644 _future/textlanguageattr/attrstr_darwin.m create mode 100644 _future/textlanguageattr/attrstr_unix.c create mode 100644 _future/textlanguageattr/attrstr_windows.cpp create mode 100644 _future/textlanguageattr/common_attrlist.c create mode 100644 _future/textlanguageattr/drawtext_example.c create mode 100644 _future/textlanguageattr/fontmatch_darwin.m create mode 100644 _future/textlanguageattr/ui.h diff --git a/_future/textlanguageattr/README b/_future/textlanguageattr/README new file mode 100644 index 00000000..f3bb4916 --- /dev/null +++ b/_future/textlanguageattr/README @@ -0,0 +1 @@ +Removed because proper support on OS X doesn't come until 10.9 unless we use a font with an ltag table; none of the fonts I have come with ltag tables (none of the fonts on OS X do, or at least don't come with a sr entry in their ltag table, and OpenType has replaced ltag with what appears to be custom sub-tables of the GPOS and GSUB tables.) diff --git a/_future/textlanguageattr/attrstr_darwin.m b/_future/textlanguageattr/attrstr_darwin.m new file mode 100644 index 00000000..1717d875 --- /dev/null +++ b/_future/textlanguageattr/attrstr_darwin.m @@ -0,0 +1,19 @@ +struct fontParams { + uiDrawFontDescriptor desc; + uint16_t featureTypes[maxFeatures]; + uint16_t featureSelectors[maxFeatures]; + size_t nFeatures; + const char *language; +}; + + + // locale identifiers are specified as BCP 47: https://developer.apple.com/reference/corefoundation/cflocale?language=objc + case uiAttributeLanguage: + // LONGTERM FUTURE when we move to 10.9, switch to using kCTLanguageAttributeName + ensureFontInRange(p, start, end); + adjustFontInRange(p, start, end, ^(struct fontParams *fp) { + fp->language = (const char *) (spec->Value); + }); + break; + + desc = fontdescAppendFeatures(desc, fp->featureTypes, fp->featureSelectors, fp->nFeatures, fp->language); diff --git a/_future/textlanguageattr/attrstr_unix.c b/_future/textlanguageattr/attrstr_unix.c new file mode 100644 index 00000000..47d22b15 --- /dev/null +++ b/_future/textlanguageattr/attrstr_unix.c @@ -0,0 +1,9 @@ + PangoLanguage *lang; + + // language strings are specified as BCP 47: https://developer.gnome.org/pango/1.30/pango-Scripts-and-Languages.html#pango-language-from-string https://www.ietf.org/rfc/rfc3066.txt + case uiAttributeLanguage: + lang = pango_language_from_string((const char *) (spec->Value)); + addattr(p, start, end, + pango_attr_language_new(lang)); + // lang *cannot* be freed + break; diff --git a/_future/textlanguageattr/attrstr_windows.cpp b/_future/textlanguageattr/attrstr_windows.cpp new file mode 100644 index 00000000..f8238c7f --- /dev/null +++ b/_future/textlanguageattr/attrstr_windows.cpp @@ -0,0 +1,10 @@ + WCHAR *localeName; + + // locale names are specified as BCP 47: https://msdn.microsoft.com/en-us/library/windows/desktop/dd373814(v=vs.85).aspx https://www.ietf.org/rfc/rfc4646.txt + case uiAttributeLanguage: + localeName = toUTF16((char *) (spec->Value)); + hr = p->layout->SetLocaleName(localeName, range); + if (hr != S_OK) + logHRESULT(L"error applying locale name attribute", hr); + uiFree(localeName); + break; \ No newline at end of file diff --git a/_future/textlanguageattr/common_attrlist.c b/_future/textlanguageattr/common_attrlist.c new file mode 100644 index 00000000..680a2d09 --- /dev/null +++ b/_future/textlanguageattr/common_attrlist.c @@ -0,0 +1,2 @@ + case uiAttributeLanguage: + return asciiStringsEqualCaseFold((char *) (attr->spec.Value), (char *) (spec->Value)); diff --git a/_future/textlanguageattr/drawtext_example.c b/_future/textlanguageattr/drawtext_example.c new file mode 100644 index 00000000..e006e7e1 --- /dev/null +++ b/_future/textlanguageattr/drawtext_example.c @@ -0,0 +1,27 @@ +before "or any combination of the above" + + // thanks to https://twitter.com/codeman38/status/831924064012886017 + next = "\xD0\xB1\xD0\xB3\xD0\xB4\xD0\xBF\xD1\x82"; + uiAttributedStringAppendUnattributed(attrstr, "multiple languages (compare "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeItalic; + spec.Value = uiDrawTextItalicItalic; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + spec.Type = uiAttributeLanguage; + spec.Value = (uintptr_t) "ru"; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " to "); + start = uiAttributedStringLen(attrstr); + end = start + strlen(next); + uiAttributedStringAppendUnattributed(attrstr, next); + spec.Type = uiAttributeItalic; + spec.Value = uiDrawTextItalicItalic; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + spec.Type = uiAttributeLanguage; + spec.Value = (uintptr_t) "sr"; + uiAttributedStringSetAttribute(attrstr, &spec, start, end); + uiAttributedStringAppendUnattributed(attrstr, " \xE2\x80\x94 may require changing the font)"); + + uiAttributedStringAppendUnattributed(attrstr, ", "); diff --git a/_future/textlanguageattr/fontmatch_darwin.m b/_future/textlanguageattr/fontmatch_darwin.m new file mode 100644 index 00000000..cd4df7a1 --- /dev/null +++ b/_future/textlanguageattr/fontmatch_darwin.m @@ -0,0 +1,112 @@ +// note: this doesn't work for languages; we have to parse the ltag table + +// fortunately features that aren't supported are simply ignored, so we can copy them all in +// LONGTERM FUTURE when we switch to 10.9, the language parameter won't be needed anymore +// LONGTERM FUTURE and on 10.10 we can use OpenType tags directly! +CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uint16_t *types, const uint16_t *selectors, size_t n, const char *language) +{ + CTFontDescriptorRef new; + CFMutableArrayRef outerArray; + CFDictionaryRef innerDict; + CFNumberRef numType, numSelector; + const void *keys[2], *values[2]; + size_t i; + CFArrayRef languages; + CFIndex il, nl; + CFStringRef curlang; + char d[2]; + + outerArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + if (outerArray == NULL) { + // TODO + } + keys[0] = kCTFontFeatureTypeIdentifierKey; + keys[1] = kCTFontFeatureSelectorIdentifierKey; + for (i = 0; i < n; i++) { + numType = CFNumberCreate(NULL, kCFNumberSInt16Type, + (const SInt16 *) (types + i)); + numSelector = CFNumberCreate(NULL, kCFNumberSInt16Type, + (const SInt16 *) (selectors + i)); + values[0] = numType; + values[1] = numSelector; + innerDict = CFDictionaryCreate(NULL, + keys, values, 2, + // TODO are these correct? + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (innerDict == NULL) { + // TODO + } + CFArrayAppendValue(outerArray, innerDict); + CFRelease(innerDict); + CFRelease(numSelector); + CFRelease(numType); + } + + // now we have to take care of the language + if (language != NULL) { + languages = CTFontDescriptorCopyAttribute(desc, kCTFontLanguagesAttribute); + if (languages != NULL) { + nl = CFArrayGetCount(languages); + d[0] = language[0]; + if (d[0] >= 'A' && d[0] <= 'Z') + d[0] += 'a' - 'A'; + d[1] = language[1]; + if (d[1] >= 'A' && d[1] <= 'Z') + d[1] += 'a' - 'A'; + for (il = 0; il < nl; il++) { + char c[2]; + + curlang = (CFStringRef) CFArrayGetValueAtIndex(languages, il); + // TODO check for failure + CFStringGetBytes(curlang, CFRangeMake(0, 2), + kCFStringEncodingUTF8, 0, false, + (UInt8 *) c, 2, NULL); + if (c[0] >= 'A' && c[0] <= 'Z') + c[0] += 'a' - 'A'; + if (c[1] >= 'A' && c[1] <= 'Z') + c[1] += 'a' - 'A'; + if (c[0] == d[0] && c[1] == d[1]) + break; + } + if (il != nl) { + uint16_t typ; + + typ = kLanguageTagType; + il++; + numType = CFNumberCreate(NULL, kCFNumberSInt16Type, + (const SInt16 *) (&typ)); + numSelector = CFNumberCreate(NULL, kCFNumberCFIndexType, + &il); + values[0] = numType; + values[1] = numSelector; + innerDict = CFDictionaryCreate(NULL, + keys, values, 2, + // TODO are these correct? + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (innerDict == NULL) { + // TODO + } + CFArrayAppendValue(outerArray, innerDict); + CFRelease(innerDict); + CFRelease(numSelector); + CFRelease(numType); + } + CFRelease(languages); + } + } + + keys[0] = kCTFontFeatureSettingsAttribute; + values[0] = outerArray; + innerDict = CFDictionaryCreate(NULL, + keys, values, 1, + // TODO are these correct? + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFRelease(outerArray); + new = CTFontDescriptorCreateCopyWithAttributes(desc, innerDict); + CFRelease(desc); + CFRelease(innerDict); + return new; +} diff --git a/_future/textlanguageattr/ui.h b/_future/textlanguageattr/ui.h new file mode 100644 index 00000000..2c3c9ec7 --- /dev/null +++ b/_future/textlanguageattr/ui.h @@ -0,0 +1,5 @@ +after UnderlineColor, before feature tags + + // TODO document that this will also enable language-specific font features (TODO on DirectWrite too?) + // TODO document that this should be strict BCP 47 form (A-Z, a-z, 0-9, and -) for maximum compatibility + uiAttributeLanguage, // BCP 47 string diff --git a/_future/verticaltext/README b/_future/verticaltext/README index 1bac02e5..7f30a15c 100644 --- a/_future/verticaltext/README +++ b/_future/verticaltext/README @@ -12,3 +12,6 @@ If readded, this will need to be a layout-wide setting, not a per-character sett More links: https://www.w3.org/TR/2012/NOTE-jlreq-20120403/#line-composition https://www.w3.org/TR/REC-CSS2/notes.html + +TODO indicate where in the attributes.c file that block of code should go (or drop it entirely for the reasons listed above) +TODO same for ui.h diff --git a/common/attrlist.c b/common/attrlist.c index 20aaf97f..7ea65fa6 100644 --- a/common/attrlist.c +++ b/common/attrlist.c @@ -342,9 +342,6 @@ static int specsIdentical(struct attr *attr, uiAttributeSpec *spec) attr->spec.G == spec->G && attr->spec.B == spec->B && attr->spec.A == spec->A; - case uiAttributeLanguage: - return asciiStringsEqualCaseFold((char *) (attr->spec.Value), (char *) (spec->Value)); - // TODO // TODO use boolsEqual() on boolean features } // handles the rest diff --git a/darwin/attrstr.m b/darwin/attrstr.m index f4644b37..3e5797b4 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -74,7 +74,6 @@ struct fontParams { uint16_t featureTypes[maxFeatures]; uint16_t featureSelectors[maxFeatures]; size_t nFeatures; - const char *language; }; static void ensureFontInRange(struct foreachParams *p, size_t start, size_t end) @@ -258,15 +257,6 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t if (spec->Value == uiDrawUnderlineColorCustom) CFRelease(color); break; - // locale identifiers are specified as BCP 47: https://developer.apple.com/reference/corefoundation/cflocale?language=objc - case uiAttributeLanguage: - // LONGTERM FUTURE when we move to 10.9, switch to using kCTLanguageAttributeName - ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(struct fontParams *fp) { - fp->language = (const char *) (spec->Value); - }); - break; - // TODO default: // handle typographic features ap.p = p; @@ -285,7 +275,7 @@ static CTFontRef fontdescToCTFont(struct fontParams *fp) CTFontRef font; desc = fontdescToCTFontDescriptor(&(fp->desc)); - desc = fontdescAppendFeatures(desc, fp->featureTypes, fp->featureSelectors, fp->nFeatures, fp->language); + desc = fontdescAppendFeatures(desc, fp->featureTypes, fp->featureSelectors, fp->nFeatures); font = CTFontCreateWithFontDescriptor(desc, fp->desc.Size, NULL); CFRelease(desc); // TODO correct? return font; diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index 9acb2edf..0c87a427 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -257,7 +257,7 @@ CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd) // fortunately features that aren't supported are simply ignored, so we can copy them all in // LONGTERM FUTURE when we switch to 10.9, the language parameter won't be needed anymore // LONGTERM FUTURE and on 10.10 we can use OpenType tags directly! -CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uint16_t *types, const uint16_t *selectors, size_t n, const char *language) +CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uint16_t *types, const uint16_t *selectors, size_t n) { CTFontDescriptorRef new; CFMutableArrayRef outerArray; @@ -265,10 +265,6 @@ CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uint1 CFNumberRef numType, numSelector; const void *keys[2], *values[2]; size_t i; - CFArrayRef languages; - CFIndex il, nl; - CFStringRef curlang; - char d[2]; outerArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); if (outerArray == NULL) { @@ -297,60 +293,6 @@ CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uint1 CFRelease(numType); } - // now we have to take care of the language - if (language != NULL) { - languages = CTFontDescriptorCopyAttribute(desc, kCTFontLanguagesAttribute); - if (languages != NULL) { - nl = CFArrayGetCount(languages); - d[0] = language[0]; - if (d[0] >= 'A' && d[0] <= 'Z') - d[0] += 'a' - 'A'; - d[1] = language[1]; - if (d[1] >= 'A' && d[1] <= 'Z') - d[1] += 'a' - 'A'; - for (il = 0; il < nl; il++) { - char c[2]; - - curlang = (CFStringRef) CFArrayGetValueAtIndex(languages, il); - // TODO check for failure - CFStringGetBytes(curlang, CFRangeMake(0, 2), - kCFStringEncodingUTF8, 0, false, - (UInt8 *) c, 2, NULL); - if (c[0] >= 'A' && c[0] <= 'Z') - c[0] += 'a' - 'A'; - if (c[1] >= 'A' && c[1] <= 'Z') - c[1] += 'a' - 'A'; - if (c[0] == d[0] && c[1] == d[1]) - break; - } - if (il != nl) { - uint16_t typ; - - typ = kLanguageTagType; - il++; - numType = CFNumberCreate(NULL, kCFNumberSInt16Type, - (const SInt16 *) (&typ)); - numSelector = CFNumberCreate(NULL, kCFNumberCFIndexType, - &il); - values[0] = numType; - values[1] = numSelector; - innerDict = CFDictionaryCreate(NULL, - keys, values, 2, - // TODO are these correct? - &kCFCopyStringDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - if (innerDict == NULL) { - // TODO - } - CFArrayAppendValue(outerArray, innerDict); - CFRelease(innerDict); - CFRelease(numSelector); - CFRelease(numType); - } - CFRelease(languages); - } - } - keys[0] = kCTFontFeatureSettingsAttribute; values[0] = outerArray; innerDict = CFDictionaryCreate(NULL, diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 281a4b57..c3de53e2 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -142,7 +142,7 @@ extern void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdg // fontmatch.m extern CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd); -extern CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uint16_t *types, const uint16_t *selectors, size_t n, const char *language); +extern CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uint16_t *types, const uint16_t *selectors, size_t n); extern void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescriptor *uidesc); // attrstr.m diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index b4a697a8..fbcdd797 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -130,32 +130,6 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, ", "); - // thanks to https://twitter.com/codeman38/status/831924064012886017 - next = "\xD0\xB1\xD0\xB3\xD0\xB4\xD0\xBF\xD1\x82"; - uiAttributedStringAppendUnattributed(attrstr, "multiple languages (compare "); - start = uiAttributedStringLen(attrstr); - end = start + strlen(next); - uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeItalic; - spec.Value = uiDrawTextItalicItalic; - uiAttributedStringSetAttribute(attrstr, &spec, start, end); - spec.Type = uiAttributeLanguage; - spec.Value = (uintptr_t) "ru"; - uiAttributedStringSetAttribute(attrstr, &spec, start, end); - uiAttributedStringAppendUnattributed(attrstr, " to "); - start = uiAttributedStringLen(attrstr); - end = start + strlen(next); - uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeItalic; - spec.Value = uiDrawTextItalicItalic; - uiAttributedStringSetAttribute(attrstr, &spec, start, end); - spec.Type = uiAttributeLanguage; - spec.Value = (uintptr_t) "sr"; - uiAttributedStringSetAttribute(attrstr, &spec, start, end); - uiAttributedStringAppendUnattributed(attrstr, " \xE2\x80\x94 may require changing the font)"); - - uiAttributedStringAppendUnattributed(attrstr, ", "); - // TODO randomize these ranges better // TODO make some overlap to test that // TODO also change colors to light foreground dark background diff --git a/ui_attrstr.h b/ui_attrstr.h index 3206efe4..0fb4a586 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -15,10 +15,6 @@ _UI_ENUM(uiAttribute) { // TODO ensure the color in the case we don't specify it is the text color? uiAttributeUnderlineColor, // enum uiDrawUnderlineColor - // TODO document that this will also enable language-specific font features (TODO on DirectWrite too?) - // TODO document that this should be strict BCP 47 form (A-Z, a-z, 0-9, and -) for maximum compatibility - uiAttributeLanguage, // BCP 47 string - // These attributes represent typographic features. Each feature // is a separate attribute, to make composition easier. The // availability of for each attribute are defined by the font; the diff --git a/unix/attrstr.c b/unix/attrstr.c index cf5bc509..88eabef4 100644 --- a/unix/attrstr.c +++ b/unix/attrstr.c @@ -142,7 +142,6 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t struct foreachParams *p = (struct foreachParams *) data; GClosure *closure; PangoUnderline underline; - PangoLanguage *lang; struct otParam op; switch (spec->Type) { @@ -226,14 +225,6 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t break; } break; - // language strings are specified as BCP 47: https://developer.gnome.org/pango/1.30/pango-Scripts-and-Languages.html#pango-language-from-string https://www.ietf.org/rfc/rfc3066.txt - case uiAttributeLanguage: - lang = pango_language_from_string((const char *) (spec->Value)); - addattr(p, start, end, - pango_attr_language_new(lang)); - // lang *cannot* be freed - break; - // TODO default: // handle typographic features op.p = p; diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index d0a4b3cc..ad2fad0e 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -112,7 +112,6 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t WCHAR *wfamily; size_t ostart, oend; BOOL hasUnderline; - WCHAR *localeName; struct otParam op; HRESULT hr; @@ -230,15 +229,6 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t break; } break; - // locale names are specified as BCP 47: https://msdn.microsoft.com/en-us/library/windows/desktop/dd373814(v=vs.85).aspx https://www.ietf.org/rfc/rfc4646.txt - case uiAttributeLanguage: - localeName = toUTF16((char *) (spec->Value)); - hr = p->layout->SetLocaleName(localeName, range); - if (hr != S_OK) - logHRESULT(L"error applying locale name attribute", hr); - uiFree(localeName); - break; - // TODO default: // handle typographic features op.p = p; From 0db03e5f440fadeeca9e73d428983240fb1a9000 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 24 Feb 2017 12:26:04 -0500 Subject: [PATCH 0699/1329] More TODO and documentation cleanup, including removing redundant or now-pointless TODOs. --- _future/verticaltext/README | 2 ++ ui_attrstr.h | 18 +++++++----------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/_future/verticaltext/README b/_future/verticaltext/README index 7f30a15c..99310823 100644 --- a/_future/verticaltext/README +++ b/_future/verticaltext/README @@ -15,3 +15,5 @@ https://www.w3.org/TR/REC-CSS2/notes.html TODO indicate where in the attributes.c file that block of code should go (or drop it entirely for the reasons listed above) TODO same for ui.h + +TODO vertical carets diff --git a/ui_attrstr.h b/ui_attrstr.h index 0fb4a586..6f23fa0c 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -276,7 +276,6 @@ _UI_ENUM(uiDrawTextStretch) { struct uiDrawFontDescriptor { char *Family; - // TODO rename to PointSize? double Size; uiDrawTextWeight Weight; uiDrawTextItalic Italic; @@ -301,9 +300,15 @@ struct uiDrawTextLayoutParams { uiDrawTextLayoutAlign Align; }; +// Height will equal ParagraphSpacingBefore + LineHeightSpace + Ascent + Descent + Leading + LineSpacing + ParagraphSpacing. +// The above values are listed in vertical order, from top to bottom. +// Ascent + Descent + Leading will give you the typographic bounds +// of the text. BaselineY is the boundary between Ascent and Descent. +// X, Y, and BaselineY are all in the layout's coordinate system, so the +// start point of the baseline will be at (X, BaselineY). All values are +// nonnegative. struct uiDrawTextLayoutLineMetrics { // This describes the overall bounding box of the line. - // TODO figure out if X is correct regardless of both alignment and writing direction double X; double Y; double Width; @@ -322,13 +327,6 @@ struct uiDrawTextLayoutLineMetrics { double LineSpacing; double ParagraphSpacing; - // Height should equal ParagraphSpacingBefore + LineHeightSpace + Ascent + Descent + Leading + LineSpacing + ParagraphSpacing. - // The above values are listed in vertical order, from top to bottom. - // Ascent + Descent + Leading will give you the typographic bounds of the text. - // BaselineY will be the boundary between Ascent and Descent. - // X, Y, and BaselineY are all in the layout's coordinate system, so the start point of the baseline will be at (X, BaselineY). - // All values will be nonnegative. - // TODO trailing whitespace? }; @@ -375,13 +373,11 @@ _UI_EXTERN void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y // TODO make sure this works right for right-aligned and center-aligned lines and justified lines and RTL text _UI_EXTERN double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line); -// TODO vertical carets _UI_EXTERN void uiDrawCaret(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t pos, int *line); // TODO allow blinking typedef struct uiFontButton uiFontButton; #define uiFontButton(this) ((uiFontButton *) (this)) -// TODO document this returns a new font _UI_EXTERN void uiFontButtonFont(uiFontButton *b, uiDrawFontDescriptor *desc); // TOOD SetFont, mechanics _UI_EXTERN void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data); From ce1a54a9d12e2cb3ea6bc5eb80354b80a4ae1d74 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 24 Feb 2017 12:39:29 -0500 Subject: [PATCH 0700/1329] More documentation works. --- ui_attrstr.h | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index 6f23fa0c..57f3e79a 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -1,5 +1,22 @@ +// uiAttributedString represents a string of UTF-8 text that can +// optionally be embellished with formatting attributes. libui +// provides the list of formatting attributes, which cover common +// formatting traits like boldface and color as well as advanced +// typographical features like superscripts and small caps. +// These attributes can be combined in a variety of ways. +// +// In addition, uiAttributedString provides facilities for moving +// between grapheme clusters, which represent a character +// from the point of view of the end user. The cursor of a text editor +// is always placed on a grapheme boundary, so you can use these +// features to move the cursor left or right by one "character". +// +// uiAttributedString does not provide enough information to be able +// to draw itself onto a uiDrawContext or respond to user actions. +// In order to do that, you'll need to use a uiDrawTextLayout, which +// is built from the combination of a uiAttributedString and a set of +// layout-specific properties. typedef struct uiAttributedString uiAttributedString; -typedef struct uiAttributeSpec uiAttributeSpec; // Note: where you say "1 = on", any nonzero value means "on". (TODO) _UI_ENUM(uiAttribute) { @@ -194,7 +211,7 @@ _UI_ENUM(uiAttributeCapForm) { uiAttributeCapFormPetiteCaps, }; -// TODO number case +typedef struct uiAttributeSpec uiAttributeSpec; struct uiAttributeSpec { uiAttribute Type; From f6e9da916a6501531b831445deb728c74151168d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 24 Feb 2017 14:25:16 -0500 Subject: [PATCH 0701/1329] Fixed Unix cursor positioning. --- unix/drawtext.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/unix/drawtext.c b/unix/drawtext.c index 8b45530a..d7a1d3ce 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -218,6 +218,7 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *p int p, trailing; int i; + // this is layout-global, so it takes line origins into account pango_layout_xy_to_index(tl->layout, cairoToPango(x), cairoToPango(y), &p, &trailing); @@ -264,7 +265,8 @@ double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int } pango_layout_line_index_to_x(pll, pos, trailing, &pangox); // TODO unref pll? - return pangoToCairo(pangox); + // this is relative to the beginning of the line + return pangoToCairo(pangox) + tl->lineMetrics[line].X; } // note: we can't use gtk_render_insertion_cursor() because that doesn't take information about what line to render on From cca4db5ce9eec791070958848f6445a3352d3049 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 24 Feb 2017 18:15:20 -0500 Subject: [PATCH 0702/1329] More TODO resolution and pointless TODO elimination. --- _future/otherattributes/ui.h | 2 +- common/opentype.c | 2 +- darwin/aat.m | 2 +- examples/drawtext/attributes.c | 6 +++--- ui_attrstr.h | 6 ++---- 5 files changed, 8 insertions(+), 10 deletions(-) diff --git a/_future/otherattributes/ui.h b/_future/otherattributes/ui.h index 89a0c5a1..0d931033 100644 --- a/_future/otherattributes/ui.h +++ b/_future/otherattributes/ui.h @@ -129,7 +129,7 @@ _UI_ENUM(uiAttribute) { // 9 = inverted box // 10 = inverted rounded box // TODO rename to AnnotatedForms? - uiAttributeGlyphAnnotations, // an integer from 0 to a font-specified upper bound + uiAttributeAnnotatedGlyphForms, // an integer from 0 to a font-specified upper bound // TODO provide a function to get the upper bound? // TODO kKanaSpacingType diff --git a/common/opentype.c b/common/opentype.c index a3e4f852..8db4dc4c 100644 --- a/common/opentype.c +++ b/common/opentype.c @@ -144,7 +144,7 @@ void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) case uiAttributeHanjaToHangul: boolspec(spec, "hngl", f, data); return; - case uiAttributeGlyphAnnotations: + case uiAttributeAnnotatedGlyphForms: (*f)("nalt", (uint32_t) (spec->Value), data); return; case uiAttributeRubyKanaForms: diff --git a/darwin/aat.m b/darwin/aat.m index 9aab5f89..e2b3595c 100644 --- a/darwin/aat.m +++ b/darwin/aat.m @@ -162,7 +162,7 @@ int specToAAT(uiAttributeSpec *spec, specToAATEnumFunc f, void *data) if (spec->Value != 0) (*f)(kTransliterationType, kHanjaToHangulSelector, data); return 1; - case uiAttributeGlyphAnnotations: + case uiAttributeAnnotatedGlyphForms: (*f)(kAnnotationType, (uint16_t) (spec->Value), data); return 1; case uiAttributeRubyKanaForms: diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index fbcdd797..b9845605 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -483,21 +483,21 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeGlyphAnnotations; + spec.Type = uiAttributeAnnotatedGlyphForms; spec.Value = 0; uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeGlyphAnnotations; + spec.Type = uiAttributeAnnotatedGlyphForms; spec.Value = 1; uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeGlyphAnnotations; + spec.Type = uiAttributeAnnotatedGlyphForms; spec.Value = 4; // AAT inverted circle uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); diff --git a/ui_attrstr.h b/ui_attrstr.h index 57f3e79a..e4193b05 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -29,7 +29,7 @@ _UI_ENUM(uiAttribute) { uiAttributeBackground, // use R, G, B, A uiAttributeUnderline, // enum uiDrawUnderlineStyle - // TODO ensure the color in the case we don't specify it is the text color? + // TODO document that the color in the case we don't specify it is the text color uiAttributeUnderlineColor, // enum uiDrawUnderlineColor // These attributes represent typographic features. Each feature @@ -85,7 +85,6 @@ _UI_ENUM(uiAttribute) { // following values: // 0 = none // OpenType calls this "access all alternates". - // TODO doesn't OpenType do the same about 0? uiAttributeSpecificCharacterForm, // an integer from 0 to a font-specified upper bound // TODO provide a function to get the upper bound? @@ -111,8 +110,7 @@ _UI_ENUM(uiAttribute) { // 8 = diamond // 9 = inverted box // 10 = inverted rounded box - // TODO rename to AnnotatedForms? - uiAttributeGlyphAnnotations, // an integer from 0 to a font-specified upper bound + uiAttributeAnnotatedGlyphForms, // an integer from 0 to a font-specified upper bound // TODO provide a function to get the upper bound? uiAttributeRubyKanaForms, // 0 = off, 1 = on From 28b30367e29845e1ab3d14cf994623728dd0d784 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 24 Feb 2017 18:19:34 -0500 Subject: [PATCH 0703/1329] Still more TODO resolution. --- _future/otherattributes/ui.h | 40 ++++++++++++++++----------------- common/opentype.c | 40 ++++++++++++++++----------------- darwin/aat.m | 40 ++++++++++++++++----------------- examples/drawtext/attributes.c | 2 +- ui_attrstr.h | 41 +++++++++++++++++----------------- 5 files changed, 81 insertions(+), 82 deletions(-) diff --git a/_future/otherattributes/ui.h b/_future/otherattributes/ui.h index 0d931033..697f09bc 100644 --- a/_future/otherattributes/ui.h +++ b/_future/otherattributes/ui.h @@ -155,26 +155,26 @@ _UI_ENUM(uiAttribute) { // TODO "Alternate"? unify all this // TODO document that these are guaranteed to be consecutive - uiAttributeStylisticAlternative1, // 0 = off, 1 = on - uiAttributeStylisticAlternative2, // 0 = off, 1 = on - uiAttributeStylisticAlternative3, // 0 = off, 1 = on - uiAttributeStylisticAlternative4, // 0 = off, 1 = on - uiAttributeStylisticAlternative5, // 0 = off, 1 = on - uiAttributeStylisticAlternative6, // 0 = off, 1 = on - uiAttributeStylisticAlternative7, // 0 = off, 1 = on - uiAttributeStylisticAlternative8, // 0 = off, 1 = on - uiAttributeStylisticAlternative9, // 0 = off, 1 = on - uiAttributeStylisticAlternative10, // 0 = off, 1 = on - uiAttributeStylisticAlternative11, // 0 = off, 1 = on - uiAttributeStylisticAlternative12, // 0 = off, 1 = on - uiAttributeStylisticAlternative13, // 0 = off, 1 = on - uiAttributeStylisticAlternative14, // 0 = off, 1 = on - uiAttributeStylisticAlternative15, // 0 = off, 1 = on - uiAttributeStylisticAlternative16, // 0 = off, 1 = on - uiAttributeStylisticAlternative17, // 0 = off, 1 = on - uiAttributeStylisticAlternative18, // 0 = off, 1 = on - uiAttributeStylisticAlternative19, // 0 = off, 1 = on - uiAttributeStylisticAlternative20, // 0 = off, 1 = on + uiAttributeStylisticAlternate1, // 0 = off, 1 = on + uiAttributeStylisticAlternate2, // 0 = off, 1 = on + uiAttributeStylisticAlternate3, // 0 = off, 1 = on + uiAttributeStylisticAlternate4, // 0 = off, 1 = on + uiAttributeStylisticAlternate5, // 0 = off, 1 = on + uiAttributeStylisticAlternate6, // 0 = off, 1 = on + uiAttributeStylisticAlternate7, // 0 = off, 1 = on + uiAttributeStylisticAlternate8, // 0 = off, 1 = on + uiAttributeStylisticAlternate9, // 0 = off, 1 = on + uiAttributeStylisticAlternate10, // 0 = off, 1 = on + uiAttributeStylisticAlternate11, // 0 = off, 1 = on + uiAttributeStylisticAlternate12, // 0 = off, 1 = on + uiAttributeStylisticAlternate13, // 0 = off, 1 = on + uiAttributeStylisticAlternate14, // 0 = off, 1 = on + uiAttributeStylisticAlternate15, // 0 = off, 1 = on + uiAttributeStylisticAlternate16, // 0 = off, 1 = on + uiAttributeStylisticAlternate17, // 0 = off, 1 = on + uiAttributeStylisticAlternate18, // 0 = off, 1 = on + uiAttributeStylisticAlternate19, // 0 = off, 1 = on + uiAttributeStylisticAlternate20, // 0 = off, 1 = on uiAttributeContextualAlternates, // 0 = off, 1 = on uiAttributeSwashes, // 0 = off, 1 = on diff --git a/common/opentype.c b/common/opentype.c index 8db4dc4c..3b2ddf64 100644 --- a/common/opentype.c +++ b/common/opentype.c @@ -165,64 +165,64 @@ void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) case uiAttributeAlternateVerticalKana: boolspec(spec, "vkna", f, data); return; - case uiAttributeStylisticAlternative1: + case uiAttributeStylisticAlternate1: boolspec(spec, "ss01", f, data); return; - case uiAttributeStylisticAlternative2: + case uiAttributeStylisticAlternate2: boolspec(spec, "ss02", f, data); return; - case uiAttributeStylisticAlternative3: + case uiAttributeStylisticAlternate3: boolspec(spec, "ss03", f, data); return; - case uiAttributeStylisticAlternative4: + case uiAttributeStylisticAlternate4: boolspec(spec, "ss04", f, data); return; - case uiAttributeStylisticAlternative5: + case uiAttributeStylisticAlternate5: boolspec(spec, "ss05", f, data); return; - case uiAttributeStylisticAlternative6: + case uiAttributeStylisticAlternate6: boolspec(spec, "ss06", f, data); return; - case uiAttributeStylisticAlternative7: + case uiAttributeStylisticAlternate7: boolspec(spec, "ss07", f, data); return; - case uiAttributeStylisticAlternative8: + case uiAttributeStylisticAlternate8: boolspec(spec, "ss08", f, data); return; - case uiAttributeStylisticAlternative9: + case uiAttributeStylisticAlternate9: boolspec(spec, "ss09", f, data); return; - case uiAttributeStylisticAlternative10: + case uiAttributeStylisticAlternate10: boolspec(spec, "ss10", f, data); return; - case uiAttributeStylisticAlternative11: + case uiAttributeStylisticAlternate11: boolspec(spec, "ss11", f, data); return; - case uiAttributeStylisticAlternative12: + case uiAttributeStylisticAlternate12: boolspec(spec, "ss12", f, data); return; - case uiAttributeStylisticAlternative13: + case uiAttributeStylisticAlternate13: boolspec(spec, "ss13", f, data); return; - case uiAttributeStylisticAlternative14: + case uiAttributeStylisticAlternate14: boolspec(spec, "ss14", f, data); return; - case uiAttributeStylisticAlternative15: + case uiAttributeStylisticAlternate15: boolspec(spec, "ss15", f, data); return; - case uiAttributeStylisticAlternative16: + case uiAttributeStylisticAlternate16: boolspec(spec, "ss16", f, data); return; - case uiAttributeStylisticAlternative17: + case uiAttributeStylisticAlternate17: boolspec(spec, "ss17", f, data); return; - case uiAttributeStylisticAlternative18: + case uiAttributeStylisticAlternate18: boolspec(spec, "ss18", f, data); return; - case uiAttributeStylisticAlternative19: + case uiAttributeStylisticAlternate19: boolspec(spec, "ss19", f, data); return; - case uiAttributeStylisticAlternative20: + case uiAttributeStylisticAlternate20: boolspec(spec, "ss20", f, data); return; case uiAttributeContextualAlternates: diff --git a/darwin/aat.m b/darwin/aat.m index e2b3595c..c7fc42e1 100644 --- a/darwin/aat.m +++ b/darwin/aat.m @@ -213,121 +213,121 @@ int specToAAT(uiAttributeSpec *spec, specToAATEnumFunc f, void *data) kAlternateVertKanaOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative1: + case uiAttributeStylisticAlternate1: boolspec(spec, kStylisticAlternativesType, kStylisticAltOneOnSelector, kStylisticAltOneOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative2: + case uiAttributeStylisticAlternate2: boolspec(spec, kStylisticAlternativesType, kStylisticAltTwoOnSelector, kStylisticAltTwoOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative3: + case uiAttributeStylisticAlternate3: boolspec(spec, kStylisticAlternativesType, kStylisticAltThreeOnSelector, kStylisticAltThreeOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative4: + case uiAttributeStylisticAlternate4: boolspec(spec, kStylisticAlternativesType, kStylisticAltFourOnSelector, kStylisticAltFourOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative5: + case uiAttributeStylisticAlternate5: boolspec(spec, kStylisticAlternativesType, kStylisticAltFiveOnSelector, kStylisticAltFiveOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative6: + case uiAttributeStylisticAlternate6: boolspec(spec, kStylisticAlternativesType, kStylisticAltSixOnSelector, kStylisticAltSixOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative7: + case uiAttributeStylisticAlternate7: boolspec(spec, kStylisticAlternativesType, kStylisticAltSevenOnSelector, kStylisticAltSevenOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative8: + case uiAttributeStylisticAlternate8: boolspec(spec, kStylisticAlternativesType, kStylisticAltEightOnSelector, kStylisticAltEightOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative9: + case uiAttributeStylisticAlternate9: boolspec(spec, kStylisticAlternativesType, kStylisticAltNineOnSelector, kStylisticAltNineOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative10: + case uiAttributeStylisticAlternate10: boolspec(spec, kStylisticAlternativesType, kStylisticAltTenOnSelector, kStylisticAltTenOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative11: + case uiAttributeStylisticAlternate11: boolspec(spec, kStylisticAlternativesType, kStylisticAltElevenOnSelector, kStylisticAltElevenOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative12: + case uiAttributeStylisticAlternate12: boolspec(spec, kStylisticAlternativesType, kStylisticAltTwelveOnSelector, kStylisticAltTwelveOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative13: + case uiAttributeStylisticAlternate13: boolspec(spec, kStylisticAlternativesType, kStylisticAltThirteenOnSelector, kStylisticAltThirteenOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative14: + case uiAttributeStylisticAlternate14: boolspec(spec, kStylisticAlternativesType, kStylisticAltFourteenOnSelector, kStylisticAltFourteenOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative15: + case uiAttributeStylisticAlternate15: boolspec(spec, kStylisticAlternativesType, kStylisticAltFifteenOnSelector, kStylisticAltFifteenOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative16: + case uiAttributeStylisticAlternate16: boolspec(spec, kStylisticAlternativesType, kStylisticAltSixteenOnSelector, kStylisticAltSixteenOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative17: + case uiAttributeStylisticAlternate17: boolspec(spec, kStylisticAlternativesType, kStylisticAltSeventeenOnSelector, kStylisticAltSeventeenOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative18: + case uiAttributeStylisticAlternate18: boolspec(spec, kStylisticAlternativesType, kStylisticAltEighteenOnSelector, kStylisticAltEighteenOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative19: + case uiAttributeStylisticAlternate19: boolspec(spec, kStylisticAlternativesType, kStylisticAltNineteenOnSelector, kStylisticAltNineteenOffSelector, f, data); return 1; - case uiAttributeStylisticAlternative20: + case uiAttributeStylisticAlternate20: boolspec(spec, kStylisticAlternativesType, kStylisticAltTwentyOnSelector, kStylisticAltTwentyOffSelector, diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index b9845605..7fb23fe5 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -616,7 +616,7 @@ static void setupAttributedString(void) next = "g"; uiAttributedStringAppendUnattributed(attrstr, "stylistic alternates ("); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeStylisticAlternative1; + spec.Type = uiAttributeStylisticAlternate1; spec.Value = 1; for (i = 0; i < 20; i++) { start = uiAttributedStringLen(attrstr); diff --git a/ui_attrstr.h b/ui_attrstr.h index e4193b05..f5f153c2 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -125,28 +125,27 @@ _UI_ENUM(uiAttribute) { uiAttributeAlternateHorizontalKana, // 0 = off, 1 = on uiAttributeAlternateVerticalKana, // 0 = off, 1 = on - // TODO "Alternate"? unify all this // TODO document that these are guaranteed to be consecutive - uiAttributeStylisticAlternative1, // 0 = off, 1 = on - uiAttributeStylisticAlternative2, // 0 = off, 1 = on - uiAttributeStylisticAlternative3, // 0 = off, 1 = on - uiAttributeStylisticAlternative4, // 0 = off, 1 = on - uiAttributeStylisticAlternative5, // 0 = off, 1 = on - uiAttributeStylisticAlternative6, // 0 = off, 1 = on - uiAttributeStylisticAlternative7, // 0 = off, 1 = on - uiAttributeStylisticAlternative8, // 0 = off, 1 = on - uiAttributeStylisticAlternative9, // 0 = off, 1 = on - uiAttributeStylisticAlternative10, // 0 = off, 1 = on - uiAttributeStylisticAlternative11, // 0 = off, 1 = on - uiAttributeStylisticAlternative12, // 0 = off, 1 = on - uiAttributeStylisticAlternative13, // 0 = off, 1 = on - uiAttributeStylisticAlternative14, // 0 = off, 1 = on - uiAttributeStylisticAlternative15, // 0 = off, 1 = on - uiAttributeStylisticAlternative16, // 0 = off, 1 = on - uiAttributeStylisticAlternative17, // 0 = off, 1 = on - uiAttributeStylisticAlternative18, // 0 = off, 1 = on - uiAttributeStylisticAlternative19, // 0 = off, 1 = on - uiAttributeStylisticAlternative20, // 0 = off, 1 = on + uiAttributeStylisticAlternate1, // 0 = off, 1 = on + uiAttributeStylisticAlternate2, // 0 = off, 1 = on + uiAttributeStylisticAlternate3, // 0 = off, 1 = on + uiAttributeStylisticAlternate4, // 0 = off, 1 = on + uiAttributeStylisticAlternate5, // 0 = off, 1 = on + uiAttributeStylisticAlternate6, // 0 = off, 1 = on + uiAttributeStylisticAlternate7, // 0 = off, 1 = on + uiAttributeStylisticAlternate8, // 0 = off, 1 = on + uiAttributeStylisticAlternate9, // 0 = off, 1 = on + uiAttributeStylisticAlternate10, // 0 = off, 1 = on + uiAttributeStylisticAlternate11, // 0 = off, 1 = on + uiAttributeStylisticAlternate12, // 0 = off, 1 = on + uiAttributeStylisticAlternate13, // 0 = off, 1 = on + uiAttributeStylisticAlternate14, // 0 = off, 1 = on + uiAttributeStylisticAlternate15, // 0 = off, 1 = on + uiAttributeStylisticAlternate16, // 0 = off, 1 = on + uiAttributeStylisticAlternate17, // 0 = off, 1 = on + uiAttributeStylisticAlternate18, // 0 = off, 1 = on + uiAttributeStylisticAlternate19, // 0 = off, 1 = on + uiAttributeStylisticAlternate20, // 0 = off, 1 = on uiAttributeContextualAlternates, // 0 = off, 1 = on uiAttributeSwashes, // 0 = off, 1 = on From 5fe4e27c5d3931053388ae6d8d957a8695e21fe3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 24 Feb 2017 18:46:53 -0500 Subject: [PATCH 0704/1329] TODO cleanup and stale TODO removal. That nLines == 0 TODO needs more testing... --- darwin/drawtext.m | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index c0123117..579d3863 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -37,7 +37,7 @@ struct uiDrawTextLayout { size_t nUTF16; }; -// TODO this is wrong for our hit-test example's multiple combining character example +// TODO document that lines may or may not overlap because ours do in the case of multiple combining characters static uiDrawTextLayoutLineMetrics *computeLineMetrics(CTFrameRef frame, CGSize size) { uiDrawTextLayoutLineMetrics *metrics; @@ -65,7 +65,6 @@ static uiDrawTextLayoutLineMetrics *computeLineMetrics(CTFrameRef frame, CGSize // this is equivalent to boundsNoLeading.size.height + boundsNoLeading.origin.y (manually verified) ascent = bounds.size.height + bounds.origin.y; descent = -boundsNoLeading.origin.y; - // TODO does this preserve leading sign? leading = -bounds.origin.y - descent; // Core Text always rounds these up for paragraph style calculations; there is a flag to control it but it's inaccessible (and this behavior is turned off for old versions of iPhoto) @@ -101,7 +100,6 @@ static uiDrawTextLayoutLineMetrics *computeLineMetrics(CTFrameRef frame, CGSize // go from bottom-left corner to top-left metrics[i].Y -= metrics[i].Height; metrics[i].BaselineY = size.height - metrics[i].BaselineY; - // TODO also adjust by metrics[i].Height? } uiFree(origins); @@ -121,7 +119,6 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) range.length = CFAttributedStringGetLength(tl->attrstr); tl->width = p->Width; - // TODO CTFrameProgression for RTL/LTR // TODO kCTParagraphStyleSpecifierMaximumLineSpacing, kCTParagraphStyleSpecifierMinimumLineSpacing, kCTParagraphStyleSpecifierLineSpacingAdjustment for line spacing tl->framesetter = CTFramesetterCreateWithAttributedString(tl->attrstr); if (tl->framesetter == NULL) { @@ -131,8 +128,6 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) cgwidth = (CGFloat) (tl->width); if (cgwidth < 0) cgwidth = CGFLOAT_MAX; - // TODO these seem to be floor()'d or truncated? - // TODO double check to make sure this TODO was right tl->size = CTFramesetterSuggestFrameSizeWithConstraints(tl->framesetter, range, // TODO kCTFramePathWidthAttributeName? @@ -209,8 +204,8 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) CGContextRestoreGState(c->c); } -// TODO document that the width and height of a layout is not necessarily the sum of the widths and heights of its constituent lines; this is definitely untrue on OS X, where lines are placed in such a way that the distance between baselines is always integral -// TODO width doesn't include trailing whitespace... +// TODO document that the width and height of a layout is not necessarily the sum of the widths and heights of its constituent lines +// TODO width doesn't include trailing whitespace... (TODO on which platforms?) // TODO figure out how paragraph spacing should play into this void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height) { @@ -239,8 +234,8 @@ void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLa *m = tl->lineMetrics[line]; } -// TODO note that in some cases lines can overlap slightly -// in our case, we read lines first to last and use their bottommost point (Y + Height) to determine where the next line should start for hit-testing +// in the case of overlapping lines, we read lines first to last and use their bottommost point (Y + Height) to determine where the next line should start for hit-testing +// TODO should we document this? void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *pos, int *line) { int i; From 5234586ead8842f26cd14a72428210fd9dbda983 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 24 Feb 2017 18:54:35 -0500 Subject: [PATCH 0705/1329] Added a test for making uiDrawTextLayouts on empty strings. We're already off to a good start since we have a ~0 error on OS X... --- examples/CMakeLists.txt | 1 + examples/drawtext/drawtext.h | 3 + examples/drawtext/emptystr_hittest.c | 254 +++++++++++++++++++++++++++ examples/drawtext/main.c | 5 + 4 files changed, 263 insertions(+) create mode 100644 examples/drawtext/emptystr_hittest.c diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 64c7098c..e011db23 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -34,6 +34,7 @@ endif() _add_example(drawtext drawtext/attributes.c drawtext/basic.c + drawtext/emptystr_hittest.c drawtext/hittest.c drawtext/main.c ${_EXAMPLE_RESOURCES_RC} diff --git a/examples/drawtext/drawtext.h b/examples/drawtext/drawtext.h index 0ac4cb36..072c2f02 100644 --- a/examples/drawtext/drawtext.h +++ b/examples/drawtext/drawtext.h @@ -23,3 +23,6 @@ extern struct example *mkHitTestExample(void); // attributes.c extern struct example *mkAttributesExample(void); + +// emptystr_hittest.c +extern struct example *mkEmptyStringExample(void); diff --git a/examples/drawtext/emptystr_hittest.c b/examples/drawtext/emptystr_hittest.c new file mode 100644 index 00000000..5542e469 --- /dev/null +++ b/examples/drawtext/emptystr_hittest.c @@ -0,0 +1,254 @@ +// 20 january 2017 +#include "drawtext.h" + +// TODO FOR THIS FILE +// get rid of it once we rewrite this example (which requires having more fine-grained scrolling control) + +// TODO double-check ligatures on all platforms to make sure we can place the cursor at the right place +// TODO the hiding and showing does not work properly on GTK+ +// TODO using the arrow keys allows us to walk back to the end of the line on some platforms (TODO which?); IIRC arrow keys shouldn't do that +// TODO make sure to check the cursor positions of RTL on all platforms + +static const char *text = ""; +static char fontFamily[] = "Helvetica"; +static uiDrawFontDescriptor defaultFont = { + .Family = fontFamily, + .Size = 14, + .Weight = uiDrawTextWeightNormal, + .Italic = uiDrawTextItalicNormal, + .Stretch = uiDrawTextStretchNormal, +}; +static uiAttributedString *attrstr; +static uiDrawTextLayoutParams params; + +#define margins 10 + +static uiBox *panel; +static uiBox *vbox; +static uiLabel *caretLabel; +static uiCheckbox *showLineBounds; +static uiFontButton *fontButton; +static uiCombobox *textAlign; + +static int caretLine = -1; +static size_t caretPos; + +// TODO should be const? +static uiDrawBrush fillBrushes[4] = { + { + .Type = uiDrawBrushTypeSolid, + .R = 1.0, + .G = 0.0, + .B = 0.0, + .A = 0.5, + }, + { + .Type = uiDrawBrushTypeSolid, + .R = 0.0, + .G = 1.0, + .B = 0.0, + .A = 0.5, + }, + { + .Type = uiDrawBrushTypeSolid, + .R = 0.0, + .G = 0.0, + .B = 1.0, + .A = 0.5, + }, + { + .Type = uiDrawBrushTypeSolid, + .R = 0.0, + .G = 1.0, + .B = 1.0, + .A = 0.5, + }, +}; + +static void draw(uiAreaDrawParams *p) +{ + uiDrawPath *path; + uiDrawTextLayout *layout; + uiDrawTextLayoutLineMetrics m; + + // only clip the text, not the guides + uiDrawSave(p->Context); + + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathAddRectangle(path, margins, margins, + p->AreaWidth - 2 * margins, + p->AreaHeight - 2 * margins); + uiDrawPathEnd(path); + uiDrawClip(p->Context, path); + uiDrawFreePath(path); + + params.Width = p->AreaWidth - 2 * margins; + layout = uiDrawNewTextLayout(¶ms); + uiDrawText(p->Context, layout, margins, margins); + + uiDrawRestore(p->Context); + + if (caretLine == -1) { + caretLine = uiDrawTextLayoutNumLines(layout) - 1; + caretPos = uiAttributedStringLen(attrstr); + } + uiDrawCaret(p->Context, margins, margins, + layout, caretPos, &caretLine); + + if (uiCheckboxChecked(showLineBounds)) { + int i, n; + int fill = 0; + + n = uiDrawTextLayoutNumLines(layout); + for (i = 0; i < n; i++) { + uiDrawTextLayoutLineGetMetrics(layout, i, &m); + path = uiDrawNewPath(uiDrawFillModeWinding); + uiDrawPathAddRectangle(path, margins + m.X, margins + m.Y, + m.Width, m.Height); + uiDrawPathEnd(path); + uiDrawFill(p->Context, path, fillBrushes + fill); + uiDrawFreePath(path); + fill = (fill + 1) % 4; + } + } + + uiDrawFreeTextLayout(layout); +} + +static void mouse(uiAreaMouseEvent *e) +{ + uiDrawTextLayout *layout; + char labelText[128]; + + if (e->Down != 1) + return; + + params.Width = e->AreaWidth - 2 * margins; + layout = uiDrawNewTextLayout(¶ms); + uiDrawTextLayoutHitTest(layout, + e->X - margins, e->Y - margins, + &caretPos, &caretLine); + uiDrawFreeTextLayout(layout); + + // TODO move this into the draw handler so it is reflected by keyboard-based position changes + // urgh %zd is not supported by MSVC with sprintf() + // TODO get that part in test/ about having no other option + sprintf(labelText, "pos %d line %d", + (int) caretPos, caretLine); + uiLabelSetText(caretLabel, labelText); + + redraw(); +} + +static int key(uiAreaKeyEvent *e) +{ + size_t grapheme; + + if (e->Up) + return 0; + if (e->Key != 0) + return 0; + switch (e->ExtKey) { + case uiExtKeyUp: + // TODO + return 1; + case uiExtKeyDown: + // TODO + return 1; + case uiExtKeyLeft: + grapheme = uiAttributedStringByteIndexToGrapheme(attrstr, caretPos); + if (grapheme == 0) + return 0; + grapheme--; + caretPos = uiAttributedStringGraphemeToByteIndex(attrstr, grapheme); + redraw(); + return 1; + case uiExtKeyRight: + grapheme = uiAttributedStringByteIndexToGrapheme(attrstr, caretPos); + if (grapheme == uiAttributedStringNumGraphemes(attrstr)) + return 0; + grapheme++; + caretPos = uiAttributedStringGraphemeToByteIndex(attrstr, grapheme); + redraw(); + return 1; + } + return 0; +} + +static struct example hitTestExample; + +// TODO share? +static void checkboxChecked(uiCheckbox *c, void *data) +{ + redraw(); +} + +static void changeFont(uiFontButton *b, void *data) +{ + if (defaultFont.Family != fontFamily) + uiFreeText(defaultFont.Family); + // TODO rename defaultFont + uiFontButtonFont(fontButton, &defaultFont); + printf("{\n\tfamily: %s\n\tsize: %g\n\tweight: %d\n\titalic: %d\n\tstretch: %d\n}\n", + defaultFont.Family, + defaultFont.Size, + (int) (defaultFont.Weight), + (int) (defaultFont.Italic), + (int) (defaultFont.Stretch)); + redraw(); +} + +static void changeTextAlign(uiCombobox *c, void *data) +{ + // note the order of the items added below + params.Align = (uiDrawTextLayoutAlign) uiComboboxSelected(textAlign); + redraw(); +} + +// TODO share? +static uiCheckbox *newCheckbox(uiBox *box, const char *text) +{ + uiCheckbox *c; + + c = uiNewCheckbox(text); + uiCheckboxOnToggled(c, checkboxChecked, NULL); + uiBoxAppend(box, uiControl(c), 0); + return c; +} + +struct example *mkEmptyStringExample(void) +{ + panel = uiNewHorizontalBox(); + vbox = uiNewVerticalBox(); + // TODO the second vbox causes this not to stretch at least on OS X + uiBoxAppend(panel, uiControl(vbox), 1); + caretLabel = uiNewLabel("Caret information is shown here"); + uiBoxAppend(vbox, uiControl(caretLabel), 0); + showLineBounds = newCheckbox(vbox, "Show Line Bounds (for debugging metrics)"); + vbox = uiNewVerticalBox(); + uiBoxAppend(panel, uiControl(vbox), 0); + fontButton = uiNewFontButton(); + uiFontButtonOnChanged(fontButton, changeFont, NULL); + // TODO set the font button to the current defaultFont + uiBoxAppend(vbox, uiControl(fontButton), 0); + textAlign = uiNewCombobox(); + // note that these are in the order in the enum + uiComboboxAppend(textAlign, "Left"); + uiComboboxAppend(textAlign, "Center"); + uiComboboxAppend(textAlign, "Right"); + uiComboboxOnSelected(textAlign, changeTextAlign, NULL); + uiBoxAppend(vbox, uiControl(textAlign), 0); + + hitTestExample.name = "Empty String"; + hitTestExample.panel = uiControl(panel); + hitTestExample.draw = draw; + hitTestExample.mouse = mouse; + hitTestExample.key = key; + + attrstr = uiNewAttributedString(text); + params.String = attrstr; + params.DefaultFont = &defaultFont; + params.Align = uiDrawTextLayoutAlignLeft; + + return &hitTestExample; +} diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c index 822382d0..4e72b25e 100644 --- a/examples/drawtext/main.c +++ b/examples/drawtext/main.c @@ -115,6 +115,11 @@ int main(void) uiControlHide(examples[n]->panel); uiBoxAppend(box, examples[n]->panel, 0); n++; + examples[n] = mkEmptyStringExample(); + uiComboboxAppend(exampleList, examples[n]->name); + uiControlHide(examples[n]->panel); + uiBoxAppend(box, examples[n]->panel, 0); + n++; // and set things up for the initial state uiComboboxSetSelected(exampleList, 0); uiComboboxOnSelected(exampleList, onExampleChanged, NULL); From 58fff53f614d9170eb2faef3f3d92abb1773fed1 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 24 Feb 2017 21:18:15 -0500 Subject: [PATCH 0706/1329] More TODOs... --- common/drawtext.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/drawtext.c b/common/drawtext.c index 938e1aeb..ca66796a 100644 --- a/common/drawtext.c +++ b/common/drawtext.c @@ -2,6 +2,8 @@ #include "../ui.h" #include "uipriv.h" +// TODO this doesn't handle the case where nLines == 0 +// TODO this should never happen even if there are no characters?? // TODO figure out how to make this work on GTK+ void uiDrawCaret(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t pos, int *line) { From c4400b83f35fe9242d494fd51812943d649d5eed Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 24 Feb 2017 21:24:52 -0500 Subject: [PATCH 0707/1329] Okay, notes on linelessness on OS X. --- darwin/drawtext.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 579d3863..c6bd9e86 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -2,7 +2,9 @@ #import "uipriv_darwin.h" #import "draw.h" -// TODO what happens if nLines == 0 in any function? +// TODO on an empty string nLines == 0 +// we must prevent this somehow +// TODO in general, every function could be more robust, but we cannot have a situation where there are zero lines struct uiDrawTextLayout { CFAttributedStringRef attrstr; From 3d5fbc0880db25a7ce819af9546a515cb02e24f4 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 24 Feb 2017 21:42:40 -0500 Subject: [PATCH 0708/1329] Checked empty strings on Pango. --- darwin/drawtext.m | 2 +- unix/drawtext.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index c6bd9e86..e5f0d12e 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -207,7 +207,7 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) } // TODO document that the width and height of a layout is not necessarily the sum of the widths and heights of its constituent lines -// TODO width doesn't include trailing whitespace... (TODO on which platforms?) +// TODO width doesn't include trailing whitespace... // TODO figure out how paragraph spacing should play into this void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height) { diff --git a/unix/drawtext.c b/unix/drawtext.c index d7a1d3ce..842b70dd 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -4,11 +4,13 @@ // TODO // - if the RTL override is at the beginning of a line, the preceding space is included? +// - nLines == 0: mostly works, except the width is wrong if the paragraph alignment is center or right... struct uiDrawTextLayout { PangoLayout *layout; GPtrArray *backgroundClosures; uiDrawTextLayoutLineMetrics *lineMetrics; + // TODO change everything to use this int nLines; }; From 70940e5c065102f87d819d01ad5689a224165942 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 24 Feb 2017 21:43:37 -0500 Subject: [PATCH 0709/1329] More TODOs. --- darwin/drawtext.m | 1 + unix/drawtext.c | 1 + 2 files changed, 2 insertions(+) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index e5f0d12e..fd5a5bbb 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -5,6 +5,7 @@ // TODO on an empty string nLines == 0 // we must prevent this somehow // TODO in general, every function could be more robust, but we cannot have a situation where there are zero lines +// TODO what happens to extents if only whitespace? struct uiDrawTextLayout { CFAttributedStringRef attrstr; diff --git a/unix/drawtext.c b/unix/drawtext.c index 842b70dd..6acb848a 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -5,6 +5,7 @@ // TODO // - if the RTL override is at the beginning of a line, the preceding space is included? // - nLines == 0: mostly works, except the width is wrong if the paragraph alignment is center or right... +// - TODO check whitespace and line bounds struct uiDrawTextLayout { PangoLayout *layout; From df2a726c1beabf7068674a6a111faad004b87da9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 25 Feb 2017 01:24:43 -0500 Subject: [PATCH 0710/1329] And fixed on Windows too. We're good. --- windows/drawtext.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 67ec0a47..275420da 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -5,7 +5,7 @@ // TODO // - consider the warnings about antialiasing in the PadWrite sample // - if that's not a problem, do we have overlapping rects in the hittest sample? I can't tell... -// - what happens if any nLines == 0? +// - empty string: nLines == 1 and all checks out except extents has x == 0 when not left aligned // - paragraph alignment is subject to RTL mirroring; see if it is on other platforms // TODO verify our renderer is correct, especially with regards to snapping From a0454a6b43ebf2bb705646580a7f25bd422b8a9c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 7 May 2017 10:30:08 -0400 Subject: [PATCH 0711/1329] Started dropping the whole features system in favor of a homogenous OpenType feature attribute just like every other API. Will make some things easier, hopefully... --- ui_attrstr.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/ui_attrstr.h b/ui_attrstr.h index f5f153c2..0077b6ee 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -32,6 +32,10 @@ _UI_ENUM(uiAttribute) { // TODO document that the color in the case we don't specify it is the text color uiAttributeUnderlineColor, // enum uiDrawUnderlineColor + uiAttributeFeatures, // object of type uiOpenTypeFeatures + +#if 0 + // These attributes represent typographic features. Each feature // is a separate attribute, to make composition easier. The // availability of for each attribute are defined by the font; the @@ -154,6 +158,8 @@ _UI_ENUM(uiAttribute) { uiAttributeLowercaseCapForms, // enum uiAttributeCapForm uiAttributeUppercaseCapForms, // enum uiAttributeCapForm }; +#endif +}; _UI_ENUM(uiDrawUnderlineStyle) { uiDrawUnderlineStyleNone, @@ -169,6 +175,8 @@ _UI_ENUM(uiDrawUnderlineColor) { uiDrawUnderlineColorAuxiliary, // for instance, the color used by smart replacements on OS X }; +#if 0 + _UI_ENUM(uiAttributeNumberSpacing) { uiAttributeNumberSpacingProportional, // AAT calls this "monospaced" @@ -208,6 +216,18 @@ _UI_ENUM(uiAttributeCapForm) { uiAttributeCapFormPetiteCaps, }; +#endif + +// TODO rename? +typedef struct uiOpenTypeFeatures uiOpenTypeFeatures; +// TODO detailed constructor? +_UI_EXTERN uiOpenTypeFeatures *uiNewOpenTypeFeatures(void); +_UI_EXTERN void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf); +_UI_EXTERN uiOpenTypeFeatures *uiOpenTypeFeaturesClone(uiOpenTypeFeatures *otf); +_UI_EXTERN void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value); +// TODO remove, query, enumerate +// TODO make the compare function public? + typedef struct uiAttributeSpec uiAttributeSpec; struct uiAttributeSpec { From 676dfb87f2a61e3e4bf24dd2036767beead67a52 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 11 May 2017 10:27:34 -0400 Subject: [PATCH 0712/1329] Started the uiOpenTypeFeatures implementationss, filling in holes in the API. --- darwin/opentype.m | 52 ++++++++++++++++++++++++++++++++++++++++++++ ui_attrstr.h | 11 ++++++++-- unix/opentype.c | 52 ++++++++++++++++++++++++++++++++++++++++++++ windows/opentype.cpp | 52 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 165 insertions(+), 2 deletions(-) create mode 100644 darwin/opentype.m create mode 100644 unix/opentype.c create mode 100644 windows/opentype.cpp diff --git a/darwin/opentype.m b/darwin/opentype.m new file mode 100644 index 00000000..4273b80f --- /dev/null +++ b/darwin/opentype.m @@ -0,0 +1,52 @@ +// 11 may 2017 +#include "uipriv_windows.hpp" + +struct uiOpenTypeFeatures { + xxxx; +}; + +uiOpenTypeFeatures *uiNewOpenTypeFeatures(void) +{ + uiOpenTypeFeatures *otf; + + otf = uiNew(uiOpenTypeFeatures); + xxxx; + return otf; +} + +void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf) +{ + xxxxxx; + uiFree(otf); +} + +uiOpenTypeFeatures *uiOpenTypeFeaturesClone(uiOpenTypeFeatures *otf) +{ + uiOpenTypeFeatures *out; + + out = uiNew(uiOpenTypeFeatures); + xxxxxx; + return out; +} + +void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value) +{ +} + +void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d) +{ +} + +int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value) +{ +} + +void uiOpenTypeFeaturesForEach(uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data) +{ +} + +int uiOpenTypeFeaturesEqual(uiOpenTypeFeatures *a, uiOpenTypeFeatures *b) +{ +} + +// TODO put the internal function to produce a backend object from one here diff --git a/ui_attrstr.h b/ui_attrstr.h index 0077b6ee..f7c03b03 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -220,13 +220,19 @@ _UI_ENUM(uiAttributeCapForm) { // TODO rename? typedef struct uiOpenTypeFeatures uiOpenTypeFeatures; +// TODO pass the feature set? +typedef int (*uiOpenTypeFeaturesForEachFunc)(char a, char b, char c, char d, uint32_t value, void *data); // TODO detailed constructor? _UI_EXTERN uiOpenTypeFeatures *uiNewOpenTypeFeatures(void); _UI_EXTERN void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf); +// TODO put above Free? +// TODO Copy instead of Clone? _UI_EXTERN uiOpenTypeFeatures *uiOpenTypeFeaturesClone(uiOpenTypeFeatures *otf); _UI_EXTERN void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value); -// TODO remove, query, enumerate -// TODO make the compare function public? +_UI_EXTERN void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d); +_UI_EXTERN int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value); +_UI_EXTERN void uiOpenTypeFeaturesForEach(uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data); +_UI_EXTERN int uiOpenTypeFeaturesEqual(uiOpenTypeFeatures *a, uiOpenTypeFeatures *b); typedef struct uiAttributeSpec uiAttributeSpec; @@ -240,6 +246,7 @@ struct uiAttributeSpec { double A; }; +// TODO name the foreach return values typedef int (*uiAttributedStringForEachAttributeFunc)(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data); // @role uiAttributedString constructor diff --git a/unix/opentype.c b/unix/opentype.c new file mode 100644 index 00000000..4273b80f --- /dev/null +++ b/unix/opentype.c @@ -0,0 +1,52 @@ +// 11 may 2017 +#include "uipriv_windows.hpp" + +struct uiOpenTypeFeatures { + xxxx; +}; + +uiOpenTypeFeatures *uiNewOpenTypeFeatures(void) +{ + uiOpenTypeFeatures *otf; + + otf = uiNew(uiOpenTypeFeatures); + xxxx; + return otf; +} + +void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf) +{ + xxxxxx; + uiFree(otf); +} + +uiOpenTypeFeatures *uiOpenTypeFeaturesClone(uiOpenTypeFeatures *otf) +{ + uiOpenTypeFeatures *out; + + out = uiNew(uiOpenTypeFeatures); + xxxxxx; + return out; +} + +void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value) +{ +} + +void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d) +{ +} + +int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value) +{ +} + +void uiOpenTypeFeaturesForEach(uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data) +{ +} + +int uiOpenTypeFeaturesEqual(uiOpenTypeFeatures *a, uiOpenTypeFeatures *b) +{ +} + +// TODO put the internal function to produce a backend object from one here diff --git a/windows/opentype.cpp b/windows/opentype.cpp new file mode 100644 index 00000000..4273b80f --- /dev/null +++ b/windows/opentype.cpp @@ -0,0 +1,52 @@ +// 11 may 2017 +#include "uipriv_windows.hpp" + +struct uiOpenTypeFeatures { + xxxx; +}; + +uiOpenTypeFeatures *uiNewOpenTypeFeatures(void) +{ + uiOpenTypeFeatures *otf; + + otf = uiNew(uiOpenTypeFeatures); + xxxx; + return otf; +} + +void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf) +{ + xxxxxx; + uiFree(otf); +} + +uiOpenTypeFeatures *uiOpenTypeFeaturesClone(uiOpenTypeFeatures *otf) +{ + uiOpenTypeFeatures *out; + + out = uiNew(uiOpenTypeFeatures); + xxxxxx; + return out; +} + +void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value) +{ +} + +void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d) +{ +} + +int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value) +{ +} + +void uiOpenTypeFeaturesForEach(uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data) +{ +} + +int uiOpenTypeFeaturesEqual(uiOpenTypeFeatures *a, uiOpenTypeFeatures *b) +{ +} + +// TODO put the internal function to produce a backend object from one here From 4e6ccc05f1ed71870a40ede1212de1346be3ac37 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 17 May 2017 15:57:39 -0400 Subject: [PATCH 0713/1329] Implemented the new opentype.cpp on Windows. --- windows/CMakeLists.txt | 1 + windows/opentype.cpp | 63 +++++++++++++++++++++++++++++++++++--- windows/uipriv_windows.hpp | 3 ++ 3 files changed, 62 insertions(+), 5 deletions(-) diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index f4a10998..739c1300 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -38,6 +38,7 @@ list(APPEND _LIBUI_SOURCES windows/main.cpp windows/menu.cpp windows/multilineentry.cpp + windows/opentype.cpp windows/parent.cpp windows/progressbar.cpp windows/radiobuttons.cpp diff --git a/windows/opentype.cpp b/windows/opentype.cpp index 4273b80f..21e681b7 100644 --- a/windows/opentype.cpp +++ b/windows/opentype.cpp @@ -1,8 +1,10 @@ // 11 may 2017 #include "uipriv_windows.hpp" +typedef std::map tagmap; + struct uiOpenTypeFeatures { - xxxx; + tagmap *tags; }; uiOpenTypeFeatures *uiNewOpenTypeFeatures(void) @@ -10,13 +12,13 @@ uiOpenTypeFeatures *uiNewOpenTypeFeatures(void) uiOpenTypeFeatures *otf; otf = uiNew(uiOpenTypeFeatures); - xxxx; + otf->tags = new tagmap; return otf; } void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf) { - xxxxxx; + delete otf->tags; uiFree(otf); } @@ -25,28 +27,79 @@ uiOpenTypeFeatures *uiOpenTypeFeaturesClone(uiOpenTypeFeatures *otf) uiOpenTypeFeatures *out; out = uiNew(uiOpenTypeFeatures); - xxxxxx; + out->tags = new tagmap; + *(out->tags) = *(otf->tags); return out; } +#define mktag(a, b, c, d) ((uint32_t) DWRITE_MAKE_OPENTYPE_TAG(a, b, c, d)) + void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value) { + (*(otf->tags))[mktag(a, b, c, d)] = value; } +// TODO what should happen if a/b/c/d isn't defined? +// TODO what does std::map do if a/b/c/d isn't defined? void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d) { + otf->tags->erase(mktag(a, b, c, d)); } int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value) { + tagmap::const_iterator iter; + + iter = otf->tags->find(mktag(a, b, c, d)); + if (iter == otf->tags->end()) + return 0; + *value = iter->second; + return 1; } void uiOpenTypeFeaturesForEach(uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data) { + tagmap::const_iterator iter, end; + + end = otf->tags->end(); + for (iter = otf->tags->begin(); iter != end; iter++) { + uint8_t a, b, c, d; + + a = (uint8_t) (iter->first & 0xFF); + b = (uint8_t) ((iter->first >> 8) & 0xFF); + c = (uint8_t) ((iter->first >> 16) & 0xFF); + d = (uint8_t) ((iter->first >> 24) & 0xFF); + // TODO handle return value + (*f)((char) a, (char) b, (char) c, (char) d, + iter->second, data); + } } +// TODO wait, is this function even necessary? how do we unify attributes?? int uiOpenTypeFeaturesEqual(uiOpenTypeFeatures *a, uiOpenTypeFeatures *b) { + // TODO make sure this is correct + return *(a->tags) == *(b->tags); } -// TODO put the internal function to produce a backend object from one here +IDWriteTypography *otfToDirectWrite(uiOpenTypeFeatures *otf) +{ + IDWriteTypography *dt; + tagmap::const_iterator iter, end; + DWRITE_FONT_FEATURE dff; + HRESULT hr; + + hr = dwfactory->CreateTypography(&dt); + if (hr != S_OK) + logHRESULT(L"error creating IDWriteTypography", hr); + end = otf->tags->end(); + for (iter = otf->tags->begin(); iter != end; iter++) { + ZeroMemory(&dff, sizeof (DWRITE_FONT_FEATURE)); + dff.nameTag = (DWRITE_FONT_FEATURE_TAG) (iter->first); + dff.parameter = (UINT32) (iter->second); + hr = dt->AddFontFeature(dff); + if (hr != S_OK) + logHRESULT(L"error adding OpenType feature to IDWriteTypography", hr); + } + return dt; +} diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index f1de95cf..aa39dd07 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -162,3 +162,6 @@ extern ID2D1DCRenderTarget *makeHDCRenderTarget(HDC dc, RECT *r); // drawtext.cpp extern void fontdescFromIDWriteFont(IDWriteFont *font, uiDrawFontDescriptor *uidesc); + +// opentype.cpp +extern IDWriteTypography *otfToDirectWrite(uiOpenTypeFeatures *otf); From 2f73df09e3b851c4392f11c72c86b3f8ec172730 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 17 May 2017 16:15:54 -0400 Subject: [PATCH 0714/1329] And adjusted the Windows attrstr.cpp to boot. Now to rewrite the example and test. --- ui_attrstr.h | 1 + windows/attrstr.cpp | 68 +++++++++++--------------------------------- windows/opentype.cpp | 1 - 3 files changed, 17 insertions(+), 53 deletions(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index f7c03b03..d66f2e9a 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -32,6 +32,7 @@ _UI_ENUM(uiAttribute) { // TODO document that the color in the case we don't specify it is the text color uiAttributeUnderlineColor, // enum uiDrawUnderlineColor + // TODO note that for the purpose of uiAttributedString two sets of features are only the same (and thus their attributes are merged) only if the pointers are the same; whether the tag sets are the same only become relevant to uiDrawTextLayout uiAttributeFeatures, // object of type uiOpenTypeFeatures #if 0 diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index ad2fad0e..3b652979 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -2,9 +2,11 @@ #include "uipriv_windows.hpp" #include "draw.hpp" +// TODO this whole file needs cleanup + // we need to combine color and underline style into one unit for IDWriteLayout::SetDrawingEffect() // we also need to collect all the OpenType features and background blocks and add them all at once -// TODO(TODO does not seem to apply here) this is the wrong approach; it causes Pango to end runs early, meaning attributes like the ligature attributes never get applied properly +// TODO(TODO does not seem to apply here?) this is the wrong approach; it causes Pango to end runs early, meaning attributes like the ligature attributes never get applied properly // TODO contextual alternates override ligatures? // TODO rename this struct to something that isn't exclusively foreach-ing? struct foreachParams { @@ -38,25 +40,22 @@ static void ensureEffectsInRange(struct foreachParams *p, size_t start, size_t e } } -static void ensureFeaturesInRange(struct foreachParams *p, size_t start, size_t end) +static void setFeaturesInRange(struct foreachParams *p, size_t start, size_t end, uiOpenTypeFeatures *otf) { + IDWriteTypography *dt; size_t i; - size_t *key; - IDWriteTypography *t; - HRESULT hr; + dt = otfToDirectWrite(otf); for (i = start; i < end; i++) { // don't create redundant entries; see below + // TODO explain this more clearly (surrogate pairs) if (!isCodepointStart(p->s[i])) continue; - t = (*(p->features))[i]; - if (t != NULL) - continue; - hr = dwfactory->CreateTypography(&t); - if (hr != S_OK) - logHRESULT(L"error creating IDWriteTypography", hr); - (*(p->features))[i] = t; + dt->AddRef(); + (*(p->features))[i] = dt; } + // and release the initial reference + dt->Release(); } static backgroundFunc mkBackgroundFunc(size_t start, size_t end, double r, double g, double b, double a) @@ -73,38 +72,6 @@ static backgroundFunc mkBackgroundFunc(size_t start, size_t end, double r, doubl }; } -struct otParam { - struct foreachParams *p; - size_t start; - size_t end; -}; - -static void doOpenType(const char *featureTag, uint32_t param, void *data) -{ - struct otParam *p = (struct otParam *) data; - size_t i; - IDWriteTypography *t; - DWRITE_FONT_FEATURE feature; - HRESULT hr; - - feature.nameTag = (DWRITE_FONT_FEATURE_TAG) DWRITE_MAKE_OPENTYPE_TAG( - featureTag[0], - featureTag[1], - featureTag[2], - featureTag[3]); - feature.parameter = param; - ensureFeaturesInRange(p->p, p->start, p->end); - for (i = p->start; i < p->end; i++) { - // don't use redundant entries; see below - if (!isCodepointStart(p->p->s[i])) - continue; - t = (*(p->p->features))[i]; - hr = t->AddFontFeature(feature); - if (hr != S_OK) - logHRESULT(L"error adding feature to IDWriteTypography", hr); - } -} - static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) { struct foreachParams *p = (struct foreachParams *) data; @@ -112,7 +79,6 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t WCHAR *wfamily; size_t ostart, oend; BOOL hasUnderline; - struct otParam op; HRESULT hr; ostart = start; @@ -229,14 +195,12 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t break; } break; - default: - // handle typographic features - op.p = p; - op.start = start; - op.end = end; - // TODO check if unhandled and complain - specToOpenType(spec, doOpenType, &op); + cae uiAttributeOpenTypeFeatures: + setFeaturesInRange(p, start, end, (uiOpenTypeFeatures *) (spec->Value)); break; + default: + // TODO complain + ; } return 0; } diff --git a/windows/opentype.cpp b/windows/opentype.cpp index 21e681b7..f3ca36c3 100644 --- a/windows/opentype.cpp +++ b/windows/opentype.cpp @@ -75,7 +75,6 @@ void uiOpenTypeFeaturesForEach(uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEac } } -// TODO wait, is this function even necessary? how do we unify attributes?? int uiOpenTypeFeaturesEqual(uiOpenTypeFeatures *a, uiOpenTypeFeatures *b) { // TODO make sure this is correct From 4f31a1331c04736cbe43477216baae4656f25e5b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 17 May 2017 16:18:28 -0400 Subject: [PATCH 0715/1329] And omitted the common OpenType stuff from the build. --- common/CMakeLists.txt | 1 - common/uipriv.h | 4 ---- 2 files changed, 5 deletions(-) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index c0542c82..8ecff8b3 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -8,7 +8,6 @@ list(APPEND _LIBUI_SOURCES common/debug.c common/drawtext.c common/matrix.c - common/opentype.c common/shouldquit.c common/userbugs.c common/utf.c diff --git a/common/uipriv.h b/common/uipriv.h index 1c61f9c7..553073f5 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -101,10 +101,6 @@ struct caretDrawParams { extern void caretDrawParams(uiDrawContext *c, double height, struct caretDrawParams *p); extern void drawTextBackground(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t start, size_t end, uiDrawBrush *brush, int isSelection); -// opentype.c -typedef void (*specToOpenTypeEnumFunc)(const char *featureTag, uint32_t param, void *data); -extern void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data); - #ifdef __cplusplus } #endif From 3e28887a24bdb36b64607ab07b1c0589ca4c9a15 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 17 May 2017 19:21:27 -0400 Subject: [PATCH 0716/1329] Fixed the build. --- common/attrlist.c | 4 ++-- windows/attrstr.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/common/attrlist.c b/common/attrlist.c index 7ea65fa6..ec0b12bd 100644 --- a/common/attrlist.c +++ b/common/attrlist.c @@ -342,9 +342,9 @@ static int specsIdentical(struct attr *attr, uiAttributeSpec *spec) attr->spec.G == spec->G && attr->spec.B == spec->B && attr->spec.A == spec->A; - // TODO use boolsEqual() on boolean features } - // handles the rest + // handles the rest, including pointer comparison for uiAttributeFeatures + // TODO rename it to uiAttributeOpenTypeFeatures? return attr->spec.Value == spec->Value; } diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index 3b652979..8f5e0adf 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -195,7 +195,7 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t break; } break; - cae uiAttributeOpenTypeFeatures: + case uiAttributeFeatures: setFeaturesInRange(p, start, end, (uiOpenTypeFeatures *) (spec->Value)); break; default: From 4f427b21218f4d342d06ea85a7761b7543bfeb4d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 17 May 2017 21:41:08 -0400 Subject: [PATCH 0717/1329] And ported the drawtext example to use the new features system. --- examples/drawtext/attributes.c | 264 +++++++++++++++++++-------------- examples/drawtext/drawtext.h | 1 + 2 files changed, 153 insertions(+), 112 deletions(-) diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index 7fb23fe5..56ed4d13 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -3,12 +3,32 @@ static uiAttributedString *attrstr; +#define nFeatures 256 +static uiOpenTypeFeatures *features[nFeatures]; +static int curFeature = 0; + +static uintptr_t addFeature(const char tag[4], uint32_t value) +{ + uiOpenTypeFeatures *otf; + + if (curFeature >= nFeatures) { + fprintf(stderr, "TODO (also TODO is there a panic function?)\n"); + exit(EXIT_FAILURE); + } + otf = uiNewOpenTypeFeatures(); + uiOpenTypeFeaturesAdd(otf, tag[0], tag[1], tag[2], tag[3], value); + features[curFeature] = otf; + curFeature++; + return (uintptr_t) otf; +} + // some of these examples come from Microsoft's and Apple's lists of typographic features and also https://www.fontfont.com/staticcontent/downloads/FF_OT_User_Guide.pdf static void setupAttributedString(void) { uiAttributeSpec spec; size_t start, end; const char *next; + uiOpenTypeFeatures *otf; int i; attrstr = uiNewAttributedString("uiAttributedString isn't just for plain text! It supports "); @@ -162,6 +182,8 @@ static void setupAttributedString(void) spec.Value = uiDrawUnderlineStyleSingle; uiAttributedStringSetAttribute(attrstr, &spec, start + 9, end - 1); + // TODO rewrite this to talk about OpenTpe instead + // TODO also shorten this to something more useful and that covers the general gist of things (and combines features arbitrarily like the previous demo) when we add a general OpenType demo (see the last TODO in this function) uiAttributedStringAppendUnattributed(attrstr, ". In addition, a variety of typographical features are available (depending on the chosen font) that can be switched on (or off, if the font enables them by default): "); next = "fi"; @@ -169,8 +191,8 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeStandardLigatures; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("liga", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -183,8 +205,8 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeRequiredLigatures; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("rlig", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, "\xE2\x80\xAC)"); @@ -195,8 +217,8 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeDiscretionaryLigatures; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("clig", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -207,20 +229,23 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeContextualLigatures; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("clig", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); uiAttributedStringAppendUnattributed(attrstr, ", "); + otf = (uiOpenTypeFeatures *) addFeature("hlig", 1); + // This technically isn't what is meant by "historical ligatures", but Core Text's internal AAT-to-OpenType mapping says to include it, so we include it too + uiOpenTypeFeaturesAdd(otf, 'h', 'i', 's', 't', 1); next = "\xC3\x9F"; uiAttributedStringAppendUnattributed(attrstr, "historical ligatures like the decomposition of \xC3\x9F ("); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeHistoricalLigatures; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = (uintptr_t) otf; uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -231,8 +256,8 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeUnicase; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("unic", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ", "); @@ -242,15 +267,15 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeNumberSpacings; - spec.Value = uiAttributeNumberSpacingProportional; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("pnum", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ") and tabular/monospaced ("); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeNumberSpacings; - spec.Value = uiAttributeNumberSpacingTabular; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("tnum", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ") numbers"); @@ -261,8 +286,8 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeSuperscripts; - spec.Value = uiAttributeSuperscriptSuperscript; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("sups", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -273,8 +298,8 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeSuperscripts; - spec.Value = uiAttributeSuperscriptSubscript; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("subs", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -285,8 +310,8 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeSuperscripts; - spec.Value = uiAttributeSuperscriptOrdinal; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("ordn", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -297,8 +322,8 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeSuperscripts; - spec.Value = uiAttributeSuperscriptScientificInferior; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("sinf", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -308,23 +333,25 @@ static void setupAttributedString(void) uiAttributedStringAppendUnattributed(attrstr, "fraction forms ("); start = uiAttributedStringLen(attrstr); end = start + strlen(next); +#if 0 /* TODO */ uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFractionForms; spec.Value = uiAttributeFractionFormNone; uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ", "); +#endif start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeFractionForms; - spec.Value = uiAttributeFractionFormVertical; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("afrc", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ", "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeFractionForms; - spec.Value = uiAttributeFractionFormDiagonal; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("frac", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -335,15 +362,15 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeSlashedZero; - spec.Value = 0; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("zero", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeSlashedZero; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("zero", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -354,15 +381,15 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeMathematicalGreek; - spec.Value = 0; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("mgrk", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeMathematicalGreek; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("mgrk", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -374,8 +401,8 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeOrnamentalForms; - spec.Value = (uintptr_t) i; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("ornm", i); uiAttributedStringSetAttribute(attrstr, &spec, start, end); next = "\xE2\x80\xA2"; } @@ -388,15 +415,15 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeSpecificCharacterForm; - spec.Value = 0; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("aalt", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeSpecificCharacterForm; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("aalt", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -407,15 +434,15 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeTitlingCapitalForms; - spec.Value = 0; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("titl", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeTitlingCapitalForms; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("titl", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -426,34 +453,40 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeHanCharacterForms; - spec.Value = uiAttributeHanCharacterFormJIS1978; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("jp78", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeHanCharacterForms; - spec.Value = uiAttributeHanCharacterFormJIS1983; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("jp83", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); uiAttributedStringAppendUnattributed(attrstr, ", "); + otf = (uiOpenTypeFeatures *) addFeature("onum", 0); + // Core Text's internal AAT-to-OpenType mapping says to include this, so we include it too + // TODO is it always set? + uiOpenTypeFeaturesAdd(otf, 'l', 'n', 'u', 'm', 0); next = "0123456789"; uiAttributedStringAppendUnattributed(attrstr, "lowercase numbers ("); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeLowercaseNumbers; - spec.Value = 0; + spec.Type = uiAttributeFeatures; + spec.Value = (uintptr_t) otf; uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeLowercaseNumbers; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + otf = (uiOpenTypeFeatures *) addFeature("onum", 1); + uiOpenTypeFeaturesAdd(otf, 'l', 'n', 'u', 'm', 1); + spec.Value = (uintptr_t) otf; uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -464,15 +497,15 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeHanjaToHangul; - spec.Value = 0; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("hngl", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeHanjaToHangul; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("hngl", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -483,22 +516,22 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeAnnotatedGlyphForms; - spec.Value = 0; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("nalt", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeAnnotatedGlyphForms; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("nalt", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeAnnotatedGlyphForms; - spec.Value = 4; // AAT inverted circle + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("nalt", 4); // AAT inverted circle uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -509,15 +542,15 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeRubyKanaForms; - spec.Value = 0; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("ruby", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeRubyKanaForms; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("ruby", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -528,15 +561,15 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeCJKRomansToItalics; - spec.Value = 0; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("ital", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeCJKRomansToItalics; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("ital", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -547,15 +580,15 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeCaseSensitiveForms; - spec.Value = 0; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("case", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeCaseSensitiveForms; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("case", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -566,15 +599,15 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeCapitalSpacing; - spec.Value = 0; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("cpsp", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeCapitalSpacing; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("cpsp", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -585,29 +618,29 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeAlternateHorizontalKana; - spec.Value = 0; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("hkna", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeAlternateHorizontalKana; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("hkna", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ") and vertical ("); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeAlternateVerticalKana; - spec.Value = 0; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("vkna", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeAlternateVerticalKana; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("vkna", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ") kana forms"); @@ -616,13 +649,20 @@ static void setupAttributedString(void) next = "g"; uiAttributedStringAppendUnattributed(attrstr, "stylistic alternates ("); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeStylisticAlternate1; - spec.Value = 1; - for (i = 0; i < 20; i++) { + spec.Type = uiAttributeFeatures; + for (i = 1; i <= 20; i++) { + char tag[4]; + + tag[0] = 's'; + tag[1] = 's'; + tag[2] = '0'; + if (i >= 10) + tag[2] = '1'; + tag[3] = (i % 10) + '0'; // TODO see how I wrote this elsewhere start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type++; + spec.Value = addFeature(tag, 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); } uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -634,15 +674,15 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeContextualAlternates; - spec.Value = 0; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("calt", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeContextualAlternates; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("calt", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -653,15 +693,15 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeSwashes; - spec.Value = 0; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("swsh", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeSwashes; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("swsh", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -672,15 +712,15 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeContextualSwashes; - spec.Value = 0; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("cswh", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeContextualSwashes; - spec.Value = 1; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("cswh", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -690,16 +730,16 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeLowercaseCapForms; - spec.Value = uiAttributeCapFormSmallCaps; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("smcp", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ", "); next = "Petite Caps"; start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeLowercaseCapForms; - spec.Value = uiAttributeCapFormPetiteCaps; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("pcap", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ", "); @@ -708,16 +748,16 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeUppercaseCapForms; - spec.Value = uiAttributeCapFormSmallCaps; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("c2sp", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ", and "); next = "PETITE UPPERCASES"; start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Type = uiAttributeUppercaseCapForms; - spec.Value = uiAttributeCapFormPetiteCaps; + spec.Type = uiAttributeFeatures; + spec.Value = addFeature("c2pc", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, "."); diff --git a/examples/drawtext/drawtext.h b/examples/drawtext/drawtext.h index 072c2f02..484dcec9 100644 --- a/examples/drawtext/drawtext.h +++ b/examples/drawtext/drawtext.h @@ -1,6 +1,7 @@ // 20 january 2017 #include #include +#include #include "../../ui.h" struct example { From ff4ab7110cb8b5ee303130484615145b3ee33acb Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 17 May 2017 22:56:55 -0400 Subject: [PATCH 0718/1329] Filled in GTK+ opentype.c. This is gonna suck as much as it does now... --- unix/CMakeLists.txt | 1 + unix/opentype.c | 158 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 153 insertions(+), 6 deletions(-) diff --git a/unix/CMakeLists.txt b/unix/CMakeLists.txt index 949c4a11..f21256c3 100644 --- a/unix/CMakeLists.txt +++ b/unix/CMakeLists.txt @@ -34,6 +34,7 @@ list(APPEND _LIBUI_SOURCES unix/main.c unix/menu.c unix/multilineentry.c + unix/opentype.c unix/progressbar.c unix/radiobuttons.c unix/separator.c diff --git a/unix/opentype.c b/unix/opentype.c index 4273b80f..12869a6a 100644 --- a/unix/opentype.c +++ b/unix/opentype.c @@ -1,8 +1,10 @@ // 11 may 2017 #include "uipriv_windows.hpp" +// TODO switch from GINT_FROM_POINTER() and so to a fake GUINT_FROM_POINTER()? + struct uiOpenTypeFeatures { - xxxx; + GHashTable *tags; }; uiOpenTypeFeatures *uiNewOpenTypeFeatures(void) @@ -10,43 +12,187 @@ uiOpenTypeFeatures *uiNewOpenTypeFeatures(void) uiOpenTypeFeatures *otf; otf = uiNew(uiOpenTypeFeatures); - xxxx; + otf->tags = g_hash_table_new(g_direct_hash, g_direct_equal); return otf; } void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf) { - xxxxxx; + g_hash_table_destroy(otf->tags); uiFree(otf); } +static void cloneTags(gpointer key, gpointer value, gpointer data) +{ + // TODO is there a G_HASH_TABLE()? + g_hash_table_replace((GHashTable *) data, key, value); +} + uiOpenTypeFeatures *uiOpenTypeFeaturesClone(uiOpenTypeFeatures *otf) { uiOpenTypeFeatures *out; - out = uiNew(uiOpenTypeFeatures); - xxxxxx; + // TODO switch the windows one to use this + out = uiNewOpenTypeFeatures(); + g_hash_table_foreach(otf->tags, cloneTags, out->tags); return out; } +static gpointer mkTag(char a, char b, char c, char d) +{ + uint32_t tag; + + tag = (((uint32_t) a) & 0xFF) << 24; + tag |= (((uint32_t) b) & 0xFF) << 16; + tag |= (((uint32_t) c) & 0xFF) << 8; + tag |= ((uint32_t) d) & 0xFF; + return GINT_TO_POINTER(tag); +} + void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value) { + g_hash_table_replace(otf->tags, mkTag(a, b, c, d), GINT_TO_POINTER(value)); } void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d) { + g_hash_table_remove(otf->tags, mkTag(a, b, c, d)); } +// TODO should this be before Add and Remove? +// TODO better name than Get? int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value) { + gboolean found; + gpointer gv; + + found = g_hash_table_lookup_extended(otf->tags, + mkTag(a, b, c, d), + NULL, &gv); + if (!found) + return 0; + *value = GPOINTER_TO_INT(gv); + return 1; +} + +struct otfForEach { + uiOpenTypeFeaturesForEachFunc f; + void *data; + // TODO store continuation status here +}; + +static void foreach(gpointer key, gpointer value, gpointer data) +{ + struct otfForEach *ofe = (struct otfForEach *) data; + uint32_t tag; + uint8_t a, b, c, d; + + tag = GPOINTER_TO_INT(key); + a = (uint8_t) ((tag >> 24) & 0xFF); + b = (uint8_t) ((tag >> 16) & 0xFF); + c = (uint8_t) ((tag >> 8) & 0xFF); + d = (uint8_t) (tag & 0xFF); + // TODO handle return value + (*(ofe->f))((char) a, (char) b, (char) c, (char) d, GPOINTER_TO_INT(value), ofe->data); } void uiOpenTypeFeaturesForEach(uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data) { + struct otfForEach ofe; + + memset(&ofe, 0, sizeof (struct otfForEach)); + ofe.f = f; + ofe.data = data; + g_hash_table_foreach(otf->tags, foreach, &ofe); +} + +static gint tagcmp(gpointer a, gpointer b) +{ + return GINT_FROM_POINTER(a) - GINT_FROM_POINTER(b); +} + +static GList *copySortedKeys(GHashTable *tags) +{ + GList *k, *copy; + + k = g_hash_table_get_keys(tags); + copy = g_list_copy(k); + copy = g_list_sort(copy, tagcmp); + // TODO do we free k? the docs contradict themselves + return copy; } int uiOpenTypeFeaturesEqual(uiOpenTypeFeatures *a, uiOpenTypeFeatures *b) { + GList *ak, *bk; + GList *ai, *bi; + guint na, nb; + guint i; + int equal = 0; + + ak = copySortedKeys(a); + bk = copySortedKeys(b); + + na = g_list_length(ak); + nb = g_list_length(bk); + if (na != nb) { + equal = 0; + goto out; + } + + // TODO use GINT_FROM_POINTER() in these? + ai = ak; + bi = bk; + for (i = 0; i < na; i++) { + gpointer av, bv; + + // compare keys + // this is why we needed to sort earlier + if (ai->data != bi->data) { + equal = 0; + goto out; + } + // and compare values + av = g_hash_table_lookup(a, ai->data); + bv = g_hash_table_lookup(b, bi->data); + if (av != bv) { + equal = 0; + goto out; + } + ai = ai->next; + bi = bi->next; + } + + // all good + equal = 1; + +out: + g_list_free(bk); + g_list_free(ak); + return equal; } -// TODO put the internal function to produce a backend object from one here +// TODO make this a g_hash_table_foreach() function (which requires duplicating code)? +static int toCSS(char a, char b, char c, char d, uint32_t value, void *data) +{ + // TODO is there a G_STRING()? + GString *s = (GString *) data; + + // the last trailing comma is removed after foreach is done + g_string_append_printf(s, "\"%c%c%c%c\" %" PRIu32 ", ", + a, b, c, d, value); + // TODO use this + return 0; +} + +GString *otfToPangoCSSString(uiOpenTypeFeatures *otf) +{ + GString *s; + + s = g_string_new(""); + uiOpenTypeFeaturesForEach(otf, toCSS, s); + if (s->len != 0) + // and remove the last comma + g_string_truncate(s, s->len - 2); + return s; +} From 39cec570d93d59c68ffc34ea2d5d5d56cc10b604 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 17 May 2017 23:37:16 -0400 Subject: [PATCH 0719/1329] And implemented the new features stuff on the GTK+ side. --- unix/attrstr.c | 58 +++++++++++----------------------------------- unix/opentype.c | 19 ++++++++------- unix/uipriv_unix.h | 3 +++ 3 files changed, 27 insertions(+), 53 deletions(-) diff --git a/unix/attrstr.c b/unix/attrstr.c index 88eabef4..7bf556c9 100644 --- a/unix/attrstr.c +++ b/unix/attrstr.c @@ -10,7 +10,7 @@ struct foreachParams { const char *s; PangoAttrList *attrs; // keys are pointers to size_t maintained by g_new0()/g_free() - // values are GStrings + // values are strings GHashTable *features; // TODO use pango's built-in background attribute? GPtrArray *backgroundClosures; @@ -38,24 +38,22 @@ static void freeFeatureString(gpointer s) #define isCodepointStart(b) (((uint8_t) (b)) <= 0x7F || ((uint8_t) (b)) >= 0xC0) -static void ensureFeaturesInRange(struct foreachParams *p, size_t start, size_t end) +static void setFeaturesInRange(struct foreachParams *p, size_t start, size_t end, uiOpenTypeFeatures *otf) { size_t i; size_t *key; GString *new; + new = otfToPangoCSSString(otf); for (i = start; i < end; i++) { // don't create redundant entries; see below if (!isCodepointStart(p->s[i])) continue; - new = (GString *) g_hash_table_lookup(p->features, &i); - if (new != NULL) - continue; - new = g_string_new(""); key = g_new0(size_t, 1); *key = i; - g_hash_table_replace(p->features, key, new); + g_hash_table_replace(p->features, key, g_strdup(new->str)); } + g_string_free(new, TRUE); } struct closureParams { @@ -104,30 +102,6 @@ static GClosure *mkBackgroundClosure(size_t start, size_t end, double r, double return closure; } -struct otParam { - struct foreachParams *p; - size_t start; - size_t end; -}; - -// see https://developer.mozilla.org/en/docs/Web/CSS/font-feature-settings -static void doOpenType(const char *featureTag, uint32_t param, void *data) -{ - struct otParam *p = (struct otParam *) data; - size_t i; - GString *s; - - ensureFeaturesInRange(p->p, p->start, p->end); - for (i = p->start; i < p->end; i++) { - // don't use redundant entries; see below - if (!isCodepointStart(p->p->s[i])) - continue; - s = (GString *) g_hash_table_lookup(p->p->features, &i); - g_string_append_printf(s, "\"%s\" %" PRIu32 ", ", - featureTag, param); - } -} - static void addattr(struct foreachParams *p, size_t start, size_t end, PangoAttribute *attr) { if (attr == NULL) // in case of a future attribute @@ -142,7 +116,6 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t struct foreachParams *p = (struct foreachParams *) data; GClosure *closure; PangoUnderline underline; - struct otParam op; switch (spec->Type) { case uiAttributeFamily: @@ -225,14 +198,13 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t break; } break; - default: - // handle typographic features - op.p = p; - op.start = start; - op.end = end; - // TODO check if unhandled and complain - specToOpenType(spec, doOpenType, &op); + case uiAttributeFeatures: + // TODO ensure the parentheses around spec->Value are provided on Windows + setFeaturesInRange(p, start, end, (uiOpenTypeFeatures *) (spec->Value)); break; + default: + // TODO complain + ; } return 0; } @@ -241,18 +213,16 @@ static gboolean applyFeatures(gpointer key, gpointer value, gpointer data) { struct foreachParams *p = (struct foreachParams *) data; size_t *pos = (size_t *) key; - GString *s = (GString *) value; + char *s = (char *) value; size_t n; - // remove the trailing comma/space - g_string_truncate(s, s->len - 2); // make sure we cover an entire code point // otherwise Pango will break apart multi-byte characters, spitting out U+FFFD characters at the invalid points n = 1; while (!isCodepointStart(p->s[*pos + n])) n++; addattr(p, *pos, *pos + n, - FUTURE_pango_attr_font_features_new(s->str)); + FUTURE_pango_attr_font_features_new(s)); return TRUE; // always delete; we're emptying the map } @@ -275,7 +245,7 @@ PangoAttrList *attrstrToPangoAttrList(uiDrawTextLayoutParams *p, GPtrArray **bac fep.attrs = pango_attr_list_new(); fep.features = g_hash_table_new_full( featurePosHash, featurePosEqual, - g_free, freeFeatureString); + g_free, g_free); fep.backgroundClosures = g_ptr_array_new_with_free_func(unrefClosure); uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); applyAndFreeFeatureAttributes(&fep); diff --git a/unix/opentype.c b/unix/opentype.c index 12869a6a..6ea317c5 100644 --- a/unix/opentype.c +++ b/unix/opentype.c @@ -1,7 +1,7 @@ // 11 may 2017 -#include "uipriv_windows.hpp" +#include "uipriv_unix.h" -// TODO switch from GINT_FROM_POINTER() and so to a fake GUINT_FROM_POINTER()? +// TODO switch from GINT_TO_POINTER() and so to a fake GUINT_TO_POINTER()? struct uiOpenTypeFeatures { GHashTable *tags; @@ -106,9 +106,9 @@ void uiOpenTypeFeaturesForEach(uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEac g_hash_table_foreach(otf->tags, foreach, &ofe); } -static gint tagcmp(gpointer a, gpointer b) +static gint tagcmp(gconstpointer a, gconstpointer b) { - return GINT_FROM_POINTER(a) - GINT_FROM_POINTER(b); + return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b); } static GList *copySortedKeys(GHashTable *tags) @@ -130,8 +130,8 @@ int uiOpenTypeFeaturesEqual(uiOpenTypeFeatures *a, uiOpenTypeFeatures *b) guint i; int equal = 0; - ak = copySortedKeys(a); - bk = copySortedKeys(b); + ak = copySortedKeys(a->tags); + bk = copySortedKeys(b->tags); na = g_list_length(ak); nb = g_list_length(bk); @@ -140,7 +140,7 @@ int uiOpenTypeFeaturesEqual(uiOpenTypeFeatures *a, uiOpenTypeFeatures *b) goto out; } - // TODO use GINT_FROM_POINTER() in these? + // TODO use GPOINTER_TO_INT() in these? ai = ak; bi = bk; for (i = 0; i < na; i++) { @@ -153,8 +153,8 @@ int uiOpenTypeFeaturesEqual(uiOpenTypeFeatures *a, uiOpenTypeFeatures *b) goto out; } // and compare values - av = g_hash_table_lookup(a, ai->data); - bv = g_hash_table_lookup(b, bi->data); + av = g_hash_table_lookup(a->tags, ai->data); + bv = g_hash_table_lookup(b->tags, bi->data); if (av != bv) { equal = 0; goto out; @@ -172,6 +172,7 @@ out: return equal; } +// see https://developer.mozilla.org/en/docs/Web/CSS/font-feature-settings // TODO make this a g_hash_table_foreach() function (which requires duplicating code)? static int toCSS(char a, char b, char c, char d, uint32_t value, void *data) { diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index bde6502f..cb5bf281 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -71,3 +71,6 @@ extern void invokeBackgroundClosure(GClosure *closure, uiDrawContext *c, uiDrawT #define cairoToPango(cairo) (pango_units_from_double(cairo)) extern const PangoStyle pangoItalics[]; extern const PangoStretch pangoStretches[]; + +// opentype.c +extern GString *otfToPangoCSSString(uiOpenTypeFeatures *otf); From 72f5b680f23e8dde26e0795af868f53fda2a1d2d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 19 May 2017 14:12:04 -0400 Subject: [PATCH 0720/1329] Filled in darwin/opentype.m. --- darwin/CMakeLists.txt | 1 + darwin/opentype.m | 58 ++++++++++++++++++++++++++++++++++++++----- 2 files changed, 53 insertions(+), 6 deletions(-) diff --git a/darwin/CMakeLists.txt b/darwin/CMakeLists.txt index 96db9c09..e260f266 100644 --- a/darwin/CMakeLists.txt +++ b/darwin/CMakeLists.txt @@ -31,6 +31,7 @@ list(APPEND _LIBUI_SOURCES darwin/map.m darwin/menu.m darwin/multilineentry.m + darwin/opentype.m darwin/progressbar.m darwin/radiobuttons.m darwin/scrollview.m diff --git a/darwin/opentype.m b/darwin/opentype.m index 4273b80f..c8158356 100644 --- a/darwin/opentype.m +++ b/darwin/opentype.m @@ -1,8 +1,8 @@ // 11 may 2017 -#include "uipriv_windows.hpp" +#import "uipriv_darwin.h" struct uiOpenTypeFeatures { - xxxx; + NSMutableDictionary *tags; }; uiOpenTypeFeatures *uiNewOpenTypeFeatures(void) @@ -10,13 +10,13 @@ uiOpenTypeFeatures *uiNewOpenTypeFeatures(void) uiOpenTypeFeatures *otf; otf = uiNew(uiOpenTypeFeatures); - xxxx; + otf->tags = [NSMutableDictionary new]; return otf; } void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf) { - xxxxxx; + [otf->tags release]; uiFree(otf); } @@ -25,28 +25,74 @@ uiOpenTypeFeatures *uiOpenTypeFeaturesClone(uiOpenTypeFeatures *otf) uiOpenTypeFeatures *out; out = uiNew(uiOpenTypeFeatures); - xxxxxx; + out->tags = [otf->tags mutableCopy]; return out; } +// TODO provide to aat.m too; remove x8tox32() when doing so +#define x8to32(x) ((uint32_t) (((uint8_t) (x)) & 0xFF)) +#define mkTag(a, b, c, d) \ + ((x8tox32(a) << 24) | \ + (x8tox32(b) << 16) | \ + (x8tox32(c) << 8) | \ + x8tox32(d)) + +// why are there no NSNumber methods for stdint.h or the equivalent core foundation types?... +#define mkMapObject(tag) [NSNumber numberWithUnsignedLongLong:((unsigned long long) tag)] +#define mapObjectValue(num) ((uint32_t) [num unsignedLongLongValue]) + void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value) { + NSNumber *tn, *vn; + + tn = mkMapObject(mkTag(a, b, c, d)); + vn = mkMapObject(value); + [otf->tags setObject:vn forKey:tn]; } void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d) { + NSNumber *tn; + + tn = mkMapObject(mkTag(a, b, c, d)); + [otf->tags removeObjectForKey:tn]; } int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value) { + NSNumber *tn, *vn; + + tn = mkMapObject(mkTag(a, b, c, d)); + vn = (NSNumber *) [otf->tags objectForKey:tn]; + if (vn == nil) + return 0; + *value = mapObjectValue(vn); + // TODO release vn? + return 1; } void uiOpenTypeFeaturesForEach(uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data) { + [otf->tags enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) { + NSNumber *tn = (NSNumber *) key; + NSNumber *vn = (NSNumber *) value; + uint32_t tag; + uint8_t a, b, c, d; + + tag = mapObjectValue(tn); + a = (uint8_t) ((tag >> 24) & 0xFF); + b = (uint8_t) ((tag >> 16) & 0xFF); + c = (uint8_t) ((tag >> 8) & 0xFF); + d = (uint8_t) (tag & 0xFF); + // TODO handle return value + (*f)((char) a, (char) b, (char) c, (char) d, + mapObjectValue(vn), data); + }]; } int uiOpenTypeFeaturesEqual(uiOpenTypeFeatures *a, uiOpenTypeFeatures *b) { + return [a->tags isEqualToDictionary:b->tags]; } -// TODO put the internal function to produce a backend object from one here +// actual conversion to a feature dictionary is handled in aat.m; see there for details From 1a2dd1f16bd747f357e85d62cfdf6706d16e32bc Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 19 May 2017 15:14:41 -0400 Subject: [PATCH 0721/1329] Converted darwin/aat.m to the new OpenType system. We can't use the code as-is just yet, though. --- darwin/aat.m | 583 ++++++++++++++++++++++++++------------------------- 1 file changed, 296 insertions(+), 287 deletions(-) diff --git a/darwin/aat.m b/darwin/aat.m index c7fc42e1..84b13c83 100644 --- a/darwin/aat.m +++ b/darwin/aat.m @@ -1,388 +1,397 @@ // 14 february 2017 #import "uipriv_darwin.h" -static void boolspec(uiAttributeSpec *spec, uint16_t type, uint16_t ifTrue, uint16_t ifFalse, specToAATEnumFunc f, void *data) +struct openTypeAATParams { + void (*doAAT)(uint16_t type, uint16_t selector, void *data); + void *data; +}; + +#define pcall(p, type, selector) ((*(p->doAAT))(type, selector, p->data)) + +static void boolspec(uint32_t value, uint16_t type, uint16_t ifTrue, uint16_t ifFalse, struct openTypeAATParams *p) { - if (spec->Value != 0) { - (*f)(type, ifTrue, data); + // TODO are values other than 1 accepted for true by OpenType itself? (same for the rest of the file) + if (value != 0) { + pcall(p, type, ifTrue); return; } - (*f)(type, ifFalse, data); + pcall(p, type, ifFalse); } -int specToAAT(uiAttributeSpec *spec, specToAATEnumFunc f, void *data) +void openTypeToAAT(char a, char b, char c, char d, uint32_t value, void *data) { - switch (spec->Type) { - case uiAttributeStandardLigatures: - boolspec(spec, kLigaturesType, + struct openTypeAATParams *p = (struct openTypeAATParams *) data; + + switch (mkTag(a, b, c, d)) { + case mkTag('l', 'i', 'g', 'a'): + boolspec(value, kLigaturesType, kCommonLigaturesOnSelector, kCommonLigaturesOffSelector, - f, data); - return 1; - case uiAttributeRequiredLigatures: - boolspec(spec, kLigaturesType, + p); + break; + case mkTag('r', 'l', 'i', 'g'): + boolspec(value, kLigaturesType, kRequiredLigaturesOnSelector, kRequiredLigaturesOffSelector, - f, data); - return 1; - case uiAttributeDiscretionaryLigatures: - boolspec(spec, kLigaturesType, + p); + break; + case mkTag('d', 'l', 'i', 'g'): + boolspec(value, kLigaturesType, kRareLigaturesOnSelector, kRareLigaturesOffSelector, - f, data); - return 1; - case uiAttributeContextualLigatures: - boolspec(spec, kLigaturesType, + p); + break; + case mkTag('c', 'l', 'i', 'g'): + boolspec(value, kLigaturesType, kContextualLigaturesOnSelector, kContextualLigaturesOffSelector, - f, data); - return 1; - case uiAttributeHistoricalLigatures: - boolspec(spec, kLigaturesType, + p); + break; + case mkTag('h', 'l', 'i', 'g'): + // This technically isn't what is meant by "historical ligatures", but Core Text's internal AAT-to-OpenType mapping says to include it, so we include it too + case mkTag('h', 'i', 's', 't'): + boolspec(value, kLigaturesType, kHistoricalLigaturesOnSelector, kHistoricalLigaturesOffSelector, - f, data); - return 1; - case uiAttributeUnicase: + p); + break; + case mkTag('u', 'n', 'i', 'c'): // TODO is this correct, or should we provide an else case? - if (spec->Value != 0) + if (value != 0) // this is undocumented; it comes from Core Text's internal AAT-to-OpenType conversion table - (*f)(kLetterCaseType, 14, data); - return 1; - // TODO make an array? - case uiAttributeNumberSpacings: - switch (spec->Value) { - case uiAttributeNumberSpacingProportional: - (*f)(kNumberSpacingType, kProportionalNumbersSelector, data); - break; - case uiAttributeNumberSpacingTabular: - (*f)(kNumberSpacingType, kMonospacedNumbersSelector, data); - break; - } - return 1; - // TODO make an array? - case uiAttributeSuperscripts: - switch (spec->Value) { - case uiAttributeSuperscriptNone: - (*f)(kVerticalPositionType, kNormalPositionSelector, data); - break; - case uiAttributeSuperscriptSuperscript: - (*f)(kVerticalPositionType, kSuperiorsSelector, data); - break; - case uiAttributeSuperscriptSubscript: - (*f)(kVerticalPositionType, kInferiorsSelector, data); - break; - case uiAttributeSuperscriptOrdinal: - (*f)(kVerticalPositionType, kOrdinalsSelector, data); - break; - case uiAttributeSuperscriptScientificInferior: - (*f)(kVerticalPositionType, kScientificInferiorsSelector, data); - break; - } - return 1; - // TODO make an array? - case uiAttributeFractionForms: - switch (spec->Value) { - case uiAttributeFractionFormNone: - (*f)(kFractionsType, kNoFractionsSelector, data); - break; - case uiAttributeFractionFormVertical: - (*f)(kFractionsType, kVerticalFractionsSelector, data); - break; - case uiAttributeFractionFormDiagonal: - (*f)(kFractionsType, kDiagonalFractionsSelector, data); - break; - } - return 1; - case uiAttributeSlashedZero: - boolspec(spec, kTypographicExtrasType, + pcall(p, kLetterCaseType, 14); + break; + + // TODO will the following handle all cases properly, or are elses going to be needed? + case mkTag('p', 'n', 'u', 'm'): + if (value != 0) + pcall(p, kNumberSpacingType, kProportionalNumbersSelector); + break; + case mkTag('t', 'n', 'u', 'm'): + if (value != 0) + pcall(p, kNumberSpacingType, kMonospacedNumbersSelector); + break; + + // TODO will the following handle all cases properly, or are elses going to be needed? + case mkTag('s', 'u', 'p', 's'): + if (value != 0) + pcall(p, kVerticalPositionType, kSuperiorsSelector); + break; + case mkTag('s', 'u', 'b', 's'): + if (value != 0) + pcall(p, kVerticalPositionType, kInferiorsSelector); + break; + case mkTag('o', 'r', 'd', 'n'): + if (value != 0) + pcall(p, kVerticalPositionType, kOrdinalsSelector); + break; + case mkTag('s', 'i', 'n', 'f'): + if (value != 0) + pcall(p, kVerticalPositionType, kScientificInferiorsSelector); + break; + + // TODO will the following handle all cases properly, or are elses going to be needed? + case mkTag('a', 'f', 'r', 'c'): + if (value != 0) + pcall(p, kFractionsType, kVerticalFractionsSelector); + break; + case mkTag('f', 'r', 'a', 'c'): + if (value != 0) + pcall(p, kFractionsType, kDiagonalFractionsSelector); + break; + + case mkTag('z', 'e', 'r', 'o'): + boolspec(value, kTypographicExtrasType, kSlashedZeroOnSelector, kSlashedZeroOffSelector, - f, data); - return 1; - case uiAttributeMathematicalGreek: - boolspec(spec, kMathematicalExtrasType, + p); + break; + case mkTag('m', 'g', 'r', 'k'): + boolspec(value, kMathematicalExtrasType, kMathematicalGreekOnSelector, kMathematicalGreekOffSelector, - f, data); - return 1; - case uiAttributeOrnamentalForms: - (*f)(kOrnamentSetsType, (uint16_t) (spec->Value), data); - return 1; - case uiAttributeSpecificCharacterForm: - (*f)(kCharacterAlternativesType, (uint16_t) (spec->Value), data); - return 1; - case uiAttributeTitlingCapitalForms: + p); + break; + case mkTag('o', 'r', 'n', 'm'): + pcall(p, kOrnamentSetsType, (uint16_t) value); + break; + case mkTag('a', 'a', 'l', 't'): + pcall(p, kCharacterAlternativesType, (uint16_t) value); + break; + case mkTag('t', 'i', 't', 'l'): // TODO is this correct, or should we provide an else case? if (spec->Value != 0) - (*f)(kStyleOptionsType, kTitlingCapsSelector, data); - return 1; - // TODO make an array? - case uiAttributeHanCharacterForms: - switch (spec->Value) { - case uiAttributeHanCharacterFormTraditional: - (*f)(kCharacterShapeType, kTraditionalCharactersSelector, data); - break; - case uiAttributeHanCharacterFormSimplified: - (*f)(kCharacterShapeType, kSimplifiedCharactersSelector, data); - break; - case uiAttributeHanCharacterFormJIS1978: - (*f)(kCharacterShapeType, kJIS1978CharactersSelector, data); - break; - case uiAttributeHanCharacterFormJIS1983: - (*f)(kCharacterShapeType, kJIS1983CharactersSelector, data); - break; - case uiAttributeHanCharacterFormJIS1990: - (*f)(kCharacterShapeType, kJIS1990CharactersSelector, data); - break; - case uiAttributeHanCharacterFormExpert: - (*f)(kCharacterShapeType, kExpertCharactersSelector, data); - break; - case uiAttributeHanCharacterFormJIS2004: - (*f)(kCharacterShapeType, kJIS2004CharactersSelector, data); - break; - case uiAttributeHanCharacterFormHojo: - (*f)(kCharacterShapeType, kHojoCharactersSelector, data); - break; - case uiAttributeHanCharacterFormNLC: - (*f)(kCharacterShapeType, kNLCCharactersSelector, data); - break; - case uiAttributeHanCharacterFormTraditionalNames: - (*f)(kCharacterShapeType, kTraditionalNamesCharactersSelector, data); - break; - } - return 1; - case uiAttributeLowercaseNumbers: + pcall(p, kStyleOptionsType, kTitlingCapsSelector); + break; + + // TODO will the following handle all cases properly, or are elses going to be needed? + case mkTag('t', 'r', 'a', 'd'): + if (value != 0) + pcall(p, kCharacterShapeType, kTraditionalCharactersSelector); + break; + case mkTag('s', 'm', 'p', 'l'): + if (value != 0) + pcall(p, kCharacterShapeType, kSimplifiedCharactersSelector); + break; + case mkTag('j', 'p', '7', '8'): + if (value != 0) + pcall(p, kCharacterShapeType, kJIS1978CharactersSelector); + break; + case mkTag('j', 'p', '8', '3'): + if (value != 0) + pcall(p, kCharacterShapeType, kJIS1983CharactersSelector); + break; + case mkTag('j', 'p', '9', '0'): + if (value != 0) + pcall(p, kCharacterShapeType, kJIS1990CharactersSelector); + break; + case mkTag('e', 'x', 'p', 't'): + if (value != 0) + pcall(p, kCharacterShapeType, kExpertCharactersSelector); + break; + case mkTag('j', 'p', '0', '4'): + if (value != 0) + pcall(p, kCharacterShapeType, kJIS2004CharactersSelector); + break; + case mkTag('h', 'o', 'j', 'o'): + if (value != 0) + pcall(p, kCharacterShapeType, kHojoCharactersSelector); + break; + case mkTag('n', 'l', 'c', 'k'): + if (value != 0) + pcall(p, kCharacterShapeType, kNLCCharactersSelector); + break; + case mkTag('t', 'n', 'a', 'm'): + if (value != 0) + pcall(p, kCharacterShapeType, kTraditionalNamesCharactersSelector); + break; + + case mkTag('o', 'n', 'u', 'm'): + // Core Text's internal AAT-to-OpenType mapping says to include this, so we include it too + // TODO is it always set? + case mkTag('l', 'n', 'u', 'm'): // TODO is this correct, or should we provide an else case? - if (spec->Value != 0) - (*f)(kNumberCaseType, kLowerCaseNumbersSelector, data); - return 1; - case uiAttributeHanjaToHangul: + if (value != 0) + pcall(p, kNumberCaseType, kLowerCaseNumbersSelector); + break; + case mkTag('h', 'n', 'g', 'l'): // TODO is this correct, or should we provide an else case? - if (spec->Value != 0) - (*f)(kTransliterationType, kHanjaToHangulSelector, data); - return 1; - case uiAttributeAnnotatedGlyphForms: - (*f)(kAnnotationType, (uint16_t) (spec->Value), data); - return 1; - case uiAttributeRubyKanaForms: + if (value != 0) + pcall(p, kTransliterationType, kHanjaToHangulSelector); + break; + case mkTag('n', 'a', 'l', 't'): + pcall(p, kAnnotationType, (uint16_t) value); + break; + case mkTag('r', 'u', 'b', 'y'): // include this for completeness - boolspec(spec, kRubyKanaType, + boolspec(value, kRubyKanaType, kRubyKanaSelector, kNoRubyKanaSelector, - f, data); + p); // this is the current one - boolspec(spec, kRubyKanaType, + boolspec(value, kRubyKanaType, kRubyKanaOnSelector, kRubyKanaOffSelector, - f, data); - return 1; - case uiAttributeCJKRomansToItalics: + p); + break; + case mkTag('i', 't', 'a', 'l'): // include this for completeness - boolspec(spec, kItalicCJKRomanType, + boolspec(value, kItalicCJKRomanType, kCJKItalicRomanSelector, kNoCJKItalicRomanSelector, - f, data); + p); // this is the current one - boolspec(spec, kItalicCJKRomanType, + boolspec(value, kItalicCJKRomanType, kCJKItalicRomanOnSelector, kCJKItalicRomanOffSelector, - f, data); - return 1; - case uiAttributeCaseSensitiveForms: - boolspec(spec, kCaseSensitiveLayoutType, + p); + break; + case mkTag('c', 'a', 's', 'e'): + boolspec(value, kCaseSensitiveLayoutType, kCaseSensitiveLayoutOnSelector, kCaseSensitiveLayoutOffSelector, - f, data); - return 1; - case uiAttributeCapitalSpacing: - boolspec(spec, kCaseSensitiveLayoutType, + p); + break; + case mkTag('c', 'p', 's', 'p'): + boolspec(value, kCaseSensitiveLayoutType, kCaseSensitiveSpacingOnSelector, kCaseSensitiveSpacingOffSelector, - f, data); - return 1; - case uiAttributeAlternateHorizontalKana: - boolspec(spec, kAlternateKanaType, + p); + break; + case mkTag('h', 'k', 'n', 'a'): + boolspec(value, kAlternateKanaType, kAlternateHorizKanaOnSelector, kAlternateHorizKanaOffSelector, - f, data); - return 1; - case uiAttributeAlternateVerticalKana: - boolspec(spec, kAlternateKanaType, + p); + break; + case mkTag('v', 'k', 'n', 'a'): + boolspec(value, kAlternateKanaType, kAlternateVertKanaOnSelector, kAlternateVertKanaOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate1: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '0', '1'): + boolspec(value, kStylisticAlternativesType, kStylisticAltOneOnSelector, kStylisticAltOneOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate2: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '0', '2'): + boolspec(value, kStylisticAlternativesType, kStylisticAltTwoOnSelector, kStylisticAltTwoOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate3: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '0', '3'): + boolspec(value, kStylisticAlternativesType, kStylisticAltThreeOnSelector, kStylisticAltThreeOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate4: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '0', '4'): + boolspec(value, kStylisticAlternativesType, kStylisticAltFourOnSelector, kStylisticAltFourOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate5: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '0', '5'): + boolspec(value, kStylisticAlternativesType, kStylisticAltFiveOnSelector, kStylisticAltFiveOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate6: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '0', '6'): + boolspec(value, kStylisticAlternativesType, kStylisticAltSixOnSelector, kStylisticAltSixOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate7: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '0', '7'): + boolspec(value, kStylisticAlternativesType, kStylisticAltSevenOnSelector, kStylisticAltSevenOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate8: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '0', '8'): + boolspec(value, kStylisticAlternativesType, kStylisticAltEightOnSelector, kStylisticAltEightOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate9: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '0', '9'): + boolspec(value, kStylisticAlternativesType, kStylisticAltNineOnSelector, kStylisticAltNineOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate10: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '1', '0'): + boolspec(value, kStylisticAlternativesType, kStylisticAltTenOnSelector, kStylisticAltTenOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate11: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '1', '1'): + boolspec(value, kStylisticAlternativesType, kStylisticAltElevenOnSelector, kStylisticAltElevenOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate12: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '1', '2'): + boolspec(value, kStylisticAlternativesType, kStylisticAltTwelveOnSelector, kStylisticAltTwelveOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate13: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '1', '3'): + boolspec(value, kStylisticAlternativesType, kStylisticAltThirteenOnSelector, kStylisticAltThirteenOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate14: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '1', '4'): + boolspec(value, kStylisticAlternativesType, kStylisticAltFourteenOnSelector, kStylisticAltFourteenOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate15: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '1', '5'): + boolspec(value, kStylisticAlternativesType, kStylisticAltFifteenOnSelector, kStylisticAltFifteenOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate16: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '1', '6'): + boolspec(value, kStylisticAlternativesType, kStylisticAltSixteenOnSelector, kStylisticAltSixteenOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate17: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '1', '7'): + boolspec(value, kStylisticAlternativesType, kStylisticAltSeventeenOnSelector, kStylisticAltSeventeenOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate18: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '1', '8'): + boolspec(value, kStylisticAlternativesType, kStylisticAltEighteenOnSelector, kStylisticAltEighteenOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate19: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '1', '9'): + boolspec(value, kStylisticAlternativesType, kStylisticAltNineteenOnSelector, kStylisticAltNineteenOffSelector, - f, data); - return 1; - case uiAttributeStylisticAlternate20: - boolspec(spec, kStylisticAlternativesType, + p); + break; + case mkTag('s', 's', '2', '0'): + boolspec(value, kStylisticAlternativesType, kStylisticAltTwentyOnSelector, kStylisticAltTwentyOffSelector, - f, data); - return 1; - case uiAttributeContextualAlternates: - boolspec(spec, kContextualAlternatesType, + p); + break; + case mkTag('c', 'a', 'l', 't'): + boolspec(value, kContextualAlternatesType, kContextualAlternatesOnSelector, kContextualAlternatesOffSelector, - f, data); - return 1; - case uiAttributeSwashes: - boolspec(spec, kContextualAlternatesType, + p); + break; + case mkTag('s', 'w', 's', 'h'): + boolspec(value, kContextualAlternatesType, kSwashAlternatesOnSelector, kSwashAlternatesOffSelector, - f, data); - return 1; - case uiAttributeContextualSwashes: - boolspec(spec, kContextualAlternatesType, + p); + break; + case mkTag('c', 's', 'w', 'h'): + boolspec(value, kContextualAlternatesType, kContextualSwashAlternatesOnSelector, kContextualSwashAlternatesOffSelector, - f, data); - return 1; - // TODO use arrays? - case uiAttributeLowercaseCapForms: - switch (spec->Value) { - case uiAttributeCapFormNormal: - (*f)(kLowerCaseType, kDefaultLowerCaseSelector, data); - break; - case uiAttributeCapFormSmallCaps: + p); + break; + + // TODO will the following handle all cases properly, or are elses going to be needed? + case mkTag('s', 'm', 'c', 'p'): + if (value != 0) { // include this for compatibility (some fonts that come with OS X still use this!) // TODO make it boolean? - (*f)(kLetterCaseType, kSmallCapsSelector, data); + pcall(p, kLetterCaseType, kSmallCapsSelector); // this is the current one - (*f)(kLowerCaseType, kLowerCaseSmallCapsSelector, data); - break; - case uiAttributeCapFormPetiteCaps: - (*f)(kLowerCaseType, kLowerCasePetiteCapsSelector, data); - break; + pcall(p, kLowerCaseType, kLowerCaseSmallCapsSelector); } - return 1; - // TODO use arrays? - case uiAttributeUppercaseCapForms: - switch (spec->Value) { - case uiAttributeCapFormNormal: - (*f)(kUpperCaseType, kDefaultUpperCaseSelector, data); - break; - case uiAttributeCapFormSmallCaps: - (*f)(kUpperCaseType, kUpperCaseSmallCapsSelector, data); - break; - case uiAttributeCapFormPetiteCaps: - (*f)(kUpperCaseType, kUpperCasePetiteCapsSelector, data); - break; - } - return 1; + break; + case mkTag('p', 'c', 'a', 'p'): + if (value != 0) + pcall(p, kLowerCaseType, kLowerCasePetiteCapsSelector); + break; + + // TODO will the following handle all cases properly, or are elses going to be needed? + case mkTag('c', '2', 's', 'c'): + if (value != 0) + pcall(p, kUpperCaseType, kUpperCaseSmallCapsSelector); + break; + case mkTag('c', '2', 'p', 'c'): + if (value != 0) + pcall(p, kUpperCaseType, kUpperCasePetiteCapsSelector); + break; } - return 0; } From b7d34bf4f5d4664960feeeb48215eba95a6b2a04 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 19 May 2017 15:48:15 -0400 Subject: [PATCH 0722/1329] And tied everyhting together, sort of. --- darwin/aat.m | 13 ++++++++++++- darwin/attrstr.m | 10 ++++++---- darwin/opentype.m | 8 -------- darwin/uipriv_darwin.h | 13 +++++++++++-- 4 files changed, 29 insertions(+), 15 deletions(-) diff --git a/darwin/aat.m b/darwin/aat.m index 84b13c83..45a97b00 100644 --- a/darwin/aat.m +++ b/darwin/aat.m @@ -18,7 +18,7 @@ static void boolspec(uint32_t value, uint16_t type, uint16_t ifTrue, uint16_t if pcall(p, type, ifFalse); } -void openTypeToAAT(char a, char b, char c, char d, uint32_t value, void *data) +static int foreach(char a, char b, char c, char d, uint32_t value, void *data) { struct openTypeAATParams *p = (struct openTypeAATParams *) data; @@ -394,4 +394,15 @@ void openTypeToAAT(char a, char b, char c, char d, uint32_t value, void *data) pcall(p, kUpperCaseType, kUpperCasePetiteCapsSelector); break; } + // TODO handle this properly + return 0; +} + +void openTypeToAAT(uiOpenTypeFeatures *otf, void (*doAAT)(uint16_t type, uint16_t selector, void *data), void *data) +{ + struct openTypeAATParams p; + + p.doAAT = doAAT; + p.data = data; + uiOpenTypeFeaturesForEach(otf, foreach, &p); } diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 3e5797b4..556b09ad 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -155,6 +155,7 @@ static void doAAT(uint16_t type, uint16_t selector, void *data) fp->nFeatures++; if (fp->nFeatures == maxFeatures) { // TODO + // TODO move this check to the top like in the drawtext example? and all the other instances of this? } }); } @@ -257,14 +258,15 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t if (spec->Value == uiDrawUnderlineColorCustom) CFRelease(color); break; - default: - // handle typographic features + case uiAttributeFeatures: ap.p = p; ap.start = start; ap.end = end; - // TODO check if unhandled and complain - specToAAT(spec, doAAT, &ap); + openTypeToAAT((uiOpenTypeFeatures *) (spec->Value), doAAT, &ap); break; + default: + // TODO complain + ; } return 0; } diff --git a/darwin/opentype.m b/darwin/opentype.m index c8158356..25939299 100644 --- a/darwin/opentype.m +++ b/darwin/opentype.m @@ -29,14 +29,6 @@ uiOpenTypeFeatures *uiOpenTypeFeaturesClone(uiOpenTypeFeatures *otf) return out; } -// TODO provide to aat.m too; remove x8tox32() when doing so -#define x8to32(x) ((uint32_t) (((uint8_t) (x)) & 0xFF)) -#define mkTag(a, b, c, d) \ - ((x8tox32(a) << 24) | \ - (x8tox32(b) << 16) | \ - (x8tox32(c) << 8) | \ - x8tox32(d)) - // why are there no NSNumber methods for stdint.h or the equivalent core foundation types?... #define mkMapObject(tag) [NSNumber numberWithUnsignedLongLong:((unsigned long long) tag)] #define mapObjectValue(num) ((uint32_t) [num unsignedLongLongValue]) diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index c3de53e2..8c4bff07 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -152,5 +152,14 @@ typedef void (^backgroundBlock)(uiDrawContext *c, uiDrawTextLayout *layout, doub extern CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks); // aat.m -typedef void (*specToAATEnumFunc)(uint16_t type, uint16_t selector, void *data); -extern int specToAAT(uiAttributeSpec *spec, specToAATEnumFunc f, void *data); +extern void openTypeToAAT(uiOpenTypeFeatures *otf, void (*doAAT)(uint16_t type, uint16_t selector, void *data), void *data); + +// opentype.m +// TODO this is only used by opentype.m and aat.m; figure out some better way to handle this +// TODO remove x8tox32() +#define x8to32(x) ((uint32_t) (((uint8_t) (x)) & 0xFF)) +#define mkTag(a, b, c, d) \ + ((x8tox32(a) << 24) | \ + (x8tox32(b) << 16) | \ + (x8tox32(c) << 8) | \ + x8tox32(d)) From bd39189a0e7820208b2a27c5cef566ceaa7c9ab5 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 19 May 2017 16:10:54 -0400 Subject: [PATCH 0723/1329] Fixed the build and an unspotted error in the drawtext example. --- darwin/aat.m | 3 ++- darwin/uipriv_darwin.h | 2 +- examples/drawtext/attributes.c | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/darwin/aat.m b/darwin/aat.m index 45a97b00..0c964e05 100644 --- a/darwin/aat.m +++ b/darwin/aat.m @@ -18,6 +18,7 @@ static void boolspec(uint32_t value, uint16_t type, uint16_t ifTrue, uint16_t if pcall(p, type, ifFalse); } +// TODO double-check drawtext example to make sure all of these are used properly (I already screwed dlig up by putting clig twice instead) static int foreach(char a, char b, char c, char d, uint32_t value, void *data) { struct openTypeAATParams *p = (struct openTypeAATParams *) data; @@ -120,7 +121,7 @@ static int foreach(char a, char b, char c, char d, uint32_t value, void *data) break; case mkTag('t', 'i', 't', 'l'): // TODO is this correct, or should we provide an else case? - if (spec->Value != 0) + if (value != 0) pcall(p, kStyleOptionsType, kTitlingCapsSelector); break; diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 8c4bff07..fe65f201 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -157,7 +157,7 @@ extern void openTypeToAAT(uiOpenTypeFeatures *otf, void (*doAAT)(uint16_t type, // opentype.m // TODO this is only used by opentype.m and aat.m; figure out some better way to handle this // TODO remove x8tox32() -#define x8to32(x) ((uint32_t) (((uint8_t) (x)) & 0xFF)) +#define x8tox32(x) ((uint32_t) (((uint8_t) (x)) & 0xFF)) #define mkTag(a, b, c, d) \ ((x8tox32(a) << 24) | \ (x8tox32(b) << 16) | \ diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index 56ed4d13..46bf1b0d 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -218,7 +218,7 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("clig", 1); + spec.Value = addFeature("dlig", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); From 90962e18c4e01ad1f8c9f927b1a911ed09e00773 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 19 May 2017 16:14:39 -0400 Subject: [PATCH 0724/1329] And got rid of the remaining old stuff entirely. --- common/opentype.c | 314 ---------------------------------------------- ui_attrstr.h | 169 ------------------------- 2 files changed, 483 deletions(-) delete mode 100644 common/opentype.c diff --git a/common/opentype.c b/common/opentype.c deleted file mode 100644 index 3b2ddf64..00000000 --- a/common/opentype.c +++ /dev/null @@ -1,314 +0,0 @@ -// 14 february 2017 -#include "../ui.h" -#include "uipriv.h" - -// Notes: -// - Each tag should only appear in quotes once (including within comments); this allows automated tools to determine what we cover and don't cover - -static void boolspec(uiAttributeSpec *spec, const char *featureTag, specToOpenTypeEnumFunc f, void *data) -{ - if (spec->Value != 0) { - (*f)(featureTag, 1, data); - return; - } - (*f)(featureTag, 0, data); -} - -static void boolspecnot(uiAttributeSpec *spec, const char *featureTag, specToOpenTypeEnumFunc f, void *data) -{ - if (spec->Value == 0) { - (*f)(featureTag, 1, data); - return; - } - (*f)(featureTag, 0, data); -} - -void specToOpenType(uiAttributeSpec *spec, specToOpenTypeEnumFunc f, void *data) -{ - switch (spec->Type) { - case uiAttributeStandardLigatures: - boolspec(spec, "liga", f, data); - return; - case uiAttributeRequiredLigatures: - boolspec(spec, "rlig", f, data); - return; - case uiAttributeDiscretionaryLigatures: - boolspec(spec, "dlig", f, data); - return; - case uiAttributeContextualLigatures: - boolspec(spec, "clig", f, data); - return; - case uiAttributeHistoricalLigatures: - boolspec(spec, "hlig", f, data); - // This technically isn't what is meant by "historical ligatures", but Core Text's internal AAT-to-OpenType mapping says to include it, so we include it too - boolspec(spec, "hist", f, data); - return; - case uiAttributeUnicase: - boolspec(spec, "unic", f, data); - return; - // TODO is this correct or should we explicitly switch the rest off too? - case uiAttributeNumberSpacings: - // TODO does Core Text set both? do we? - switch (spec->Value) { - case uiAttributeNumberSpacingProportional: - (*f)("pnum", 1, data); - break; - case uiAttributeNumberSpacingTabular: - (*f)("tnum", 1, data); - break; - } - return; - case uiAttributeSuperscripts: - switch (spec->Value) { - case uiAttributeSuperscriptSuperscript: - (*f)("sups", 1, data); - break; - case uiAttributeSuperscriptSubscript: - (*f)("subs", 1, data); - break; - case uiAttributeSuperscriptOrdinal: - (*f)("ordn", 1, data); - break; - case uiAttributeSuperscriptScientificInferior: - (*f)("sinf", 1, data); - break; - } - return; - // TODO is this correct or should we explicitly switch the rest off too? - case uiAttributeFractionForms: - switch (spec->Value) { - case uiAttributeFractionFormVertical: - (*f)("afrc", 1, data); - break; - case uiAttributeFractionFormDiagonal: - (*f)("frac", 1, data); - break; - } - return; - case uiAttributeSlashedZero: - boolspec(spec, "zero", f, data); - return; - case uiAttributeMathematicalGreek: - boolspec(spec, "mgrk", f, data); - return; - case uiAttributeOrnamentalForms: - (*f)("ornm", (uint32_t) (spec->Value), data); - return; - case uiAttributeSpecificCharacterForm: - (*f)("aalt", (uint32_t) (spec->Value), data); - return; - case uiAttributeTitlingCapitalForms: - boolspec(spec, "titl", f, data); - return; - // TODO is this correct or should we explicitly switch the rest off too? - case uiAttributeHanCharacterForms: - switch (spec->Value) { - case uiAttributeHanCharacterFormTraditional: - (*f)("trad", 1, data); - break; - case uiAttributeHanCharacterFormSimplified: - (*f)("smpl", 1, data); - break; - case uiAttributeHanCharacterFormJIS1978: - (*f)("jp78", 1, data); - break; - case uiAttributeHanCharacterFormJIS1983: - (*f)("jp83", 1, data); - break; - case uiAttributeHanCharacterFormJIS1990: - (*f)("jp90", 1, data); - break; - case uiAttributeHanCharacterFormExpert: - (*f)("expt", 1, data); - break; - case uiAttributeHanCharacterFormJIS2004: - (*f)("jp04", 1, data); - break; - case uiAttributeHanCharacterFormHojo: - (*f)("hojo", 1, data); - break; - case uiAttributeHanCharacterFormNLC: - (*f)("nlck", 1, data); - break; - case uiAttributeHanCharacterFormTraditionalNames: - (*f)("tnam", 1, data); - break; - } - return; - case uiAttributeLowercaseNumbers: - boolspec(spec, "onum", f, data); - // Core Text's internal AAT-to-OpenType mapping says to include this, so we include it too - // TODO is it always set? - boolspecnot(spec, "lnum", f, data); - return; - case uiAttributeHanjaToHangul: - boolspec(spec, "hngl", f, data); - return; - case uiAttributeAnnotatedGlyphForms: - (*f)("nalt", (uint32_t) (spec->Value), data); - return; - case uiAttributeRubyKanaForms: - boolspec(spec, "ruby", f, data); - return; - case uiAttributeCJKRomansToItalics: - boolspec(spec, "ital", f, data); - return; - case uiAttributeCaseSensitiveForms: - boolspec(spec, "case", f, data); - return; - case uiAttributeCapitalSpacing: - boolspec(spec, "cpsp", f, data); - return; - case uiAttributeAlternateHorizontalKana: - boolspec(spec, "hkna", f, data); - return; - case uiAttributeAlternateVerticalKana: - boolspec(spec, "vkna", f, data); - return; - case uiAttributeStylisticAlternate1: - boolspec(spec, "ss01", f, data); - return; - case uiAttributeStylisticAlternate2: - boolspec(spec, "ss02", f, data); - return; - case uiAttributeStylisticAlternate3: - boolspec(spec, "ss03", f, data); - return; - case uiAttributeStylisticAlternate4: - boolspec(spec, "ss04", f, data); - return; - case uiAttributeStylisticAlternate5: - boolspec(spec, "ss05", f, data); - return; - case uiAttributeStylisticAlternate6: - boolspec(spec, "ss06", f, data); - return; - case uiAttributeStylisticAlternate7: - boolspec(spec, "ss07", f, data); - return; - case uiAttributeStylisticAlternate8: - boolspec(spec, "ss08", f, data); - return; - case uiAttributeStylisticAlternate9: - boolspec(spec, "ss09", f, data); - return; - case uiAttributeStylisticAlternate10: - boolspec(spec, "ss10", f, data); - return; - case uiAttributeStylisticAlternate11: - boolspec(spec, "ss11", f, data); - return; - case uiAttributeStylisticAlternate12: - boolspec(spec, "ss12", f, data); - return; - case uiAttributeStylisticAlternate13: - boolspec(spec, "ss13", f, data); - return; - case uiAttributeStylisticAlternate14: - boolspec(spec, "ss14", f, data); - return; - case uiAttributeStylisticAlternate15: - boolspec(spec, "ss15", f, data); - return; - case uiAttributeStylisticAlternate16: - boolspec(spec, "ss16", f, data); - return; - case uiAttributeStylisticAlternate17: - boolspec(spec, "ss17", f, data); - return; - case uiAttributeStylisticAlternate18: - boolspec(spec, "ss18", f, data); - return; - case uiAttributeStylisticAlternate19: - boolspec(spec, "ss19", f, data); - return; - case uiAttributeStylisticAlternate20: - boolspec(spec, "ss20", f, data); - return; - case uiAttributeContextualAlternates: - boolspec(spec, "calt", f, data); - return; - case uiAttributeSwashes: - boolspec(spec, "swsh", f, data); - return; - case uiAttributeContextualSwashes: - boolspec(spec, "cswh", f, data); - return; - // TODO is this correct or should we explicitly switch the rest off too? - case uiAttributeLowercaseCapForms: - switch (spec->Value) { - case uiAttributeCapFormSmallCaps: - (*f)("smcp", 1, data); - break; - case uiAttributeCapFormPetiteCaps: - (*f)("pcap", 1, data); - break; - } - return; - // TODO is this correct or should we explicitly switch the rest off too? - case uiAttributeUppercaseCapForms: - switch (spec->Value) { - case uiAttributeCapFormSmallCaps: - (*f)("c2sc", 1, data); - break; - case uiAttributeCapFormPetiteCaps: - (*f)("c2pc", 1, data); - break; - } - return; - } -} - -// TODO missing that AAT uses directly: -// - pkna, pwid, fwid, hwid, twid, qwid, palt, valt, vpal, halt, vhal, kern, vkrn (CJK width control) -// missing that AAT knows about: -// - ccmp (compositions) -// - dnom, numr (fraction parts) — no AAT equivalent... -// - falt, jalt (Arabic support) -// - rclt (required contextual alternates) -// - lfbd, opbd, rtbd (optical bounds support) -// - locl (Cyrillic support) -// - ltra, ltrm, rtla, rtlm (bidi support) -// - mark, mkmk (mark positioning) -// - rand (random glyph selection candidates) -// - salt (stylistic alternatives) -// - size (sizing info) -// -// script-specific; core text and pango/harfbuzz use these automatically based on the language -// TODO if DirectWrite does too we can ignore them and just provide a language attribute (they all use BCP 47 syntax for language names) -// Tag Core Text? Harfbuzz? -// abvf yes yes -// abvm yes yes -// abvs yes TODO -// akhn yes yes -// blwf yes yes -// blwm yes yes -// blws yes TODO -// cjct yes yes -// curs yes yes -// dist yes yes -// falt TODO TODO -// fin2 yes yes -// fin3 yes yes -// fina yes yes -// half yes yes -// haln yes yes -// init yes yes -// isol yes yes -// jalt TODO TODO -// ljmo yes yes -// locl TODO all horz(!) -// med2 yes yes -// medi yes yes -// mset TODO yes -// nukt yes yes -// pref yes yes -// pres yes yes -// pstf yes yes -// psts yes yes -// rclt TODO all horz(!) -// rkrf yes yes -// rphf yes yes -// tjmo yes yes -// vatu yes yes -// vjmo yes yes diff --git a/ui_attrstr.h b/ui_attrstr.h index d66f2e9a..4d13acb7 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -34,132 +34,6 @@ _UI_ENUM(uiAttribute) { // TODO note that for the purpose of uiAttributedString two sets of features are only the same (and thus their attributes are merged) only if the pointers are the same; whether the tag sets are the same only become relevant to uiDrawTextLayout uiAttributeFeatures, // object of type uiOpenTypeFeatures - -#if 0 - - // These attributes represent typographic features. Each feature - // is a separate attribute, to make composition easier. The - // availability of for each attribute are defined by the font; the - // default values are defined by the font and/or by the OS. - // - // A note about features whose parameter is an enumeration: - // OS X defines typographic features using the AAT specification - // and converts to OpenType internally when needed, whereas - // other platforms use OpenType directly. OpenType is less - // precise about what each enumeration value means than AAT - // is, so enumeration values do not necessarily represent what - // OS X expects with all fonts. In cases where they do, libui - // provides an enumeration type to use. Otherwise, the AAT - // enumeration values are provided in comments for - // documentation purposes. - - // AAT calls these "common ligatures" - uiAttributeStandardLigatures, // 0 = off, 1 = on - uiAttributeRequiredLigatures, // 0 = off, 1 = on - // AAT calls these "rare ligatures" - uiAttributeDiscretionaryLigatures, // 0 = off, 1 = on - uiAttributeContextualLigatures, // 0 = off, 1 = on - uiAttributeHistoricalLigatures, // 0 = off, 1 = on - - uiAttributeUnicase, // 0 = off, 1 = on - - // TODO rename this - uiAttributeNumberSpacings, // enum uiAttributeNumberSpacing - - uiAttributeSuperscripts, // enum uiAttributeSuperscript - - uiAttributeFractionForms, // enum uiAttributeFractionForm - - uiAttributeSlashedZero, // 0 = off, 1 = on - - uiAttributeMathematicalGreek, // 0 = off, 1 = on - - // AAT defines the following values: - // 0 = none - // 1 = dingbats - // 2 = pi characters - // 3 = fleurons - // 4 = decorative borders - // 5 = international symbols - // 6 = mathematical symbols - // OpenType says alphanumeric characters must(? TODO) have one form each and the bullet character U+2022 (•) can have many - uiAttributeOrnamentalForms, // an integer from 0 to a font-specified upper bound - // TODO provide a function to get the upper bound? - - // AAT calls this "character alternatives" and defines the - // following values: - // 0 = none - // OpenType calls this "access all alternates". - uiAttributeSpecificCharacterForm, // an integer from 0 to a font-specified upper bound - // TODO provide a function to get the upper bound? - - uiAttributeTitlingCapitalForms, // 0 = off, 1 = on - - // AAT calls these "character shapes" - uiAttributeHanCharacterForms, // enum uiAttributeHanCharacterForm - - // OpenType calls these "old-style" - uiAttributeLowercaseNumbers, // 0 = off, 1 = on - - uiAttributeHanjaToHangul, // 0 = off, 1 = on - - // AAT defines the following values: - // 0 = none - // 1 = box - // 2 = rounded box - // 3 = circle - // 4 = inverted circle - // 5 = parentheses - // 6 = period - // 7 = roman numeral - // 8 = diamond - // 9 = inverted box - // 10 = inverted rounded box - uiAttributeAnnotatedGlyphForms, // an integer from 0 to a font-specified upper bound - // TODO provide a function to get the upper bound? - - uiAttributeRubyKanaForms, // 0 = off, 1 = on - - uiAttributeCJKRomansToItalics, // 0 = off, 1 = on - - // AAT calls this "case-sensitive layout" - uiAttributeCaseSensitiveForms, // 0 = off, 1 = on - // AAT: this is called "case-sensitive spacing" - uiAttributeCapitalSpacing, // 0 = off, 1 = on - - uiAttributeAlternateHorizontalKana, // 0 = off, 1 = on - uiAttributeAlternateVerticalKana, // 0 = off, 1 = on - - // TODO document that these are guaranteed to be consecutive - uiAttributeStylisticAlternate1, // 0 = off, 1 = on - uiAttributeStylisticAlternate2, // 0 = off, 1 = on - uiAttributeStylisticAlternate3, // 0 = off, 1 = on - uiAttributeStylisticAlternate4, // 0 = off, 1 = on - uiAttributeStylisticAlternate5, // 0 = off, 1 = on - uiAttributeStylisticAlternate6, // 0 = off, 1 = on - uiAttributeStylisticAlternate7, // 0 = off, 1 = on - uiAttributeStylisticAlternate8, // 0 = off, 1 = on - uiAttributeStylisticAlternate9, // 0 = off, 1 = on - uiAttributeStylisticAlternate10, // 0 = off, 1 = on - uiAttributeStylisticAlternate11, // 0 = off, 1 = on - uiAttributeStylisticAlternate12, // 0 = off, 1 = on - uiAttributeStylisticAlternate13, // 0 = off, 1 = on - uiAttributeStylisticAlternate14, // 0 = off, 1 = on - uiAttributeStylisticAlternate15, // 0 = off, 1 = on - uiAttributeStylisticAlternate16, // 0 = off, 1 = on - uiAttributeStylisticAlternate17, // 0 = off, 1 = on - uiAttributeStylisticAlternate18, // 0 = off, 1 = on - uiAttributeStylisticAlternate19, // 0 = off, 1 = on - uiAttributeStylisticAlternate20, // 0 = off, 1 = on - - uiAttributeContextualAlternates, // 0 = off, 1 = on - uiAttributeSwashes, // 0 = off, 1 = on - uiAttributeContextualSwashes, // 0 = off, 1 = on - - uiAttributeLowercaseCapForms, // enum uiAttributeCapForm - uiAttributeUppercaseCapForms, // enum uiAttributeCapForm -}; -#endif }; _UI_ENUM(uiDrawUnderlineStyle) { @@ -176,49 +50,6 @@ _UI_ENUM(uiDrawUnderlineColor) { uiDrawUnderlineColorAuxiliary, // for instance, the color used by smart replacements on OS X }; -#if 0 - -_UI_ENUM(uiAttributeNumberSpacing) { - uiAttributeNumberSpacingProportional, - // AAT calls this "monospaced" - uiAttributeNumberSpacingTabular, -}; - -_UI_ENUM(uiAttributeSuperscript) { - uiAttributeSuperscriptNone, - uiAttributeSuperscriptSuperscript, // AAT: "superior" - uiAttributeSuperscriptSubscript, // AAT: "inferior" - uiAttributeSuperscriptOrdinal, - uiAttributeSuperscriptScientificInferior, -}; - -_UI_ENUM(uiAttributeFractionForm) { - uiAttributeFractionFormNone, - uiAttributeFractionFormVertical, - uiAttributeFractionFormDiagonal, -}; - -_UI_ENUM(uiAttributeHanCharacterForm) { - uiAttributeHanCharacterFormTraditional, - uiAttributeHanCharacterFormSimplified, - uiAttributeHanCharacterFormJIS1978, - uiAttributeHanCharacterFormJIS1983, - uiAttributeHanCharacterFormJIS1990, - uiAttributeHanCharacterFormExpert, - uiAttributeHanCharacterFormJIS2004, - uiAttributeHanCharacterFormHojo, - uiAttributeHanCharacterFormNLC, - uiAttributeHanCharacterFormTraditionalNames, -}; - -_UI_ENUM(uiAttributeCapForm) { - uiAttributeCapFormNormal, - uiAttributeCapFormSmallCaps, - uiAttributeCapFormPetiteCaps, -}; - -#endif - // TODO rename? typedef struct uiOpenTypeFeatures uiOpenTypeFeatures; // TODO pass the feature set? From dd544696774a1bb620c44758cf050992429e09d0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 19 May 2017 16:40:52 -0400 Subject: [PATCH 0725/1329] Set up a future system for OS X like we have on GTK+ and moved everything we already have to it. You'll notice we also set up a loader for what we're going to use this for: using OpenType attributes directly on OS X. --- darwin/CMakeLists.txt | 1 + darwin/autolayout.m | 4 +--- darwin/future.m | 50 ++++++++++++++++++++++++++++++++++++++++++ darwin/main.m | 1 + darwin/uipriv_darwin.h | 8 +++++++ darwin/winmoveresize.m | 8 +++---- unix/uipriv_unix.h | 2 +- 7 files changed, 65 insertions(+), 9 deletions(-) create mode 100644 darwin/future.m diff --git a/darwin/CMakeLists.txt b/darwin/CMakeLists.txt index e260f266..0591643b 100644 --- a/darwin/CMakeLists.txt +++ b/darwin/CMakeLists.txt @@ -22,6 +22,7 @@ list(APPEND _LIBUI_SOURCES darwin/fontbutton.m darwin/fontmatch.m darwin/form.m + darwin/future.m darwin/graphemes.m darwin/grid.m darwin/group.m diff --git a/darwin/autolayout.m b/darwin/autolayout.m index 9964155f..3bf4acb7 100644 --- a/darwin/autolayout.m +++ b/darwin/autolayout.m @@ -12,9 +12,7 @@ NSLayoutConstraint *mkConstraint(id view1, NSLayoutAttribute attr1, NSLayoutRela attribute:attr2 multiplier:multiplier constant:c]; - // apparently only added in 10.9 - if ([constraint respondsToSelector:@selector(setIdentifier:)]) - [((id) constraint) setIdentifier:desc]; + FUTURE_NSLayoutConstraint_setIdentifier(constraint, desc); return constraint; } diff --git a/darwin/future.m b/darwin/future.m new file mode 100644 index 00000000..45208756 --- /dev/null +++ b/darwin/future.m @@ -0,0 +1,50 @@ +// 19 may 2017 +#import "uipriv_darwin.h" + +// functions and constants FROM THE FUTURE! + +// TODO add weight constants here? + +// added in OS X 10.10; we need 10.8 +CFStringRef FUTURE_kCTFontOpenTypeFeatureTag = NULL; +CFStringRef FUTURE_kCTFontOpenTypeFeatureValue = NULL; + +// note that we treat any error as "the symbols aren't there" (and don't care if dlclose() failed) +void loadFutures(void) +{ + void *handle; + + // dlsym() walks the dependency chain, so opening the current process should be sufficient + handle = dlopen(NULL, RTLD_LAZY); + if (handle == NULL) + return; +#define GET(var, fn) *((void **) (&var)) = dlsym(handle, #fn) + GET(FUTURE_kCTFontOpenTypeFeatureTag, kCTFontOpenTypeFeatureTag); + GET(FUTURE_kCTFontOpenTypeFeatureValue, kCTFontOpenTypeFeatureValue); + dlclose(handle); +} + +// wrappers for methods that exist in the future that we can check for with respondsToSelector: +// keep them in one place for convenience + +// apparently only added in 10.9; we need 10.8 +void FUTURE_NSLayoutConstraint_setIdentifier(NSLayoutConstraint *constraint, NSString *identifier) +{ + id cid = (id) constraint; + + if ([constraint respondsToSelector:@selector(setIdentifier:)]) + [cid setIdentifier:identifier]; +} + +// added in 10.11; we need 10.8 +// return whether this was done because we recreate its effects if not (see winmoveresize.m) +BOOL FUTURE_NSWindow_performWindowDragWithEvent(NSWindow *w, NSEvent *initialEvent) +{ + id cw = (id) w; + + if ([w respondsToSelector:@selector(performWindowDragWithEvent:)]) { + [cw performWindowDragWithEvent:initialEvent]; + return YES; + } + return NO; +} diff --git a/darwin/main.m b/darwin/main.m index 440343bc..60acecaf 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -119,6 +119,7 @@ const char *uiInit(uiInitOptions *o) [realNSApp() setDelegate:delegate]; initAlloc(); + loadFutures(); // always do this so we always have an application menu appDelegate().menuManager = [[menuManager new] autorelease]; diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index fe65f201..01594f48 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -2,6 +2,7 @@ #define MAC_OS_X_VERSION_MIN_REQUIRED MAC_OS_X_VERSION_10_8 #define MAC_OS_X_VERSION_MAX_ALLOWED MAC_OS_X_VERSION_10_8 #import +#include // see future.m #import "../ui.h" #import "../ui_darwin.h" #import "../common/uipriv.h" @@ -163,3 +164,10 @@ extern void openTypeToAAT(uiOpenTypeFeatures *otf, void (*doAAT)(uint16_t type, (x8tox32(b) << 16) | \ (x8tox32(c) << 8) | \ x8tox32(d)) + +// future.m +extern CFStringRef FUTURE_kCTFontOpenTypeFeatureTag; +extern CFStringRef FUTURE_kCTFontOpenTypeFeatureValue; +extern void loadFutures(void); +extern void FUTURE_NSLayoutConstraint_setIdentifier(NSLayoutConstraint *constraint, NSString *identifier); +extern BOOL FUTURE_NSWindow_performWindowDragWithEvent(NSWindow *w, NSEvent *initialEvent); diff --git a/darwin/winmoveresize.m b/darwin/winmoveresize.m index 9145b7bb..2753b93b 100644 --- a/darwin/winmoveresize.m +++ b/darwin/winmoveresize.m @@ -48,12 +48,10 @@ void doManualMove(NSWindow *w, NSEvent *initialEvent) BOOL (^handleEvent)(NSEvent *e); __block BOOL done; - // this is only available on 10.11 and newer (LONGTERM FUTURE) - // but use it if available; this lets us use the real OS dragging code, which means we can take advantage of OS features like Spaces - if ([w respondsToSelector:@selector(performWindowDragWithEvent:)]) { - [((id) w) performWindowDragWithEvent:initialEvent]; + // 10.11 gives us a method to handle this for us + // use it if available; this lets us use the real OS dragging code, which means we can take advantage of OS features like Spaces + if (FUTURE_NSWindow_performWindowDragWithEvent(w, initialEvent)) return; - } mdp.w = w; mdp.initialFrame = [mdp.w frame]; diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index cb5bf281..c80e8cc6 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -5,7 +5,7 @@ #define GDK_VERSION_MAX_ALLOWED GDK_VERSION_3_10 #include #include -#include // see drawtext.c +#include // see future.c #include #include #include From fb884abc416cc2e5bd3b46c183a339919b02d4de Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 19 May 2017 16:58:15 -0400 Subject: [PATCH 0726/1329] Fixed memory issues with future.m. --- darwin/future.m | 5 +++-- darwin/uipriv_darwin.h | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/darwin/future.m b/darwin/future.m index 45208756..30b43bd6 100644 --- a/darwin/future.m +++ b/darwin/future.m @@ -4,10 +4,11 @@ // functions and constants FROM THE FUTURE! // TODO add weight constants here? +// TOOD explain why the constants need to be pointers themselves // added in OS X 10.10; we need 10.8 -CFStringRef FUTURE_kCTFontOpenTypeFeatureTag = NULL; -CFStringRef FUTURE_kCTFontOpenTypeFeatureValue = NULL; +CFStringRef *FUTURE_kCTFontOpenTypeFeatureTag = NULL; +CFStringRef *FUTURE_kCTFontOpenTypeFeatureValue = NULL; // note that we treat any error as "the symbols aren't there" (and don't care if dlclose() failed) void loadFutures(void) diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 01594f48..c22c82b6 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -166,8 +166,8 @@ extern void openTypeToAAT(uiOpenTypeFeatures *otf, void (*doAAT)(uint16_t type, x8tox32(d)) // future.m -extern CFStringRef FUTURE_kCTFontOpenTypeFeatureTag; -extern CFStringRef FUTURE_kCTFontOpenTypeFeatureValue; +extern CFStringRef *FUTURE_kCTFontOpenTypeFeatureTag; +extern CFStringRef *FUTURE_kCTFontOpenTypeFeatureValue; extern void loadFutures(void); extern void FUTURE_NSLayoutConstraint_setIdentifier(NSLayoutConstraint *constraint, NSString *identifier); extern BOOL FUTURE_NSWindow_performWindowDragWithEvent(NSWindow *w, NSEvent *initialEvent); From a17985b4cda285a747d31e450e48a24f5fa6c847 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 19 May 2017 20:46:56 -0400 Subject: [PATCH 0727/1329] Some big TODOs I didn't realize until now. --- ui_attrstr.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ui_attrstr.h b/ui_attrstr.h index 4d13acb7..fc281f99 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -19,6 +19,7 @@ typedef struct uiAttributedString uiAttributedString; // Note: where you say "1 = on", any nonzero value means "on". (TODO) +// TODO ok, we need to figure out what to do about pointer objects: do we copy them or do we keep them safe? especially since we merge attributes... _UI_ENUM(uiAttribute) { uiAttributeFamily, uiAttributeSize, // use Double From 57873bae728f237ffdcb5717f00927b093c5f958 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 27 May 2017 18:19:08 -0400 Subject: [PATCH 0728/1329] Stuff. --- darwin/future.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/darwin/future.m b/darwin/future.m index 30b43bd6..a6988f83 100644 --- a/darwin/future.m +++ b/darwin/future.m @@ -2,9 +2,9 @@ #import "uipriv_darwin.h" // functions and constants FROM THE FUTURE! +// note: for constants, dlsym() returns the address of the constant itself, as if we had done &constantName // TODO add weight constants here? -// TOOD explain why the constants need to be pointers themselves // added in OS X 10.10; we need 10.8 CFStringRef *FUTURE_kCTFontOpenTypeFeatureTag = NULL; From 475ae4a4bf6287ba6eee94667a31fcbdd8b7c89c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 28 May 2017 00:19:49 -0400 Subject: [PATCH 0729/1329] Started making attribute manipulation better. In a sense. Pointers will be represented properly, and in a const-safe way. We'll need to make local copies of everything, of course. --- examples/drawtext/attributes.c | 124 ++++++++++++++++----------------- ui_attrstr.h | 10 +-- 2 files changed, 68 insertions(+), 66 deletions(-) diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index 46bf1b0d..69bbe032 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -7,7 +7,7 @@ static uiAttributedString *attrstr; static uiOpenTypeFeatures *features[nFeatures]; static int curFeature = 0; -static uintptr_t addFeature(const char tag[4], uint32_t value) +static uiOpenTypeFeatures *addFeature(const char tag[4], uint32_t value) { uiOpenTypeFeatures *otf; @@ -19,7 +19,7 @@ static uintptr_t addFeature(const char tag[4], uint32_t value) uiOpenTypeFeaturesAdd(otf, tag[0], tag[1], tag[2], tag[3], value); features[curFeature] = otf; curFeature++; - return (uintptr_t) otf; + return otf; } // some of these examples come from Microsoft's and Apple's lists of typographic features and also https://www.fontfont.com/staticcontent/downloads/FF_OT_User_Guide.pdf @@ -38,7 +38,7 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFamily; - spec.Value = (uintptr_t) "Courier New"; + spec.Family = "Courier New"; uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ", "); @@ -170,7 +170,7 @@ static void setupAttributedString(void) spec.A = 0.75; uiAttributedStringSetAttribute(attrstr, &spec, start + 12, end); spec.Type = uiAttributeFamily; - spec.Value = (uintptr_t) "Helvetica"; + spec.Value = "Helvetica"; uiAttributedStringSetAttribute(attrstr, &spec, start + 8, end - 1); spec.Type = uiAttributeBackground; spec.R = 1.0; @@ -192,7 +192,7 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("liga", 1); + spec.Features = addFeature("liga", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -206,7 +206,7 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("rlig", 1); + spec.Features = addFeature("rlig", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, "\xE2\x80\xAC)"); @@ -218,7 +218,7 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("dlig", 1); + spec.Features = addFeature("dlig", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -230,13 +230,13 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("clig", 1); + spec.Features = addFeature("clig", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); uiAttributedStringAppendUnattributed(attrstr, ", "); - otf = (uiOpenTypeFeatures *) addFeature("hlig", 1); + otf = addFeature("hlig", 1); // This technically isn't what is meant by "historical ligatures", but Core Text's internal AAT-to-OpenType mapping says to include it, so we include it too uiOpenTypeFeaturesAdd(otf, 'h', 'i', 's', 't', 1); next = "\xC3\x9F"; @@ -245,7 +245,7 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = (uintptr_t) otf; + spec.Value = otf; uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -257,7 +257,7 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("unic", 1); + spec.Features = addFeature("unic", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ", "); @@ -268,14 +268,14 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("pnum", 1); + spec.Features = addFeature("pnum", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ") and tabular/monospaced ("); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("tnum", 1); + spec.Features = addFeature("tnum", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ") numbers"); @@ -287,7 +287,7 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("sups", 1); + spec.Features = addFeature("sups", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -299,7 +299,7 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("subs", 1); + spec.Features = addFeature("subs", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -311,7 +311,7 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("ordn", 1); + spec.Features = addFeature("ordn", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -323,7 +323,7 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("sinf", 1); + spec.Features = addFeature("sinf", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -344,14 +344,14 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("afrc", 1); + spec.Features = addFeature("afrc", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ", "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("frac", 1); + spec.Features = addFeature("frac", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -363,14 +363,14 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("zero", 0); + spec.Features = addFeature("zero", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("zero", 1); + spec.Features = addFeature("zero", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -382,14 +382,14 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("mgrk", 0); + spec.Features = addFeature("mgrk", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("mgrk", 1); + spec.Features = addFeature("mgrk", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -402,7 +402,7 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("ornm", i); + spec.Features = addFeature("ornm", i); uiAttributedStringSetAttribute(attrstr, &spec, start, end); next = "\xE2\x80\xA2"; } @@ -416,14 +416,14 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("aalt", 0); + spec.Features = addFeature("aalt", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("aalt", 1); + spec.Features = addFeature("aalt", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -435,14 +435,14 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("titl", 0); + spec.Features = addFeature("titl", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("titl", 1); + spec.Features = addFeature("titl", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -454,20 +454,20 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("jp78", 1); + spec.Features = addFeature("jp78", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("jp83", 1); + spec.Features = addFeature("jp83", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); uiAttributedStringAppendUnattributed(attrstr, ", "); - otf = (uiOpenTypeFeatures *) addFeature("onum", 0); + otf = addFeature("onum", 0); // Core Text's internal AAT-to-OpenType mapping says to include this, so we include it too // TODO is it always set? uiOpenTypeFeaturesAdd(otf, 'l', 'n', 'u', 'm', 0); @@ -477,16 +477,16 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = (uintptr_t) otf; + spec.Features = otf; uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - otf = (uiOpenTypeFeatures *) addFeature("onum", 1); + otf = addFeature("onum", 1); uiOpenTypeFeaturesAdd(otf, 'l', 'n', 'u', 'm', 1); - spec.Value = (uintptr_t) otf; + spec.Features = otf; uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -498,14 +498,14 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("hngl", 0); + spec.Features = addFeature("hngl", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("hngl", 1); + spec.Features = addFeature("hngl", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -517,21 +517,21 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("nalt", 0); + spec.Features = addFeature("nalt", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("nalt", 1); + spec.Features = addFeature("nalt", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("nalt", 4); // AAT inverted circle + spec.Features = addFeature("nalt", 4); // AAT inverted circle uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -543,14 +543,14 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("ruby", 0); + spec.Features = addFeature("ruby", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("ruby", 1); + spec.Features = addFeature("ruby", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -562,14 +562,14 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("ital", 0); + spec.Features = addFeature("ital", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("ital", 1); + spec.Features = addFeature("ital", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -581,14 +581,14 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("case", 0); + spec.Features = addFeature("case", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("case", 1); + spec.Features = addFeature("case", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -600,14 +600,14 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("cpsp", 0); + spec.Features = addFeature("cpsp", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("cpsp", 1); + spec.Features = addFeature("cpsp", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -619,28 +619,28 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("hkna", 0); + spec.Features = addFeature("hkna", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("hkna", 1); + spec.Features = addFeature("hkna", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ") and vertical ("); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("vkna", 0); + spec.Features = addFeature("vkna", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("vkna", 1); + spec.Features = addFeature("vkna", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ") kana forms"); @@ -662,7 +662,7 @@ static void setupAttributedString(void) start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); - spec.Value = addFeature(tag, 1); + spec.Features = addFeature(tag, 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); } uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -675,14 +675,14 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("calt", 0); + spec.Features = addFeature("calt", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("calt", 1); + spec.Features = addFeature("calt", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -694,14 +694,14 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("swsh", 0); + spec.Features = addFeature("swsh", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("swsh", 1); + spec.Features = addFeature("swsh", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -713,14 +713,14 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("cswh", 0); + spec.Features = addFeature("cswh", 0); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, " vs. "); start = uiAttributedStringLen(attrstr); end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("cswh", 1); + spec.Features = addFeature("cswh", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); @@ -731,7 +731,7 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("smcp", 1); + spec.Features = addFeature("smcp", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ", "); next = "Petite Caps"; @@ -739,7 +739,7 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("pcap", 1); + spec.Features = addFeature("pcap", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ", "); @@ -749,7 +749,7 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("c2sp", 1); + spec.Features = addFeature("c2sp", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ", and "); next = "PETITE UPPERCASES"; @@ -757,7 +757,7 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = addFeature("c2pc", 1); + spec.Features = addFeature("c2pc", 1); uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, "."); diff --git a/ui_attrstr.h b/ui_attrstr.h index fc281f99..e6fb6d08 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -19,9 +19,9 @@ typedef struct uiAttributedString uiAttributedString; // Note: where you say "1 = on", any nonzero value means "on". (TODO) -// TODO ok, we need to figure out what to do about pointer objects: do we copy them or do we keep them safe? especially since we merge attributes... +// TODO just make a separate field for everything? _UI_ENUM(uiAttribute) { - uiAttributeFamily, + uiAttributeFamily, // use Family uiAttributeSize, // use Double uiAttributeWeight, uiAttributeItalic, @@ -34,7 +34,7 @@ _UI_ENUM(uiAttribute) { uiAttributeUnderlineColor, // enum uiDrawUnderlineColor // TODO note that for the purpose of uiAttributedString two sets of features are only the same (and thus their attributes are merged) only if the pointers are the same; whether the tag sets are the same only become relevant to uiDrawTextLayout - uiAttributeFeatures, // object of type uiOpenTypeFeatures + uiAttributeFeatures, // use Features }; _UI_ENUM(uiDrawUnderlineStyle) { @@ -60,7 +60,7 @@ _UI_EXTERN uiOpenTypeFeatures *uiNewOpenTypeFeatures(void); _UI_EXTERN void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf); // TODO put above Free? // TODO Copy instead of Clone? -_UI_EXTERN uiOpenTypeFeatures *uiOpenTypeFeaturesClone(uiOpenTypeFeatures *otf); +_UI_EXTERN uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures *otf); _UI_EXTERN void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value); _UI_EXTERN void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d); _UI_EXTERN int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value); @@ -71,12 +71,14 @@ typedef struct uiAttributeSpec uiAttributeSpec; struct uiAttributeSpec { uiAttribute Type; + const char *Family; uintptr_t Value; double Double; double R; double G; double B; double A; + const uiOpenTypeFeatures *Features; // TODO rename to OpenTypeFeatures? }; // TODO name the foreach return values From 02020e676a9e17c0c025165962a72e945f474802 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 28 May 2017 00:41:40 -0400 Subject: [PATCH 0730/1329] Managed attribute spec memory properly. --- common/attrlist.c | 90 +++++++++++++++++++++++++++-------------------- ui_attrstr.h | 5 +-- 2 files changed, 54 insertions(+), 41 deletions(-) diff --git a/common/attrlist.c b/common/attrlist.c index ec0b12bd..0d354571 100644 --- a/common/attrlist.c +++ b/common/attrlist.c @@ -24,6 +24,47 @@ struct attrlist { struct attr *last; }; +// We need to make local copies of any pointers in uiAttributeSpec. +// If we don't do this, we'll wind up leaking stuff, or worse, prematurely freeing stuff. +// TODO ensure this is docmented +static void attrSetSpec(struct attr *a, uiAttributeSpec *spec) +{ + const char *family; + char *familyCopy; + + a->spec = *spec; + switch (a->spec.Type) { + case uiAttributeFamily: + // TODO UTF-8 validate this? + family = a->spec.Family; + familyCopy = (char *) uiAlloc((strlen(family) + 1) * sizeof (char), "char[] (uiAttributeSpec.Family copy)"); + strcpy(familyCopy, family); + a->spec.Family = familyCopy; + break; + case uiAttributeFeatures: + a->spec.Features = uiOpenTypeFeaturesClone(a->spec.Features); + break; + } +} + +static void attrCopySpec(struct attr *dest, struct attr *src) +{ + attrSetSpec(dest, &(src->spec)); +} + +// Likewise, this is needed to clean up a spec with a pointer when finished. +static void attrClearSpec(struct attr *a) +{ + switch (a->spec.Type) { + case uiAttributeFamily: + uiFree((char *) (a->spec.Family)); + break; + case uiAttributeFeatures: + uiOpenTypeFeaturesFree((uiOpenTypeFeatures *) (a->spec.Features)); + break; + } +} + // if before is NULL, add to the end of the list static void attrInsertBefore(struct attrlist *alist, struct attr *a, struct attr *before) { @@ -128,6 +169,7 @@ static struct attr *attrDelete(struct attrlist *alist, struct attr *a) struct attr *next; next = attrUnlink(alist, a); + attrClearSpec(a); uiFree(a); return next; } @@ -177,7 +219,7 @@ static struct attr *attrDropRange(struct attrlist *alist, struct attr *a, size_t // we'll need to split the attribute into two b = uiNew(struct attr); - b->spec = a->spec; + attrCopySpec(b, a); b->start = end; b->end = a->end; *tail = b; @@ -221,7 +263,7 @@ static struct attr *attrSplitAt(struct attrlist *alist, struct attr *a, size_t a return NULL; b = uiNew(struct attr); - b->spec = a->spec; + attrCopySpec(b, a); b->start = at; b->end = a->end; @@ -284,39 +326,6 @@ static struct attr *attrDeleteRange(struct attrlist *alist, struct attr *a, size return a->next; } -static int boolsEqual(struct attr *attr, uiAttributeSpec *spec) -{ - if (attr->spec.Value == 0 && spec->Value == 0) - return 1; - return attr->spec.Value != 0 && spec->Value != 0; -} - -// BCP 47 is ASCII-only -static int asciiStringsEqualCaseFold(const char *a, const char *b) -{ - char c, d; - - for (;;) { - if (*a == *b) { - if (*a == '\0') - return 1; - a++; - b++; - continue; - } - c = *a; - if (c >= 'A' && c <= 'Z') - c += 'a' - 'A'; - d = *b; - if (d >= 'A' && d <= 'Z') - d += 'a' - 'A'; - if (c != d) - return 0; - a++; - b++; - } -} - static int specsIdentical(struct attr *attr, uiAttributeSpec *spec) { if (attr->spec.Type != spec->Type) @@ -325,7 +334,7 @@ static int specsIdentical(struct attr *attr, uiAttributeSpec *spec) case uiAttributeFamily: // TODO should we start copying these strings? // TODO should this be case-insensitive? - return strcmp((char *) (attr->spec.Value), (char *) (spec->Value)) == 0; + return strcmp(attr->spec.Family, spec->Family) == 0; case uiAttributeSize: // TODO use a closest match? return attr->spec.Double == spec->Double; @@ -342,9 +351,11 @@ static int specsIdentical(struct attr *attr, uiAttributeSpec *spec) attr->spec.G == spec->G && attr->spec.B == spec->B && attr->spec.A == spec->A; + case uiAttributeFeatures: + // TODO rename it to uiAttributeOpenTypeFeatures? + return uiOpenTypeFeaturesEqual(attr->spec.Features, spec->Features); } - // handles the rest, including pointer comparison for uiAttributeFeatures - // TODO rename it to uiAttributeOpenTypeFeatures? + // handles the rest return attr->spec.Value == spec->Value; } @@ -394,7 +405,7 @@ void attrlistInsertAttribute(struct attrlist *alist, uiAttributeSpec *spec, size // if we got here, we know we have to add the attribute before before a = uiNew(struct attr); - a->spec = *spec; + attrSetSpec(a, spec); a->start = start; a->end = end; attrInsertBefore(alist, a, before); @@ -661,6 +672,7 @@ void attrlistFree(struct attrlist *alist) a = alist->first; while (a != NULL) { next = a->next; + attrClearSpec(a); uiFree(a); a = next; } diff --git a/ui_attrstr.h b/ui_attrstr.h index e6fb6d08..d293f590 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -33,7 +33,7 @@ _UI_ENUM(uiAttribute) { // TODO document that the color in the case we don't specify it is the text color uiAttributeUnderlineColor, // enum uiDrawUnderlineColor - // TODO note that for the purpose of uiAttributedString two sets of features are only the same (and thus their attributes are merged) only if the pointers are the same; whether the tag sets are the same only become relevant to uiDrawTextLayout + // TODO note these are copied uiAttributeFeatures, // use Features }; @@ -65,7 +65,7 @@ _UI_EXTERN void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, c _UI_EXTERN void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d); _UI_EXTERN int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value); _UI_EXTERN void uiOpenTypeFeaturesForEach(uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data); -_UI_EXTERN int uiOpenTypeFeaturesEqual(uiOpenTypeFeatures *a, uiOpenTypeFeatures *b); +_UI_EXTERN int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeatures *b); typedef struct uiAttributeSpec uiAttributeSpec; @@ -82,6 +82,7 @@ struct uiAttributeSpec { }; // TODO name the foreach return values +// TODO make the spec const in a way that doesn't allow fields to be modified? typedef int (*uiAttributedStringForEachAttributeFunc)(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data); // @role uiAttributedString constructor From d979f7a93e2e96e8fd5610df4b216188838ac684 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 28 May 2017 21:55:10 -0400 Subject: [PATCH 0731/1329] Updated a stale comment. --- windows/utilwin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/utilwin.cpp b/windows/utilwin.cpp index 414ae83a..11f9a415 100644 --- a/windows/utilwin.cpp +++ b/windows/utilwin.cpp @@ -5,7 +5,7 @@ // It is not a message-only window, and it is always hidden and disabled. // Its roles: // - It is the initial parent of all controls. When a control loses its parent, it also becomes that control's parent. -// - It handles WM_QUERYENDSESSION and console end session requests. +// - It handles WM_QUERYENDSESSION requests. // - It handles WM_WININICHANGE and forwards the message to any child windows that request it. // - It handles executing functions queued to run by uiQueueMain(). From 1733c28b52517434467824d5f56a0152cce5bd57 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 30 May 2017 09:52:38 -0400 Subject: [PATCH 0732/1329] Added consts to the OpenType implementations. --- darwin/opentype.m | 4 ++-- unix/opentype.c | 5 +++-- windows/opentype.cpp | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/darwin/opentype.m b/darwin/opentype.m index 25939299..52db9560 100644 --- a/darwin/opentype.m +++ b/darwin/opentype.m @@ -20,7 +20,7 @@ void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf) uiFree(otf); } -uiOpenTypeFeatures *uiOpenTypeFeaturesClone(uiOpenTypeFeatures *otf) +uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures *otf) { uiOpenTypeFeatures *out; @@ -82,7 +82,7 @@ void uiOpenTypeFeaturesForEach(uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEac }]; } -int uiOpenTypeFeaturesEqual(uiOpenTypeFeatures *a, uiOpenTypeFeatures *b) +int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeatures *b) { return [a->tags isEqualToDictionary:b->tags]; } diff --git a/unix/opentype.c b/unix/opentype.c index 6ea317c5..6700b83e 100644 --- a/unix/opentype.c +++ b/unix/opentype.c @@ -28,7 +28,7 @@ static void cloneTags(gpointer key, gpointer value, gpointer data) g_hash_table_replace((GHashTable *) data, key, value); } -uiOpenTypeFeatures *uiOpenTypeFeaturesClone(uiOpenTypeFeatures *otf) +uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures *otf) { uiOpenTypeFeatures *out; @@ -119,10 +119,11 @@ static GList *copySortedKeys(GHashTable *tags) copy = g_list_copy(k); copy = g_list_sort(copy, tagcmp); // TODO do we free k? the docs contradict themselves + // TODO I already forgot, does g_list_sort() copy, or just change the head? return copy; } -int uiOpenTypeFeaturesEqual(uiOpenTypeFeatures *a, uiOpenTypeFeatures *b) +int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeatures *b) { GList *ak, *bk; GList *ai, *bi; diff --git a/windows/opentype.cpp b/windows/opentype.cpp index f3ca36c3..141f8a5f 100644 --- a/windows/opentype.cpp +++ b/windows/opentype.cpp @@ -22,7 +22,7 @@ void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf) uiFree(otf); } -uiOpenTypeFeatures *uiOpenTypeFeaturesClone(uiOpenTypeFeatures *otf) +uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures *otf) { uiOpenTypeFeatures *out; @@ -75,7 +75,7 @@ void uiOpenTypeFeaturesForEach(uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEac } } -int uiOpenTypeFeaturesEqual(uiOpenTypeFeatures *a, uiOpenTypeFeatures *b) +int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeatures *b) { // TODO make sure this is correct return *(a->tags) == *(b->tags); From e356f1c48a8335dac89860d1ffcca0938bc4661c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 30 May 2017 13:06:58 -0400 Subject: [PATCH 0733/1329] Started reworking darwin/attrstr.m to be a lot more sane. --- common/attrlist.c | 1 - darwin/attrstr.m | 112 +++++++++++++++++++++++++++++++++------------- 2 files changed, 80 insertions(+), 33 deletions(-) diff --git a/common/attrlist.c b/common/attrlist.c index 0d354571..ca2937dc 100644 --- a/common/attrlist.c +++ b/common/attrlist.c @@ -332,7 +332,6 @@ static int specsIdentical(struct attr *attr, uiAttributeSpec *spec) return 0; switch (attr->spec.Type) { case uiAttributeFamily: - // TODO should we start copying these strings? // TODO should this be case-insensitive? return strcmp(attr->spec.Family, spec->Family) == 0; case uiAttributeSize: diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 556b09ad..4ce242d1 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -60,48 +60,95 @@ void uninitUnderlineColors(void) // TODO opentype features and AAT fvar table info is lost, so a handful of fonts in the font panel ("Titling" variants of some fonts and Skia and possibly others but those are the examples I know about) cannot be represented by uiDrawFontDescriptor; what *can* we do about this since this info is NOT part of the font on other platforms? // TODO see if we could use NSAttributedString? // TODO consider renaming this struct and the fep variable(s) +// TODO restructure all this so the important details at the top are below with the combined font attributes type? struct foreachParams { CFMutableAttributedStringRef mas; - NSMutableDictionary *converted; + NSMutableDictionary *combinedFontAttrs; // keys are CFIndex in mas, values are combinedFontAttr objects uiDrawFontDescriptor *defaultFont; NSMutableArray *backgroundBlocks; }; -#define maxFeatures 32 +@interface combinedFontAttr : NSObject +@property const char *family; +@property double size; +@property uiDrawTextWeight weight; +@property uiDrawTextItalic italic; +@property uiDrawTextStretch stretch; +@property const uiOpenTypeFeatures *features; +- (id)initWithDefaultFont:(uiDrawFontDescriptor *)defaultFont; +- (BOOL)same:(combinedFontAttr *)b; +@end -struct fontParams { - uiDrawFontDescriptor desc; - uint16_t featureTypes[maxFeatures]; - uint16_t featureSelectors[maxFeatures]; - size_t nFeatures; -}; +@implementation combinedFontAttr + +- (id)initWithDefaultFont:(uiDrawFontDescriptor *)defaultFont +{ + self = [super init]; + if (self) { + self.family = defaultFont->Family; + self.size = defaultFont->Size; + self.weight = defaultFont->Weight; + self.italic = defaultFont->Italic; + self.stretch = defaultFont->Stretch; + self.features = NULL; + } + return self; +} + +// TODO deduplicate this with common/attrlist.c +- (BOOL)same:(combinedFontAttr *)b +{ + // TODO should this be case-insensitive? + if (strcmp(self.family, b.family) != 0) + return NO; + // TODO use a closest match? + if (self.size != b.size) + return NO; + if (self.weight != b.weight) + return NO; + if (self.italic != b.italic) + return NO; + if (self.stretch != b.stretch) + return NO; + // TODO make this part of uiOpenTypeFeaturesEqual() on all platforms + if (self.features == NULL && b.features == NULL) + return YES; + if (self.features != NULL && b.features == NULL) + return NO; + if (self.features == NULL && b.features != NULL) + return NO; + if (!uiOpenTypeFeaturesEqual(self.features, b.features)) + return NO; + return YES; +} + +@end static void ensureFontInRange(struct foreachParams *p, size_t start, size_t end) { size_t i; NSNumber *n; - struct fontParams *new; + combinedFontAttr *new; for (i = start; i < end; i++) { n = [NSNumber numberWithInteger:i]; - if ([p->converted objectForKey:n] != nil) + if ([p->combinedFontAttrs objectForKey:n] != nil) continue; - new = uiNew(struct fontParams); - new->desc = *(p->defaultFont); - [p->converted setObject:[NSValue valueWithPointer:new] forKey:n]; + new = [[combinedFontAttr alloc] initWithDefaultFont:p->defaultFont]; + [p->combinedFontAttrs setObject:new forKey:n]; } } -static void adjustFontInRange(struct foreachParams *p, size_t start, size_t end, void (^adj)(struct fontParams *fp)) +static void adjustFontInRange(struct foreachParams *p, size_t start, size_t end, void (^adj)(combinedFontAttr *cfa)) { size_t i; NSNumber *n; - NSValue *v; + combinedFontAttr *cfa; for (i = start; i < end; i++) { n = [NSNumber numberWithInteger:i]; - v = (NSValue *) [p->converted objectForKey:n]; - adj((struct fontParams *) [v pointerValue]); + v = (combinedFontAttr *) [p->combinedFontAttrs objectForKey:n]; + adj(cfa); } } @@ -146,6 +193,7 @@ struct aatParam { static void doAAT(uint16_t type, uint16_t selector, void *data) { +#if 0 /* TODO */ struct aatParam *p = (struct aatParam *) data; ensureFontInRange(p->p, p->start, p->end); @@ -158,6 +206,7 @@ static void doAAT(uint16_t type, uint16_t selector, void *data) // TODO move this check to the top like in the drawtext example? and all the other instances of this? } }); +#endif } static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) @@ -169,7 +218,6 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t backgroundBlock block; int32_t us; CFNumberRef num; - struct aatParam ap; ostart = start; oend = end; @@ -180,32 +228,32 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t switch (spec->Type) { case uiAttributeFamily: ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(struct fontParams *fp) { - fp->desc.Family = (char *) (spec->Value); + adjustFontInRange(p, start, end, ^(combinedFontAttr *cfa) { + cfa.family = spec->Family; }); break; case uiAttributeSize: ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(struct fontParams *fp) { - fp->desc.Size = spec->Double; + adjustFontInRange(p, start, end, ^(combinedFontAttr *cfa) { + cfa.size = spec->Double; }); break; case uiAttributeWeight: ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(struct fontParams *fp) { - fp->desc.Weight = (uiDrawTextWeight) (spec->Value); + adjustFontInRange(p, start, end, ^(combinedFontAttr *cfa) { + cfa.weight = (uiDrawTextWeight) (spec->Value); }); break; case uiAttributeItalic: ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(struct fontParams *fp) { - fp->desc.Italic = (uiDrawTextItalic) (spec->Value); + adjustFontInRange(p, start, end, ^(combinedFontAttr *cfa) { + cfa.italic = (uiDrawTextItalic) (spec->Value); }); break; case uiAttributeStretch: ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(struct fontParams *fp) { - fp->desc.Stretch = (uiDrawTextStretch) (spec->Value); + adjustFontInRange(p, start, end, ^(combinedFontAttr *cfa) { + cfa.stretch = (uiDrawTextStretch) (spec->Value); }); break; case uiAttributeColor: @@ -259,10 +307,10 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t CFRelease(color); break; case uiAttributeFeatures: - ap.p = p; - ap.start = start; - ap.end = end; - openTypeToAAT((uiOpenTypeFeatures *) (spec->Value), doAAT, &ap); + ensureFontInRange(p, start, end); + adjustFontInRange(p, start, end, ^(combinedFontAttr *cfa) { + cfa.features = spec->Features; + }); break; default: // TODO complain From c4dd85bece6a0a39b5e0cc0bc5986fdea9694377 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 30 May 2017 13:18:13 -0400 Subject: [PATCH 0734/1329] More reworking the OS X attributed string code. Now we need to rework the AAT code somewhat too. --- darwin/attrstr.m | 34 ++++++++++++++++++++++------------ darwin/fontmatch.m | 4 +--- darwin/uipriv_darwin.h | 2 +- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 4ce242d1..60120af9 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -61,6 +61,7 @@ void uninitUnderlineColors(void) // TODO see if we could use NSAttributedString? // TODO consider renaming this struct and the fep variable(s) // TODO restructure all this so the important details at the top are below with the combined font attributes type? +// TODO in fact I should just write something to explain everything in this file... struct foreachParams { CFMutableAttributedStringRef mas; NSMutableDictionary *combinedFontAttrs; // keys are CFIndex in mas, values are combinedFontAttr objects @@ -77,6 +78,7 @@ struct foreachParams { @property const uiOpenTypeFeatures *features; - (id)initWithDefaultFont:(uiDrawFontDescriptor *)defaultFont; - (BOOL)same:(combinedFontAttr *)b; +- (CTFontRef)toCTFont; @end @implementation combinedFontAttr @@ -85,6 +87,7 @@ struct foreachParams { { self = [super init]; if (self) { + // TODO define behaviors if defaultFont->Family or any attribute Family is NULL, same with other invalid values self.family = defaultFont->Family; self.size = defaultFont->Size; self.weight = defaultFont->Weight; @@ -122,6 +125,25 @@ struct foreachParams { return YES; } +- (CTFontRef)toCTFont +{ + uiDrawFontDescriptor uidesc; + CTFontDescriptorRef desc; + CTFontRef font; + + uidesc.Family = self.family; + uidesc.Size = self.size; + uidesc.Weight = self.weight; + uidesc.Italic = self.italic; + uidesc.Stretch = self.stretch; + desc = fontdescToCTFontDescriptor(&uidesc); + if (self.features != NULL) + desc = fontdescAppendFeatures(desc, self.features); + font = CTFontCreateWithFontDescriptor(desc, self.size, NULL); + CFRelease(desc); // TODO correct? + return font; +} + @end static void ensureFontInRange(struct foreachParams *p, size_t start, size_t end) @@ -319,18 +341,6 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t return 0; } -static CTFontRef fontdescToCTFont(struct fontParams *fp) -{ - CTFontDescriptorRef desc; - CTFontRef font; - - desc = fontdescToCTFontDescriptor(&(fp->desc)); - desc = fontdescAppendFeatures(desc, fp->featureTypes, fp->featureSelectors, fp->nFeatures); - font = CTFontCreateWithFontDescriptor(desc, fp->desc.Size, NULL); - CFRelease(desc); // TODO correct? - return font; -} - static void applyAndFreeFontAttributes(struct foreachParams *p) { [p->converted enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, NSValue *val, BOOL *stop) { diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index 0c87a427..ba740933 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -255,9 +255,7 @@ CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd) } // fortunately features that aren't supported are simply ignored, so we can copy them all in -// LONGTERM FUTURE when we switch to 10.9, the language parameter won't be needed anymore -// LONGTERM FUTURE and on 10.10 we can use OpenType tags directly! -CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uint16_t *types, const uint16_t *selectors, size_t n) +CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uiOpenTypeFeatures *otf) { CTFontDescriptorRef new; CFMutableArrayRef outerArray; diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index c22c82b6..4020dd27 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -143,7 +143,7 @@ extern void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdg // fontmatch.m extern CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd); -extern CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uint16_t *types, const uint16_t *selectors, size_t n); +extern CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uiOpenTypeFeatures *otf); extern void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescriptor *uidesc); // attrstr.m From 91bfceaf71125c87f4d2330c3074ef2166ad2277 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 30 May 2017 14:00:58 -0400 Subject: [PATCH 0735/1329] And FINALLY cleaned up all the AAT nonsense. Much saner now. --- darwin/aat.m | 162 +++++++++++++++++++---------------------- darwin/fontmatch.m | 45 ++---------- darwin/opentype.m | 51 ++++++++++++- darwin/uipriv_darwin.h | 4 +- ui_attrstr.h | 3 +- unix/opentype.c | 2 +- windows/opentype.cpp | 2 +- 7 files changed, 137 insertions(+), 132 deletions(-) diff --git a/darwin/aat.m b/darwin/aat.m index 0c964e05..d46a3506 100644 --- a/darwin/aat.m +++ b/darwin/aat.m @@ -1,25 +1,18 @@ // 14 february 2017 #import "uipriv_darwin.h" -struct openTypeAATParams { - void (*doAAT)(uint16_t type, uint16_t selector, void *data); - void *data; -}; - -#define pcall(p, type, selector) ((*(p->doAAT))(type, selector, p->data)) - -static void boolspec(uint32_t value, uint16_t type, uint16_t ifTrue, uint16_t ifFalse, struct openTypeAATParams *p) +static void boolspec(uint32_t value, uint16_t type, uint16_t ifTrue, uint16_t ifFalse, aatBlock f) { // TODO are values other than 1 accepted for true by OpenType itself? (same for the rest of the file) if (value != 0) { - pcall(p, type, ifTrue); + f(type, ifTrue); return; } - pcall(p, type, ifFalse); + f(type, ifFalse); } // TODO double-check drawtext example to make sure all of these are used properly (I already screwed dlig up by putting clig twice instead) -static int foreach(char a, char b, char c, char d, uint32_t value, void *data) +void openTypeToAAT(char a, char b, char c, char d, uint32_t value, aatBlock f) { struct openTypeAATParams *p = (struct openTypeAATParams *) data; @@ -28,25 +21,25 @@ static int foreach(char a, char b, char c, char d, uint32_t value, void *data) boolspec(value, kLigaturesType, kCommonLigaturesOnSelector, kCommonLigaturesOffSelector, - p); + f); break; case mkTag('r', 'l', 'i', 'g'): boolspec(value, kLigaturesType, kRequiredLigaturesOnSelector, kRequiredLigaturesOffSelector, - p); + f); break; case mkTag('d', 'l', 'i', 'g'): boolspec(value, kLigaturesType, kRareLigaturesOnSelector, kRareLigaturesOffSelector, - p); + f); break; case mkTag('c', 'l', 'i', 'g'): boolspec(value, kLigaturesType, kContextualLigaturesOnSelector, kContextualLigaturesOffSelector, - p); + f); break; case mkTag('h', 'l', 'i', 'g'): // This technically isn't what is meant by "historical ligatures", but Core Text's internal AAT-to-OpenType mapping says to include it, so we include it too @@ -54,117 +47,117 @@ static int foreach(char a, char b, char c, char d, uint32_t value, void *data) boolspec(value, kLigaturesType, kHistoricalLigaturesOnSelector, kHistoricalLigaturesOffSelector, - p); + f); break; case mkTag('u', 'n', 'i', 'c'): // TODO is this correct, or should we provide an else case? if (value != 0) // this is undocumented; it comes from Core Text's internal AAT-to-OpenType conversion table - pcall(p, kLetterCaseType, 14); + f(p, kLetterCaseType, 14); break; // TODO will the following handle all cases properly, or are elses going to be needed? case mkTag('p', 'n', 'u', 'm'): if (value != 0) - pcall(p, kNumberSpacingType, kProportionalNumbersSelector); + f(p, kNumberSpacingType, kProportionalNumbersSelector); break; case mkTag('t', 'n', 'u', 'm'): if (value != 0) - pcall(p, kNumberSpacingType, kMonospacedNumbersSelector); + f(p, kNumberSpacingType, kMonospacedNumbersSelector); break; // TODO will the following handle all cases properly, or are elses going to be needed? case mkTag('s', 'u', 'p', 's'): if (value != 0) - pcall(p, kVerticalPositionType, kSuperiorsSelector); + f(p, kVerticalPositionType, kSuperiorsSelector); break; case mkTag('s', 'u', 'b', 's'): if (value != 0) - pcall(p, kVerticalPositionType, kInferiorsSelector); + f(p, kVerticalPositionType, kInferiorsSelector); break; case mkTag('o', 'r', 'd', 'n'): if (value != 0) - pcall(p, kVerticalPositionType, kOrdinalsSelector); + f(p, kVerticalPositionType, kOrdinalsSelector); break; case mkTag('s', 'i', 'n', 'f'): if (value != 0) - pcall(p, kVerticalPositionType, kScientificInferiorsSelector); + f(p, kVerticalPositionType, kScientificInferiorsSelector); break; // TODO will the following handle all cases properly, or are elses going to be needed? case mkTag('a', 'f', 'r', 'c'): if (value != 0) - pcall(p, kFractionsType, kVerticalFractionsSelector); + f(p, kFractionsType, kVerticalFractionsSelector); break; case mkTag('f', 'r', 'a', 'c'): if (value != 0) - pcall(p, kFractionsType, kDiagonalFractionsSelector); + f(p, kFractionsType, kDiagonalFractionsSelector); break; case mkTag('z', 'e', 'r', 'o'): boolspec(value, kTypographicExtrasType, kSlashedZeroOnSelector, kSlashedZeroOffSelector, - p); + f); break; case mkTag('m', 'g', 'r', 'k'): boolspec(value, kMathematicalExtrasType, kMathematicalGreekOnSelector, kMathematicalGreekOffSelector, - p); + f); break; case mkTag('o', 'r', 'n', 'm'): - pcall(p, kOrnamentSetsType, (uint16_t) value); + f(p, kOrnamentSetsType, (uint16_t) value); break; case mkTag('a', 'a', 'l', 't'): - pcall(p, kCharacterAlternativesType, (uint16_t) value); + f(p, kCharacterAlternativesType, (uint16_t) value); break; case mkTag('t', 'i', 't', 'l'): // TODO is this correct, or should we provide an else case? if (value != 0) - pcall(p, kStyleOptionsType, kTitlingCapsSelector); + f(p, kStyleOptionsType, kTitlingCapsSelector); break; // TODO will the following handle all cases properly, or are elses going to be needed? case mkTag('t', 'r', 'a', 'd'): if (value != 0) - pcall(p, kCharacterShapeType, kTraditionalCharactersSelector); + f(p, kCharacterShapeType, kTraditionalCharactersSelector); break; case mkTag('s', 'm', 'p', 'l'): if (value != 0) - pcall(p, kCharacterShapeType, kSimplifiedCharactersSelector); + f(p, kCharacterShapeType, kSimplifiedCharactersSelector); break; case mkTag('j', 'p', '7', '8'): if (value != 0) - pcall(p, kCharacterShapeType, kJIS1978CharactersSelector); + f(p, kCharacterShapeType, kJIS1978CharactersSelector); break; case mkTag('j', 'p', '8', '3'): if (value != 0) - pcall(p, kCharacterShapeType, kJIS1983CharactersSelector); + f(p, kCharacterShapeType, kJIS1983CharactersSelector); break; case mkTag('j', 'p', '9', '0'): if (value != 0) - pcall(p, kCharacterShapeType, kJIS1990CharactersSelector); + f(p, kCharacterShapeType, kJIS1990CharactersSelector); break; case mkTag('e', 'x', 'p', 't'): if (value != 0) - pcall(p, kCharacterShapeType, kExpertCharactersSelector); + f(p, kCharacterShapeType, kExpertCharactersSelector); break; case mkTag('j', 'p', '0', '4'): if (value != 0) - pcall(p, kCharacterShapeType, kJIS2004CharactersSelector); + f(p, kCharacterShapeType, kJIS2004CharactersSelector); break; case mkTag('h', 'o', 'j', 'o'): if (value != 0) - pcall(p, kCharacterShapeType, kHojoCharactersSelector); + f(p, kCharacterShapeType, kHojoCharactersSelector); break; case mkTag('n', 'l', 'c', 'k'): if (value != 0) - pcall(p, kCharacterShapeType, kNLCCharactersSelector); + f(p, kCharacterShapeType, kNLCCharactersSelector); break; case mkTag('t', 'n', 'a', 'm'): if (value != 0) - pcall(p, kCharacterShapeType, kTraditionalNamesCharactersSelector); + f(p, kCharacterShapeType, kTraditionalNamesCharactersSelector); break; case mkTag('o', 'n', 'u', 'm'): @@ -173,201 +166,201 @@ static int foreach(char a, char b, char c, char d, uint32_t value, void *data) case mkTag('l', 'n', 'u', 'm'): // TODO is this correct, or should we provide an else case? if (value != 0) - pcall(p, kNumberCaseType, kLowerCaseNumbersSelector); + f(p, kNumberCaseType, kLowerCaseNumbersSelector); break; case mkTag('h', 'n', 'g', 'l'): // TODO is this correct, or should we provide an else case? if (value != 0) - pcall(p, kTransliterationType, kHanjaToHangulSelector); + f(p, kTransliterationType, kHanjaToHangulSelector); break; case mkTag('n', 'a', 'l', 't'): - pcall(p, kAnnotationType, (uint16_t) value); + f(p, kAnnotationType, (uint16_t) value); break; case mkTag('r', 'u', 'b', 'y'): // include this for completeness boolspec(value, kRubyKanaType, kRubyKanaSelector, kNoRubyKanaSelector, - p); + f); // this is the current one boolspec(value, kRubyKanaType, kRubyKanaOnSelector, kRubyKanaOffSelector, - p); + f); break; case mkTag('i', 't', 'a', 'l'): // include this for completeness boolspec(value, kItalicCJKRomanType, kCJKItalicRomanSelector, kNoCJKItalicRomanSelector, - p); + f); // this is the current one boolspec(value, kItalicCJKRomanType, kCJKItalicRomanOnSelector, kCJKItalicRomanOffSelector, - p); + f); break; case mkTag('c', 'a', 's', 'e'): boolspec(value, kCaseSensitiveLayoutType, kCaseSensitiveLayoutOnSelector, kCaseSensitiveLayoutOffSelector, - p); + f); break; case mkTag('c', 'p', 's', 'p'): boolspec(value, kCaseSensitiveLayoutType, kCaseSensitiveSpacingOnSelector, kCaseSensitiveSpacingOffSelector, - p); + f); break; case mkTag('h', 'k', 'n', 'a'): boolspec(value, kAlternateKanaType, kAlternateHorizKanaOnSelector, kAlternateHorizKanaOffSelector, - p); + f); break; case mkTag('v', 'k', 'n', 'a'): boolspec(value, kAlternateKanaType, kAlternateVertKanaOnSelector, kAlternateVertKanaOffSelector, - p); + f); break; case mkTag('s', 's', '0', '1'): boolspec(value, kStylisticAlternativesType, kStylisticAltOneOnSelector, kStylisticAltOneOffSelector, - p); + f); break; case mkTag('s', 's', '0', '2'): boolspec(value, kStylisticAlternativesType, kStylisticAltTwoOnSelector, kStylisticAltTwoOffSelector, - p); + f); break; case mkTag('s', 's', '0', '3'): boolspec(value, kStylisticAlternativesType, kStylisticAltThreeOnSelector, kStylisticAltThreeOffSelector, - p); + f); break; case mkTag('s', 's', '0', '4'): boolspec(value, kStylisticAlternativesType, kStylisticAltFourOnSelector, kStylisticAltFourOffSelector, - p); + f); break; case mkTag('s', 's', '0', '5'): boolspec(value, kStylisticAlternativesType, kStylisticAltFiveOnSelector, kStylisticAltFiveOffSelector, - p); + f); break; case mkTag('s', 's', '0', '6'): boolspec(value, kStylisticAlternativesType, kStylisticAltSixOnSelector, kStylisticAltSixOffSelector, - p); + f); break; case mkTag('s', 's', '0', '7'): boolspec(value, kStylisticAlternativesType, kStylisticAltSevenOnSelector, kStylisticAltSevenOffSelector, - p); + f); break; case mkTag('s', 's', '0', '8'): boolspec(value, kStylisticAlternativesType, kStylisticAltEightOnSelector, kStylisticAltEightOffSelector, - p); + f); break; case mkTag('s', 's', '0', '9'): boolspec(value, kStylisticAlternativesType, kStylisticAltNineOnSelector, kStylisticAltNineOffSelector, - p); + f); break; case mkTag('s', 's', '1', '0'): boolspec(value, kStylisticAlternativesType, kStylisticAltTenOnSelector, kStylisticAltTenOffSelector, - p); + f); break; case mkTag('s', 's', '1', '1'): boolspec(value, kStylisticAlternativesType, kStylisticAltElevenOnSelector, kStylisticAltElevenOffSelector, - p); + f); break; case mkTag('s', 's', '1', '2'): boolspec(value, kStylisticAlternativesType, kStylisticAltTwelveOnSelector, kStylisticAltTwelveOffSelector, - p); + f); break; case mkTag('s', 's', '1', '3'): boolspec(value, kStylisticAlternativesType, kStylisticAltThirteenOnSelector, kStylisticAltThirteenOffSelector, - p); + f); break; case mkTag('s', 's', '1', '4'): boolspec(value, kStylisticAlternativesType, kStylisticAltFourteenOnSelector, kStylisticAltFourteenOffSelector, - p); + f); break; case mkTag('s', 's', '1', '5'): boolspec(value, kStylisticAlternativesType, kStylisticAltFifteenOnSelector, kStylisticAltFifteenOffSelector, - p); + f); break; case mkTag('s', 's', '1', '6'): boolspec(value, kStylisticAlternativesType, kStylisticAltSixteenOnSelector, kStylisticAltSixteenOffSelector, - p); + f); break; case mkTag('s', 's', '1', '7'): boolspec(value, kStylisticAlternativesType, kStylisticAltSeventeenOnSelector, kStylisticAltSeventeenOffSelector, - p); + f); break; case mkTag('s', 's', '1', '8'): boolspec(value, kStylisticAlternativesType, kStylisticAltEighteenOnSelector, kStylisticAltEighteenOffSelector, - p); + f); break; case mkTag('s', 's', '1', '9'): boolspec(value, kStylisticAlternativesType, kStylisticAltNineteenOnSelector, kStylisticAltNineteenOffSelector, - p); + f); break; case mkTag('s', 's', '2', '0'): boolspec(value, kStylisticAlternativesType, kStylisticAltTwentyOnSelector, kStylisticAltTwentyOffSelector, - p); + f); break; case mkTag('c', 'a', 'l', 't'): boolspec(value, kContextualAlternatesType, kContextualAlternatesOnSelector, kContextualAlternatesOffSelector, - p); + f); break; case mkTag('s', 'w', 's', 'h'): boolspec(value, kContextualAlternatesType, kSwashAlternatesOnSelector, kSwashAlternatesOffSelector, - p); + f); break; case mkTag('c', 's', 'w', 'h'): boolspec(value, kContextualAlternatesType, kContextualSwashAlternatesOnSelector, kContextualSwashAlternatesOffSelector, - p); + f); break; // TODO will the following handle all cases properly, or are elses going to be needed? @@ -375,35 +368,26 @@ static int foreach(char a, char b, char c, char d, uint32_t value, void *data) if (value != 0) { // include this for compatibility (some fonts that come with OS X still use this!) // TODO make it boolean? - pcall(p, kLetterCaseType, kSmallCapsSelector); + f(p, kLetterCaseType, kSmallCapsSelector); // this is the current one - pcall(p, kLowerCaseType, kLowerCaseSmallCapsSelector); + f(p, kLowerCaseType, kLowerCaseSmallCapsSelector); } break; case mkTag('p', 'c', 'a', 'p'): if (value != 0) - pcall(p, kLowerCaseType, kLowerCasePetiteCapsSelector); + f(p, kLowerCaseType, kLowerCasePetiteCapsSelector); break; // TODO will the following handle all cases properly, or are elses going to be needed? case mkTag('c', '2', 's', 'c'): if (value != 0) - pcall(p, kUpperCaseType, kUpperCaseSmallCapsSelector); + f(p, kUpperCaseType, kUpperCaseSmallCapsSelector); break; case mkTag('c', '2', 'p', 'c'): if (value != 0) - pcall(p, kUpperCaseType, kUpperCasePetiteCapsSelector); + f(p, kUpperCaseType, kUpperCasePetiteCapsSelector); break; } // TODO handle this properly - return 0; -} - -void openTypeToAAT(uiOpenTypeFeatures *otf, void (*doAAT)(uint16_t type, uint16_t selector, void *data), void *data) -{ - struct openTypeAATParams p; - - p.doAAT = doAAT; - p.data = data; - uiOpenTypeFeaturesForEach(otf, foreach, &p); + // (it used to return 0 when this still returned the number of selectors produced but IDK what properly is anymore) } diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index ba740933..56b8991d 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -258,50 +258,21 @@ CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd) CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uiOpenTypeFeatures *otf) { CTFontDescriptorRef new; - CFMutableArrayRef outerArray; - CFDictionaryRef innerDict; - CFNumberRef numType, numSelector; - const void *keys[2], *values[2]; - size_t i; - - outerArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - if (outerArray == NULL) { - // TODO - } - keys[0] = kCTFontFeatureTypeIdentifierKey; - keys[1] = kCTFontFeatureSelectorIdentifierKey; - for (i = 0; i < n; i++) { - numType = CFNumberCreate(NULL, kCFNumberSInt16Type, - (const SInt16 *) (types + i)); - numSelector = CFNumberCreate(NULL, kCFNumberSInt16Type, - (const SInt16 *) (selectors + i)); - values[0] = numType; - values[1] = numSelector; - innerDict = CFDictionaryCreate(NULL, - keys, values, 2, - // TODO are these correct? - &kCFCopyStringDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - if (innerDict == NULL) { - // TODO - } - CFArrayAppendValue(outerArray, innerDict); - CFRelease(innerDict); - CFRelease(numSelector); - CFRelease(numType); - } + CFMutableArrayRef featuresArray; + CFDictionaryRef attrs; + featuresArray = otfToFeaturesArray(otf); keys[0] = kCTFontFeatureSettingsAttribute; - values[0] = outerArray; - innerDict = CFDictionaryCreate(NULL, + values[0] = featuresArray; + attrs = CFDictionaryCreate(NULL, keys, values, 1, // TODO are these correct? &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - CFRelease(outerArray); - new = CTFontDescriptorCreateCopyWithAttributes(desc, innerDict); + CFRelease(featuresArray); + new = CTFontDescriptorCreateCopyWithAttributes(desc, attrs); + CFRelease(attrs); CFRelease(desc); - CFRelease(innerDict); return new; } diff --git a/darwin/opentype.m b/darwin/opentype.m index 52db9560..d6bd6652 100644 --- a/darwin/opentype.m +++ b/darwin/opentype.m @@ -63,7 +63,7 @@ int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char return 1; } -void uiOpenTypeFeaturesForEach(uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data) +void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data) { [otf->tags enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) { NSNumber *tn = (NSNumber *) key; @@ -87,4 +87,51 @@ int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeature return [a->tags isEqualToDictionary:b->tags]; } -// actual conversion to a feature dictionary is handled in aat.m; see there for details +// TODO explain all this +// TODO rename outerArray and innerDict (the names made sense when this was part of fontdescAppendFeatures(), but not here) +// TODO make all this use enumerateKeysAndObjects (which requires duplicating code)? +static int otfArrayForEach(char a, char b, char c, char d, uint32_t value, void *data) +{ + CFMutableArrayRef outerArray = (CFMutableArrayRef) data; + const void *keys[2], *values[2]; + + keys[0] = kCTFontFeatureTypeIdentifierKey; + keys[1] = kCTFontFeatureSelectorIdentifierKey; + openTypeToAAT(a, b, c, d, value, ^(uint16_t type, uint16_t selector) { + CFDictionaryRef innerDict; + CFNumberRef numType, numSelector; + + numType = CFNumberCreate(NULL, kCFNumberSInt16Type, + (const SInt16 *) (&type)); + numSelector = CFNumberCreate(NULL, kCFNumberSInt16Type, + (const SInt16 *) (&selector)); + values[0] = numType; + values[1] = numSelector; + innerDict = CFDictionaryCreate(NULL, + keys, values, 2, + // TODO are these correct? + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (innerDict == NULL) { + // TODO + } + CFArrayAppendValue(p->outerArray, innerDict); + CFRelease(innerDict); + CFRelease(numSelector); + CFRelease(numType); + }); + // TODO + return 0; +} + +CFArrayRef otfToFeaturesArray(const uiOpenTypeFeatures *otf) +{ + CFMutableArrayRef outerArray; + + outerArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + if (outerArray == NULL) { + // TODO + } + uiOpenTypeFeaturesForEach(otf, otfArrayForEach, outerArray); + return outerArray; +} diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 4020dd27..56a576d4 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -153,7 +153,8 @@ typedef void (^backgroundBlock)(uiDrawContext *c, uiDrawTextLayout *layout, doub extern CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks); // aat.m -extern void openTypeToAAT(uiOpenTypeFeatures *otf, void (*doAAT)(uint16_t type, uint16_t selector, void *data), void *data); +typedef void (^aatBlock)(uint16_t type, uint16_t selector); +extern void openTypeToAAT(char a, char b, char c, char d, uint32_t value, aatBlock f); // opentype.m // TODO this is only used by opentype.m and aat.m; figure out some better way to handle this @@ -164,6 +165,7 @@ extern void openTypeToAAT(uiOpenTypeFeatures *otf, void (*doAAT)(uint16_t type, (x8tox32(b) << 16) | \ (x8tox32(c) << 8) | \ x8tox32(d)) +extern CFArrayRef otfToFeaturesArray(const uiOpenTypeFeatures *otf); // future.m extern CFStringRef *FUTURE_kCTFontOpenTypeFeatureTag; diff --git a/ui_attrstr.h b/ui_attrstr.h index d293f590..ad4169e9 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -64,7 +64,8 @@ _UI_EXTERN uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures _UI_EXTERN void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value); _UI_EXTERN void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d); _UI_EXTERN int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value); -_UI_EXTERN void uiOpenTypeFeaturesForEach(uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data); +// TODO make other enumerators const (and in general const-correct everything) +_UI_EXTERN void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data); _UI_EXTERN int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeatures *b); typedef struct uiAttributeSpec uiAttributeSpec; diff --git a/unix/opentype.c b/unix/opentype.c index 6700b83e..f9d14e09 100644 --- a/unix/opentype.c +++ b/unix/opentype.c @@ -96,7 +96,7 @@ static void foreach(gpointer key, gpointer value, gpointer data) (*(ofe->f))((char) a, (char) b, (char) c, (char) d, GPOINTER_TO_INT(value), ofe->data); } -void uiOpenTypeFeaturesForEach(uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data) +void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data) { struct otfForEach ofe; diff --git a/windows/opentype.cpp b/windows/opentype.cpp index 141f8a5f..b27b7d7a 100644 --- a/windows/opentype.cpp +++ b/windows/opentype.cpp @@ -57,7 +57,7 @@ int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char return 1; } -void uiOpenTypeFeaturesForEach(uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data) +void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data) { tagmap::const_iterator iter, end; From 06becce34ce834af874ec08beea6e1cdd838bb8d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 30 May 2017 14:38:25 -0400 Subject: [PATCH 0736/1329] And finished the font attribute rewrite on OS X. Now to test. --- darwin/aat.m | 2 + darwin/attrstr.m | 96 +++++++++++++++++++++++++++++------------------- 2 files changed, 61 insertions(+), 37 deletions(-) diff --git a/darwin/aat.m b/darwin/aat.m index d46a3506..c11abccd 100644 --- a/darwin/aat.m +++ b/darwin/aat.m @@ -1,6 +1,8 @@ // 14 february 2017 #import "uipriv_darwin.h" +// TODO explain the purpose of this file + static void boolspec(uint32_t value, uint16_t type, uint16_t ifTrue, uint16_t ifFalse, aatBlock f) { // TODO are values other than 1 accepted for true by OpenType itself? (same for the rest of the file) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 60120af9..ba69b9ed 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -146,6 +146,7 @@ struct foreachParams { @end +// TODO merge this with adjustFontInRange() (and TODO figure out why they were separate in the first place; probably Windows?) static void ensureFontInRange(struct foreachParams *p, size_t start, size_t end) { size_t i; @@ -207,30 +208,6 @@ static CGColorRef mkcolor(uiAttributeSpec *spec) return color; } -struct aatParam { - struct foreachParams *p; - size_t start; - size_t end; -}; - -static void doAAT(uint16_t type, uint16_t selector, void *data) -{ -#if 0 /* TODO */ - struct aatParam *p = (struct aatParam *) data; - - ensureFontInRange(p->p, p->start, p->end); - adjustFontInRange(p->p, p->start, p->end, ^(struct fontParams *fp) { - fp->featureTypes[fp->nFeatures] = type; - fp->featureSelectors[fp->nFeatures] = selector; - fp->nFeatures++; - if (fp->nFeatures == maxFeatures) { - // TODO - // TODO move this check to the top like in the drawtext example? and all the other instances of this? - } - }); -#endif -} - static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) { struct foreachParams *p = (struct foreachParams *) data; @@ -341,22 +318,67 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t return 0; } +static BOOL cfaIsEqual(combinedFontAttr *a, combinedFontAttr *b) +{ + if (a == nil && b == nil) + return YES; + if (a == nil || b == nil) + return NO; + return [a same:b]; +} + static void applyAndFreeFontAttributes(struct foreachParams *p) { - [p->converted enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, NSValue *val, BOOL *stop) { - struct fontParams *fp; - CTFontRef font; - CFRange range; + CFIndex i; + combinedFontAttr *cfa, *cfab; + CTFontRef defaultFont; + CTFontRef font; + CFRange range; - fp = (struct fontParams *) [val pointerValue]; - font = fontdescToCTFont(fp); - range.location = [key integerValue]; - // TODO this is wrong for surrogate pairs - range.length = 1; + // first get the default font as a CTFontRef + cfa = [[combinedFontAttr alloc] initWithDefaultFont:p->defaultFont]; + defaultFont = [cfa toCTFont]; + [cfa release]; + + // now go through, fililng in the font attribute for successive ranges of identical combinedFontAttrs + // we are best off treating series of identical fonts as single ranges ourselves for parity across platforms, even if OS X does something similar itself + // this also avoids breaking apart surrogate pairs (though IIRC OS X doing the something similar itself might make this a non-issue here) + cfa = nil; + n = CFAttributedStringGetLength(p->mas); + range.location = 0; + for (i = 0; i < n; i++) { + NSNumber *nn; + + nn = [NSNumber numberWithInteger:i]; + cfab = (combinedFontAttr *) [p->combinedFontAttrs objectForKey:nn]; + if (cfaIsEqual(cfa, cfab)) + continue; + + // the font has changed; write out the old one + range.length = i - range.location; + if (cfa == nil) { + font = defaultFont; + CFRetain(font); + } else + font = [cfa toCTFont]; CFAttributedStringSetAttribute(p->mas, range, kCTFontAttributeName, font); CFRelease(font); - uiFree(fp); - }]; + // and start this run + cfa = cfab; + range.location = i; + } + + // and finally, write out the last range + range.length = i - range.location; + if (cfa == nil) { + font = defaultFont; + CFRetain(font); + } else + font = [cfa toCTFont]; + CFAttributedStringSetAttribute(p->mas, range, kCTFontAttributeName, font); + CFRelease(font); + + CFRelease(defaultFont); } static const CTTextAlignment ctaligns[] = { @@ -424,12 +446,12 @@ CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray CFAttributedStringBeginEditing(mas); fep.mas = mas; - fep.converted = [NSMutableDictionary new]; + fep.combinedFontAttrs = [NSMutableDictionary new]; fep.defaultFont = p->DefaultFont; fep.backgroundBlocks = [NSMutableArray new]; uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); applyAndFreeFontAttributes(&fep); - [fep.converted release]; + [fep.combinedFontAttrs release]; CFAttributedStringEndEditing(mas); *backgroundBlocks = fep.backgroundBlocks; From 01df4631f68f1edf695080d1b58249a50eeb1647 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 30 May 2017 15:24:31 -0400 Subject: [PATCH 0737/1329] And finished integrating the new attributed string stuff on OS X. --- common/attrlist.c | 2 +- darwin/aat.m | 62 ++++++++++++++++------------------ darwin/attrstr.m | 14 +++++--- darwin/fontmatch.m | 3 +- darwin/opentype.m | 9 ++--- examples/drawtext/attributes.c | 4 +-- ui_attrstr.h | 1 + 7 files changed, 50 insertions(+), 45 deletions(-) diff --git a/common/attrlist.c b/common/attrlist.c index ca2937dc..e50ec141 100644 --- a/common/attrlist.c +++ b/common/attrlist.c @@ -60,7 +60,7 @@ static void attrClearSpec(struct attr *a) uiFree((char *) (a->spec.Family)); break; case uiAttributeFeatures: - uiOpenTypeFeaturesFree((uiOpenTypeFeatures *) (a->spec.Features)); + uiFreeOpenTypeFeatures((uiOpenTypeFeatures *) (a->spec.Features)); break; } } diff --git a/darwin/aat.m b/darwin/aat.m index c11abccd..0c3fd5fa 100644 --- a/darwin/aat.m +++ b/darwin/aat.m @@ -16,8 +16,6 @@ static void boolspec(uint32_t value, uint16_t type, uint16_t ifTrue, uint16_t if // TODO double-check drawtext example to make sure all of these are used properly (I already screwed dlig up by putting clig twice instead) void openTypeToAAT(char a, char b, char c, char d, uint32_t value, aatBlock f) { - struct openTypeAATParams *p = (struct openTypeAATParams *) data; - switch (mkTag(a, b, c, d)) { case mkTag('l', 'i', 'g', 'a'): boolspec(value, kLigaturesType, @@ -55,45 +53,45 @@ void openTypeToAAT(char a, char b, char c, char d, uint32_t value, aatBlock f) // TODO is this correct, or should we provide an else case? if (value != 0) // this is undocumented; it comes from Core Text's internal AAT-to-OpenType conversion table - f(p, kLetterCaseType, 14); + f(kLetterCaseType, 14); break; // TODO will the following handle all cases properly, or are elses going to be needed? case mkTag('p', 'n', 'u', 'm'): if (value != 0) - f(p, kNumberSpacingType, kProportionalNumbersSelector); + f(kNumberSpacingType, kProportionalNumbersSelector); break; case mkTag('t', 'n', 'u', 'm'): if (value != 0) - f(p, kNumberSpacingType, kMonospacedNumbersSelector); + f(kNumberSpacingType, kMonospacedNumbersSelector); break; // TODO will the following handle all cases properly, or are elses going to be needed? case mkTag('s', 'u', 'p', 's'): if (value != 0) - f(p, kVerticalPositionType, kSuperiorsSelector); + f(kVerticalPositionType, kSuperiorsSelector); break; case mkTag('s', 'u', 'b', 's'): if (value != 0) - f(p, kVerticalPositionType, kInferiorsSelector); + f(kVerticalPositionType, kInferiorsSelector); break; case mkTag('o', 'r', 'd', 'n'): if (value != 0) - f(p, kVerticalPositionType, kOrdinalsSelector); + f(kVerticalPositionType, kOrdinalsSelector); break; case mkTag('s', 'i', 'n', 'f'): if (value != 0) - f(p, kVerticalPositionType, kScientificInferiorsSelector); + f(kVerticalPositionType, kScientificInferiorsSelector); break; // TODO will the following handle all cases properly, or are elses going to be needed? case mkTag('a', 'f', 'r', 'c'): if (value != 0) - f(p, kFractionsType, kVerticalFractionsSelector); + f(kFractionsType, kVerticalFractionsSelector); break; case mkTag('f', 'r', 'a', 'c'): if (value != 0) - f(p, kFractionsType, kDiagonalFractionsSelector); + f(kFractionsType, kDiagonalFractionsSelector); break; case mkTag('z', 'e', 'r', 'o'): @@ -109,57 +107,57 @@ void openTypeToAAT(char a, char b, char c, char d, uint32_t value, aatBlock f) f); break; case mkTag('o', 'r', 'n', 'm'): - f(p, kOrnamentSetsType, (uint16_t) value); + f(kOrnamentSetsType, (uint16_t) value); break; case mkTag('a', 'a', 'l', 't'): - f(p, kCharacterAlternativesType, (uint16_t) value); + f(kCharacterAlternativesType, (uint16_t) value); break; case mkTag('t', 'i', 't', 'l'): // TODO is this correct, or should we provide an else case? if (value != 0) - f(p, kStyleOptionsType, kTitlingCapsSelector); + f(kStyleOptionsType, kTitlingCapsSelector); break; // TODO will the following handle all cases properly, or are elses going to be needed? case mkTag('t', 'r', 'a', 'd'): if (value != 0) - f(p, kCharacterShapeType, kTraditionalCharactersSelector); + f(kCharacterShapeType, kTraditionalCharactersSelector); break; case mkTag('s', 'm', 'p', 'l'): if (value != 0) - f(p, kCharacterShapeType, kSimplifiedCharactersSelector); + f(kCharacterShapeType, kSimplifiedCharactersSelector); break; case mkTag('j', 'p', '7', '8'): if (value != 0) - f(p, kCharacterShapeType, kJIS1978CharactersSelector); + f(kCharacterShapeType, kJIS1978CharactersSelector); break; case mkTag('j', 'p', '8', '3'): if (value != 0) - f(p, kCharacterShapeType, kJIS1983CharactersSelector); + f(kCharacterShapeType, kJIS1983CharactersSelector); break; case mkTag('j', 'p', '9', '0'): if (value != 0) - f(p, kCharacterShapeType, kJIS1990CharactersSelector); + f(kCharacterShapeType, kJIS1990CharactersSelector); break; case mkTag('e', 'x', 'p', 't'): if (value != 0) - f(p, kCharacterShapeType, kExpertCharactersSelector); + f(kCharacterShapeType, kExpertCharactersSelector); break; case mkTag('j', 'p', '0', '4'): if (value != 0) - f(p, kCharacterShapeType, kJIS2004CharactersSelector); + f(kCharacterShapeType, kJIS2004CharactersSelector); break; case mkTag('h', 'o', 'j', 'o'): if (value != 0) - f(p, kCharacterShapeType, kHojoCharactersSelector); + f(kCharacterShapeType, kHojoCharactersSelector); break; case mkTag('n', 'l', 'c', 'k'): if (value != 0) - f(p, kCharacterShapeType, kNLCCharactersSelector); + f(kCharacterShapeType, kNLCCharactersSelector); break; case mkTag('t', 'n', 'a', 'm'): if (value != 0) - f(p, kCharacterShapeType, kTraditionalNamesCharactersSelector); + f(kCharacterShapeType, kTraditionalNamesCharactersSelector); break; case mkTag('o', 'n', 'u', 'm'): @@ -168,15 +166,15 @@ void openTypeToAAT(char a, char b, char c, char d, uint32_t value, aatBlock f) case mkTag('l', 'n', 'u', 'm'): // TODO is this correct, or should we provide an else case? if (value != 0) - f(p, kNumberCaseType, kLowerCaseNumbersSelector); + f(kNumberCaseType, kLowerCaseNumbersSelector); break; case mkTag('h', 'n', 'g', 'l'): // TODO is this correct, or should we provide an else case? if (value != 0) - f(p, kTransliterationType, kHanjaToHangulSelector); + f(kTransliterationType, kHanjaToHangulSelector); break; case mkTag('n', 'a', 'l', 't'): - f(p, kAnnotationType, (uint16_t) value); + f(kAnnotationType, (uint16_t) value); break; case mkTag('r', 'u', 'b', 'y'): // include this for completeness @@ -370,24 +368,24 @@ void openTypeToAAT(char a, char b, char c, char d, uint32_t value, aatBlock f) if (value != 0) { // include this for compatibility (some fonts that come with OS X still use this!) // TODO make it boolean? - f(p, kLetterCaseType, kSmallCapsSelector); + f(kLetterCaseType, kSmallCapsSelector); // this is the current one - f(p, kLowerCaseType, kLowerCaseSmallCapsSelector); + f(kLowerCaseType, kLowerCaseSmallCapsSelector); } break; case mkTag('p', 'c', 'a', 'p'): if (value != 0) - f(p, kLowerCaseType, kLowerCasePetiteCapsSelector); + f(kLowerCaseType, kLowerCasePetiteCapsSelector); break; // TODO will the following handle all cases properly, or are elses going to be needed? case mkTag('c', '2', 's', 'c'): if (value != 0) - f(p, kUpperCaseType, kUpperCaseSmallCapsSelector); + f(kUpperCaseType, kUpperCaseSmallCapsSelector); break; case mkTag('c', '2', 'p', 'c'): if (value != 0) - f(p, kUpperCaseType, kUpperCasePetiteCapsSelector); + f(kUpperCaseType, kUpperCasePetiteCapsSelector); break; } // TODO handle this properly diff --git a/darwin/attrstr.m b/darwin/attrstr.m index ba69b9ed..6cf412a4 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -131,7 +131,8 @@ struct foreachParams { CTFontDescriptorRef desc; CTFontRef font; - uidesc.Family = self.family; + // TODO const-correct uiDrawFontDescriptor or change this function below + uidesc.Family = (char *) (self.family); uidesc.Size = self.size; uidesc.Weight = self.weight; uidesc.Italic = self.italic; @@ -170,7 +171,7 @@ static void adjustFontInRange(struct foreachParams *p, size_t start, size_t end, for (i = start; i < end; i++) { n = [NSNumber numberWithInteger:i]; - v = (combinedFontAttr *) [p->combinedFontAttrs objectForKey:n]; + cfa = (combinedFontAttr *) [p->combinedFontAttrs objectForKey:n]; adj(cfa); } } @@ -329,7 +330,7 @@ static BOOL cfaIsEqual(combinedFontAttr *a, combinedFontAttr *b) static void applyAndFreeFontAttributes(struct foreachParams *p) { - CFIndex i; + CFIndex i, n; combinedFontAttr *cfa, *cfab; CTFontRef defaultFont; CTFontRef font; @@ -349,6 +350,7 @@ static void applyAndFreeFontAttributes(struct foreachParams *p) for (i = 0; i < n; i++) { NSNumber *nn; + // TODO use NSValue or some other method of NSNumber nn = [NSNumber numberWithInteger:i]; cfab = (combinedFontAttr *) [p->combinedFontAttrs objectForKey:nn]; if (cfaIsEqual(cfa, cfab)) @@ -357,6 +359,7 @@ static void applyAndFreeFontAttributes(struct foreachParams *p) // the font has changed; write out the old one range.length = i - range.location; if (cfa == nil) { + // TODO this isn't necessary because of default font shenanigans below font = defaultFont; CFRetain(font); } else @@ -371,6 +374,7 @@ static void applyAndFreeFontAttributes(struct foreachParams *p) // and finally, write out the last range range.length = i - range.location; if (cfa == nil) { + // TODO likewise font = defaultFont; CFRetain(font); } else @@ -414,7 +418,6 @@ CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray CFAttributedStringRef base; CFMutableAttributedStringRef mas; struct foreachParams fep; - struct fontParams ffp; cfstr = CFStringCreateWithCharacters(NULL, attrstrUTF16(p->String), attrstrUTF16Len(p->String)); if (cfstr == NULL) { @@ -426,11 +429,12 @@ CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray if (defaultAttrs == NULL) { // TODO } - memset(&ffp, 0, sizeof (struct fontParams)); +#if 0 /* TODO */ ffp.desc = *(p->DefaultFont); defaultCTFont = fontdescToCTFont(&ffp); CFDictionaryAddValue(defaultAttrs, kCTFontAttributeName, defaultCTFont); CFRelease(defaultCTFont); +#endif ps = mkParagraphStyle(p); CFDictionaryAddValue(defaultAttrs, kCTParagraphStyleAttributeName, ps); CFRelease(ps); diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index 56b8991d..abd38c32 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -258,8 +258,9 @@ CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd) CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uiOpenTypeFeatures *otf) { CTFontDescriptorRef new; - CFMutableArrayRef featuresArray; + CFArrayRef featuresArray; CFDictionaryRef attrs; + const void *keys[1], *values[1]; featuresArray = otfToFeaturesArray(otf); keys[0] = kCTFontFeatureSettingsAttribute; diff --git a/darwin/opentype.m b/darwin/opentype.m index d6bd6652..00a76200 100644 --- a/darwin/opentype.m +++ b/darwin/opentype.m @@ -93,14 +93,15 @@ int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeature static int otfArrayForEach(char a, char b, char c, char d, uint32_t value, void *data) { CFMutableArrayRef outerArray = (CFMutableArrayRef) data; - const void *keys[2], *values[2]; - keys[0] = kCTFontFeatureTypeIdentifierKey; - keys[1] = kCTFontFeatureSelectorIdentifierKey; openTypeToAAT(a, b, c, d, value, ^(uint16_t type, uint16_t selector) { CFDictionaryRef innerDict; CFNumberRef numType, numSelector; + // not well documented, but fixed-size arrays don't support __block either (VLAs are documented as being unsupported) + const void *keys[2], *values[2]; + keys[0] = kCTFontFeatureTypeIdentifierKey; + keys[1] = kCTFontFeatureSelectorIdentifierKey; numType = CFNumberCreate(NULL, kCFNumberSInt16Type, (const SInt16 *) (&type)); numSelector = CFNumberCreate(NULL, kCFNumberSInt16Type, @@ -115,7 +116,7 @@ static int otfArrayForEach(char a, char b, char c, char d, uint32_t value, void if (innerDict == NULL) { // TODO } - CFArrayAppendValue(p->outerArray, innerDict); + CFArrayAppendValue(outerArray, innerDict); CFRelease(innerDict); CFRelease(numSelector); CFRelease(numType); diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index 69bbe032..f15ba070 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -170,7 +170,7 @@ static void setupAttributedString(void) spec.A = 0.75; uiAttributedStringSetAttribute(attrstr, &spec, start + 12, end); spec.Type = uiAttributeFamily; - spec.Value = "Helvetica"; + spec.Family = "Helvetica"; uiAttributedStringSetAttribute(attrstr, &spec, start + 8, end - 1); spec.Type = uiAttributeBackground; spec.R = 1.0; @@ -245,7 +245,7 @@ static void setupAttributedString(void) end = start + strlen(next); uiAttributedStringAppendUnattributed(attrstr, next); spec.Type = uiAttributeFeatures; - spec.Value = otf; + spec.Features = otf; uiAttributedStringSetAttribute(attrstr, &spec, start, end); uiAttributedStringAppendUnattributed(attrstr, ")"); diff --git a/ui_attrstr.h b/ui_attrstr.h index ad4169e9..0d1b9d81 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -256,6 +256,7 @@ _UI_EXTERN void uiDrawCaret(uiDrawContext *c, double x, double y, uiDrawTextLayo typedef struct uiFontButton uiFontButton; #define uiFontButton(this) ((uiFontButton *) (this)) +// TODO have a function that sets an entire font descriptor to a range in a uiAttributedString at once? _UI_EXTERN void uiFontButtonFont(uiFontButton *b, uiDrawFontDescriptor *desc); // TOOD SetFont, mechanics _UI_EXTERN void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data); From 81a82723d0cbee854c113043d2f57e9584ab9afd Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 30 May 2017 16:57:25 -0400 Subject: [PATCH 0738/1329] Fixed the Windows code to run after the recent changes. Now to decide whether to clean it up like we did the OS X code... --- windows/attrstr.cpp | 6 +++--- windows/opentype.cpp | 2 +- windows/uipriv_windows.hpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index 8f5e0adf..be23961e 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -40,7 +40,7 @@ static void ensureEffectsInRange(struct foreachParams *p, size_t start, size_t e } } -static void setFeaturesInRange(struct foreachParams *p, size_t start, size_t end, uiOpenTypeFeatures *otf) +static void setFeaturesInRange(struct foreachParams *p, size_t start, size_t end, const uiOpenTypeFeatures *otf) { IDWriteTypography *dt; size_t i; @@ -89,7 +89,7 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t range.length = end - start; switch (spec->Type) { case uiAttributeFamily: - wfamily = toUTF16((char *) (spec->Value)); + wfamily = toUTF16(spec->Family); hr = p->layout->SetFontFamilyName(wfamily, range); if (hr != S_OK) logHRESULT(L"error applying family name attribute", hr); @@ -196,7 +196,7 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t } break; case uiAttributeFeatures: - setFeaturesInRange(p, start, end, (uiOpenTypeFeatures *) (spec->Value)); + setFeaturesInRange(p, start, end, spec->Features); break; default: // TODO complain diff --git a/windows/opentype.cpp b/windows/opentype.cpp index b27b7d7a..0275d916 100644 --- a/windows/opentype.cpp +++ b/windows/opentype.cpp @@ -81,7 +81,7 @@ int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeature return *(a->tags) == *(b->tags); } -IDWriteTypography *otfToDirectWrite(uiOpenTypeFeatures *otf) +IDWriteTypography *otfToDirectWrite(const uiOpenTypeFeatures *otf) { IDWriteTypography *dt; tagmap::const_iterator iter, end; diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index aa39dd07..f5adacdd 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -164,4 +164,4 @@ extern ID2D1DCRenderTarget *makeHDCRenderTarget(HDC dc, RECT *r); extern void fontdescFromIDWriteFont(IDWriteFont *font, uiDrawFontDescriptor *uidesc); // opentype.cpp -extern IDWriteTypography *otfToDirectWrite(uiOpenTypeFeatures *otf); +extern IDWriteTypography *otfToDirectWrite(const uiOpenTypeFeatures *otf); From c61bbfe5c88b105ccea771c2856a5e3bc511962f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 30 May 2017 18:46:30 -0400 Subject: [PATCH 0739/1329] Updated the GTK+ code to the newest changes. I *do* need to fix this one. --- unix/attrstr.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/unix/attrstr.c b/unix/attrstr.c index 7bf556c9..21fe108c 100644 --- a/unix/attrstr.c +++ b/unix/attrstr.c @@ -120,7 +120,7 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t switch (spec->Type) { case uiAttributeFamily: addattr(p, start, end, - pango_attr_family_new((const char *) (spec->Value))); + pango_attr_family_new(spec->Family)); break; case uiAttributeSize: addattr(p, start, end, @@ -199,8 +199,8 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t } break; case uiAttributeFeatures: - // TODO ensure the parentheses around spec->Value are provided on Windows - setFeaturesInRange(p, start, end, (uiOpenTypeFeatures *) (spec->Value)); + // TODO ensure the parentheses around spec->Value are provided on Windows (TODO still relevant?) + setFeaturesInRange(p, start, end, spec->Features); break; default: // TODO complain From 2e982190094a3a2416c218d1482a49fa160ee9a3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 30 May 2017 19:07:01 -0400 Subject: [PATCH 0740/1329] And fixed the whole OpenType features nonsense on GTK+, since we now group all OpenType features together. We're now much closer to pushing this back into master! --- unix/attrstr.c | 81 +++++++--------------------------------------- unix/opentype.c | 4 +-- unix/uipriv_unix.h | 2 +- 3 files changed, 14 insertions(+), 73 deletions(-) diff --git a/unix/attrstr.c b/unix/attrstr.c index 21fe108c..ad620aed 100644 --- a/unix/attrstr.c +++ b/unix/attrstr.c @@ -3,57 +3,25 @@ // TODO pango alpha attributes turn 0 into 65535 :| -// we need to collect all the OpenType features and background blocks and add them all at once -// TODO this is the wrong approach; it causes Pango to end runs early, meaning attributes like the ligature attributes never get applied properly +// we need to collect all the background blocks and add them all at once // TODO rename this struct to something that isn't exclusively foreach-ing? struct foreachParams { const char *s; PangoAttrList *attrs; - // keys are pointers to size_t maintained by g_new0()/g_free() - // values are strings - GHashTable *features; // TODO use pango's built-in background attribute? GPtrArray *backgroundClosures; }; -static gboolean featurePosEqual(gconstpointer a, gconstpointer b) +// TODO merge this into the main function below +static PangoAttribute *mkFeaturesAttribute(const uiOpenTypeFeatures *otf) { - size_t *sa = (size_t *) a; - size_t *sb = (size_t *) b; + char *s; + PangoAttribute *attr; - return *sa == *sb; -} - -static guint featurePosHash(gconstpointer n) -{ - size_t *sn = (size_t *) n; - - return (guint) (*sn); -} - -static void freeFeatureString(gpointer s) -{ - g_string_free((GString *) s, TRUE); -} - -#define isCodepointStart(b) (((uint8_t) (b)) <= 0x7F || ((uint8_t) (b)) >= 0xC0) - -static void setFeaturesInRange(struct foreachParams *p, size_t start, size_t end, uiOpenTypeFeatures *otf) -{ - size_t i; - size_t *key; - GString *new; - - new = otfToPangoCSSString(otf); - for (i = start; i < end; i++) { - // don't create redundant entries; see below - if (!isCodepointStart(p->s[i])) - continue; - key = g_new0(size_t, 1); - *key = i; - g_hash_table_replace(p->features, key, g_strdup(new->str)); - } - g_string_free(new, TRUE); + s = otfToPangoCSSString(otf); + attr = FUTURE_pango_attr_font_features_new(s); + g_free(s); + return attr; } struct closureParams { @@ -199,8 +167,8 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t } break; case uiAttributeFeatures: - // TODO ensure the parentheses around spec->Value are provided on Windows (TODO still relevant?) - setFeaturesInRange(p, start, end, spec->Features); + // TODO handle NULLs properly on all platforms + addattr(p, start, end, mkFeaturesAttribute(spec->Features)); break; default: // TODO complain @@ -209,29 +177,6 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t return 0; } -static gboolean applyFeatures(gpointer key, gpointer value, gpointer data) -{ - struct foreachParams *p = (struct foreachParams *) data; - size_t *pos = (size_t *) key; - char *s = (char *) value; - size_t n; - - // make sure we cover an entire code point - // otherwise Pango will break apart multi-byte characters, spitting out U+FFFD characters at the invalid points - n = 1; - while (!isCodepointStart(p->s[*pos + n])) - n++; - addattr(p, *pos, *pos + n, - FUTURE_pango_attr_font_features_new(s)); - return TRUE; // always delete; we're emptying the map -} - -static void applyAndFreeFeatureAttributes(struct foreachParams *p) -{ - g_hash_table_foreach_remove(p->features, applyFeatures, p); - g_hash_table_destroy(p->features); -} - static void unrefClosure(gpointer data) { g_closure_unref((GClosure *) data); @@ -243,12 +188,8 @@ PangoAttrList *attrstrToPangoAttrList(uiDrawTextLayoutParams *p, GPtrArray **bac fep.s = uiAttributedStringString(p->String); fep.attrs = pango_attr_list_new(); - fep.features = g_hash_table_new_full( - featurePosHash, featurePosEqual, - g_free, g_free); fep.backgroundClosures = g_ptr_array_new_with_free_func(unrefClosure); uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); - applyAndFreeFeatureAttributes(&fep); *backgroundClosures = fep.backgroundClosures; return fep.attrs; } diff --git a/unix/opentype.c b/unix/opentype.c index f9d14e09..608a0815 100644 --- a/unix/opentype.c +++ b/unix/opentype.c @@ -187,7 +187,7 @@ static int toCSS(char a, char b, char c, char d, uint32_t value, void *data) return 0; } -GString *otfToPangoCSSString(uiOpenTypeFeatures *otf) +gchar *otfToPangoCSSString(const uiOpenTypeFeatures *otf) { GString *s; @@ -196,5 +196,5 @@ GString *otfToPangoCSSString(uiOpenTypeFeatures *otf) if (s->len != 0) // and remove the last comma g_string_truncate(s, s->len - 2); - return s; + return g_string_free(s, FALSE); } diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index c80e8cc6..46a2426f 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -73,4 +73,4 @@ extern const PangoStyle pangoItalics[]; extern const PangoStretch pangoStretches[]; // opentype.c -extern GString *otfToPangoCSSString(uiOpenTypeFeatures *otf); +extern gchar *otfToPangoCSSString(const uiOpenTypeFeatures *otf); From 1d40ab659c9004e4da33720c842d2c5d5aa13fb3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 30 May 2017 19:07:59 -0400 Subject: [PATCH 0741/1329] More TODOs. --- ui_attrstr.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ui_attrstr.h b/ui_attrstr.h index 0d1b9d81..2f41cebb 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -34,6 +34,7 @@ _UI_ENUM(uiAttribute) { uiAttributeUnderlineColor, // enum uiDrawUnderlineColor // TODO note these are copied + // TODO figure out how we're going to deal with being able to edit features over time... uiAttributeFeatures, // use Features }; From 1e31ef24c6dec9b72b8ea9b900a74d2d64ba36a8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 30 May 2017 22:22:56 -0400 Subject: [PATCH 0742/1329] Minor code cleanup. Not sure what to do next... --- unix/attrstr.c | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/unix/attrstr.c b/unix/attrstr.c index ad620aed..7f12fff8 100644 --- a/unix/attrstr.c +++ b/unix/attrstr.c @@ -3,27 +3,13 @@ // TODO pango alpha attributes turn 0 into 65535 :| -// we need to collect all the background blocks and add them all at once -// TODO rename this struct to something that isn't exclusively foreach-ing? +// TODO make this name less generic? struct foreachParams { - const char *s; PangoAttrList *attrs; // TODO use pango's built-in background attribute? GPtrArray *backgroundClosures; }; -// TODO merge this into the main function below -static PangoAttribute *mkFeaturesAttribute(const uiOpenTypeFeatures *otf) -{ - char *s; - PangoAttribute *attr; - - s = otfToPangoCSSString(otf); - attr = FUTURE_pango_attr_font_features_new(s); - g_free(s); - return attr; -} - struct closureParams { size_t start; size_t end; @@ -63,7 +49,7 @@ static GClosure *mkBackgroundClosure(size_t start, size_t end, double r, double p->g = g; p->b = b; p->a = a; - closure = (GClosure *) g_cclosure_new(G_CALLBACK(backgroundClosure), p, freeClosureParams); + closure = g_cclosure_new(G_CALLBACK(backgroundClosure), p, freeClosureParams); // TODO write a specific marshaler // TODO or drop the closure stuff entirely g_closure_set_marshal(closure, g_cclosure_marshal_generic); @@ -84,6 +70,7 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t struct foreachParams *p = (struct foreachParams *) data; GClosure *closure; PangoUnderline underline; + char *featurestr; switch (spec->Type) { case uiAttributeFamily: @@ -168,7 +155,10 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t break; case uiAttributeFeatures: // TODO handle NULLs properly on all platforms - addattr(p, start, end, mkFeaturesAttribute(spec->Features)); + featurestr = otfToPangoCSSString(spec->Features); + addattr(p, start, end, + FUTURE_pango_attr_font_features_new(featurestr)); + g_free(featurestr); break; default: // TODO complain @@ -186,7 +176,6 @@ PangoAttrList *attrstrToPangoAttrList(uiDrawTextLayoutParams *p, GPtrArray **bac { struct foreachParams fep; - fep.s = uiAttributedStringString(p->String); fep.attrs = pango_attr_list_new(); fep.backgroundClosures = g_ptr_array_new_with_free_func(unrefClosure); uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); From b47431cd7c6f00370c3f29143db5f4aab0f6e15f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 30 May 2017 22:57:38 -0400 Subject: [PATCH 0743/1329] More TODOs. --- windows/separator.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/windows/separator.cpp b/windows/separator.cpp index e123e275..28e77e83 100644 --- a/windows/separator.cpp +++ b/windows/separator.cpp @@ -1,6 +1,10 @@ // 20 may 2015 #include "uipriv_windows.hpp" +// TODO +// - font scaling issues? https://www.viksoe.dk/code/bevelline.htm +// - isn't something in vista app guidelines suggesting this too? or some other microsoft doc? and what about VS itself? + // references: // - http://stackoverflow.com/questions/2892703/how-do-i-draw-separators // - https://msdn.microsoft.com/en-us/library/windows/desktop/dn742405%28v=vs.85%29.aspx From 98459d28787e92621cd6c1ffa24a9f98a2cb0f22 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 31 May 2017 18:45:11 -0400 Subject: [PATCH 0744/1329] Dropped the redundant features collection stuff on Windows like we did on OS X and GTK+ yesterday. That just leaves all the drawing effects, which we have to collect in a different way (like we did on OS X). --- windows/attrstr.cpp | 50 ++++++++------------------------------------- 1 file changed, 8 insertions(+), 42 deletions(-) diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index be23961e..91475602 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -5,7 +5,7 @@ // TODO this whole file needs cleanup // we need to combine color and underline style into one unit for IDWriteLayout::SetDrawingEffect() -// we also need to collect all the OpenType features and background blocks and add them all at once +// we also need to collect all the background blocks and add them all at once // TODO(TODO does not seem to apply here?) this is the wrong approach; it causes Pango to end runs early, meaning attributes like the ligature attributes never get applied properly // TODO contextual alternates override ligatures? // TODO rename this struct to something that isn't exclusively foreach-ing? @@ -13,7 +13,6 @@ struct foreachParams { const uint16_t *s; IDWriteTextLayout *layout; std::map *effects; - std::map *features; std::vector *backgroundFuncs; }; @@ -27,6 +26,7 @@ static void ensureEffectsInRange(struct foreachParams *p, size_t start, size_t e for (i = start; i < end; i++) { // don't create redundant entries; see below + // TODO explain this more clearly (surrogate pairs) if (!isCodepointStart(p->s[i])) continue; t = (*(p->effects))[i]; @@ -40,24 +40,6 @@ static void ensureEffectsInRange(struct foreachParams *p, size_t start, size_t e } } -static void setFeaturesInRange(struct foreachParams *p, size_t start, size_t end, const uiOpenTypeFeatures *otf) -{ - IDWriteTypography *dt; - size_t i; - - dt = otfToDirectWrite(otf); - for (i = start; i < end; i++) { - // don't create redundant entries; see below - // TODO explain this more clearly (surrogate pairs) - if (!isCodepointStart(p->s[i])) - continue; - dt->AddRef(); - (*(p->features))[i] = dt; - } - // and release the initial reference - dt->Release(); -} - static backgroundFunc mkBackgroundFunc(size_t start, size_t end, double r, double g, double b, double a) { return [=](uiDrawContext *c, uiDrawTextLayout *layout, double x, double y) { @@ -79,6 +61,7 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t WCHAR *wfamily; size_t ostart, oend; BOOL hasUnderline; + IDWriteTypography *dt; HRESULT hr; ostart = start; @@ -196,7 +179,11 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t } break; case uiAttributeFeatures: - setFeaturesInRange(p, start, end, spec->Features); + dt = otfToDirectWrite(spec->Features); + hr = p->layout->SetTypography(dt, range); + if (hr != S_OK) + logHRESULT(L"error applying features attribute", hr); + dt->Release(); break; default: // TODO complain @@ -224,25 +211,6 @@ static void applyAndFreeEffectsAttributes(struct foreachParams *p) delete p->effects; } -static void applyAndFreeFeatureAttributes(struct foreachParams *p) -{ - DWRITE_TEXT_RANGE range; - HRESULT hr; - - for (auto iter = p->features->begin(); iter != p->features->end(); iter++) { - // make sure we cover an entire code point - range.startPosition = iter->first; - range.length = 1; - if (!isCodepointStart(p->s[iter->first])) - range.length = 2; - hr = p->layout->SetTypography(iter->second, range); - if (hr != S_OK) - logHRESULT(L"error applying typographic features attributes", hr); - iter->second->Release(); - } - delete p->features; -} - void attrstrToIDWriteTextLayoutAttrs(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout, std::vector **backgroundFuncs) { struct foreachParams fep; @@ -250,10 +218,8 @@ void attrstrToIDWriteTextLayoutAttrs(uiDrawTextLayoutParams *p, IDWriteTextLayou fep.s = attrstrUTF16(p->String); fep.layout = layout; fep.effects = new std::map; - fep.features = new std::map; fep.backgroundFuncs = new std::vector; uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); applyAndFreeEffectsAttributes(&fep); - applyAndFreeFeatureAttributes(&fep); *backgroundFuncs = fep.backgroundFuncs; } From 2118259769d4ea7a2fb5a38cee4a0c71feb3c6a2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 31 May 2017 20:33:40 -0400 Subject: [PATCH 0745/1329] Set up text effect stuff. --- windows/draw.hpp | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/windows/draw.hpp b/windows/draw.hpp index f3a8d387..8cfbbf72 100644 --- a/windows/draw.hpp +++ b/windows/draw.hpp @@ -1,5 +1,7 @@ // 5 may 2016 +// TODO resolve overlap between this and the other hpp files (some functions leaked into uipriv_windows.hpp) + // draw.cpp extern ID2D1Factory *d2dfactory; struct uiDrawContext { @@ -20,6 +22,7 @@ typedef std::function **backgroundFuncs); // drawtext.cpp +// TODO reconcile this with attrstr.cpp class textDrawingEffect : public IUnknown { ULONG refcount; public: @@ -76,6 +79,50 @@ public: return this->refcount; } + // TODO deduplicate this with common/attrlist.c + bool same(textDrawingEffect *b) + { + static auto boolsDiffer = [](bool a, bool b) -> bool { + if (a && b) + return false; + if (!a && !b) + return false; + return true; + }; + + if (boolsDiffer(this->hasColor, b->hasColor)) + return false; + if (this->hasColor) { + // TODO use a closest match? + if (this->r != b->r) + return false; + if (this->g != b->g) + return false; + if (this->b != b->b) + return false; + if (this->a != b->a) + return false; + } + if (boolsDiffer(this->hasUnderline, b->hasUnderline)) + return false; + if (this->hasUnderline) + if (this->u != b->u) + return false; + if (boolsDiffer(this->hasUnderlineColor, b->hasUnderlineColor)) + return false; + if (this->hasUnderlineColor) { + // TODO use a closest match? + if (this->ur != b->ur) + return false; + if (this->ug != b->ug) + return false; + if (this->ub != b->ub) + return false; + if (this->ua != b->ua) + return false; + } + return true; + } }; // TODO these should not be exported extern std::map dwriteItalics; From 933a8f592ac7ccaaacd4eccf960f8c46622837ab Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 31 May 2017 22:24:34 -0400 Subject: [PATCH 0746/1329] And cleaned up the Windows drawing effect code like we did yesterday. --- windows/attrstr.cpp | 57 ++++++++++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index 91475602..1552deb8 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -6,29 +6,24 @@ // we need to combine color and underline style into one unit for IDWriteLayout::SetDrawingEffect() // we also need to collect all the background blocks and add them all at once -// TODO(TODO does not seem to apply here?) this is the wrong approach; it causes Pango to end runs early, meaning attributes like the ligature attributes never get applied properly // TODO contextual alternates override ligatures? // TODO rename this struct to something that isn't exclusively foreach-ing? struct foreachParams { const uint16_t *s; + size_t len; IDWriteTextLayout *layout; std::map *effects; std::vector *backgroundFuncs; }; -#define isCodepointStart(w) ((((uint16_t) (w)) < 0xDC00) || (((uint16_t) (w)) >= 0xE000)) - static void ensureEffectsInRange(struct foreachParams *p, size_t start, size_t end, std::function f) { size_t i; size_t *key; textDrawingEffect *t; + // TODO explain why we make one for every character for (i = start; i < end; i++) { - // don't create redundant entries; see below - // TODO explain this more clearly (surrogate pairs) - if (!isCodepointStart(p->s[i])) - continue; t = (*(p->effects))[i]; if (t != NULL) { f(t); @@ -194,20 +189,49 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t static void applyAndFreeEffectsAttributes(struct foreachParams *p) { + size_t i, n; + textDrawingEffect *effect, *effectb; DWRITE_TEXT_RANGE range; - HRESULT hr; + auto apply = [](IDWriteTextLayout *layout, textDrawingEffect *effect, DWRITE_TEXT_RANGE range) { + HRESULT hr; - for (auto iter = p->effects->begin(); iter != p->effects->end(); iter++) { - // make sure we cover an entire code point - range.startPosition = iter->first; - range.length = 1; - if (!isCodepointStart(p->s[iter->first])) - range.length = 2; - hr = p->layout->SetDrawingEffect(iter->second, range); + if (effect == NULL) + return; + hr = layout->SetDrawingEffect(effect, range); if (hr != S_OK) logHRESULT(L"error applying drawing effects attributes", hr); - iter->second->Release(); + effect->Release(); + }; + + // go through, fililng in the effect attribute for successive ranges of identical textDrawingEffects + // we are best off treating series of identical effects as single ranges ourselves for parity across platforms, even if Windows does something similar itself + // this also avoids breaking apart surrogate pairs (though IIRC Windows doing the something similar itself might make this a non-issue here) + effect = NULL; + n = p->len; + range.startPosition = 0; + for (i = 0; i < n; i++) { + effectb = (*(p->effects))[i]; + // run of no effect? + if (effect == NULL && effectb == NULL) + continue; + // run of the same effect? + if (effect != NULL && effectb != NULL) + if (effect->same(effectb)) { + effectb->Release(); + continue; + } + + // the effect has changed; commit the old effect + range.length = i - range.startPosition; + apply(p->layout, effect, range); + + range.startPosition = i; + effect = effectb; } + // and apply the last effect + range.length = i - range.startPosition; + apply(p->layout, effect, range); + delete p->effects; } @@ -216,6 +240,7 @@ void attrstrToIDWriteTextLayoutAttrs(uiDrawTextLayoutParams *p, IDWriteTextLayou struct foreachParams fep; fep.s = attrstrUTF16(p->String); + fep.len = attrstrUTF16Len(p->String); fep.layout = layout; fep.effects = new std::map; fep.backgroundFuncs = new std::vector; From 8cacd0ba03c5f661051c53afe44a9fb6976a6210 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 31 May 2017 22:25:10 -0400 Subject: [PATCH 0747/1329] Quick fixup. --- windows/attrstr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index 1552deb8..71f753d4 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -192,7 +192,7 @@ static void applyAndFreeEffectsAttributes(struct foreachParams *p) size_t i, n; textDrawingEffect *effect, *effectb; DWRITE_TEXT_RANGE range; - auto apply = [](IDWriteTextLayout *layout, textDrawingEffect *effect, DWRITE_TEXT_RANGE range) { + static auto apply = [](IDWriteTextLayout *layout, textDrawingEffect *effect, DWRITE_TEXT_RANGE range) { HRESULT hr; if (effect == NULL) From 96d06121c8b8a8696dcced378df02c4eeb95cd74 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 1 Jun 2017 10:57:34 -0400 Subject: [PATCH 0748/1329] And finally used OpenType features directly on supported OS X versions. --- darwin/opentype.m | 50 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/darwin/opentype.m b/darwin/opentype.m index 00a76200..56bdc755 100644 --- a/darwin/opentype.m +++ b/darwin/opentype.m @@ -90,7 +90,7 @@ int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeature // TODO explain all this // TODO rename outerArray and innerDict (the names made sense when this was part of fontdescAppendFeatures(), but not here) // TODO make all this use enumerateKeysAndObjects (which requires duplicating code)? -static int otfArrayForEach(char a, char b, char c, char d, uint32_t value, void *data) +static int otfArrayForEachAAT(char a, char b, char c, char d, uint32_t value, void *data) { CFMutableArrayRef outerArray = (CFMutableArrayRef) data; @@ -125,14 +125,60 @@ static int otfArrayForEach(char a, char b, char c, char d, uint32_t value, void return 0; } +// TODO find out which fonts differ in AAT small caps and test them with this +static int otfArrayForEachOT(char a, char b, char c, char d, uint32_t value, void *data) +{ + CFMutableArrayRef outerArray = (CFMutableArrayRef) data; + CFDictionaryRef innerDict; + // TODO rename this to tagstr (and all the other variables likewise...) + CFStringRef strTag; + CFNumberRef numValue; + char tagcstr[5]; + const void *keys[2], *values[2]; + + tagcstr[0] = a; + tagcstr[1] = b; + tagcstr[2] = c; + tagcstr[3] = d; + tagcstr[4] = '\0'; + keys[0] = *FUTURE_kCTFontOpenTypeFeatureTag; + keys[1] = *FUTURE_kCTFontOpenTypeFeatureValue; + strTag = CFStringCreateWithCString(NULL, tagcstr, kCFStringEncodingUTF8); + if (strTag == NULL) { + // TODO + } + numValue = CFNumberCreate(NULL, kCFNumberSInt32Type, + (const SInt32 *) (&value)); + values[0] = strTag; + values[1] = numValue; + innerDict = CFDictionaryCreate(NULL, + keys, values, 2, + // TODO are these correct? + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (innerDict == NULL) { + // TODO + } + CFArrayAppendValue(outerArray, innerDict); + CFRelease(innerDict); + CFRelease(numValue); + CFRelease(strTag); + // TODO + return 0; +} + CFArrayRef otfToFeaturesArray(const uiOpenTypeFeatures *otf) { CFMutableArrayRef outerArray; + uiOpenTypeFeaturesForEachFunc f; outerArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); if (outerArray == NULL) { // TODO } - uiOpenTypeFeaturesForEach(otf, otfArrayForEach, outerArray); + f = otfArrayForEachAAT; + if (FUTURE_kCTFontOpenTypeFeatureTag != NULL && FUTURE_kCTFontOpenTypeFeatureValue != NULL) + f = otfArrayForEachOT; + uiOpenTypeFeaturesForEach(otf, f, outerArray); return outerArray; } From 18d67a016c6c5e50b1af9f14720ed307678e3db2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 2 Jun 2017 23:57:40 -0400 Subject: [PATCH 0749/1329] Started work on OS X 10.12 API stupids. --- darwin/sierra.h | 79 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 darwin/sierra.h diff --git a/darwin/sierra.h b/darwin/sierra.h new file mode 100644 index 00000000..1a76cb76 --- /dev/null +++ b/darwin/sierra.h @@ -0,0 +1,79 @@ +// 2 june 2017 + +// The OS X 10.12 SDK introduces a number of new names for +// existing constants to align the naming conventions of +// Objective-C and Swift (particularly in AppKit). +// +// Unfortunately, in a baffling move, instead of using the existing +// AvailabilityMacros.h method of marking things deprecated, they +// rewrote the relevant constants in ways that make +// source-compatible building much more annoying: +// +// - The replacement names are now the only names in the enum +// or define sets they used to be in. +// - The old names are provided as static const variables, which +// means any code that used the old names in a switch case now +// spit out a compiler warning in strict C99 mode (TODO and in C++ mode?). +// - The old names are marked with new deprecated-symbol +// macros that are *not* compatible with the AvailabilityMacros.h +// macros, meaning their deprecation warnings still come +// through. (It should be noted that AvailabilityMacros.h was still +// updated for 10.12 regardless, hence our #ifdef below.) +// +// As far as I can gather, these facts are not documented *at all*, so +// in the meantime, other open-source projects just use their own +// #defines to maintain source compatibility, either by making the +// new names available everywhere or the old ones un-deprecated. +// We choose the latter. +// TODO file a radar on the issue (after determining C++ compatibility) so this can be pinned down once and for all +// TODO after that, link my stackoverflow question here too +// TODO make sure this #ifdef does actually work on older systems + +#ifdef MAC_OS_X_VERSION_10_12 + +#define NSControlKeyMask NSEventModifierFlagControl +#define NSAlternateKeyMask NSEventModifierFlagOption +#define NSShiftKeyMask NSEventModifierFlagShift +#define NSCommandKeyMask NSEventModifierFlagCommand + +#define NSLeftMouseDown NSEventTypeLeftMouseDown +#define NSRightMouseDown NSEventTypeRightMouseDown +#define NSOtherMouseDown NSEventTypeOtherMouseDown +#define NSLeftMouseUp NSEventTypeLeftMouseUp +#define NSRightMouseUp NSEventTypeRightMouseUp +#define NSOtherMouseUp NSEventTypeOtherMouseUp +#define NSLeftMouseDragged NSEventTypeLeftMouseDragged +#define NSRightMouseDragged NSEventTypeRightMouseDragged +#define NSOtherMouseDragged NSEventTypeOtherMouseDragged +#define NSKeyDown NSEventTypeKeyDown +#define NSKeyUp NSEventTypeKeyUp +#define NSFlagsChanged NSEventTypeFlagsChanged +#define NSApplicationDefined NSEventTypeApplicationDefined +#define NSPeriodic NSEventTypePeriodic +#define NSMouseMoved NSEventTypeMouseMoved + +#define NSRegularControlSize NSControlSizeRegular +#define NSSmallControlSize NSControlSizeSmall + +#define NSAnyEventMask NSEventMaskAny +#define NSLeftMouseDraggedMask NSEventMaskLeftMouseDragged +#define NSLeftMouseUpMask NSEventMaskLeftMouseUp + +#define NSTickMarkAbove NSTickMarkPositionAbove + +#define NSLinearSlider NSSliderTypeLinear + +#define NSInformationalAlertStyle NSAlertStyleInformational +#define NSCriticalAlertStyle NSAlertStyleCritical + +#define NSBorderlessWindowMask NSWindowStyleMaskBorderless +#define NSTitledWindowMask NSWindowStyleMaskTitled +#define NSClosableWindowMask NSWindowStyleMaskClosable +#define NSMiniaturizableWindowMask NSWindowStyleMaskMiniaturizable +#define NSResizableWindowMask NSWindowStyleMaskResizable + +#endif + +// TODO /Users/pietro/src/github.com/andlabs/libui/darwin/stddialogs.m:83:15: warning: 'beginSheetModalForWindow:modalDelegate:didEndSelector:contextInfo:' is deprecated: first deprecated in macOS 10.10 - Use -beginSheetModalForWindow:completionHandler: instead [-Wdeprecated-declarations] + +// TODO https://developer.apple.com/library/content/releasenotes/Miscellaneous/RN-Foundation-OSX10.12/ From aa455be1aed1063f3fec9433757f0a3323b66b3c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 3 Jun 2017 00:33:40 -0400 Subject: [PATCH 0750/1329] Sigh, cmake... --- CMakeLists.txt | 6 +++++- darwin/uipriv_darwin.h | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f189e2b9..fbcb3358 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,11 @@ cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR) # - switch to 3.1.0 features # the docs say we need to set this up prior to project() -set(CMAKE_OSX_DEPLOYMENT_TARGET "10.8") +# the docs don't say this, but the CACHE part is important; see https://stackoverflow.com/questions/34208360/cmake-seems-to-ignore-cmake-osx-deployment-target +# TODO figure out what other variables must be set with CACHE +# TODO figure out if FORCE is needed here +# TODO figure out whether STRING "" is best or if something else is better; also what FORCE does because I forget and later I say it's needed +set(CMAKE_OSX_DEPLOYMENT_TARGET "10.8" CACHE STRING "" FORCE) # we want to disable incremental linking # see also: diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 56a576d4..26366520 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -1,4 +1,5 @@ // 6 january 2015 +// note: as of OS X Sierra, the -mmacosx-version-min compiler options governs deprecation warnings; keep these around anyway just in case #define MAC_OS_X_VERSION_MIN_REQUIRED MAC_OS_X_VERSION_10_8 #define MAC_OS_X_VERSION_MAX_ALLOWED MAC_OS_X_VERSION_10_8 #import From 8c850a26b2d353f631e36885414f31fb3032353a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 3 Jun 2017 17:53:10 -0400 Subject: [PATCH 0751/1329] More TODOs. --- TODO.md | 3 +++ common/areaevents.c | 2 ++ 2 files changed, 5 insertions(+) diff --git a/TODO.md b/TODO.md index 8bf48004..41184b93 100644 --- a/TODO.md +++ b/TODO.md @@ -170,3 +170,6 @@ FONT LOADING [01:15:53] FcConfigAppFontAddFile() <-- that API [01:16:30] great, and they don't say what version the API was added in teh docs function: ide_editor_map_bin_add() + +- Mouse ClickLock: do we need to do anything special? *should* we? https://msdn.microsoft.com/en-us/library/windows/desktop/ms724947(v=vs.85).aspx +- consider a uiAnticipateDoubleClick() or uiDoubleClickTime() (for a uiQueueTimer()) or something: https://blogs.msdn.microsoft.com/oldnewthing/20041015-00/?p=37553 diff --git a/common/areaevents.c b/common/areaevents.c index cf3c288c..189673a2 100644 --- a/common/areaevents.c +++ b/common/areaevents.c @@ -10,6 +10,8 @@ For GTK+, we pull the double-click time and double-click distance, which work th On GTK+ this will also allow us to discard the GDK_BUTTON_2PRESS and GDK_BUTTON_3PRESS events, so the button press stream will be just like on other platforms. Thanks to mclasen, garnacho_, halfline, and tristan in irc.gimp.net/#gtk+. + +TODO note the bits about asymmetry and g_rcClick initial value not mattering in the oldnewthing article */ // x, y, xdist, ydist, and c.rect must have the same units From ba2e9154f79c39d1a7e19e32e82be0c57196d8d8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 3 Jun 2017 17:59:50 -0400 Subject: [PATCH 0752/1329] Some notes and failed bugfixes on NSProgressIndicators in tables. --- darwin/table.m | 1 + 1 file changed, 1 insertion(+) diff --git a/darwin/table.m b/darwin/table.m index 9e578c85..5e29de26 100644 --- a/darwin/table.m +++ b/darwin/table.m @@ -9,6 +9,7 @@ // - changing a part property does not refresh views // - is the Y position of checkbox cells correct? // - progressbars appear ABOVE the table header +// - threaded animation (which was known to have some issues: https://stackoverflow.com/questions/18142801/nstableview-nsprogressindicator-flickering-issues) is NOT the cause; happens regardless of setting (or does the setting not stick? TODO) // LONGTERM // - reuse row views instead of creating a new one each time From 8e8cc12105e2db6ff0291d28a0d2acde1c28ec08 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 6 Jun 2017 12:47:07 -0400 Subject: [PATCH 0753/1329] Added uiForEach for canonicalizing foreach function returns and used it everywhere. --- common/attrlist.c | 10 ++++++---- darwin/attrstr.m | 4 ++-- darwin/opentype.m | 17 +++++++++-------- ui.h | 6 ++++++ ui_attrstr.h | 5 ++--- unix/attrstr.c | 4 ++-- unix/opentype.c | 13 +++++++------ windows/attrstr.cpp | 4 ++-- windows/opentype.cpp | 6 ++++-- 9 files changed, 40 insertions(+), 29 deletions(-) diff --git a/common/attrlist.c b/common/attrlist.c index e50ec141..cb0833bc 100644 --- a/common/attrlist.c +++ b/common/attrlist.c @@ -651,12 +651,14 @@ void attrlistRemoveCharacters(struct attrlist *alist, size_t start, size_t end) void attrlistForEach(struct attrlist *alist, uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data) { struct attr *a; + uiForEach ret; - for (a = alist->first; a != NULL; a = a->next) - // TODO document this - // TODO should this be return 0 to break? - if ((*f)(s, &(a->spec), a->start, a->end, data)) + for (a = alist->first; a != NULL; a = a->next) { + ret = (*f)(s, &(a->spec), a->start, a->end, data); + if (ret == uiForEachStop) + // TODO for all: break or return? break; + } } struct attrlist *attrlistNew(void) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 6cf412a4..63539702 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -209,7 +209,7 @@ static CGColorRef mkcolor(uiAttributeSpec *spec) return color; } -static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) +static uiForEach processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) { struct foreachParams *p = (struct foreachParams *) data; CFRange range; @@ -316,7 +316,7 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t // TODO complain ; } - return 0; + return uiForEachContinue; } static BOOL cfaIsEqual(combinedFontAttr *a, combinedFontAttr *b) diff --git a/darwin/opentype.m b/darwin/opentype.m index 56bdc755..24f35f63 100644 --- a/darwin/opentype.m +++ b/darwin/opentype.m @@ -70,15 +70,18 @@ void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeatures NSNumber *vn = (NSNumber *) value; uint32_t tag; uint8_t a, b, c, d; + uiForEach ret; tag = mapObjectValue(tn); a = (uint8_t) ((tag >> 24) & 0xFF); b = (uint8_t) ((tag >> 16) & 0xFF); c = (uint8_t) ((tag >> 8) & 0xFF); d = (uint8_t) (tag & 0xFF); - // TODO handle return value - (*f)((char) a, (char) b, (char) c, (char) d, + ret = (*f)((char) a, (char) b, (char) c, (char) d, mapObjectValue(vn), data); + // TODO for all: require exact match? + if (ret == uiForEachStop) + *stop = YES; }]; } @@ -90,7 +93,7 @@ int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeature // TODO explain all this // TODO rename outerArray and innerDict (the names made sense when this was part of fontdescAppendFeatures(), but not here) // TODO make all this use enumerateKeysAndObjects (which requires duplicating code)? -static int otfArrayForEachAAT(char a, char b, char c, char d, uint32_t value, void *data) +static uiForEach otfArrayForEachAAT(char a, char b, char c, char d, uint32_t value, void *data) { CFMutableArrayRef outerArray = (CFMutableArrayRef) data; @@ -121,12 +124,11 @@ static int otfArrayForEachAAT(char a, char b, char c, char d, uint32_t value, vo CFRelease(numSelector); CFRelease(numType); }); - // TODO - return 0; + return uiForEachContinue; } // TODO find out which fonts differ in AAT small caps and test them with this -static int otfArrayForEachOT(char a, char b, char c, char d, uint32_t value, void *data) +static uiForEach otfArrayForEachOT(char a, char b, char c, char d, uint32_t value, void *data) { CFMutableArrayRef outerArray = (CFMutableArrayRef) data; CFDictionaryRef innerDict; @@ -163,8 +165,7 @@ static int otfArrayForEachOT(char a, char b, char c, char d, uint32_t value, voi CFRelease(innerDict); CFRelease(numValue); CFRelease(strTag); - // TODO - return 0; + return uiForEachContinue; } CFArrayRef otfToFeaturesArray(const uiOpenTypeFeatures *otf) diff --git a/ui.h b/ui.h index 922d0625..72e5eaef 100644 --- a/ui.h +++ b/ui.h @@ -34,6 +34,12 @@ extern "C" { // TODO uiBool? +// uiForEach represents the return value from one of libui's various ForEach functions. +_UI_ENUM(uiForEach) { + uiForEachContinue, + uiForEachStop, +}; + typedef struct uiInitOptions uiInitOptions; struct uiInitOptions { diff --git a/ui_attrstr.h b/ui_attrstr.h index 2f41cebb..9dc72a5a 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -55,7 +55,7 @@ _UI_ENUM(uiDrawUnderlineColor) { // TODO rename? typedef struct uiOpenTypeFeatures uiOpenTypeFeatures; // TODO pass the feature set? -typedef int (*uiOpenTypeFeaturesForEachFunc)(char a, char b, char c, char d, uint32_t value, void *data); +typedef uiForEach (*uiOpenTypeFeaturesForEachFunc)(char a, char b, char c, char d, uint32_t value, void *data); // TODO detailed constructor? _UI_EXTERN uiOpenTypeFeatures *uiNewOpenTypeFeatures(void); _UI_EXTERN void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf); @@ -83,9 +83,8 @@ struct uiAttributeSpec { const uiOpenTypeFeatures *Features; // TODO rename to OpenTypeFeatures? }; -// TODO name the foreach return values // TODO make the spec const in a way that doesn't allow fields to be modified? -typedef int (*uiAttributedStringForEachAttributeFunc)(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data); +typedef uiForEach (*uiAttributedStringForEachAttributeFunc)(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data); // @role uiAttributedString constructor // uiNewAttributedString() creates a new uiAttributedString from diff --git a/unix/attrstr.c b/unix/attrstr.c index 7f12fff8..7dc83a6e 100644 --- a/unix/attrstr.c +++ b/unix/attrstr.c @@ -65,7 +65,7 @@ static void addattr(struct foreachParams *p, size_t start, size_t end, PangoAttr pango_attr_list_insert(p->attrs, attr); } -static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) +static uiForEach processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) { struct foreachParams *p = (struct foreachParams *) data; GClosure *closure; @@ -164,7 +164,7 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t // TODO complain ; } - return 0; + return uiForEachContinue; } static void unrefClosure(gpointer data) diff --git a/unix/opentype.c b/unix/opentype.c index 608a0815..9b90d0ba 100644 --- a/unix/opentype.c +++ b/unix/opentype.c @@ -78,7 +78,7 @@ int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char struct otfForEach { uiOpenTypeFeaturesForEachFunc f; void *data; - // TODO store continuation status here + uiForEach ret; }; static void foreach(gpointer key, gpointer value, gpointer data) @@ -87,13 +87,15 @@ static void foreach(gpointer key, gpointer value, gpointer data) uint32_t tag; uint8_t a, b, c, d; + // we can't stop early, so just ignore the rest if we have to + if (ofe->ret == uiForEachStop) + return; tag = GPOINTER_TO_INT(key); a = (uint8_t) ((tag >> 24) & 0xFF); b = (uint8_t) ((tag >> 16) & 0xFF); c = (uint8_t) ((tag >> 8) & 0xFF); d = (uint8_t) (tag & 0xFF); - // TODO handle return value - (*(ofe->f))((char) a, (char) b, (char) c, (char) d, GPOINTER_TO_INT(value), ofe->data); + ofe->ret = (*(ofe->f))((char) a, (char) b, (char) c, (char) d, GPOINTER_TO_INT(value), ofe->data); } void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data) @@ -175,7 +177,7 @@ out: // see https://developer.mozilla.org/en/docs/Web/CSS/font-feature-settings // TODO make this a g_hash_table_foreach() function (which requires duplicating code)? -static int toCSS(char a, char b, char c, char d, uint32_t value, void *data) +static uiForEach toCSS(char a, char b, char c, char d, uint32_t value, void *data) { // TODO is there a G_STRING()? GString *s = (GString *) data; @@ -183,8 +185,7 @@ static int toCSS(char a, char b, char c, char d, uint32_t value, void *data) // the last trailing comma is removed after foreach is done g_string_append_printf(s, "\"%c%c%c%c\" %" PRIu32 ", ", a, b, c, d, value); - // TODO use this - return 0; + return uiForEachContinue; } gchar *otfToPangoCSSString(const uiOpenTypeFeatures *otf) diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index 71f753d4..7bcd5e53 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -49,7 +49,7 @@ static backgroundFunc mkBackgroundFunc(size_t start, size_t end, double r, doubl }; } -static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) +static uiForEach processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) { struct foreachParams *p = (struct foreachParams *) data; DWRITE_TEXT_RANGE range; @@ -184,7 +184,7 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t // TODO complain ; } - return 0; + return uiForEachContinue; } static void applyAndFreeEffectsAttributes(struct foreachParams *p) diff --git a/windows/opentype.cpp b/windows/opentype.cpp index 0275d916..21833e2c 100644 --- a/windows/opentype.cpp +++ b/windows/opentype.cpp @@ -64,14 +64,16 @@ void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeatures end = otf->tags->end(); for (iter = otf->tags->begin(); iter != end; iter++) { uint8_t a, b, c, d; + uiForEach ret; a = (uint8_t) (iter->first & 0xFF); b = (uint8_t) ((iter->first >> 8) & 0xFF); c = (uint8_t) ((iter->first >> 16) & 0xFF); d = (uint8_t) ((iter->first >> 24) & 0xFF); - // TODO handle return value - (*f)((char) a, (char) b, (char) c, (char) d, + ret = (*f)((char) a, (char) b, (char) c, (char) d, iter->second, data); + if (ret == uiForEachStop) + return; } } From a415a846f76490e902d1382e7f3cd5fe73242284 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 6 Jun 2017 13:46:54 -0400 Subject: [PATCH 0754/1329] Some comment fixups. --- ui_attrstr.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index 9dc72a5a..096267ea 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -2,8 +2,9 @@ // optionally be embellished with formatting attributes. libui // provides the list of formatting attributes, which cover common // formatting traits like boldface and color as well as advanced -// typographical features like superscripts and small caps. -// These attributes can be combined in a variety of ways. +// typographical features provided by OpenType like superscripts +// and small caps. These attributes can be combined in a variety of +// ways. // // In addition, uiAttributedString provides facilities for moving // between grapheme clusters, which represent a character @@ -18,7 +19,6 @@ // layout-specific properties. typedef struct uiAttributedString uiAttributedString; -// Note: where you say "1 = on", any nonzero value means "on". (TODO) // TODO just make a separate field for everything? _UI_ENUM(uiAttribute) { uiAttributeFamily, // use Family From b3e3b27f717901550c61c32ad35cb85651efcd39 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 6 Jun 2017 14:14:33 -0400 Subject: [PATCH 0755/1329] Some more TODO cleanup and name adjustment. --- darwin/attrstr.m | 6 +++--- examples/drawtext/attributes.c | 2 +- examples/drawtext/basic.c | 2 +- examples/drawtext/emptystr_hittest.c | 4 ++-- examples/drawtext/hittest.c | 4 ++-- ui_attrstr.h | 24 +++++++++++------------- unix/drawtext.c | 6 +++--- windows/drawtext.cpp | 8 ++++---- 8 files changed, 27 insertions(+), 29 deletions(-) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 63539702..c3b8b0b3 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -386,9 +386,9 @@ static void applyAndFreeFontAttributes(struct foreachParams *p) } static const CTTextAlignment ctaligns[] = { - [uiDrawTextLayoutAlignLeft] = kCTTextAlignmentLeft, - [uiDrawTextLayoutAlignCenter] = kCTTextAlignmentCenter, - [uiDrawTextLayoutAlignRight] = kCTTextAlignmentRight, + [uiDrawTextAlignLeft] = kCTTextAlignmentLeft, + [uiDrawTextAlignCenter] = kCTTextAlignmentCenter, + [uiDrawTextAlignRight] = kCTTextAlignmentRight, }; static CTParagraphStyleRef mkParagraphStyle(uiDrawTextLayoutParams *p) diff --git a/examples/drawtext/attributes.c b/examples/drawtext/attributes.c index f15ba070..ed3cb973 100644 --- a/examples/drawtext/attributes.c +++ b/examples/drawtext/attributes.c @@ -906,7 +906,7 @@ struct example *mkAttributesExample(void) setupAttributedString(); params.String = attrstr; params.DefaultFont = &defaultFont; - params.Align = uiDrawTextLayoutAlignLeft; + params.Align = uiDrawTextAlignLeft; return &attributesExample; } diff --git a/examples/drawtext/basic.c b/examples/drawtext/basic.c index 1ddbbe6c..ede973b3 100644 --- a/examples/drawtext/basic.c +++ b/examples/drawtext/basic.c @@ -259,7 +259,7 @@ struct example *mkBasicExample(void) attrstr = uiNewAttributedString(text); params.String = attrstr; params.DefaultFont = &defaultFont; - params.Align = uiDrawTextLayoutAlignLeft; + params.Align = uiDrawTextAlignLeft; return &basicExample; } diff --git a/examples/drawtext/emptystr_hittest.c b/examples/drawtext/emptystr_hittest.c index 5542e469..fba386d8 100644 --- a/examples/drawtext/emptystr_hittest.c +++ b/examples/drawtext/emptystr_hittest.c @@ -201,7 +201,7 @@ static void changeFont(uiFontButton *b, void *data) static void changeTextAlign(uiCombobox *c, void *data) { // note the order of the items added below - params.Align = (uiDrawTextLayoutAlign) uiComboboxSelected(textAlign); + params.Align = (uiDrawTextAlign) uiComboboxSelected(textAlign); redraw(); } @@ -248,7 +248,7 @@ struct example *mkEmptyStringExample(void) attrstr = uiNewAttributedString(text); params.String = attrstr; params.DefaultFont = &defaultFont; - params.Align = uiDrawTextLayoutAlignLeft; + params.Align = uiDrawTextAlignLeft; return &hitTestExample; } diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index f5252388..ce0685d1 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -210,7 +210,7 @@ static void changeFont(uiFontButton *b, void *data) static void changeTextAlign(uiCombobox *c, void *data) { // note the order of the items added below - params.Align = (uiDrawTextLayoutAlign) uiComboboxSelected(textAlign); + params.Align = (uiDrawTextAlign) uiComboboxSelected(textAlign); redraw(); } @@ -257,7 +257,7 @@ struct example *mkHitTestExample(void) attrstr = uiNewAttributedString(text); params.String = attrstr; params.DefaultFont = &defaultFont; - params.Align = uiDrawTextLayoutAlignLeft; + params.Align = uiDrawTextAlignLeft; return &hitTestExample; } diff --git a/ui_attrstr.h b/ui_attrstr.h index 096267ea..fef4f26f 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -34,7 +34,8 @@ _UI_ENUM(uiAttribute) { uiAttributeUnderlineColor, // enum uiDrawUnderlineColor // TODO note these are copied - // TODO figure out how we're going to deal with being able to edit features over time... + // TODO add a function to uiAttributedString to get an attribute's value at a specific index or in a specific range, so we can edit + // (TODO related to above: what about memory?) uiAttributeFeatures, // use Features }; @@ -52,11 +53,9 @@ _UI_ENUM(uiDrawUnderlineColor) { uiDrawUnderlineColorAuxiliary, // for instance, the color used by smart replacements on OS X }; -// TODO rename? typedef struct uiOpenTypeFeatures uiOpenTypeFeatures; // TODO pass the feature set? typedef uiForEach (*uiOpenTypeFeaturesForEachFunc)(char a, char b, char c, char d, uint32_t value, void *data); -// TODO detailed constructor? _UI_EXTERN uiOpenTypeFeatures *uiNewOpenTypeFeatures(void); _UI_EXTERN void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf); // TODO put above Free? @@ -80,10 +79,10 @@ struct uiAttributeSpec { double G; double B; double A; - const uiOpenTypeFeatures *Features; // TODO rename to OpenTypeFeatures? + const uiOpenTypeFeatures *Features; }; -// TODO make the spec const in a way that doesn't allow fields to be modified? +// TODO how would we make spec const in this case, to prevent fields from being modified? typedef uiForEach (*uiAttributedStringForEachAttributeFunc)(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data); // @role uiAttributedString constructor @@ -139,7 +138,6 @@ _UI_ENUM(uiDrawTextItalic) { uiDrawTextItalicItalic, }; -// TODO realign this so that Normal == 0? _UI_ENUM(uiDrawTextStretch) { uiDrawTextStretchUltraCondensed, uiDrawTextStretchExtraCondensed, @@ -164,18 +162,17 @@ typedef struct uiDrawTextLayout uiDrawTextLayout; typedef struct uiDrawTextLayoutParams uiDrawTextLayoutParams; typedef struct uiDrawTextLayoutLineMetrics uiDrawTextLayoutLineMetrics; -// TODO drop the Layout from this? -_UI_ENUM(uiDrawTextLayoutAlign) { - uiDrawTextLayoutAlignLeft, - uiDrawTextLayoutAlignCenter, - uiDrawTextLayoutAlignRight, +_UI_ENUM(uiDrawTextAlign) { + uiDrawTextAlignLeft, + uiDrawTextAlignCenter, + uiDrawTextAlignRight, }; struct uiDrawTextLayoutParams { uiAttributedString *String; uiDrawFontDescriptor *DefaultFont; double Width; - uiDrawTextLayoutAlign Align; + uiDrawTextAlign Align; }; // Height will equal ParagraphSpacingBefore + LineHeightSpace + Ascent + Descent + Leading + LineSpacing + ParagraphSpacing. @@ -253,10 +250,11 @@ _UI_EXTERN double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_ _UI_EXTERN void uiDrawCaret(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t pos, int *line); // TODO allow blinking +// TODO allow secondary carets typedef struct uiFontButton uiFontButton; #define uiFontButton(this) ((uiFontButton *) (this)) -// TODO have a function that sets an entire font descriptor to a range in a uiAttributedString at once? +// TODO have a function that sets an entire font descriptor to a range in a uiAttributedString at once, for SetFont? _UI_EXTERN void uiFontButtonFont(uiFontButton *b, uiDrawFontDescriptor *desc); // TOOD SetFont, mechanics _UI_EXTERN void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data); diff --git a/unix/drawtext.c b/unix/drawtext.c index 6acb848a..373702cd 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -95,9 +95,9 @@ static void computeLineMetrics(uiDrawTextLayout *tl) } static const PangoAlignment pangoAligns[] = { - [uiDrawTextLayoutAlignLeft] = PANGO_ALIGN_LEFT, - [uiDrawTextLayoutAlignCenter] = PANGO_ALIGN_CENTER, - [uiDrawTextLayoutAlignRight] = PANGO_ALIGN_RIGHT, + [uiDrawTextAlignLeft] = PANGO_ALIGN_LEFT, + [uiDrawTextAlignCenter] = PANGO_ALIGN_CENTER, + [uiDrawTextAlignRight] = PANGO_ALIGN_RIGHT, }; uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 275420da..f173f471 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -134,10 +134,10 @@ static void computeLineInfo(uiDrawTextLayout *tl) } // TODO should be const but then I can't operator[] on it; the real solution is to find a way to do designated array initializers in C++11 but I do not know enough C++ voodoo to make it work (it is possible but no one else has actually done it before) -static std::map dwriteAligns = { - { uiDrawTextLayoutAlignLeft, DWRITE_TEXT_ALIGNMENT_LEADING }, - { uiDrawTextLayoutAlignCenter, DWRITE_TEXT_ALIGNMENT_CENTER }, - { uiDrawTextLayoutAlignRight, DWRITE_TEXT_ALIGNMENT_TRAILING }, +static std::map dwriteAligns = { + { uiDrawTextAlignLeft, DWRITE_TEXT_ALIGNMENT_LEADING }, + { uiDrawTextAlignCenter, DWRITE_TEXT_ALIGNMENT_CENTER }, + { uiDrawTextAlignRight, DWRITE_TEXT_ALIGNMENT_TRAILING }, }; uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) From ddf91df7644d01833a1f9d041cdd2cf23f6cbed0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 6 Jun 2017 15:32:51 -0400 Subject: [PATCH 0756/1329] More cmake TODOs. --- darwin/CMakeLists.txt | 1 + unix/CMakeLists.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/darwin/CMakeLists.txt b/darwin/CMakeLists.txt index 0591643b..ef1d30b1 100644 --- a/darwin/CMakeLists.txt +++ b/darwin/CMakeLists.txt @@ -59,6 +59,7 @@ if(NOT BUILD_SHARED_LIBS) set(_LIBUINAME libui-temporary PARENT_SCOPE) endif() # thanks to Mr-Hide in irc.freenode.net/#cmake +# TODO remove all these temporary files after linking the final archive file macro(_handle_static) set_target_properties(${_LIBUINAME} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}") diff --git a/unix/CMakeLists.txt b/unix/CMakeLists.txt index f21256c3..c20cf266 100644 --- a/unix/CMakeLists.txt +++ b/unix/CMakeLists.txt @@ -57,6 +57,7 @@ set(_LIBUINAME libui PARENT_SCOPE) if(NOT BUILD_SHARED_LIBS) set(_LIBUINAME libui-temporary PARENT_SCOPE) endif() +# TODO remove all these temporary files after linking the final archive file macro(_handle_static) set_target_properties(${_LIBUINAME} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}") From ef3ed04e2d9bdbe821e51aaa27fd9804e218e46e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 6 Jun 2017 16:19:08 -0400 Subject: [PATCH 0757/1329] More hacking to fix visibility issues on GTK+. This is a mess. --- ui_unix.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ui_unix.h b/ui_unix.h index 5a91257b..23269186 100644 --- a/ui_unix.h +++ b/ui_unix.h @@ -16,6 +16,7 @@ struct uiUnixControl { uiControl c; uiControl *parent; gboolean addedBefore; + gboolean explicitlyHidden; void (*SetContainer)(uiUnixControl *, GtkContainer *, gboolean); }; #define uiUnixControl(this) ((uiUnixControl *) (this)) @@ -58,11 +59,13 @@ _UI_EXTERN void uiUnixControlSetContainer(uiUnixControl *, GtkContainer *, gbool #define uiUnixControlDefaultShow(type) \ static void type ## Show(uiControl *c) \ { \ + /*TODO part of massive hack about hidden before*/uiUnixControl(c)->explicitlyHidden=FALSE; \ gtk_widget_show(type(c)->widget); \ } #define uiUnixControlDefaultHide(type) \ static void type ## Hide(uiControl *c) \ { \ + /*TODO part of massive hack about hidden before*/uiUnixControl(c)->explicitlyHidden=TRUE; \ gtk_widget_hide(type(c)->widget); \ } #define uiUnixControlDefaultEnabled(type) \ @@ -86,7 +89,7 @@ _UI_EXTERN void uiUnixControlSetContainer(uiUnixControl *, GtkContainer *, gbool { \ if (!uiUnixControl(c)->addedBefore) { \ g_object_ref_sink(type(c)->widget); /* our own reference, which we release in Destroy() */ \ - gtk_widget_show(type(c)->widget); \ + /*TODO*/if(!uiUnixControl(c)->explicitlyHidden) gtk_widget_show(type(c)->widget); \ uiUnixControl(c)->addedBefore = TRUE; \ } \ if (remove) \ From dbb17e441db3a8a94b669446764f65527b1c095c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 6 Jun 2017 16:20:58 -0400 Subject: [PATCH 0758/1329] Comments related to above. --- examples/drawtext/hittest.c | 1 - ui_unix.h | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/drawtext/hittest.c b/examples/drawtext/hittest.c index ce0685d1..a8092b47 100644 --- a/examples/drawtext/hittest.c +++ b/examples/drawtext/hittest.c @@ -2,7 +2,6 @@ #include "drawtext.h" // TODO double-check ligatures on all platforms to make sure we can place the cursor at the right place -// TODO the hiding and showing does not work properly on GTK+ // TODO using the arrow keys allows us to walk back to the end of the line on some platforms (TODO which?); IIRC arrow keys shouldn't do that // TODO make sure to check the cursor positions of RTL on all platforms diff --git a/ui_unix.h b/ui_unix.h index 23269186..ed019260 100644 --- a/ui_unix.h +++ b/ui_unix.h @@ -89,6 +89,7 @@ _UI_EXTERN void uiUnixControlSetContainer(uiUnixControl *, GtkContainer *, gbool { \ if (!uiUnixControl(c)->addedBefore) { \ g_object_ref_sink(type(c)->widget); /* our own reference, which we release in Destroy() */ \ + /* massive hack notes: without any of this, nothing gets shown when we show a window; without the if, all things get shown even if some were explicitly hidden (TODO why don't we just show everything except windows on create? */ \ /*TODO*/if(!uiUnixControl(c)->explicitlyHidden) gtk_widget_show(type(c)->widget); \ uiUnixControl(c)->addedBefore = TRUE; \ } \ From 39a8d1a07ef0a932bc9f885f4c2e48c141b464fd Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 7 Jun 2017 15:58:11 -0400 Subject: [PATCH 0759/1329] More TODOs. --- windows/uipriv_windows.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index f5adacdd..3f199cbc 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -7,6 +7,7 @@ #include "compilerver.hpp" // ui internal window messages +// TODO make these either not messages or WM_USER-based, so we can be sane about reserving WM_APP enum { // redirected WM_COMMAND and WM_NOTIFY msgCOMMAND = WM_APP + 0x40, // start offset just to be safe From 20239df6f284b56f231d76af252b0131741ef14e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 8 Jun 2017 00:59:17 -0400 Subject: [PATCH 0760/1329] Some TODO resolution via documentation writing. --- ui_attrstr.h | 52 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 11 deletions(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index fef4f26f..9a5e1bfe 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -19,23 +19,50 @@ // layout-specific properties. typedef struct uiAttributedString uiAttributedString; -// TODO just make a separate field for everything? +// TODO just make a separate field in uiAttributeSpec for everything? or make attribute objects opaque instead? _UI_ENUM(uiAttribute) { - uiAttributeFamily, // use Family - uiAttributeSize, // use Double + // uiAttributeFamily changes the font family of the text it is + // applied to. Use the Family field of uiAttributeSpec. + // TODO case-sensitive? + uiAttributeFamily, + // uiAttributeSize changes the size of the text it is applied to, + // in typographical points. Use the Double field of + // uiAttributeSpec. + uiAttributeSize, + // uiAttributeWeight changes the weight of the text it is applied + // to. Use the Value field of uiAttributeSpec and the + // uiDrawTextWeight constants. uiAttributeWeight, + // uiAttributeItalic changes the italicness of the text it is applied + // to. Use the Value field of uiAttributeSpec and the + // uiDrawTextItalic constants. uiAttributeItalic, + // uiAttributeStretch changes the stretch of the text it is applied + // to. Use the Value field of uiAttributeSpec and the + // uiDrawTextStretch constants. uiAttributeStretch, - uiAttributeColor, // use R, G, B, A - uiAttributeBackground, // use R, G, B, A + // uiAttributeColor changes the color of the text it is applied to. + // Use the R, G, B, and A fields of uiAttributeSpec. + uiAttributeColor, + // uiAttributeBackground changes the color of the text it is + // applied to. Use the R, G, B, and A fields of uiAttributeSpec. + uiAttributeBackground, - uiAttributeUnderline, // enum uiDrawUnderlineStyle - // TODO document that the color in the case we don't specify it is the text color - uiAttributeUnderlineColor, // enum uiDrawUnderlineColor + // uiAttributeUnderline changes the underline style of the text + // it is applied to. Use the Value field of uiAttributeSpec and the + // uiDrawUnderlineStyle constants. + uiAttributeUnderline, + // uiAttributeUnderlineColor changes the color of any underline + // on the text it is applied to, regardless of the style. Use the + // Value field of uiAttributeSpec and the uiDrawUnderlineColor + // constants (refer to its documentation for more information). + // + // If an underline style is applied but no underline color is + // specified, the text color is used instead. + uiAttributeUnderlineColor, - // TODO note these are copied - // TODO add a function to uiAttributedString to get an attribute's value at a specific index or in a specific range, so we can edit - // (TODO related to above: what about memory?) + // uiAttributeFeatures changes the OpenType features of the + // text it is applied to. Use the Features field of uiAttributeSpec. uiAttributeFeatures, // use Features }; @@ -70,6 +97,9 @@ _UI_EXTERN int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpen typedef struct uiAttributeSpec uiAttributeSpec; +// TODO note that pointers are copied +// TODO add a function to uiAttributedString to get an attribute's value at a specific index or in a specific range, so we can edit +// (TODO related to above: what about memory consumption during a read-modify-write cycle due to copying?) struct uiAttributeSpec { uiAttribute Type; const char *Family; From cde1a201f4bc479d7df744d4948be59de7acfd97 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 8 Jun 2017 15:31:28 -0400 Subject: [PATCH 0761/1329] Expanded documentation in ui_attrstr.h in an attempt to reduce TODOs. Instead, I added more. :D --- ui_attrstr.h | 60 ++++++++++++++++++++++++++++++++++++++++++++++++---- unix/area.c | 3 +++ 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index 9a5e1bfe..e5147a02 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -19,6 +19,7 @@ // layout-specific properties. typedef struct uiAttributedString uiAttributedString; +// TODO either here or above, say that only one attribute can be applied per attribute type per character // TODO just make a separate field in uiAttributeSpec for everything? or make attribute objects opaque instead? _UI_ENUM(uiAttribute) { // uiAttributeFamily changes the font family of the text it is @@ -80,19 +81,70 @@ _UI_ENUM(uiDrawUnderlineColor) { uiDrawUnderlineColorAuxiliary, // for instance, the color used by smart replacements on OS X }; +// uiOpenTypeFeatures represents a set of OpenType feature +// tag-value pairs, for applying OpenType features to text. +// OpenType feature tags are four-character codes defined by +// OpenType that cover things from design features like small +// caps and swashes to language-specific glyph shapes and +// beyond. Each tag may only appear once in any given +// uiOpenTypeFeatures instance. Each value is a 32-bit integer, +// often used as a Boolean flag, but sometimes as an index to choose +// a glyph shape to use. +// +// The full list of OpenType features is part of the OpenType +// specification: +// https://www.microsoft.com/typography/otspec/featuretags.htm +// Refer to it for information on specific features and how to use +// them. +// TODO reformat this somehow (how do Go packages do things like this?) typedef struct uiOpenTypeFeatures uiOpenTypeFeatures; -// TODO pass the feature set? + +// TODO pass the feature set? (resolve const struct issue below first) typedef uiForEach (*uiOpenTypeFeaturesForEachFunc)(char a, char b, char c, char d, uint32_t value, void *data); + +// @role uiOpenTypeFeatures constructor +// uiNewOpenTypeFeatures() returns a new uiOpenTypeFeatures +// instance, with no tags yet added. _UI_EXTERN uiOpenTypeFeatures *uiNewOpenTypeFeatures(void); + +// @role uiOpenTypeFeatures destructor +// uiFreeOpenTypeFeatures() frees otf. _UI_EXTERN void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf); -// TODO put above Free? -// TODO Copy instead of Clone? + +// uiOpenTypeFeaturesClone() makes a copy of otf and returns it. +// Changing one will not affect the other. _UI_EXTERN uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures *otf); + +// uiOpenTypeFeaturesAdd() adds the given feature tag and value +// to otf. The feature tag is specified by a, b, c, and d. If there is +// already a value associated with the specified tag in otf, the old +// value is removed. _UI_EXTERN void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value); + +// uiOpenTypeFeaturesRemove() removes the given feature tag +// and value from otf. +// TODO what happens if the tag isn't there? _UI_EXTERN void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d); + +// uiOpenTypeFeaturesGet() determines whether the given feature +// tag is present in otf. If it is, *value is set to the tag's value and +// nonzero is returned. Otherwise, zero is returned. +// TODO zero-fill value unconditionally? and if so, to other functions in libui +// TODO allow NULL for value? and throughout libui? +// TODO const-correct this function (can we do that given the members of the struct on some platforms being full blown objects that may or may not themselves be const-correct?) _UI_EXTERN int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value); -// TODO make other enumerators const (and in general const-correct everything) + +// uiOpenTypeFeaturesForEach() executes f for every tag-value +// pair in otf. The enumeration order is unspecified. +// TODO make other enumerators const (and in general const-correct everything) (but see the const struct TODO below and the const struct object member TODO above) _UI_EXTERN void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data); + +// uiOpenTypeFeaturesEqual() returns nonzero if a is equal to b. +// a is defined as equal to b if and only if both +// - contain the same tags, without any extras or missing tags +// either way, and +// - have each tag have the same values +// TODO what if either or both are NULL? _UI_EXTERN int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeatures *b); typedef struct uiAttributeSpec uiAttributeSpec; diff --git a/unix/area.c b/unix/area.c index ca99e602..24cd9513 100644 --- a/unix/area.c +++ b/unix/area.c @@ -92,6 +92,9 @@ static void areaWidget_size_allocate(GtkWidget *w, GtkAllocation *allocation) if (!a->scrolling) // we must redraw everything on resize because Windows requires it + // TODO https://developer.gnome.org/gtk3/3.10/GtkWidget.html#gtk-widget-set-redraw-on-allocate ? + // TODO drop this rule; it was stupid and documenting this was stupid — let platforms where it matters do it on their own + // TODO or do we not, for parity of performance? gtk_widget_queue_resize(w); } From b73721905faf6d1d959a937f11905b9494194aab Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 9 Jun 2017 19:43:55 -0400 Subject: [PATCH 0762/1329] Meh. --- ui_attrstr.h | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index e5147a02..c7ab225c 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -91,12 +91,14 @@ _UI_ENUM(uiDrawUnderlineColor) { // often used as a Boolean flag, but sometimes as an index to choose // a glyph shape to use. // -// The full list of OpenType features is part of the OpenType -// specification: +// If a font does not support a certain feature, that feature will be +// ignored. +// +// See the OpenType specification at // https://www.microsoft.com/typography/otspec/featuretags.htm -// Refer to it for information on specific features and how to use -// them. -// TODO reformat this somehow (how do Go packages do things like this?) +// for the complete list of available features, information on specific +// features, and how to use them. +// TODO invalid features typedef struct uiOpenTypeFeatures uiOpenTypeFeatures; // TODO pass the feature set? (resolve const struct issue below first) From d63a5b23b1725bd53504f952eca1cd887b685773 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 9 Jun 2017 19:59:48 -0400 Subject: [PATCH 0763/1329] Handled uiOpenTypeFeatures NULL equality. This only added more TODOs elsewhere :| --- darwin/attrstr.m | 8 +------- darwin/opentype.m | 4 ++++ ui_attrstr.h | 7 ++----- unix/attrstr.c | 2 +- unix/opentype.c | 5 +++++ windows/attrstr.cpp | 1 + windows/opentype.cpp | 4 ++++ 7 files changed, 18 insertions(+), 13 deletions(-) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index c3b8b0b3..4d166fb0 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -113,13 +113,7 @@ struct foreachParams { return NO; if (self.stretch != b.stretch) return NO; - // TODO make this part of uiOpenTypeFeaturesEqual() on all platforms - if (self.features == NULL && b.features == NULL) - return YES; - if (self.features != NULL && b.features == NULL) - return NO; - if (self.features == NULL && b.features != NULL) - return NO; + // this also handles NULL cases if (!uiOpenTypeFeaturesEqual(self.features, b.features)) return NO; return YES; diff --git a/darwin/opentype.m b/darwin/opentype.m index 24f35f63..46bdaf19 100644 --- a/darwin/opentype.m +++ b/darwin/opentype.m @@ -87,6 +87,10 @@ void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeatures int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeatures *b) { + if (a == NULL && b == NULL) + return 1; + if (a == NULL || b == NULL) + return 0; return [a->tags isEqualToDictionary:b->tags]; } diff --git a/ui_attrstr.h b/ui_attrstr.h index c7ab225c..b39b39a2 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -142,11 +142,8 @@ _UI_EXTERN int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, ch _UI_EXTERN void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data); // uiOpenTypeFeaturesEqual() returns nonzero if a is equal to b. -// a is defined as equal to b if and only if both -// - contain the same tags, without any extras or missing tags -// either way, and -// - have each tag have the same values -// TODO what if either or both are NULL? +// a is defined as equal to b if and only if both have exactly the same +// tags with exactly the same values, or if both are NULL. _UI_EXTERN int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeatures *b); typedef struct uiAttributeSpec uiAttributeSpec; diff --git a/unix/attrstr.c b/unix/attrstr.c index 7dc83a6e..4d384daf 100644 --- a/unix/attrstr.c +++ b/unix/attrstr.c @@ -154,7 +154,7 @@ static uiForEach processAttribute(uiAttributedString *s, uiAttributeSpec *spec, } break; case uiAttributeFeatures: - // TODO handle NULLs properly on all platforms + // TODO make sure NULLs are handled properly on all platforms in this part of the code featurestr = otfToPangoCSSString(spec->Features); addattr(p, start, end, FUTURE_pango_attr_font_features_new(featurestr)); diff --git a/unix/opentype.c b/unix/opentype.c index 9b90d0ba..79b1d93a 100644 --- a/unix/opentype.c +++ b/unix/opentype.c @@ -133,6 +133,11 @@ int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeature guint i; int equal = 0; + if (a == NULL && b == NULL) + return 1; + if (a == NULL || b == NULL) + return 0; + ak = copySortedKeys(a->tags); bk = copySortedKeys(b->tags); diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index 7bcd5e53..9dceaea6 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -174,6 +174,7 @@ static uiForEach processAttribute(uiAttributedString *s, uiAttributeSpec *spec, } break; case uiAttributeFeatures: + // TODO make sure this behaves properly if spec->Features is NULL dt = otfToDirectWrite(spec->Features); hr = p->layout->SetTypography(dt, range); if (hr != S_OK) diff --git a/windows/opentype.cpp b/windows/opentype.cpp index 21833e2c..89b5913c 100644 --- a/windows/opentype.cpp +++ b/windows/opentype.cpp @@ -79,6 +79,10 @@ void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeatures int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeatures *b) { + if (a == NULL && b == NULL) + return 1; + if (a == NULL || b == NULL) + return 0; // TODO make sure this is correct return *(a->tags) == *(b->tags); } From f0813ac6e422758c0f778bacc772f328e918a961 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 10 Jun 2017 03:37:17 -0400 Subject: [PATCH 0764/1329] More stuff. I should probably write that OpenType features test now. --- darwin/attrstr.m | 2 ++ ui_attrstr.h | 2 +- unix/attrstr.c | 5 ++++- windows/attrstr.cpp | 4 +++- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 4d166fb0..6975ca06 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -372,6 +372,8 @@ static void applyAndFreeFontAttributes(struct foreachParams *p) font = defaultFont; CFRetain(font); } else + // note that this handles the difference between NULL and empty uiOpenTypeFeatures properly as far as conversion to native data formats is concerned (NULL does not generate a features dictionary; empty just produces an empty one) + // TODO but what about from the OS's perspective on all OSs? font = [cfa toCTFont]; CFAttributedStringSetAttribute(p->mas, range, kCTFontAttributeName, font); CFRelease(font); diff --git a/ui_attrstr.h b/ui_attrstr.h index b39b39a2..0024442c 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -92,7 +92,7 @@ _UI_ENUM(uiDrawUnderlineColor) { // a glyph shape to use. // // If a font does not support a certain feature, that feature will be -// ignored. +// ignored. (TODO verify this on all OSs) // // See the OpenType specification at // https://www.microsoft.com/typography/otspec/featuretags.htm diff --git a/unix/attrstr.c b/unix/attrstr.c index 4d384daf..245ae7f6 100644 --- a/unix/attrstr.c +++ b/unix/attrstr.c @@ -154,7 +154,10 @@ static uiForEach processAttribute(uiAttributedString *s, uiAttributeSpec *spec, } break; case uiAttributeFeatures: - // TODO make sure NULLs are handled properly on all platforms in this part of the code + // only generate an attribute if spec->Features is not NULL + // TODO state that this is allowed + if (spec->Features == NULL) + break; featurestr = otfToPangoCSSString(spec->Features); addattr(p, start, end, FUTURE_pango_attr_font_features_new(featurestr)); diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index 9dceaea6..f180f74b 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -174,7 +174,9 @@ static uiForEach processAttribute(uiAttributedString *s, uiAttributeSpec *spec, } break; case uiAttributeFeatures: - // TODO make sure this behaves properly if spec->Features is NULL + // only generate an attribute if spec->Features is not NULL + if (spec->Features == NULL) + break; dt = otfToDirectWrite(spec->Features); hr = p->layout->SetTypography(dt, range); if (hr != S_OK) From 99e65d49a70998eac8c9aaf58c7a614ebdfe93b8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 10 Jun 2017 16:56:17 -0400 Subject: [PATCH 0765/1329] Added some real contribution guidelines. --- CONTRIBUTING.md | 120 ++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 4 ++ 2 files changed, 124 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..3b8dcda9 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,120 @@ +# Contributing to libui + +libui is an open source project that openly accepts contributions. I appreciate your help! + +## Rules for contributing code + +While libui is open to contributions, a number of recent, significantly large contributions and uncontributed forks have recently surfaced that do not present themselves in a form that makes it easy for libui to accept them. In order to give your contribution a high chance of being accepted into libui, please keep the following in mind as you prepare your contribution. + +### Commit messages and pull request description + +libui does not enforce rules about the length or detail that a commit message. I'm not looking for an essay. However, single-word descriptions of nontrivial changes are *not* acceptable. I should be able to get a glimpse of what a commit does from the commit message, even if it's just one sentence to describe a trivial change. (Yes, I know I haven't followed this rule strictly myself, but I try not to break it too.) And a commit message should encompass everything; typically, I make a number of incremental commits toward a feature, so the commit messages don't have to be too long to explain everything. + +Your pull request description, on the other hand, must be a summary of the sum total of all the changes made to libui. Don't just drop a pull request on me with a one-line-long elevator pitch of what you added. Describe your proposed API changes, implementation requirements, and any important consequences of your work. + +### Code formatting + +libui uses K&R C formatting rules for overall code structure: spaces after keywords like `if`, `{` on the same line as a statement with a space, `{` on its own line after a function or method signature (even those inside the class body), no space after the name of a function, etc. + +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. + +When breaking expressions into multiple lines, always break *after* an operator, such as `,` or `&&`. + +In the event you are unsure of something, refer to existing libui code for examples. I may wind up fixing minor details later anyway, so don't fret about getting minor details right the first time. + +### Naming + +libui uses camel-case for naming, with a handful of very specific exceptions (namely GObject method names, where GObject itself enforces the naming convention). + +All public API names should begin with `ui` and followed by a capital letter. All public struct names should begin with a capital letter. This is identical to the visibiilty rules of Go, assuming a package name of `ui`. + +No private API name should begin with `ui` followed by a capital letter; in fact, I would ideally like to be able to get away with lax naming for private functions. I may have to change that if it becomes technically difficult to do in the case of static linking later on down the road. (`uiMalloc()`, `uiRealloc()`, and `uiFree()` are grandfathered-in exceptions.) + +Acronyms should **NOT** be mixed-case. `http` for the first word in a camel-case name, `HTTP` for all else, but **NEVER** `Http`. This is possibly the only aspect of the controversial nature of code style that I consider indefensibly stupid. + +### API documentation + +(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.) + +### 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. + +Choosing to drop older versions of Windows, GTK+, and OS X that I could have easily continued to support was not done lightly. If you want to discuss dropping support for an older version of any of these for the benefit of libui, file an issue pleading your case (see below). + +GTK+ versions are harder to drop because I am limited by Linux distribution packaging. In general, I will consider bumping GTK+ versions on a new Ubuntu LTS release, choosing the earliest version available on the major distributions at the time of the *previous* Ubuntu LTS release. As of writing, the next milestone will be *after* April 2018, and the target GTK+ version appears to be 3.18, judging by Ubuntu 16.04 LTS alone. This may be bumped back depending on other distros (or it may not be bumped at all), but you may wish to keep this in mind as you write. + +(TODO talk about future.c/.cpp/.m files) + +As for language compatibility, libui is written in C99. I have no intention of changing this. + +As for build system compatibility, libui uses CMake 3.1.0. If you wish to bump the version, file an issue pleading your case (but see below). + +**If you do plead your case**, keep in mind that "it's old" is not a sufficient reason to drop things. If you can prove that **virtually no one** uses the minimum version anymore, then that is stronger evidence. The best evidence, however, is that not upgrading will hold libui back in some significant way — but beware that there are some things I won't add to libui itself. + +### Windows-specific notes + +The Windows backend of libui is written in C++ using C++11. + +Despite using C++, please refrain from using the following: + +- using C++ in ui_windows.h (this file should still be C compatible) +- smart pointers +- namespaces +- `using namespace` +- ATL, MFC, WTL + +The following are not recommended, for consistency with the rest of libui: + +- variable declarations anywhere in a function (keep them all at the top) +- `for (int...` (C++11 foreach syntax is fine, though) +- omitting the `struct` on type names for ordinary structs + +The format of a class should be + +```c++ +class name : public ancestor { + // private variables here +public: + // public stuff here +}; +``` + +### GTK+-specific notes + +Avoid GNU-specific language features. I build with strict C99 conformance. + +### OS X-specific notes + +libui is presently **not** ARC-compliant. Features that require ARC should be avoided for now. I may consider changing this in the future, but it will be a significant change. + +To ensure maximum compiler output in the event of a coding error, there should not be any implicit method calls in Objective-C code. For instance, don't do + +```objective-c +[[array objectAtIndex:i] method] +``` + +Instead, cast the result of `objectAtIndex:` to the appropriate type, and then call the method. (TODO learn about, then decide a policy on, soft-generics on things other than `id`) + +The format of a class should be + +```objective-c +@interface name : parent { + // ivars +} +// properties +- (ret)method; +// more methods +@end + +@implementation name + +- (ret)method +{ + // note the lack of semicolon +} + +@end +``` diff --git a/README.md b/README.md index 01275c0e..dfa5b0b5 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,10 @@ 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). + * Also introduced a formal set of contribution guidelines, see `CONTRIBUTING.md` for details. + * **27 November 2016** * Decided to split the table stuff into its own branch. It will be developed independently of everything else, along with a few other features. From fcbf706559dfeda6253589cc526ba428a31421fe Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 10 Jun 2017 16:58:18 -0400 Subject: [PATCH 0766/1329] More README fixups. --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index dfa5b0b5..ca2c5ac0 100644 --- a/README.md +++ b/README.md @@ -178,6 +178,10 @@ When you run a binary directly from the Terminal, however, you are running it di See also [this](https://github.com/andlabs/libui/pull/20#issuecomment-211381971) and [this](http://stackoverflow.com/questions/25318524/what-exactly-should-i-pass-to-nsapp-activateignoringotherapps-to-get-my-appl). +## Contributing + +See `CONTRIBUTING.md`. + ## Screenshots From examples/controlgallery: From 0c7ca9a34691ebe7d3d184f618b5e159f505c9fc Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 10 Jun 2017 17:03:29 -0400 Subject: [PATCH 0767/1329] Quick contributtion fixups. --- CONTRIBUTING.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3b8dcda9..1a35c211 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -22,6 +22,8 @@ Expressions should have a spce around binary operators, and use parentheses wher When breaking expressions into multiple lines, always break *after* an operator, such as `,` or `&&`. +There should be a newline between a function's variables and a function's code. After that, you can place newlines to delimit different parts of a function, but don't go crazy. + In the event you are unsure of something, refer to existing libui code for examples. I may wind up fixing minor details later anyway, so don't fret about getting minor details right the first time. ### Naming @@ -40,7 +42,7 @@ Acronyms should **NOT** be mixed-case. `http` for the first word in a camel-case ### 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. +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. Choosing to drop older versions of Windows, GTK+, and OS X that I could have easily continued to support was not done lightly. If you want to discuss dropping support for an older version of any of these for the benefit of libui, file an issue pleading your case (see below). @@ -76,7 +78,8 @@ The format of a class should be ```c++ class name : public ancestor { - // private variables here + int privateVariable; + // etc. public: // public stuff here }; @@ -88,6 +91,8 @@ Avoid GNU-specific language features. I build with strict C99 conformance. ### OS X-specific notes +Avoid GNU-specific/clang-specific language features. I build with strict C99 conformance. + libui is presently **not** ARC-compliant. Features that require ARC should be avoided for now. I may consider changing this in the future, but it will be a significant change. To ensure maximum compiler output in the event of a coding error, there should not be any implicit method calls in Objective-C code. For instance, don't do @@ -105,13 +110,13 @@ The format of a class should be // ivars } // properties -- (ret)method; +- (ret)method:(int)arg; // more methods @end @implementation name -- (ret)method +- (ret)method:(int)arg { // note the lack of semicolon } From 39530a0a2edd06b726716afa04337ab2816e557a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 10 Jun 2017 17:08:10 -0400 Subject: [PATCH 0768/1329] Another quick CONTRIBUTING fix. --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1a35c211..d04792f2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -71,7 +71,7 @@ Despite using C++, please refrain from using the following: The following are not recommended, for consistency with the rest of libui: - variable declarations anywhere in a function (keep them all at the top) -- `for (int...` (C++11 foreach syntax is fine, though) +- `for (int x...` (C++11 foreach syntax is fine, though) - omitting the `struct` on type names for ordinary structs The format of a class should be From 8728dcb5c07402883d080d54712346cb01dd2a67 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 10 Jun 2017 23:33:25 -0400 Subject: [PATCH 0769/1329] Started a new example for showing off OpenType features. This just lays out the UI and draws the string. --- CONTRIBUTING.md | 6 +- README.md | 2 +- examples/CMakeLists.txt | 10 ++- examples/opentype/main.c | 173 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 188 insertions(+), 3 deletions(-) create mode 100644 examples/opentype/main.c 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; +} From 85c39c6cb32e5b2c0183e8c788836d10d5f5d138 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 11 Jun 2017 00:37:26 -0400 Subject: [PATCH 0770/1329] Connected events in the opentype example. --- examples/opentype/main.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/examples/opentype/main.c b/examples/opentype/main.c index 00c3f451..5f8c0f70 100644 --- a/examples/opentype/main.c +++ b/examples/opentype/main.c @@ -5,6 +5,8 @@ #include #include "../../ui.h" +// TODO the grid simply flat out does not work on OS X + uiWindow *mainwin; uiFontButton *fontButton; uiEntry *textEntry; @@ -81,6 +83,21 @@ static int handlerKeyEvent(uiAreaHandler *ah, uiArea *a, uiAreaKeyEvent *e) return 0; } +static void onFontChanged(uiFontButton *b, void *data) +{ + remakeAttrStr(); +} + +static void onTextChanged(uiEntry *e, void *data) +{ + remakeAttrStr(); +} + +static void onNULLToggled(uiCheckbox *c, void *data) +{ + remakeAttrStr(); +} + static int onClosing(uiWindow *w, void *data) { // TODO change the others to be like this? (the others destroy here rather than later) @@ -129,6 +146,7 @@ int main(void) uiWindowSetChild(mainwin, uiControl(grid)); fontButton = uiNewFontButton(); + uiFontButtonOnChanged(fontButton, onFontChanged, NULL); uiGridAppend(grid, uiControl(fontButton), 0, 0, 1, 1, // TODO are these Y values correct? @@ -136,6 +154,7 @@ int main(void) textEntry = uiNewEntry(); uiEntrySetText(textEntry, "afford afire aflight"); + uiEntryOnChanged(textEntry, onTextChanged, NULL); uiGridAppend(grid, uiControl(textEntry), 1, 0, 1, 1, // TODO are these Y values correct too? @@ -149,6 +168,7 @@ int main(void) 0, uiAlignFill, 1, uiAlignFill); nullFeatures = uiNewCheckbox("NULL uiOpenTypeFeatures"); + uiCheckboxOnToggled(nullFeatures, onNULLToggled, NULL); uiBoxAppend(vbox, uiControl(nullFeatures), 0); // TODO separator (if other stuff isn't a tab) From c8f4ccc712dfde6453ed6494bb83915973cef50b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 11 Jun 2017 12:21:49 -0400 Subject: [PATCH 0771/1329] Did a patch for OS X. Fixing grid is gonna be FUN. --- examples/opentype/main.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/examples/opentype/main.c b/examples/opentype/main.c index 5f8c0f70..0872aaa2 100644 --- a/examples/opentype/main.c +++ b/examples/opentype/main.c @@ -173,6 +173,14 @@ int main(void) // TODO separator (if other stuff isn't a tab) + // TODO needed for this to be testable on os x without rewriting everything again + { + int x; + + for (x = 0; x < 10; x++) + uiBoxAppend(vbox, uiControl(uiNewEntry()), 0); + } + // TODO other stuff area = uiNewArea(&handler); From c3568d51624465912b6432c8b1766dd8551e2a76 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 12 Jun 2017 23:42:45 -0400 Subject: [PATCH 0772/1329] More TODOs. --- TODO.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TODO.md b/TODO.md index 41184b93..e15ef19e 100644 --- a/TODO.md +++ b/TODO.md @@ -173,3 +173,5 @@ function: ide_editor_map_bin_add() - Mouse ClickLock: do we need to do anything special? *should* we? https://msdn.microsoft.com/en-us/library/windows/desktop/ms724947(v=vs.85).aspx - consider a uiAnticipateDoubleClick() or uiDoubleClickTime() (for a uiQueueTimer()) or something: https://blogs.msdn.microsoft.com/oldnewthing/20041015-00/?p=37553 + +- determine whether MSGF_USER is for and if it's correct for our uiArea message filter (if we have one) From 6b989f80caf4ba1fc10b83d1f232d46361c9a842 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 13 Jun 2017 21:17:44 -0400 Subject: [PATCH 0773/1329] More TODOs. --- TODO.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/TODO.md b/TODO.md index e15ef19e..0a8cf8ee 100644 --- a/TODO.md +++ b/TODO.md @@ -175,3 +175,11 @@ function: ide_editor_map_bin_add() - consider a uiAnticipateDoubleClick() or uiDoubleClickTime() (for a uiQueueTimer()) or something: https://blogs.msdn.microsoft.com/oldnewthing/20041015-00/?p=37553 - determine whether MSGF_USER is for and if it's correct for our uiArea message filter (if we have one) + +- source file encoding and MSVC compiler itself? https://stackoverflow.com/questions/20518040/how-can-i-get-the-directwrite-padwrite-sample-to-work + - also need to worry about object file and output encoding... + - this also names the author of the padwrite sample + +- OpenType features TODOs + - https://stackoverflow.com/questions/32545675/what-are-the-default-typography-settings-used-by-idwritetextlayout + - feature/shaping interaction rules for arabic: https://www.microsoft.com/typography/OpenTypeDev/arabic/intro.htm From fd7c436b8a4b2c391cd731cf97414ecb93187887 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 14 Jun 2017 22:07:41 -0400 Subject: [PATCH 0774/1329] More TODOs. --- windows/drawtext.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index f173f471..59d64097 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -7,6 +7,7 @@ // - if that's not a problem, do we have overlapping rects in the hittest sample? I can't tell... // - empty string: nLines == 1 and all checks out except extents has x == 0 when not left aligned // - paragraph alignment is subject to RTL mirroring; see if it is on other platforms +// - add overhang info to metrics? // TODO verify our renderer is correct, especially with regards to snapping From cc8a41268779d0ba732b508e2a5f511761a26c04 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Jun 2017 17:15:32 -0400 Subject: [PATCH 0775/1329] More TODOs. --- windows/dwrite.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/windows/dwrite.cpp b/windows/dwrite.cpp index 2e9ce928..498ef376 100644 --- a/windows/dwrite.cpp +++ b/windows/dwrite.cpp @@ -66,6 +66,7 @@ WCHAR *fontCollectionCorrectString(fontCollection *fc, IDWriteLocalizedStrings * hr = names->FindLocaleName(fc->userLocale, &index, &exists); if (hr != S_OK || (hr == S_OK && !exists)) hr = names->FindLocaleName(L"en-us", &index, &exists); + // TODO check hr again here? or did I decide that would be redundant because COM requires output arguments to be filled regardless of return value? if (!exists) index = 0; From 4610a5a363a55933ce2f425b0a40066072e7621e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 18 Jun 2017 11:19:24 -0400 Subject: [PATCH 0776/1329] More TODOs. --- TODO.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/TODO.md b/TODO.md index 0a8cf8ee..e5be9a4a 100644 --- a/TODO.md +++ b/TODO.md @@ -183,3 +183,10 @@ function: ide_editor_map_bin_add() - OpenType features TODOs - https://stackoverflow.com/questions/32545675/what-are-the-default-typography-settings-used-by-idwritetextlayout - feature/shaping interaction rules for arabic: https://www.microsoft.com/typography/OpenTypeDev/arabic/intro.htm + - other stuff, mostly about UIs and what users expect to be able to set + - https://klim.co.nz/blog/towards-an-ideal-opentype-user-interface/ + - https://libregraphicsmeeting.org/2016/designing-for-many-applications-opentype-features-ui/ + - https://www.youtube.com/watch?v=wEyDhsH076Y + - https://twitter.com/peter_works + - http://ilovetypography.com/2014/10/22/better-ui-for-better-typography-adobe-petition/ + - http://silgraphite.sourceforge.net/ui/studynote.html From e6ee2b0dbd2b94618fea200405b41bb549537f8b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 18 Jun 2017 20:10:42 -0400 Subject: [PATCH 0777/1329] Some TODO elminiation (for once). One TODO got reshuffled. --- darwin/opentype.m | 1 + ui.h | 3 +++ ui_attrstr.h | 22 ++++++++++++++++------ unix/opentype.c | 1 + windows/opentype.cpp | 3 +-- 5 files changed, 22 insertions(+), 8 deletions(-) diff --git a/darwin/opentype.m b/darwin/opentype.m index 46bdaf19..1a2f3ab1 100644 --- a/darwin/opentype.m +++ b/darwin/opentype.m @@ -47,6 +47,7 @@ void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, c NSNumber *tn; tn = mkMapObject(mkTag(a, b, c, d)); + // documented as doing nothing if tn is not in otf->tags [otf->tags removeObjectForKey:tn]; } diff --git a/ui.h b/ui.h index 72e5eaef..1a875cab 100644 --- a/ui.h +++ b/ui.h @@ -2,6 +2,9 @@ // TODO add a uiVerifyControlType() function that can be used by control implementations to verify controls +// TODOs +// - make getters that return whether something exists accept a NULL pointer to discard the value (and thus only return that the thing exists?) + #ifndef __LIBUI_UI_H__ #define __LIBUI_UI_H__ diff --git a/ui_attrstr.h b/ui_attrstr.h index 0024442c..835cee39 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -19,7 +19,10 @@ // layout-specific properties. typedef struct uiAttributedString uiAttributedString; -// TODO either here or above, say that only one attribute can be applied per attribute type per character +// uiAttribute specifies the types of possible attributes that can be +// applied to a uiAttributedString. For every byte in the +// uiAttributedString, at most one value of each attribute type can +// be applied. // TODO just make a separate field in uiAttributeSpec for everything? or make attribute objects opaque instead? _UI_ENUM(uiAttribute) { // uiAttributeFamily changes the font family of the text it is @@ -64,7 +67,7 @@ _UI_ENUM(uiAttribute) { // uiAttributeFeatures changes the OpenType features of the // text it is applied to. Use the Features field of uiAttributeSpec. - uiAttributeFeatures, // use Features + uiAttributeFeatures, }; _UI_ENUM(uiDrawUnderlineStyle) { @@ -124,15 +127,22 @@ _UI_EXTERN uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures _UI_EXTERN void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value); // uiOpenTypeFeaturesRemove() removes the given feature tag -// and value from otf. -// TODO what happens if the tag isn't there? +// and value from otf. If the tag is not present in otf, +// uiOpenTypeFeaturesRemove() does nothing. _UI_EXTERN void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d); // uiOpenTypeFeaturesGet() determines whether the given feature // tag is present in otf. If it is, *value is set to the tag's value and // nonzero is returned. Otherwise, zero is returned. -// TODO zero-fill value unconditionally? and if so, to other functions in libui -// TODO allow NULL for value? and throughout libui? +// +// Note that if uiOpenTypeFeaturesGet() returns zero, value isn't +// changed. This is important: if a feature is not present in a +// uiOpenTypeFeatures, the feature is NOT treated as if its +// value was zero anyway. Script-specific font shaping rules and +// font-specific feature settings may use a different default value +// for a feature. You should likewise not treat a missing feature as +// having a value of zero either. Instead, a missing feature should +// be treated as having some unspecified default value. // TODO const-correct this function (can we do that given the members of the struct on some platforms being full blown objects that may or may not themselves be const-correct?) _UI_EXTERN int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value); diff --git a/unix/opentype.c b/unix/opentype.c index 79b1d93a..764da50a 100644 --- a/unix/opentype.c +++ b/unix/opentype.c @@ -56,6 +56,7 @@ void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d) { + // will just return FALSE if the tag is not in otf->tags (half-documented as such), so we can use it safely g_hash_table_remove(otf->tags, mkTag(a, b, c, d)); } diff --git a/windows/opentype.cpp b/windows/opentype.cpp index 89b5913c..8fc37175 100644 --- a/windows/opentype.cpp +++ b/windows/opentype.cpp @@ -39,10 +39,9 @@ void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char (*(otf->tags))[mktag(a, b, c, d)] = value; } -// TODO what should happen if a/b/c/d isn't defined? -// TODO what does std::map do if a/b/c/d isn't defined? void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d) { + // this will just return 0 if nothing was removed (if I'm reading the help pages I've found correctly) otf->tags->erase(mktag(a, b, c, d)); } From 3e20e4670c7937a1a2b6814313a8f2a8df87a05a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 19 Jun 2017 14:50:03 -0400 Subject: [PATCH 0778/1329] Made a decision on const correctness in uiOpenTypeFeatures. --- darwin/opentype.m | 9 +++++---- ui.h | 1 + ui_attrstr.h | 13 +++++++------ unix/opentype.c | 11 ++++++----- windows/opentype.cpp | 5 +++-- 5 files changed, 22 insertions(+), 17 deletions(-) diff --git a/darwin/opentype.m b/darwin/opentype.m index 1a2f3ab1..890aa02a 100644 --- a/darwin/opentype.m +++ b/darwin/opentype.m @@ -51,7 +51,8 @@ void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, c [otf->tags removeObjectForKey:tn]; } -int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value) +// TODO will the const wreck stuff up? +int uiOpenTypeFeaturesGet(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value) { NSNumber *tn, *vn; @@ -78,7 +79,7 @@ void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeatures b = (uint8_t) ((tag >> 16) & 0xFF); c = (uint8_t) ((tag >> 8) & 0xFF); d = (uint8_t) (tag & 0xFF); - ret = (*f)((char) a, (char) b, (char) c, (char) d, + ret = (*f)(otf, (char) a, (char) b, (char) c, (char) d, mapObjectValue(vn), data); // TODO for all: require exact match? if (ret == uiForEachStop) @@ -98,7 +99,7 @@ int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeature // TODO explain all this // TODO rename outerArray and innerDict (the names made sense when this was part of fontdescAppendFeatures(), but not here) // TODO make all this use enumerateKeysAndObjects (which requires duplicating code)? -static uiForEach otfArrayForEachAAT(char a, char b, char c, char d, uint32_t value, void *data) +static uiForEach otfArrayForEachAAT(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data) { CFMutableArrayRef outerArray = (CFMutableArrayRef) data; @@ -133,7 +134,7 @@ static uiForEach otfArrayForEachAAT(char a, char b, char c, char d, uint32_t val } // TODO find out which fonts differ in AAT small caps and test them with this -static uiForEach otfArrayForEachOT(char a, char b, char c, char d, uint32_t value, void *data) +static uiForEach otfArrayForEachOT(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data) { CFMutableArrayRef outerArray = (CFMutableArrayRef) data; CFDictionaryRef innerDict; diff --git a/ui.h b/ui.h index 1a875cab..039d45f4 100644 --- a/ui.h +++ b/ui.h @@ -4,6 +4,7 @@ // TODOs // - make getters that return whether something exists accept a NULL pointer to discard the value (and thus only return that the thing exists?) +// - const-correct everything #ifndef __LIBUI_UI_H__ #define __LIBUI_UI_H__ diff --git a/ui_attrstr.h b/ui_attrstr.h index 835cee39..430d65d0 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -104,8 +104,10 @@ _UI_ENUM(uiDrawUnderlineColor) { // TODO invalid features typedef struct uiOpenTypeFeatures uiOpenTypeFeatures; -// TODO pass the feature set? (resolve const struct issue below first) -typedef uiForEach (*uiOpenTypeFeaturesForEachFunc)(char a, char b, char c, char d, uint32_t value, void *data); +// uiOpenTypeFeaturesForEachFunc is the type of the function +// invoked by uiOpenTypeFeaturesForEach() for every OpenType +// feature in otf. +typedef uiForEach (*uiOpenTypeFeaturesForEachFunc)(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data); // @role uiOpenTypeFeatures constructor // uiNewOpenTypeFeatures() returns a new uiOpenTypeFeatures @@ -143,12 +145,11 @@ _UI_EXTERN void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b // for a feature. You should likewise not treat a missing feature as // having a value of zero either. Instead, a missing feature should // be treated as having some unspecified default value. -// TODO const-correct this function (can we do that given the members of the struct on some platforms being full blown objects that may or may not themselves be const-correct?) -_UI_EXTERN int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value); +_UI_EXTERN int uiOpenTypeFeaturesGet(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value); // uiOpenTypeFeaturesForEach() executes f for every tag-value -// pair in otf. The enumeration order is unspecified. -// TODO make other enumerators const (and in general const-correct everything) (but see the const struct TODO below and the const struct object member TODO above) +// pair in otf. The enumeration order is unspecified. You cannot +// modify otf while uiOpenTypeFeaturesForEach() is running. _UI_EXTERN void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data); // uiOpenTypeFeaturesEqual() returns nonzero if a is equal to b. diff --git a/unix/opentype.c b/unix/opentype.c index 764da50a..30933ea8 100644 --- a/unix/opentype.c +++ b/unix/opentype.c @@ -60,9 +60,8 @@ void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, c g_hash_table_remove(otf->tags, mkTag(a, b, c, d)); } -// TODO should this be before Add and Remove? -// TODO better name than Get? -int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value) +// TODO will the const wreck stuff up? +int uiOpenTypeFeaturesGet(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value) { gboolean found; gpointer gv; @@ -77,6 +76,7 @@ int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char } struct otfForEach { + const uiOpenTypeFeatures *otf; uiOpenTypeFeaturesForEachFunc f; void *data; uiForEach ret; @@ -96,7 +96,7 @@ static void foreach(gpointer key, gpointer value, gpointer data) b = (uint8_t) ((tag >> 16) & 0xFF); c = (uint8_t) ((tag >> 8) & 0xFF); d = (uint8_t) (tag & 0xFF); - ofe->ret = (*(ofe->f))((char) a, (char) b, (char) c, (char) d, GPOINTER_TO_INT(value), ofe->data); + ofe->ret = (*(ofe->f))(ofe->otf, (char) a, (char) b, (char) c, (char) d, GPOINTER_TO_INT(value), ofe->data); } void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data) @@ -104,6 +104,7 @@ void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeatures struct otfForEach ofe; memset(&ofe, 0, sizeof (struct otfForEach)); + ofe.otf = otf; ofe.f = f; ofe.data = data; g_hash_table_foreach(otf->tags, foreach, &ofe); @@ -183,7 +184,7 @@ out: // see https://developer.mozilla.org/en/docs/Web/CSS/font-feature-settings // TODO make this a g_hash_table_foreach() function (which requires duplicating code)? -static uiForEach toCSS(char a, char b, char c, char d, uint32_t value, void *data) +static uiForEach toCSS(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data) { // TODO is there a G_STRING()? GString *s = (GString *) data; diff --git a/windows/opentype.cpp b/windows/opentype.cpp index 8fc37175..235f0388 100644 --- a/windows/opentype.cpp +++ b/windows/opentype.cpp @@ -45,7 +45,8 @@ void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, c otf->tags->erase(mktag(a, b, c, d)); } -int uiOpenTypeFeaturesGet(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value) +// TODO will the const wreck stuff up? +int uiOpenTypeFeaturesGet(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value) { tagmap::const_iterator iter; @@ -69,7 +70,7 @@ void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeatures b = (uint8_t) ((iter->first >> 8) & 0xFF); c = (uint8_t) ((iter->first >> 16) & 0xFF); d = (uint8_t) ((iter->first >> 24) & 0xFF); - ret = (*f)((char) a, (char) b, (char) c, (char) d, + ret = (*f)(otf, (char) a, (char) b, (char) c, (char) d, iter->second, data); if (ret == uiForEachStop) return; From 5d7128781b163f603a0dc596314755b26152e4fe Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 19 Jun 2017 21:45:18 -0400 Subject: [PATCH 0779/1329] And const-corrected uiAttributedStringForEachAttribute(). --- common/attrstr.c | 2 +- darwin/attrstr.m | 2 +- ui_attrstr.h | 13 ++++++++++--- unix/attrstr.c | 2 +- windows/attrstr.cpp | 2 +- 5 files changed, 14 insertions(+), 7 deletions(-) diff --git a/common/attrstr.c b/common/attrstr.c index c5466edf..ab406ad8 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -306,7 +306,7 @@ void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttributeSpec *spec attrlistInsertAttribute(s->attrs, spec, start, end); } -// TODO introduce an iterator? +// LONGTERM introduce an iterator object instead? void uiAttributedStringForEachAttribute(uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data) { attrlistForEach(s->attrs, s, f, data); diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 6975ca06..89c09d22 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -203,7 +203,7 @@ static CGColorRef mkcolor(uiAttributeSpec *spec) return color; } -static uiForEach processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) +static uiForEach processAttribute(const uiAttributedString *s, const uiAttributeSpec *spec, size_t start, size_t end, void *data) { struct foreachParams *p = (struct foreachParams *) data; CFRange range; diff --git a/ui_attrstr.h b/ui_attrstr.h index 430d65d0..58ec6199 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -106,7 +106,8 @@ typedef struct uiOpenTypeFeatures uiOpenTypeFeatures; // uiOpenTypeFeaturesForEachFunc is the type of the function // invoked by uiOpenTypeFeaturesForEach() for every OpenType -// feature in otf. +// feature in otf. Refer to that function's documentation for more +// details. typedef uiForEach (*uiOpenTypeFeaturesForEachFunc)(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data); // @role uiOpenTypeFeatures constructor @@ -162,6 +163,7 @@ typedef struct uiAttributeSpec uiAttributeSpec; // TODO note that pointers are copied // TODO add a function to uiAttributedString to get an attribute's value at a specific index or in a specific range, so we can edit // (TODO related to above: what about memory consumption during a read-modify-write cycle due to copying?) +// TODO normalize documentation between typedefs and structs struct uiAttributeSpec { uiAttribute Type; const char *Family; @@ -174,8 +176,11 @@ struct uiAttributeSpec { const uiOpenTypeFeatures *Features; }; -// TODO how would we make spec const in this case, to prevent fields from being modified? -typedef uiForEach (*uiAttributedStringForEachAttributeFunc)(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data); +// uiAttributedStringForEachAttributeFunc is the type of the function +// invoked by uiAttributedStringForEachAttribute() for every +// attribute in s. Refer to that function's documentation for more +// details. +typedef uiForEach (*uiAttributedStringForEachAttributeFunc)(const uiAttributedString *s, const uiAttributeSpec *spec, size_t start, size_t end, void *data); // @role uiAttributedString constructor // uiNewAttributedString() creates a new uiAttributedString from @@ -201,6 +206,8 @@ _UI_EXTERN void uiAttributedStringDelete(uiAttributedString *s, size_t start, si _UI_EXTERN size_t uiAttributedStringNumGraphemes(uiAttributedString *s); _UI_EXTERN size_t uiAttributedStringByteIndexToGrapheme(uiAttributedString *s, size_t pos); _UI_EXTERN size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos); +// TODO document this +// TODO possibly copy the spec each time to ensure it doesn't get clobbered _UI_EXTERN void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end); _UI_EXTERN void uiAttributedStringForEachAttribute(uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); diff --git a/unix/attrstr.c b/unix/attrstr.c index 245ae7f6..36141b7c 100644 --- a/unix/attrstr.c +++ b/unix/attrstr.c @@ -65,7 +65,7 @@ static void addattr(struct foreachParams *p, size_t start, size_t end, PangoAttr pango_attr_list_insert(p->attrs, attr); } -static uiForEach processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) +static uiForEach processAttribute(const uiAttributedString *s, const uiAttributeSpec *spec, size_t start, size_t end, void *data) { struct foreachParams *p = (struct foreachParams *) data; GClosure *closure; diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index f180f74b..ca805237 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -49,7 +49,7 @@ static backgroundFunc mkBackgroundFunc(size_t start, size_t end, double r, doubl }; } -static uiForEach processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end, void *data) +static uiForEach processAttribute(const uiAttributedString *s, const uiAttributeSpec *spec, size_t start, size_t end, void *data) { struct foreachParams *p = (struct foreachParams *) data; DWRITE_TEXT_RANGE range; From 180b6429ef2c297a64509699b80d0a6d0153c28e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 19 Jun 2017 22:00:46 -0400 Subject: [PATCH 0780/1329] More documentation, const correctness, and TODO wrangling. --- common/attrstr.c | 4 ++-- ui.h | 1 + ui_attrstr.h | 28 ++++++++++++++++++++++++---- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/common/attrstr.c b/common/attrstr.c index ab406ad8..e34b7d27 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -73,12 +73,12 @@ void uiFreeAttributedString(uiAttributedString *s) uiFree(s); } -const char *uiAttributedStringString(uiAttributedString *s) +const char *uiAttributedStringString(const uiAttributedString *s) { return s->s; } -size_t uiAttributedStringLen(uiAttributedString *s) +size_t uiAttributedStringLen(const uiAttributedString *s) { return s->len; } diff --git a/ui.h b/ui.h index 039d45f4..0f7dce64 100644 --- a/ui.h +++ b/ui.h @@ -5,6 +5,7 @@ // TODOs // - make getters that return whether something exists accept a NULL pointer to discard the value (and thus only return that the thing exists?) // - const-correct everything +// - normalize documentation between typedefs and structs #ifndef __LIBUI_UI_H__ #define __LIBUI_UI_H__ diff --git a/ui_attrstr.h b/ui_attrstr.h index 58ec6199..82a532ba 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -163,7 +163,6 @@ typedef struct uiAttributeSpec uiAttributeSpec; // TODO note that pointers are copied // TODO add a function to uiAttributedString to get an attribute's value at a specific index or in a specific range, so we can edit // (TODO related to above: what about memory consumption during a read-modify-write cycle due to copying?) -// TODO normalize documentation between typedefs and structs struct uiAttributeSpec { uiAttribute Type; const char *Family; @@ -194,21 +193,42 @@ _UI_EXTERN void uiFreeAttributedString(uiAttributedString *s); // uiAttributedStringString() returns the textual content of s as a // '\0'-terminated UTF-8 string. The returned pointer is valid until // the next change to the textual content of s. -_UI_EXTERN const char *uiAttributedStringString(uiAttributedString *s); +_UI_EXTERN const char *uiAttributedStringString(const uiAttributedString *s); // uiAttributedStringLength() returns the number of UTF-8 bytes in // the textual content of s, excluding the terminating '\0'. -_UI_EXTERN size_t uiAttributedStringLen(uiAttributedString *s); +_UI_EXTERN size_t uiAttributedStringLen(const uiAttributedString *s); +// uiAttributedStringAppendUnattributed() adds the '\0'-terminated +// UTF-8 string str to the end of s. The new substring will be +// unattributed. _UI_EXTERN void uiAttributedStringAppendUnattributed(uiAttributedString *s, const char *str); + +// uiAttributedStringAppendUnattributed() adds the '\0'-terminated +// UTF-8 string str to s at the byte position specified by at. The new +// substring will be unattributed; existing attributes will be moved +// along with its text. _UI_EXTERN void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *str, size_t at); + +// TODO add the Append and InsertAtAttributed functions + +// uiAttributedStringDelete() deletes the characters and attributes of +// s in the range [start, end). _UI_EXTERN void uiAttributedStringDelete(uiAttributedString *s, size_t start, size_t end); + +// TODO const correct this somehow (the implementation needs to mutate the structure) _UI_EXTERN size_t uiAttributedStringNumGraphemes(uiAttributedString *s); + +// TODO const correct this somehow (the implementation needs to mutate the structure) _UI_EXTERN size_t uiAttributedStringByteIndexToGrapheme(uiAttributedString *s, size_t pos); + +// TODO const correct this somehow (the implementation needs to mutate the structure) _UI_EXTERN size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos); + +_UI_EXTERN void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end); + // TODO document this // TODO possibly copy the spec each time to ensure it doesn't get clobbered -_UI_EXTERN void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end); _UI_EXTERN void uiAttributedStringForEachAttribute(uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); typedef struct uiDrawFontDescriptor uiDrawFontDescriptor; From 271a3bc0222e1526487de524e381f10de454344d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 19 Jun 2017 23:34:59 -0400 Subject: [PATCH 0781/1329] More TODOs. --- ui_attrstr.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index 82a532ba..27fd7473 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -210,7 +210,8 @@ _UI_EXTERN void uiAttributedStringAppendUnattributed(uiAttributedString *s, cons // along with its text. _UI_EXTERN void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *str, size_t at); -// TODO add the Append and InsertAtAttributed functions +// TODO add the Append and InsertAtExtendingAttributes functions +// TODO and add functions that take a string + length // uiAttributedStringDelete() deletes the characters and attributes of // s in the range [start, end). From 2bea99116d3192f2fad1f401380d4a7e563ffd73 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 20 Jun 2017 19:54:35 -0400 Subject: [PATCH 0782/1329] More TODOs. --- TODO.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/TODO.md b/TODO.md index e5be9a4a..4b1c4452 100644 --- a/TODO.md +++ b/TODO.md @@ -190,3 +190,6 @@ function: ide_editor_map_bin_add() - https://twitter.com/peter_works - http://ilovetypography.com/2014/10/22/better-ui-for-better-typography-adobe-petition/ - http://silgraphite.sourceforge.net/ui/studynote.html + +- add NXCOMPAT (DEP awareness) to the Windows builds + - and ASLR too? or is that not a linker setting From 40bc1505cef3a23bdf2d6aee0a8fe8c0456dd8ea Mon Sep 17 00:00:00 2001 From: Ashley Date: Mon, 24 Jul 2017 02:21:18 +0200 Subject: [PATCH 0783/1329] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 01275c0e..b8b407e3 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # libui: a portable GUI library for C This README is being written.
-[![Build Status](https://travis-ci.org/andlabs/libui.svg)](https://travis-ci.org/andlabs/libui) +[![Build Status](https://travis-ci.org/andlabs/libui.svg?branch=master)](https://travis-ci.org/andlabs/libui) ## Announcements From b98888a628dcecc64c31e18a29aa8a31c6ae2dff Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 20 Aug 2017 05:30:36 -0400 Subject: [PATCH 0784/1329] More TODOs. --- windows/utilwin.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/windows/utilwin.cpp b/windows/utilwin.cpp index 11f9a415..28950674 100644 --- a/windows/utilwin.cpp +++ b/windows/utilwin.cpp @@ -8,6 +8,7 @@ // - It handles WM_QUERYENDSESSION requests. // - It handles WM_WININICHANGE and forwards the message to any child windows that request it. // - It handles executing functions queued to run by uiQueueMain(). +// TODO explain why it isn't message-only #define utilWindowClass L"libui_utilWindowClass" From 2702f4f8748dd0a905c529a1d0f1b3e1b8a71246 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 3 Sep 2017 16:14:58 -0400 Subject: [PATCH 0785/1329] More TODOs. --- TODO.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/TODO.md b/TODO.md index 4b1c4452..7d148d7f 100644 --- a/TODO.md +++ b/TODO.md @@ -193,3 +193,8 @@ function: ide_editor_map_bin_add() - add NXCOMPAT (DEP awareness) to the Windows builds - and ASLR too? or is that not a linker setting + +OS X: embedding an Info.plist into a binary directly +https://www.objc.io/issues/6-build-tools/mach-o-executables/ +TODO will this let Dictation work? +TODO investigate ad-hoc codesigning From 21c99859e48dff785f3fe0202dfd73e5c4f4924b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 17 Sep 2017 15:33:46 -0400 Subject: [PATCH 0786/1329] More TODOs. --- TODO.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TODO.md b/TODO.md index 7d148d7f..adf8e9de 100644 --- a/TODO.md +++ b/TODO.md @@ -198,3 +198,5 @@ OS X: embedding an Info.plist into a binary directly https://www.objc.io/issues/6-build-tools/mach-o-executables/ TODO will this let Dictation work? TODO investigate ad-hoc codesigning + +https://blogs.msdn.microsoft.com/oldnewthing/20040112-00/?p=41083 def files for decoration (I forget if I said this earlier) From 7b89fb21a13e2b08834742a68cea4e41fff42b33 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 17 Sep 2017 21:03:41 -0400 Subject: [PATCH 0787/1329] More TODOs. --- TODO.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/TODO.md b/TODO.md index adf8e9de..aa9c75b3 100644 --- a/TODO.md +++ b/TODO.md @@ -200,3 +200,8 @@ TODO will this let Dictation work? TODO investigate ad-hoc codesigning https://blogs.msdn.microsoft.com/oldnewthing/20040112-00/?p=41083 def files for decoration (I forget if I said this earlier) + +TODO ClipCursor() stuff; probably not useful for libui but still +https://blogs.msdn.microsoft.com/oldnewthing/20140102-00/?p=2183 +https://blogs.msdn.microsoft.com/oldnewthing/20061117-03/?p=28973 +https://msdn.microsoft.com/en-us/library/windows/desktop/ms648383(v=vs.85).aspx From c8c51502cb27ba0c9b939d5f09850d4476bd48c7 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 19 Sep 2017 22:22:04 -0400 Subject: [PATCH 0788/1329] More TODOs. --- TODO.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/TODO.md b/TODO.md index aa9c75b3..6653a6cf 100644 --- a/TODO.md +++ b/TODO.md @@ -205,3 +205,8 @@ TODO ClipCursor() stuff; probably not useful for libui but still https://blogs.msdn.microsoft.com/oldnewthing/20140102-00/?p=2183 https://blogs.msdn.microsoft.com/oldnewthing/20061117-03/?p=28973 https://msdn.microsoft.com/en-us/library/windows/desktop/ms648383(v=vs.85).aspx + +https://cmake.org/Wiki/CMake_Useful_Variables +set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined") +On Unix systems, this will make linker report any unresolved symbols from object files (which is quite typical when you compile many targets in CMake projects, but do not bother with linking target dependencies in proper order). +(I used to have something like this back when I used makefiles; did it convert in? I forget) From cb5f9b3c9c79bc2dcd7782b158b566439f3a520c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 26 Sep 2017 18:54:23 -0400 Subject: [PATCH 0789/1329] More TODOs. --- TODO.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/TODO.md b/TODO.md index 6653a6cf..d831395b 100644 --- a/TODO.md +++ b/TODO.md @@ -210,3 +210,12 @@ https://cmake.org/Wiki/CMake_Useful_Variables set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined") On Unix systems, this will make linker report any unresolved symbols from object files (which is quite typical when you compile many targets in CMake projects, but do not bother with linking target dependencies in proper order). (I used to have something like this back when I used makefiles; did it convert in? I forget) + +look into these for the os x port +https://developer.apple.com/documentation/appkit/view_management/nseditor?language=objc +https://developer.apple.com/documentation/appkit/view_management/nseditorregistration?language=objc + +for future versions of the os x port +https://developer.apple.com/documentation/appkit/nslayoutguide?language=objc and anchors +https://developer.apple.com/documentation/appkit/nsuserinterfacecompression?language=objc https://developer.apple.com/documentation/appkit/nsuserinterfacecompressionoptions?language=objc +though at some point we'll be able to use NSStackView and NSGridView directly, so... From e597d8ce3ba96eab2836f5d34b787049d9373a75 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 27 Sep 2017 14:24:07 -0400 Subject: [PATCH 0790/1329] More TODOs. --- darwin/drawtext.m | 1 + 1 file changed, 1 insertion(+) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index fd5a5bbb..43c8b4e9 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -190,6 +190,7 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) // TODO how is this affected by a non-identity CTM? CGContextTranslateCTM(c->c, 0, c->height); CGContextScaleCTM(c->c, 1.0, -1.0); + // TODO save the text matrix CGContextSetTextMatrix(c->c, CGAffineTransformIdentity); // wait, that's not enough; we need to offset y values to account for our new flipping From 55a97093f54a7bb0e102b0feb83dd2e5cce0cd17 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 29 Sep 2017 09:58:43 -0400 Subject: [PATCH 0791/1329] Removed a stale TODO. --- darwin/attrstr.m | 2 -- 1 file changed, 2 deletions(-) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 89c09d22..cdf7334f 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -1,8 +1,6 @@ // 12 february 2017 #import "uipriv_darwin.h" -// LONGTERM FUTURE for typographic features, on 10.10 we can use OpenType tags directly! - // this is what AppKit does internally // WebKit does this too; see https://github.com/adobe/webkit/blob/master/Source/WebCore/platform/graphics/mac/GraphicsContextMac.mm static NSColor *spellingColor = nil; From 98e06716b6b3ff029ac112b2759a15c8476d4021 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 8 Oct 2017 14:42:15 -0400 Subject: [PATCH 0792/1329] More TODOs. --- TODO.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/TODO.md b/TODO.md index d831395b..dee9d997 100644 --- a/TODO.md +++ b/TODO.md @@ -219,3 +219,31 @@ for future versions of the os x port https://developer.apple.com/documentation/appkit/nslayoutguide?language=objc and anchors https://developer.apple.com/documentation/appkit/nsuserinterfacecompression?language=objc https://developer.apple.com/documentation/appkit/nsuserinterfacecompressionoptions?language=objc though at some point we'll be able to use NSStackView and NSGridView directly, so... + +Cocoa PDFs +https://developer.apple.com/documentation/appkit/nspdfimagerep?language=objc +https://developer.apple.com/documentation/coregraphics?language=objc +https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Printing/osxp_pagination/osxp_pagination.html#//apple_ref/doc/uid/20001051-119037 +https://developer.apple.com/documentation/appkit/nsprintoperation/1529269-pdfoperationwithview?language=objc +https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Printing/osxp_printapps/osxp_printapps.html#//apple_ref/doc/uid/20000861-BAJBFGED +https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Printing/osxp_printingapi/osxp_printingapi.html#//apple_ref/doc/uid/10000083i-CH2-SW2 +https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Printing/osxp_printinfo/osxp_printinfo.html#//apple_ref/doc/uid/20000864-BAJBFGED +https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Printing/osxp_printlayoutpanel/osxp_printlayoutpanel.html#//apple_ref/doc/uid/20000863-BAJBFGED +https://developer.apple.com/documentation/appkit/nspagelayout?language=objc +https://developer.apple.com/documentation/appkit/nsprintinfo?language=objc +https://developer.apple.com/documentation/applicationservices/core_printing?language=objc +https://developer.apple.com/documentation/applicationservices/1463247-pmcreatesession?language=objc +https://developer.apple.com/documentation/applicationservices/pmprintsession?language=objc +https://developer.apple.com/documentation/applicationservices/1460101-pmsessionbegincgdocumentnodialog?language=objc +https://developer.apple.com/documentation/applicationservices/1463416-pmsessionbeginpagenodialog?language=objc +https://developer.apple.com/documentation/applicationservices/1506831-anonymous/kpmdestinationprocesspdf?language=objc +https://developer.apple.com/documentation/applicationservices/1461960-pmcreategenericprinter?language=objc +https://developer.apple.com/documentation/applicationservices/1460101-pmsessionbegincgdocumentnodialog?language=objc +https://developer.apple.com/documentation/applicationservices/1464527-pmsessionenddocumentnodialog?language=objc +https://developer.apple.com/documentation/applicationservices/1461952-pmsessiongetcggraphicscontext?language=objc +https://developer.apple.com/library/content/technotes/tn2248/_index.html +https://developer.apple.com/library/content/samplecode/PMPrinterPrintWithFile/Introduction/Intro.html +https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Printing/osxp_aboutprinting/osxp_aboutprt.html + +- run os x code with `OBJC_DEBUG_MISSING_POOLS=YES` and other `OBJC_HELP=YES` options + - turn off the autorelease pool to make sure we're not autoreleasing improperly From eab8a98aa9eb0928657bf47e98fe51785124ff6d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 8 Oct 2017 17:05:16 -0400 Subject: [PATCH 0793/1329] More TODOs. --- TODO.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TODO.md b/TODO.md index dee9d997..cfb31fe3 100644 --- a/TODO.md +++ b/TODO.md @@ -247,3 +247,5 @@ https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Print - run os x code with `OBJC_DEBUG_MISSING_POOLS=YES` and other `OBJC_HELP=YES` options - turn off the autorelease pool to make sure we're not autoreleasing improperly + +TODO investigate -Weverything in clang alongside -Wall in MSVC (and in gcc too maybe...) From 4d57a12dbc9678d8183ba309f3d8df8d6bb796be Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 9 Oct 2017 20:48:35 -0400 Subject: [PATCH 0794/1329] More general TODOs... I'm starting to feel like I'm over my head with text stuff, and stopping for those few months has made me forget what I wanted to do :| or maybe forget the specifics, I'm not sure what. All I know is that I accumulated more TODOs specific to this branch than I thought and now I'm just :S --- TODO.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/TODO.md b/TODO.md index cfb31fe3..7a783af9 100644 --- a/TODO.md +++ b/TODO.md @@ -249,3 +249,8 @@ https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Print - turn off the autorelease pool to make sure we're not autoreleasing improperly TODO investigate -Weverything in clang alongside -Wall in MSVC (and in gcc too maybe...) + +mac os x accessibility +- https://developer.apple.com/documentation/appkit/nsworkspace/1524656-accessibilitydisplayshoulddiffer?language=objc +- https://developer.apple.com/documentation/appkit/nsworkspace/1526290-accessibilitydisplayshouldincrea?language=objc +- https://developer.apple.com/documentation/appkit/nsworkspace/1533006-accessibilitydisplayshouldreduce?language=objc From 9f1e1b25be2f417dbb50074a9005d15b0e506b38 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 17 Oct 2017 23:40:07 -0400 Subject: [PATCH 0795/1329] Started converting the code in Core Text itself that determines what weight a font has into pseudocode. --- doc/export/ctweights | 218 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 218 insertions(+) create mode 100644 doc/export/ctweights diff --git a/doc/export/ctweights b/doc/export/ctweights new file mode 100644 index 00000000..31508150 --- /dev/null +++ b/doc/export/ctweights @@ -0,0 +1,218 @@ +// pseudo-go + +func (f *CTFont) IsRegistered() bool { + n := f.Attribute(kCTFontRegistrationScopeAttribute) + if n == nil { + return false + } + return n.(*CFNumber).Uint32Value() == kCTFontManagerScopeNone +} + +// based on libFontRegistry.dylib's __ZN11TFontTraitsC2EP6CGFontRK13TFontMetadata — TFontTraits::TFontTraits(CGFont*, TFontMetadata const&) +func (f *Font) WeightFromFontRegistry32() float32 { + var weight float32 + + cgfont := f.CGFont() +} + +// because Core Text gets registry traits as a CFDictionary, convert the float to a double with CFNumber as that is what actually would be done +func (f *Font) WeightFromFontRegistry() float64 { + return CFNumberWithFloat32(f.WeightFromFontRegistry32()).Float64Value() +} + +// based on CoreText dylib's __Z13WeightOfClasst — WeightOfClass(unsigned short) +func CoreText_WeightOfClass(usWeightClass uint16) float64 { + if usWeightClass >= 11 { + // do nothing; we are preserving the original asm comparisons + } else { + usWeightClass *= 100 + } + + // figure out what two floats our weight will be between + i := usWeightClass / 100 + j := i + 1 + if j > 10 { + j = 10 + } + b := float64(i * 100) + c := float64(j * 100) + + a := float64(0) + if b != c { + a = float64(usWeightClass) + a -= b + c -= b + a /= c + } + scales := []float32{ + float32as(-1.000000, 0xbf800000), + float32as(-0.700000, 0xbf333333), + float32as(-0.500000, 0xbf000000), + float32as(-0.230000, 0xbe6b851f), + float32as(0.000000, 0x0), + float32as(0.200000, 0x3e4ccccd), + float32as(0.300000, 0x3e99999a), + float32as(0.400000, 0x3ecccccd), + float32as(0.600000, 0x3f19999a), + float32as(0.800000, 0x3f4ccccd), + float32as(1.000000, 0x3f800000), + } + c = float64(scale[i]) + b = float64[scale[j]) + return fma(a, b, c) +} + +// based on CoreText dylib's __ZL33CreateTraitsByStyleGlossaryStringPK10__CFString — CreateTraitsByStyleGlossaryString(__CFString const*) +func CoreText_WeightByStyleGlossaryString(str string) (weight float64, ok bool) { + str.Fold(kCFCompareCaseInsensitive, nil) + + var weightNameMap = []struct { + key string + val float32 + }{ + { "ultra light", float32as(-0.800000, 0xbf4ccccd) }, + { "ultra black", float32as(0.750000, 0x3f400000) }, + { "extra light", float32as(-0.500000, 0xbf000000) }, + { "ultralight", float32as(-0.800000, 0xbf4ccccd) }, + { "ultrablack", float32as(0.750000, 0x3f400000) }, + { "extrablack", float32as(0.800000, 0x3f4ccccd) }, + { "extralight", float32as(-0.500000, 0xbf000000) } + { "heavy face", float32as(0.560000, 0x3f0f5c29) }, + { "semi light", float32as(-0.200000, 0xbe4ccccd) }, + { "extra bold", float32as(0.500000, 0x3f000000) }, + { "ultra bold", float32as(0.700000, 0x3f333333) }, + { "heavyface", float32as(0.560000, 0x3f0f5c29) }, + { "extrabold", float32as(0.500000, 0x3f000000) }, + { "ultrabold", float32as(0.700000, 0x3f333333) }, + { "semilight", float32as(-0.200000, 0xbe4ccccd) }, + { "demi bold", float32as(0.250000, 0x3e800000) }, + { "semi bold", float32as(0.300000, 0x3e99999a) }, + { "demibold", float32as(0.250000, 0x3e800000) }, + { "semibold", float32as(0.300000, 0x3e99999a) }, + { "hairline", float32as(-0.700000, 0xbf333333) }, + { "medium", float32as(0.230000, 0x3e6b851f) }, + { "poster", float32as(0.800000, 0x3f4ccccd) }, + { "light", float32as(-0.400000, 0xbecccccd) }, + { "heavy", float32as(0.560000, 0x3f0f5c29) }, + { "extra", float32as(0.500000, 0x3f000000) }, + { "black", float32as(0.620000, 0x3f1eb852) }, + { "super", float32as(0.620000, 0x3f1eb852) }, + { "obese", float32as(0.850000, 0x3f59999a) }, + { "lite", float32as(-0.400000, 0xbecccccd) }, + { "book", float32as(-0.230000, 0xbe6b851f) }, + { "demi", float32as(0.250000, 0x3e800000) }, + { "semi", float32as(0.300000, 0x3e99999a) }, + { "thin", float32as(-0.500000, 0xbf000000) }, + { "bold", float32as(0.400000, 0x3ecccccd) }, + { "nord", float32as(0.800000, 0x3f4ccccd) }, + { "fat", float32as(0.750000, 0x3f400000) }, + { "w1", float32as(-0.700000, 0xbf333333) }, + { "w2", float32as(-0.500000, 0xbf000000) }, + { "w3", float32as(-0.230000, 0xbe6b851f) }, + { "w4", float32as(0.000000, 0x0) }, + { "w5", float32as(0.230000, 0x3e6b851f) }, + { "w6", float32as(0.300000, 0x3e99999a) }, + { "w7", float32as(0.440000, 0x3ee147ae) }, + { "w8", float32as(0.540000, 0x3f0a3d71) }, + { "w9", float32as(0.620000, 0x3f1eb852) }, + } + for _, m := range weightNameMap { + if strstr(str, m.key) != nil { + val := CFNumberWithFloat32(m.val) + return val.Float64Value(), true + } + } + return 0, false +} + +// based on CoreText dylib's __ZNK9TBaseFont29CreateTraitsValuesPerFontInfoEP12MetadataFlag — TBaseFont::CreateTraitsValuesPerFontInfo(MetadataFlag*) const +func (f *CTFont) Weight() float64 { + if f.IsRegistered() { + return f.WeightFromFontRegistry() + } + + weight := float64as(2.0, 0x4000000000000000) + ebx := -1 + hasWeight := false + + name := f.Name(kCTFontPostScriptNameKey) + if name != nil { + switch *name { + case "LucidaGrande": + weight = float64as(0.000000, 0x0) + hasWeight = true + case ".LucidaGrandeUI": + weight = float64as(0.000000, 0x0) + hasWeight = true + case "STHeiti": + weight = float64as(0.240000, 0x3fceb851eb851eb8) + hasWeight = true + case "STXihei": + weight = float64as(-0.100000, 0xbfb999999999999a) + hasWeight = true + case "TimesNewRomanPSMT": + weight = float64as(0.000000, 0x0) + hasWeight = true + // there is one more hardcoded case, for "Times-Roman", but that will only set the class style, not the weight + } + } + + os2table := f.Table('OS/2') + if os2table != nil { + if !hasWeight { + var usWeightClass uint16 + + valid := false + if os2table.Len() > 77 { + b := os2table.Bytes() + usWeightClass = uint16be(b[4:6]) + if usWeightClass > 1000 { + weight = 0 + hasWeight = false + } else { + valid = true + } + } else { + usWeightClass = 0 + valid = true + } + if valid { + weight = CoreText_WeightOfClass(usWeightClass) + hasWeight = true + } + } + } + + styleGlossaryNames := []string{ + kCTFontSubFamilyNameKey, + kCTFontFullNameKey, + kCTFontFamilyNameKey, + } + for _, key := range styleGlossaryNames { + name := f.Name(key) + if name == nil { + continue + } + candidate, ok := CoreText_WeightByStyleGlossaryString(*name) + if !ok { + continue + } + if !hasWeight { + weight = candidate + hasWeight = true + } + } + + if hasWeight { + return weight + } + return 0 +} + +func (f *Font) ShouldEnableBoldSymbolicTrait() bool { + if f.IsRegistered() { + return f.ShouldEnableBoldSymbolicTraitFromRegistry() + } + no := f.Weight() <= float64as(0.239000, 0x3fce978d4fdf3b64) + return !no +} From 683bd47491a4444267d5d7d0ddad5c907ab85139 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 18 Oct 2017 23:59:54 -0400 Subject: [PATCH 0796/1329] More ctweights stuff. --- doc/export/ctweights | 68 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/doc/export/ctweights b/doc/export/ctweights index 31508150..4cf2839d 100644 --- a/doc/export/ctweights +++ b/doc/export/ctweights @@ -8,11 +8,79 @@ func (f *CTFont) IsRegistered() bool { return n.(*CFNumber).Uint32Value() == kCTFontManagerScopeNone } +// based on CoreGraphics dylib's _CGFontCopyName +// note that this is different from the public API function CGFontCopyPostScriptName() (which is font type-independent) +// also note that in reality these keys are strings but the implementation of the function turns them into ints and only uses them as such +const ( + kCGFontNameKeyPostScriptName = xxx +) +func (f *CGFont) CopyName(key int) (string, bool) { +} + // based on libFontRegistry.dylib's __ZN11TFontTraitsC2EP6CGFontRK13TFontMetadata — TFontTraits::TFontTraits(CGFont*, TFontMetadata const&) func (f *Font) WeightFromFontRegistry32() float32 { var weight float32 + var hasWeight bool = false cgfont := f.CGFont() + if f.RegistryHasMetadata() { + wv := f.RegistryMetadataValueForKey("MTD_Typeface_Weight_VisualDescriptor") + if wv != nil { + if wn, ok := wv.(string); ok { + // note: uses CFStringCompare(0) + switch wn { + case "reg": + weight = float32as(0.000000, 0x0) + hasWeight = true + case "semi": + weight = float32as(0.300000, 0x3e99999a) + hasWeight = true + case "bold": + weight = float32as(0.400000, 0x3ecccccd) + hasWeight = true + case "light": + weight = float32as(-0.400000, 0xbecccccd) + hasWeight = true + case "med": + weight = float32as(0.230000, 0x3e6b851f) + hasWeight = true + case "heavy": + weight = float32as(0.560000, 0x3f0f5c29) + hasWeight = true + case "black": + weight = float32as(0.620000, 0x3f1eb852) + hasWeight = true + case "thin": + weight = float32as(-0.600000, 0xbf19999a) + hasWeight = true + case "ulight": + weight = float32as(-0.800000, 0xbf4ccccd) + hasWeight = true + } + } + } + } + + cgpsname, ok := cgfont.CopyName(kCGFontNameKeyPostScriptName) + if ok { + // note: uses CFStringCompare(0) + switch cgpsname { + case "LucidaGrande", + ".LucidaGrandeUI", + ".Keyboard": + weight = float32as(0.000000, 0x0) + hasWeight = true + case "STHeiti": + weight = float32as(0.240000, 0x3e75c28f) + hasWeight = true + case "STXihei": + weight = float32as(-0.100000, 0xbdcccccd) + hasWeight = true + case "TimesNewRomanPSMT": + weight = float32as(0.000000, 0x0) + hasWeight = true + } + } } // because Core Text gets registry traits as a CFDictionary, convert the float to a double with CFNumber as that is what actually would be done From f94de2eef850086bba72667e23890675fb7cca75 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 19 Oct 2017 19:15:46 -0400 Subject: [PATCH 0797/1329] And finally finished the core Core Text weight determination functionality pseudo-Go-code. God damn. And I have a funny feeling stretches are going to be a tad bit more inconsistent too... :| Not quite complete, though; need to fill in the name table parsing rules first. --- doc/export/ctweights | 263 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 261 insertions(+), 2 deletions(-) diff --git a/doc/export/ctweights b/doc/export/ctweights index 4cf2839d..4d25cc5f 100644 --- a/doc/export/ctweights +++ b/doc/export/ctweights @@ -13,8 +13,125 @@ func (f *CTFont) IsRegistered() bool { // also note that in reality these keys are strings but the implementation of the function turns them into ints and only uses them as such const ( kCGFontNameKeyPostScriptName = xxx + kCGFontNameKeyPreferredSubfamily = xxx + kCGFontNameKeyFontSubfamily = xxx + kCGFontNameKeyFullName = xxx + kCGFontNameKeyPreferredFamily = xxx + kCGFontNameKeyFontFamily = xxx ) func (f *CGFont) CopyName(key int) (string, bool) { + // TODO +} + +// based on libFontRegistry.dylib's __ZNK8OS2Table15DetermineWeightERf — OS2Table::DetermineWeight(float&) const +func RegistryDetermineOS2Weight(table *CFData) (float32, bool) { + if table == nil { + return 0, false + } + if table.Len() < 78 { + return 0, false + } + + b := table.Bytes() + usWeightClass := uint16be(b[4:6]) + if usWeightClass >= 10 { + // do nothing; we are preserving the original asm comparisons + } else { + usWeightClass *= 100 + } + /* TODO: +000000000000b37e mov dx, word [rax+4] +000000000000b382 mov cx, dx +000000000000b385 rol cx, 0x8 +000000000000b389 movzx esi, cx +000000000000b38c imul ecx, ecx, 100 +000000000000b38f cmp esi, 10 +000000000000b392 cmovae cx, si +000000000000b396 test dx, dx +000000000000b399 cmove cx, si + what's the function of the last two instructions? */ + + // note that this is an unsigned comparison, so underflow will result in a number > 998 + // the effect is the same as (usWeightClass == 0) || (usWeightClass >= 1000) + if (usWeightClass - 1) > 998 { + // note the - 2 here; the switch cases below reflect that! + // also note that b[0x22] and panose will be unsigned, so underflow will result in a number > 9 + panose := b[0x22] - 2 + if panose > 9 { + return 0, false + } + switch panose { + case 0: + return float32as(-0.500000, 0xbf000000), true + case 1: + return float32as(-0.400000, 0xbecccccd), true + case 2: + // yes, this returns false; I don't know why + return float32as(-0.300000, 0xbe99999a), false + case 3: + return float32as(-0.230000, 0xbe6b851f), true + case 4: + return float32as(0.230000, 0x3e6b851f), true + case 5: + return float32as(0.250000, 0x3e800000), true + case 6: + return float32as(0.400000, 0x3ecccccd), true + case 7: + return float32as(0.560000, 0x3f0f5c29), true + case 8: + return float32as(0.620000, 0x3f1eb852), true + case 9: + return float32as(0.800000, 0x3f4ccccd), true + } + // should not reach here + } + + // let's mimic the assembly here too + // the gotos avoid the massive if nesting + // also note I'm using Go idioms and not saying "else return", imagine those if you must + if usWeightClass > 100 { + if usWeightClass > 200 { + goto do201AndUp + } + return float32as(-0.500000, 0xbf000000), true + } + return float32as(-0.800000, 0xbf4ccccd), true + +do201AndUp: + if usWeightClass > 300 { + if usWeightClass > 400 { + goto do401AndUp + } + return float32as(0.000000, 0x0), true + } + return float32as(-0.400000, 0xbecccccd), true + +do401AndUp: + if usWeightClass > 500 { + if usWeightClass > 600 { + goto do601AndUp + } + return float32as(0.250000, 0x3e800000), true + } + return float32as(0.230000, 0x3e6b851), true + +do601AndUp: + if usWeightClass > 700 { + if usWeightClass > 800 { + goto do801AndUp + } + return float32as(0.500000, 0x3f000000), true + } + return float32as(0.400000, 0x3ecccccd), true + +do801AndUp: + if usWeightClass > 900 { + if usWeightClass > 950 { + return float32(0.800000, 0x3f4ccccd), true + } + return float32(0.750000, 0x3f400000), true + } + return float32as(0.620000, 0x3f1eb852), true } // based on libFontRegistry.dylib's __ZN11TFontTraitsC2EP6CGFontRK13TFontMetadata — TFontTraits::TFontTraits(CGFont*, TFontMetadata const&) @@ -81,6 +198,148 @@ func (f *Font) WeightFromFontRegistry32() float32 { hasWeight = true } } + + styleGlossaryStrings := []int{ + kCGFontNameKeyPreferredSubfamily, + kCGFontNameKeyFontSubfamily, + kCGFontNameKeyFullName, + kCGFontNameKeyPreferredFamily, + kCGFontNameKeyFontFamily, + } + weightNameMap := []struct { + key string + val float32 + }{ + { "Ultra Light", float32as(-0.800000f, 0xbf4ccccd) }, + { "Ultra Black", float32as(0.750000f, 0x3f400000) }, + { "Extra Light", float32as(-0.500000f, 0xbf000000) }, + { "UltraBlack", float32as(0.750000f, 0x3f400000) }, + { "ExtraBlack", float32as(0.800000f, 0x3f4ccccd) }, + { "UltraLight", float32as(-0.800000f, 0xbf4ccccd) }, + { "ExtraLight", float32as(-0.500000f, 0xbf000000) }, + { "Ultra Thin", float32as(-0.800000f, 0xbf4ccccd) }, + { "Extra Thin", float32as(-0.800000f, 0xbf4ccccd) }, + { "Heavy Face", float32as(0.560000f, 0x3f0f5c29) }, + { "Semi Light", float32as(-0.200000f, 0xbe4ccccd) }, + { "Extra Bold", float32as(0.500000f, 0x3f000000) }, + { "Ultra Bold", float32as(0.700000f, 0x3f333333) }, + { "HeavyFace", float32as(0.560000f, 0x3f0f5c29) }, + { "ExtraBold", float32as(0.500000f, 0x3f000000) }, + { "UltraBold", float32as(0.700000f, 0x3f333333) }, + { "Ext Black", float32as(0.800000f, 0x3f4ccccd) }, + { "SemiLight", float32as(-0.200000f, 0xbe4ccccd) }, + { "Demi Bold", float32as(0.250000f, 0x3e800000) }, + { "Semi Bold", float32as(0.300000f, 0x3e99999a) }, + { "Ext Light", float32as(-0.500000f, 0xbf000000) }, + { "Ext Bold", float32as(0.500000f, 0x3f000000) }, + { "DemiBold", float32as(0.250000f, 0x3e800000) }, + { "SemiBold", float32as(0.300000f, 0x3e99999a) }, + { "HairLine", float32as(-0.800000f, 0xbf4ccccd) }, + { "Ext Thin", float32as(-0.800000f, 0xbf4ccccd) }, + { "Medium", float32as(0.230000f, 0x3e6b851f) }, + { "Poster", float32as(0.800000f, 0x3f4ccccd) }, + { "Light", float32as(-0.400000f, 0xbecccccd) }, + { "Ultra", float32as(0.500000f, 0x3f000000) }, + { "Heavy", float32as(0.560000f, 0x3f0f5c29) }, + { "Extra", float32as(0.500000f, 0x3f000000) }, + { "Black", float32as(0.620000f, 0x3f1eb852) }, + { "Super", float32as(0.620000f, 0x3f1eb852) }, + { "Obese", float32as(0.850000f, 0x3f59999a) }, + { "Lite", float32as(-0.400000f, 0xbecccccd) }, + { "Book", float32as(-0.230000f, 0xbe6b851f) }, + { "Demi", float32as(0.250000f, 0x3e800000) }, + { "Semi", float32as(0.300000f, 0x3e99999a) }, + { "Thin", float32as(-0.500000f, 0xbf000000) }, + { "Bold", float32as(0.400000f, 0x3ecccccd) }, + { "Nord", float32as(0.800000f, 0x3f4ccccd) }, + { "Fat", float32as(0.750000f, 0x3f400000) }, + { "W1", float32as(-0.230000f, 0xbe6b851f) }, + { "W2", float32as(-0.500000f, 0xbf000000) }, + { "W3", float32as(-0.230000f, 0xbe6b851f) }, + { "W4", float32as(0.000000f, 0x0) }, + { "W5", float32as(0.230000f, 0x3e6b851f) }, + { "W6", float32as(0.300000f, 0x3e99999a) }, + { "W7", float32as(0.440000f, 0x3ee147ae) }, + { "W8", float32as(0.540000f, 0x3f0a3d71) }, + { "W9", float32as(0.620000f, 0x3f1eb852) }, + } + for _, key := range styleGlossaryStrings { + if hasWeight { + break + } + str, ok := cgfont.CopyName(key) + if !ok { + continue + } + for _, m := range weightNameMap { + if str.FindWithOptions(m.key, CFRangeMake(0, str.CFStringLength()), kCFCompareCaseInsensitive | kCFCompareBackwards | kCFCompareNonliteral, nil) { + weight = m.val + hasWeight = true + break + } + } + } + + if !hasWeight { + os2table := cgfont.TableForTag('OS/2') + weight, hasWeight = RegistryDetermineOS2Weight(os2table) + } + + if !hasWeight { + headtable := cgfont.TableForTag('head') + if headtable != nil { + if headtable.Len() >= 54 { + b := headtable.Bytes() + if (b[0x2d] & 1) != 0 { + weight = float32as(0.400000, 0x3ecccccd) + hasWeight = true + } + } + } + } + + styleGlossaryAbbreviationKeys := []int{ + kCGFontNameKeyPreferredSubfamily, + kCGFontNameKeyFontSubfamily, + } + abbreviatedWeightNameMap := []struct { + key string + val float32 + }{ + { "EL", float32as(-0.200000, 0xbe4ccccd) }, + { "EB", float32as(0.500000, 0x3f000000) }, + { "SB", float32as(0.300000, 0x3e99999a) }, + { "UH", float32as(0.800000, 0x3f4ccccd) }, + { "U", float32as(0.700000, 0x3f333333) }, + { "L", float32as(-0.400000, 0xbecccccd) }, + { "H", float32as(0.560000, 0x3f0f5c29) }, + { "B", float32as(0.400000, 0x3ecccccd) }, + { "M", float32as(0.230000, 0x3e6b851f) }, + { "R", float32as(0.000000, 0x0) }, + } + if !hasWeight { + for _, key := range styleGlossaryAbbreviationStrings { + str, ok := cgfont.CopyName(key) + if !ok { + continue + } + for _, m := range abbreviatedWeightNameMap { + if str.Compare(m.key, kCFCompareCaseInsensitive) == kCFCompareEqualTo { + weight = m.val + hasWeight = true + break + } + } + if hasWeight { + break + } + } + } + + if !hasWeight { + return float32as(0.000000, 0x0) + } + return weight } // because Core Text gets registry traits as a CFDictionary, convert the float to a double with CFNumber as that is what actually would be done @@ -92,6 +351,7 @@ func (f *Font) WeightFromFontRegistry() float64 { func CoreText_WeightOfClass(usWeightClass uint16) float64 { if usWeightClass >= 11 { // do nothing; we are preserving the original asm comparisons + // and yes, this one is 11, but the one above is 10 } else { usWeightClass *= 100 } @@ -133,8 +393,7 @@ func CoreText_WeightOfClass(usWeightClass uint16) float64 { // based on CoreText dylib's __ZL33CreateTraitsByStyleGlossaryStringPK10__CFString — CreateTraitsByStyleGlossaryString(__CFString const*) func CoreText_WeightByStyleGlossaryString(str string) (weight float64, ok bool) { str.Fold(kCFCompareCaseInsensitive, nil) - - var weightNameMap = []struct { + weightNameMap := []struct { key string val float32 }{ From 9731d2e836ce866c339bfa7e94c951eabe6ebf68 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 19 Oct 2017 23:54:46 -0400 Subject: [PATCH 0798/1329] More work on ctweights. --- doc/export/ctweights | 42 +++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/doc/export/ctweights b/doc/export/ctweights index 4d25cc5f..d89c0e9b 100644 --- a/doc/export/ctweights +++ b/doc/export/ctweights @@ -12,15 +12,43 @@ func (f *CTFont) IsRegistered() bool { // note that this is different from the public API function CGFontCopyPostScriptName() (which is font type-independent) // also note that in reality these keys are strings but the implementation of the function turns them into ints and only uses them as such const ( - kCGFontNameKeyPostScriptName = xxx - kCGFontNameKeyPreferredSubfamily = xxx - kCGFontNameKeyFontSubfamily = xxx - kCGFontNameKeyFullName = xxx - kCGFontNameKeyPreferredFamily = xxx - kCGFontNameKeyFontFamily = xxx + kCGFontNameKeyPostScriptName = 0x6 + kCGFontNameKeyPreferredSubfamily = 0x11 + kCGFontNameKeyFontSubfamily = 0x2 + kCGFontNameKeyFullName = 0x4 + kCGFontNameKeyPreferredFamily = 0x10 + kCGFontNameKeyFontFamily = 0x1 ) func (f *CGFont) CopyName(key int) (string, bool) { - // TODO + var nameCount int + var stringOffset int + + table := f.TableForTag('name') + b := table.Bytes() + n := table.Len() + + // the asm does some obtuse logic to get to these conditions, so I will simplify the logic here + if n >= 4 { + nameCount = int(uint16be(b[2:4])) + } else { + nameCount = 0 + } + if n >= 6 { + stringOffset = int(uint16be(b[4:6])) + } else { + stringOffset = 0 + } + + type NameRecord struct { + PlatformID uint16 + PlatformSpecificID uint16 + LanguageID uint16 + NameID uint16 + Length uint16 + Offset uint16 + } + + pos := x } // based on libFontRegistry.dylib's __ZNK8OS2Table15DetermineWeightERf — OS2Table::DetermineWeight(float&) const From 9b28bd5ecd0bd1f9251d05b3a31f7c0adde69765 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 20 Oct 2017 14:14:32 -0400 Subject: [PATCH 0799/1329] More name table work. --- doc/export/ctweights | 152 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 139 insertions(+), 13 deletions(-) diff --git a/doc/export/ctweights b/doc/export/ctweights index d89c0e9b..7d50567b 100644 --- a/doc/export/ctweights +++ b/doc/export/ctweights @@ -8,6 +8,42 @@ func (f *CTFont) IsRegistered() bool { return n.(*CFNumber).Uint32Value() == kCTFontManagerScopeNone } +xx this type is in libFontRegistry.dylib; functions like x_list.Prepend() are called things like x_list_prepend() there +type x_list struct { + Data interface{} + Next *x_list +} + +func (x *x_list) Prepend(data interface{}) *x_list { + y := malloc(sizeof (x_list)) + if y != nil { + y.data = data + y.next = x + return y + } + return x +} + +func (x *x_list) Reverse() *x_list { + if x == nil { + return nil + } + + var old, next *x_list + + next = nil + for { + old = x + x = old.next + old.next = next + next = old + if x == nil { + break + } + } + return old +} + // based on CoreGraphics dylib's _CGFontCopyName // note that this is different from the public API function CGFontCopyPostScriptName() (which is font type-independent) // also note that in reality these keys are strings but the implementation of the function turns them into ints and only uses them as such @@ -20,23 +56,27 @@ const ( kCGFontNameKeyFontFamily = 0x1 ) func (f *CGFont) CopyName(key int) (string, bool) { - var nameCount int - var stringOffset int - table := f.TableForTag('name') b := table.Bytes() n := table.Len() - // the asm does some obtuse logic to get to these conditions, so I will simplify the logic here - if n >= 4 { - nameCount = int(uint16be(b[2:4])) - } else { - nameCount = 0 + xx this code looks weird, but we're imitating the assembly, or the effective effects thereof + offCount := uint16(0) + offStringOffset := uint16(2) + if n > 1 { + offCount = 2 + offStringOffset = 4 } - if n >= 6 { - stringOffset = int(uint16be(b[4:6])) - } else { - stringOffset = 0 + + count := uint16(0) + if int(offCount) <= n { + count = uint16be(b[offCount:offCount + 2]) + } + + offNameRecord := offStringOffset + 2 + stringOffset := uint16(0) + if int(offNameRecord) <= n { + stringOffset = uint16be(b[offStringOffset:offStringOffset + 2]) } type NameRecord struct { @@ -48,7 +88,93 @@ func (f *CGFont) CopyName(key int) (string, bool) { Offset uint16 } - pos := x + var nameList *x_list + + addrStrings := offNameRecords + (12 * count) + if addrStrings != stringOffset { + goto hasLanguageTags + } + pos := offNameRecords + if count == 0 { + xx TODO note assembly logic here + } else { + for { + var nr NameRecord + + nr.PlatformID = 0 + next := pos + 2 + if int(next) <= n { + nr.PlatformID = uint16be(b[pos:pos + 2]) + pos = next + } + + nr.PlatformSpecificID = 0 + next = pos + 2 + if int(next) <= n { + nr.PlatformSpecificID = uint16be(b[pos:pos + 2]) + pos = next + } + + nr.LanguageID = 0 + next = pos + 2 + if int(next) <= n { + nr.LanguageID = uint16be(b[pos:pos + 2]) + pos = next + } + + nr.NameID = 0 + next = pos + 2 + if int(next) <= n { + nr.NameID = uint16be(b[pos:pos + 2]) + pos = next + } + + nr.Length = 0 + next = pos + 2 + if int(next) <= n { + nr.Length = uint16be(b[pos:pos + 2]) + pos = next + } + + nr.Offset = 0 + next = pos + 2 + if int(next) <= n { + nr.Offset = uint16be(b[pos:pos + 2]) + pos = next + } + + strpos := stringOffset + nr.Offset + if strpos >= n { + xx TODO put comment about imitating the assembly comparisons here + } else { + realLen := nr.Length + strend = strpos + nr.Length + if strend > n { + realLen = nr.Length - strpos + strend = strpos + realLen + } + b := malloc(12 + realLen + 1) + if b != nil { + name := (*sfnt_name_t)(b) + name.PlatformID = nr.PlatformID + name.PlatformSpecificID = nr.PlatformSpecificID + name.LanguageID = nr.LanguageID + name.NameID = nr.NameID + name.Length = realLen + memcpy(&(name.Name), b[strpos:strend], realLen) + name.Name[realLen] = 0 + nameList = nameList.Prepend(name) + } + } + count-- + if count == 0 { + break + } + } + } + nameList = nameList.Reverse() + +hasLanguageTags: } // based on libFontRegistry.dylib's __ZNK8OS2Table15DetermineWeightERf — OS2Table::DetermineWeight(float&) const From 2858c56528933df84fdc0548c8bffcc324e55c11 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 21 Oct 2017 02:54:39 -0400 Subject: [PATCH 0800/1329] CGFontCopyName() is too intricate to recreate. We might have to wind up calling it directly... --- doc/export/ctweights | 59 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 4 deletions(-) diff --git a/doc/export/ctweights b/doc/export/ctweights index 7d50567b..f9feb90b 100644 --- a/doc/export/ctweights +++ b/doc/export/ctweights @@ -8,7 +8,7 @@ func (f *CTFont) IsRegistered() bool { return n.(*CFNumber).Uint32Value() == kCTFontManagerScopeNone } -xx this type is in libFontRegistry.dylib; functions like x_list.Prepend() are called things like x_list_prepend() there +// this type is in libFontRegistry.dylib; functions like x_list.Prepend() are called things like x_list_prepend() there type x_list struct { Data interface{} Next *x_list @@ -44,6 +44,23 @@ func (x *x_list) Reverse() *x_list { return old } +func (x *x_list) Concat(y *x_list) *x_list { + if x == nil { + return y + } + start := x + z := x + for { + x = z + z = z.next + if z == nil { + break + } + } + x.next = y + return start +} + // based on CoreGraphics dylib's _CGFontCopyName // note that this is different from the public API function CGFontCopyPostScriptName() (which is font type-independent) // also note that in reality these keys are strings but the implementation of the function turns them into ints and only uses them as such @@ -60,7 +77,7 @@ func (f *CGFont) CopyName(key int) (string, bool) { b := table.Bytes() n := table.Len() - xx this code looks weird, but we're imitating the assembly, or the effective effects thereof + // this code looks weird, but we're imitating the assembly, or the effective effects thereof offCount := uint16(0) offStringOffset := uint16(2) if n > 1 { @@ -96,7 +113,7 @@ func (f *CGFont) CopyName(key int) (string, bool) { } pos := offNameRecords if count == 0 { - xx TODO note assembly logic here + // TODO note assembly logic here } else { for { var nr NameRecord @@ -145,7 +162,7 @@ func (f *CGFont) CopyName(key int) (string, bool) { strpos := stringOffset + nr.Offset if strpos >= n { - xx TODO put comment about imitating the assembly comparisons here + // TODO put comment about imitating the assembly comparisons here } else { realLen := nr.Length strend = strpos + nr.Length @@ -175,6 +192,40 @@ func (f *CGFont) CopyName(key int) (string, bool) { nameList = nameList.Reverse() hasLanguageTags: + add_localized_names := func(platformID uint16, platformSpecificID uint16, to *x_list) *x_list { + out := (*x_list)(nil) + if nameList == nil { + xx TODO logic verbatim etc. + } else { + x := nameList + for { + name := (*sfnt_name_t)(x.data) + if name.PlatformID != platformID { + xx TODO + } else { + if platformSpecificID == 0xFFFF || name.PlatformSpecificID == platformSpecificID { + out = out.Prepend(name) + } + } + x = x.next + if x == nil { + break + } + } + } + out = out.Reverse() + return to.Concat(out) + } + localized := (*x_list)(nil) + localized = add_localized_names(0x1, 0xFFFF, localized) + localized = add_localized_names(0, 0xFFFF, localized) + localized = add_localized_names(0x3, 0xFFFF, localized) + localized = add_localized_names(0x1, 0, localized) + localized = add_localized_names(0x3, 0x9, localized) + localized = add_localized_names(0x3, 0x409, localized) + + sysLocale := CFLocaleGetSystem() + } // based on libFontRegistry.dylib's __ZNK8OS2Table15DetermineWeightERf — OS2Table::DetermineWeight(float&) const From 3b316ddb5bdf932487277c9da8f1a958a0b868dc Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 21 Oct 2017 12:07:07 -0400 Subject: [PATCH 0801/1329] Sorted all the possible Core Text weight values into lists. Now to process those lists. --- doc/export/ctweightvalues | 247 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 247 insertions(+) create mode 100644 doc/export/ctweightvalues diff --git a/doc/export/ctweightvalues b/doc/export/ctweightvalues new file mode 100644 index 00000000..ee7f17dc --- /dev/null +++ b/doc/export/ctweightvalues @@ -0,0 +1,247 @@ +registered font, preexisting metadata weight +"reg": float32as(0.000000, 0x0) +"semi": float32as(0.300000, 0x3e99999a) +"bold": float32as(0.400000, 0x3ecccccd) +"light": float32as(-0.400000, 0xbecccccd) +"med": float32as(0.230000, 0x3e6b851f) +"heavy": float32as(0.560000, 0x3f0f5c29) +"black": float32as(0.620000, 0x3f1eb852) +"thin": float32as(-0.600000, 0xbf19999a) +"ulight": float32as(-0.800000, 0xbf4ccccd) + +registered font, postscript name (probably only for TrueType and OpenType) special cases, overrides the above +"LucidaGrande": float32as(0.000000, 0x0) +".LucidaGrandeUI": float32as(0.000000, 0x0) +".Keyboard": float32as(0.000000, 0x0) +"STHeiti": float32as(0.240000, 0x3e75c28f) +"STXihei": float32as(-0.100000, 0xbdcccccd) +"TimesNewRomanPSMT": float32as(0.000000, 0x0) + +registered font, style glossary strings, tried in this order (possibly TrueType and OpenType only): preferred subfamily, subfamily, full name, preferred family, family; case-insensitive search, reverse search, "nonliteral" (kCFCompareNonliteral) +"Ultra Light": float32as(-0.800000, 0xbf4ccccd) +"Ultra Black": float32as(0.750000, 0x3f400000) +"Extra Light": float32as(-0.500000, 0xbf000000) +"UltraBlack": float32as(0.750000, 0x3f400000) +"ExtraBlack": float32as(0.800000, 0x3f4ccccd) +"UltraLight": float32as(-0.800000, 0xbf4ccccd) +"ExtraLight": float32as(-0.500000, 0xbf000000) +"Ultra Thin": float32as(-0.800000, 0xbf4ccccd) +"Extra Thin": float32as(-0.800000, 0xbf4ccccd) +"Heavy Face": float32as(0.560000, 0x3f0f5c29) +"Semi Light": float32as(-0.200000, 0xbe4ccccd) +"Extra Bold": float32as(0.500000, 0x3f000000) +"Ultra Bold": float32as(0.700000, 0x3f333333) +"HeavyFace": float32as(0.560000, 0x3f0f5c29) +"ExtraBold": float32as(0.500000, 0x3f000000) +"UltraBold": float32as(0.700000, 0x3f333333) +"Ext Black": float32as(0.800000, 0x3f4ccccd) +"SemiLight": float32as(-0.200000, 0xbe4ccccd) +"Demi Bold": float32as(0.250000, 0x3e800000) +"Semi Bold": float32as(0.300000, 0x3e99999a) +"Ext Light": float32as(-0.500000, 0xbf000000) +"Ext Bold": float32as(0.500000, 0x3f000000) +"DemiBold": float32as(0.250000, 0x3e800000) +"SemiBold": float32as(0.300000, 0x3e99999a) +"HairLine": float32as(-0.800000, 0xbf4ccccd) +"Ext Thin": float32as(-0.800000, 0xbf4ccccd) +"Medium": float32as(0.230000, 0x3e6b851f) +"Poster": float32as(0.800000, 0x3f4ccccd) +"Light": float32as(-0.400000, 0xbecccccd) +"Ultra": float32as(0.500000, 0x3f000000) +"Heavy": float32as(0.560000, 0x3f0f5c29) +"Extra": float32as(0.500000, 0x3f000000) +"Black": float32as(0.620000, 0x3f1eb852) +"Super": float32as(0.620000, 0x3f1eb852) +"Obese": float32as(0.850000, 0x3f59999a) +"Lite": float32as(-0.400000, 0xbecccccd) +"Book": float32as(-0.230000, 0xbe6b851f) +"Demi": float32as(0.250000, 0x3e800000) +"Semi": float32as(0.300000, 0x3e99999a) +"Thin": float32as(-0.500000, 0xbf000000) +"Bold": float32as(0.400000, 0x3ecccccd) +"Nord": float32as(0.800000, 0x3f4ccccd) +"Fat": float32as(0.750000, 0x3f400000) +"W1": float32as(-0.230000, 0xbe6b851f) +"W2": float32as(-0.500000, 0xbf000000) +"W3": float32as(-0.230000, 0xbe6b851f) +"W4": float32as(0.000000, 0x0) +"W5": float32as(0.230000, 0x3e6b851f) +"W6": float32as(0.300000, 0x3e99999a) +"W7": float32as(0.440000, 0x3ee147ae) +"W8": float32as(0.540000, 0x3f0a3d71) +"W9": float32as(0.620000, 0x3f1eb852) + +registered font, OS2 weights; table length >= 78 +1, 10 - 100: float32as(-0.800000, 0xbf4ccccd) +2, 101 - 200: float32as(-0.500000, 0xbf000000) +3, 201 - 300: float32as(-0.400000, 0xbecccccd) +4, 301 - 400: float32as(0.000000, 0x0) +5, 401 - 500: float32as(0.230000, 0x3e6b851) +6, 501 - 600: float32as(0.250000, 0x3e800000) +7, 601 - 700: float32as(0.400000, 0x3ecccccd) +8, 701 - 800: float32as(0.500000, 0x3f000000) +9, 801 - 900: float32as(0.620000, 0x3f1eb852) +901 - 950: float32as(0.750000, 0x3f400000) +951 - 999: float32as(0.800000, 0x3f4ccccd) +0, 1000: panose + 2: float32as(-0.500000, 0xbf000000) + 3: float32as(-0.400000, 0xbecccccd) + 4: !!!! should be float32as(-0.300000, 0xbe99999a) but is treated as invalid instead due to returning false instead of true + 5: float32as(-0.230000, 0xbe6b851f) + 6: float32as(0.230000, 0x3e6b851f) + 7: float32as(0.250000, 0x3e800000) + 8: float32as(0.400000, 0x3ecccccd) + 9: float32as(0.560000, 0x3f0f5c29) + 10: float32as(0.620000, 0x3f1eb852) + 11: float32as(0.800000, 0x3f4ccccd) + +registered font, head table, low bit of byte 0x2D +1: float32as(0.400000, 0x3ecccccd) + +registered font, abbreviated weight glossary, checks for (possibly in TrueType and OpenType only) in order: preferred subfamily, family; case-insensitive strict comparison +"EL": float32as(-0.200000, 0xbe4ccccd) +"EB": float32as(0.500000, 0x3f000000) +"SB": float32as(0.300000, 0x3e99999a) +"UH": float32as(0.800000, 0x3f4ccccd) +"U": float32as(0.700000, 0x3f333333) +"L": float32as(-0.400000, 0xbecccccd) +"H": float32as(0.560000, 0x3f0f5c29) +"B": float32as(0.400000, 0x3ecccccd) +"M": float32as(0.230000, 0x3e6b851f) +"R": float32as(0.000000, 0x0) + +registered font +default: float32as(0.000000, 0x0) + +// based on CoreText dylib's __Z13WeightOfClasst — WeightOfClass(unsigned short) +func CoreText_WeightOfClass(usWeightClass uint16) float64 { + if usWeightClass >= 11 { + // do nothing; we are preserving the original asm comparisons + // and yes, this one is 11, but the one above is 10 + } else { + usWeightClass *= 100 + } + + // figure out what two floats our weight will be between + i := usWeightClass / 100 + j := i + 1 + if j > 10 { + j = 10 + } + b := float64(i * 100) + c := float64(j * 100) + + a := float64(0) + if b != c { + a = float64(usWeightClass) + a -= b + c -= b + a /= c + } + scales := []float32{ + float32as(-1.000000, 0xbf800000), + float32as(-0.700000, 0xbf333333), + float32as(-0.500000, 0xbf000000), + float32as(-0.230000, 0xbe6b851f), + float32as(0.000000, 0x0), + float32as(0.200000, 0x3e4ccccd), + float32as(0.300000, 0x3e99999a), + float32as(0.400000, 0x3ecccccd), + float32as(0.600000, 0x3f19999a), + float32as(0.800000, 0x3f4ccccd), + float32as(1.000000, 0x3f800000), + } + c = float64(scale[i]) + b = float64[scale[j]) + return fma(a, b, c) +} + +unregistered font: kCTFontPostScriptNameKey defaults +"LucidaGrande": float64as(0.000000, 0x0) +".LucidaGrandeUI": float64as(0.000000, 0x0) +"STHeiti": float64as(0.240000, 0x3fceb851eb851eb8) +"STXihei": float64as(-0.100000, 0xbfb999999999999a) +"TimesNewRomanPSMT": float64as(0.000000, 0x0) + + + os2table := f.Table('OS/2') + if os2table != nil { + if !hasWeight { + var usWeightClass uint16 + + valid := false + if os2table.Len() > 77 { + b := os2table.Bytes() + usWeightClass = uint16be(b[4:6]) + if usWeightClass > 1000 { + weight = 0 + hasWeight = false + } else { + valid = true + } + } else { + usWeightClass = 0 + valid = true + } + if valid { + weight = CoreText_WeightOfClass(usWeightClass) + hasWeight = true + } + } + } + + +unregistered font, style glossary, checks against kCTFontSubFamilyNameKey, kCTFontFullNameKey, kCTFontFamilyNameKey; case-insensitive (folded by Unicode rules) strstr() +"ultra light": float32as(-0.800000, 0xbf4ccccd) +"ultra black": float32as(0.750000, 0x3f400000) +"extra light": float32as(-0.500000, 0xbf000000) +"ultralight": float32as(-0.800000, 0xbf4ccccd) +"ultrablack": float32as(0.750000, 0x3f400000) +"extrablack": float32as(0.800000, 0x3f4ccccd) +"extralight": float32as(-0.500000, 0xbf000000) +"heavy face": float32as(0.560000, 0x3f0f5c29) +"semi light": float32as(-0.200000, 0xbe4ccccd) +"extra bold": float32as(0.500000, 0x3f000000) +"ultra bold": float32as(0.700000, 0x3f333333) +"heavyface": float32as(0.560000, 0x3f0f5c29) +"extrabold": float32as(0.500000, 0x3f000000) +"ultrabold": float32as(0.700000, 0x3f333333) +"semilight": float32as(-0.200000, 0xbe4ccccd) +"demi bold": float32as(0.250000, 0x3e800000) +"semi bold": float32as(0.300000, 0x3e99999a) +"demibold": float32as(0.250000, 0x3e800000) +"semibold": float32as(0.300000, 0x3e99999a) +"hairline": float32as(-0.700000, 0xbf333333) +"medium": float32as(0.230000, 0x3e6b851f) +"poster": float32as(0.800000, 0x3f4ccccd) +"light": float32as(-0.400000, 0xbecccccd) +"heavy": float32as(0.560000, 0x3f0f5c29) +"extra": float32as(0.500000, 0x3f000000) +"black": float32as(0.620000, 0x3f1eb852) +"super": float32as(0.620000, 0x3f1eb852) +"obese": float32as(0.850000, 0x3f59999a) +"lite": float32as(-0.400000, 0xbecccccd) +"book": float32as(-0.230000, 0xbe6b851f) +"demi": float32as(0.250000, 0x3e800000) +"semi": float32as(0.300000, 0x3e99999a) +"thin": float32as(-0.500000, 0xbf000000) +"bold": float32as(0.400000, 0x3ecccccd) +"nord": float32as(0.800000, 0x3f4ccccd) +"fat": float32as(0.750000, 0x3f400000) +"w1": float32as(-0.700000, 0xbf333333) +"w2": float32as(-0.500000, 0xbf000000) +"w3": float32as(-0.230000, 0xbe6b851f) +"w4": float32as(0.000000, 0x0) +"w5": float32as(0.230000, 0x3e6b851f) +"w6": float32as(0.300000, 0x3e99999a) +"w7": float32as(0.440000, 0x3ee147ae) +"w8": float32as(0.540000, 0x3f0a3d71) +"w9": float32as(0.620000, 0x3f1eb852) + +func (f *Font) ShouldEnableBoldSymbolicTrait() bool { + if f.IsRegistered() { + return f.ShouldEnableBoldSymbolicTraitFromRegistry() + } + no := f.Weight() <= float64as(0.239000, 0x3fce978d4fdf3b64) + return !no +} From e3deebaa1d01b3bdda5576cbc0ac062216d866e8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 21 Oct 2017 12:22:50 -0400 Subject: [PATCH 0802/1329] And sorted out the weights. Now to determine what is and isn't sane. --- doc/export/ctweightsraw | 150 ++++++++++++++++++++++++++++++++++++++ doc/export/weightlist1.sh | 8 ++ doc/export/weightlist2.sh | 10 +++ 3 files changed, 168 insertions(+) create mode 100644 doc/export/ctweightsraw create mode 100644 doc/export/weightlist1.sh create mode 100644 doc/export/weightlist2.sh diff --git a/doc/export/ctweightsraw b/doc/export/ctweightsraw new file mode 100644 index 00000000..d34cf0e0 --- /dev/null +++ b/doc/export/ctweightsraw @@ -0,0 +1,150 @@ +0.000000 0x0 "reg" +0.300000 0x3e99999a "semi" +0.400000 0x3ecccccd "bold" +-0.400000 0xbecccccd "light" +0.230000 0x3e6b851f "med" +0.560000 0x3f0f5c29 "heavy" +0.620000 0x3f1eb852 "black" +-0.600000 0xbf19999a "thin" +-0.800000 0xbf4ccccd "ulight" +0.000000 0x0 "LucidaGrande" +0.000000 0x0 ".LucidaGrandeUI" +0.000000 0x0 ".Keyboard" +0.240000 0x3e75c28f "STHeiti" +-0.100000 0xbdcccccd "STXihei" +0.000000 0x0 "TimesNewRomanPSMT" +-0.800000 0xbf4ccccd "Ultra Light" +0.750000 0x3f400000 "Ultra Black" +-0.500000 0xbf000000 "Extra Light" +0.750000 0x3f400000 "UltraBlack" +0.800000 0x3f4ccccd "ExtraBlack" +-0.800000 0xbf4ccccd "UltraLight" +-0.500000 0xbf000000 "ExtraLight" +-0.800000 0xbf4ccccd "Ultra Thin" +-0.800000 0xbf4ccccd "Extra Thin" +0.560000 0x3f0f5c29 "Heavy Face" +-0.200000 0xbe4ccccd "Semi Light" +0.500000 0x3f000000 "Extra Bold" +0.700000 0x3f333333 "Ultra Bold" +0.560000 0x3f0f5c29 "HeavyFace" +0.500000 0x3f000000 "ExtraBold" +0.700000 0x3f333333 "UltraBold" +0.800000 0x3f4ccccd "Ext Black" +-0.200000 0xbe4ccccd "SemiLight" +0.250000 0x3e800000 "Demi Bold" +0.300000 0x3e99999a "Semi Bold" +-0.500000 0xbf000000 "Ext Light" +0.500000 0x3f000000 "Ext Bold" +0.250000 0x3e800000 "DemiBold" +0.300000 0x3e99999a "SemiBold" +-0.800000 0xbf4ccccd "HairLine" +-0.800000 0xbf4ccccd "Ext Thin" +0.230000 0x3e6b851f "Medium" +0.800000 0x3f4ccccd "Poster" +-0.400000 0xbecccccd "Light" +0.500000 0x3f000000 "Ultra" +0.560000 0x3f0f5c29 "Heavy" +0.500000 0x3f000000 "Extra" +0.620000 0x3f1eb852 "Black" +0.620000 0x3f1eb852 "Super" +0.850000 0x3f59999a "Obese" +-0.400000 0xbecccccd "Lite" +-0.230000 0xbe6b851f "Book" +0.250000 0x3e800000 "Demi" +0.300000 0x3e99999a "Semi" +-0.500000 0xbf000000 "Thin" +0.400000 0x3ecccccd "Bold" +0.800000 0x3f4ccccd "Nord" +0.750000 0x3f400000 "Fat" +-0.230000 0xbe6b851f "W1" +-0.500000 0xbf000000 "W2" +-0.230000 0xbe6b851f "W3" +0.000000 0x0 "W4" +0.230000 0x3e6b851f "W5" +0.300000 0x3e99999a "W6" +0.440000 0x3ee147ae "W7" +0.540000 0x3f0a3d71 "W8" +0.620000 0x3f1eb852 "W9" +-0.800000 0xbf4ccccd 1, 10 - 100 +-0.500000 0xbf000000 2, 101 - 200 +-0.400000 0xbecccccd 3, 201 - 300 +0.000000 0x0 4, 301 - 400 +0.230000 0x3e6b851 5, 401 - 500 +0.250000 0x3e800000 6, 501 - 600 +0.400000 0x3ecccccd 7, 601 - 700 +0.500000 0x3f000000 8, 701 - 800 +0.620000 0x3f1eb852 9, 801 - 900 +0.750000 0x3f400000 901 - 950 +0.800000 0x3f4ccccd 951 - 999 +-0.500000 0xbf000000 2 +-0.400000 0xbecccccd 3 +of true 4 +-0.230000 0xbe6b851f 5 +0.230000 0x3e6b851f 6 +0.250000 0x3e800000 7 +0.400000 0x3ecccccd 8 +0.560000 0x3f0f5c29 9 +0.620000 0x3f1eb852 10 +0.800000 0x3f4ccccd 11 +0.400000 0x3ecccccd 1 +-0.200000 0xbe4ccccd "EL" +0.500000 0x3f000000 "EB" +0.300000 0x3e99999a "SB" +0.800000 0x3f4ccccd "UH" +0.700000 0x3f333333 "U" +-0.400000 0xbecccccd "L" +0.560000 0x3f0f5c29 "H" +0.400000 0x3ecccccd "B" +0.230000 0x3e6b851f "M" +0.000000 0x0 "R" +0.000000 0x0 default +0.000000 0x0 "LucidaGrande" +0.000000 0x0 ".LucidaGrandeUI" +0.240000 0x3fceb851eb851eb8 "STHeiti" +-0.100000 0xbfb999999999999a "STXihei" +0.000000 0x0 "TimesNewRomanPSMT" +-0.800000 0xbf4ccccd "ultra light" +0.750000 0x3f400000 "ultra black" +-0.500000 0xbf000000 "extra light" +-0.800000 0xbf4ccccd "ultralight" +0.750000 0x3f400000 "ultrablack" +0.800000 0x3f4ccccd "extrablack" +-0.500000 0xbf000000 "extralight" +0.560000 0x3f0f5c29 "heavy face" +-0.200000 0xbe4ccccd "semi light" +0.500000 0x3f000000 "extra bold" +0.700000 0x3f333333 "ultra bold" +0.560000 0x3f0f5c29 "heavyface" +0.500000 0x3f000000 "extrabold" +0.700000 0x3f333333 "ultrabold" +-0.200000 0xbe4ccccd "semilight" +0.250000 0x3e800000 "demi bold" +0.300000 0x3e99999a "semi bold" +0.250000 0x3e800000 "demibold" +0.300000 0x3e99999a "semibold" +-0.700000 0xbf333333 "hairline" +0.230000 0x3e6b851f "medium" +0.800000 0x3f4ccccd "poster" +-0.400000 0xbecccccd "light" +0.560000 0x3f0f5c29 "heavy" +0.500000 0x3f000000 "extra" +0.620000 0x3f1eb852 "black" +0.620000 0x3f1eb852 "super" +0.850000 0x3f59999a "obese" +-0.400000 0xbecccccd "lite" +-0.230000 0xbe6b851f "book" +0.250000 0x3e800000 "demi" +0.300000 0x3e99999a "semi" +-0.500000 0xbf000000 "thin" +0.400000 0x3ecccccd "bold" +0.800000 0x3f4ccccd "nord" +0.750000 0x3f400000 "fat" +-0.700000 0xbf333333 "w1" +-0.500000 0xbf000000 "w2" +-0.230000 0xbe6b851f "w3" +0.000000 0x0 "w4" +0.230000 0x3e6b851f "w5" +0.300000 0x3e99999a "w6" +0.440000 0x3ee147ae "w7" +0.540000 0x3f0a3d71 "w8" +0.620000 0x3f1eb852 "w9" diff --git a/doc/export/weightlist1.sh b/doc/export/weightlist1.sh new file mode 100644 index 00000000..0a1267ea --- /dev/null +++ b/doc/export/weightlist1.sh @@ -0,0 +1,8 @@ +# 21 october 2017 +gawk ' +BEGIN { FS = "\t+" } +!/float..as/ { next } +{ i = 0; if ($1 == "") i++ } +(NF-i) != 2 { next } +{ print } +' "$@" diff --git a/doc/export/weightlist2.sh b/doc/export/weightlist2.sh new file mode 100644 index 00000000..df2c546d --- /dev/null +++ b/doc/export/weightlist2.sh @@ -0,0 +1,10 @@ +# 21 october 2017 +gawk ' +{ + gsub(/float..as\(/, "") + gsub(/,/, "", $(NF - 1)) + gsub(/\)$/, "") + split($0, parts, /:/) + print $(NF - 1) "\t" $NF "\t" parts[1] +} +' "$@" From e5e0dca360531d44dce8aea34bb987b55aa0ae15 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 21 Oct 2017 12:33:38 -0400 Subject: [PATCH 0803/1329] And processed the weights list. --- doc/export/ctweightsprocessed | 149 ++++++++++++++++++++++++++++++++++ doc/export/ctweightsraw | 21 +++-- doc/export/ctweightvalues | 22 ++--- doc/export/weightlist3.sh | 3 + 4 files changed, 173 insertions(+), 22 deletions(-) create mode 100644 doc/export/ctweightsprocessed create mode 100644 doc/export/weightlist3.sh diff --git a/doc/export/ctweightsprocessed b/doc/export/ctweightsprocessed new file mode 100644 index 00000000..d3ec018f --- /dev/null +++ b/doc/export/ctweightsprocessed @@ -0,0 +1,149 @@ +-0.100000 0xbdcccccd "STXihei" +-0.100000 0xbfb999999999999a "STXihei" +-0.200000 0xbe4ccccd "EL" +-0.200000 0xbe4ccccd "Semi Light" +-0.200000 0xbe4ccccd "SemiLight" +-0.200000 0xbe4ccccd "semi light" +-0.200000 0xbe4ccccd "semilight" +-0.230000 0xbe6b851f "Book" +-0.230000 0xbe6b851f "W1" +-0.230000 0xbe6b851f "W3" +-0.230000 0xbe6b851f "book" +-0.230000 0xbe6b851f "w3" +-0.230000 0xbe6b851f panose 5 +-0.400000 0xbecccccd "L" +-0.400000 0xbecccccd "Light" +-0.400000 0xbecccccd "Lite" +-0.400000 0xbecccccd "light" +-0.400000 0xbecccccd "light" +-0.400000 0xbecccccd "lite" +-0.400000 0xbecccccd 3, 201 - 300 +-0.400000 0xbecccccd panose 3 +-0.500000 0xbf000000 "Ext Light" +-0.500000 0xbf000000 "Extra Light" +-0.500000 0xbf000000 "ExtraLight" +-0.500000 0xbf000000 "Thin" +-0.500000 0xbf000000 "W2" +-0.500000 0xbf000000 "extra light" +-0.500000 0xbf000000 "extralight" +-0.500000 0xbf000000 "thin" +-0.500000 0xbf000000 "w2" +-0.500000 0xbf000000 2, 101 - 200 +-0.500000 0xbf000000 panose 2 +-0.600000 0xbf19999a "thin" +-0.700000 0xbf333333 "hairline" +-0.700000 0xbf333333 "w1" +-0.800000 0xbf4ccccd "Ext Thin" +-0.800000 0xbf4ccccd "Extra Thin" +-0.800000 0xbf4ccccd "HairLine" +-0.800000 0xbf4ccccd "Ultra Light" +-0.800000 0xbf4ccccd "Ultra Thin" +-0.800000 0xbf4ccccd "UltraLight" +-0.800000 0xbf4ccccd "ulight" +-0.800000 0xbf4ccccd "ultra light" +-0.800000 0xbf4ccccd "ultralight" +-0.800000 0xbf4ccccd 1, 10 - 100 +0.000000 0x0 ".Keyboard" +0.000000 0x0 ".LucidaGrandeUI" +0.000000 0x0 ".LucidaGrandeUI" +0.000000 0x0 "LucidaGrande" +0.000000 0x0 "LucidaGrande" +0.000000 0x0 "R" +0.000000 0x0 "TimesNewRomanPSMT" +0.000000 0x0 "TimesNewRomanPSMT" +0.000000 0x0 "W4" +0.000000 0x0 "reg" +0.000000 0x0 "w4" +0.000000 0x0 4, 301 - 400 +0.000000 0x0 default +0.230000 0x3e6b851 5, 401 - 500 +0.230000 0x3e6b851f "M" +0.230000 0x3e6b851f "Medium" +0.230000 0x3e6b851f "W5" +0.230000 0x3e6b851f "med" +0.230000 0x3e6b851f "medium" +0.230000 0x3e6b851f "w5" +0.230000 0x3e6b851f panose 6 +0.240000 0x3e75c28f "STHeiti" +0.240000 0x3fceb851eb851eb8 "STHeiti" +0.250000 0x3e800000 "Demi Bold" +0.250000 0x3e800000 "Demi" +0.250000 0x3e800000 "DemiBold" +0.250000 0x3e800000 "demi bold" +0.250000 0x3e800000 "demi" +0.250000 0x3e800000 "demibold" +0.250000 0x3e800000 6, 501 - 600 +0.250000 0x3e800000 panose 7 +0.300000 0x3e99999a "SB" +0.300000 0x3e99999a "Semi Bold" +0.300000 0x3e99999a "Semi" +0.300000 0x3e99999a "SemiBold" +0.300000 0x3e99999a "W6" +0.300000 0x3e99999a "semi bold" +0.300000 0x3e99999a "semi" +0.300000 0x3e99999a "semi" +0.300000 0x3e99999a "semibold" +0.300000 0x3e99999a "w6" +0.400000 0x3ecccccd "B" +0.400000 0x3ecccccd "Bold" +0.400000 0x3ecccccd "bold" +0.400000 0x3ecccccd "bold" +0.400000 0x3ecccccd 'head'[0x2D] & 1 +0.400000 0x3ecccccd 7, 601 - 700 +0.400000 0x3ecccccd panose 8 +0.440000 0x3ee147ae "W7" +0.440000 0x3ee147ae "w7" +0.500000 0x3f000000 "EB" +0.500000 0x3f000000 "Ext Bold" +0.500000 0x3f000000 "Extra Bold" +0.500000 0x3f000000 "Extra" +0.500000 0x3f000000 "ExtraBold" +0.500000 0x3f000000 "Ultra" +0.500000 0x3f000000 "extra bold" +0.500000 0x3f000000 "extra" +0.500000 0x3f000000 "extrabold" +0.500000 0x3f000000 8, 701 - 800 +0.540000 0x3f0a3d71 "W8" +0.540000 0x3f0a3d71 "w8" +0.560000 0x3f0f5c29 "H" +0.560000 0x3f0f5c29 "Heavy Face" +0.560000 0x3f0f5c29 "Heavy" +0.560000 0x3f0f5c29 "HeavyFace" +0.560000 0x3f0f5c29 "heavy face" +0.560000 0x3f0f5c29 "heavy" +0.560000 0x3f0f5c29 "heavy" +0.560000 0x3f0f5c29 "heavyface" +0.560000 0x3f0f5c29 panose 9 +0.620000 0x3f1eb852 "Black" +0.620000 0x3f1eb852 "Super" +0.620000 0x3f1eb852 "W9" +0.620000 0x3f1eb852 "black" +0.620000 0x3f1eb852 "black" +0.620000 0x3f1eb852 "super" +0.620000 0x3f1eb852 "w9" +0.620000 0x3f1eb852 9, 801 - 900 +0.620000 0x3f1eb852 panose 10 +0.700000 0x3f333333 "U" +0.700000 0x3f333333 "Ultra Bold" +0.700000 0x3f333333 "UltraBold" +0.700000 0x3f333333 "ultra bold" +0.700000 0x3f333333 "ultrabold" +0.750000 0x3f400000 "Fat" +0.750000 0x3f400000 "Ultra Black" +0.750000 0x3f400000 "UltraBlack" +0.750000 0x3f400000 "fat" +0.750000 0x3f400000 "ultra black" +0.750000 0x3f400000 "ultrablack" +0.750000 0x3f400000 901 - 950 +0.800000 0x3f4ccccd "Ext Black" +0.800000 0x3f4ccccd "ExtraBlack" +0.800000 0x3f4ccccd "Nord" +0.800000 0x3f4ccccd "Poster" +0.800000 0x3f4ccccd "UH" +0.800000 0x3f4ccccd "extrablack" +0.800000 0x3f4ccccd "nord" +0.800000 0x3f4ccccd "poster" +0.800000 0x3f4ccccd 951 - 999 +0.800000 0x3f4ccccd panose 11 +0.850000 0x3f59999a "Obese" +0.850000 0x3f59999a "obese" diff --git a/doc/export/ctweightsraw b/doc/export/ctweightsraw index d34cf0e0..0eda5021 100644 --- a/doc/export/ctweightsraw +++ b/doc/export/ctweightsraw @@ -76,17 +76,16 @@ 0.620000 0x3f1eb852 9, 801 - 900 0.750000 0x3f400000 901 - 950 0.800000 0x3f4ccccd 951 - 999 --0.500000 0xbf000000 2 --0.400000 0xbecccccd 3 -of true 4 --0.230000 0xbe6b851f 5 -0.230000 0x3e6b851f 6 -0.250000 0x3e800000 7 -0.400000 0x3ecccccd 8 -0.560000 0x3f0f5c29 9 -0.620000 0x3f1eb852 10 -0.800000 0x3f4ccccd 11 -0.400000 0x3ecccccd 1 +-0.500000 0xbf000000 panose 2 +-0.400000 0xbecccccd panose 3 +-0.230000 0xbe6b851f panose 5 +0.230000 0x3e6b851f panose 6 +0.250000 0x3e800000 panose 7 +0.400000 0x3ecccccd panose 8 +0.560000 0x3f0f5c29 panose 9 +0.620000 0x3f1eb852 panose 10 +0.800000 0x3f4ccccd panose 11 +0.400000 0x3ecccccd 'head'[0x2D] & 1 -0.200000 0xbe4ccccd "EL" 0.500000 0x3f000000 "EB" 0.300000 0x3e99999a "SB" diff --git a/doc/export/ctweightvalues b/doc/export/ctweightvalues index ee7f17dc..24f5fe91 100644 --- a/doc/export/ctweightvalues +++ b/doc/export/ctweightvalues @@ -84,19 +84,19 @@ registered font, OS2 weights; table length >= 78 901 - 950: float32as(0.750000, 0x3f400000) 951 - 999: float32as(0.800000, 0x3f4ccccd) 0, 1000: panose - 2: float32as(-0.500000, 0xbf000000) - 3: float32as(-0.400000, 0xbecccccd) - 4: !!!! should be float32as(-0.300000, 0xbe99999a) but is treated as invalid instead due to returning false instead of true - 5: float32as(-0.230000, 0xbe6b851f) - 6: float32as(0.230000, 0x3e6b851f) - 7: float32as(0.250000, 0x3e800000) - 8: float32as(0.400000, 0x3ecccccd) - 9: float32as(0.560000, 0x3f0f5c29) - 10: float32as(0.620000, 0x3f1eb852) - 11: float32as(0.800000, 0x3f4ccccd) +panose 2: float32as(-0.500000, 0xbf000000) +panose 3: float32as(-0.400000, 0xbecccccd) +panose 4: !!!! see note should be float32as(-0.300000, 0xbe99999a) but is treated as invalid instead due to returning false instead of true +panose 5: float32as(-0.230000, 0xbe6b851f) +panose 6: float32as(0.230000, 0x3e6b851f) +panose 7: float32as(0.250000, 0x3e800000) +panose 8: float32as(0.400000, 0x3ecccccd) +panose 9: float32as(0.560000, 0x3f0f5c29) +panose 10: float32as(0.620000, 0x3f1eb852) +panose 11: float32as(0.800000, 0x3f4ccccd) registered font, head table, low bit of byte 0x2D -1: float32as(0.400000, 0x3ecccccd) +'head'[0x2D] & 1: float32as(0.400000, 0x3ecccccd) registered font, abbreviated weight glossary, checks for (possibly in TrueType and OpenType only) in order: preferred subfamily, family; case-insensitive strict comparison "EL": float32as(-0.200000, 0xbe4ccccd) diff --git a/doc/export/weightlist3.sh b/doc/export/weightlist3.sh new file mode 100644 index 00000000..f772226b --- /dev/null +++ b/doc/export/weightlist3.sh @@ -0,0 +1,3 @@ +# 21 october 2017 +sort -t$'\t' -k1,1 -k2,2 "$@" | + column -t -s$'\t' From b2b5bc36b1a13c8691f47918583329c6e64f80c4 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 21 Oct 2017 12:45:15 -0400 Subject: [PATCH 0804/1329] And annotated the ctweights list. --- doc/export/ctweightsannotated | 149 ++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 doc/export/ctweightsannotated diff --git a/doc/export/ctweightsannotated b/doc/export/ctweightsannotated new file mode 100644 index 00000000..23db5436 --- /dev/null +++ b/doc/export/ctweightsannotated @@ -0,0 +1,149 @@ +-0.100000 0xbdcccccd registered postscript name "STXihei" +-0.100000 0xbfb999999999999a unregistered postscript name "STXihei" +-0.200000 0xbe4ccccd registered subfamily abbr "EL" +-0.200000 0xbe4ccccd "Semi Light" +-0.200000 0xbe4ccccd "SemiLight" +-0.200000 0xbe4ccccd "semi light" +-0.200000 0xbe4ccccd "semilight" +-0.230000 0xbe6b851f "Book" +-0.230000 0xbe6b851f "W1" +-0.230000 0xbe6b851f "W3" +-0.230000 0xbe6b851f "book" +-0.230000 0xbe6b851f "w3" +-0.230000 0xbe6b851f panose 5 +-0.400000 0xbecccccd registered subfamily abbr "L" +-0.400000 0xbecccccd "Light" +-0.400000 0xbecccccd "Lite" +-0.400000 0xbecccccd "light" +-0.400000 0xbecccccd "light" +-0.400000 0xbecccccd "lite" +-0.400000 0xbecccccd registered OS2 weights 3, 201 - 300 +-0.400000 0xbecccccd panose 3 +-0.500000 0xbf000000 "Ext Light" +-0.500000 0xbf000000 "Extra Light" +-0.500000 0xbf000000 "ExtraLight" +-0.500000 0xbf000000 "Thin" +-0.500000 0xbf000000 "W2" +-0.500000 0xbf000000 "extra light" +-0.500000 0xbf000000 "extralight" +-0.500000 0xbf000000 "thin" +-0.500000 0xbf000000 "w2" +-0.500000 0xbf000000 registered OS2 weights 2, 101 - 200 +-0.500000 0xbf000000 panose 2 +-0.600000 0xbf19999a "thin" +-0.700000 0xbf333333 "hairline" +-0.700000 0xbf333333 "w1" +-0.800000 0xbf4ccccd "Ext Thin" +-0.800000 0xbf4ccccd "Extra Thin" +-0.800000 0xbf4ccccd "HairLine" +-0.800000 0xbf4ccccd "Ultra Light" +-0.800000 0xbf4ccccd "Ultra Thin" +-0.800000 0xbf4ccccd "UltraLight" +-0.800000 0xbf4ccccd "ulight" +-0.800000 0xbf4ccccd "ultra light" +-0.800000 0xbf4ccccd "ultralight" +-0.800000 0xbf4ccccd registered OS2 weights 1, 10 - 100 +0.000000 0x0 registered postscript name ".Keyboard" +0.000000 0x0 registered postscript name ".LucidaGrandeUI" +0.000000 0x0 unregistered postscript name ".LucidaGrandeUI" +0.000000 0x0 registered postscript name "LucidaGrande" +0.000000 0x0 unregistered postscript name "LucidaGrande" +0.000000 0x0 registered subfamily abbr "R" +0.000000 0x0 registered postscript name "TimesNewRomanPSMT" +0.000000 0x0 unregistered postscript name "TimesNewRomanPSMT" +0.000000 0x0 "W4" +0.000000 0x0 "reg" +0.000000 0x0 "w4" +0.000000 0x0 registered OS2 weights 4, 301 - 400 +0.000000 0x0 default +0.230000 0x3e6b851 registered OS2 weights 5, 401 - 500 +0.230000 0x3e6b851f registered subfamily abbr "M" +0.230000 0x3e6b851f "Medium" +0.230000 0x3e6b851f "W5" +0.230000 0x3e6b851f "med" +0.230000 0x3e6b851f "medium" +0.230000 0x3e6b851f "w5" +0.230000 0x3e6b851f panose 6 +0.240000 0x3e75c28f registered postscript name "STHeiti" +0.240000 0x3fceb851eb851eb8 unregistered postscript name "STHeiti" +0.250000 0x3e800000 "Demi Bold" +0.250000 0x3e800000 "Demi" +0.250000 0x3e800000 "DemiBold" +0.250000 0x3e800000 "demi bold" +0.250000 0x3e800000 "demi" +0.250000 0x3e800000 "demibold" +0.250000 0x3e800000 registered OS2 weights 6, 501 - 600 +0.250000 0x3e800000 panose 7 +0.300000 0x3e99999a registered subfamily abbr "SB" +0.300000 0x3e99999a "Semi Bold" +0.300000 0x3e99999a "Semi" +0.300000 0x3e99999a "SemiBold" +0.300000 0x3e99999a "W6" +0.300000 0x3e99999a "semi bold" +0.300000 0x3e99999a "semi" +0.300000 0x3e99999a "semi" +0.300000 0x3e99999a "semibold" +0.300000 0x3e99999a "w6" +0.400000 0x3ecccccd registered subfamily abbr "B" +0.400000 0x3ecccccd "Bold" +0.400000 0x3ecccccd "bold" +0.400000 0x3ecccccd "bold" +0.400000 0x3ecccccd 'head'[0x2D] & 1 +0.400000 0x3ecccccd registered OS2 weights 7, 601 - 700 +0.400000 0x3ecccccd panose 8 +0.440000 0x3ee147ae "W7" +0.440000 0x3ee147ae "w7" +0.500000 0x3f000000 registered subfamily abbr "EB" +0.500000 0x3f000000 "Ext Bold" +0.500000 0x3f000000 "Extra Bold" +0.500000 0x3f000000 "Extra" +0.500000 0x3f000000 "ExtraBold" +0.500000 0x3f000000 "Ultra" +0.500000 0x3f000000 "extra bold" +0.500000 0x3f000000 "extra" +0.500000 0x3f000000 "extrabold" +0.500000 0x3f000000 registered OS2 weights 8, 701 - 800 +0.540000 0x3f0a3d71 "W8" +0.540000 0x3f0a3d71 "w8" +0.560000 0x3f0f5c29 registered subfamily abbr "H" +0.560000 0x3f0f5c29 "Heavy Face" +0.560000 0x3f0f5c29 "Heavy" +0.560000 0x3f0f5c29 "HeavyFace" +0.560000 0x3f0f5c29 "heavy face" +0.560000 0x3f0f5c29 "heavy" +0.560000 0x3f0f5c29 "heavy" +0.560000 0x3f0f5c29 "heavyface" +0.560000 0x3f0f5c29 panose 9 +0.620000 0x3f1eb852 "Black" +0.620000 0x3f1eb852 "Super" +0.620000 0x3f1eb852 "W9" +0.620000 0x3f1eb852 "black" +0.620000 0x3f1eb852 "black" +0.620000 0x3f1eb852 "super" +0.620000 0x3f1eb852 "w9" +0.620000 0x3f1eb852 registered OS2 weights 9, 801 - 900 +0.620000 0x3f1eb852 panose 10 +0.700000 0x3f333333 registered subfamily abbr "U" +0.700000 0x3f333333 "Ultra Bold" +0.700000 0x3f333333 "UltraBold" +0.700000 0x3f333333 "ultra bold" +0.700000 0x3f333333 "ultrabold" +0.750000 0x3f400000 "Fat" +0.750000 0x3f400000 "Ultra Black" +0.750000 0x3f400000 "UltraBlack" +0.750000 0x3f400000 "fat" +0.750000 0x3f400000 "ultra black" +0.750000 0x3f400000 "ultrablack" +0.750000 0x3f400000 registered OS2 weights 901 - 950 +0.800000 0x3f4ccccd "Ext Black" +0.800000 0x3f4ccccd "ExtraBlack" +0.800000 0x3f4ccccd "Nord" +0.800000 0x3f4ccccd "Poster" +0.800000 0x3f4ccccd registered subfamily abbr "UH" +0.800000 0x3f4ccccd "extrablack" +0.800000 0x3f4ccccd "nord" +0.800000 0x3f4ccccd "poster" +0.800000 0x3f4ccccd registered OS2 weights 951 - 999 +0.800000 0x3f4ccccd panose 11 +0.850000 0x3f59999a "Obese" +0.850000 0x3f59999a "obese" From e186c0e69a96ea1c82a8c41c13d72769d731c9f2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 21 Oct 2017 22:43:43 -0400 Subject: [PATCH 0805/1329] Mapped out Core Text width value deduction. Will need to do the other files for this one too. --- doc/export/ctwidths | 160 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 doc/export/ctwidths diff --git a/doc/export/ctwidths b/doc/export/ctwidths new file mode 100644 index 00000000..dd6d2ca2 --- /dev/null +++ b/doc/export/ctwidths @@ -0,0 +1,160 @@ +xx pseudo-go + +func (f *CTFont) RegistryWidth32() float32 { + metadata visual descriptors + { "med", float32as(0.000000, 0x0) }, + { "cond", float32as(-0.200000, 0xbe4ccccd) }, + { "ext", float32as(0.200000, 0x3e4ccccd) }, + + style dictionary + { "Extra Compressed", float32as(-0.700000, 0xbf333333) }, + { "Ultra Compressed", float32as(-0.700000, 0xbf333333) }, + { "Ultra Condensed", float32as(-0.700000, 0xbf333333) }, + { "Extra Condensed", float32as(-0.500000, 0xbf000000) }, + { "Extra Extended", float32as(0.400000, 0x3ecccccd) }, + { "Ext Compressed", float32as(-0.700000, 0xbf333333) }, + { "Ultra Expanded", float32as(0.800000, 0x3f4ccccd) }, + { "Ultra Extended", float32as(0.800000, 0x3f4ccccd) }, + { "Extra Expanded", float32as(0.400000, 0x3ecccccd) }, + xx TODO this is weird, but correct... + { "Semi Condensed", float32as(-0.700000, 0xbf333333) }, + { "Semi Condensed", float32as(-0.100000, 0xbdcccccd) }, + xx end TODO + { "Ext Condensed", float32as(-0.500000, 0xbf000000) }, + { "SemiCondensed", float32as(-0.100000, 0xbdcccccd) }, + { "ExtraExpanded", float32as(0.400000, 0x3ecccccd) }, + { "Semi Expanded", float32as(0.100000, 0x3dcccccd) }, + { "Semi Extended", float32as(0.100000, 0x3dcccccd) }, + { "Ext Expanded", float32as(0.400000, 0x3ecccccd) }, + { "Ext Extended", float32as(0.400000, 0x3ecccccd) }, + { "SemiExpanded", float32as(0.100000, 0x3dcccccd) }, + { "Extra Narrow", float32as(-0.500000, 0xbf000000) }, + { "ExtraNarrow", float32as(-0.500000, 0xbf000000) }, + { "Extra Wide", float32as(0.800000, 0x3f4ccccd) }, + { "Ultra Cond", float32as(-0.700000, 0xbf333333) }, + { "Compressed", float32as(-0.500000, 0xbf000000) }, + { "Extra Cond", float32as(-0.500000, 0xbf000000) }, + { "Semi Cond", float32as(-0.100000, 0xbdcccccd) }, + { "Condensed", float32as(-0.200000, 0xbe4ccccd) }, + { "ExtraWide", float32as(0.800000, 0x3f4ccccd) }, + { "Extended", float32as(0.200000, 0x3e4ccccd) }, + { "Expanded", float32as(0.200000, 0x3e4ccccd) }, + { "Ext Cond", float32as(-0.500000, 0xbf000000) }, + { "Narrow", float32as(-0.400000 , 0xbecccccd) }, + { "Compact", float32as(-0.400000, 0xbecccccd) }, + { "Cond", float32as(-0.200000, 0xbe4ccccd) }, + { "Wide", float32as(0.600000, 0x3f19999a) }, + { "Thin", float32as(-0.700000, 0xbf333333) }, + + get os2 table + if os2table.Len() >= 78 { + usWidthClass := uint16be(b[6:8]) - 1 + xx this handles the case where the original usWidthClass == 0 + if usWidthClass > 8 { + panose := b[0x23] - 2 + if panose > 6 { + xx TODO + } else { + switch panose { + case 0, 1, 2: + width = float32as(0.000000, 0x0) + case 3: + width = float32as(0.200000, 0x3e4ccccd) + case 4: + width = float32as(-0.200000, 0xbe4ccccd) + case 5: + width = float32as(0.400000, 0x3ecccccd) + case 6: + width = float32as(-0.400000, 0xbecccccd) + } + } + } + switch usWidthClass { + case 0: + width = float32as(-0.700000, 0xbf333333) + case 1: + width = float32as(-0.500000, 0xbf000000) + case 2: + width = float32as(-0.200000, 0xbe4ccccd) + case 3: + width = float32as(-0.100000, 0xbdcccccd) + case 4: + width = float32as(0.000000, 0x0) + case 5: + width = float32as(0.100000, 0x3dcccccd) + case 6: + width = float32as(0.400000, 0x3ecccccd) + case 7: + width = float32as(0.600000, 0x3f19999a) + case 8: + width = float32as(0.800000, 0x3f4ccccd) + } + } + + headtable := f.CopyTable('head') + if headtable != nil { + if headtable.Len() >= 54 { + x := b[0x2d] + if (x & 0x20) != 0 { + width = float32as(-0.200000, 0xbe4ccccd) + } else if (x & 0x40) != 0 { + width = float32as(0.200000, 0x3e4ccccd) + } + } + } + + xx and if all else fails + return float32as(0.000000, 0x0) +} + +func (f *CTFont) Width() float64 { + if f.IsRegistered() { + return f.RegistryWidth() + } + + width := 0.0 + hasWidth := false + + if there is an OS2 table { + var usWidthClass uint16 + + valid := false + if it's 78 bytes or more { + usWidthClass = uint16be(table[6:8]) + if usWeightClass <= 10 { + valid = true + } else { + valid = false + } + } else { + usWidthClass = 0 + valid = true + } + if valid { + ten := float64as(10.000000, 0x4024000000000000) + negPointFive := float64as(-0.500000, 0xbfe0000000000000) + width = (float64(usWidthClass) div ten) + negPointFive + hasWidth = true + } + } + + then there's the style glossary strings comparison: + { "semi condensed", float32as(-0.100000, 0xbdcccccd) }, + { "extra expanded", float32as(0.400000, 0x3ecccccd) }, + { "semicondensed", float32as(-0.100000, 0xbdcccccd) }, + { "extraexpanded", float32as(0.400000, 0x3ecccccd) }, + { "semi expanded", float32as(0.100000, 0x3dcccccd) }, + { "semiexpanded", float32as(0.100000, 0x3dcccccd) }, + { "extra narrow", float32as(-0.500000, 0xbf000000) }, + { "extranarrow", float32as(-0.500000, 0xbf000000) }, + { "extra wide", float32as(0.800000, 0x3f4ccccd) }, + { "condensed", float32as(-0.200000, 0xbe4ccccd) }, + { "extrawide", float32as(0.800000, 0x3f4ccccd) }, + { "extended", float32as(0.200000, 0x3e4ccccd) }, + { "expanded", float32as(0.200000, 0x3e4ccccd) }, + { "narrow", float32as(-0.400000, 0xbecccccd) }, + { "wide", float32as(0.600000, 0x3f19999a) }, + { "thin", float32as(-0.700000, 0xbf333333) }, + + otherwise just return float64as(0.000000, 0x0) +} From e2369df648c0860c2557ce665a25b1c4797e5f91 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 21 Oct 2017 23:55:03 -0400 Subject: [PATCH 0806/1329] More width stuff. --- doc/export/ctwidthvalues | 112 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 doc/export/ctwidthvalues diff --git a/doc/export/ctwidthvalues b/doc/export/ctwidthvalues new file mode 100644 index 00000000..fbccfc3f --- /dev/null +++ b/doc/export/ctwidthvalues @@ -0,0 +1,112 @@ +metadata "med": float32as(0.000000, 0x0) +metadata "cond": float32as(-0.200000, 0xbe4ccccd) +metadata "ext": float32as(0.200000, 0x3e4ccccd) + +registered "Extra Compressed": float32as(-0.700000, 0xbf333333) +registered "Ultra Compressed": float32as(-0.700000, 0xbf333333) +registered "Ultra Condensed": float32as(-0.700000, 0xbf333333) +registered "Extra Condensed": float32as(-0.500000, 0xbf000000) +registered "Extra Extended": float32as(0.400000, 0x3ecccccd) +registered "Ext Compressed": float32as(-0.700000, 0xbf333333) +registered "Ultra Expanded": float32as(0.800000, 0x3f4ccccd) +registered "Ultra Extended": float32as(0.800000, 0x3f4ccccd) +registered "Extra Expanded": float32as(0.400000, 0x3ecccccd) +registered "Semi Condensed": float32as(-0.700000, 0xbf333333) +registered "Semi Condensed": float32as(-0.100000, 0xbdcccccd) +registered "Ext Condensed": float32as(-0.500000, 0xbf000000) +registered "SemiCondensed": float32as(-0.100000, 0xbdcccccd) +registered "ExtraExpanded": float32as(0.400000, 0x3ecccccd) +registered "Semi Expanded": float32as(0.100000, 0x3dcccccd) +registered "Semi Extended": float32as(0.100000, 0x3dcccccd) +registered "Ext Expanded": float32as(0.400000, 0x3ecccccd) +registered "Ext Extended": float32as(0.400000, 0x3ecccccd) +registered "SemiExpanded": float32as(0.100000, 0x3dcccccd) +registered "Extra Narrow": float32as(-0.500000, 0xbf000000) +registered "ExtraNarrow": float32as(-0.500000, 0xbf000000) +registered "Extra Wide": float32as(0.800000, 0x3f4ccccd) +registered "Ultra Cond": float32as(-0.700000, 0xbf333333) +registered "Compressed": float32as(-0.500000, 0xbf000000) +registered "Extra Cond": float32as(-0.500000, 0xbf000000) +registered "Semi Cond": float32as(-0.100000, 0xbdcccccd) +registered "Condensed": float32as(-0.200000, 0xbe4ccccd) +registered "ExtraWide": float32as(0.800000, 0x3f4ccccd) +registered "Extended": float32as(0.200000, 0x3e4ccccd) +registered "Expanded": float32as(0.200000, 0x3e4ccccd) +registered "Ext Cond": float32as(-0.500000, 0xbf000000) +registered "Narrow": float32as(-0.400000 , 0xbecccccd) +registered "Compact": float32as(-0.400000, 0xbecccccd) +registered "Cond": float32as(-0.200000, 0xbe4ccccd) +registered "Wide": float32as(0.600000, 0x3f19999a) +registered "Thin": float32as(-0.700000, 0xbf333333) + +panose 2, 3, 4: float32as(0.000000, 0x0) +panose 5: float32as(0.200000, 0x3e4ccccd) +panose 6: float32as(-0.200000, 0xbe4ccccd) +panose 7: float32as(0.400000, 0x3ecccccd) +panose 8: float32as(-0.400000, 0xbecccccd) + +registered OS2 1: float32as(-0.700000, 0xbf333333) +registered OS2 2: float32as(-0.500000, 0xbf000000) +registered OS2 3: float32as(-0.200000, 0xbe4ccccd) +registered OS2 4: float32as(-0.100000, 0xbdcccccd) +registered OS2 5: float32as(0.000000, 0x0) +registered OS2 6: float32as(0.100000, 0x3dcccccd) +registered OS2 7: float32as(0.400000, 0x3ecccccd) +registered OS2 8: float32as(0.600000, 0x3f19999a) +registered OS2 9: float32as(0.800000, 0x3f4ccccd) + +head[0x2d] & 0x20: float32as(-0.200000, 0xbe4ccccd) +head[0x2d] & 0x40: float32as(0.200000, 0x3e4ccccd) + +registered default: float32as(0.000000, 0x0) + +func (f *CTFont) Width() float64 { + if f.IsRegistered() { + return f.RegistryWidth() + } + + width := 0.0 + hasWidth := false + + if there is an OS2 table { + var usWidthClass uint16 + + valid := false + if it's 78 bytes or more { + usWidthClass = uint16be(table[6:8]) + if usWeightClass <= 10 { + valid = true + } else { + valid = false + } + } else { + usWidthClass = 0 + valid = true + } + if valid { + ten := float64as(10.000000, 0x4024000000000000) + negPointFive := float64as(-0.500000, 0xbfe0000000000000) + width = (float64(usWidthClass) div ten) + negPointFive + hasWidth = true + } + } + + then there's the style glossary strings comparison: +unregistered "semi condensed": float32as(-0.100000, 0xbdcccccd) +unregistered "extra expanded": float32as(0.400000, 0x3ecccccd) +unregistered "semicondensed": float32as(-0.100000, 0xbdcccccd) +unregistered "extraexpanded": float32as(0.400000, 0x3ecccccd) +unregistered "semi expanded": float32as(0.100000, 0x3dcccccd) +unregistered "semiexpanded": float32as(0.100000, 0x3dcccccd) +unregistered "extra narrow": float32as(-0.500000, 0xbf000000) +unregistered "extranarrow": float32as(-0.500000, 0xbf000000) +unregistered "extra wide": float32as(0.800000, 0x3f4ccccd) +unregistered "condensed": float32as(-0.200000, 0xbe4ccccd) +unregistered "extrawide": float32as(0.800000, 0x3f4ccccd) +unregistered "extended": float32as(0.200000, 0x3e4ccccd) +unregistered "expanded": float32as(0.200000, 0x3e4ccccd) +unregistered "narrow": float32as(-0.400000, 0xbecccccd) +unregistered "wide": float32as(0.600000, 0x3f19999a) +unregistered "thin": float32as(-0.700000, 0xbf333333) + +default: float64as(0.000000, 0x0) From 1851fc80459e799f8a816ff0e14b1c1986398892 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 22 Oct 2017 01:27:57 -0400 Subject: [PATCH 0807/1329] Added code to print the unregistered OS2 width values using the real instructions and constants used by Core Text. --- doc/export/writewidths.c | 3 +++ doc/export/writewidths.out | 11 ++++++++ doc/export/writewidths.s | 51 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+) create mode 100644 doc/export/writewidths.c create mode 100644 doc/export/writewidths.out create mode 100644 doc/export/writewidths.s diff --git a/doc/export/writewidths.c b/doc/export/writewidths.c new file mode 100644 index 00000000..2ba7d01b --- /dev/null +++ b/doc/export/writewidths.c @@ -0,0 +1,3 @@ +// 22 october 2017 +extern int realMain(void); +int main(void) { return realMain(); } diff --git a/doc/export/writewidths.out b/doc/export/writewidths.out new file mode 100644 index 00000000..83f6b413 --- /dev/null +++ b/doc/export/writewidths.out @@ -0,0 +1,11 @@ +unregistered OS2 0: float64as(-0.5, 0xbfe0000000000000) +unregistered OS2 1: float64as(-0.4, 0xbfd999999999999a) +unregistered OS2 2: float64as(-0.3, 0xbfd3333333333333) +unregistered OS2 3: float64as(-0.2, 0xbfc999999999999a) +unregistered OS2 4: float64as(-0.1, 0xbfb9999999999998) +unregistered OS2 5: float64as(0, 0x0000000000000000) +unregistered OS2 6: float64as(0.1, 0x3fb9999999999998) +unregistered OS2 7: float64as(0.2, 0x3fc9999999999998) +unregistered OS2 8: float64as(0.3, 0x3fd3333333333334) +unregistered OS2 9: float64as(0.4, 0x3fd999999999999a) +unregistered OS2 10: float64as(0.5, 0x3fe0000000000000) diff --git a/doc/export/writewidths.s b/doc/export/writewidths.s new file mode 100644 index 00000000..d10208d5 --- /dev/null +++ b/doc/export/writewidths.s @@ -0,0 +1,51 @@ +# 22 october 2017 +# clang -o writewidths writewidths.c writewidths.s -g -Wall -Wextra -pedantic -g +# thanks to: +# - http://www.idryman.org/blog/2014/12/02/writing-64-bit-assembly-on-mac-os-x/ +# - https://developer.apple.com/library/content/documentation/DeveloperTools/Reference/Assembler/060-i386_Addressing_Modes_and_Assembler_Instructions/i386_intructions.html#//apple_ref/doc/uid/TP30000825-TPXREF101 +# - https://stackoverflow.com/questions/46309041/trivial-macos-assembly-64-bit-program-has-incorrect-stack-alignment +# - https://www.google.com/search?q=macos+implement+main+in+assembly+-nasm&oq=macos+implement+main+in+assembly+-nasm&gs_l=psy-ab.3...12877.13839.0.13988.6.6.0.0.0.0.117.407.4j1.5.0....0...1.1.64.psy-ab..1.0.0....0.et6MkokjvwA +# - https://stackoverflow.com/questions/2529185/what-are-cfi-directives-in-gnu-assembler-gas-used-for + +.section __DATA,__data + +double10: + .quad 0x4024000000000000 +doubleNeg05: + .quad 0xbfe0000000000000 + +fmt: + .asciz "unregistered OS2 %ld:\tfloat64as(%g, 0x%016lx)\n" + +.section __TEXT,__text +.globl _realMain +_realMain: + pushq %rbp + movq %rsp, %rbp + addq $8, %rsp + + xorq %rcx, %rcx +loop: + pushq %rcx + # the code from core text + movzwl %cx, %ecx + xorps %xmm0, %xmm0 + cvtsi2sdl %ecx, %xmm0 + divsd double10(%rip), %xmm0 + addsd doubleNeg05(%rip), %xmm0 + # end core text code + popq %rcx + pushq %rcx + movd %xmm0, %rdx + movzwq %cx, %rsi + leaq fmt(%rip), %rdi + callq _printf + popq %rcx + incw %cx + cmpw $10, %cx + jbe loop + + xorq %rax, %rax + subq $8, %rsp + popq %rbp + ret From 1673156fd67b96a1785e55829d58d043c37ce7c4 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 22 Oct 2017 01:31:26 -0400 Subject: [PATCH 0808/1329] And the last bits for widths. --- doc/export/ctwidthsprocessed | 149 +++++++++++++++++++++++++++++++ doc/export/writewidths.processed | 11 +++ 2 files changed, 160 insertions(+) create mode 100644 doc/export/ctwidthsprocessed create mode 100644 doc/export/writewidths.processed diff --git a/doc/export/ctwidthsprocessed b/doc/export/ctwidthsprocessed new file mode 100644 index 00000000..d3ec018f --- /dev/null +++ b/doc/export/ctwidthsprocessed @@ -0,0 +1,149 @@ +-0.100000 0xbdcccccd "STXihei" +-0.100000 0xbfb999999999999a "STXihei" +-0.200000 0xbe4ccccd "EL" +-0.200000 0xbe4ccccd "Semi Light" +-0.200000 0xbe4ccccd "SemiLight" +-0.200000 0xbe4ccccd "semi light" +-0.200000 0xbe4ccccd "semilight" +-0.230000 0xbe6b851f "Book" +-0.230000 0xbe6b851f "W1" +-0.230000 0xbe6b851f "W3" +-0.230000 0xbe6b851f "book" +-0.230000 0xbe6b851f "w3" +-0.230000 0xbe6b851f panose 5 +-0.400000 0xbecccccd "L" +-0.400000 0xbecccccd "Light" +-0.400000 0xbecccccd "Lite" +-0.400000 0xbecccccd "light" +-0.400000 0xbecccccd "light" +-0.400000 0xbecccccd "lite" +-0.400000 0xbecccccd 3, 201 - 300 +-0.400000 0xbecccccd panose 3 +-0.500000 0xbf000000 "Ext Light" +-0.500000 0xbf000000 "Extra Light" +-0.500000 0xbf000000 "ExtraLight" +-0.500000 0xbf000000 "Thin" +-0.500000 0xbf000000 "W2" +-0.500000 0xbf000000 "extra light" +-0.500000 0xbf000000 "extralight" +-0.500000 0xbf000000 "thin" +-0.500000 0xbf000000 "w2" +-0.500000 0xbf000000 2, 101 - 200 +-0.500000 0xbf000000 panose 2 +-0.600000 0xbf19999a "thin" +-0.700000 0xbf333333 "hairline" +-0.700000 0xbf333333 "w1" +-0.800000 0xbf4ccccd "Ext Thin" +-0.800000 0xbf4ccccd "Extra Thin" +-0.800000 0xbf4ccccd "HairLine" +-0.800000 0xbf4ccccd "Ultra Light" +-0.800000 0xbf4ccccd "Ultra Thin" +-0.800000 0xbf4ccccd "UltraLight" +-0.800000 0xbf4ccccd "ulight" +-0.800000 0xbf4ccccd "ultra light" +-0.800000 0xbf4ccccd "ultralight" +-0.800000 0xbf4ccccd 1, 10 - 100 +0.000000 0x0 ".Keyboard" +0.000000 0x0 ".LucidaGrandeUI" +0.000000 0x0 ".LucidaGrandeUI" +0.000000 0x0 "LucidaGrande" +0.000000 0x0 "LucidaGrande" +0.000000 0x0 "R" +0.000000 0x0 "TimesNewRomanPSMT" +0.000000 0x0 "TimesNewRomanPSMT" +0.000000 0x0 "W4" +0.000000 0x0 "reg" +0.000000 0x0 "w4" +0.000000 0x0 4, 301 - 400 +0.000000 0x0 default +0.230000 0x3e6b851 5, 401 - 500 +0.230000 0x3e6b851f "M" +0.230000 0x3e6b851f "Medium" +0.230000 0x3e6b851f "W5" +0.230000 0x3e6b851f "med" +0.230000 0x3e6b851f "medium" +0.230000 0x3e6b851f "w5" +0.230000 0x3e6b851f panose 6 +0.240000 0x3e75c28f "STHeiti" +0.240000 0x3fceb851eb851eb8 "STHeiti" +0.250000 0x3e800000 "Demi Bold" +0.250000 0x3e800000 "Demi" +0.250000 0x3e800000 "DemiBold" +0.250000 0x3e800000 "demi bold" +0.250000 0x3e800000 "demi" +0.250000 0x3e800000 "demibold" +0.250000 0x3e800000 6, 501 - 600 +0.250000 0x3e800000 panose 7 +0.300000 0x3e99999a "SB" +0.300000 0x3e99999a "Semi Bold" +0.300000 0x3e99999a "Semi" +0.300000 0x3e99999a "SemiBold" +0.300000 0x3e99999a "W6" +0.300000 0x3e99999a "semi bold" +0.300000 0x3e99999a "semi" +0.300000 0x3e99999a "semi" +0.300000 0x3e99999a "semibold" +0.300000 0x3e99999a "w6" +0.400000 0x3ecccccd "B" +0.400000 0x3ecccccd "Bold" +0.400000 0x3ecccccd "bold" +0.400000 0x3ecccccd "bold" +0.400000 0x3ecccccd 'head'[0x2D] & 1 +0.400000 0x3ecccccd 7, 601 - 700 +0.400000 0x3ecccccd panose 8 +0.440000 0x3ee147ae "W7" +0.440000 0x3ee147ae "w7" +0.500000 0x3f000000 "EB" +0.500000 0x3f000000 "Ext Bold" +0.500000 0x3f000000 "Extra Bold" +0.500000 0x3f000000 "Extra" +0.500000 0x3f000000 "ExtraBold" +0.500000 0x3f000000 "Ultra" +0.500000 0x3f000000 "extra bold" +0.500000 0x3f000000 "extra" +0.500000 0x3f000000 "extrabold" +0.500000 0x3f000000 8, 701 - 800 +0.540000 0x3f0a3d71 "W8" +0.540000 0x3f0a3d71 "w8" +0.560000 0x3f0f5c29 "H" +0.560000 0x3f0f5c29 "Heavy Face" +0.560000 0x3f0f5c29 "Heavy" +0.560000 0x3f0f5c29 "HeavyFace" +0.560000 0x3f0f5c29 "heavy face" +0.560000 0x3f0f5c29 "heavy" +0.560000 0x3f0f5c29 "heavy" +0.560000 0x3f0f5c29 "heavyface" +0.560000 0x3f0f5c29 panose 9 +0.620000 0x3f1eb852 "Black" +0.620000 0x3f1eb852 "Super" +0.620000 0x3f1eb852 "W9" +0.620000 0x3f1eb852 "black" +0.620000 0x3f1eb852 "black" +0.620000 0x3f1eb852 "super" +0.620000 0x3f1eb852 "w9" +0.620000 0x3f1eb852 9, 801 - 900 +0.620000 0x3f1eb852 panose 10 +0.700000 0x3f333333 "U" +0.700000 0x3f333333 "Ultra Bold" +0.700000 0x3f333333 "UltraBold" +0.700000 0x3f333333 "ultra bold" +0.700000 0x3f333333 "ultrabold" +0.750000 0x3f400000 "Fat" +0.750000 0x3f400000 "Ultra Black" +0.750000 0x3f400000 "UltraBlack" +0.750000 0x3f400000 "fat" +0.750000 0x3f400000 "ultra black" +0.750000 0x3f400000 "ultrablack" +0.750000 0x3f400000 901 - 950 +0.800000 0x3f4ccccd "Ext Black" +0.800000 0x3f4ccccd "ExtraBlack" +0.800000 0x3f4ccccd "Nord" +0.800000 0x3f4ccccd "Poster" +0.800000 0x3f4ccccd "UH" +0.800000 0x3f4ccccd "extrablack" +0.800000 0x3f4ccccd "nord" +0.800000 0x3f4ccccd "poster" +0.800000 0x3f4ccccd 951 - 999 +0.800000 0x3f4ccccd panose 11 +0.850000 0x3f59999a "Obese" +0.850000 0x3f59999a "obese" diff --git a/doc/export/writewidths.processed b/doc/export/writewidths.processed new file mode 100644 index 00000000..e0437d81 --- /dev/null +++ b/doc/export/writewidths.processed @@ -0,0 +1,11 @@ +-0.1 0xbfb9999999999998 unregistered OS2 4 +-0.2 0xbfc999999999999a unregistered OS2 3 +-0.3 0xbfd3333333333333 unregistered OS2 2 +-0.4 0xbfd999999999999a unregistered OS2 1 +-0.5 0xbfe0000000000000 unregistered OS2 0 +0 0x0000000000000000 unregistered OS2 5 +0.1 0x3fb9999999999998 unregistered OS2 6 +0.2 0x3fc9999999999998 unregistered OS2 7 +0.3 0x3fd3333333333334 unregistered OS2 8 +0.4 0x3fd999999999999a unregistered OS2 9 +0.5 0x3fe0000000000000 unregistered OS2 10 From 78bfc623993858842f6e503192fb1856638571cb Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 22 Oct 2017 10:06:28 -0400 Subject: [PATCH 0809/1329] Oops; generated weights instead of widths. --- doc/export/ctwidthsprocessed | 222 ++++++++++++----------------------- 1 file changed, 73 insertions(+), 149 deletions(-) diff --git a/doc/export/ctwidthsprocessed b/doc/export/ctwidthsprocessed index d3ec018f..26f9fcc2 100644 --- a/doc/export/ctwidthsprocessed +++ b/doc/export/ctwidthsprocessed @@ -1,149 +1,73 @@ --0.100000 0xbdcccccd "STXihei" --0.100000 0xbfb999999999999a "STXihei" --0.200000 0xbe4ccccd "EL" --0.200000 0xbe4ccccd "Semi Light" --0.200000 0xbe4ccccd "SemiLight" --0.200000 0xbe4ccccd "semi light" --0.200000 0xbe4ccccd "semilight" --0.230000 0xbe6b851f "Book" --0.230000 0xbe6b851f "W1" --0.230000 0xbe6b851f "W3" --0.230000 0xbe6b851f "book" --0.230000 0xbe6b851f "w3" --0.230000 0xbe6b851f panose 5 --0.400000 0xbecccccd "L" --0.400000 0xbecccccd "Light" --0.400000 0xbecccccd "Lite" --0.400000 0xbecccccd "light" --0.400000 0xbecccccd "light" --0.400000 0xbecccccd "lite" --0.400000 0xbecccccd 3, 201 - 300 --0.400000 0xbecccccd panose 3 --0.500000 0xbf000000 "Ext Light" --0.500000 0xbf000000 "Extra Light" --0.500000 0xbf000000 "ExtraLight" --0.500000 0xbf000000 "Thin" --0.500000 0xbf000000 "W2" --0.500000 0xbf000000 "extra light" --0.500000 0xbf000000 "extralight" --0.500000 0xbf000000 "thin" --0.500000 0xbf000000 "w2" --0.500000 0xbf000000 2, 101 - 200 --0.500000 0xbf000000 panose 2 --0.600000 0xbf19999a "thin" --0.700000 0xbf333333 "hairline" --0.700000 0xbf333333 "w1" --0.800000 0xbf4ccccd "Ext Thin" --0.800000 0xbf4ccccd "Extra Thin" --0.800000 0xbf4ccccd "HairLine" --0.800000 0xbf4ccccd "Ultra Light" --0.800000 0xbf4ccccd "Ultra Thin" --0.800000 0xbf4ccccd "UltraLight" --0.800000 0xbf4ccccd "ulight" --0.800000 0xbf4ccccd "ultra light" --0.800000 0xbf4ccccd "ultralight" --0.800000 0xbf4ccccd 1, 10 - 100 -0.000000 0x0 ".Keyboard" -0.000000 0x0 ".LucidaGrandeUI" -0.000000 0x0 ".LucidaGrandeUI" -0.000000 0x0 "LucidaGrande" -0.000000 0x0 "LucidaGrande" -0.000000 0x0 "R" -0.000000 0x0 "TimesNewRomanPSMT" -0.000000 0x0 "TimesNewRomanPSMT" -0.000000 0x0 "W4" -0.000000 0x0 "reg" -0.000000 0x0 "w4" -0.000000 0x0 4, 301 - 400 -0.000000 0x0 default -0.230000 0x3e6b851 5, 401 - 500 -0.230000 0x3e6b851f "M" -0.230000 0x3e6b851f "Medium" -0.230000 0x3e6b851f "W5" -0.230000 0x3e6b851f "med" -0.230000 0x3e6b851f "medium" -0.230000 0x3e6b851f "w5" -0.230000 0x3e6b851f panose 6 -0.240000 0x3e75c28f "STHeiti" -0.240000 0x3fceb851eb851eb8 "STHeiti" -0.250000 0x3e800000 "Demi Bold" -0.250000 0x3e800000 "Demi" -0.250000 0x3e800000 "DemiBold" -0.250000 0x3e800000 "demi bold" -0.250000 0x3e800000 "demi" -0.250000 0x3e800000 "demibold" -0.250000 0x3e800000 6, 501 - 600 -0.250000 0x3e800000 panose 7 -0.300000 0x3e99999a "SB" -0.300000 0x3e99999a "Semi Bold" -0.300000 0x3e99999a "Semi" -0.300000 0x3e99999a "SemiBold" -0.300000 0x3e99999a "W6" -0.300000 0x3e99999a "semi bold" -0.300000 0x3e99999a "semi" -0.300000 0x3e99999a "semi" -0.300000 0x3e99999a "semibold" -0.300000 0x3e99999a "w6" -0.400000 0x3ecccccd "B" -0.400000 0x3ecccccd "Bold" -0.400000 0x3ecccccd "bold" -0.400000 0x3ecccccd "bold" -0.400000 0x3ecccccd 'head'[0x2D] & 1 -0.400000 0x3ecccccd 7, 601 - 700 -0.400000 0x3ecccccd panose 8 -0.440000 0x3ee147ae "W7" -0.440000 0x3ee147ae "w7" -0.500000 0x3f000000 "EB" -0.500000 0x3f000000 "Ext Bold" -0.500000 0x3f000000 "Extra Bold" -0.500000 0x3f000000 "Extra" -0.500000 0x3f000000 "ExtraBold" -0.500000 0x3f000000 "Ultra" -0.500000 0x3f000000 "extra bold" -0.500000 0x3f000000 "extra" -0.500000 0x3f000000 "extrabold" -0.500000 0x3f000000 8, 701 - 800 -0.540000 0x3f0a3d71 "W8" -0.540000 0x3f0a3d71 "w8" -0.560000 0x3f0f5c29 "H" -0.560000 0x3f0f5c29 "Heavy Face" -0.560000 0x3f0f5c29 "Heavy" -0.560000 0x3f0f5c29 "HeavyFace" -0.560000 0x3f0f5c29 "heavy face" -0.560000 0x3f0f5c29 "heavy" -0.560000 0x3f0f5c29 "heavy" -0.560000 0x3f0f5c29 "heavyface" -0.560000 0x3f0f5c29 panose 9 -0.620000 0x3f1eb852 "Black" -0.620000 0x3f1eb852 "Super" -0.620000 0x3f1eb852 "W9" -0.620000 0x3f1eb852 "black" -0.620000 0x3f1eb852 "black" -0.620000 0x3f1eb852 "super" -0.620000 0x3f1eb852 "w9" -0.620000 0x3f1eb852 9, 801 - 900 -0.620000 0x3f1eb852 panose 10 -0.700000 0x3f333333 "U" -0.700000 0x3f333333 "Ultra Bold" -0.700000 0x3f333333 "UltraBold" -0.700000 0x3f333333 "ultra bold" -0.700000 0x3f333333 "ultrabold" -0.750000 0x3f400000 "Fat" -0.750000 0x3f400000 "Ultra Black" -0.750000 0x3f400000 "UltraBlack" -0.750000 0x3f400000 "fat" -0.750000 0x3f400000 "ultra black" -0.750000 0x3f400000 "ultrablack" -0.750000 0x3f400000 901 - 950 -0.800000 0x3f4ccccd "Ext Black" -0.800000 0x3f4ccccd "ExtraBlack" -0.800000 0x3f4ccccd "Nord" -0.800000 0x3f4ccccd "Poster" -0.800000 0x3f4ccccd "UH" -0.800000 0x3f4ccccd "extrablack" -0.800000 0x3f4ccccd "nord" -0.800000 0x3f4ccccd "poster" -0.800000 0x3f4ccccd 951 - 999 -0.800000 0x3f4ccccd panose 11 -0.850000 0x3f59999a "Obese" -0.850000 0x3f59999a "obese" +-0.100000 0xbdcccccd registered "Semi Cond" +-0.100000 0xbdcccccd registered "Semi Condensed" +-0.100000 0xbdcccccd registered "SemiCondensed" +-0.100000 0xbdcccccd registered OS2 4 +-0.100000 0xbdcccccd unregistered "semi condensed" +-0.100000 0xbdcccccd unregistered "semicondensed" +-0.200000 0xbe4ccccd head[0x2d] & 0x20 +-0.200000 0xbe4ccccd metadata "cond" +-0.200000 0xbe4ccccd panose 6 +-0.200000 0xbe4ccccd registered "Cond" +-0.200000 0xbe4ccccd registered "Condensed" +-0.200000 0xbe4ccccd registered OS2 3 +-0.200000 0xbe4ccccd unregistered "condensed" +-0.400000 0xbecccccd panose 8 +-0.400000 0xbecccccd registered "Compact" +-0.400000 0xbecccccd registered "Narrow" +-0.400000 0xbecccccd unregistered "narrow" +-0.500000 0xbf000000 registered "Compressed" +-0.500000 0xbf000000 registered "Ext Cond" +-0.500000 0xbf000000 registered "Ext Condensed" +-0.500000 0xbf000000 registered "Extra Cond" +-0.500000 0xbf000000 registered "Extra Condensed" +-0.500000 0xbf000000 registered "Extra Narrow" +-0.500000 0xbf000000 registered "ExtraNarrow" +-0.500000 0xbf000000 registered OS2 2 +-0.500000 0xbf000000 unregistered "extra narrow" +-0.500000 0xbf000000 unregistered "extranarrow" +-0.700000 0xbf333333 registered "Ext Compressed" +-0.700000 0xbf333333 registered "Extra Compressed" +-0.700000 0xbf333333 registered "Semi Condensed" +-0.700000 0xbf333333 registered "Thin" +-0.700000 0xbf333333 registered "Ultra Compressed" +-0.700000 0xbf333333 registered "Ultra Cond" +-0.700000 0xbf333333 registered "Ultra Condensed" +-0.700000 0xbf333333 registered OS2 1 +-0.700000 0xbf333333 unregistered "thin" +0.000000 0x0 default +0.000000 0x0 metadata "med" +0.000000 0x0 panose 2, 3, 4 +0.000000 0x0 registered OS2 5 +0.000000 0x0 registered default +0.100000 0x3dcccccd registered "Semi Expanded" +0.100000 0x3dcccccd registered "Semi Extended" +0.100000 0x3dcccccd registered "SemiExpanded" +0.100000 0x3dcccccd registered OS2 6 +0.100000 0x3dcccccd unregistered "semi expanded" +0.100000 0x3dcccccd unregistered "semiexpanded" +0.200000 0x3e4ccccd head[0x2d] & 0x40 +0.200000 0x3e4ccccd metadata "ext" +0.200000 0x3e4ccccd panose 5 +0.200000 0x3e4ccccd registered "Expanded" +0.200000 0x3e4ccccd registered "Extended" +0.200000 0x3e4ccccd unregistered "expanded" +0.200000 0x3e4ccccd unregistered "extended" +0.400000 0x3ecccccd panose 7 +0.400000 0x3ecccccd registered "Ext Expanded" +0.400000 0x3ecccccd registered "Ext Extended" +0.400000 0x3ecccccd registered "Extra Expanded" +0.400000 0x3ecccccd registered "Extra Extended" +0.400000 0x3ecccccd registered "ExtraExpanded" +0.400000 0x3ecccccd registered OS2 7 +0.400000 0x3ecccccd unregistered "extra expanded" +0.400000 0x3ecccccd unregistered "extraexpanded" +0.600000 0x3f19999a registered "Wide" +0.600000 0x3f19999a registered OS2 8 +0.600000 0x3f19999a unregistered "wide" +0.800000 0x3f4ccccd registered "Extra Wide" +0.800000 0x3f4ccccd registered "ExtraWide" +0.800000 0x3f4ccccd registered "Ultra Expanded" +0.800000 0x3f4ccccd registered "Ultra Extended" +0.800000 0x3f4ccccd registered OS2 9 +0.800000 0x3f4ccccd unregistered "extra wide" +0.800000 0x3f4ccccd unregistered "extrawide" From 48594e3cd155e4fe85388b130e49982ada2672cf Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 26 Oct 2017 11:44:29 -0400 Subject: [PATCH 0810/1329] More TODOs. --- TODO.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/TODO.md b/TODO.md index 7a783af9..6389f1f8 100644 --- a/TODO.md +++ b/TODO.md @@ -254,3 +254,6 @@ mac os x accessibility - https://developer.apple.com/documentation/appkit/nsworkspace/1524656-accessibilitydisplayshoulddiffer?language=objc - https://developer.apple.com/documentation/appkit/nsworkspace/1526290-accessibilitydisplayshouldincrea?language=objc - https://developer.apple.com/documentation/appkit/nsworkspace/1533006-accessibilitydisplayshouldreduce?language=objc + +uiEntry disabling bugs http://www.cocoabuilder.com/archive/cocoa/215525-nstextfield-bug-can-be.html +uiMultilineEntry disabling https://developer.apple.com/library/content/qa/qa1461/_index.html From 0a1b5ff6a867409c207f4c6516fe7d7ab6e5b185 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 31 Oct 2017 22:48:02 -0400 Subject: [PATCH 0811/1329] Oops, accidentally chopped off a hex digit from one of the ctweights values. Fixed (manually). --- doc/export/ctweights | 2 +- doc/export/ctweightsannotated | 2 +- doc/export/ctweightsprocessed | 2 +- doc/export/ctweightsraw | 2 +- doc/export/ctweightvalues | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/export/ctweights b/doc/export/ctweights index f9feb90b..0bdbd0d4 100644 --- a/doc/export/ctweights +++ b/doc/export/ctweights @@ -318,7 +318,7 @@ do401AndUp: } return float32as(0.250000, 0x3e800000), true } - return float32as(0.230000, 0x3e6b851), true + return float32as(0.230000, 0x3e6b851f), true do601AndUp: if usWeightClass > 700 { diff --git a/doc/export/ctweightsannotated b/doc/export/ctweightsannotated index 23db5436..8df56f25 100644 --- a/doc/export/ctweightsannotated +++ b/doc/export/ctweightsannotated @@ -56,7 +56,7 @@ 0.000000 0x0 "w4" 0.000000 0x0 registered OS2 weights 4, 301 - 400 0.000000 0x0 default -0.230000 0x3e6b851 registered OS2 weights 5, 401 - 500 +0.230000 0x3e6b851f registered OS2 weights 5, 401 - 500 0.230000 0x3e6b851f registered subfamily abbr "M" 0.230000 0x3e6b851f "Medium" 0.230000 0x3e6b851f "W5" diff --git a/doc/export/ctweightsprocessed b/doc/export/ctweightsprocessed index d3ec018f..fe315cc7 100644 --- a/doc/export/ctweightsprocessed +++ b/doc/export/ctweightsprocessed @@ -56,7 +56,7 @@ 0.000000 0x0 "w4" 0.000000 0x0 4, 301 - 400 0.000000 0x0 default -0.230000 0x3e6b851 5, 401 - 500 +0.230000 0x3e6b851f 5, 401 - 500 0.230000 0x3e6b851f "M" 0.230000 0x3e6b851f "Medium" 0.230000 0x3e6b851f "W5" diff --git a/doc/export/ctweightsraw b/doc/export/ctweightsraw index 0eda5021..502d473e 100644 --- a/doc/export/ctweightsraw +++ b/doc/export/ctweightsraw @@ -69,7 +69,7 @@ -0.500000 0xbf000000 2, 101 - 200 -0.400000 0xbecccccd 3, 201 - 300 0.000000 0x0 4, 301 - 400 -0.230000 0x3e6b851 5, 401 - 500 +0.230000 0x3e6b851f 5, 401 - 500 0.250000 0x3e800000 6, 501 - 600 0.400000 0x3ecccccd 7, 601 - 700 0.500000 0x3f000000 8, 701 - 800 diff --git a/doc/export/ctweightvalues b/doc/export/ctweightvalues index 24f5fe91..acbcb794 100644 --- a/doc/export/ctweightvalues +++ b/doc/export/ctweightvalues @@ -76,7 +76,7 @@ registered font, OS2 weights; table length >= 78 2, 101 - 200: float32as(-0.500000, 0xbf000000) 3, 201 - 300: float32as(-0.400000, 0xbecccccd) 4, 301 - 400: float32as(0.000000, 0x0) -5, 401 - 500: float32as(0.230000, 0x3e6b851) +5, 401 - 500: float32as(0.230000, 0x3e6b851f) 6, 501 - 600: float32as(0.250000, 0x3e800000) 7, 601 - 700: float32as(0.400000, 0x3ecccccd) 8, 701 - 800: float32as(0.500000, 0x3f000000) From b769b8163985ecf1d30cd614b5f31e349adc00d4 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 31 Oct 2017 23:49:45 -0400 Subject: [PATCH 0812/1329] Added an aggregation of Core Text weight values for my own thinking and planning. --- doc/export/ctweightscombined | 146 +++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 doc/export/ctweightscombined diff --git a/doc/export/ctweightscombined b/doc/export/ctweightscombined new file mode 100644 index 00000000..d5017938 --- /dev/null +++ b/doc/export/ctweightscombined @@ -0,0 +1,146 @@ +-0.100000 + postscript name "STXihei" (which, according to http://www.typophile.com/node/93813, means "ST Hei Light", and so we can assume it's the Light version of STHeiti, which I think is Medium though is given Regular status below because meh) + +-0.200000 + subfamily abbreviation "EL" (???????????) + style string contains "Semi Light" + style string contains "SemiLight" + +-0.230000 + style string contains "Book" + style string contains "W1" (registered fonts only; probably a typo and -0.7 was expected instead) + style string contains "W3" + panose 5 + +-0.400000 + subfamily abbreviation "L" + style string contains "Light" + style string contains "Lite" + ATSD style "light" + OS2 weights 3, 201 - 300 + panose 3 + +-0.500000 + style string contains "Ext Light" + style string contains "Extra Light" + style string contains "ExtraLight" + style string contains "Thin" + style string contains "W2" + OS2 weights 2, 101 - 200 + panose 2 + +-0.600000 + ATSD style "thin" + +-0.700000 + style string contains "hairline" + style string contains "w1" + +-0.800000 + style string contains "Ext Thin" + style string contains "Extra Thin" + style string contains "HairLine" + style string contains "Ultra Light" + style string contains "Ultra Thin" + style string contains "UltraLight" + ATSD style "ulight" + OS2 weights 1, 10 - 100 + +0.000000 + ostscript name ".Keyboard" + postscript name ".LucidaGrandeUI" + postscript name "LucidaGrande" + subfamily abbreviation "R" + postscript name "TimesNewRomanPSMT" + style string contains "W4" + ATSD style "reg" + OS2 weights 4, 301 - 400 + default + +0.230000 + OS2 weights 5, 401 - 500 + subfamily abbreviation "M" + style string contains "Medium" + style string contains "W5" + ATSD style "med" + panose 6 + +0.240000 + postscript name "STHeiti" + +0.250000 + style string contains "Demi Bold" + style string contains "Demi" + style string contains "DemiBold" + OS2 weights 6, 501 - 600 + panose 7 + +0.300000 + subfamily abbreviation "SB" + style string contains "Semi Bold" + style string contains "Semi" + style string contains "SemiBold" + style string contains "W6" + ATSD style "semi" + +0.400000 + subfamily abbreviation "B" + style string contains "Bold" + ATSD style "bold" + ('head' table byte 0x2D) & 1 != 0 + OS2 weights 7, 601 - 700 + panose 8 + +0.440000 + style string contains "W7" + +0.500000 + subfamily abbreviation "EB" + style string contains "Ext Bold" + style string contains "Extra Bold" + style string contains "Extra" + style string contains "ExtraBold" + style string contains "Ultra" + OS2 weights 8, 701 - 800 + +0.540000 + style string contains "W8" + +0.560000 + subfamily abbreviation "H" + style string contains "Heavy Face" + style string contains "Heavy" + style string contains "HeavyFace" + ATSD style "heavy" + panose 9 + +0.620000 + style string contains "Black" + style string contains "Super" + style string contains "W9" + ATSD style "black" + OS2 weights 9, 801 - 900 + panose 10 + +0.700000 + subfamily abbreviation "U" + style string contains "Ultra Bold" + style string contains "UltraBold" + +0.750000 + style string contains "Fat" + style string contains "Ultra Black" + style string contains "UltraBlack" + OS2 weights 901 - 950 + +0.800000 + style string contains "Ext Black" + style string contains "ExtraBlack" + style string contains "Nord" + style string contains "Poster" + subfamily abbreviation "UH" + OS2 weights 951 - 999 + panose 11 + +0.850000 + style string contains "Obese" From 2481610ee46f879864cd4825f02bd33bb60a68d4 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 1 Nov 2017 13:34:54 -0400 Subject: [PATCH 0813/1329] And made a similar ctwidthscombined to the previous commit's ctweightscombined. Now I can start thinking about implementation. --- doc/export/ctwidthscombined | 77 +++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 doc/export/ctwidthscombined diff --git a/doc/export/ctwidthscombined b/doc/export/ctwidthscombined new file mode 100644 index 00000000..fe4ad9d9 --- /dev/null +++ b/doc/export/ctwidthscombined @@ -0,0 +1,77 @@ +-0.100000 + style string contains "Semi Cond" + style string contains "Semi Condensed"; unregistered fonts only (see below) + style string contains "SemiCondensed" + OS2 width 4 + +-0.200000 + ('head' table byte 0x2d) & 0x20 != 0 + ATSD style "cond" + panose 6 + style string contains "Cond" + style string contains "Condensed" + OS2 width 3 + +-0.400000 + panose 8 + style string contains "Compact" + style string contains "Narrow" + +-0.500000 + style string contains "Compressed" + style string contains "Ext Cond" + style string contains "Ext Condensed" + style string contains "Extra Cond" + style string contains "Extra Condensed" + style string contains "Extra Narrow" + style string contains "ExtraNarrow" + OS2 width 2 + +-0.700000 + style string contains "Ext Compressed" + style string contains "Extra Compressed" + style string contains "Semi Condensed" (this is probably a typo, since another "Semi Condensed" with a value of -0.1 follows this in the table it comes from); registered fonts only + style string contains "Thin" + style string contains "Ultra Compressed" + style string contains "Ultra Cond" + style string contains "Ultra Condensed" + OS2 width 1 + +0.000000 + default + ATSD style "med" + panose 2, 3, 4 + OS2 width 5 + +0.100000 + style string contains "Semi Expanded" + style string contains "Semi Extended" + style string contains "SemiExpanded" + OS2 width 6 + +0.200000 + ('head' table byte 0x2d) & 0x40 != 0 + ATSD style "ext" + panose 5 + style string contains "Expanded" + style string contains "Extended" + +0.400000 + panose 7 + style string contains "Ext Expanded" + style string contains "Ext Extended" + style string contains "Extra Expanded" + style string contains "Extra Extended" + style string contains "ExtraExpanded" + OS2 width 7 + +0.600000 + style string contains "Wide" + OS2 width 8 + +0.800000 + style string contains "Extra Wide" + style string contains "ExtraWide" + style string contains "Ultra Expanded" + style string contains "Ultra Extended" + OS2 width 9 From 3108f65b394b792897c9cb0617db86c59580d698 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 1 Nov 2017 20:14:44 -0400 Subject: [PATCH 0814/1329] Wrote the initial version of the final code for converting Core Text traits into libui traits. --- doc/export/fonttraits.m | 315 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 315 insertions(+) create mode 100644 doc/export/fonttraits.m diff --git a/doc/export/fonttraits.m b/doc/export/fonttraits.m new file mode 100644 index 00000000..f3a90816 --- /dev/null +++ b/doc/export/fonttraits.m @@ -0,0 +1,315 @@ +// 1 november 2017 +#import "uipriv_darwin.h" + +static BOOL fontRegistered(CTFontDescriptorRef desc) +{ + CFNumberRef scope; + CTFontManagerScope val; + + scope = (CFNumberRef) CTFontDescriptorCopyAttribute(desc, kCTFontRegistrationScopeAttribute); + if (scope == NULL) + // header says this should be treated as the same as not registered + return NO; + if (CFNumberGetValue(scope, kCFNumberSInt32Type, &val) == false) { + // TODO + CFRelease(scope); + return NO; + } + CFRelease(scope); + // examination of Core Text shows this is accurate + return val != kCTFontManagerScopeNone; +} + +static BOOL getTraits(CTFontDescriptorRef desc, CTFontSymbolicTraits *symbolic, double *weight, double *width) +{ + CFDictionaryRef traits = NULL; + CFNumberRef num = NULL; + + traits = (CFDictionaryRef) CTFontDescriptorCopyAttribute(desc, kCTFontTraitsAttribute); + if (traits == NULL) + return NO; + + num = (CFNumberRef) CFDictionaryGetValue(traits, kCTFontSymbolicTrait); + if (num == NULL) + goto fail; + if (CFNumberGetValue(num, kCFNumberSInt32Type, symbolic) == false) + goto fail; + + num = (CFNumberRef) CFDictionaryGetValue(traits, kCTFontWeightTrait); + if (num == NULL) + goto fail; + if (CFNumberGetValue(num, kCFNumberDoubleType, weight) == false) + goto fail; + + num = (CFNumberRef) CFDictionaryGetValue(traits, kCTFontWidthTrait); + if (num == NULL) + goto fail; + if (CFNumberGetValue(num, kCFNumberDoubleType, width) == false) + goto fail; + + CFRelease(traits); + return YES; + +fail: + CFRelease(traits); + return NO; +} + +// Core Text doesn't seem to differentiate between Italic and Oblique. +// Pango's Core Text code just does a g_strrstr() (backwards case-sensitive search) for "Oblique" in the font's style name (see https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c); let's do that too I guess +static uiDrawTextFontItalic guessItalicOblique(CTFontDescriptorRef desc) +{ + CFStringRef styleName; + BOOL isOblique; + + isOblique = NO; // default value + styleName = (CFStringRef) CTFontDescriptorCopyAttribute(desc, kCTFontStyleNameAttribute); + if (styleName != NULL) { + CFRange range; + + range = CFStringFind(styleName, CFSTR("Oblique"), kCFCompareBackwards); + if (range.location != kCFNotFound) + isOblique = YES; + CFRelease(styleName); + } + if (isOblique) + return uiDrawFontItalicOblique; + return uiDrawFontItalicItalic; +} + +// Core Text does (usWidthClass - 0.5) x 10 +// this roughly maps to our values with increments of 0.1, except for the fact 0 and 10 are allowed by Core Text, despite being banned by TrueType and OpenType themselves +// we'll just treat them as identical to 1 and 9, respectively +static const uiDrawFontStretch os2WidthsToStretches[] = { + uiDrawTextStretchUltraCondensed, + uiDrawTextStretchUltraCondensed, + uiDrawTextStretchExtraCondensed, + uiDrawTextStretchCondensed, + uiDrawTextStretchSemiCondensed, + uiDrawTextStretchNormal, + uiDrawTextStretchSemiExpanded, + uiDrawTextStretchExpanded, + uiDrawTextStretchExtraExpanded, + uiDrawTextStretchUltraExpanded, + uiDrawTextStretchUltraExpanded, +}; + +static const CFStringRef exceptions[] = { + CFSTR("LucidaGrande"), + CFSTR(".LucidaGrandeUI"), + CFSTR("STHeiti"), + CFSTR("STXihei"), + CFSTR("TimesNewRomanPSMT"), + NULL, +}; + +static void trySecondaryOS2Values(CTFontDescriptorRef desc, uiDrawFontDescriptor *out, BOOL *hasWeight, BOOL *hasWidth) +{ + CTFontRef font; + CFDataRef os2; + uint16_t usWeightClass, usWidthClass; + CFStringRef psname; + CFStringRef *ex; + + *hasWeight = NO; + *hasWidth = NO; + + // only applies to unregistered fonts + if (fontRegistered(desc)) + return; + + font = CTFontCreateWithFontDescriptor(desc, 0.0, NULL); + + os2 = CTFontCopyTable(font, kCTFontTableOS2, kCTFontTableOptionNoOptions); + if (os2 == NULL) { + // no OS2 table, so no secondary values + CFRelease(font); + return; + } + + if (CFDataGetLength(os2) > 77) { + const UInt8 *b; + + b = CFDataGetBytePtr(os2); + + usWeightClass = ((uint16_t) (b[4])) << 8; + usWeightClass |= (uint16_t) (b[5]); + if (usWeightClass <= 1000) { + if (usWeightClass < 11) + usWeigthClass *= 100; + *hasWeight = YES; + } + + usWidthClass = ((uint16_t) (b[6])) << 8; + usWidthClass |= (uint16_t) (b[7]); + if (usWidthClass <= 10) + *hasWidth = YES; + } else { + usWeightClass = 0; + *hasWeight = YES; + + usWidthClass = 0; + *hasWidth = YES; + } + if (*hasWeight) + // we can just use this directly + out->Weight = usWeightClass; + if (*hasWidth) + out->Stretch = os2WidthsToStretches[usWidthClass]; + CFRelease(os2); + + // don't use secondary weights in the event of special predefined names + psname = CTFontCopyPostScriptName(font); + for (ex = exceptions; *ex != NULL; ex++) + if (CFEqual(psname, *ex)) { + *hasWeight = NO; + break; + } + CFRelease(psname); + + CFRelease(font); +} + +static const CFStringRef subfamilyKeys[] = { + kCTFontSubFamilyNameKey, + // TODO explicitly mark these as undocumented + CFSTR("CTFontPreferredSubFamilyName"), + kCTFontFullNameKey, + CFSTR("CTFontPreferredFamilyName"), + kCTFontFamilyNameKey, + NULL, +}; + +static BOOL testTTFOTFSubfamilyNames(CTFontDescriptorRef desc, CFStringRef want) +{ + CFNumberRef num; + CTFontFormat type; + CGFontRef font; + CFString *key; + + num = (CFNumberRef) CTFontDescriptorCopyAttribute(desc, kCTFontFormatAttribute); + if (num == NULL) { + // TODO + return NO; + } + if (CFNumberGetValue(num, kCFNumberSInt32Type, &type) == false) { + // TODO + CFRelease(num); + return NO; + } + CFRelease(num); + switch (type) { + case kCTFontFormatOpenTypePostScript: + case kCTFontFormatOpenTypeTrueType: + case kCTFontFormatTrueType: + break; + default: + return NO; + } + + font = CTFontCreateWithFontDescriptor(desc, 0.0, NULL); + for (key = subfamilyKeys; *key != NULL; key++) { + CFStringRef val; + CFRange range; + + val = CTFontCopyName(font, *key); + if (val == NULL) + continue; + range.location = 0; + range.length = CFStringGetLength(val); + if (CFStringFindWithOptions(val, want, range, + (kCFCompareCaseInsensitive | kCFCompareBackwards | kCFCompareNonliteral), NULL) != false) { + CFRelease(val); + CFRelease(font); + return YES; + } + CFRelease(val); + } + CFRelease(font); + return NO; +} + +// work around a bug in libFontRegistry.dylib +static BOOL shouldReallyBeThin(CTFontDescriptorRef desc) +{ + return testTTFOTFSubfamilyNames(desc, CFSTR("W1")); +} + +// work around a bug in libFontRegistry.dylib +static BOOL shouldReallyBeSemiCondensed(CTFontDescriptorRef desc) +{ + return testTTFOTFSubfamilyNames(desc, CFSTR("Semi Condensed")); +} + +void processFontTraits(CTFontDescriptorRef desc, uiDrawFontDescriptor *out) +{ + CTFontSymbolicTraits symbolic; + double weight; + double width; + BOOL hasWeight, hasWidth; + uint16_t usWeightClasss, usWidthClass; + CTFontRef font; + + if (!getTraits(desc, &symbolic, &weight, &width)) { + // TODO + goto fail; + } + + out->Italic = uiDrawTextItalicNormal; + if ((symbolic & kCTFontItalicTrait) != 0) + out->Italic = guessItalicOblique(desc); + + hasWeight = NO; + hasWidth = NO; + trySecondaryOS2Values(desc, out, &hasWeight, &hasWidth); + + if (!hasWeight) + // TODO this scale is a bit lopsided + if (weight <= -0.7) + out->Weight = uiDrawTextWeightThin; + else if (weight <= -0.5) + out->Weight = uiDrawTextWeightUltraLight; + else if (weight <= -0.3) + out->Weight = uiDrawTextWeightLight; + else if (weight <= -0.23) { + out->Weight = uiDrawTextWeightBook; + if (shouldReallyBeThin(desc)) + out->Weight = uiDrawTextWeightThin; + } else if (weight <= 0.0) + out->Weight = uiDrawTextWeightNormal; + else if (weight <= 0.23) + out->Weight = uiDrawTextWeightMedium; + else if (weight <= 0.3) + out->Weight = uiDrawTextWeightSemiBold; + else if (weight <= 0.4) + out->Weight = uiDrawTextWeightBold; + else if (weight <= 0.5) + out->Weight = uiDrawTextWeightUltraBold; + else if (weight <= 0.7) + out->Weight = uiDrawTextWeightHeavy; + else + out->Weight = uiDrawTextWeightUltraHeavy; + + if (!hasWidth) + // TODO this scale is a bit lopsided + if (width <= -0.7) { + out->Stretch = uiDrawTextStretchUltraCondensed; + if (shouldReallyBeSemiCondensed(desc)) + out->Stretch = uiDrawTextStretchSemiCondensed; + } else if (width <= -0.5) + out->Stretch = uiDrawTextStretchExtraCondensed; + else if (width <= -0.2) + out->Stretch = uiDrawTextStretchCondensed; + else if (width <= -0.1) + out->Stretch = uiDrawTextStretchSemiCondensed; + else if (width <= 0.0) + out->Stretch = uiDrawTextStretchNormal; + else if (width <= 0.1) + out->Stretch = uiDrawTextStretchSemiExpanded; + else if (width <= 0.2) + out->Stretch = uiDrawTextStretchExpanded; + else if (width <= 0.6) + out->Stretch = uiDrawTextStretchExtraExpanded; + else + out->Stretch = uiDrawTextStretchUltraExpanded; +} From a7bbbc8bb9e304ce2de80ebecb8aafc44d2d74cd Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 1 Nov 2017 20:20:12 -0400 Subject: [PATCH 0815/1329] Changed a slight thing in fonttraits.m. --- doc/export/fonttraits.m | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/doc/export/fonttraits.m b/doc/export/fonttraits.m index f3a90816..1f49fc65 100644 --- a/doc/export/fonttraits.m +++ b/doc/export/fonttraits.m @@ -170,12 +170,15 @@ static void trySecondaryOS2Values(CTFontDescriptorRef desc, uiDrawFontDescriptor CFRelease(font); } +// TODO explicitly mark these as undocumented +extern const CFStringRef kCTFontPreferredSubFamilyNameKey; +extern const CFStringRef kCTFontPreferredFamilyNameKey; + static const CFStringRef subfamilyKeys[] = { kCTFontSubFamilyNameKey, - // TODO explicitly mark these as undocumented - CFSTR("CTFontPreferredSubFamilyName"), + kCTFontPreferredSubFamilyNameKey, kCTFontFullNameKey, - CFSTR("CTFontPreferredFamilyName"), + kCTFontPreferredFamilyNameKey, kCTFontFamilyNameKey, NULL, }; From 6b295b2d3f0a58dd539234154b47e0604990bc2c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 1 Nov 2017 20:23:52 -0400 Subject: [PATCH 0816/1329] And fixed a comment placeholder (and typo) and reformatted it slightly. --- doc/export/fonttraits.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/export/fonttraits.m b/doc/export/fonttraits.m index 1f49fc65..10aa45ea 100644 --- a/doc/export/fonttraits.m +++ b/doc/export/fonttraits.m @@ -77,9 +77,9 @@ static uiDrawTextFontItalic guessItalicOblique(CTFontDescriptorRef desc) return uiDrawFontItalicItalic; } -// Core Text does (usWidthClass - 0.5) x 10 -// this roughly maps to our values with increments of 0.1, except for the fact 0 and 10 are allowed by Core Text, despite being banned by TrueType and OpenType themselves -// we'll just treat them as identical to 1 and 9, respectively +// Core Text does (usWidthClass / 10) - 0.5 here. +// This roughly maps to our values with increments of 0.1, except for the fact 0 and 10 are allowed by Core Text, despite being banned by TrueType and OpenType themselves. +// We'll just treat them as identical to 1 and 9, respectively. static const uiDrawFontStretch os2WidthsToStretches[] = { uiDrawTextStretchUltraCondensed, uiDrawTextStretchUltraCondensed, From 03b089c972eaa84b409f7b06b1a33a38c1a63851 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 2 Nov 2017 02:46:16 -0400 Subject: [PATCH 0817/1329] Added a fvar table test. This might be a bit more complicated... --- doc/export/fvar.swift | 61 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 doc/export/fvar.swift diff --git a/doc/export/fvar.swift b/doc/export/fvar.swift new file mode 100644 index 00000000..74f02395 --- /dev/null +++ b/doc/export/fvar.swift @@ -0,0 +1,61 @@ +// 2 november 2017 +import Cocoa +import CoreText + +func fourccString(_ k: FourCharCode) -> String { + var c: [UInt8] = [0, 0, 0, 0] + c[0] = UInt8((k >> 24) & 0xFF) + c[1] = UInt8((k >> 16) & 0xFF) + c[2] = UInt8((k >> 8) & 0xFF) + c[3] = UInt8(k & 0xFF) + return String(bytes: c, encoding: .utf8)! +} + +var weightMin: Double = 0 +var weightMax: Double = 0 +var weightDef: Double = 0 +var weightVals: [String: Double] = [:] + +let attrs: [String: String] = [ + kCTFontFamilyNameAttribute as String: "Skia", +] +let bd = CTFontDescriptorCreateWithAttributes(attrs as CFDictionary) +let matches = CTFontDescriptorCreateMatchingFontDescriptors(bd, nil) as! [CTFontDescriptor] +let mfont = CTFontCreateWithFontDescriptor(matches[0], 0.0, nil) +let master = CTFontCopyVariationAxes(mfont) as! [NSDictionary] +master.forEach { d in + print("axis {") + d.forEach { k, v in + if (k as! String) == (kCTFontVariationAxisIdentifierKey as String) { + let c = v as! FourCharCode + print("\t\(k) \(fourccString(c))") + } else { + print("\t\(k) \(v)") + } + } + print("}") + if (d[kCTFontVariationAxisNameKey] as! String) == "Weight" { + weightMin = d[kCTFontVariationAxisMinimumValueKey] as! Double + weightMax = d[kCTFontVariationAxisMaximumValueKey] as! Double + weightDef = d[kCTFontVariationAxisDefaultValueKey] as! Double + } +} +print("") +matches.forEach { d in + let f = CTFontDescriptorCopyAttribute(d, kCTFontVariationAttribute) as! [FourCharCode: Double] + let n = CTFontDescriptorCopyAttribute(d, kCTFontStyleNameAttribute) as! String + print("\(n) {") + f.forEach { k, v in + print("\t\(fourccString(k)) \(v)") + } + print("}") + weightVals[n] = weightDef + if let v = f[2003265652] { + weightVals[n] = v + } +} +print("") +weightVals.forEach { k, v in + let scaled = (v - weightMin) / (weightMax - weightMin) + print("\(k) scaled = \(scaled) (OS2 \(UInt16(scaled * 1000)))") +} From 830753d888a66c556d6dc56cb26c7d145a5b23c9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 2 Nov 2017 03:17:33 -0400 Subject: [PATCH 0818/1329] More fvar.swift stuff. This is gonna hurt... --- doc/export/fvar.swift | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/doc/export/fvar.swift b/doc/export/fvar.swift index 74f02395..30617e45 100644 --- a/doc/export/fvar.swift +++ b/doc/export/fvar.swift @@ -56,6 +56,16 @@ matches.forEach { d in } print("") weightVals.forEach { k, v in - let scaled = (v - weightMin) / (weightMax - weightMin) - print("\(k) scaled = \(scaled) (OS2 \(UInt16(scaled * 1000)))") + let basicScaled = (v - weightMin) / (weightMax - weightMin) + print("\(k) basic scaled = \(basicScaled) (OS2 \(UInt16(basicScaled * 1000)))") + // https://www.microsoft.com/typography/otspec/otvaroverview.htm#CSN + var opentypeScaled: Double = 0 + if v < weightDef { + opentypeScaled = -((weightDef - v) / (weightDef - weightMin)) + } else if v > weightDef { + opentypeScaled = (v - weightDef) / (weightMax - weightDef) + } + print("\(k) opentype scaled = \(opentypeScaled)") } +print("") +print("\(CTFontDescriptorCreateMatchingFontDescriptors(CTFontDescriptorCreateCopyWithVariation(matches[0], FourCharCode(2003265652) as CFNumber, CGFloat(weightMax)), Set([kCTFontVariationAttribute as String]) as CFSet))") From 2e5f2c273b0b69629fc85f185797b970c8504bab Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 2 Nov 2017 10:39:43 -0400 Subject: [PATCH 0819/1329] More fvar.swift stuff. I'm going to need a font with an avar table... --- doc/export/fvar.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/export/fvar.swift b/doc/export/fvar.swift index 30617e45..3d81c9d8 100644 --- a/doc/export/fvar.swift +++ b/doc/export/fvar.swift @@ -68,4 +68,6 @@ weightVals.forEach { k, v in print("\(k) opentype scaled = \(opentypeScaled)") } print("") -print("\(CTFontDescriptorCreateMatchingFontDescriptors(CTFontDescriptorCreateCopyWithVariation(matches[0], FourCharCode(2003265652) as CFNumber, CGFloat(weightMax)), Set([kCTFontVariationAttribute as String]) as CFSet))") +print("\(String(describing: CTFontDescriptorCreateMatchingFontDescriptors(CTFontDescriptorCreateCopyWithVariation(matches[0], FourCharCode(2003265652) as CFNumber, CGFloat(weightMax)), Set([kCTFontVariationAttribute as String]) as CFSet)))") +print("") +print("\(CTFontCopyTable(mfont, CTFontTableTag(kCTFontTableAvar), []) != nil)") From b7e6311621957f94d7893c7f4b58de0618a1920d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 2 Nov 2017 19:27:57 -0400 Subject: [PATCH 0820/1329] Started writing the code for processing font variations for Core Text so we can process Skia correctly. --- doc/export/fontvariation.m | 161 +++++++++++++++++++++++++++++++++++++ doc/export/ttfixedtest.go | 58 +++++++++++++ 2 files changed, 219 insertions(+) create mode 100644 doc/export/fontvariation.m create mode 100644 doc/export/ttfixedtest.go diff --git a/doc/export/fontvariation.m b/doc/export/fontvariation.m new file mode 100644 index 00000000..58bae666 --- /dev/null +++ b/doc/export/fontvariation.m @@ -0,0 +1,161 @@ +// 2 november 2017 +#import "uipriv_darwin.h" + +// references: +// - https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6fvar.html +// - https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6avar.html +// - https://www.microsoft.com/typography/otspec/fvar.htm +// - https://www.microsoft.com/typography/otspec/otvaroverview.htm#CSN +// - https://www.microsoft.com/typography/otspec/otff.htm +// - https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6.html#Types +// - https://www.microsoft.com/typography/otspec/avar.htm + +typedef uint32_t fixed1616; +typedef uint16_t fixed214; + +static fixed1616 doubleToFixed1616(double d) +{ + double ipart, fpart; + long flong; + int16_t i16; + uint32_t ret; + + fpart = fabs(modf(d, &ipart)); + fpart *= 65536; + flong = lround(fpart); + i16 = (int16_t) ipart; + ret = (uint32_t) ((uint16_t) i16); + ret <<= 16; + ret |= (uint16_t) (flong & 0xFFFF); + return (fixed1616) ret; +} + +static double fixed1616ToDouble(fixed1616 f) +{ + int16_t base; + double frac; + + base = (int16_t) ((f >> 16) & 0xFFFF); + frac = ((double) (f & 0xFFFF)) / 65536; + return ((double) base) + frac; +} + +static fixed214 fixed1616ToFixed214(fixed1616 f) +{ + uint32_t t; + uint32_t topbit; + + t = f + 0x00000002; + topbit = t & 0x80000000; + t >>= 2; + if (topbit != 0) + t |= 0xC000000; + return (fixed214) (t & 0xFFFF); +} + +static double fixed214ToDouble(fixed214 f) +{ + double base; + double frac; + + switch ((f >> 14) & 0x3) { + case 0: + base = 0; + break; + case 1: + base = 1; + break; + case 2: + base = -2: + break; + case 3: + base = -1; + } + frac = ((double) (f & 0x3FFF)) / 16384; + return base + frac; +} + +static fixed1616 fixed214ToFixed1616(fixed214 f) +{ + int32_t t; + uint32_t x; + + t = (int32_t) ((int16_t) f); + t <<= 2; + x = (uint32_t) t; + return (float1616) (x - 0x00000002); +} + +static const fixed1616Negative1 = 0xFFFF0000; +static const fixed1616Zero = 0x00000000; +static const fixed1616Positive1 = 0x00010000; + +static fixed1616 normalize1616(fixed1616 val, fixed1616 min, fixed1616 max, fixed1616 def) +{ + if (val < min) + val = min; + if (val > max) + val = max; + if (val < def) + return -(def - val) / (def - min); + if (val > def) + return (val - def) / (max - def); + return fixed1616Zero; +} + +static fixed214 normalizedTo214(fixed1616 val, const fixed1616 *avarMappings, size_t avarCount) +{ + if (val < fixed1616Negative1) + val = fixed1616Negative1; + if (val > fixed1616Positive1) + val = fixed1616Positive1; + if (avarCount != 0) { + size_t start, end; + float1616 startFrom, endFrom; + float1616 startTo, endTo; + + for (end = 0; end < avarCount; end += 2) { + endFrom = avarMappings[end]; + endTo = avarMappings[end + 1]; + if (endFrom >= val) + break; + } + if (endFrom == val) + val = endTo; + else { + start = end - 2; + startFrom = avarMappings[start]; + startTo = avarMappings[start + 1]; + val = (val - startFrom) / (endFrom - startFrom); + val *= (endTo - startTo); + val += startTo; + } + } + return fixed1616ToFixed214(val); +} + +fixed1616 *avarExtract(CFDataRef table, size_t index, size_t *n) +{ + const UInt8 *b; + size_t off; + size_t i, nEntries; + fixed1616 *entries; + fixed1616 *p; + + b = CFDataGetBytePtr(table); + off = 8; +#define nextuint16be() ((((uint16_t) (b[off])) << 8) | ((uint16_t) (b[off + 1]))) + for (; index > 0; index--) { + nEntries = (size_t) nextuint16be(); + off += 2; + off += 4 * nEntries; + } + nEntries = nextuint16be(); + *n = nEntries * 2; + entries = (fixed1616 *) uiAlloc(*n * sizeof (fixed1616), "fixed1616[]"); + for (i = 0; i < *n; i++) { + *p++ = fixed214ToFixed1616((fixed214) nextuint16be()); + off += 2; + } + return entries; +} diff --git a/doc/export/ttfixedtest.go b/doc/export/ttfixedtest.go new file mode 100644 index 00000000..217afa50 --- /dev/null +++ b/doc/export/ttfixedtest.go @@ -0,0 +1,58 @@ +// 2 november 2017 +package main + +import ( + "fmt" +) + +type fixed1616 uint32 +type fixed214 uint16 + +func fixed1616To214(f fixed1616) fixed214 { + f += 0x00000002 + g := int32(f) >> 2 + return fixed214(uint32(g) & 0xFFFF) +} + +func (f fixed1616) In214Range() bool { + base := int16((f >> 16) & 0xFFFF) + return base >= -2 && base < 2 +} + +func (f fixed1616) String() string { + base := int16((f >> 16) & 0xFFFF) + frac := float64(f & 0xFFFF) / 65536 + res := float64(base) + frac + return fmt.Sprintf("%f 0x%08X", res, uint32(f)) +} + +func (f fixed214) String() string { + base := []int16{ + 0, + 1, + -2, + -1, + }[(f & 0xC000) >> 14] + frac := float64(f & 0x3FFF) / 16384 + res := float64(base) + frac + return fmt.Sprintf("%f 0x%04X", res, uint16(f)) +} + +func main() { + fmt.Println(fixed214(0x7fff)) + fmt.Println(fixed214(0x8000)) + fmt.Println(fixed214(0x4000)) + fmt.Println(fixed214(0xc000)) + fmt.Println(fixed214(0x7000)) + fmt.Println(fixed214(0x0000)) + fmt.Println(fixed214(0x0001)) + fmt.Println(fixed214(0xffff)) + + fmt.Println() + + for i := uint64(0x00000000); i <= 0xFFFFFFFF; i++ { + j := fixed1616(i) + if !j.In214Range() { continue } + fmt.Println(j, "->", fixed1616To214(j)) + } +} From f2971f637f11f9d8381bf705efc337d2ddf3544a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 2 Nov 2017 19:33:41 -0400 Subject: [PATCH 0821/1329] Added fvar axis identifiers. --- doc/export/fontvariation.m | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/export/fontvariation.m b/doc/export/fontvariation.m index 58bae666..d97a6158 100644 --- a/doc/export/fontvariation.m +++ b/doc/export/fontvariation.m @@ -10,6 +10,9 @@ // - https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6.html#Types // - https://www.microsoft.com/typography/otspec/avar.htm +#define fvarWeight 0x77676874 +#define fvarWidth 0x77647468 + typedef uint32_t fixed1616; typedef uint16_t fixed214; From 57a6ea0a77a233439b6c38a38e52bf5698fe24aa Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 2 Nov 2017 21:10:38 -0400 Subject: [PATCH 0822/1329] Moved fontmatch.m alongside the other font files as we prepare to combine everything. --- {darwin => doc/export}/fontmatch.m | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {darwin => doc/export}/fontmatch.m (100%) diff --git a/darwin/fontmatch.m b/doc/export/fontmatch.m similarity index 100% rename from darwin/fontmatch.m rename to doc/export/fontmatch.m From be6a07755b8b99eb219ebd63b9f235c89407b6af Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 2 Nov 2017 21:51:00 -0400 Subject: [PATCH 0823/1329] Described what each of the three .m files are going to do. --- doc/export/fontmatch.m | 30 +++++++++++++++++++++++------- doc/export/fonttraits.m | 17 +++++++++++++++++ doc/export/fontvariation.m | 20 +++++++++++++++++++- 3 files changed, 59 insertions(+), 8 deletions(-) diff --git a/doc/export/fontmatch.m b/doc/export/fontmatch.m index abd38c32..fe2dceae 100644 --- a/doc/export/fontmatch.m +++ b/doc/export/fontmatch.m @@ -1,13 +1,29 @@ // 3 january 2017 #import "uipriv_darwin.h" -// Stupidity: Core Text requires an **exact match for the entire traits dictionary**, otherwise it will **drop ALL the traits**. -// This seems to be true for every function in Core Text that advertises that it performs font matching; I can confirm at the time of writing this is the case for -// - CTFontDescriptorCreateMatchingFontDescriptors() (though the conditions here seem odd) -// - CTFontCreateWithFontDescriptor() -// - CTFontCreateCopyWithAttributes() -// We have to implement the closest match ourselves. -// This needs to be done early in the matching process; in particular, we have to do this before adding any features (such as small caps), because the matching descriptors won't have those. +// Core Text exposes font style info in two forms: +// - Fonts with a QuickDraw GX font variation (fvar) table, a feature +// adopted by OpenType, expose variations directly. +// - All other fonts have Core Text normalize the font style info +// into a traits dictionary. +// Of course this setup doesn't come without its hiccups and +// glitches. In particular, not only are the exact rules not well +// defined, but also font matching doesn't work as we want it to +// (exactly how varies based on the way the style info is exposed). +// So we'll have to implement style matching ourselves. +// We can use Core Text's matching to get a complete list of +// *possible* matches, and then we can filter out the ones we don't +// want ourselves. +// +// To make things easier for us, we'll match by converting Core +// Text's values back into libui values. This allows us to also use the +// normalization code for filling in uiDrawFontDescriptors from +// Core Text fonts and font descriptors. +// +// Style matching needs to be done early in the font loading process; +// in particular, we have to do this before adding any features, +// because the descriptors returned by Core Text's own font +// matching won't have any. struct closeness { CFIndex index; diff --git a/doc/export/fonttraits.m b/doc/export/fonttraits.m index 10aa45ea..0c57b9d4 100644 --- a/doc/export/fonttraits.m +++ b/doc/export/fonttraits.m @@ -1,6 +1,23 @@ // 1 november 2017 #import "uipriv_darwin.h" +// This is the part of the font style matching and normalization code +// that handles fonts that use a traits dictionary. +// +// Matching stupidity: Core Text requires an **exact match for the +// entire traits dictionary**, otherwise it will **drop ALL the traits**. +// +// Normalization stupidity: Core Text uses its own scaled values for +// weight and width, but the values are different if the font is not +// registered and if said font is TrueType or OpenType. The values +// for all other cases do have some named constants starting with +// OS X 10.11, but even these aren't very consistent in practice. +// +// Of course, none of this is documented anywhere, so I had to do +// both trial-and-error AND reverse engineering to figure out what's +// what. We'll just convert Core Text's values into libui constants +// and use those for matching. + static BOOL fontRegistered(CTFontDescriptorRef desc) { CFNumberRef scope; diff --git a/doc/export/fontvariation.m b/doc/export/fontvariation.m index d97a6158..79236613 100644 --- a/doc/export/fontvariation.m +++ b/doc/export/fontvariation.m @@ -1,7 +1,25 @@ // 2 november 2017 #import "uipriv_darwin.h" -// references: +// This is the part of the font style matching and normalization code +// that handles fonts that use the fvar table. +// +// Matching stupidity: Core Text **doesn't even bother** matching +// these, even if you tell it to do so explicitly. It'll always return +// all variations for a given font. +// +// Normalization stupidity: Core Text doesn't normalize the fvar +// table values for us, so we'll have to do it ourselves. Furthermore, +// Core Text doesn't provide an API for accessing the avar table, if +// any, so we must do so ourselves. (TODO does Core Text even +// follow the avar table if a font has it?) +// +// Thankfully, normalization is well-defined in both TrueType and +// OpenType and seems identical in both, so we can just normalize +// the values and then convert them linearly to libui values for +// matching. +// +// References: // - https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6fvar.html // - https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6avar.html // - https://www.microsoft.com/typography/otspec/fvar.htm From 4e7fb5e264aa84f16de198474c0504c844ee7f1b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 3 Nov 2017 15:51:21 -0400 Subject: [PATCH 0824/1329] Added code that handles all the attribute stuff for a CTFontRef AND CTFontDescriptorRef for us; we'll use this throughout the various font style files instead of doing everything ourselves. --- doc/export/fontmatch.m | 257 +++++++++++++++++++++++++++++++++++++++++ doc/export/fontstyle.h | 56 +++++++++ 2 files changed, 313 insertions(+) create mode 100644 doc/export/fontstyle.h diff --git a/doc/export/fontmatch.m b/doc/export/fontmatch.m index fe2dceae..74cbe2e1 100644 --- a/doc/export/fontmatch.m +++ b/doc/export/fontmatch.m @@ -25,6 +25,263 @@ // because the descriptors returned by Core Text's own font // matching won't have any. +@implementation fontStyleData + +- (id)initWithFont:(CTFontRef)f +{ + self = [super init]; + if (self) { + self->font = f; + CFRetain(self->font); + self->desc = CTFontCopyDescriptor(self->font); + if (![self prepare]) { + [self release]; + return nil; + } + } + return self; +} + +- (id)initWithDescriptor:(CTFontDescriptorRef)d +{ + self = [super init]; + if (self) { + self->font = NULL; + self->desc = d; + CFRetain(self->desc); + if (![self prepare]) { + [self release]; + return nil; + } + } + return self; +} + +- (void)dealloc +{ +#define REL(x) if (x != NULL) { CFRelease(x); x = NULL; } + REL(self->variationAxes); + REL(self->familyName); + REL(self->preferredFamilyName); + REL(self->fullName); + REL(self->subFamilyName); + REL(self->preferredSubFamilyName); + REL(self->postScriptName); + REL(self->variations); + REL(self->styleName); + REL(self->traits); + CFRelease(self->desc); + REL(self->font); + [super dealloc]; +} + +- (BOOL)prepare +{ + CFNumberRef num; + + self->traits = NULL; + self->symbolic = 0; + self->weight = 0; + self->width = 0; + self->didStyleName = NO; + self->styleName = NULL; + self->didVariations = NO; + self->variations = NULL; + self->didRegistrationScope = NO; + self->hasRegistrationScope = NO; + self->registrationScope = 0; + self->didPostScriptName = NO; + self->postScriptName = NULL; + self->didFontFormat = NO; + self->fontFormat = 0; + self->didPreferredSubFamilyName = NO; + self->preferredSubFamilyName = NULL; + self->didSubFamilyName = NO; + self->subFamilyName = NULL; + self->didFullName = NO; + self->fullName = NULL; + self->didPreferredFamilyName = NO; + self->preferredFamilyName = NULL; + self->didFamilyName = NO; + self->familyName = NULL; + self->didVariationAxes = NO; + self->variationAxes = NULL; + + self->traits = (CFDictionaryRef) CTFontDescriptorCopyAttribute(self->desc, kCTFontTraitsAttribute); + if (self->traits == NULL) + return NO; + + num = (CFNumberRef) CFDictionaryGetValue(self->traits, kCTFontSymbolicTrait); + if (num == NULL) + return NO; + if (CFNumberGetValue(num, kCFNumberSInt32Type, &(self->symbolic)) == false) + return NO; + + num = (CFNumberRef) CFDictionaryGetValue(self->traits, kCTFontWeightTrait); + if (num == NULL) + return NO; + if (CFNumberGetValue(num, kCFNumberDoubleType, &(self->weight)) == false) + return NO; + + num = (CFNumberRef) CFDictionaryGetValue(self->traits, kCTFontWidthTrait); + if (num == NULL) + return NO; + if (CFNumberGetValue(num, kCFNumberDoubleType, &(self->width)) == false) + return NO; + + return YES; +} + +- (void)ensureFont +{ + if (self->font != NULL) + return; + self->font = CTFontCreateWithFontDescriptor(self->desc, 0.0, NULL); +} + +- (CTFontSymbolicTraits)symbolicTraits +{ + return self->symbolic; +} + +- (double)weight +{ + return self->weight; +} + +- (double)width +{ + return self->width; +} + +- (CFStringRef)styleName +{ + if (!self->didStyleName) { + self->didStyleName = YES; + self->styleName = (CFStringRef) CTFontDescriptorCopyAttribute(self->desc, kCTFontStyleNameAttribute); + // The code we use this for (guessItalicOblique() below) checks if this is NULL or not, so we're good. + } + return self->styleName; +} + +- (CFDictionaryRef)variations +{ + if (!self->didVariations) { + self->didVariations = YES; + self->variations = (CFDictionaryRef) CTFontDescriptorCopyAttribute(self->desc, kCTFontVariationsAttribute); + // This being NULL is used to determine whether a font uses variations at all, so we don't need to worry now. + } + return self->variations; +} + +- (BOOL)hasRegistrationScope +{ + if (!self->didRegistrationScope) { + CFNumberRef num; + + self->didRegistrationScope = YES; + num = (CFNumberRef) CTFontDescriptorCopyAttribute(desc, kCTFontRegistrationScopeAttribute); + self->hasRegistrationScope = num != NULL; + if (self->hasRegistrationScope) { + if (CFNumberGetValue(num, kCFNumberSInt32Type, &(self->registrationScope)) == false) { + // TODO + } + CFRelease(num); + } + } + return self->hasRegistrationScope; +} + +- (CTFontManagerScope)registrationScope +{ + [self hasRegistrationScope]; + return self->registrationScope; +} + +- (CFStringRef)postScriptName +{ + if (!self->didPostScriptName) { + self->didPostScriptName = YES; + [self ensureFont]; + self->postScriptName = CTFontCopyPostScriptName(self->font); + } + return self->postScriptName; +} + +- (CFDataRef)table:(CTFontTableTag)tag +{ + [self ensureFont]; + return CTFontCopyTable(self->font, tag, kCTFontTableOptionNoOptions); +} + +- (CTFontFormat)fontFormat +{ + if (!self->didFontFormat) { + CFNumberRef num; + + self->didFontFormat = YES; + num = (CFNumberRef) CTFontDescriptorCopyAttribute(self->desc, kCTFontFormatAttribute); + if (num == NULL) { + // TODO + } + if (CFNumberGetValue(num, kCFNumberSInt32Type, &(self->fontFormat)) == false) { + // TODO + } + CFRelease(num); + } + return self->fontFormat; +} + +// We don't need to worry if this or any of the functions that use it return NULL, because the code that uses it (libFontRegistry.dylib bug workarounds in fonttraits.m) checks for NULL. +- (CFStringRef)fontName:(CFStringRef)key +{ + [self ensureFont]; + return CTFontCopyName(self->font, key); +} + +#define FONTNAME(sel, did, var, key) \ + - (CFString)sel \ + { \ + if (!did) { \ + did = YES; \ + var = [self fontName:key]; \ + } \ + return var; \ + } +FONTNAME(preferredSubFamilyName, + self->didPreferredSubFamilyName, + self->preferredSubFamilyName, + kCTFontPreferredSubFamilyNameKey) +FONTNAME(subFamilyName, + self->didSubFamilyName, + self->subFamilyName, + kCTFontSubFamilyNameKey) +FONTNAME(fullName, + self->didFullName, + self->fullName, + kCTFontFullNameKey) +FONTNAME(preferredFamilyName, + self->didPreferredFamilyName, + self->preferredFamilyName, + kCTFontPreferredFamilyNameKey) +FONTNAME(familyName, + self->didFamilyName, + self->familyName, + kCTFontFamilyNameKey) + +- (CFArrayRef)variationAxes +{ + if (!self->didVariationAxes) { + self->didVariationAxes = YES; + [self ensureFont]; + self->variationAxes = CTFontCopyVariationAxes(self->font); + // We don't care about the return value because we call this only on fonts that we know have variations anyway. + } + return self->variationAxes; +} + +@end + struct closeness { CFIndex index; double weight; diff --git a/doc/export/fontstyle.h b/doc/export/fontstyle.h new file mode 100644 index 00000000..c94ef3fd --- /dev/null +++ b/doc/export/fontstyle.h @@ -0,0 +1,56 @@ +// 3 november 2017 + +// fontstyle.m +@interface fontStyleData : NSObject { + CTFontRef font; + CTFontDescriptorRef desc; + CFDictionaryRef traits; + CTFontSymbolicTraits symbolic; + double weight; + double width; + BOOL didStyleName; + CFStringRef styleName; + BOOL didVariations; + CFDictionaryRef variations; + BOOL didRegistrationScope; + BOOL hasRegistrationScope; + CTFontManagerScope registrationScope; + BOOL didPostScriptName; + CFStringRef postScriptName; + BOOL didFontFormat; + CTFontFormat fontFormat; + BOOL didPreferredSubFamilyName; + CFStringRef preferredSubFamilyName; + BOOL didSubFamilyName; + CFStringRef subFamilyName; + BOOL didFullName; + CFStringRef fullName; + BOOL didPreferredFamilyName; + CFStringRef preferredFamilyName; + BOOL didFamilyName; + CFStringRef familyName; + BOOL didVariationAxes; + CFArrayRef variationAxes; +} +- (id)initWithFont:(CTFontRef)f; +- (id)initWithDescriptor:(CTFontDescriptorRef)d; +- (BOOL)prepare; +- (void)ensureFont; +- (CTFontSymbolicTraits)symbolicTraits; +- (double)weight; +- (double)width; +- (CFStringRef)styleName; +- (CFDictionaryRef)variations; +- (BOOL)hasRegistrationScope; +- (CTFontManagerScope)registrationScope; +- (CFStringRef)postScriptName; +- (CFDataRef)table:(CTFontTableTag)tag; +- (CTFontFormat)fontFormat; +- (CFStringRef)fontName:(CFStringRef)key; +- (CFStringRef)preferredSubFamilyName; +- (CFStringRef)subFamilyName; +- (CFStringRef)fullName; +- (CFStringRef)preferredFamilyName; +- (CFStringRef)familyName; +- (CFArrayRef)variationAxes; +@end From 51f0e3dbe5f2720510072fbcd984b0362b81735f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 3 Nov 2017 18:41:13 -0400 Subject: [PATCH 0825/1329] Folded all CFNumber accesses into [self prepare] for error checking. This shouldn't make things *significantly* slower... --- doc/export/fontmatch.m | 48 +++++++++++++++++------------------------- doc/export/fontstyle.h | 2 -- 2 files changed, 19 insertions(+), 31 deletions(-) diff --git a/doc/export/fontmatch.m b/doc/export/fontmatch.m index 74cbe2e1..ddb7b906 100644 --- a/doc/export/fontmatch.m +++ b/doc/export/fontmatch.m @@ -78,6 +78,7 @@ - (BOOL)prepare { CFNumberRef num; + Boolean success; self->traits = NULL; self->symbolic = 0; @@ -87,12 +88,10 @@ self->styleName = NULL; self->didVariations = NO; self->variations = NULL; - self->didRegistrationScope = NO; self->hasRegistrationScope = NO; self->registrationScope = 0; self->didPostScriptName = NO; self->postScriptName = NULL; - self->didFontFormat = NO; self->fontFormat = 0; self->didPreferredSubFamilyName = NO; self->preferredSubFamilyName = NULL; @@ -129,6 +128,24 @@ if (CFNumberGetValue(num, kCFNumberDoubleType, &(self->width)) == false) return NO; + // do these now for the sake of error checking + num = (CFNumberRef) CTFontDescriptorCopyAttribute(desc, kCTFontRegistrationScopeAttribute); + self->hasRegistrationScope = num != NULL; + if (self->hasRegistrationScope) { + success = CFNumberGetValue(num, kCFNumberSInt32Type, &(self->registrationScope)); + CFRelease(num); + if (success == false) + return NO; + } + + num = (CFNumberRef) CTFontDescriptorCopyAttribute(self->desc, kCTFontFormatAttribute); + if (num == NULL) + return NO; + success = CFNumberGetValue(num, kCFNumberSInt32Type, &(self->fontFormat)); + CFRelease(num); + if (success == false) + return NO; + return YES; } @@ -176,25 +193,11 @@ - (BOOL)hasRegistrationScope { - if (!self->didRegistrationScope) { - CFNumberRef num; - - self->didRegistrationScope = YES; - num = (CFNumberRef) CTFontDescriptorCopyAttribute(desc, kCTFontRegistrationScopeAttribute); - self->hasRegistrationScope = num != NULL; - if (self->hasRegistrationScope) { - if (CFNumberGetValue(num, kCFNumberSInt32Type, &(self->registrationScope)) == false) { - // TODO - } - CFRelease(num); - } - } return self->hasRegistrationScope; } - (CTFontManagerScope)registrationScope { - [self hasRegistrationScope]; return self->registrationScope; } @@ -216,19 +219,6 @@ - (CTFontFormat)fontFormat { - if (!self->didFontFormat) { - CFNumberRef num; - - self->didFontFormat = YES; - num = (CFNumberRef) CTFontDescriptorCopyAttribute(self->desc, kCTFontFormatAttribute); - if (num == NULL) { - // TODO - } - if (CFNumberGetValue(num, kCFNumberSInt32Type, &(self->fontFormat)) == false) { - // TODO - } - CFRelease(num); - } return self->fontFormat; } diff --git a/doc/export/fontstyle.h b/doc/export/fontstyle.h index c94ef3fd..636e0992 100644 --- a/doc/export/fontstyle.h +++ b/doc/export/fontstyle.h @@ -12,12 +12,10 @@ CFStringRef styleName; BOOL didVariations; CFDictionaryRef variations; - BOOL didRegistrationScope; BOOL hasRegistrationScope; CTFontManagerScope registrationScope; BOOL didPostScriptName; CFStringRef postScriptName; - BOOL didFontFormat; CTFontFormat fontFormat; BOOL didPreferredSubFamilyName; CFStringRef preferredSubFamilyName; From 72d31285c17e325a6ee47f4c1341ce3200290183 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 3 Nov 2017 18:52:15 -0400 Subject: [PATCH 0826/1329] Migrated fonttraits.m to use the new fontStyleData class. --- doc/export/fontmatch.m | 4 + doc/export/fonttraits.m | 172 ++++++++++------------------------------ 2 files changed, 48 insertions(+), 128 deletions(-) diff --git a/doc/export/fontmatch.m b/doc/export/fontmatch.m index ddb7b906..91a3b8af 100644 --- a/doc/export/fontmatch.m +++ b/doc/export/fontmatch.m @@ -25,6 +25,10 @@ // because the descriptors returned by Core Text's own font // matching won't have any. +// TODO explicitly mark these as undocumented +extern const CFStringRef kCTFontPreferredSubFamilyNameKey; +extern const CFStringRef kCTFontPreferredFamilyNameKey; + @implementation fontStyleData - (id)initWithFont:(CTFontRef)f diff --git a/doc/export/fonttraits.m b/doc/export/fonttraits.m index 0c57b9d4..919dd2a8 100644 --- a/doc/export/fonttraits.m +++ b/doc/export/fonttraits.m @@ -1,5 +1,6 @@ // 1 november 2017 #import "uipriv_darwin.h" +#import "fontstyle.h" // This is the part of the font style matching and normalization code // that handles fonts that use a traits dictionary. @@ -18,76 +19,30 @@ // what. We'll just convert Core Text's values into libui constants // and use those for matching. -static BOOL fontRegistered(CTFontDescriptorRef desc) +static BOOL fontRegistered(fontStyleData *d) { - CFNumberRef scope; - CTFontManagerScope val; - - scope = (CFNumberRef) CTFontDescriptorCopyAttribute(desc, kCTFontRegistrationScopeAttribute); - if (scope == NULL) + if (![d hasRegistrationScope]) // header says this should be treated as the same as not registered return NO; - if (CFNumberGetValue(scope, kCFNumberSInt32Type, &val) == false) { - // TODO - CFRelease(scope); - return NO; - } - CFRelease(scope); // examination of Core Text shows this is accurate - return val != kCTFontManagerScopeNone; -} - -static BOOL getTraits(CTFontDescriptorRef desc, CTFontSymbolicTraits *symbolic, double *weight, double *width) -{ - CFDictionaryRef traits = NULL; - CFNumberRef num = NULL; - - traits = (CFDictionaryRef) CTFontDescriptorCopyAttribute(desc, kCTFontTraitsAttribute); - if (traits == NULL) - return NO; - - num = (CFNumberRef) CFDictionaryGetValue(traits, kCTFontSymbolicTrait); - if (num == NULL) - goto fail; - if (CFNumberGetValue(num, kCFNumberSInt32Type, symbolic) == false) - goto fail; - - num = (CFNumberRef) CFDictionaryGetValue(traits, kCTFontWeightTrait); - if (num == NULL) - goto fail; - if (CFNumberGetValue(num, kCFNumberDoubleType, weight) == false) - goto fail; - - num = (CFNumberRef) CFDictionaryGetValue(traits, kCTFontWidthTrait); - if (num == NULL) - goto fail; - if (CFNumberGetValue(num, kCFNumberDoubleType, width) == false) - goto fail; - - CFRelease(traits); - return YES; - -fail: - CFRelease(traits); - return NO; + return [d registrationScope] != kCTFontManagerScopeNone; } // Core Text doesn't seem to differentiate between Italic and Oblique. // Pango's Core Text code just does a g_strrstr() (backwards case-sensitive search) for "Oblique" in the font's style name (see https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c); let's do that too I guess -static uiDrawTextFontItalic guessItalicOblique(CTFontDescriptorRef desc) +static uiDrawTextFontItalic guessItalicOblique(fontStyleData *d) { CFStringRef styleName; BOOL isOblique; isOblique = NO; // default value - styleName = (CFStringRef) CTFontDescriptorCopyAttribute(desc, kCTFontStyleNameAttribute); + styleName = [d styleName]; if (styleName != NULL) { CFRange range; range = CFStringFind(styleName, CFSTR("Oblique"), kCFCompareBackwards); if (range.location != kCFNotFound) isOblique = YES; - CFRelease(styleName); } if (isOblique) return uiDrawFontItalicOblique; @@ -120,9 +75,8 @@ static const CFStringRef exceptions[] = { NULL, }; -static void trySecondaryOS2Values(CTFontDescriptorRef desc, uiDrawFontDescriptor *out, BOOL *hasWeight, BOOL *hasWidth) +static void trySecondaryOS2Values(fontStyleData *d, uiDrawFontDescriptor *out, BOOL *hasWeight, BOOL *hasWidth) { - CTFontRef font; CFDataRef os2; uint16_t usWeightClass, usWidthClass; CFStringRef psname; @@ -132,17 +86,13 @@ static void trySecondaryOS2Values(CTFontDescriptorRef desc, uiDrawFontDescriptor *hasWidth = NO; // only applies to unregistered fonts - if (fontRegistered(desc)) + if (fontRegistered(d)) return; - font = CTFontCreateWithFontDescriptor(desc, 0.0, NULL); - - os2 = CTFontCopyTable(font, kCTFontTableOS2, kCTFontTableOptionNoOptions); - if (os2 == NULL) { + os2 = [d table:kCTFontTableOS2]; + if (os2 == NULL) // no OS2 table, so no secondary values - CFRelease(font); return; - } if (CFDataGetLength(os2) > 77) { const UInt8 *b; @@ -176,49 +126,32 @@ static void trySecondaryOS2Values(CTFontDescriptorRef desc, uiDrawFontDescriptor CFRelease(os2); // don't use secondary weights in the event of special predefined names - psname = CTFontCopyPostScriptName(font); + psname = [d postScriptName]; for (ex = exceptions; *ex != NULL; ex++) if (CFEqual(psname, *ex)) { *hasWeight = NO; break; } - CFRelease(psname); - - CFRelease(font); } -// TODO explicitly mark these as undocumented -extern const CFStringRef kCTFontPreferredSubFamilyNameKey; -extern const CFStringRef kCTFontPreferredFamilyNameKey; - -static const CFStringRef subfamilyKeys[] = { - kCTFontSubFamilyNameKey, - kCTFontPreferredSubFamilyNameKey, - kCTFontFullNameKey, - kCTFontPreferredFamilyNameKey, - kCTFontFamilyNameKey, - NULL, -}; - -static BOOL testTTFOTFSubfamilyNames(CTFontDescriptorRef desc, CFStringRef want) +static BOOL testTTFOTFSubfamilyName(CFStringRef name, CFStringRef want) +{ + CFRange range; + + if (name == NULL) + return NO; + range.location = 0; + range.length = CFStringGetLength(name); + return CFStringFindWithOptions(name, want, range, + (kCFCompareCaseInsensitive | kCFCompareBackwards | kCFCompareNonliteral), NULL) != false; +} + +static BOOL testTTFOTFSubfamilyNames(fontStyleData *d, CFStringRef want) { - CFNumberRef num; - CTFontFormat type; CGFontRef font; CFString *key; - num = (CFNumberRef) CTFontDescriptorCopyAttribute(desc, kCTFontFormatAttribute); - if (num == NULL) { - // TODO - return NO; - } - if (CFNumberGetValue(num, kCFNumberSInt32Type, &type) == false) { - // TODO - CFRelease(num); - return NO; - } - CFRelease(num); - switch (type) { + switch ([d fontFormat]) { case kCTFontFormatOpenTypePostScript: case kCTFontFormatOpenTypeTrueType: case kCTFontFormatTrueType: @@ -227,26 +160,15 @@ static BOOL testTTFOTFSubfamilyNames(CTFontDescriptorRef desc, CFStringRef want) return NO; } - font = CTFontCreateWithFontDescriptor(desc, 0.0, NULL); - for (key = subfamilyKeys; *key != NULL; key++) { - CFStringRef val; - CFRange range; - - val = CTFontCopyName(font, *key); - if (val == NULL) - continue; - range.location = 0; - range.length = CFStringGetLength(val); - if (CFStringFindWithOptions(val, want, range, - (kCFCompareCaseInsensitive | kCFCompareBackwards | kCFCompareNonliteral), NULL) != false) { - CFRelease(val); - CFRelease(font); - return YES; - } - CFRelease(val); - } - CFRelease(font); - return NO; + if (testTTFOTFSubfamilyName([d preferredSubFamilyName], want)) + return YES; + if (testTTFOTFSubfamilyName([d subFamilyName], want)) + return YES; + if (testTTFOTFSubfamilyName([d fullName], want)) + return YES; + if (testTTFOTFSubfamilyName([d preferredFamilyName], want)) + return YES; + return testTTFOTFSubfamilyName([d familyName], want); } // work around a bug in libFontRegistry.dylib @@ -261,27 +183,21 @@ static BOOL shouldReallyBeSemiCondensed(CTFontDescriptorRef desc) return testTTFOTFSubfamilyNames(desc, CFSTR("Semi Condensed")); } -void processFontTraits(CTFontDescriptorRef desc, uiDrawFontDescriptor *out) +void processFontTraits(fontStyleData *d, uiDrawFontDescriptor *out) { - CTFontSymbolicTraits symbolic; - double weight; - double width; + double weight, width; BOOL hasWeight, hasWidth; - uint16_t usWeightClasss, usWidthClass; - CTFontRef font; - - if (!getTraits(desc, &symbolic, &weight, &width)) { - // TODO - goto fail; - } out->Italic = uiDrawTextItalicNormal; - if ((symbolic & kCTFontItalicTrait) != 0) - out->Italic = guessItalicOblique(desc); + if (([d symbolicTraits] & kCTFontItalicTrait) != 0) + out->Italic = guessItalicOblique(d); hasWeight = NO; hasWidth = NO; - trySecondaryOS2Values(desc, out, &hasWeight, &hasWidth); + trySecondaryOS2Values(d, out, &hasWeight, &hasWidth); + + weight = [d weight]; + width = [d width]; if (!hasWeight) // TODO this scale is a bit lopsided @@ -293,7 +209,7 @@ void processFontTraits(CTFontDescriptorRef desc, uiDrawFontDescriptor *out) out->Weight = uiDrawTextWeightLight; else if (weight <= -0.23) { out->Weight = uiDrawTextWeightBook; - if (shouldReallyBeThin(desc)) + if (shouldReallyBeThin(d)) out->Weight = uiDrawTextWeightThin; } else if (weight <= 0.0) out->Weight = uiDrawTextWeightNormal; @@ -314,7 +230,7 @@ void processFontTraits(CTFontDescriptorRef desc, uiDrawFontDescriptor *out) // TODO this scale is a bit lopsided if (width <= -0.7) { out->Stretch = uiDrawTextStretchUltraCondensed; - if (shouldReallyBeSemiCondensed(desc)) + if (shouldReallyBeSemiCondensed(d)) out->Stretch = uiDrawTextStretchSemiCondensed; } else if (width <= -0.5) out->Stretch = uiDrawTextStretchExtraCondensed; From 8333063cc0b8b45109e1606542f94c72cad57d56 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 3 Nov 2017 19:52:49 -0400 Subject: [PATCH 0827/1329] And implemented fontvariation.m. --- doc/export/fontmatch.m | 1 + doc/export/fontstyle.h | 7 ++ doc/export/fontvariation.m | 140 ++++++++++++++++++++++++++++++++++++- 3 files changed, 147 insertions(+), 1 deletion(-) diff --git a/doc/export/fontmatch.m b/doc/export/fontmatch.m index 91a3b8af..ae56601e 100644 --- a/doc/export/fontmatch.m +++ b/doc/export/fontmatch.m @@ -1,5 +1,6 @@ // 3 january 2017 #import "uipriv_darwin.h" +#import "fontstyle.h" // Core Text exposes font style info in two forms: // - Fonts with a QuickDraw GX font variation (fvar) table, a feature diff --git a/doc/export/fontstyle.h b/doc/export/fontstyle.h index 636e0992..a61393fb 100644 --- a/doc/export/fontstyle.h +++ b/doc/export/fontstyle.h @@ -52,3 +52,10 @@ - (CFStringRef)familyName; - (CFArrayRef)variationAxes; @end + +// fonttraits.m +extern void processFontTraits(fontStyleData *d, uiDrawFontDescriptor *out); + +// fontvariation.m +extern NSDictionary *mkVariationAxisDict(CFArrayRef axes, CFDataRef avarTable); +extern void processFontVariation(fontStyleData *d, NSDictionary *axisDict, uiDrawFontDescriptor *out); diff --git a/doc/export/fontvariation.m b/doc/export/fontvariation.m index 79236613..bf89220c 100644 --- a/doc/export/fontvariation.m +++ b/doc/export/fontvariation.m @@ -1,5 +1,6 @@ // 2 november 2017 #import "uipriv_darwin.h" +#import "fontstyle.h" // This is the part of the font style matching and normalization code // that handles fonts that use the fvar table. @@ -155,7 +156,7 @@ static fixed214 normalizedTo214(fixed1616 val, const fixed1616 *avarMappings, si return fixed1616ToFixed214(val); } -fixed1616 *avarExtract(CFDataRef table, size_t index, size_t *n) +static fixed1616 *avarExtract(CFDataRef table, CFIndex index, size_t *n) { const UInt8 *b; size_t off; @@ -180,3 +181,140 @@ fixed1616 *avarExtract(CFDataRef table, size_t index, size_t *n) } return entries; } + +staatic BOOL extractAxisDictValue(CFDictionaryRef dict, CFStringRef key, fixed1616 *out) +{ + CFNumberRef num; + double v; + + num = (CFNumberRef) CFDictionaryGetValue(dict, key); + if (CFNumberGetValue(num, kCFNumberTypeDouble, &v) == false) + return NO; + *out = doubleToFixed1616(v); + return YES; +} + +@interface fvarAxis : NSObject { + fixed1616 min; + fixed1616 max; + fixed1616 def; + fixed1616 *avarMappings; + size_t avarCount; +} +- (id)initWithIndex:(CFIndex)i dict:(CFDictionaryRef)dict avarTable:(CFDataRef)table; +- (double)normalize:(double)v; +@end + +@implementation fvarAxis + +- (id)initWithIndex:(CFIndex)i dict:(CFDictionaryRef)dict avarTable:(CFDataRef)table +{ + self = [super init]; + if (self) { + self->avarMappings = NULL; + self->avarCount = 0; + if (!extractAxisDictValue(dict, kCTFontVariationAxisMinimumValueKey, &(self->min))) + goto fail; + if (!extractAxisDictValue(dict, kCTFontVariationAxisMaximumValueKey, &(self->max))) + goto fail; + if (!extractAxisDictValue(dict, kCTFontVariationAxisDefaultValueKey, &(self->def))) + goto fail; + if (table != NULL) + self->avarMappings = avarExtract(table, i, &(self->avarCount)); + } + return self; + +fail: + [self release]; + return nil; +} + +- (void)dealloc +{ + if (self->avarMappings != NULL) { + uiFree(self->avarMappings); + self->avarMappings = NULL; + } + [super dealloc]; +} + +- (double)normalize:(double)d +{ + fixed1616 n; + fixed214 n2; + + n = doubleToFixed1616(d); + n = fixed16161Normalize(n, self->min, self->max, self->def); + n2 = normalizedTo214(n, self->avarMappings, self->avarCount); + return fixed214ToDouble(n2); +} + +@end + +NSDictionary *mkVariationAxisDict(CFArrayRef axes, CFDataRef avarTable) +{ + CFDictionaryRef axis; + CFIndex i, n; + NSMutableDictionary *out; + + n = CFArrayGetLength(axes); + out = [NSMutableDictionary new]; + for (i = 0; i < n; i++) { + CFNumberRef key; + + axis = (CFDictionaryRef) CFArrayGetValueAtIndex(axes, i); + key = (CFNumberRef) CFDictionaryGetValue(axis, kCTFontVariationAxisIdentifierKey); + [out setObject:[[fvarAxis alloc] initWithIndex:i dict:axis avarTable:table] + forKey:((NSNumber *) key)]; + } + if (table != NULL) + CFRelease(table); + return out; +} + +#define fvarAxisKey(n) [NSNumber numberWithUnsignedInteger:k] + +static BOOL tryAxis(NSDictionary *axisDict, CFDictionaryRef var, NSNumber *key, double *out) +{ + fvarAxis *axis; + CFNumberRef num; + + axis = (fvarAxis *) [axisDict objectForKey:key]; + if (axis == nil) + return NO; + num = (CFNumberRef) CFDictionaryGetValue(var, (CFNumberRef) key); + if (num == nil) + return NO; + if (CFNumberGetValue(num, kCFNumberTypeDouble, out) == false) { + // TODO + return NO; + } + *out = [axis normalize:*out]; + return YES; +} + +void processFontVariation(fontStyleData *d, NSDictionary *axisDict, uiDrawFontDescriptor *out) +{ + double v; + + out->Weight = uiDrawTextWeightNormal; + out->Stretch = uiDrawTextStretchNormal; + + var = [d variation]; + + if (tryAxis(axisDict, var, fvarAxisKey(fvarWeight), &v)) { + // v is now a value between -1 and 1 scaled linearly between discrete points + // we want a linear value between 0 and 1000 with 400 being normal + if (v < 0) { + v += 1; + out->Weight = (uiDrawTextWeight) (v * 400); + } else if (v > 0) + out->Weight += (uiDrawTextWeight) (v * 600); + } + + if (tryAxis(axisDict, var, fvarAxisKey(fvarWidth), &v)) { + // likewise, but with stretches, we go from 0 to 8 with 4 being directly between the two, so this is sufficient + v += 1; + out->Stretch = (uiDrawTextStretch) (v * 4); + } +} From 2276a136cb35d24313c0220c73e263da4a2d4743 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 3 Nov 2017 20:33:08 -0400 Subject: [PATCH 0828/1329] And tied everything together. Now we move everything back and test. --- doc/export/fontmatch.m | 288 ++++++++++--------------------------- doc/export/fontstyle.h | 2 +- doc/export/fonttraits.m | 25 ---- doc/export/fontvariation.m | 2 +- 4 files changed, 79 insertions(+), 238 deletions(-) diff --git a/doc/export/fontmatch.m b/doc/export/fontmatch.m index ae56601e..68e9d72b 100644 --- a/doc/export/fontmatch.m +++ b/doc/export/fontmatch.m @@ -279,96 +279,74 @@ FONTNAME(familyName, struct closeness { CFIndex index; - double weight; + uiDrawTextWeight weight; double italic; - double stretch; + uiDrawTextStretch stretch; double distance; }; -static double doubleAttr(CFDictionaryRef traits, CFStringRef attr) -{ - CFNumberRef cfnum; - double val; - - cfnum = (CFNumberRef) CFDictionaryGetValue(traits, attr); - if (cfnum == NULL) { - // TODO - } - if (CFNumberGetValue(cfnum, kCFNumberDoubleType, &val) == false) { - // TODO - } - // Get Rule; do not release cfnum - return val; -} - -struct italicCloseness { - double normal; - double oblique; - double italic; -}; - // remember that in closeness, 0 means exact // in this case, since we define the range, we use 0.5 to mean "close enough" (oblique for italic and italic for oblique) and 1 to mean "not a match" -static const struct italicCloseness italicClosenesses[] = { - [uiDrawTextItalicNormal] = { 0, 1, 1 }, - [uiDrawTextItalicOblique] = { 1, 0, 0.5 }, - [uiDrawTextItalicItalic] = { 1, 0.5, 0 }, +static const double italicClosenessNormal[] = { 0, 1, 1 }; +static const double italicClosenessOblique[] = { 1, 0, 0.5 }; +static const double italicClosenessItalic[] = { 1, 0.5, 0 }; +static const double *italicClosenesses[] = { + [uiDrawTextItalicNormal] = italicClosenessNormal, + [uiDrawTextItalicOblique] = italicClosenessOblique, + [uiDrawTextItalicItalic] = italicClosenessItalic, }; +// Core Text doesn't seem to differentiate between Italic and Oblique. +// Pango's Core Text code just does a g_strrstr() (backwards case-sensitive search) for "Oblique" in the font's style name (see https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c); let's do that too I guess +static uiDrawTextFontItalic guessItalicOblique(fontStyleData *d) +{ + CFStringRef styleName; + BOOL isOblique; + + isOblique = NO; // default value + styleName = [d styleName]; + if (styleName != NULL) { + CFRange range; + + range = CFStringFind(styleName, CFSTR("Oblique"), kCFCompareBackwards); + if (range.location != kCFNotFound) + isOblique = YES; + } + if (isOblique) + return uiDrawFontItalicOblique; + return uiDrawFontItalicItalic; +} + // Italics are hard because Core Text does NOT distinguish between italic and oblique. // All Core Text provides is a slant value and the italic bit of the symbolic traits mask. // However, Core Text does seem to guarantee (from experimentation; see below) that the slant will be nonzero if and only if the italic bit is set, so we don't need to use the slant value. // Core Text also seems to guarantee that if a font lists itself as Italic or Oblique by name (font subfamily name, font style name, whatever), it will also have that bit set, so testing this bit does cover all fonts that name themselves as Italic and Oblique. (Again, this is from the below experimentation.) // TODO there is still one catch that might matter from a user's POV: the reverse is not true — the italic bit can be set even if the style of the font face/subfamily/style isn't named as Italic (for example, script typefaces like Adobe's Palace Script MT Std); I don't know what to do about this... I know how to start: find a script font that has an italic form (Adobe's Palace Script MT Std does not; only Regular and Semibold) -static double italicCloseness(CTFontDescriptorRef desc, CFDictionaryRef traits, uiDrawTextItalic italic) +static void setItalic(fontStyleData *d, uiDrawFontDescriptor *out) { - const struct italicCloseness *ic = &(italicClosenesses[italic]); - CFNumberRef cfnum; - CTFontSymbolicTraits symbolic; - // there is no kCFNumberUInt32Type, but CTFontSymbolicTraits is uint32_t, so SInt32 should work - SInt32 s; - CFStringRef styleName; - BOOL isOblique; - - cfnum = CFDictionaryGetValue(traits, kCTFontSymbolicTrait); - if (cfnum == NULL) { - // TODO - } - if (CFNumberGetValue(cfnum, kCFNumberSInt32Type, &s) == false) { - // TODO - } - symbolic = (CTFontSymbolicTraits) s; - // Get Rule; do not release cfnum - if ((symbolic & kCTFontItalicTrait) == 0) - return ic->normal; - - // Okay, now we know it's either Italic or Oblique - // Pango's Core Text code just does a g_strrstr() (backwards case-sensitive search) for "Oblique" in the font's style name (see https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c); let's do that too I guess - isOblique = NO; // default value - styleName = (CFStringRef) CTFontDescriptorCopyAttribute(desc, kCTFontStyleNameAttribute); - // TODO is styleName guaranteed? - if (styleName != NULL) { - CFRange range; - - // note the use of the toll-free bridge for the string literal, since CFSTR() *can* return NULL - // TODO is this really the case? or is that just a copy-paste error from the other CFStringCreateXxx() functions? and what's this about -fconstant-cfstring? - range = CFStringFind(styleName, (CFStringRef) @"Oblique", kCFCompareBackwards); - if (range.location != kCFNotFound) - isOblique = YES; - CFRelease(styleName); - } - if (isOblique) - return ic->oblique; - return ic->italic; + out->Italic = uiDrawTextItalicNormal; + if (([d symbolicTraits] & kCTFontItalicTrait) != 0) + out->Italic = guessItalicOblique(d); } -static CTFontDescriptorRef matchTraits(CTFontDescriptorRef against, double targetWeight, uiDrawTextItalic targetItalic, double targetStretch) +static void fillDescStyleFields(fontStyleData *d, NSDictionary *axisDict, uiDrawFontDescriptor *out) +{ + setItalic(d, out); + if (axisDict != nil) + processFontVariation(d, axisDict, out); + else + processFontTraits(d, out); +} + +static CTFontDescriptorRef matchStyle(CTFontDescriptorRef against, uiDrawFontDescriptor *styles) { CFArrayRef matching; CFIndex i, n; struct closeness *closeness; CTFontDescriptorRef current; CTFontDescriptorRef out; + fontStyleData *d; + NSDictionary *axisDict; matching = CTFontDescriptorCreateMatchingFontDescriptors(against, NULL); if (matching == NULL) @@ -381,37 +359,38 @@ static CTFontDescriptorRef matchTraits(CTFontDescriptorRef against, double targe return against; } + current = (CTFontDescriptorRef) CFArrayGetValueAtIndex(matches, 0); + d = [[fontStyleData alloc] initWithDescriptor:current]; + axisDict = nil; + if ([d variations] != nil) + axisDict = mkAxisDict(d, [d table:kCTFontTableAvar]); + closeness = (struct closeness *) uiAlloc(n * sizeof (struct closeness), "struct closeness[]"); for (i = 0; i < n; i++) { - CFDictionaryRef traits; + uiDrawFontDescriptor fields; closeness[i].index = i; - current = (CTFontDescriptorRef) CFArrayGetValueAtIndex(matching, i); - traits = (CFDictionaryRef) CTFontDescriptorCopyAttribute(current, kCTFontTraitsAttribute); - if (traits == NULL) { - // couldn't get traits; be safe by ranking it lowest - // LONGTERM figure out what the longest possible distances are - closeness[i].weight = 3; - closeness[i].italic = 2; - closeness[i].stretch = 3; - continue; + if (i != 0) { + current = (CTFontDescriptorRef) CFArrayGetValueAtIndex(matching, i); + d = [[fontStyleData alloc] initWithDescriptor:current]; } - closeness[i].weight = doubleAttr(traits, kCTFontWeightTrait) - targetWeight; - closeness[i].italic = italicCloseness(current, traits, targetItalic); - closeness[i].stretch = doubleAttr(traits, kCTFontWidthTrait) - targetStretch; - CFRelease(traits); + fillDescStyleFields(d, axisDict, &fields); + closeness[i].weight = fields.Weight - styles->Weight; + closeness[i].italic = italicClosenesses[styles->Italic][fields.Italic]; + closeness[i].stretch = fields.Stretch - styles->Stretch; + [d release]; } // now figure out the 3-space difference between the three and sort by that // TODO merge this loop with the previous loop? for (i = 0; i < n; i++) { - double weight, italic, stretch; + double weight, stretch; - weight = closeness[i].weight; + weight = (double) (closeness[i].weight); weight *= weight; italic = closeness[i].italic; italic *= italic; - stretch = closeness[i].stretch; + stretch = (double) (closeness[i].stretch); stretch *= stretch; closeness[i].distance = sqrt(weight + italic + stretch); } @@ -428,6 +407,8 @@ static CTFontDescriptorRef matchTraits(CTFontDescriptorRef against, double targe CFRetain(out); // get rule // release everything + if (axisDict != nil) + [axisDict release]; uiFree(closeness); CFRelease(matching); // and release the original descriptor since we no longer need it @@ -436,60 +417,6 @@ static CTFontDescriptorRef matchTraits(CTFontDescriptorRef against, double targe return out; } -// since uiDrawTextWeight effectively corresponds to OS/2 weights (which roughly correspond to GDI, Pango, and DirectWrite weights, and to a lesser(? TODO) degree, CSS weights), let's just do what Core Text does with OS/2 weights -// TODO this will not be correct for system fonts, which use cached values that have no relation to the OS/2 weights; we need to figure out how to reconcile these -// for more information, see https://bugzilla.gnome.org/show_bug.cgi?id=766148 and TODO_put_blog_post_here_once_I_write_it (TODO keep this line when resolving the above TODO) -static const double weightsToCTWeights[] = { - -1.0, // 0..99 - -0.7, // 100..199 - -0.5, // 200..299 - -0.23, // 300..399 - 0.0, // 400..499 - 0.2, // 500..599 - 0.3, // 600..699 - 0.4, // 700..799 - 0.6, // 800..899 - 0.8, // 900..999 - 1.0, // 1000 -}; - -static double weightToCTWeight(uiDrawTextWeight weight) -{ - int weightClass; - double ctclass; - double rest, weightFloor, nextFloor; - - if (weight <= 0) - return -1.0; - if (weight >= 1000) - return 1.0; - - weightClass = weight / 100; - rest = (double) weight; - weightFloor = (double) (weightClass * 100); - nextFloor = (double) ((weightClass + 1) * 100); - rest = (rest - weightFloor) / (nextFloor - weightFloor); - - ctclass = weightsToCTWeights[weightClass]; - return fma(rest, - weightsToCTWeights[weightClass + 1] - ctclass, - ctclass); -} - -// based on what Core Text says about actual fonts (system fonts, system fonts in another folder to avoid using cached values, Adobe Font Folio 11, Google Fonts archive, fonts in Windows 7/8.1/10) -static const double stretchesToCTWidths[] = { - [uiDrawTextStretchUltraCondensed] = -0.400000, - [uiDrawTextStretchExtraCondensed] = -0.300000, - [uiDrawTextStretchCondensed] = -0.200000, - [uiDrawTextStretchSemiCondensed] = -0.100000, - [uiDrawTextStretchNormal] = 0.000000, - [uiDrawTextStretchSemiExpanded] = 0.100000, - [uiDrawTextStretchExpanded] = 0.200000, - [uiDrawTextStretchExtraExpanded] = 0.300000, - // this one isn't present in any of the fonts I tested, but it follows naturally from the pattern of the rest, so... (TODO verify by checking the font files directly) - [uiDrawTextStretchUltraExpanded] = 0.400000, -}; - CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd) { CFMutableDictionaryRef attrs; @@ -516,10 +443,7 @@ CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd) basedesc = CTFontDescriptorCreateWithAttributes(attrs); CFRelease(attrs); // TODO correct? - return matchTraits(basedesc, - weightToCTWeight(fd->Weight), - fd->Italic, - stretchesToCTWidths[fd->Stretch]); + return matchTraits(basedesc, fd); } // fortunately features that aren't supported are simply ignored, so we can copy them all in @@ -545,55 +469,11 @@ CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uiOpe return new; } -// TODO deduplicate this from italicCloseness() -static uiDrawTextItalic italicFromCTItalic(CTFontDescriptorRef desc, CFDictionaryRef traits) -{ - CFNumberRef cfnum; - CTFontSymbolicTraits symbolic; - // there is no kCFNumberUInt32Type, but CTFontSymbolicTraits is uint32_t, so SInt32 should work - SInt32 s; - CFStringRef styleName; - BOOL isOblique; - - cfnum = CFDictionaryGetValue(traits, kCTFontSymbolicTrait); - if (cfnum == NULL) { - // TODO - } - if (CFNumberGetValue(cfnum, kCFNumberSInt32Type, &s) == false) { - // TODO - } - symbolic = (CTFontSymbolicTraits) s; - // Get Rule; do not release cfnum - if ((symbolic & kCTFontItalicTrait) == 0) - return uiDrawTextItalicNormal; - - // Okay, now we know it's either Italic or Oblique - // Pango's Core Text code just does a g_strrstr() (backwards case-sensitive search) for "Oblique" in the font's style name (see https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c); let's do that too I guess - isOblique = NO; // default value - styleName = (CFStringRef) CTFontDescriptorCopyAttribute(desc, kCTFontStyleNameAttribute); - // TODO is styleName guaranteed? - if (styleName != NULL) { - CFRange range; - - // note the use of the toll-free bridge for the string literal, since CFSTR() *can* return NULL - // TODO is this really the case? or is that just a copy-paste error from the other CFStringCreateXxx() functions? and what's this about -fconstant-cfstring? - range = CFStringFind(styleName, (CFStringRef) @"Oblique", kCFCompareBackwards); - if (range.location != kCFNotFound) - isOblique = YES; - CFRelease(styleName); - } - if (isOblique) - return uiDrawTextItalicOblique; - return uiDrawTextItalicItalic; -} - void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescriptor *uidesc) { CFStringRef cffamily; - CFDictionaryRef traits; - double ctweight, ctstretch; - int wc; - uiDrawTextStretch stretch; + fontStyleData *d; + NSDictionary *axisDict; cffamily = (CFStringRef) CTFontDescriptorCopyAttribute(ctdesc, kCTFontFamilyNameAttribute); if (cffamily == NULL) { @@ -603,26 +483,12 @@ void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescript uidesc->Family = uiDarwinNSStringToText((NSString *) cffamily); CFRelease(cffamily); - traits = (CFDictionaryRef) CTFontDescriptorCopyAttribute(ctdesc, kCTFontTraitsAttribute); - if (traits == NULL) { - // TODO - } - ctweight = doubleAttr(traits, kCTFontWeightTrait); - uidesc->Italic = italicFromCTItalic(ctdesc, traits); - ctstretch = doubleAttr(traits, kCTFontWidthTrait); - CFRelease(traits); - - // TODO make sure this is correct - for (wc = 0; wc < 10; wc++) - if (ctweight >= weightsToCTWeights[wc]) - if (ctweight < weightsToCTWeights[wc + 1]) - break; - uidesc->Weight = ((ctweight - weightsToCTWeights[wc]) / (weightsToCTWeights[wc + 1] - weightsToCTWeights[wc])) * 100; - uidesc->Weight += wc * 100; - - // TODO is this correct? - for (stretch = uiDrawTextStretchUltraCondensed; stretch < uiDrawTextStretchUltraExpanded; stretch++) - if (ctstretch <= stretchesToCTWidths[stretch]) - break; - uidesc->Stretch = stretch; + d = [[fontStyleData alloc] initWithDescriptor:current]; + axisDict = nil; + if ([d variations] != nil) + axisDict = mkAxisDict(d, [d table:kCTFontTableAvar]); + fillDescStyleFields(d, axisDict, uidesc); + if (axisDict != nil) + [axisDict release]; + [d release]; } diff --git a/doc/export/fontstyle.h b/doc/export/fontstyle.h index a61393fb..a1bc66f7 100644 --- a/doc/export/fontstyle.h +++ b/doc/export/fontstyle.h @@ -1,6 +1,6 @@ // 3 november 2017 -// fontstyle.m +// fontmatch.m @interface fontStyleData : NSObject { CTFontRef font; CTFontDescriptorRef desc; diff --git a/doc/export/fonttraits.m b/doc/export/fonttraits.m index 919dd2a8..6732b894 100644 --- a/doc/export/fonttraits.m +++ b/doc/export/fonttraits.m @@ -28,27 +28,6 @@ static BOOL fontRegistered(fontStyleData *d) return [d registrationScope] != kCTFontManagerScopeNone; } -// Core Text doesn't seem to differentiate between Italic and Oblique. -// Pango's Core Text code just does a g_strrstr() (backwards case-sensitive search) for "Oblique" in the font's style name (see https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c); let's do that too I guess -static uiDrawTextFontItalic guessItalicOblique(fontStyleData *d) -{ - CFStringRef styleName; - BOOL isOblique; - - isOblique = NO; // default value - styleName = [d styleName]; - if (styleName != NULL) { - CFRange range; - - range = CFStringFind(styleName, CFSTR("Oblique"), kCFCompareBackwards); - if (range.location != kCFNotFound) - isOblique = YES; - } - if (isOblique) - return uiDrawFontItalicOblique; - return uiDrawFontItalicItalic; -} - // Core Text does (usWidthClass / 10) - 0.5 here. // This roughly maps to our values with increments of 0.1, except for the fact 0 and 10 are allowed by Core Text, despite being banned by TrueType and OpenType themselves. // We'll just treat them as identical to 1 and 9, respectively. @@ -188,10 +167,6 @@ void processFontTraits(fontStyleData *d, uiDrawFontDescriptor *out) double weight, width; BOOL hasWeight, hasWidth; - out->Italic = uiDrawTextItalicNormal; - if (([d symbolicTraits] & kCTFontItalicTrait) != 0) - out->Italic = guessItalicOblique(d); - hasWeight = NO; hasWidth = NO; trySecondaryOS2Values(d, out, &hasWeight, &hasWidth); diff --git a/doc/export/fontvariation.m b/doc/export/fontvariation.m index bf89220c..0e4c993b 100644 --- a/doc/export/fontvariation.m +++ b/doc/export/fontvariation.m @@ -300,7 +300,7 @@ void processFontVariation(fontStyleData *d, NSDictionary *axisDict, uiDrawFontDe out->Weight = uiDrawTextWeightNormal; out->Stretch = uiDrawTextStretchNormal; - var = [d variation]; + var = [d variations]; if (tryAxis(axisDict, var, fvarAxisKey(fvarWeight), &v)) { // v is now a value between -1 and 1 scaled linearly between discrete points From e0b584082dc0dabedac8ec51856af49249150be1 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 3 Nov 2017 20:59:27 -0400 Subject: [PATCH 0829/1329] Reintegrated everything and fixed more compiler errors. Now we have to deal with linker errors, and then with testing to see if everything worked... --- darwin/CMakeLists.txt | 2 ++ {doc/export => darwin}/fontmatch.m | 42 +++++++++++++------------- {doc/export => darwin}/fontstyle.h | 6 ++-- {doc/export => darwin}/fonttraits.m | 17 +++++------ {doc/export => darwin}/fontvariation.m | 38 ++++++++++++----------- 5 files changed, 53 insertions(+), 52 deletions(-) rename {doc/export => darwin}/fontmatch.m (94%) rename {doc/export => darwin}/fontstyle.h (95%) rename {doc/export => darwin}/fonttraits.m (94%) rename {doc/export => darwin}/fontvariation.m (89%) diff --git a/darwin/CMakeLists.txt b/darwin/CMakeLists.txt index ef1d30b1..253b621c 100644 --- a/darwin/CMakeLists.txt +++ b/darwin/CMakeLists.txt @@ -21,6 +21,8 @@ list(APPEND _LIBUI_SOURCES darwin/entry.m darwin/fontbutton.m darwin/fontmatch.m + darwin/fonttraits.m + darwin/fontvariation.m darwin/form.m darwin/future.m darwin/graphemes.m diff --git a/doc/export/fontmatch.m b/darwin/fontmatch.m similarity index 94% rename from doc/export/fontmatch.m rename to darwin/fontmatch.m index 68e9d72b..6cec26b6 100644 --- a/doc/export/fontmatch.m +++ b/darwin/fontmatch.m @@ -38,7 +38,7 @@ extern const CFStringRef kCTFontPreferredFamilyNameKey; if (self) { self->font = f; CFRetain(self->font); - self->desc = CTFontCopyDescriptor(self->font); + self->desc = CTFontCopyFontDescriptor(self->font); if (![self prepare]) { [self release]; return nil; @@ -72,7 +72,7 @@ extern const CFStringRef kCTFontPreferredFamilyNameKey; REL(self->subFamilyName); REL(self->preferredSubFamilyName); REL(self->postScriptName); - REL(self->variations); + REL(self->variation); REL(self->styleName); REL(self->traits); CFRelease(self->desc); @@ -91,8 +91,8 @@ extern const CFStringRef kCTFontPreferredFamilyNameKey; self->width = 0; self->didStyleName = NO; self->styleName = NULL; - self->didVariations = NO; - self->variations = NULL; + self->didVariation = NO; + self->variation = NULL; self->hasRegistrationScope = NO; self->registrationScope = 0; self->didPostScriptName = NO; @@ -186,14 +186,14 @@ extern const CFStringRef kCTFontPreferredFamilyNameKey; return self->styleName; } -- (CFDictionaryRef)variations +- (CFDictionaryRef)variation { - if (!self->didVariations) { - self->didVariations = YES; - self->variations = (CFDictionaryRef) CTFontDescriptorCopyAttribute(self->desc, kCTFontVariationsAttribute); + if (!self->didVariation) { + self->didVariation = YES; + self->variation = (CFDictionaryRef) CTFontDescriptorCopyAttribute(self->desc, kCTFontVariationAttribute); // This being NULL is used to determine whether a font uses variations at all, so we don't need to worry now. } - return self->variations; + return self->variation; } - (BOOL)hasRegistrationScope @@ -235,7 +235,7 @@ extern const CFStringRef kCTFontPreferredFamilyNameKey; } #define FONTNAME(sel, did, var, key) \ - - (CFString)sel \ + - (CFStringRef)sel \ { \ if (!did) { \ did = YES; \ @@ -298,7 +298,7 @@ static const double *italicClosenesses[] = { // Core Text doesn't seem to differentiate between Italic and Oblique. // Pango's Core Text code just does a g_strrstr() (backwards case-sensitive search) for "Oblique" in the font's style name (see https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c); let's do that too I guess -static uiDrawTextFontItalic guessItalicOblique(fontStyleData *d) +static uiDrawTextItalic guessItalicOblique(fontStyleData *d) { CFStringRef styleName; BOOL isOblique; @@ -313,8 +313,8 @@ static uiDrawTextFontItalic guessItalicOblique(fontStyleData *d) isOblique = YES; } if (isOblique) - return uiDrawFontItalicOblique; - return uiDrawFontItalicItalic; + return uiDrawTextItalicOblique; + return uiDrawTextItalicItalic; } // Italics are hard because Core Text does NOT distinguish between italic and oblique. @@ -359,11 +359,11 @@ static CTFontDescriptorRef matchStyle(CTFontDescriptorRef against, uiDrawFontDes return against; } - current = (CTFontDescriptorRef) CFArrayGetValueAtIndex(matches, 0); + current = (CTFontDescriptorRef) CFArrayGetValueAtIndex(matching, 0); d = [[fontStyleData alloc] initWithDescriptor:current]; axisDict = nil; - if ([d variations] != nil) - axisDict = mkAxisDict(d, [d table:kCTFontTableAvar]); + if ([d variation] != NULL) + axisDict = mkVariationAxisDict([d variationAxes], [d table:kCTFontTableAvar]); closeness = (struct closeness *) uiAlloc(n * sizeof (struct closeness), "struct closeness[]"); for (i = 0; i < n; i++) { @@ -384,7 +384,7 @@ static CTFontDescriptorRef matchStyle(CTFontDescriptorRef against, uiDrawFontDes // now figure out the 3-space difference between the three and sort by that // TODO merge this loop with the previous loop? for (i = 0; i < n; i++) { - double weight, stretch; + double weight, italic, stretch; weight = (double) (closeness[i].weight); weight *= weight; @@ -443,7 +443,7 @@ CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd) basedesc = CTFontDescriptorCreateWithAttributes(attrs); CFRelease(attrs); // TODO correct? - return matchTraits(basedesc, fd); + return matchStyle(basedesc, fd); } // fortunately features that aren't supported are simply ignored, so we can copy them all in @@ -483,10 +483,10 @@ void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescript uidesc->Family = uiDarwinNSStringToText((NSString *) cffamily); CFRelease(cffamily); - d = [[fontStyleData alloc] initWithDescriptor:current]; + d = [[fontStyleData alloc] initWithDescriptor:ctdesc]; axisDict = nil; - if ([d variations] != nil) - axisDict = mkAxisDict(d, [d table:kCTFontTableAvar]); + if ([d variation] != NULL) + axisDict = mkVariationAxisDict([d variationAxes], [d table:kCTFontTableAvar]); fillDescStyleFields(d, axisDict, uidesc); if (axisDict != nil) [axisDict release]; diff --git a/doc/export/fontstyle.h b/darwin/fontstyle.h similarity index 95% rename from doc/export/fontstyle.h rename to darwin/fontstyle.h index a1bc66f7..8a27380c 100644 --- a/doc/export/fontstyle.h +++ b/darwin/fontstyle.h @@ -10,8 +10,8 @@ double width; BOOL didStyleName; CFStringRef styleName; - BOOL didVariations; - CFDictionaryRef variations; + BOOL didVariation; + CFDictionaryRef variation; BOOL hasRegistrationScope; CTFontManagerScope registrationScope; BOOL didPostScriptName; @@ -38,7 +38,7 @@ - (double)weight; - (double)width; - (CFStringRef)styleName; -- (CFDictionaryRef)variations; +- (CFDictionaryRef)variation; - (BOOL)hasRegistrationScope; - (CTFontManagerScope)registrationScope; - (CFStringRef)postScriptName; diff --git a/doc/export/fonttraits.m b/darwin/fonttraits.m similarity index 94% rename from doc/export/fonttraits.m rename to darwin/fonttraits.m index 6732b894..feb37b8b 100644 --- a/doc/export/fonttraits.m +++ b/darwin/fonttraits.m @@ -31,7 +31,7 @@ static BOOL fontRegistered(fontStyleData *d) // Core Text does (usWidthClass / 10) - 0.5 here. // This roughly maps to our values with increments of 0.1, except for the fact 0 and 10 are allowed by Core Text, despite being banned by TrueType and OpenType themselves. // We'll just treat them as identical to 1 and 9, respectively. -static const uiDrawFontStretch os2WidthsToStretches[] = { +static const uiDrawTextStretch os2WidthsToStretches[] = { uiDrawTextStretchUltraCondensed, uiDrawTextStretchUltraCondensed, uiDrawTextStretchExtraCondensed, @@ -59,7 +59,7 @@ static void trySecondaryOS2Values(fontStyleData *d, uiDrawFontDescriptor *out, B CFDataRef os2; uint16_t usWeightClass, usWidthClass; CFStringRef psname; - CFStringRef *ex; + const CFStringRef *ex; *hasWeight = NO; *hasWidth = NO; @@ -82,7 +82,7 @@ static void trySecondaryOS2Values(fontStyleData *d, uiDrawFontDescriptor *out, B usWeightClass |= (uint16_t) (b[5]); if (usWeightClass <= 1000) { if (usWeightClass < 11) - usWeigthClass *= 100; + usWeightClass *= 100; *hasWeight = YES; } @@ -127,9 +127,6 @@ static BOOL testTTFOTFSubfamilyName(CFStringRef name, CFStringRef want) static BOOL testTTFOTFSubfamilyNames(fontStyleData *d, CFStringRef want) { - CGFontRef font; - CFString *key; - switch ([d fontFormat]) { case kCTFontFormatOpenTypePostScript: case kCTFontFormatOpenTypeTrueType: @@ -151,15 +148,15 @@ static BOOL testTTFOTFSubfamilyNames(fontStyleData *d, CFStringRef want) } // work around a bug in libFontRegistry.dylib -static BOOL shouldReallyBeThin(CTFontDescriptorRef desc) +static BOOL shouldReallyBeThin(fontStyleData *d) { - return testTTFOTFSubfamilyNames(desc, CFSTR("W1")); + return testTTFOTFSubfamilyNames(d, CFSTR("W1")); } // work around a bug in libFontRegistry.dylib -static BOOL shouldReallyBeSemiCondensed(CTFontDescriptorRef desc) +static BOOL shouldReallyBeSemiCondensed(fontStyleData *d) { - return testTTFOTFSubfamilyNames(desc, CFSTR("Semi Condensed")); + return testTTFOTFSubfamilyNames(d, CFSTR("Semi Condensed")); } void processFontTraits(fontStyleData *d, uiDrawFontDescriptor *out) diff --git a/doc/export/fontvariation.m b/darwin/fontvariation.m similarity index 89% rename from doc/export/fontvariation.m rename to darwin/fontvariation.m index 0e4c993b..3c6e0005 100644 --- a/doc/export/fontvariation.m +++ b/darwin/fontvariation.m @@ -88,7 +88,7 @@ static double fixed214ToDouble(fixed214 f) base = 1; break; case 2: - base = -2: + base = -2; break; case 3: base = -1; @@ -105,14 +105,14 @@ static fixed1616 fixed214ToFixed1616(fixed214 f) t = (int32_t) ((int16_t) f); t <<= 2; x = (uint32_t) t; - return (float1616) (x - 0x00000002); + return (fixed1616) (x - 0x00000002); } -static const fixed1616Negative1 = 0xFFFF0000; -static const fixed1616Zero = 0x00000000; -static const fixed1616Positive1 = 0x00010000; +static const fixed1616 fixed1616Negative1 = 0xFFFF0000; +static const fixed1616 fixed1616Zero = 0x00000000; +static const fixed1616 fixed1616Positive1 = 0x00010000; -static fixed1616 normalize1616(fixed1616 val, fixed1616 min, fixed1616 max, fixed1616 def) +static fixed1616 fixed1616Normalize(fixed1616 val, fixed1616 min, fixed1616 max, fixed1616 def) { if (val < min) val = min; @@ -133,8 +133,8 @@ static fixed214 normalizedTo214(fixed1616 val, const fixed1616 *avarMappings, si val = fixed1616Positive1; if (avarCount != 0) { size_t start, end; - float1616 startFrom, endFrom; - float1616 startTo, endTo; + fixed1616 startFrom, endFrom; + fixed1616 startTo, endTo; for (end = 0; end < avarCount; end += 2) { endFrom = avarMappings[end]; @@ -175,6 +175,7 @@ static fixed1616 *avarExtract(CFDataRef table, CFIndex index, size_t *n) nEntries = nextuint16be(); *n = nEntries * 2; entries = (fixed1616 *) uiAlloc(*n * sizeof (fixed1616), "fixed1616[]"); + p = entries; for (i = 0; i < *n; i++) { *p++ = fixed214ToFixed1616((fixed214) nextuint16be()); off += 2; @@ -182,13 +183,13 @@ static fixed1616 *avarExtract(CFDataRef table, CFIndex index, size_t *n) return entries; } -staatic BOOL extractAxisDictValue(CFDictionaryRef dict, CFStringRef key, fixed1616 *out) +static BOOL extractAxisDictValue(CFDictionaryRef dict, CFStringRef key, fixed1616 *out) { CFNumberRef num; double v; num = (CFNumberRef) CFDictionaryGetValue(dict, key); - if (CFNumberGetValue(num, kCFNumberTypeDouble, &v) == false) + if (CFNumberGetValue(num, kCFNumberDoubleType, &v) == false) return NO; *out = doubleToFixed1616(v); return YES; @@ -244,7 +245,7 @@ fail: fixed214 n2; n = doubleToFixed1616(d); - n = fixed16161Normalize(n, self->min, self->max, self->def); + n = fixed1616Normalize(n, self->min, self->max, self->def); n2 = normalizedTo214(n, self->avarMappings, self->avarCount); return fixed214ToDouble(n2); } @@ -257,22 +258,22 @@ NSDictionary *mkVariationAxisDict(CFArrayRef axes, CFDataRef avarTable) CFIndex i, n; NSMutableDictionary *out; - n = CFArrayGetLength(axes); + n = CFArrayGetCount(axes); out = [NSMutableDictionary new]; for (i = 0; i < n; i++) { CFNumberRef key; axis = (CFDictionaryRef) CFArrayGetValueAtIndex(axes, i); key = (CFNumberRef) CFDictionaryGetValue(axis, kCTFontVariationAxisIdentifierKey); - [out setObject:[[fvarAxis alloc] initWithIndex:i dict:axis avarTable:table] + [out setObject:[[fvarAxis alloc] initWithIndex:i dict:axis avarTable:avarTable] forKey:((NSNumber *) key)]; } - if (table != NULL) - CFRelease(table); + if (avarTable != NULL) + CFRelease(avarTable); return out; } -#define fvarAxisKey(n) [NSNumber numberWithUnsignedInteger:k] +#define fvarAxisKey(n) [NSNumber numberWithUnsignedInteger:n] static BOOL tryAxis(NSDictionary *axisDict, CFDictionaryRef var, NSNumber *key, double *out) { @@ -285,7 +286,7 @@ static BOOL tryAxis(NSDictionary *axisDict, CFDictionaryRef var, NSNumber *key, num = (CFNumberRef) CFDictionaryGetValue(var, (CFNumberRef) key); if (num == nil) return NO; - if (CFNumberGetValue(num, kCFNumberTypeDouble, out) == false) { + if (CFNumberGetValue(num, kCFNumberDoubleType, out) == false) { // TODO return NO; } @@ -295,12 +296,13 @@ static BOOL tryAxis(NSDictionary *axisDict, CFDictionaryRef var, NSNumber *key, void processFontVariation(fontStyleData *d, NSDictionary *axisDict, uiDrawFontDescriptor *out) { + CFDictionaryRef var; double v; out->Weight = uiDrawTextWeightNormal; out->Stretch = uiDrawTextStretchNormal; - var = [d variations]; + var = [d variation]; if (tryAxis(axisDict, var, fvarAxisKey(fvarWeight), &v)) { // v is now a value between -1 and 1 scaled linearly between discrete points From ad34745327e80c26e4361a5df120644eb07c7f28 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 3 Nov 2017 21:55:43 -0400 Subject: [PATCH 0830/1329] =?UTF-8?q?Fixed=20loading=20of=20undocumented?= =?UTF-8?q?=20symbols.=20Now=20we're=20making=20progress!=20And=20what's?= =?UTF-8?q?=20more,=20fvar=20support=20is=20working!=20But=20not=20perfect?= =?UTF-8?q?ly=20=E2=80=94=20everything=20seems=20to=20be=20hitting=20extre?= =?UTF-8?q?mes...?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- darwin/CMakeLists.txt | 1 + darwin/fontmatch.m | 8 ++------ darwin/future.m | 2 -- darwin/main.m | 1 + darwin/uipriv_darwin.h | 5 +++++ darwin/undocumented.m | 31 +++++++++++++++++++++++++++++++ 6 files changed, 40 insertions(+), 8 deletions(-) create mode 100644 darwin/undocumented.m diff --git a/darwin/CMakeLists.txt b/darwin/CMakeLists.txt index 253b621c..d03d0f0d 100644 --- a/darwin/CMakeLists.txt +++ b/darwin/CMakeLists.txt @@ -44,6 +44,7 @@ list(APPEND _LIBUI_SOURCES darwin/stddialogs.m darwin/tab.m darwin/text.m + darwin/undocumented.m darwin/util.m darwin/window.m darwin/winmoveresize.m diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index 6cec26b6..25476900 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -26,10 +26,6 @@ // because the descriptors returned by Core Text's own font // matching won't have any. -// TODO explicitly mark these as undocumented -extern const CFStringRef kCTFontPreferredSubFamilyNameKey; -extern const CFStringRef kCTFontPreferredFamilyNameKey; - @implementation fontStyleData - (id)initWithFont:(CTFontRef)f @@ -246,7 +242,7 @@ extern const CFStringRef kCTFontPreferredFamilyNameKey; FONTNAME(preferredSubFamilyName, self->didPreferredSubFamilyName, self->preferredSubFamilyName, - kCTFontPreferredSubFamilyNameKey) + UNDOC_kCTFontPreferredSubFamilyNameKey) FONTNAME(subFamilyName, self->didSubFamilyName, self->subFamilyName, @@ -258,7 +254,7 @@ FONTNAME(fullName, FONTNAME(preferredFamilyName, self->didPreferredFamilyName, self->preferredFamilyName, - kCTFontPreferredFamilyNameKey) + UNDOC_kCTFontPreferredFamilyNameKey) FONTNAME(familyName, self->didFamilyName, self->familyName, diff --git a/darwin/future.m b/darwin/future.m index a6988f83..60936f40 100644 --- a/darwin/future.m +++ b/darwin/future.m @@ -4,8 +4,6 @@ // functions and constants FROM THE FUTURE! // note: for constants, dlsym() returns the address of the constant itself, as if we had done &constantName -// TODO add weight constants here? - // added in OS X 10.10; we need 10.8 CFStringRef *FUTURE_kCTFontOpenTypeFeatureTag = NULL; CFStringRef *FUTURE_kCTFontOpenTypeFeatureValue = NULL; diff --git a/darwin/main.m b/darwin/main.m index 60acecaf..d85000dd 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -120,6 +120,7 @@ const char *uiInit(uiInitOptions *o) initAlloc(); loadFutures(); + loadUndocumented(); // always do this so we always have an application menu appDelegate().menuManager = [[menuManager new] autorelease]; diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 26366520..0303c32c 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -174,3 +174,8 @@ extern CFStringRef *FUTURE_kCTFontOpenTypeFeatureValue; extern void loadFutures(void); extern void FUTURE_NSLayoutConstraint_setIdentifier(NSLayoutConstraint *constraint, NSString *identifier); extern BOOL FUTURE_NSWindow_performWindowDragWithEvent(NSWindow *w, NSEvent *initialEvent); + +// undocumented.m +extern CFStringRef UNDOC_kCTFontPreferredSubFamilyNameKey; +extern CFStringRef UNDOC_kCTFontPreferredFamilyNameKey; +extern void loadUndocumented(void); diff --git a/darwin/undocumented.m b/darwin/undocumented.m new file mode 100644 index 00000000..0e016dd6 --- /dev/null +++ b/darwin/undocumented.m @@ -0,0 +1,31 @@ +// 3 november 2017 +#import "uipriv_darwin.h" + +// functions and constants FROM THE DEPTHS BELOW! +// note: for constants, dlsym() returns the address of the constant itself, as if we had done &constantName +// we also provide default values just in case + +// these values come from 10.12.6 +CFStringRef UNDOC_kCTFontPreferredSubFamilyNameKey = CFSTR("CTFontPreferredSubFamilyName"); +CFStringRef UNDOC_kCTFontPreferredFamilyNameKey = CFSTR("CTFontPreferredFamilyName"); + +// note that we treat any error as "the symbols aren't there" (and don't care if dlclose() failed) +void loadUndocumented(void) +{ + void *handle; + CFStringRef *str; + + // dlsym() walks the dependency chain, so opening the current process should be sufficient + handle = dlopen(NULL, RTLD_LAZY); + if (handle == NULL) + return; +#define GET(var, fn) *((void **) (&var)) = dlsym(handle, #fn) + GET(str, kCTFontPreferredSubFamilyNameKey); +NSLog(@"get %p", str); + if (str != NULL) + UNDOC_kCTFontPreferredSubFamilyNameKey = *str; + GET(str, kCTFontPreferredFamilyNameKey); + if (str != NULL) + UNDOC_kCTFontPreferredFamilyNameKey = *str; + dlclose(handle); +} From 6869f28718eaee3bfd4d2139e8cf13fd22c26e77 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 4 Nov 2017 02:44:38 -0400 Subject: [PATCH 0831/1329] Fixed fixed-point math issues. fvar tables now work fine, which means Skia finally works! --- darwin/attrstr.m | 2 +- darwin/fontvariation.m | 47 +++++++++++++++++++++++++----------------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index cdf7334f..fd45ec25 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -55,7 +55,7 @@ void uninitUnderlineColors(void) } // unlike the other systems, Core Text rolls family, size, weight, italic, width, AND opentype features into the "font" attribute -// TODO opentype features and AAT fvar table info is lost, so a handful of fonts in the font panel ("Titling" variants of some fonts and Skia and possibly others but those are the examples I know about) cannot be represented by uiDrawFontDescriptor; what *can* we do about this since this info is NOT part of the font on other platforms? +// TODO opentype features are lost, so a handful of fonts in the font panel ("Titling" variants of some fonts and possibly others but those are the examples I know about) cannot be represented by uiDrawFontDescriptor; what *can* we do about this since this info is NOT part of the font on other platforms? // TODO see if we could use NSAttributedString? // TODO consider renaming this struct and the fep variable(s) // TODO restructure all this so the important details at the top are below with the combined font attributes type? diff --git a/darwin/fontvariation.m b/darwin/fontvariation.m index 3c6e0005..1eae6333 100644 --- a/darwin/fontvariation.m +++ b/darwin/fontvariation.m @@ -32,9 +32,10 @@ #define fvarWeight 0x77676874 #define fvarWidth 0x77647468 -typedef uint32_t fixed1616; -typedef uint16_t fixed214; +typedef int32_t fixed1616; +typedef int16_t fixed214; +// note that Microsoft's data type list implies that *all* fixed-point types have the same format; it only gives specific examples for the 2.14 format, which confused me because I thought 16.16 worked differently, but eh static fixed1616 doubleToFixed1616(double d) { double ipart, fpart; @@ -42,7 +43,12 @@ static fixed1616 doubleToFixed1616(double d) int16_t i16; uint32_t ret; - fpart = fabs(modf(d, &ipart)); + fpart = modf(d, &ipart); + // fpart must be unsigned; modf() gives us fpart with the same sign as f (so we have to adjust both ipart and fpart appropriately) + if (fpart < 0) { + ipart -= 1; + fpart = 1 + fpart; + } fpart *= 65536; flong = lround(fpart); i16 = (int16_t) ipart; @@ -52,14 +58,16 @@ static fixed1616 doubleToFixed1616(double d) return (fixed1616) ret; } -static double fixed1616ToDouble(fixed1616 f) +// see also https://stackoverflow.com/questions/8506317/fixed-point-unsigned-division-in-c and freetype's FT_DivFix() +// TODO figure out the specifics of freetype's more complex implementation that shifts b and juggles signs +static fixed1616 fixed1616Divide(fixed1616 a, fixed1616 b) { - int16_t base; - double frac; + uint32_t u; + int64_t a64; - base = (int16_t) ((f >> 16) & 0xFFFF); - frac = ((double) (f & 0xFFFF)) / 65536; - return ((double) base) + frac; + u = (uint32_t) a; + a64 = (int64_t) (((uint64_t) u) << 16); + return (fixed1616) (a64 / b); } static fixed214 fixed1616ToFixed214(fixed1616 f) @@ -67,7 +75,7 @@ static fixed214 fixed1616ToFixed214(fixed1616 f) uint32_t t; uint32_t topbit; - t = f + 0x00000002; + t = (uint32_t) (f + 0x00000002); topbit = t & 0x80000000; t >>= 2; if (topbit != 0) @@ -77,10 +85,12 @@ static fixed214 fixed1616ToFixed214(fixed1616 f) static double fixed214ToDouble(fixed214 f) { + uint16_t u; double base; double frac; - switch ((f >> 14) & 0x3) { + u = (uint16_t) f; + switch ((u >> 14) & 0x3) { case 0: base = 0; break; @@ -93,22 +103,20 @@ static double fixed214ToDouble(fixed214 f) case 3: base = -1; } - frac = ((double) (f & 0x3FFF)) / 16384; + frac = ((double) (u & 0x3FFF)) / 16384; return base + frac; } static fixed1616 fixed214ToFixed1616(fixed214 f) { int32_t t; - uint32_t x; t = (int32_t) ((int16_t) f); t <<= 2; - x = (uint32_t) t; - return (fixed1616) (x - 0x00000002); + return (fixed1616) (t - 0x00000002); } -static const fixed1616 fixed1616Negative1 = 0xFFFF0000; +static const fixed1616 fixed1616Negative1 = (int32_t) ((uint32_t) 0xFFFF0000); static const fixed1616 fixed1616Zero = 0x00000000; static const fixed1616 fixed1616Positive1 = 0x00010000; @@ -119,9 +127,9 @@ static fixed1616 fixed1616Normalize(fixed1616 val, fixed1616 min, fixed1616 max, if (val > max) val = max; if (val < def) - return -(def - val) / (def - min); + return fixed1616Divide(-(def - val), (def - min)); if (val > def) - return (val - def) / (max - def); + return fixed1616Divide((val - def), (max - def)); return fixed1616Zero; } @@ -148,7 +156,8 @@ static fixed214 normalizedTo214(fixed1616 val, const fixed1616 *avarMappings, si start = end - 2; startFrom = avarMappings[start]; startTo = avarMappings[start + 1]; - val = (val - startFrom) / (endFrom - startFrom); + val = fixed1616Divide((val - startFrom), (endFrom - startFrom)); + // TODO find a font with an avar table and make sure this works, or if we need to use special code for this too val *= (endTo - startTo); val += startTo; } From 67a7b64f568081c6e4f43e894d7b8aeffed2824b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 4 Nov 2017 02:52:12 -0400 Subject: [PATCH 0832/1329] More TODOs. --- darwin/fontvariation.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/darwin/fontvariation.m b/darwin/fontvariation.m index 1eae6333..3e22c710 100644 --- a/darwin/fontvariation.m +++ b/darwin/fontvariation.m @@ -32,6 +32,7 @@ #define fvarWeight 0x77676874 #define fvarWidth 0x77647468 +// TODO explain why these are signed typedef int32_t fixed1616; typedef int16_t fixed214; @@ -44,7 +45,7 @@ static fixed1616 doubleToFixed1616(double d) uint32_t ret; fpart = modf(d, &ipart); - // fpart must be unsigned; modf() gives us fpart with the same sign as f (so we have to adjust both ipart and fpart appropriately) + // fpart must be unsigned; modf() gives us fpart with the same sign as d (so we have to adjust both ipart and fpart appropriately) if (fpart < 0) { ipart -= 1; fpart = 1 + fpart; From 84d49cd45bfe5d6e0d126689add89d5ce347878a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 4 Nov 2017 11:18:25 -0400 Subject: [PATCH 0833/1329] Decided to keep the minimum and maximum weights as 0 and 1000. --- ui_attrstr.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index 27fd7473..a30302e0 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -234,8 +234,6 @@ _UI_EXTERN void uiAttributedStringForEachAttribute(uiAttributedString *s, uiAttr typedef struct uiDrawFontDescriptor uiDrawFontDescriptor; -// TODO Minimum == 1? IIRC there is at least one font on OS X that actually has a weight of 0 -// TODO Maximum == 999? IIRC there is at least one font on OS X that actually has a weight of 1000 _UI_ENUM(uiDrawTextWeight) { uiDrawTextWeightMinimum = 0, uiDrawTextWeightThin = 100, From 243e210cbcab06602ce4ded4aea5361605e84d2f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 4 Nov 2017 11:52:33 -0400 Subject: [PATCH 0834/1329] Resolved some darwin/drawtext.m TODOs. --- darwin/drawtext.m | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 43c8b4e9..1fa5920e 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -167,7 +167,6 @@ void uiDrawFreeTextLayout(uiDrawTextLayout *tl) uiFree(tl->u8tou16); [tl->backgroundBlocks release]; uiFree(tl->lineMetrics); - // TODO release tl->lines? CFRelease(tl->frame); CFRelease(tl->path); CFRelease(tl->framesetter); @@ -179,8 +178,11 @@ void uiDrawFreeTextLayout(uiDrawTextLayout *tl) void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) { backgroundBlock b; + CGAffineTransform textMatrix; CGContextSaveGState(c->c); + // save the text matrix because it's not part of the graphics state + textMatrix = CGContextGetTextMatrix(c->c); for (b in tl->backgroundBlocks) b(c, tl, x, y); @@ -190,7 +192,6 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) // TODO how is this affected by a non-identity CTM? CGContextTranslateCTM(c->c, 0, c->height); CGContextScaleCTM(c->c, 1.0, -1.0); - // TODO save the text matrix CGContextSetTextMatrix(c->c, CGAffineTransformIdentity); // wait, that's not enough; we need to offset y values to account for our new flipping @@ -205,6 +206,7 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) CTFrameDraw(tl->frame, c->c); + CGContextSetTextMatrix(c->c, textMatrix); CGContextRestoreGState(c->c); } From d44c20c4a122f44b1e51dc8fae91933013893d3c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 4 Nov 2017 16:48:02 -0400 Subject: [PATCH 0835/1329] Stashed diffs for fixing empty text layouts on OS X; the code is now utterly complicated AND my memory of what I did so far on this branch is starting to fail. --- textDarwinEmptyLayout.diff | 171 +++++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 textDarwinEmptyLayout.diff diff --git a/textDarwinEmptyLayout.diff b/textDarwinEmptyLayout.diff new file mode 100644 index 00000000..bff4959f --- /dev/null +++ b/textDarwinEmptyLayout.diff @@ -0,0 +1,171 @@ +diff --git a/darwin/attrstr.m b/darwin/attrstr.m +index fd45ec25..86039fad 100644 +--- a/darwin/attrstr.m ++++ b/darwin/attrstr.m +@@ -403,8 +403,13 @@ static CTParagraphStyleRef mkParagraphStyle(uiDrawTextLayoutParams *p) + return ps; + } + +-CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks) ++static const UniChar emptyChars[] = { 0x20, 0x0 }; ++static const CFIndex emptyCharCount = 1; ++ ++CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, BOOL *isEmpty, NSArray **backgroundBlocks) + { ++ const UniChar *chars; ++ CFIndex charCount; + CFStringRef cfstr; + CFMutableDictionaryRef defaultAttrs; + CTFontRef defaultCTFont; +@@ -413,7 +418,15 @@ CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray + CFMutableAttributedStringRef mas; + struct foreachParams fep; + +- cfstr = CFStringCreateWithCharacters(NULL, attrstrUTF16(p->String), attrstrUTF16Len(p->String)); ++ *isEmpty = NO; ++ chars = attrstrUTF16(p->String); ++ charCount = attrstrUTF16Len(p->String); ++ if (charCount == 0) { ++ *isEmpty = YES; ++ chars = emptyChars; ++ charCount = emptyCharCount; ++ } ++ cfstr = CFStringCreateWithCharacters(NULL, chars, charCount); + if (cfstr == NULL) { + // TODO + } +diff --git a/darwin/drawtext.m b/darwin/drawtext.m +index 1fa5920e..65912383 100644 +--- a/darwin/drawtext.m ++++ b/darwin/drawtext.m +@@ -2,13 +2,16 @@ + #import "uipriv_darwin.h" + #import "draw.h" + +-// TODO on an empty string nLines == 0 +-// we must prevent this somehow +-// TODO in general, every function could be more robust, but we cannot have a situation where there are zero lines ++// TODO in general, every function could be more robust + // TODO what happens to extents if only whitespace? ++// TODO for empty layouts: ++// - check if alignment correct compared to other OSs, or expected behavior at all ++// - double-check if uiAttributedString allows zero-length attributes; I forget if I did + + struct uiDrawTextLayout { + CFAttributedStringRef attrstr; ++ // this is needed because Core Text will give us an empty line array on a frame made with an empty string ++ BOOL isEmpty; + + // the width as passed into uiDrawTextLayout constructors + double width; +@@ -41,7 +44,7 @@ + }; + + // TODO document that lines may or may not overlap because ours do in the case of multiple combining characters +-static uiDrawTextLayoutLineMetrics *computeLineMetrics(CTFrameRef frame, CGSize size) ++static uiDrawTextLayoutLineMetrics *computeLineMetrics(CTFrameRef frame, CGSize size, BOOL isEmpty) + { + uiDrawTextLayoutLineMetrics *metrics; + CFArrayRef lines; +@@ -79,6 +82,8 @@ + metrics[i].X = origins[i].x; + metrics[i].Y = origins[i].y - descent - leading; + metrics[i].Width = bounds.size.width; ++ if (isEmpty) ++ metrics[i].Width = 0; + metrics[i].Height = ascent + descent + leading; + + metrics[i].BaselineY = origins[i].y; +@@ -117,7 +122,7 @@ + CGRect rect; + + tl = uiNew(uiDrawTextLayout); +- tl->attrstr = attrstrToCoreFoundation(p, &(tl->backgroundBlocks)); ++ tl->attrstr = attrstrToCoreFoundation(p, &(tl->isEmpty), &(tl->backgroundBlocks)); + range.location = 0; + range.length = CFAttributedStringGetLength(tl->attrstr); + tl->width = p->Width; +@@ -152,7 +157,7 @@ + + tl->lines = CTFrameGetLines(tl->frame); + tl->nLines = CFArrayGetCount(tl->lines); +- tl->lineMetrics = computeLineMetrics(tl->frame, tl->size); ++ tl->lineMetrics = computeLineMetrics(tl->frame, tl->size, tl->isEmpty); + + // and finally copy the UTF-8/UTF-16 conversion tables + tl->u8tou16 = attrstrCopyUTF8ToUTF16(p->String, &(tl->nUTF8)); +@@ -180,6 +185,9 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) + backgroundBlock b; + CGAffineTransform textMatrix; + ++ if (tl->isEmpty) ++ return; ++ + CGContextSaveGState(c->c); + // save the text matrix because it's not part of the graphics state + textMatrix = CGContextGetTextMatrix(c->c); +@@ -216,6 +224,8 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) + void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height) + { + *width = tl->size.width; ++ if (tl->isEmpty) ++ *width = 0; + *height = tl->size.height; + } + +@@ -233,6 +243,8 @@ void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start + range = CTLineGetStringRange(lr); + *start = tl->u16tou8[range.location]; + *end = tl->u16tou8[range.location + range.length]; ++ if (tl->isEmpty) ++ *start = *end; + } + + void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m) +@@ -262,14 +274,17 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *p + *line = i; + + ln = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, i); +- // note: according to the docs, we pass a y of 0 for this since the is the baseline of that line (the point is relative to the line) +- // note: x is relative to the line origin + x -= tl->lineMetrics[*line].X; +- p = CTLineGetStringIndexForPosition(ln, CGPointMake(x, 0)); +- if (p == kCFNotFound) { +- // TODO ++ *pos = 0; ++ if (!tl->isEmpty) { ++ // note: according to the docs, we pass a y of 0 for this since the is the baseline of that line (the point is relative to the line) ++ // note: x is relative to the line origin ++ p = CTLineGetStringIndexForPosition(ln, CGPointMake(x, 0)); ++ if (p == kCFNotFound) { ++ // TODO ++ } ++ *pos = tl->u16tou8[p]; + } +- *pos = tl->u16tou8[p]; + } + + double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line) +@@ -282,6 +297,9 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *p + return -1; + lr = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, line); + range = CTLineGetStringRange(lr); ++ // TODO is the behavior of this part correct? ++ if (tl->isEmpty) ++ range.length = 0; + // note: >, not >=, because the position at end is valid! + if (pos < range.location || pos > (range.location + range.length)) + return -1; +diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h +index 0303c32c..d44ee410 100644 +--- a/darwin/uipriv_darwin.h ++++ b/darwin/uipriv_darwin.h +@@ -151,7 +151,7 @@ extern void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontD + extern void initUnderlineColors(void); + extern void uninitUnderlineColors(void); + typedef void (^backgroundBlock)(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y); +-extern CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks); ++extern CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, BOOL *isEmpty, NSArray **backgroundBlocks); + + // aat.m + typedef void (^aatBlock)(uint16_t type, uint16_t selector); From ca5a5f1f72183edddc383cb4a5b29fb5e6de5392 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 4 Nov 2017 20:47:09 -0400 Subject: [PATCH 0836/1329] More TODOs. I have to really think about this API and build a point-by-point test... --- common/attrlist.c | 1 + 1 file changed, 1 insertion(+) diff --git a/common/attrlist.c b/common/attrlist.c index cb0833bc..53217754 100644 --- a/common/attrlist.c +++ b/common/attrlist.c @@ -9,6 +9,7 @@ The list is kept sorted in increasing order by start position. Whether or not th Overlapping attributes are not allowed; if an attribute is added that conflicts with an existing one, the existing one is removed. In addition, the linked list tries to reduce fragmentation: if an attribute is added that just expands another, then there will only be one entry in alist, not two. (TODO does it really?) The linked list is not a ring; alist->fist->prev == NULL and alist->last->next == NULL. +TODO verify that this disallows attributes of length zero */ struct attr { From 1f33ca14d8e99a3be6b066bf790595839b01e73a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 4 Nov 2017 21:44:28 -0400 Subject: [PATCH 0837/1329] Fixed Windows build issues and resolved a question in libui that I need to enshrine in documentation next. --- ui_attrstr.h | 2 +- windows/attrstr.cpp | 5 +++-- windows/drawtext.cpp | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index a30302e0..e2f1f8b3 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -27,7 +27,7 @@ typedef struct uiAttributedString uiAttributedString; _UI_ENUM(uiAttribute) { // uiAttributeFamily changes the font family of the text it is // applied to. Use the Family field of uiAttributeSpec. - // TODO case-sensitive? + // TODO this is case-insensitive on all platforms; codify this uiAttributeFamily, // uiAttributeSize changes the size of the text it is applied to, // in typographical points. Use the Double field of diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index ca805237..c87804b5 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -61,8 +61,9 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute ostart = start; oend = end; - start = attrstrUTF8ToUTF16(s, start); - end = attrstrUTF8ToUTF16(s, end); + // TODO fix const correctness + start = attrstrUTF8ToUTF16((uiAttributedString *) s, start); + end = attrstrUTF8ToUTF16((uiAttributedString *) s, end); range.startPosition = start; range.length = end - start; switch (spec->Type) { diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 59d64097..84a8874e 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -381,7 +381,7 @@ public: return E_UNEXPECTED; } - virtual HRESULT DrawUnderline(void *clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, const DWRITE_UNDERLINE *underline, IUnknown *clientDrawingEffect) + virtual HRESULT STDMETHODCALLTYPE DrawUnderline(void *clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, const DWRITE_UNDERLINE *underline, IUnknown *clientDrawingEffect) { textDrawingEffect *t = (textDrawingEffect *) clientDrawingEffect; ID2D1SolidColorBrush *brush; From e33879a283cdaace2ee0d0688dacd65f1f69098e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 5 Nov 2017 22:52:14 -0500 Subject: [PATCH 0838/1329] Okay, I need to rethink these APIs. Let's start doing so. Then I should also write a testsuite for uiAttributedString. --- checklist_attrstr | 8 +++++ new_ui_attrstr.h | 92 +++++++++++++++++++++++++++++++++++++++++++++++ ui_attrstr.h | 77 --------------------------------------- 3 files changed, 100 insertions(+), 77 deletions(-) create mode 100644 checklist_attrstr create mode 100644 new_ui_attrstr.h diff --git a/checklist_attrstr b/checklist_attrstr new file mode 100644 index 00000000..b4b86278 --- /dev/null +++ b/checklist_attrstr @@ -0,0 +1,8 @@ += attributed strings +attribute lengths are rounded to complete unicode codepoints +zero-length attributes are elided +consecutive attributes of the same type and value are merged +overlapping attributes of different types do not split each other +overlapping attributes of the same type but different values do split +empty string is allowed +empty string cannot have attributes diff --git a/new_ui_attrstr.h b/new_ui_attrstr.h new file mode 100644 index 00000000..083e404f --- /dev/null +++ b/new_ui_attrstr.h @@ -0,0 +1,92 @@ +// uiAttributedString represents a string of UTF-8 text that can +// optionally be embellished with formatting attributes. libui +// provides the list of formatting attributes, which cover common +// formatting traits like boldface and color as well as advanced +// typographical features provided by OpenType like superscripts +// and small caps. These attributes can be combined in a variety of +// ways. +// +// Attributes are applied to runs of Unicode codepoints in the string. +// Zero-length runs are elided. Consecutive runs that have the same +// attribute type and value are merged. Each attribute is independent +// of each other attribute; overlapping attributes of different types +// do not split each other apart, but different values of the same +// attribute type do. +// +// The empty string can also be represented by uiAttributedString, +// but because of the no-zero-length-attribute rule, it will not have +// attributes. +// +// TODO note here about attribute ownership +// +// In addition, uiAttributedString provides facilities for moving +// between grapheme clusters, which represent a character +// from the point of view of the end user. The cursor of a text editor +// is always placed on a grapheme boundary, so you can use these +// features to move the cursor left or right by one "character". +// TODO does uiAttributedString itself need this +// +// uiAttributedString does not provide enough information to be able +// to draw itself onto a uiDrawContext or respond to user actions. +// In order to do that, you'll need to use a uiDrawTextLayout, which +// is built from the combination of a uiAttributedString and a set of +// layout-specific properties. +typedef struct uiAttributedString uiAttributedString; + +// uiAttributedStringForEachAttributeFunc is the type of the function +// invoked by uiAttributedStringForEachAttribute() for every +// attribute in s. Refer to that function's documentation for more +// details. +typedef uiForEach (*uiAttributedStringForEachAttributeFunc)(const uiAttributedString *s, const uiAttributeSpec *spec, size_t start, size_t end, void *data); + +// @role uiAttributedString constructor +// uiNewAttributedString() creates a new uiAttributedString from +// initialString. The string will be entirely unattributed. +_UI_EXTERN uiAttributedString *uiNewAttributedString(const char *initialString); + +// @role uiAttributedString destructor +// uiFreeAttributedString() destroys the uiAttributedString s. +// TODO note here about attribute ownership +_UI_EXTERN void uiFreeAttributedString(uiAttributedString *s); + +// uiAttributedStringString() returns the textual content of s as a +// '\0'-terminated UTF-8 string. The returned pointer is valid until +// the next change to the textual content of s. +_UI_EXTERN const char *uiAttributedStringString(const uiAttributedString *s); + +// uiAttributedStringLength() returns the number of UTF-8 bytes in +// the textual content of s, excluding the terminating '\0'. +_UI_EXTERN size_t uiAttributedStringLen(const uiAttributedString *s); + +// uiAttributedStringAppendUnattributed() adds the '\0'-terminated +// UTF-8 string str to the end of s. The new substring will be +// unattributed. +_UI_EXTERN void uiAttributedStringAppendUnattributed(uiAttributedString *s, const char *str); + +// uiAttributedStringInsertAtUnattributed() adds the '\0'-terminated +// UTF-8 string str to s at the byte position specified by at. The new +// substring will be unattributed; existing attributes will be moved +// along with its text. +_UI_EXTERN void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *str, size_t at); + +// TODO add the Append and InsertAtExtendingAttributes functions +// TODO and add functions that take a string + length + +// uiAttributedStringDelete() deletes the characters and attributes of +// s in the range [start, end). +_UI_EXTERN void uiAttributedStringDelete(uiAttributedString *s, size_t start, size_t end); + +// TODO const correct this somehow (the implementation needs to mutate the structure) +_UI_EXTERN size_t uiAttributedStringNumGraphemes(uiAttributedString *s); + +// TODO const correct this somehow (the implementation needs to mutate the structure) +_UI_EXTERN size_t uiAttributedStringByteIndexToGrapheme(uiAttributedString *s, size_t pos); + +// TODO const correct this somehow (the implementation needs to mutate the structure) +_UI_EXTERN size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos); + +_UI_EXTERN void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end); + +// TODO document this +// TODO possibly copy the spec each time to ensure it doesn't get clobbered +_UI_EXTERN void uiAttributedStringForEachAttribute(uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); diff --git a/ui_attrstr.h b/ui_attrstr.h index e2f1f8b3..3f8d9f0e 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -1,23 +1,3 @@ -// uiAttributedString represents a string of UTF-8 text that can -// optionally be embellished with formatting attributes. libui -// provides the list of formatting attributes, which cover common -// formatting traits like boldface and color as well as advanced -// typographical features provided by OpenType like superscripts -// and small caps. These attributes can be combined in a variety of -// ways. -// -// In addition, uiAttributedString provides facilities for moving -// between grapheme clusters, which represent a character -// from the point of view of the end user. The cursor of a text editor -// is always placed on a grapheme boundary, so you can use these -// features to move the cursor left or right by one "character". -// -// uiAttributedString does not provide enough information to be able -// to draw itself onto a uiDrawContext or respond to user actions. -// In order to do that, you'll need to use a uiDrawTextLayout, which -// is built from the combination of a uiAttributedString and a set of -// layout-specific properties. -typedef struct uiAttributedString uiAttributedString; // uiAttribute specifies the types of possible attributes that can be // applied to a uiAttributedString. For every byte in the @@ -175,63 +155,6 @@ struct uiAttributeSpec { const uiOpenTypeFeatures *Features; }; -// uiAttributedStringForEachAttributeFunc is the type of the function -// invoked by uiAttributedStringForEachAttribute() for every -// attribute in s. Refer to that function's documentation for more -// details. -typedef uiForEach (*uiAttributedStringForEachAttributeFunc)(const uiAttributedString *s, const uiAttributeSpec *spec, size_t start, size_t end, void *data); - -// @role uiAttributedString constructor -// uiNewAttributedString() creates a new uiAttributedString from -// initialString. The string will be entirely unattributed. -_UI_EXTERN uiAttributedString *uiNewAttributedString(const char *initialString); - -// @role uiAttributedString destructor -// uiFreeAttributedString() destroys the uiAttributedString s. -_UI_EXTERN void uiFreeAttributedString(uiAttributedString *s); - -// uiAttributedStringString() returns the textual content of s as a -// '\0'-terminated UTF-8 string. The returned pointer is valid until -// the next change to the textual content of s. -_UI_EXTERN const char *uiAttributedStringString(const uiAttributedString *s); - -// uiAttributedStringLength() returns the number of UTF-8 bytes in -// the textual content of s, excluding the terminating '\0'. -_UI_EXTERN size_t uiAttributedStringLen(const uiAttributedString *s); - -// uiAttributedStringAppendUnattributed() adds the '\0'-terminated -// UTF-8 string str to the end of s. The new substring will be -// unattributed. -_UI_EXTERN void uiAttributedStringAppendUnattributed(uiAttributedString *s, const char *str); - -// uiAttributedStringAppendUnattributed() adds the '\0'-terminated -// UTF-8 string str to s at the byte position specified by at. The new -// substring will be unattributed; existing attributes will be moved -// along with its text. -_UI_EXTERN void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *str, size_t at); - -// TODO add the Append and InsertAtExtendingAttributes functions -// TODO and add functions that take a string + length - -// uiAttributedStringDelete() deletes the characters and attributes of -// s in the range [start, end). -_UI_EXTERN void uiAttributedStringDelete(uiAttributedString *s, size_t start, size_t end); - -// TODO const correct this somehow (the implementation needs to mutate the structure) -_UI_EXTERN size_t uiAttributedStringNumGraphemes(uiAttributedString *s); - -// TODO const correct this somehow (the implementation needs to mutate the structure) -_UI_EXTERN size_t uiAttributedStringByteIndexToGrapheme(uiAttributedString *s, size_t pos); - -// TODO const correct this somehow (the implementation needs to mutate the structure) -_UI_EXTERN size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos); - -_UI_EXTERN void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end); - -// TODO document this -// TODO possibly copy the spec each time to ensure it doesn't get clobbered -_UI_EXTERN void uiAttributedStringForEachAttribute(uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); - typedef struct uiDrawFontDescriptor uiDrawFontDescriptor; _UI_ENUM(uiDrawTextWeight) { From bad2325323310832b17288bd883d85f2c95a4f19 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 6 Nov 2017 23:58:12 -0500 Subject: [PATCH 0839/1329] More attributed string API work. Of note, decided to make each type of attribute have its own field in uiAttributeSpec, to make thinking about what to do next easier (and because why not). --- checklist_attrstr | 1 + ui_attrstr.h | 38 ++++++++++++++++++++------------------ 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/checklist_attrstr b/checklist_attrstr index b4b86278..14dd4580 100644 --- a/checklist_attrstr +++ b/checklist_attrstr @@ -6,3 +6,4 @@ overlapping attributes of different types do not split each other overlapping attributes of the same type but different values do split empty string is allowed empty string cannot have attributes +font family names are case-insensitive both in attributes and in descriptors diff --git a/ui_attrstr.h b/ui_attrstr.h index 3f8d9f0e..b74f7408 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -1,29 +1,25 @@ // uiAttribute specifies the types of possible attributes that can be -// applied to a uiAttributedString. For every byte in the +// applied to a uiAttributedString. For every Unicode codepoint in the // uiAttributedString, at most one value of each attribute type can // be applied. // TODO just make a separate field in uiAttributeSpec for everything? or make attribute objects opaque instead? _UI_ENUM(uiAttribute) { // uiAttributeFamily changes the font family of the text it is - // applied to. Use the Family field of uiAttributeSpec. - // TODO this is case-insensitive on all platforms; codify this + // applied to. Font family names are case-insensitive. Use the + // Family field of uiAttributeSpec. uiAttributeFamily, // uiAttributeSize changes the size of the text it is applied to, - // in typographical points. Use the Double field of - // uiAttributeSpec. + // in typographical points. Use the Size field of uiAttributeSpec. uiAttributeSize, // uiAttributeWeight changes the weight of the text it is applied - // to. Use the Value field of uiAttributeSpec and the - // uiDrawTextWeight constants. + // to. Use the Weight field of uiAttributeSpec. uiAttributeWeight, // uiAttributeItalic changes the italicness of the text it is applied - // to. Use the Value field of uiAttributeSpec and the - // uiDrawTextItalic constants. + // to. Use the Italic field of uiAttributeSpec. uiAttributeItalic, // uiAttributeStretch changes the stretch of the text it is applied - // to. Use the Value field of uiAttributeSpec and the - // uiDrawTextStretch constants. + // to. Use the Stretch field of uiAttributeSpec. uiAttributeStretch, // uiAttributeColor changes the color of the text it is applied to. // Use the R, G, B, and A fields of uiAttributeSpec. @@ -33,16 +29,18 @@ _UI_ENUM(uiAttribute) { uiAttributeBackground, // uiAttributeUnderline changes the underline style of the text - // it is applied to. Use the Value field of uiAttributeSpec and the - // uiDrawUnderlineStyle constants. + // it is applied to. Use the UnderlineStyle field of + // uiAttributeSpec. uiAttributeUnderline, // uiAttributeUnderlineColor changes the color of any underline // on the text it is applied to, regardless of the style. Use the - // Value field of uiAttributeSpec and the uiDrawUnderlineColor - // constants (refer to its documentation for more information). + // UnderlineColor field of uiAttributeSpec, and also the R, G, B, + // and A fields if specifying uiDrawUnderlineColorCustom. // // If an underline style is applied but no underline color is - // specified, the text color is used instead. + // specified, the text color is used instead. If an underline color + // is specified without an underline style, the underline color + // attribute is ignored, but not elided. uiAttributeUnderlineColor, // uiAttributeFeatures changes the OpenType features of the @@ -146,12 +144,16 @@ typedef struct uiAttributeSpec uiAttributeSpec; struct uiAttributeSpec { uiAttribute Type; const char *Family; - uintptr_t Value; - double Double; + double Size; + uiDrawTextWeight Weight; + uiDrawTextItalic Italic; + uiDrawTextStretch Stretch; double R; double G; double B; double A; + uiDrawUnderlineStyle UnderlineStyle; + uiDrawUnderlineColor UnderlineColor; const uiOpenTypeFeatures *Features; }; From 4b7ca92ce768bcca9f5a4a5bcaeabd1a434a7bdc Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 9 Dec 2017 23:47:50 -0500 Subject: [PATCH 0840/1329] Decided what to do about attributes. --- checklist_attrstr | 3 ++ ui_attrstr.h | 106 ++++++++++++++++++++++++++++++++-------------- 2 files changed, 78 insertions(+), 31 deletions(-) diff --git a/checklist_attrstr b/checklist_attrstr index 14dd4580..8dbf504e 100644 --- a/checklist_attrstr +++ b/checklist_attrstr @@ -7,3 +7,6 @@ overlapping attributes of the same type but different values do split empty string is allowed empty string cannot have attributes font family names are case-insensitive both in attributes and in descriptors +attributes are unique throughout a Unicode codepoint, not just to UTF-8 bytes +define what "it is an error" means in the case of uiFreeAttribute() and all uiAttributeValue() functions +does uiAttributeFamily() return a normalized string diff --git a/ui_attrstr.h b/ui_attrstr.h index b74f7408..cb562e02 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -1,37 +1,81 @@ +// uiAttribute stores information about an attribute in a +// uiAttributedString. +// +// You do not create uiAttributes directly; instead, you create a +// uiAttribute of a given type using the specialized constructor +// functions. For every Unicode codepoint in the uiAttributedString, +// at most one value of each attribute type can be applied. +// +// uiAttributes are immutable and the uiAttributedString takes +// ownership of the uiAttribute object once assigned, copying its +// contents as necessary. +typedef struct uiAttribute uiAttribute; + +// uiFreeAttribute() frees a uiAttribute. You generally do not need to +// call this yourself, as uiAttributedString does this for you. In fact, +// it is an error to call this function on a uiAttribute that has been +// given to a uiAttributedString. You can call this, however, if you +// created a uiAttribute that you aren't going to use later. +_UI_EXTERN void uiFreeAttribute(uiAttribute *a); + +// uiAttributeType holds the possible uiAttribute types that may be +// returned by uiAttributeGetType(). Refer to the documentation for +// each type's constructor function for details on each type. +_UI_ENUM(uiAttributeType) { + uiAttributeTypeFamily, + uiAttributeTypeSize, + uiAttributeTypeWeight, + uiAttributeTypeItalic, + uiAttributeTypeStretch, + uiAttributeTypeColor, + uiAttributeTypeBackground, + uiAttributeTypeUnderline, + uiAttributeTypeUnderlineColor, + uiAttributeTypeFeatures, +}; + +// uiAttributeGetType() returns the type of a. +// TODO I don't like this name +_UI_EXTERN uiAttributeType uiAttributeGetType(const uiAttribute *a); + +// uiNewFamilyAttribute() creates a new uiAttribute that changes the +// font family of the text it is applied to. Font family names are +// case-insensitive. +_UI_EXTERN uiAttribute *uiNewFamilyAttribute(const char *family); + +// uiAttributeFamily() returns the font family stored in a. The +// returned string is owned by a. It is an error to call this on a +// uiAttribute that does not hold a font family. +_UI_EXTERN const char *uiAttributeFamily(const uiAttribute *a); + +// uiNewSizeAttribute() creates a new uiAttribute that changes the +// size of the text it is applied to, in typographical points. +_UI_EXTERN uiAttribute *uiNewFamilyAttribute(double size); + +// uiAttributeSize() returns the font size stored in a. It is an error to +// call this on a uiAttribute that does not hold a font size. +_UI_EXTERN double uiAttributeSize(const uiAttribute *a); -// uiAttribute specifies the types of possible attributes that can be -// applied to a uiAttributedString. For every Unicode codepoint in the -// uiAttributedString, at most one value of each attribute type can -// be applied. -// TODO just make a separate field in uiAttributeSpec for everything? or make attribute objects opaque instead? -_UI_ENUM(uiAttribute) { - // uiAttributeFamily changes the font family of the text it is - // applied to. Font family names are case-insensitive. Use the - // Family field of uiAttributeSpec. - uiAttributeFamily, - // uiAttributeSize changes the size of the text it is applied to, - // in typographical points. Use the Size field of uiAttributeSpec. - uiAttributeSize, // uiAttributeWeight changes the weight of the text it is applied // to. Use the Weight field of uiAttributeSpec. - uiAttributeWeight, + uiAttributeTypeWeight, // uiAttributeItalic changes the italicness of the text it is applied // to. Use the Italic field of uiAttributeSpec. - uiAttributeItalic, + uiAttributeTypeItalic, // uiAttributeStretch changes the stretch of the text it is applied // to. Use the Stretch field of uiAttributeSpec. - uiAttributeStretch, + uiAttributeTypeStretch, // uiAttributeColor changes the color of the text it is applied to. // Use the R, G, B, and A fields of uiAttributeSpec. - uiAttributeColor, + uiAttributeTypeColor, // uiAttributeBackground changes the color of the text it is // applied to. Use the R, G, B, and A fields of uiAttributeSpec. - uiAttributeBackground, + uiAttributeTypeBackground, // uiAttributeUnderline changes the underline style of the text // it is applied to. Use the UnderlineStyle field of // uiAttributeSpec. - uiAttributeUnderline, + uiAttributeTypeUnderline, // uiAttributeUnderlineColor changes the color of any underline // on the text it is applied to, regardless of the style. Use the // UnderlineColor field of uiAttributeSpec, and also the R, G, B, @@ -41,25 +85,25 @@ _UI_ENUM(uiAttribute) { // specified, the text color is used instead. If an underline color // is specified without an underline style, the underline color // attribute is ignored, but not elided. - uiAttributeUnderlineColor, + uiAttributeTypeUnderlineColor, // uiAttributeFeatures changes the OpenType features of the // text it is applied to. Use the Features field of uiAttributeSpec. - uiAttributeFeatures, + uiAttributeTypeFeatures, }; -_UI_ENUM(uiDrawUnderlineStyle) { - uiDrawUnderlineStyleNone, - uiDrawUnderlineStyleSingle, - uiDrawUnderlineStyleDouble, - uiDrawUnderlineStyleSuggestion, // wavy or dotted underlines used for spelling/grammar checkers +_UI_ENUM(uiUnderlineStyle) { + uiUnderlineStyleNone, + uiUnderlineStyleSingle, + uiUnderlineStyleDouble, + uiUnderlineStyleSuggestion, // wavy or dotted underlines used for spelling/grammar checkers }; -_UI_ENUM(uiDrawUnderlineColor) { - uiDrawUnderlineColorCustom, // also use R/G/B/A fields - uiDrawUnderlineColorSpelling, - uiDrawUnderlineColorGrammar, - uiDrawUnderlineColorAuxiliary, // for instance, the color used by smart replacements on OS X +_UI_ENUM(uiUnderlineColor) { + uiUnderlineColorCustom, + uiUnderlineColorSpelling, + uiUnderlineColorGrammar, + uiUnderlineColorAuxiliary, // for instance, the color used by smart replacements on OS X }; // uiOpenTypeFeatures represents a set of OpenType feature From 84756ab0ecea7bf82dd38877024f22ce02db28c2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 10 Dec 2017 19:22:49 -0500 Subject: [PATCH 0841/1329] Finished setting up the new uiAttribute format. --- checklist_attrstr | 11 +- ui_attrstr.h | 261 +++++++++++++++++++++++++++++----------------- 2 files changed, 173 insertions(+), 99 deletions(-) diff --git a/checklist_attrstr b/checklist_attrstr index 8dbf504e..89157d11 100644 --- a/checklist_attrstr +++ b/checklist_attrstr @@ -8,5 +8,14 @@ empty string is allowed empty string cannot have attributes font family names are case-insensitive both in attributes and in descriptors attributes are unique throughout a Unicode codepoint, not just to UTF-8 bytes -define what "it is an error" means in the case of uiFreeAttribute() and all uiAttributeValue() functions +define what "it is an error" means in the case of uiFreeAttribute() and all uiAttributeValue() functions and constructors does uiAttributeFamily() return a normalized string +should uiNewAttributeBackground() be renamed to uiNewAttributeBackgroundColor() and likewise for the type constant +should underline colors just ignore non-custom component arguments +should any color getter function accept a NULL pointer +what should uiAttributeUnderlineColor() do if the color type isn't Custom but the other pointers are non-NULL +should uiOpenTypeFeaturesGet() accept a NULL value pointer +what happens if uiOpenTypeFeaturesForEach() is given a NULl function pointer +should FeaturesAttribute be changed to OpenTypeFeaturesAttribute and likewise for the type enum +should uiNewFeaturesAttribute() accept NULL +should uiNewFamilyAttribute() accept NULL diff --git a/ui_attrstr.h b/ui_attrstr.h index cb562e02..05d541a9 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -39,8 +39,9 @@ _UI_ENUM(uiAttributeType) { _UI_EXTERN uiAttributeType uiAttributeGetType(const uiAttribute *a); // uiNewFamilyAttribute() creates a new uiAttribute that changes the -// font family of the text it is applied to. Font family names are -// case-insensitive. +// font family of the text it is applied to. family is copied; you do not +// need to keep it alive after uiNewFamilyAttribute() returns. Font +// family names are case-insensitive. _UI_EXTERN uiAttribute *uiNewFamilyAttribute(const char *family); // uiAttributeFamily() returns the font family stored in a. The @@ -56,56 +57,165 @@ _UI_EXTERN uiAttribute *uiNewFamilyAttribute(double size); // call this on a uiAttribute that does not hold a font size. _UI_EXTERN double uiAttributeSize(const uiAttribute *a); - // uiAttributeWeight changes the weight of the text it is applied - // to. Use the Weight field of uiAttributeSpec. - uiAttributeTypeWeight, - // uiAttributeItalic changes the italicness of the text it is applied - // to. Use the Italic field of uiAttributeSpec. - uiAttributeTypeItalic, - // uiAttributeStretch changes the stretch of the text it is applied - // to. Use the Stretch field of uiAttributeSpec. - uiAttributeTypeStretch, - // uiAttributeColor changes the color of the text it is applied to. - // Use the R, G, B, and A fields of uiAttributeSpec. - uiAttributeTypeColor, - // uiAttributeBackground changes the color of the text it is - // applied to. Use the R, G, B, and A fields of uiAttributeSpec. - uiAttributeTypeBackground, - - // uiAttributeUnderline changes the underline style of the text - // it is applied to. Use the UnderlineStyle field of - // uiAttributeSpec. - uiAttributeTypeUnderline, - // uiAttributeUnderlineColor changes the color of any underline - // on the text it is applied to, regardless of the style. Use the - // UnderlineColor field of uiAttributeSpec, and also the R, G, B, - // and A fields if specifying uiDrawUnderlineColorCustom. - // - // If an underline style is applied but no underline color is - // specified, the text color is used instead. If an underline color - // is specified without an underline style, the underline color - // attribute is ignored, but not elided. - uiAttributeTypeUnderlineColor, - - // uiAttributeFeatures changes the OpenType features of the - // text it is applied to. Use the Features field of uiAttributeSpec. - uiAttributeTypeFeatures, +// uiTextWeight represents possible text weights. These roughly +// map to the OSx2 text weight field of TrueType and OpenType +// fonts, or to CSS weight numbers. The named constants are +// nominal values; the actual values may vary by font and by OS, +// though this isn't particularly likely. Any value between +// uiTextWeightMinimum and uiDrawTextWeightMaximum, +// inclusive, is allowed. +// +// Note that due to restrictions in early versions of Windows, some +// fonts have "special" weights be exposed in many programs as +// separate font families. This is perhaps most notable with +// Arial Black. libui does not do this, even on Windows (because the +// DirectWrite API libui uses on Windows does not do this); to +// specify Arial Black, use family Arial and weight uiTextWeightBlack. +_UI_ENUM(uiTextWeight) { + uiTextWeightMinimum = 0, + uiTextWeightThin = 100, + uiTextWeightUltraLight = 200, + uiTextWeightLight = 300, + uiTextWeightBook = 350, + uiTextWeightNormal = 400, + uiTextWeightMedium = 500, + uiTextWeightSemiBold = 600, + uiTextWeightBold = 700, + uiTextWeightUltraBold = 800, + uiTextWeightHeavy = 900, + uiTextWeightUltraHeavy = 950, + uiTextWeightMaximum = 1000, }; -_UI_ENUM(uiUnderlineStyle) { - uiUnderlineStyleNone, - uiUnderlineStyleSingle, - uiUnderlineStyleDouble, - uiUnderlineStyleSuggestion, // wavy or dotted underlines used for spelling/grammar checkers +// uiNewWeightAttribute() creates a new uiAttribute that changes the +// weight of the text it is applied to. It is an error to specify a weight +// outside the range [uiTextWeightMinimum, +// uiTextWeightMaximum]. +_UI_EXTERN uiAttribute *uiNewWeightAttribute(uiTextWeight weight); + +// uiAttributeWeight() returns the font weight stored in a. It is an error +// to call this on a uiAttribute that does not hold a font weight. +_UI_EXTERN uiTextWeight uiAttributeWeight(const uiAttribute *a); + +// uiTextItalic represents possible italic modes for a font. Italic +// represents "true" italics where the slanted glyphs have custom +// shapes, whereas oblique represents italics that are merely slanted +// versions of the normal glyphs. Most fonts usually have one or the +// other. +_UI_ENUM(uiTextItalic) { + uiTextItalicNormal, + uiTextItalicOblique, + uiTextItalicItalic, }; +// uiNewItalicAttribute() creates a new uiAttribute that changes the +// italic mode of the text it is applied to. It is an error to specify an +// italic mode not specified in uiTextItalic. +_UI_EXTERN uiAttribute *uiNewItalicAttribute(uiTextItalic italic); + +// uiAttributeItalic() returns the font italic mode stored in a. It is an +// error to call this on a uiAttribute that does not hold a font italic +// mode. +_UI_EXTERN uiTextItalic uiAttributeItalic(const uiAttribute *a); + +// uiTextStretch represents possible stretches (also called "widths") +// of a font. +// +// Note that due to restrictions in early versions of Windows, some +// fonts have "special" stretches be exposed in many programs as +// separate font families. This is perhaps most notable with +// Arial Condensed. libui does not do this, even on Windows (because +// the DirectWrite API libui uses on Windows does not do this); to +// specify Arial Condensed, use family Arial and stretch +// uiTextStretchCondensed. +_UI_ENUM(uiTextStretch) { + uiTextStretchUltraCondensed, + uiTextStretchExtraCondensed, + uiTextStretchCondensed, + uiTextStretchSemiCondensed, + uiTextStretchNormal, + uiTextStretchSemiExpanded, + uiTextStretchExpanded, + uiTextStretchExtraExpanded, + uiTextStretchUltraExpanded, +}; + +// uiNewStretchAttribute() creates a new uiAttribute that changes the +// stretch of the text it is applied to. It is an error to specify a strech +// not specified in uiTextStretch. +_UI_EXTERN uiAttribute *uiNewStretchAttribute(uiTextStretch stretch); + +// uiAttributeStretch() returns the font stretch stored in a. It is an +// error to call this on a uiAttribute that does not hold a font stretch. +_UI_EXTERN uiTextStretch uiAttributeStretch(const uiAttribute *a); + +// uiNewColorAttribute() creates a new uiAttribute that changes the +// color of the text it is applied to. It is an error to specify an invalid +// color. +_UI_EXTERN uiAttribute *uiNewColorAttribute(double r, double g, double b, double a); + +// uiAttributeColor() returns the text color stored in a. It is an +// error to call this on a uiAttribute that does not hold a text color. +_UI_EXTERN void uiAttributeColor(const uiAttribute *a, double *r, double *g, double *b, double *alpha); + +// uiNewBackgroundAttribute() creates a new uiAttribute that +// changes the background color of the text it is applied to. It is an +// error to specify an invalid color. +_UI_EXTERN uiAttribute *uiNewBackgroundAttribute(double r, double g, double b, double a); + +// TODO reuse uiAttributeColor() for background colors, or make a new function... + +// uiUnderline specifies a type of underline to use on text. +_UI_ENUM(uiUnderline) { + uiUnderlineNone, + uiUnderlineSingle, + uiUnderlineDouble, + uiUnderlineSuggestion, // wavy or dotted underlines used for spelling/grammar checkers +}; + +// uiNewUnderlineAttribute() creates a new uiAttribute that changes +// the type of underline on the text it is applied to. It is an error to +// specify an underline type not specified in uiUnderline. +_UI_EXTERN uiAttribute *uiNewUnderlineAttribute(uiUnderline u); + +// uiAttributeUnderline() returns the underline type stored in a. It is +// an error to call this on a uiAttribute that does not hold an underline +// style. +_UI_EXTERN uiUnderline uiAttributeUnderline(const uiAttribute *a); + +// uiUnderlineColor specifies the color of any underline on the text it +// is applied to, regardless of the type of underline. In addition to +// being able to specify a custom color, you can explicitly specify +// platform-specific colors for suggestion underlines; to use them +// correctly, pair them with uiUnderlineSuggestion (though they can +// be used on other types of underline as well). +// +// If an underline type is applied but no underline color is +// specified, the text color is used instead. If an underline color +// is specified without an underline type, the underline color +// attribute is ignored, but not removed from the uiAttributedString. _UI_ENUM(uiUnderlineColor) { uiUnderlineColorCustom, uiUnderlineColorSpelling, uiUnderlineColorGrammar, - uiUnderlineColorAuxiliary, // for instance, the color used by smart replacements on OS X + uiUnderlineColorAuxiliary, // for instance, the color used by smart replacements on macOS or in Microsoft Office }; +// uiNewUnderlineColorAttribute() creates a new uiAttribute that +// changes the color of the underline on the text it is applied to. +// It is an error to specify an underline color not specified in +// uiUnderlineColor. +// +// If the specified color type is uiUnderlineColorCustom, it is an +// error to specify an invalid color value. Otherwise, the color values +// are ignored and should be specified as zero. +_UI_EXTERN uiAttribute *uiNewUnderlineColorAttribute(uiUnderlineColor u, double r, double g, double b, double a); + +// uiAttributeUnderlineColor() returns the underline color stored in +// a. It is an error to call this on a uiAttribute that does not hold an +// underline color. +_UI_EXTERN void uiAttributeUnderline(const uiAttribute *a, uiUnderlineColor *u, double *r, double *g, double *b, double *alpha); + // uiOpenTypeFeatures represents a set of OpenType feature // tag-value pairs, for applying OpenType features to text. // OpenType feature tags are four-character codes defined by @@ -175,68 +285,23 @@ _UI_EXTERN int uiOpenTypeFeaturesGet(const uiOpenTypeFeatures *otf, char a, char // modify otf while uiOpenTypeFeaturesForEach() is running. _UI_EXTERN void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data); -// uiOpenTypeFeaturesEqual() returns nonzero if a is equal to b. -// a is defined as equal to b if and only if both have exactly the same -// tags with exactly the same values, or if both are NULL. -_UI_EXTERN int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeatures *b); +// uiNewFeaturesAttribute() creates a new uiAttribute that changes +// the font family of the text it is applied to. otf is copied; you may +// free it after uiNewFeaturesAttribute() returns. +_UI_EXTERN uiAttribute *uiNewFeaturesAttribute(const uiOpenTypeFeatures *otf); -typedef struct uiAttributeSpec uiAttributeSpec; +// uiAttributeFeatures() returns the OpenType features stored in a. +// The returned uiOpenTypeFeatures object is owned by a. It is an +// error to call this on a uiAttribute that does not hold OpenType +// features. +_UI_EXTERN const uiOpenTypeFeatures *uiAttributeFeatures(const uiAttribute *a); + +// TODO CONTINUE HERE -// TODO note that pointers are copied // TODO add a function to uiAttributedString to get an attribute's value at a specific index or in a specific range, so we can edit -// (TODO related to above: what about memory consumption during a read-modify-write cycle due to copying?) -struct uiAttributeSpec { - uiAttribute Type; - const char *Family; - double Size; - uiDrawTextWeight Weight; - uiDrawTextItalic Italic; - uiDrawTextStretch Stretch; - double R; - double G; - double B; - double A; - uiDrawUnderlineStyle UnderlineStyle; - uiDrawUnderlineColor UnderlineColor; - const uiOpenTypeFeatures *Features; -}; typedef struct uiDrawFontDescriptor uiDrawFontDescriptor; -_UI_ENUM(uiDrawTextWeight) { - uiDrawTextWeightMinimum = 0, - uiDrawTextWeightThin = 100, - uiDrawTextWeightUltraLight = 200, - uiDrawTextWeightLight = 300, - uiDrawTextWeightBook = 350, - uiDrawTextWeightNormal = 400, - uiDrawTextWeightMedium = 500, - uiDrawTextWeightSemiBold = 600, - uiDrawTextWeightBold = 700, - uiDrawTextWeightUltraBold = 800, - uiDrawTextWeightHeavy = 900, - uiDrawTextWeightUltraHeavy = 950, - uiDrawTextWeightMaximum = 1000, -}; - -_UI_ENUM(uiDrawTextItalic) { - uiDrawTextItalicNormal, - uiDrawTextItalicOblique, - uiDrawTextItalicItalic, -}; - -_UI_ENUM(uiDrawTextStretch) { - uiDrawTextStretchUltraCondensed, - uiDrawTextStretchExtraCondensed, - uiDrawTextStretchCondensed, - uiDrawTextStretchSemiCondensed, - uiDrawTextStretchNormal, - uiDrawTextStretchSemiExpanded, - uiDrawTextStretchExpanded, - uiDrawTextStretchExtraExpanded, - uiDrawTextStretchUltraExpanded, -}; - struct uiDrawFontDescriptor { char *Family; double Size; From cbc78248e77a3b5eba75def70d81eb67764123ad Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 13 Dec 2017 23:41:49 -0800 Subject: [PATCH 0842/1329] More attrstr API finalization work, --- checklist_attrstr | 1 + new_ui_attrstr.h | 344 ++++++++++++++++++++++++++++++++++++++++++++-- ui_attrstr.h | 312 ----------------------------------------- 3 files changed, 336 insertions(+), 321 deletions(-) diff --git a/checklist_attrstr b/checklist_attrstr index 89157d11..e4c6bf3f 100644 --- a/checklist_attrstr +++ b/checklist_attrstr @@ -19,3 +19,4 @@ what happens if uiOpenTypeFeaturesForEach() is given a NULl function pointer should FeaturesAttribute be changed to OpenTypeFeaturesAttribute and likewise for the type enum should uiNewFeaturesAttribute() accept NULL should uiNewFamilyAttribute() accept NULL +it is an error in ForEach too diff --git a/new_ui_attrstr.h b/new_ui_attrstr.h index 083e404f..8772f4e0 100644 --- a/new_ui_attrstr.h +++ b/new_ui_attrstr.h @@ -1,3 +1,301 @@ +// uiAttribute stores information about an attribute in a +// uiAttributedString. +// +// You do not create uiAttributes directly; instead, you create a +// uiAttribute of a given type using the specialized constructor +// functions. For every Unicode codepoint in the uiAttributedString, +// at most one value of each attribute type can be applied. +// +// uiAttributes are immutable and the uiAttributedString takes +// ownership of the uiAttribute object once assigned, copying its +// contents as necessary. +typedef struct uiAttribute uiAttribute; + +// uiFreeAttribute() frees a uiAttribute. You generally do not need to +// call this yourself, as uiAttributedString does this for you. In fact, +// it is an error to call this function on a uiAttribute that has been +// given to a uiAttributedString. You can call this, however, if you +// created a uiAttribute that you aren't going to use later. +_UI_EXTERN void uiFreeAttribute(uiAttribute *a); + +// uiAttributeType holds the possible uiAttribute types that may be +// returned by uiAttributeGetType(). Refer to the documentation for +// each type's constructor function for details on each type. +_UI_ENUM(uiAttributeType) { + uiAttributeTypeFamily, + uiAttributeTypeSize, + uiAttributeTypeWeight, + uiAttributeTypeItalic, + uiAttributeTypeStretch, + uiAttributeTypeColor, + uiAttributeTypeBackground, + uiAttributeTypeUnderline, + uiAttributeTypeUnderlineColor, + uiAttributeTypeFeatures, +}; + +// uiAttributeGetType() returns the type of a. +// TODO I don't like this name +_UI_EXTERN uiAttributeType uiAttributeGetType(const uiAttribute *a); + +// uiNewFamilyAttribute() creates a new uiAttribute that changes the +// font family of the text it is applied to. family is copied; you do not +// need to keep it alive after uiNewFamilyAttribute() returns. Font +// family names are case-insensitive. +_UI_EXTERN uiAttribute *uiNewFamilyAttribute(const char *family); + +// uiAttributeFamily() returns the font family stored in a. The +// returned string is owned by a. It is an error to call this on a +// uiAttribute that does not hold a font family. +_UI_EXTERN const char *uiAttributeFamily(const uiAttribute *a); + +// uiNewSizeAttribute() creates a new uiAttribute that changes the +// size of the text it is applied to, in typographical points. +_UI_EXTERN uiAttribute *uiNewFamilyAttribute(double size); + +// uiAttributeSize() returns the font size stored in a. It is an error to +// call this on a uiAttribute that does not hold a font size. +_UI_EXTERN double uiAttributeSize(const uiAttribute *a); + +// uiTextWeight represents possible text weights. These roughly +// map to the OSx2 text weight field of TrueType and OpenType +// fonts, or to CSS weight numbers. The named constants are +// nominal values; the actual values may vary by font and by OS, +// though this isn't particularly likely. Any value between +// uiTextWeightMinimum and uiDrawTextWeightMaximum, +// inclusive, is allowed. +// +// Note that due to restrictions in early versions of Windows, some +// fonts have "special" weights be exposed in many programs as +// separate font families. This is perhaps most notable with +// Arial Black. libui does not do this, even on Windows (because the +// DirectWrite API libui uses on Windows does not do this); to +// specify Arial Black, use family Arial and weight uiTextWeightBlack. +_UI_ENUM(uiTextWeight) { + uiTextWeightMinimum = 0, + uiTextWeightThin = 100, + uiTextWeightUltraLight = 200, + uiTextWeightLight = 300, + uiTextWeightBook = 350, + uiTextWeightNormal = 400, + uiTextWeightMedium = 500, + uiTextWeightSemiBold = 600, + uiTextWeightBold = 700, + uiTextWeightUltraBold = 800, + uiTextWeightHeavy = 900, + uiTextWeightUltraHeavy = 950, + uiTextWeightMaximum = 1000, +}; + +// uiNewWeightAttribute() creates a new uiAttribute that changes the +// weight of the text it is applied to. It is an error to specify a weight +// outside the range [uiTextWeightMinimum, +// uiTextWeightMaximum]. +_UI_EXTERN uiAttribute *uiNewWeightAttribute(uiTextWeight weight); + +// uiAttributeWeight() returns the font weight stored in a. It is an error +// to call this on a uiAttribute that does not hold a font weight. +_UI_EXTERN uiTextWeight uiAttributeWeight(const uiAttribute *a); + +// uiTextItalic represents possible italic modes for a font. Italic +// represents "true" italics where the slanted glyphs have custom +// shapes, whereas oblique represents italics that are merely slanted +// versions of the normal glyphs. Most fonts usually have one or the +// other. +_UI_ENUM(uiTextItalic) { + uiTextItalicNormal, + uiTextItalicOblique, + uiTextItalicItalic, +}; + +// uiNewItalicAttribute() creates a new uiAttribute that changes the +// italic mode of the text it is applied to. It is an error to specify an +// italic mode not specified in uiTextItalic. +_UI_EXTERN uiAttribute *uiNewItalicAttribute(uiTextItalic italic); + +// uiAttributeItalic() returns the font italic mode stored in a. It is an +// error to call this on a uiAttribute that does not hold a font italic +// mode. +_UI_EXTERN uiTextItalic uiAttributeItalic(const uiAttribute *a); + +// uiTextStretch represents possible stretches (also called "widths") +// of a font. +// +// Note that due to restrictions in early versions of Windows, some +// fonts have "special" stretches be exposed in many programs as +// separate font families. This is perhaps most notable with +// Arial Condensed. libui does not do this, even on Windows (because +// the DirectWrite API libui uses on Windows does not do this); to +// specify Arial Condensed, use family Arial and stretch +// uiTextStretchCondensed. +_UI_ENUM(uiTextStretch) { + uiTextStretchUltraCondensed, + uiTextStretchExtraCondensed, + uiTextStretchCondensed, + uiTextStretchSemiCondensed, + uiTextStretchNormal, + uiTextStretchSemiExpanded, + uiTextStretchExpanded, + uiTextStretchExtraExpanded, + uiTextStretchUltraExpanded, +}; + +// uiNewStretchAttribute() creates a new uiAttribute that changes the +// stretch of the text it is applied to. It is an error to specify a strech +// not specified in uiTextStretch. +_UI_EXTERN uiAttribute *uiNewStretchAttribute(uiTextStretch stretch); + +// uiAttributeStretch() returns the font stretch stored in a. It is an +// error to call this on a uiAttribute that does not hold a font stretch. +_UI_EXTERN uiTextStretch uiAttributeStretch(const uiAttribute *a); + +// uiNewColorAttribute() creates a new uiAttribute that changes the +// color of the text it is applied to. It is an error to specify an invalid +// color. +_UI_EXTERN uiAttribute *uiNewColorAttribute(double r, double g, double b, double a); + +// uiAttributeColor() returns the text color stored in a. It is an +// error to call this on a uiAttribute that does not hold a text color. +_UI_EXTERN void uiAttributeColor(const uiAttribute *a, double *r, double *g, double *b, double *alpha); + +// uiNewBackgroundAttribute() creates a new uiAttribute that +// changes the background color of the text it is applied to. It is an +// error to specify an invalid color. +_UI_EXTERN uiAttribute *uiNewBackgroundAttribute(double r, double g, double b, double a); + +// TODO reuse uiAttributeColor() for background colors, or make a new function... + +// uiUnderline specifies a type of underline to use on text. +_UI_ENUM(uiUnderline) { + uiUnderlineNone, + uiUnderlineSingle, + uiUnderlineDouble, + uiUnderlineSuggestion, // wavy or dotted underlines used for spelling/grammar checkers +}; + +// uiNewUnderlineAttribute() creates a new uiAttribute that changes +// the type of underline on the text it is applied to. It is an error to +// specify an underline type not specified in uiUnderline. +_UI_EXTERN uiAttribute *uiNewUnderlineAttribute(uiUnderline u); + +// uiAttributeUnderline() returns the underline type stored in a. It is +// an error to call this on a uiAttribute that does not hold an underline +// style. +_UI_EXTERN uiUnderline uiAttributeUnderline(const uiAttribute *a); + +// uiUnderlineColor specifies the color of any underline on the text it +// is applied to, regardless of the type of underline. In addition to +// being able to specify a custom color, you can explicitly specify +// platform-specific colors for suggestion underlines; to use them +// correctly, pair them with uiUnderlineSuggestion (though they can +// be used on other types of underline as well). +// +// If an underline type is applied but no underline color is +// specified, the text color is used instead. If an underline color +// is specified without an underline type, the underline color +// attribute is ignored, but not removed from the uiAttributedString. +_UI_ENUM(uiUnderlineColor) { + uiUnderlineColorCustom, + uiUnderlineColorSpelling, + uiUnderlineColorGrammar, + uiUnderlineColorAuxiliary, // for instance, the color used by smart replacements on macOS or in Microsoft Office +}; + +// uiNewUnderlineColorAttribute() creates a new uiAttribute that +// changes the color of the underline on the text it is applied to. +// It is an error to specify an underline color not specified in +// uiUnderlineColor. +// +// If the specified color type is uiUnderlineColorCustom, it is an +// error to specify an invalid color value. Otherwise, the color values +// are ignored and should be specified as zero. +_UI_EXTERN uiAttribute *uiNewUnderlineColorAttribute(uiUnderlineColor u, double r, double g, double b, double a); + +// uiAttributeUnderlineColor() returns the underline color stored in +// a. It is an error to call this on a uiAttribute that does not hold an +// underline color. +_UI_EXTERN void uiAttributeUnderline(const uiAttribute *a, uiUnderlineColor *u, double *r, double *g, double *b, double *alpha); + +// uiOpenTypeFeatures represents a set of OpenType feature +// tag-value pairs, for applying OpenType features to text. +// OpenType feature tags are four-character codes defined by +// OpenType that cover things from design features like small +// caps and swashes to language-specific glyph shapes and +// beyond. Each tag may only appear once in any given +// uiOpenTypeFeatures instance. Each value is a 32-bit integer, +// often used as a Boolean flag, but sometimes as an index to choose +// a glyph shape to use. +// +// If a font does not support a certain feature, that feature will be +// ignored. (TODO verify this on all OSs) +// +// See the OpenType specification at +// https://www.microsoft.com/typography/otspec/featuretags.htm +// for the complete list of available features, information on specific +// features, and how to use them. +// TODO invalid features +typedef struct uiOpenTypeFeatures uiOpenTypeFeatures; + +// uiOpenTypeFeaturesForEachFunc is the type of the function +// invoked by uiOpenTypeFeaturesForEach() for every OpenType +// feature in otf. Refer to that function's documentation for more +// details. +typedef uiForEach (*uiOpenTypeFeaturesForEachFunc)(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data); + +// @role uiOpenTypeFeatures constructor +// uiNewOpenTypeFeatures() returns a new uiOpenTypeFeatures +// instance, with no tags yet added. +_UI_EXTERN uiOpenTypeFeatures *uiNewOpenTypeFeatures(void); + +// @role uiOpenTypeFeatures destructor +// uiFreeOpenTypeFeatures() frees otf. +_UI_EXTERN void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf); + +// uiOpenTypeFeaturesClone() makes a copy of otf and returns it. +// Changing one will not affect the other. +_UI_EXTERN uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures *otf); + +// uiOpenTypeFeaturesAdd() adds the given feature tag and value +// to otf. The feature tag is specified by a, b, c, and d. If there is +// already a value associated with the specified tag in otf, the old +// value is removed. +_UI_EXTERN void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value); + +// uiOpenTypeFeaturesRemove() removes the given feature tag +// and value from otf. If the tag is not present in otf, +// uiOpenTypeFeaturesRemove() does nothing. +_UI_EXTERN void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d); + +// uiOpenTypeFeaturesGet() determines whether the given feature +// tag is present in otf. If it is, *value is set to the tag's value and +// nonzero is returned. Otherwise, zero is returned. +// +// Note that if uiOpenTypeFeaturesGet() returns zero, value isn't +// changed. This is important: if a feature is not present in a +// uiOpenTypeFeatures, the feature is NOT treated as if its +// value was zero anyway. Script-specific font shaping rules and +// font-specific feature settings may use a different default value +// for a feature. You should likewise not treat a missing feature as +// having a value of zero either. Instead, a missing feature should +// be treated as having some unspecified default value. +_UI_EXTERN int uiOpenTypeFeaturesGet(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value); + +// uiOpenTypeFeaturesForEach() executes f for every tag-value +// pair in otf. The enumeration order is unspecified. You cannot +// modify otf while uiOpenTypeFeaturesForEach() is running. +_UI_EXTERN void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data); + +// uiNewFeaturesAttribute() creates a new uiAttribute that changes +// the font family of the text it is applied to. otf is copied; you may +// free it after uiNewFeaturesAttribute() returns. +_UI_EXTERN uiAttribute *uiNewFeaturesAttribute(const uiOpenTypeFeatures *otf); + +// uiAttributeFeatures() returns the OpenType features stored in a. +// The returned uiOpenTypeFeatures object is owned by a. It is an +// error to call this on a uiAttribute that does not hold OpenType +// features. +_UI_EXTERN const uiOpenTypeFeatures *uiAttributeFeatures(const uiAttribute *a); + // uiAttributedString represents a string of UTF-8 text that can // optionally be embellished with formatting attributes. libui // provides the list of formatting attributes, which cover common @@ -17,7 +315,11 @@ // but because of the no-zero-length-attribute rule, it will not have // attributes. // -// TODO note here about attribute ownership +// A uiAttributedString takes ownership of all attributes given to +// it, as it may need to duplicate or delete uiAttribute objects at +// any time. By extension, when you free a uiAttributedString, +// all uiAttributes within will also be freed. Each method will +// describe its own rules in more details. // // In addition, uiAttributedString provides facilities for moving // between grapheme clusters, which represent a character @@ -37,7 +339,7 @@ typedef struct uiAttributedString uiAttributedString; // invoked by uiAttributedStringForEachAttribute() for every // attribute in s. Refer to that function's documentation for more // details. -typedef uiForEach (*uiAttributedStringForEachAttributeFunc)(const uiAttributedString *s, const uiAttributeSpec *spec, size_t start, size_t end, void *data); +typedef uiForEach (*uiAttributedStringForEachAttributeFunc)(const uiAttributedString *s, const uiAttribute *a, size_t start, size_t end, void *data); // @role uiAttributedString constructor // uiNewAttributedString() creates a new uiAttributedString from @@ -46,7 +348,7 @@ _UI_EXTERN uiAttributedString *uiNewAttributedString(const char *initialString); // @role uiAttributedString destructor // uiFreeAttributedString() destroys the uiAttributedString s. -// TODO note here about attribute ownership +// It will also free all uiAttributes within. _UI_EXTERN void uiFreeAttributedString(uiAttributedString *s); // uiAttributedStringString() returns the textual content of s as a @@ -66,16 +368,31 @@ _UI_EXTERN void uiAttributedStringAppendUnattributed(uiAttributedString *s, cons // uiAttributedStringInsertAtUnattributed() adds the '\0'-terminated // UTF-8 string str to s at the byte position specified by at. The new // substring will be unattributed; existing attributes will be moved -// along with its text. +// along with their text. _UI_EXTERN void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *str, size_t at); // TODO add the Append and InsertAtExtendingAttributes functions // TODO and add functions that take a string + length // uiAttributedStringDelete() deletes the characters and attributes of -// s in the range [start, end). +// s in the byte range [start, end). _UI_EXTERN void uiAttributedStringDelete(uiAttributedString *s, size_t start, size_t end); +// TODO add a function to uiAttributedString to get an attribute's value at a specific index or in a specific range, so we can edit + +// uiAttributedStringSetAttribute() sets a in the byte range [start, end) +// of s. Any existing attributes in that byte range of the same type are +// removed. s takes ownership of a; you should not use it after +// uiAttributedStringSetAttribute() returns. +_UI_EXTERN void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttribute *a, size_t start, size_t end); + +// uiAttributedStringForEachAttribute() enumerates all the +// uiAttributes in s. It is an error to modify s in f. Within f, s still +// owns the attribute; you can neither free it nor save it for later +// use. +// TODO reword the above for consistency +_UI_EXTERN void uiAttributedStringForEachAttribute(const uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); + // TODO const correct this somehow (the implementation needs to mutate the structure) _UI_EXTERN size_t uiAttributedStringNumGraphemes(uiAttributedString *s); @@ -85,8 +402,17 @@ _UI_EXTERN size_t uiAttributedStringByteIndexToGrapheme(uiAttributedString *s, s // TODO const correct this somehow (the implementation needs to mutate the structure) _UI_EXTERN size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos); -_UI_EXTERN void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end); +// uiFontDescriptor provides a complete description of a font where +// one is needed. Currently, this means as the default font of a +// uiDrawTextLayout and as the data returned by uiFontButton. +// All the members operate like the respective uiAttributes. +typedef struct uiFontDescriptor uiFontDescriptor; -// TODO document this -// TODO possibly copy the spec each time to ensure it doesn't get clobbered -_UI_EXTERN void uiAttributedStringForEachAttribute(uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); +struct uiDrawFontDescriptor { + // TODO const-correct this or figure out how to deal with this when getting a value + char *Family; + double Size; + uiTextWeight Weight; + uiTextItalic Italic; + uiTextStretch Stretch; +}; diff --git a/ui_attrstr.h b/ui_attrstr.h index 05d541a9..c4d8382b 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -1,315 +1,3 @@ -// uiAttribute stores information about an attribute in a -// uiAttributedString. -// -// You do not create uiAttributes directly; instead, you create a -// uiAttribute of a given type using the specialized constructor -// functions. For every Unicode codepoint in the uiAttributedString, -// at most one value of each attribute type can be applied. -// -// uiAttributes are immutable and the uiAttributedString takes -// ownership of the uiAttribute object once assigned, copying its -// contents as necessary. -typedef struct uiAttribute uiAttribute; - -// uiFreeAttribute() frees a uiAttribute. You generally do not need to -// call this yourself, as uiAttributedString does this for you. In fact, -// it is an error to call this function on a uiAttribute that has been -// given to a uiAttributedString. You can call this, however, if you -// created a uiAttribute that you aren't going to use later. -_UI_EXTERN void uiFreeAttribute(uiAttribute *a); - -// uiAttributeType holds the possible uiAttribute types that may be -// returned by uiAttributeGetType(). Refer to the documentation for -// each type's constructor function for details on each type. -_UI_ENUM(uiAttributeType) { - uiAttributeTypeFamily, - uiAttributeTypeSize, - uiAttributeTypeWeight, - uiAttributeTypeItalic, - uiAttributeTypeStretch, - uiAttributeTypeColor, - uiAttributeTypeBackground, - uiAttributeTypeUnderline, - uiAttributeTypeUnderlineColor, - uiAttributeTypeFeatures, -}; - -// uiAttributeGetType() returns the type of a. -// TODO I don't like this name -_UI_EXTERN uiAttributeType uiAttributeGetType(const uiAttribute *a); - -// uiNewFamilyAttribute() creates a new uiAttribute that changes the -// font family of the text it is applied to. family is copied; you do not -// need to keep it alive after uiNewFamilyAttribute() returns. Font -// family names are case-insensitive. -_UI_EXTERN uiAttribute *uiNewFamilyAttribute(const char *family); - -// uiAttributeFamily() returns the font family stored in a. The -// returned string is owned by a. It is an error to call this on a -// uiAttribute that does not hold a font family. -_UI_EXTERN const char *uiAttributeFamily(const uiAttribute *a); - -// uiNewSizeAttribute() creates a new uiAttribute that changes the -// size of the text it is applied to, in typographical points. -_UI_EXTERN uiAttribute *uiNewFamilyAttribute(double size); - -// uiAttributeSize() returns the font size stored in a. It is an error to -// call this on a uiAttribute that does not hold a font size. -_UI_EXTERN double uiAttributeSize(const uiAttribute *a); - -// uiTextWeight represents possible text weights. These roughly -// map to the OSx2 text weight field of TrueType and OpenType -// fonts, or to CSS weight numbers. The named constants are -// nominal values; the actual values may vary by font and by OS, -// though this isn't particularly likely. Any value between -// uiTextWeightMinimum and uiDrawTextWeightMaximum, -// inclusive, is allowed. -// -// Note that due to restrictions in early versions of Windows, some -// fonts have "special" weights be exposed in many programs as -// separate font families. This is perhaps most notable with -// Arial Black. libui does not do this, even on Windows (because the -// DirectWrite API libui uses on Windows does not do this); to -// specify Arial Black, use family Arial and weight uiTextWeightBlack. -_UI_ENUM(uiTextWeight) { - uiTextWeightMinimum = 0, - uiTextWeightThin = 100, - uiTextWeightUltraLight = 200, - uiTextWeightLight = 300, - uiTextWeightBook = 350, - uiTextWeightNormal = 400, - uiTextWeightMedium = 500, - uiTextWeightSemiBold = 600, - uiTextWeightBold = 700, - uiTextWeightUltraBold = 800, - uiTextWeightHeavy = 900, - uiTextWeightUltraHeavy = 950, - uiTextWeightMaximum = 1000, -}; - -// uiNewWeightAttribute() creates a new uiAttribute that changes the -// weight of the text it is applied to. It is an error to specify a weight -// outside the range [uiTextWeightMinimum, -// uiTextWeightMaximum]. -_UI_EXTERN uiAttribute *uiNewWeightAttribute(uiTextWeight weight); - -// uiAttributeWeight() returns the font weight stored in a. It is an error -// to call this on a uiAttribute that does not hold a font weight. -_UI_EXTERN uiTextWeight uiAttributeWeight(const uiAttribute *a); - -// uiTextItalic represents possible italic modes for a font. Italic -// represents "true" italics where the slanted glyphs have custom -// shapes, whereas oblique represents italics that are merely slanted -// versions of the normal glyphs. Most fonts usually have one or the -// other. -_UI_ENUM(uiTextItalic) { - uiTextItalicNormal, - uiTextItalicOblique, - uiTextItalicItalic, -}; - -// uiNewItalicAttribute() creates a new uiAttribute that changes the -// italic mode of the text it is applied to. It is an error to specify an -// italic mode not specified in uiTextItalic. -_UI_EXTERN uiAttribute *uiNewItalicAttribute(uiTextItalic italic); - -// uiAttributeItalic() returns the font italic mode stored in a. It is an -// error to call this on a uiAttribute that does not hold a font italic -// mode. -_UI_EXTERN uiTextItalic uiAttributeItalic(const uiAttribute *a); - -// uiTextStretch represents possible stretches (also called "widths") -// of a font. -// -// Note that due to restrictions in early versions of Windows, some -// fonts have "special" stretches be exposed in many programs as -// separate font families. This is perhaps most notable with -// Arial Condensed. libui does not do this, even on Windows (because -// the DirectWrite API libui uses on Windows does not do this); to -// specify Arial Condensed, use family Arial and stretch -// uiTextStretchCondensed. -_UI_ENUM(uiTextStretch) { - uiTextStretchUltraCondensed, - uiTextStretchExtraCondensed, - uiTextStretchCondensed, - uiTextStretchSemiCondensed, - uiTextStretchNormal, - uiTextStretchSemiExpanded, - uiTextStretchExpanded, - uiTextStretchExtraExpanded, - uiTextStretchUltraExpanded, -}; - -// uiNewStretchAttribute() creates a new uiAttribute that changes the -// stretch of the text it is applied to. It is an error to specify a strech -// not specified in uiTextStretch. -_UI_EXTERN uiAttribute *uiNewStretchAttribute(uiTextStretch stretch); - -// uiAttributeStretch() returns the font stretch stored in a. It is an -// error to call this on a uiAttribute that does not hold a font stretch. -_UI_EXTERN uiTextStretch uiAttributeStretch(const uiAttribute *a); - -// uiNewColorAttribute() creates a new uiAttribute that changes the -// color of the text it is applied to. It is an error to specify an invalid -// color. -_UI_EXTERN uiAttribute *uiNewColorAttribute(double r, double g, double b, double a); - -// uiAttributeColor() returns the text color stored in a. It is an -// error to call this on a uiAttribute that does not hold a text color. -_UI_EXTERN void uiAttributeColor(const uiAttribute *a, double *r, double *g, double *b, double *alpha); - -// uiNewBackgroundAttribute() creates a new uiAttribute that -// changes the background color of the text it is applied to. It is an -// error to specify an invalid color. -_UI_EXTERN uiAttribute *uiNewBackgroundAttribute(double r, double g, double b, double a); - -// TODO reuse uiAttributeColor() for background colors, or make a new function... - -// uiUnderline specifies a type of underline to use on text. -_UI_ENUM(uiUnderline) { - uiUnderlineNone, - uiUnderlineSingle, - uiUnderlineDouble, - uiUnderlineSuggestion, // wavy or dotted underlines used for spelling/grammar checkers -}; - -// uiNewUnderlineAttribute() creates a new uiAttribute that changes -// the type of underline on the text it is applied to. It is an error to -// specify an underline type not specified in uiUnderline. -_UI_EXTERN uiAttribute *uiNewUnderlineAttribute(uiUnderline u); - -// uiAttributeUnderline() returns the underline type stored in a. It is -// an error to call this on a uiAttribute that does not hold an underline -// style. -_UI_EXTERN uiUnderline uiAttributeUnderline(const uiAttribute *a); - -// uiUnderlineColor specifies the color of any underline on the text it -// is applied to, regardless of the type of underline. In addition to -// being able to specify a custom color, you can explicitly specify -// platform-specific colors for suggestion underlines; to use them -// correctly, pair them with uiUnderlineSuggestion (though they can -// be used on other types of underline as well). -// -// If an underline type is applied but no underline color is -// specified, the text color is used instead. If an underline color -// is specified without an underline type, the underline color -// attribute is ignored, but not removed from the uiAttributedString. -_UI_ENUM(uiUnderlineColor) { - uiUnderlineColorCustom, - uiUnderlineColorSpelling, - uiUnderlineColorGrammar, - uiUnderlineColorAuxiliary, // for instance, the color used by smart replacements on macOS or in Microsoft Office -}; - -// uiNewUnderlineColorAttribute() creates a new uiAttribute that -// changes the color of the underline on the text it is applied to. -// It is an error to specify an underline color not specified in -// uiUnderlineColor. -// -// If the specified color type is uiUnderlineColorCustom, it is an -// error to specify an invalid color value. Otherwise, the color values -// are ignored and should be specified as zero. -_UI_EXTERN uiAttribute *uiNewUnderlineColorAttribute(uiUnderlineColor u, double r, double g, double b, double a); - -// uiAttributeUnderlineColor() returns the underline color stored in -// a. It is an error to call this on a uiAttribute that does not hold an -// underline color. -_UI_EXTERN void uiAttributeUnderline(const uiAttribute *a, uiUnderlineColor *u, double *r, double *g, double *b, double *alpha); - -// uiOpenTypeFeatures represents a set of OpenType feature -// tag-value pairs, for applying OpenType features to text. -// OpenType feature tags are four-character codes defined by -// OpenType that cover things from design features like small -// caps and swashes to language-specific glyph shapes and -// beyond. Each tag may only appear once in any given -// uiOpenTypeFeatures instance. Each value is a 32-bit integer, -// often used as a Boolean flag, but sometimes as an index to choose -// a glyph shape to use. -// -// If a font does not support a certain feature, that feature will be -// ignored. (TODO verify this on all OSs) -// -// See the OpenType specification at -// https://www.microsoft.com/typography/otspec/featuretags.htm -// for the complete list of available features, information on specific -// features, and how to use them. -// TODO invalid features -typedef struct uiOpenTypeFeatures uiOpenTypeFeatures; - -// uiOpenTypeFeaturesForEachFunc is the type of the function -// invoked by uiOpenTypeFeaturesForEach() for every OpenType -// feature in otf. Refer to that function's documentation for more -// details. -typedef uiForEach (*uiOpenTypeFeaturesForEachFunc)(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data); - -// @role uiOpenTypeFeatures constructor -// uiNewOpenTypeFeatures() returns a new uiOpenTypeFeatures -// instance, with no tags yet added. -_UI_EXTERN uiOpenTypeFeatures *uiNewOpenTypeFeatures(void); - -// @role uiOpenTypeFeatures destructor -// uiFreeOpenTypeFeatures() frees otf. -_UI_EXTERN void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf); - -// uiOpenTypeFeaturesClone() makes a copy of otf and returns it. -// Changing one will not affect the other. -_UI_EXTERN uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures *otf); - -// uiOpenTypeFeaturesAdd() adds the given feature tag and value -// to otf. The feature tag is specified by a, b, c, and d. If there is -// already a value associated with the specified tag in otf, the old -// value is removed. -_UI_EXTERN void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value); - -// uiOpenTypeFeaturesRemove() removes the given feature tag -// and value from otf. If the tag is not present in otf, -// uiOpenTypeFeaturesRemove() does nothing. -_UI_EXTERN void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d); - -// uiOpenTypeFeaturesGet() determines whether the given feature -// tag is present in otf. If it is, *value is set to the tag's value and -// nonzero is returned. Otherwise, zero is returned. -// -// Note that if uiOpenTypeFeaturesGet() returns zero, value isn't -// changed. This is important: if a feature is not present in a -// uiOpenTypeFeatures, the feature is NOT treated as if its -// value was zero anyway. Script-specific font shaping rules and -// font-specific feature settings may use a different default value -// for a feature. You should likewise not treat a missing feature as -// having a value of zero either. Instead, a missing feature should -// be treated as having some unspecified default value. -_UI_EXTERN int uiOpenTypeFeaturesGet(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value); - -// uiOpenTypeFeaturesForEach() executes f for every tag-value -// pair in otf. The enumeration order is unspecified. You cannot -// modify otf while uiOpenTypeFeaturesForEach() is running. -_UI_EXTERN void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data); - -// uiNewFeaturesAttribute() creates a new uiAttribute that changes -// the font family of the text it is applied to. otf is copied; you may -// free it after uiNewFeaturesAttribute() returns. -_UI_EXTERN uiAttribute *uiNewFeaturesAttribute(const uiOpenTypeFeatures *otf); - -// uiAttributeFeatures() returns the OpenType features stored in a. -// The returned uiOpenTypeFeatures object is owned by a. It is an -// error to call this on a uiAttribute that does not hold OpenType -// features. -_UI_EXTERN const uiOpenTypeFeatures *uiAttributeFeatures(const uiAttribute *a); - -// TODO CONTINUE HERE - -// TODO add a function to uiAttributedString to get an attribute's value at a specific index or in a specific range, so we can edit - -typedef struct uiDrawFontDescriptor uiDrawFontDescriptor; - -struct uiDrawFontDescriptor { - char *Family; - double Size; - uiDrawTextWeight Weight; - uiDrawTextItalic Italic; - uiDrawTextStretch Stretch; -}; - typedef struct uiDrawTextLayout uiDrawTextLayout; typedef struct uiDrawTextLayoutParams uiDrawTextLayoutParams; typedef struct uiDrawTextLayoutLineMetrics uiDrawTextLayoutLineMetrics; From 686458cdb78907560ed722284f71bc8aafaa638a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Feb 2018 11:01:50 -0500 Subject: [PATCH 0843/1329] Added notes stuff. The TODO.md file will eventually wind up moving there. --- _notes/caretWidths | 1 + _notes/darwinNSAlertIcons | 8 ++++++++ _notes/misc | 2 ++ _notes/windowsHighDPI | 6 ++++++ _notes/windowsPrinting | 7 +++++++ 5 files changed, 24 insertions(+) create mode 100644 _notes/caretWidths create mode 100644 _notes/darwinNSAlertIcons create mode 100644 _notes/misc create mode 100644 _notes/windowsHighDPI create mode 100644 _notes/windowsPrinting diff --git a/_notes/caretWidths b/_notes/caretWidths new file mode 100644 index 00000000..e9eef62b --- /dev/null +++ b/_notes/caretWidths @@ -0,0 +1 @@ +UWP has this (TODO check its implementation to see if it matches ours) https://docs.microsoft.com/en-us/uwp/api/windows.ui.viewmanagement.uisettings#Windows_UI_ViewManagement_UISettings_CaretWidth diff --git a/_notes/darwinNSAlertIcons b/_notes/darwinNSAlertIcons new file mode 100644 index 00000000..5e8acfed --- /dev/null +++ b/_notes/darwinNSAlertIcons @@ -0,0 +1,8 @@ +https://www.google.com/search?q=nsalert+error+icon&client=firefox-b&tbm=isch&source=iu&ictx=1&fir=2iRctS5fJByN0M%253A%252Cw324MTzjHa1bAM%252C_&usg=__x3wpwdNN1L8VI2kHtkKAXFMtpj4%3D&sa=X&ved=0ahUKEwjJzpjN2qDZAhVjw1kKHfOHDoQQ9QEIMTAB#imgrc=2iRctS5fJByN0M: +http://0xced.blogspot.com/2009/11/clalert-nsalert-done-right.html +https://gist.github.com/0xced/228140 +http://editra.org/uploads/code/artmac.html +http://mirror.informatimago.com/next/developer.apple.com/documentation/Carbon/Reference/IconServices/index.html +http://www.cocoabuilder.com/archive/cocoa/15427-iconref-to-nsimage.html +https://github.com/lukakerr/Swift-NSUserNotificationPrivate +https://stackoverflow.com/questions/32943220/the-sidebar-icon-image-name-in-osx diff --git a/_notes/misc b/_notes/misc new file mode 100644 index 00000000..72381721 --- /dev/null +++ b/_notes/misc @@ -0,0 +1,2 @@ +windows data types, "open specifications" on msdn https://msdn.microsoft.com/en-us/library/cc230321.aspx + (I usually use https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx for windows data types) diff --git a/_notes/windowsHighDPI b/_notes/windowsHighDPI new file mode 100644 index 00000000..bafa40f9 --- /dev/null +++ b/_notes/windowsHighDPI @@ -0,0 +1,6 @@ +https://msdn.microsoft.com/en-us/library/windows/desktop/mt843498(v=vs.85).aspx +https://msdn.microsoft.com/library/windows/desktop/mt791579(v=vs.85).aspx +https://blogs.windows.com/buildingapps/2017/04/04/high-dpi-scaling-improvements-desktop-applications-windows-10-creators-update/ +https://channel9.msdn.com/Events/Windows/Windows-Developer-Day-Creators-Update/High-DPI-Improvements-for-Desktop-Developers +https://social.msdn.microsoft.com/Forums/vstudio/en-US/31d2a89f-3518-403e-b1e4-bbe37ac1b0f0/per-monitor-high-dpi-awareness-wmdpichanged?forum=vcgeneral +https://stackoverflow.com/questions/36864894/scaling-the-non-client-area-title-bar-menu-bar-for-per-monitor-high-dpi-suppo/37624363 diff --git a/_notes/windowsPrinting b/_notes/windowsPrinting new file mode 100644 index 00000000..c5d0b6c7 --- /dev/null +++ b/_notes/windowsPrinting @@ -0,0 +1,7 @@ +https://msdn.microsoft.com/en-us/library/windows/desktop/ff686805(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/dn495653(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/hh448422(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/dd316975(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/dd372919(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/ee264335(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/dd145198(v=vs.85).aspx From a74923d57421a4ce192356475156ba97eb878841 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Feb 2018 11:04:02 -0500 Subject: [PATCH 0844/1329] Finally decided to add that httext file; gzipped to avoid git mangling it. --- examples/drawtext/httext.gz | Bin 0 -> 440 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 examples/drawtext/httext.gz diff --git a/examples/drawtext/httext.gz b/examples/drawtext/httext.gz new file mode 100644 index 0000000000000000000000000000000000000000..7fd947cf0ae1eb3e4094ecc5fa8b1687c098a5cf GIT binary patch literal 440 zcmV;p0Z0BHiwFQ3!h=`<17(uIiW5N)hF|3e4_u1;ItphZ!3ExP^3tO&GRahKZo)P% zD3pwqjGdPbCK0R&fgCo;Y&+^HFY&y^z(tc(3xj45LNakfnm!g5+6x-qQ$|C9cWU8a zgSQ{Xa8pD$F2*8O-80ej9tzbo^n9%^j9gOUqPfx;x#P2d(FQ4m@mpgxUE3}u+-)b4 zrJPL~;I-}dTprexkoj!sqoe(aN9$9T^Pra~ekGE;woC4f{=^MBHEfOb!Hd`FT28PW zuPI55HY7Wuh_~x!7mqaH2wPU9c?57OTur^LAXm<59ga$PSlj~Q31Oc66v70fu1mKe zOT06pE1Wb&N}g!nGH6)Q_ORVJzc4Sc9@$Z?mW_9h@%!`3<44Ra_l=MB*;uQ)%yX=c z-~RmkidkQR_-)YLpEX*s-UTI<-5h>AxH){s?DhJ^bVPIf{qpG-W|8B?ldD(%)l=Yj iIN9kT-P2s6Hv^L1pq`V?U0V;s9RC2*gcNnF0ssK*w%%m` literal 0 HcmV?d00001 From 17f149924682d1f47b027e29849e7466522b5d47 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Feb 2018 11:10:37 -0500 Subject: [PATCH 0845/1329] Some cleanup of the top-level directory. --- oldhaiku.tgz => _abort/oldhaiku.tgz | Bin _notes/windowsHighDPI | 3 +++ hidpinotes | 3 --- 3 files changed, 3 insertions(+), 3 deletions(-) rename oldhaiku.tgz => _abort/oldhaiku.tgz (100%) delete mode 100644 hidpinotes diff --git a/oldhaiku.tgz b/_abort/oldhaiku.tgz similarity index 100% rename from oldhaiku.tgz rename to _abort/oldhaiku.tgz diff --git a/_notes/windowsHighDPI b/_notes/windowsHighDPI index bafa40f9..bfec3e7c 100644 --- a/_notes/windowsHighDPI +++ b/_notes/windowsHighDPI @@ -1,3 +1,6 @@ +https://msdn.microsoft.com/en-us/library/windows/desktop/dn469266(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/mt744321(v=vs.85).aspx +!!!! http://stackoverflow.com/questions/41917279/do-child-windows-have-the-same-dpi-as-their-parents-in-a-per-monitor-aware-appli https://msdn.microsoft.com/en-us/library/windows/desktop/mt843498(v=vs.85).aspx https://msdn.microsoft.com/library/windows/desktop/mt791579(v=vs.85).aspx https://blogs.windows.com/buildingapps/2017/04/04/high-dpi-scaling-improvements-desktop-applications-windows-10-creators-update/ diff --git a/hidpinotes b/hidpinotes deleted file mode 100644 index 74b1b021..00000000 --- a/hidpinotes +++ /dev/null @@ -1,3 +0,0 @@ -https://msdn.microsoft.com/en-us/library/windows/desktop/dn469266(v=vs.85).aspx -https://msdn.microsoft.com/en-us/library/windows/desktop/mt744321(v=vs.85).aspx -!!!! http://stackoverflow.com/questions/41917279/do-child-windows-have-the-same-dpi-as-their-parents-in-a-per-monitor-aware-appli From ee87a9db2383fd9333b3bb41644c3f06c9ae21be Mon Sep 17 00:00:00 2001 From: Alexander Neumann Date: Sat, 17 Feb 2018 18:53:32 +0100 Subject: [PATCH 0846/1329] Fix enter/escape crashes on Windows (#202) * Do not let slip through IDOK and IDCANCEL as menu events, fixes #55 * Add comment about IDOK and IDCANCEL --- windows/window.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/windows/window.cpp b/windows/window.cpp index 9cf13a25..fbec558b 100644 --- a/windows/window.cpp +++ b/windows/window.cpp @@ -87,7 +87,8 @@ static LRESULT CALLBACK windowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARA // not a menu if (lParam != 0) break; - if (HIWORD(wParam) != 0) + // IDOK (1) and IDCANCEL (2) aren't menu events either + if (HIWORD(wParam) != 0 || LOWORD(wParam) <= IDCANCEL) break; runMenuEvent(LOWORD(wParam), uiWindow(w)); return 0; From 6c85f39584801c782275fb387ef360e86fb4159f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Feb 2018 12:56:47 -0500 Subject: [PATCH 0847/1329] Improved the comment in the previous commit. --- windows/window.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/windows/window.cpp b/windows/window.cpp index fbec558b..34baf545 100644 --- a/windows/window.cpp +++ b/windows/window.cpp @@ -87,7 +87,9 @@ static LRESULT CALLBACK windowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARA // not a menu if (lParam != 0) break; - // IDOK (1) and IDCANCEL (2) aren't menu events either + // IsDialogMessage() will also generate IDOK and IDCANCEL when pressing Enter and Escape (respectively) on some controls, like EDIT controls + // swallow those too; they'll cause runMenuEvent() to panic + // TODO fix the root cause somehow if (HIWORD(wParam) != 0 || LOWORD(wParam) <= IDCANCEL) break; runMenuEvent(LOWORD(wParam), uiWindow(w)); From dd54b3da93dd2a701078938288aafd77e163be56 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Feb 2018 12:59:31 -0500 Subject: [PATCH 0848/1329] And updated the README. Now to pull out a release. --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 01275c0e..deba4dcb 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,10 @@ This README is being written.
## Announcements +* **17 February 2018** + * The longstanding Enter+Escape crashes on Windows have finally been fixed (thanks to @lxn). + * **Alpha 3.5 is now here.** This is a quickie release primiarly intended to deploy the above fix to package ui itself. More new things will come in the next release, which will also introduce semver (so it will be called v0.4.0 instead). + * **27 November 2016** * Decided to split the table stuff into its own branch. It will be developed independently of everything else, along with a few other features. @@ -42,9 +46,6 @@ This README is being written.
*Note that today's entry (Eastern Time) may be updated later today.* -* ** Date: Sat, 17 Feb 2018 16:44:52 -0500 Subject: [PATCH 0849/1329] asdf. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index deba4dcb..794a2bf4 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ This README is being written.
* **17 February 2018** * The longstanding Enter+Escape crashes on Windows have finally been fixed (thanks to @lxn). - * **Alpha 3.5 is now here.** This is a quickie release primiarly intended to deploy the above fix to package ui itself. More new things will come in the next release, which will also introduce semver (so it will be called v0.4.0 instead). + * **Alpha 3.5 is now here.** This is a quickie release primiarly intended to deploy the above fix to package ui itself. **It is a partial binary release; sorry!** More new things will come in the next release, which will also introduce semver (so it will be called v0.4.0 instead). * **27 November 2016** * Decided to split the table stuff into its own branch. It will be developed independently of everything else, along with a few other features. From a245ced3dcb13a45d1107a9e75dec26b08f6d2ad Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Feb 2018 21:25:53 -0500 Subject: [PATCH 0850/1329] One last overdue README update before the package. Alpha 3.5. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 794a2bf4..d42f7e6b 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ This README is being written.
* **17 February 2018** * The longstanding Enter+Escape crashes on Windows have finally been fixed (thanks to @lxn). * **Alpha 3.5 is now here.** This is a quickie release primiarly intended to deploy the above fix to package ui itself. **It is a partial binary release; sorry!** More new things will come in the next release, which will also introduce semver (so it will be called v0.4.0 instead). + * Alpha 3.5 also includes a new control gallery example. The screenshots below have not been updated yet. * **27 November 2016** * Decided to split the table stuff into its own branch. It will be developed independently of everything else, along with a few other features. From ec8952a8e8b261e75938dad5b974da20004f78c0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 18 Feb 2018 22:02:42 -0500 Subject: [PATCH 0851/1329] More work on new_ui_attrstr.h. Almost finished with this, actually... --- checklist_attrstr | 2 ++ new_ui_attrstr.h | 78 +++++++++++++++++++++++++++++++++++++++++++++++ ui_attrstr.h | 34 ++------------------- 3 files changed, 82 insertions(+), 32 deletions(-) diff --git a/checklist_attrstr b/checklist_attrstr index e4c6bf3f..47117def 100644 --- a/checklist_attrstr +++ b/checklist_attrstr @@ -20,3 +20,5 @@ should FeaturesAttribute be changed to OpenTypeFeaturesAttribute and likewise fo should uiNewFeaturesAttribute() accept NULL should uiNewFamilyAttribute() accept NULL it is an error in ForEach too +invalid values for uiDrawTextAlign +empty text layouts have one line diff --git a/new_ui_attrstr.h b/new_ui_attrstr.h index 8772f4e0..f180ad66 100644 --- a/new_ui_attrstr.h +++ b/new_ui_attrstr.h @@ -416,3 +416,81 @@ struct uiDrawFontDescriptor { uiTextItalic Italic; uiTextStretch Stretch; }; + +// uiDrawTextLayout is a concrete representation of a +// uiAttributedString that can be displayed in a uiDrawContext. +// It includes information important for the drawing of a block of +// text, including the bounding box to wrap the text within, the +// alignment of lines of text within that box, areas to mark as +// being selected, and other things. +// +// Unlike uiAttributedString, the content of a uiDrawTextLayout is +// immutable once it has been created. +// +// TODO talk about OS-specific differences with text drawing that libui can't account for... +typedef struct uiDrawTextLayout uiDrawTextLayout; + +// uiDrawTextAlign specifies the alignment of lines of text in a +// uiDrawTextLayout. +_UI_ENUM(uiDrawTextAlign) { + uiDrawTextAlignLeft, + uiDrawTextAlignCenter, + uiDrawTextAlignRight, +}; + +// uiDrawTextLayoutParams describes a uiDrawTextLayout. +// DefaultFont is used to render any text that is not attributed +// sufficiently in String. Width determines the width of the bounding +// box of the text; the height is determined automatically. +typedef struct uiDrawTextLayoutParams uiDrawTextLayoutParams; + +// TODO const-correct this somehow +struct uiDrawTextLayoutParams { + uiAttributedString *String; + uiDrawFontDescriptor *DefaultFont; + double Width; + uiDrawTextAlign Align; +}; + +// @role uiDrawTextLayout constructor +// uiDrawNewTextLayout() creates a new uiDrawTextLayout from +// the given parameters. +// +// TODO +// - allow creating a layout out of a substring +// - allow marking compositon strings +// - allow marking selections, even after creation +// - add the following functions: +// - uiDrawTextLayoutHeightForWidth() (returns the height that a layout would need to be to display the entire string at a given width) +// - uiDrawTextLayoutRangeForSize() (returns what substring would fit in a given size) +// - uiDrawTextLayoutNewWithHeight() (limits amount of string used by the height) +// - some function to fix up a range (for text editing) +_UI_EXTERN uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *params); + +// @role uiDrawFreeTextLayout destructor +// uiDrawFreeTextLayout() frees tl. The underlying +// uiAttributedString is not freed. +_UI_EXTERN void uiDrawFreeTextLayout(uiDrawTextLayout *tl); + +// uiDrawText() draws tl in c with the top-left point of tl at (x, y). +_UI_EXTERN void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y); + +// uiDrawTextLayoutExtents() returns the width and height of tl +// in width and height. The returned width may be smaller than +// the width passed into uiDrawNewTextLayout() depending on +// how the text in tl is wrapped. Therefore, you can use this +// function to get the actual size of the text layout. +_UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height); + +// uiDrawTextLayoutNumLines() returns the number of lines in tl. +// This number will always be greater than or equal to 1; a text +// layout with no text only has one line. +_UI_EXTERN int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl); + +// uiDrawTextLayoutLineByteRange() returns the byte indices of the +// text that falls into the given line of tl as [start, end). +_UI_EXTERN void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end); + +// TODO metrics functions + +// TODO number of lines visible for clipping rect, range visible for clipping rect? diff --git a/ui_attrstr.h b/ui_attrstr.h index c4d8382b..5dfc921c 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -1,20 +1,6 @@ -typedef struct uiDrawTextLayout uiDrawTextLayout; -typedef struct uiDrawTextLayoutParams uiDrawTextLayoutParams; + typedef struct uiDrawTextLayoutLineMetrics uiDrawTextLayoutLineMetrics; -_UI_ENUM(uiDrawTextAlign) { - uiDrawTextAlignLeft, - uiDrawTextAlignCenter, - uiDrawTextAlignRight, -}; - -struct uiDrawTextLayoutParams { - uiAttributedString *String; - uiDrawFontDescriptor *DefaultFont; - double Width; - uiDrawTextAlign Align; -}; - // Height will equal ParagraphSpacingBefore + LineHeightSpace + Ascent + Descent + Leading + LineSpacing + ParagraphSpacing. // The above values are listed in vertical order, from top to bottom. // Ascent + Descent + Leading will give you the typographic bounds @@ -45,25 +31,9 @@ struct uiDrawTextLayoutLineMetrics { // TODO trailing whitespace? }; -// TODO -// - allow creating a layout out of a substring -// - allow marking compositon strings -// - allow marking selections, even after creation -// - add the following functions: -// - uiDrawTextLayoutHeightForWidth() (returns the height that a layout would need to be to display the entire string at a given width) -// - uiDrawTextLayoutRangeForSize() (returns what substring would fit in a given size) -// - uiDrawTextLayoutNewWithHeight() (limits amount of string used by the height) -// - some function to fix up a range (for text editing) -_UI_EXTERN uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *params); -_UI_EXTERN void uiDrawFreeTextLayout(uiDrawTextLayout *tl); -_UI_EXTERN void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y); -_UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height); -_UI_EXTERN int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl); -_UI_EXTERN void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end); _UI_EXTERN void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m); -// TODO number of lines visible for clipping rect, range visible for clipping rect? -// TODO rewrite all this documentation +// TODO rewrite this documentation // uiDrawTextLayoutHitTest() returns the byte offset and line closest // to the given point. The point is relative to the top-left of the layout. From 6a737ba48eb35d166d93461563a806b775e3e779 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 19 Feb 2018 01:46:16 -0500 Subject: [PATCH 0852/1329] Flipped old and new ui_attrstr.h. --- new_ui_attrstr.h | 496 ------------------------------------------ old_ui_attrstr.h | 71 ++++++ ui_attrstr.h | 549 +++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 558 insertions(+), 558 deletions(-) delete mode 100644 new_ui_attrstr.h create mode 100644 old_ui_attrstr.h diff --git a/new_ui_attrstr.h b/new_ui_attrstr.h deleted file mode 100644 index f180ad66..00000000 --- a/new_ui_attrstr.h +++ /dev/null @@ -1,496 +0,0 @@ -// uiAttribute stores information about an attribute in a -// uiAttributedString. -// -// You do not create uiAttributes directly; instead, you create a -// uiAttribute of a given type using the specialized constructor -// functions. For every Unicode codepoint in the uiAttributedString, -// at most one value of each attribute type can be applied. -// -// uiAttributes are immutable and the uiAttributedString takes -// ownership of the uiAttribute object once assigned, copying its -// contents as necessary. -typedef struct uiAttribute uiAttribute; - -// uiFreeAttribute() frees a uiAttribute. You generally do not need to -// call this yourself, as uiAttributedString does this for you. In fact, -// it is an error to call this function on a uiAttribute that has been -// given to a uiAttributedString. You can call this, however, if you -// created a uiAttribute that you aren't going to use later. -_UI_EXTERN void uiFreeAttribute(uiAttribute *a); - -// uiAttributeType holds the possible uiAttribute types that may be -// returned by uiAttributeGetType(). Refer to the documentation for -// each type's constructor function for details on each type. -_UI_ENUM(uiAttributeType) { - uiAttributeTypeFamily, - uiAttributeTypeSize, - uiAttributeTypeWeight, - uiAttributeTypeItalic, - uiAttributeTypeStretch, - uiAttributeTypeColor, - uiAttributeTypeBackground, - uiAttributeTypeUnderline, - uiAttributeTypeUnderlineColor, - uiAttributeTypeFeatures, -}; - -// uiAttributeGetType() returns the type of a. -// TODO I don't like this name -_UI_EXTERN uiAttributeType uiAttributeGetType(const uiAttribute *a); - -// uiNewFamilyAttribute() creates a new uiAttribute that changes the -// font family of the text it is applied to. family is copied; you do not -// need to keep it alive after uiNewFamilyAttribute() returns. Font -// family names are case-insensitive. -_UI_EXTERN uiAttribute *uiNewFamilyAttribute(const char *family); - -// uiAttributeFamily() returns the font family stored in a. The -// returned string is owned by a. It is an error to call this on a -// uiAttribute that does not hold a font family. -_UI_EXTERN const char *uiAttributeFamily(const uiAttribute *a); - -// uiNewSizeAttribute() creates a new uiAttribute that changes the -// size of the text it is applied to, in typographical points. -_UI_EXTERN uiAttribute *uiNewFamilyAttribute(double size); - -// uiAttributeSize() returns the font size stored in a. It is an error to -// call this on a uiAttribute that does not hold a font size. -_UI_EXTERN double uiAttributeSize(const uiAttribute *a); - -// uiTextWeight represents possible text weights. These roughly -// map to the OSx2 text weight field of TrueType and OpenType -// fonts, or to CSS weight numbers. The named constants are -// nominal values; the actual values may vary by font and by OS, -// though this isn't particularly likely. Any value between -// uiTextWeightMinimum and uiDrawTextWeightMaximum, -// inclusive, is allowed. -// -// Note that due to restrictions in early versions of Windows, some -// fonts have "special" weights be exposed in many programs as -// separate font families. This is perhaps most notable with -// Arial Black. libui does not do this, even on Windows (because the -// DirectWrite API libui uses on Windows does not do this); to -// specify Arial Black, use family Arial and weight uiTextWeightBlack. -_UI_ENUM(uiTextWeight) { - uiTextWeightMinimum = 0, - uiTextWeightThin = 100, - uiTextWeightUltraLight = 200, - uiTextWeightLight = 300, - uiTextWeightBook = 350, - uiTextWeightNormal = 400, - uiTextWeightMedium = 500, - uiTextWeightSemiBold = 600, - uiTextWeightBold = 700, - uiTextWeightUltraBold = 800, - uiTextWeightHeavy = 900, - uiTextWeightUltraHeavy = 950, - uiTextWeightMaximum = 1000, -}; - -// uiNewWeightAttribute() creates a new uiAttribute that changes the -// weight of the text it is applied to. It is an error to specify a weight -// outside the range [uiTextWeightMinimum, -// uiTextWeightMaximum]. -_UI_EXTERN uiAttribute *uiNewWeightAttribute(uiTextWeight weight); - -// uiAttributeWeight() returns the font weight stored in a. It is an error -// to call this on a uiAttribute that does not hold a font weight. -_UI_EXTERN uiTextWeight uiAttributeWeight(const uiAttribute *a); - -// uiTextItalic represents possible italic modes for a font. Italic -// represents "true" italics where the slanted glyphs have custom -// shapes, whereas oblique represents italics that are merely slanted -// versions of the normal glyphs. Most fonts usually have one or the -// other. -_UI_ENUM(uiTextItalic) { - uiTextItalicNormal, - uiTextItalicOblique, - uiTextItalicItalic, -}; - -// uiNewItalicAttribute() creates a new uiAttribute that changes the -// italic mode of the text it is applied to. It is an error to specify an -// italic mode not specified in uiTextItalic. -_UI_EXTERN uiAttribute *uiNewItalicAttribute(uiTextItalic italic); - -// uiAttributeItalic() returns the font italic mode stored in a. It is an -// error to call this on a uiAttribute that does not hold a font italic -// mode. -_UI_EXTERN uiTextItalic uiAttributeItalic(const uiAttribute *a); - -// uiTextStretch represents possible stretches (also called "widths") -// of a font. -// -// Note that due to restrictions in early versions of Windows, some -// fonts have "special" stretches be exposed in many programs as -// separate font families. This is perhaps most notable with -// Arial Condensed. libui does not do this, even on Windows (because -// the DirectWrite API libui uses on Windows does not do this); to -// specify Arial Condensed, use family Arial and stretch -// uiTextStretchCondensed. -_UI_ENUM(uiTextStretch) { - uiTextStretchUltraCondensed, - uiTextStretchExtraCondensed, - uiTextStretchCondensed, - uiTextStretchSemiCondensed, - uiTextStretchNormal, - uiTextStretchSemiExpanded, - uiTextStretchExpanded, - uiTextStretchExtraExpanded, - uiTextStretchUltraExpanded, -}; - -// uiNewStretchAttribute() creates a new uiAttribute that changes the -// stretch of the text it is applied to. It is an error to specify a strech -// not specified in uiTextStretch. -_UI_EXTERN uiAttribute *uiNewStretchAttribute(uiTextStretch stretch); - -// uiAttributeStretch() returns the font stretch stored in a. It is an -// error to call this on a uiAttribute that does not hold a font stretch. -_UI_EXTERN uiTextStretch uiAttributeStretch(const uiAttribute *a); - -// uiNewColorAttribute() creates a new uiAttribute that changes the -// color of the text it is applied to. It is an error to specify an invalid -// color. -_UI_EXTERN uiAttribute *uiNewColorAttribute(double r, double g, double b, double a); - -// uiAttributeColor() returns the text color stored in a. It is an -// error to call this on a uiAttribute that does not hold a text color. -_UI_EXTERN void uiAttributeColor(const uiAttribute *a, double *r, double *g, double *b, double *alpha); - -// uiNewBackgroundAttribute() creates a new uiAttribute that -// changes the background color of the text it is applied to. It is an -// error to specify an invalid color. -_UI_EXTERN uiAttribute *uiNewBackgroundAttribute(double r, double g, double b, double a); - -// TODO reuse uiAttributeColor() for background colors, or make a new function... - -// uiUnderline specifies a type of underline to use on text. -_UI_ENUM(uiUnderline) { - uiUnderlineNone, - uiUnderlineSingle, - uiUnderlineDouble, - uiUnderlineSuggestion, // wavy or dotted underlines used for spelling/grammar checkers -}; - -// uiNewUnderlineAttribute() creates a new uiAttribute that changes -// the type of underline on the text it is applied to. It is an error to -// specify an underline type not specified in uiUnderline. -_UI_EXTERN uiAttribute *uiNewUnderlineAttribute(uiUnderline u); - -// uiAttributeUnderline() returns the underline type stored in a. It is -// an error to call this on a uiAttribute that does not hold an underline -// style. -_UI_EXTERN uiUnderline uiAttributeUnderline(const uiAttribute *a); - -// uiUnderlineColor specifies the color of any underline on the text it -// is applied to, regardless of the type of underline. In addition to -// being able to specify a custom color, you can explicitly specify -// platform-specific colors for suggestion underlines; to use them -// correctly, pair them with uiUnderlineSuggestion (though they can -// be used on other types of underline as well). -// -// If an underline type is applied but no underline color is -// specified, the text color is used instead. If an underline color -// is specified without an underline type, the underline color -// attribute is ignored, but not removed from the uiAttributedString. -_UI_ENUM(uiUnderlineColor) { - uiUnderlineColorCustom, - uiUnderlineColorSpelling, - uiUnderlineColorGrammar, - uiUnderlineColorAuxiliary, // for instance, the color used by smart replacements on macOS or in Microsoft Office -}; - -// uiNewUnderlineColorAttribute() creates a new uiAttribute that -// changes the color of the underline on the text it is applied to. -// It is an error to specify an underline color not specified in -// uiUnderlineColor. -// -// If the specified color type is uiUnderlineColorCustom, it is an -// error to specify an invalid color value. Otherwise, the color values -// are ignored and should be specified as zero. -_UI_EXTERN uiAttribute *uiNewUnderlineColorAttribute(uiUnderlineColor u, double r, double g, double b, double a); - -// uiAttributeUnderlineColor() returns the underline color stored in -// a. It is an error to call this on a uiAttribute that does not hold an -// underline color. -_UI_EXTERN void uiAttributeUnderline(const uiAttribute *a, uiUnderlineColor *u, double *r, double *g, double *b, double *alpha); - -// uiOpenTypeFeatures represents a set of OpenType feature -// tag-value pairs, for applying OpenType features to text. -// OpenType feature tags are four-character codes defined by -// OpenType that cover things from design features like small -// caps and swashes to language-specific glyph shapes and -// beyond. Each tag may only appear once in any given -// uiOpenTypeFeatures instance. Each value is a 32-bit integer, -// often used as a Boolean flag, but sometimes as an index to choose -// a glyph shape to use. -// -// If a font does not support a certain feature, that feature will be -// ignored. (TODO verify this on all OSs) -// -// See the OpenType specification at -// https://www.microsoft.com/typography/otspec/featuretags.htm -// for the complete list of available features, information on specific -// features, and how to use them. -// TODO invalid features -typedef struct uiOpenTypeFeatures uiOpenTypeFeatures; - -// uiOpenTypeFeaturesForEachFunc is the type of the function -// invoked by uiOpenTypeFeaturesForEach() for every OpenType -// feature in otf. Refer to that function's documentation for more -// details. -typedef uiForEach (*uiOpenTypeFeaturesForEachFunc)(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data); - -// @role uiOpenTypeFeatures constructor -// uiNewOpenTypeFeatures() returns a new uiOpenTypeFeatures -// instance, with no tags yet added. -_UI_EXTERN uiOpenTypeFeatures *uiNewOpenTypeFeatures(void); - -// @role uiOpenTypeFeatures destructor -// uiFreeOpenTypeFeatures() frees otf. -_UI_EXTERN void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf); - -// uiOpenTypeFeaturesClone() makes a copy of otf and returns it. -// Changing one will not affect the other. -_UI_EXTERN uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures *otf); - -// uiOpenTypeFeaturesAdd() adds the given feature tag and value -// to otf. The feature tag is specified by a, b, c, and d. If there is -// already a value associated with the specified tag in otf, the old -// value is removed. -_UI_EXTERN void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value); - -// uiOpenTypeFeaturesRemove() removes the given feature tag -// and value from otf. If the tag is not present in otf, -// uiOpenTypeFeaturesRemove() does nothing. -_UI_EXTERN void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d); - -// uiOpenTypeFeaturesGet() determines whether the given feature -// tag is present in otf. If it is, *value is set to the tag's value and -// nonzero is returned. Otherwise, zero is returned. -// -// Note that if uiOpenTypeFeaturesGet() returns zero, value isn't -// changed. This is important: if a feature is not present in a -// uiOpenTypeFeatures, the feature is NOT treated as if its -// value was zero anyway. Script-specific font shaping rules and -// font-specific feature settings may use a different default value -// for a feature. You should likewise not treat a missing feature as -// having a value of zero either. Instead, a missing feature should -// be treated as having some unspecified default value. -_UI_EXTERN int uiOpenTypeFeaturesGet(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value); - -// uiOpenTypeFeaturesForEach() executes f for every tag-value -// pair in otf. The enumeration order is unspecified. You cannot -// modify otf while uiOpenTypeFeaturesForEach() is running. -_UI_EXTERN void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data); - -// uiNewFeaturesAttribute() creates a new uiAttribute that changes -// the font family of the text it is applied to. otf is copied; you may -// free it after uiNewFeaturesAttribute() returns. -_UI_EXTERN uiAttribute *uiNewFeaturesAttribute(const uiOpenTypeFeatures *otf); - -// uiAttributeFeatures() returns the OpenType features stored in a. -// The returned uiOpenTypeFeatures object is owned by a. It is an -// error to call this on a uiAttribute that does not hold OpenType -// features. -_UI_EXTERN const uiOpenTypeFeatures *uiAttributeFeatures(const uiAttribute *a); - -// uiAttributedString represents a string of UTF-8 text that can -// optionally be embellished with formatting attributes. libui -// provides the list of formatting attributes, which cover common -// formatting traits like boldface and color as well as advanced -// typographical features provided by OpenType like superscripts -// and small caps. These attributes can be combined in a variety of -// ways. -// -// Attributes are applied to runs of Unicode codepoints in the string. -// Zero-length runs are elided. Consecutive runs that have the same -// attribute type and value are merged. Each attribute is independent -// of each other attribute; overlapping attributes of different types -// do not split each other apart, but different values of the same -// attribute type do. -// -// The empty string can also be represented by uiAttributedString, -// but because of the no-zero-length-attribute rule, it will not have -// attributes. -// -// A uiAttributedString takes ownership of all attributes given to -// it, as it may need to duplicate or delete uiAttribute objects at -// any time. By extension, when you free a uiAttributedString, -// all uiAttributes within will also be freed. Each method will -// describe its own rules in more details. -// -// In addition, uiAttributedString provides facilities for moving -// between grapheme clusters, which represent a character -// from the point of view of the end user. The cursor of a text editor -// is always placed on a grapheme boundary, so you can use these -// features to move the cursor left or right by one "character". -// TODO does uiAttributedString itself need this -// -// uiAttributedString does not provide enough information to be able -// to draw itself onto a uiDrawContext or respond to user actions. -// In order to do that, you'll need to use a uiDrawTextLayout, which -// is built from the combination of a uiAttributedString and a set of -// layout-specific properties. -typedef struct uiAttributedString uiAttributedString; - -// uiAttributedStringForEachAttributeFunc is the type of the function -// invoked by uiAttributedStringForEachAttribute() for every -// attribute in s. Refer to that function's documentation for more -// details. -typedef uiForEach (*uiAttributedStringForEachAttributeFunc)(const uiAttributedString *s, const uiAttribute *a, size_t start, size_t end, void *data); - -// @role uiAttributedString constructor -// uiNewAttributedString() creates a new uiAttributedString from -// initialString. The string will be entirely unattributed. -_UI_EXTERN uiAttributedString *uiNewAttributedString(const char *initialString); - -// @role uiAttributedString destructor -// uiFreeAttributedString() destroys the uiAttributedString s. -// It will also free all uiAttributes within. -_UI_EXTERN void uiFreeAttributedString(uiAttributedString *s); - -// uiAttributedStringString() returns the textual content of s as a -// '\0'-terminated UTF-8 string. The returned pointer is valid until -// the next change to the textual content of s. -_UI_EXTERN const char *uiAttributedStringString(const uiAttributedString *s); - -// uiAttributedStringLength() returns the number of UTF-8 bytes in -// the textual content of s, excluding the terminating '\0'. -_UI_EXTERN size_t uiAttributedStringLen(const uiAttributedString *s); - -// uiAttributedStringAppendUnattributed() adds the '\0'-terminated -// UTF-8 string str to the end of s. The new substring will be -// unattributed. -_UI_EXTERN void uiAttributedStringAppendUnattributed(uiAttributedString *s, const char *str); - -// uiAttributedStringInsertAtUnattributed() adds the '\0'-terminated -// UTF-8 string str to s at the byte position specified by at. The new -// substring will be unattributed; existing attributes will be moved -// along with their text. -_UI_EXTERN void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *str, size_t at); - -// TODO add the Append and InsertAtExtendingAttributes functions -// TODO and add functions that take a string + length - -// uiAttributedStringDelete() deletes the characters and attributes of -// s in the byte range [start, end). -_UI_EXTERN void uiAttributedStringDelete(uiAttributedString *s, size_t start, size_t end); - -// TODO add a function to uiAttributedString to get an attribute's value at a specific index or in a specific range, so we can edit - -// uiAttributedStringSetAttribute() sets a in the byte range [start, end) -// of s. Any existing attributes in that byte range of the same type are -// removed. s takes ownership of a; you should not use it after -// uiAttributedStringSetAttribute() returns. -_UI_EXTERN void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttribute *a, size_t start, size_t end); - -// uiAttributedStringForEachAttribute() enumerates all the -// uiAttributes in s. It is an error to modify s in f. Within f, s still -// owns the attribute; you can neither free it nor save it for later -// use. -// TODO reword the above for consistency -_UI_EXTERN void uiAttributedStringForEachAttribute(const uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); - -// TODO const correct this somehow (the implementation needs to mutate the structure) -_UI_EXTERN size_t uiAttributedStringNumGraphemes(uiAttributedString *s); - -// TODO const correct this somehow (the implementation needs to mutate the structure) -_UI_EXTERN size_t uiAttributedStringByteIndexToGrapheme(uiAttributedString *s, size_t pos); - -// TODO const correct this somehow (the implementation needs to mutate the structure) -_UI_EXTERN size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos); - -// uiFontDescriptor provides a complete description of a font where -// one is needed. Currently, this means as the default font of a -// uiDrawTextLayout and as the data returned by uiFontButton. -// All the members operate like the respective uiAttributes. -typedef struct uiFontDescriptor uiFontDescriptor; - -struct uiDrawFontDescriptor { - // TODO const-correct this or figure out how to deal with this when getting a value - char *Family; - double Size; - uiTextWeight Weight; - uiTextItalic Italic; - uiTextStretch Stretch; -}; - -// uiDrawTextLayout is a concrete representation of a -// uiAttributedString that can be displayed in a uiDrawContext. -// It includes information important for the drawing of a block of -// text, including the bounding box to wrap the text within, the -// alignment of lines of text within that box, areas to mark as -// being selected, and other things. -// -// Unlike uiAttributedString, the content of a uiDrawTextLayout is -// immutable once it has been created. -// -// TODO talk about OS-specific differences with text drawing that libui can't account for... -typedef struct uiDrawTextLayout uiDrawTextLayout; - -// uiDrawTextAlign specifies the alignment of lines of text in a -// uiDrawTextLayout. -_UI_ENUM(uiDrawTextAlign) { - uiDrawTextAlignLeft, - uiDrawTextAlignCenter, - uiDrawTextAlignRight, -}; - -// uiDrawTextLayoutParams describes a uiDrawTextLayout. -// DefaultFont is used to render any text that is not attributed -// sufficiently in String. Width determines the width of the bounding -// box of the text; the height is determined automatically. -typedef struct uiDrawTextLayoutParams uiDrawTextLayoutParams; - -// TODO const-correct this somehow -struct uiDrawTextLayoutParams { - uiAttributedString *String; - uiDrawFontDescriptor *DefaultFont; - double Width; - uiDrawTextAlign Align; -}; - -// @role uiDrawTextLayout constructor -// uiDrawNewTextLayout() creates a new uiDrawTextLayout from -// the given parameters. -// -// TODO -// - allow creating a layout out of a substring -// - allow marking compositon strings -// - allow marking selections, even after creation -// - add the following functions: -// - uiDrawTextLayoutHeightForWidth() (returns the height that a layout would need to be to display the entire string at a given width) -// - uiDrawTextLayoutRangeForSize() (returns what substring would fit in a given size) -// - uiDrawTextLayoutNewWithHeight() (limits amount of string used by the height) -// - some function to fix up a range (for text editing) -_UI_EXTERN uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *params); - -// @role uiDrawFreeTextLayout destructor -// uiDrawFreeTextLayout() frees tl. The underlying -// uiAttributedString is not freed. -_UI_EXTERN void uiDrawFreeTextLayout(uiDrawTextLayout *tl); - -// uiDrawText() draws tl in c with the top-left point of tl at (x, y). -_UI_EXTERN void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y); - -// uiDrawTextLayoutExtents() returns the width and height of tl -// in width and height. The returned width may be smaller than -// the width passed into uiDrawNewTextLayout() depending on -// how the text in tl is wrapped. Therefore, you can use this -// function to get the actual size of the text layout. -_UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height); - -// uiDrawTextLayoutNumLines() returns the number of lines in tl. -// This number will always be greater than or equal to 1; a text -// layout with no text only has one line. -_UI_EXTERN int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl); - -// uiDrawTextLayoutLineByteRange() returns the byte indices of the -// text that falls into the given line of tl as [start, end). -_UI_EXTERN void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end); - -// TODO metrics functions - -// TODO number of lines visible for clipping rect, range visible for clipping rect? diff --git a/old_ui_attrstr.h b/old_ui_attrstr.h new file mode 100644 index 00000000..5dfc921c --- /dev/null +++ b/old_ui_attrstr.h @@ -0,0 +1,71 @@ + +typedef struct uiDrawTextLayoutLineMetrics uiDrawTextLayoutLineMetrics; + +// Height will equal ParagraphSpacingBefore + LineHeightSpace + Ascent + Descent + Leading + LineSpacing + ParagraphSpacing. +// The above values are listed in vertical order, from top to bottom. +// Ascent + Descent + Leading will give you the typographic bounds +// of the text. BaselineY is the boundary between Ascent and Descent. +// X, Y, and BaselineY are all in the layout's coordinate system, so the +// start point of the baseline will be at (X, BaselineY). All values are +// nonnegative. +struct uiDrawTextLayoutLineMetrics { + // This describes the overall bounding box of the line. + double X; + double Y; + double Width; + double Height; + + // This describes the typographic bounds of the line. + double BaselineY; + double Ascent; + double Descent; + double Leading; + + // This describes any additional whitespace. + // TODO come up with better names for these. + double ParagraphSpacingBefore; + double LineHeightSpace; + double LineSpacing; + double ParagraphSpacing; + + // TODO trailing whitespace? +}; + +_UI_EXTERN void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m); + +// TODO rewrite this documentation + +// uiDrawTextLayoutHitTest() returns the byte offset and line closest +// to the given point. The point is relative to the top-left of the layout. +// If the point is outside the layout itself, the closest point is chosen; +// this allows the function to be used for cursor positioning with the +// mouse. Do keep the returned line in mind if used in this way; the +// user might click on the end of a line, at which point the cursor +// might be at the trailing edge of the last grapheme on the line +// (subject to the operating system's APIs). +_UI_EXTERN void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *pos, int *line); + +// uiDrawTextLayoutByteLocationInLine() returns the point offset +// into the given line that the given byte position stands. This is +// relative to the line's X position (as returned by +// uiDrawTextLayoutLineGetMetrics()), which in turn is relative to +// the top-left of the layout. This function can be used for cursor +// positioning: if start and end are the start and end of the line +// (as returned by uiDrawTextLayoutLineByteRange()), you will get +// the correct offset, even if pos is at the end of the line. If pos is not +// in the range [start, end], a negative value will be returned, +// indicating you need to move the cursor to another line. +// TODO make sure this works right for right-aligned and center-aligned lines and justified lines and RTL text +_UI_EXTERN double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line); + +_UI_EXTERN void uiDrawCaret(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t pos, int *line); +// TODO allow blinking +// TODO allow secondary carets + +typedef struct uiFontButton uiFontButton; +#define uiFontButton(this) ((uiFontButton *) (this)) +// TODO have a function that sets an entire font descriptor to a range in a uiAttributedString at once, for SetFont? +_UI_EXTERN void uiFontButtonFont(uiFontButton *b, uiDrawFontDescriptor *desc); +// TOOD SetFont, mechanics +_UI_EXTERN void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data); +_UI_EXTERN uiFontButton *uiNewFontButton(void); diff --git a/ui_attrstr.h b/ui_attrstr.h index 5dfc921c..f180ad66 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -1,71 +1,496 @@ +// uiAttribute stores information about an attribute in a +// uiAttributedString. +// +// You do not create uiAttributes directly; instead, you create a +// uiAttribute of a given type using the specialized constructor +// functions. For every Unicode codepoint in the uiAttributedString, +// at most one value of each attribute type can be applied. +// +// uiAttributes are immutable and the uiAttributedString takes +// ownership of the uiAttribute object once assigned, copying its +// contents as necessary. +typedef struct uiAttribute uiAttribute; -typedef struct uiDrawTextLayoutLineMetrics uiDrawTextLayoutLineMetrics; +// uiFreeAttribute() frees a uiAttribute. You generally do not need to +// call this yourself, as uiAttributedString does this for you. In fact, +// it is an error to call this function on a uiAttribute that has been +// given to a uiAttributedString. You can call this, however, if you +// created a uiAttribute that you aren't going to use later. +_UI_EXTERN void uiFreeAttribute(uiAttribute *a); -// Height will equal ParagraphSpacingBefore + LineHeightSpace + Ascent + Descent + Leading + LineSpacing + ParagraphSpacing. -// The above values are listed in vertical order, from top to bottom. -// Ascent + Descent + Leading will give you the typographic bounds -// of the text. BaselineY is the boundary between Ascent and Descent. -// X, Y, and BaselineY are all in the layout's coordinate system, so the -// start point of the baseline will be at (X, BaselineY). All values are -// nonnegative. -struct uiDrawTextLayoutLineMetrics { - // This describes the overall bounding box of the line. - double X; - double Y; - double Width; - double Height; - - // This describes the typographic bounds of the line. - double BaselineY; - double Ascent; - double Descent; - double Leading; - - // This describes any additional whitespace. - // TODO come up with better names for these. - double ParagraphSpacingBefore; - double LineHeightSpace; - double LineSpacing; - double ParagraphSpacing; - - // TODO trailing whitespace? +// uiAttributeType holds the possible uiAttribute types that may be +// returned by uiAttributeGetType(). Refer to the documentation for +// each type's constructor function for details on each type. +_UI_ENUM(uiAttributeType) { + uiAttributeTypeFamily, + uiAttributeTypeSize, + uiAttributeTypeWeight, + uiAttributeTypeItalic, + uiAttributeTypeStretch, + uiAttributeTypeColor, + uiAttributeTypeBackground, + uiAttributeTypeUnderline, + uiAttributeTypeUnderlineColor, + uiAttributeTypeFeatures, }; -_UI_EXTERN void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m); +// uiAttributeGetType() returns the type of a. +// TODO I don't like this name +_UI_EXTERN uiAttributeType uiAttributeGetType(const uiAttribute *a); -// TODO rewrite this documentation +// uiNewFamilyAttribute() creates a new uiAttribute that changes the +// font family of the text it is applied to. family is copied; you do not +// need to keep it alive after uiNewFamilyAttribute() returns. Font +// family names are case-insensitive. +_UI_EXTERN uiAttribute *uiNewFamilyAttribute(const char *family); -// uiDrawTextLayoutHitTest() returns the byte offset and line closest -// to the given point. The point is relative to the top-left of the layout. -// If the point is outside the layout itself, the closest point is chosen; -// this allows the function to be used for cursor positioning with the -// mouse. Do keep the returned line in mind if used in this way; the -// user might click on the end of a line, at which point the cursor -// might be at the trailing edge of the last grapheme on the line -// (subject to the operating system's APIs). -_UI_EXTERN void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *pos, int *line); +// uiAttributeFamily() returns the font family stored in a. The +// returned string is owned by a. It is an error to call this on a +// uiAttribute that does not hold a font family. +_UI_EXTERN const char *uiAttributeFamily(const uiAttribute *a); -// uiDrawTextLayoutByteLocationInLine() returns the point offset -// into the given line that the given byte position stands. This is -// relative to the line's X position (as returned by -// uiDrawTextLayoutLineGetMetrics()), which in turn is relative to -// the top-left of the layout. This function can be used for cursor -// positioning: if start and end are the start and end of the line -// (as returned by uiDrawTextLayoutLineByteRange()), you will get -// the correct offset, even if pos is at the end of the line. If pos is not -// in the range [start, end], a negative value will be returned, -// indicating you need to move the cursor to another line. -// TODO make sure this works right for right-aligned and center-aligned lines and justified lines and RTL text -_UI_EXTERN double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line); +// uiNewSizeAttribute() creates a new uiAttribute that changes the +// size of the text it is applied to, in typographical points. +_UI_EXTERN uiAttribute *uiNewFamilyAttribute(double size); -_UI_EXTERN void uiDrawCaret(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t pos, int *line); -// TODO allow blinking -// TODO allow secondary carets +// uiAttributeSize() returns the font size stored in a. It is an error to +// call this on a uiAttribute that does not hold a font size. +_UI_EXTERN double uiAttributeSize(const uiAttribute *a); -typedef struct uiFontButton uiFontButton; -#define uiFontButton(this) ((uiFontButton *) (this)) -// TODO have a function that sets an entire font descriptor to a range in a uiAttributedString at once, for SetFont? -_UI_EXTERN void uiFontButtonFont(uiFontButton *b, uiDrawFontDescriptor *desc); -// TOOD SetFont, mechanics -_UI_EXTERN void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data); -_UI_EXTERN uiFontButton *uiNewFontButton(void); +// uiTextWeight represents possible text weights. These roughly +// map to the OSx2 text weight field of TrueType and OpenType +// fonts, or to CSS weight numbers. The named constants are +// nominal values; the actual values may vary by font and by OS, +// though this isn't particularly likely. Any value between +// uiTextWeightMinimum and uiDrawTextWeightMaximum, +// inclusive, is allowed. +// +// Note that due to restrictions in early versions of Windows, some +// fonts have "special" weights be exposed in many programs as +// separate font families. This is perhaps most notable with +// Arial Black. libui does not do this, even on Windows (because the +// DirectWrite API libui uses on Windows does not do this); to +// specify Arial Black, use family Arial and weight uiTextWeightBlack. +_UI_ENUM(uiTextWeight) { + uiTextWeightMinimum = 0, + uiTextWeightThin = 100, + uiTextWeightUltraLight = 200, + uiTextWeightLight = 300, + uiTextWeightBook = 350, + uiTextWeightNormal = 400, + uiTextWeightMedium = 500, + uiTextWeightSemiBold = 600, + uiTextWeightBold = 700, + uiTextWeightUltraBold = 800, + uiTextWeightHeavy = 900, + uiTextWeightUltraHeavy = 950, + uiTextWeightMaximum = 1000, +}; + +// uiNewWeightAttribute() creates a new uiAttribute that changes the +// weight of the text it is applied to. It is an error to specify a weight +// outside the range [uiTextWeightMinimum, +// uiTextWeightMaximum]. +_UI_EXTERN uiAttribute *uiNewWeightAttribute(uiTextWeight weight); + +// uiAttributeWeight() returns the font weight stored in a. It is an error +// to call this on a uiAttribute that does not hold a font weight. +_UI_EXTERN uiTextWeight uiAttributeWeight(const uiAttribute *a); + +// uiTextItalic represents possible italic modes for a font. Italic +// represents "true" italics where the slanted glyphs have custom +// shapes, whereas oblique represents italics that are merely slanted +// versions of the normal glyphs. Most fonts usually have one or the +// other. +_UI_ENUM(uiTextItalic) { + uiTextItalicNormal, + uiTextItalicOblique, + uiTextItalicItalic, +}; + +// uiNewItalicAttribute() creates a new uiAttribute that changes the +// italic mode of the text it is applied to. It is an error to specify an +// italic mode not specified in uiTextItalic. +_UI_EXTERN uiAttribute *uiNewItalicAttribute(uiTextItalic italic); + +// uiAttributeItalic() returns the font italic mode stored in a. It is an +// error to call this on a uiAttribute that does not hold a font italic +// mode. +_UI_EXTERN uiTextItalic uiAttributeItalic(const uiAttribute *a); + +// uiTextStretch represents possible stretches (also called "widths") +// of a font. +// +// Note that due to restrictions in early versions of Windows, some +// fonts have "special" stretches be exposed in many programs as +// separate font families. This is perhaps most notable with +// Arial Condensed. libui does not do this, even on Windows (because +// the DirectWrite API libui uses on Windows does not do this); to +// specify Arial Condensed, use family Arial and stretch +// uiTextStretchCondensed. +_UI_ENUM(uiTextStretch) { + uiTextStretchUltraCondensed, + uiTextStretchExtraCondensed, + uiTextStretchCondensed, + uiTextStretchSemiCondensed, + uiTextStretchNormal, + uiTextStretchSemiExpanded, + uiTextStretchExpanded, + uiTextStretchExtraExpanded, + uiTextStretchUltraExpanded, +}; + +// uiNewStretchAttribute() creates a new uiAttribute that changes the +// stretch of the text it is applied to. It is an error to specify a strech +// not specified in uiTextStretch. +_UI_EXTERN uiAttribute *uiNewStretchAttribute(uiTextStretch stretch); + +// uiAttributeStretch() returns the font stretch stored in a. It is an +// error to call this on a uiAttribute that does not hold a font stretch. +_UI_EXTERN uiTextStretch uiAttributeStretch(const uiAttribute *a); + +// uiNewColorAttribute() creates a new uiAttribute that changes the +// color of the text it is applied to. It is an error to specify an invalid +// color. +_UI_EXTERN uiAttribute *uiNewColorAttribute(double r, double g, double b, double a); + +// uiAttributeColor() returns the text color stored in a. It is an +// error to call this on a uiAttribute that does not hold a text color. +_UI_EXTERN void uiAttributeColor(const uiAttribute *a, double *r, double *g, double *b, double *alpha); + +// uiNewBackgroundAttribute() creates a new uiAttribute that +// changes the background color of the text it is applied to. It is an +// error to specify an invalid color. +_UI_EXTERN uiAttribute *uiNewBackgroundAttribute(double r, double g, double b, double a); + +// TODO reuse uiAttributeColor() for background colors, or make a new function... + +// uiUnderline specifies a type of underline to use on text. +_UI_ENUM(uiUnderline) { + uiUnderlineNone, + uiUnderlineSingle, + uiUnderlineDouble, + uiUnderlineSuggestion, // wavy or dotted underlines used for spelling/grammar checkers +}; + +// uiNewUnderlineAttribute() creates a new uiAttribute that changes +// the type of underline on the text it is applied to. It is an error to +// specify an underline type not specified in uiUnderline. +_UI_EXTERN uiAttribute *uiNewUnderlineAttribute(uiUnderline u); + +// uiAttributeUnderline() returns the underline type stored in a. It is +// an error to call this on a uiAttribute that does not hold an underline +// style. +_UI_EXTERN uiUnderline uiAttributeUnderline(const uiAttribute *a); + +// uiUnderlineColor specifies the color of any underline on the text it +// is applied to, regardless of the type of underline. In addition to +// being able to specify a custom color, you can explicitly specify +// platform-specific colors for suggestion underlines; to use them +// correctly, pair them with uiUnderlineSuggestion (though they can +// be used on other types of underline as well). +// +// If an underline type is applied but no underline color is +// specified, the text color is used instead. If an underline color +// is specified without an underline type, the underline color +// attribute is ignored, but not removed from the uiAttributedString. +_UI_ENUM(uiUnderlineColor) { + uiUnderlineColorCustom, + uiUnderlineColorSpelling, + uiUnderlineColorGrammar, + uiUnderlineColorAuxiliary, // for instance, the color used by smart replacements on macOS or in Microsoft Office +}; + +// uiNewUnderlineColorAttribute() creates a new uiAttribute that +// changes the color of the underline on the text it is applied to. +// It is an error to specify an underline color not specified in +// uiUnderlineColor. +// +// If the specified color type is uiUnderlineColorCustom, it is an +// error to specify an invalid color value. Otherwise, the color values +// are ignored and should be specified as zero. +_UI_EXTERN uiAttribute *uiNewUnderlineColorAttribute(uiUnderlineColor u, double r, double g, double b, double a); + +// uiAttributeUnderlineColor() returns the underline color stored in +// a. It is an error to call this on a uiAttribute that does not hold an +// underline color. +_UI_EXTERN void uiAttributeUnderline(const uiAttribute *a, uiUnderlineColor *u, double *r, double *g, double *b, double *alpha); + +// uiOpenTypeFeatures represents a set of OpenType feature +// tag-value pairs, for applying OpenType features to text. +// OpenType feature tags are four-character codes defined by +// OpenType that cover things from design features like small +// caps and swashes to language-specific glyph shapes and +// beyond. Each tag may only appear once in any given +// uiOpenTypeFeatures instance. Each value is a 32-bit integer, +// often used as a Boolean flag, but sometimes as an index to choose +// a glyph shape to use. +// +// If a font does not support a certain feature, that feature will be +// ignored. (TODO verify this on all OSs) +// +// See the OpenType specification at +// https://www.microsoft.com/typography/otspec/featuretags.htm +// for the complete list of available features, information on specific +// features, and how to use them. +// TODO invalid features +typedef struct uiOpenTypeFeatures uiOpenTypeFeatures; + +// uiOpenTypeFeaturesForEachFunc is the type of the function +// invoked by uiOpenTypeFeaturesForEach() for every OpenType +// feature in otf. Refer to that function's documentation for more +// details. +typedef uiForEach (*uiOpenTypeFeaturesForEachFunc)(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data); + +// @role uiOpenTypeFeatures constructor +// uiNewOpenTypeFeatures() returns a new uiOpenTypeFeatures +// instance, with no tags yet added. +_UI_EXTERN uiOpenTypeFeatures *uiNewOpenTypeFeatures(void); + +// @role uiOpenTypeFeatures destructor +// uiFreeOpenTypeFeatures() frees otf. +_UI_EXTERN void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf); + +// uiOpenTypeFeaturesClone() makes a copy of otf and returns it. +// Changing one will not affect the other. +_UI_EXTERN uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures *otf); + +// uiOpenTypeFeaturesAdd() adds the given feature tag and value +// to otf. The feature tag is specified by a, b, c, and d. If there is +// already a value associated with the specified tag in otf, the old +// value is removed. +_UI_EXTERN void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value); + +// uiOpenTypeFeaturesRemove() removes the given feature tag +// and value from otf. If the tag is not present in otf, +// uiOpenTypeFeaturesRemove() does nothing. +_UI_EXTERN void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d); + +// uiOpenTypeFeaturesGet() determines whether the given feature +// tag is present in otf. If it is, *value is set to the tag's value and +// nonzero is returned. Otherwise, zero is returned. +// +// Note that if uiOpenTypeFeaturesGet() returns zero, value isn't +// changed. This is important: if a feature is not present in a +// uiOpenTypeFeatures, the feature is NOT treated as if its +// value was zero anyway. Script-specific font shaping rules and +// font-specific feature settings may use a different default value +// for a feature. You should likewise not treat a missing feature as +// having a value of zero either. Instead, a missing feature should +// be treated as having some unspecified default value. +_UI_EXTERN int uiOpenTypeFeaturesGet(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value); + +// uiOpenTypeFeaturesForEach() executes f for every tag-value +// pair in otf. The enumeration order is unspecified. You cannot +// modify otf while uiOpenTypeFeaturesForEach() is running. +_UI_EXTERN void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data); + +// uiNewFeaturesAttribute() creates a new uiAttribute that changes +// the font family of the text it is applied to. otf is copied; you may +// free it after uiNewFeaturesAttribute() returns. +_UI_EXTERN uiAttribute *uiNewFeaturesAttribute(const uiOpenTypeFeatures *otf); + +// uiAttributeFeatures() returns the OpenType features stored in a. +// The returned uiOpenTypeFeatures object is owned by a. It is an +// error to call this on a uiAttribute that does not hold OpenType +// features. +_UI_EXTERN const uiOpenTypeFeatures *uiAttributeFeatures(const uiAttribute *a); + +// uiAttributedString represents a string of UTF-8 text that can +// optionally be embellished with formatting attributes. libui +// provides the list of formatting attributes, which cover common +// formatting traits like boldface and color as well as advanced +// typographical features provided by OpenType like superscripts +// and small caps. These attributes can be combined in a variety of +// ways. +// +// Attributes are applied to runs of Unicode codepoints in the string. +// Zero-length runs are elided. Consecutive runs that have the same +// attribute type and value are merged. Each attribute is independent +// of each other attribute; overlapping attributes of different types +// do not split each other apart, but different values of the same +// attribute type do. +// +// The empty string can also be represented by uiAttributedString, +// but because of the no-zero-length-attribute rule, it will not have +// attributes. +// +// A uiAttributedString takes ownership of all attributes given to +// it, as it may need to duplicate or delete uiAttribute objects at +// any time. By extension, when you free a uiAttributedString, +// all uiAttributes within will also be freed. Each method will +// describe its own rules in more details. +// +// In addition, uiAttributedString provides facilities for moving +// between grapheme clusters, which represent a character +// from the point of view of the end user. The cursor of a text editor +// is always placed on a grapheme boundary, so you can use these +// features to move the cursor left or right by one "character". +// TODO does uiAttributedString itself need this +// +// uiAttributedString does not provide enough information to be able +// to draw itself onto a uiDrawContext or respond to user actions. +// In order to do that, you'll need to use a uiDrawTextLayout, which +// is built from the combination of a uiAttributedString and a set of +// layout-specific properties. +typedef struct uiAttributedString uiAttributedString; + +// uiAttributedStringForEachAttributeFunc is the type of the function +// invoked by uiAttributedStringForEachAttribute() for every +// attribute in s. Refer to that function's documentation for more +// details. +typedef uiForEach (*uiAttributedStringForEachAttributeFunc)(const uiAttributedString *s, const uiAttribute *a, size_t start, size_t end, void *data); + +// @role uiAttributedString constructor +// uiNewAttributedString() creates a new uiAttributedString from +// initialString. The string will be entirely unattributed. +_UI_EXTERN uiAttributedString *uiNewAttributedString(const char *initialString); + +// @role uiAttributedString destructor +// uiFreeAttributedString() destroys the uiAttributedString s. +// It will also free all uiAttributes within. +_UI_EXTERN void uiFreeAttributedString(uiAttributedString *s); + +// uiAttributedStringString() returns the textual content of s as a +// '\0'-terminated UTF-8 string. The returned pointer is valid until +// the next change to the textual content of s. +_UI_EXTERN const char *uiAttributedStringString(const uiAttributedString *s); + +// uiAttributedStringLength() returns the number of UTF-8 bytes in +// the textual content of s, excluding the terminating '\0'. +_UI_EXTERN size_t uiAttributedStringLen(const uiAttributedString *s); + +// uiAttributedStringAppendUnattributed() adds the '\0'-terminated +// UTF-8 string str to the end of s. The new substring will be +// unattributed. +_UI_EXTERN void uiAttributedStringAppendUnattributed(uiAttributedString *s, const char *str); + +// uiAttributedStringInsertAtUnattributed() adds the '\0'-terminated +// UTF-8 string str to s at the byte position specified by at. The new +// substring will be unattributed; existing attributes will be moved +// along with their text. +_UI_EXTERN void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *str, size_t at); + +// TODO add the Append and InsertAtExtendingAttributes functions +// TODO and add functions that take a string + length + +// uiAttributedStringDelete() deletes the characters and attributes of +// s in the byte range [start, end). +_UI_EXTERN void uiAttributedStringDelete(uiAttributedString *s, size_t start, size_t end); + +// TODO add a function to uiAttributedString to get an attribute's value at a specific index or in a specific range, so we can edit + +// uiAttributedStringSetAttribute() sets a in the byte range [start, end) +// of s. Any existing attributes in that byte range of the same type are +// removed. s takes ownership of a; you should not use it after +// uiAttributedStringSetAttribute() returns. +_UI_EXTERN void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttribute *a, size_t start, size_t end); + +// uiAttributedStringForEachAttribute() enumerates all the +// uiAttributes in s. It is an error to modify s in f. Within f, s still +// owns the attribute; you can neither free it nor save it for later +// use. +// TODO reword the above for consistency +_UI_EXTERN void uiAttributedStringForEachAttribute(const uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); + +// TODO const correct this somehow (the implementation needs to mutate the structure) +_UI_EXTERN size_t uiAttributedStringNumGraphemes(uiAttributedString *s); + +// TODO const correct this somehow (the implementation needs to mutate the structure) +_UI_EXTERN size_t uiAttributedStringByteIndexToGrapheme(uiAttributedString *s, size_t pos); + +// TODO const correct this somehow (the implementation needs to mutate the structure) +_UI_EXTERN size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos); + +// uiFontDescriptor provides a complete description of a font where +// one is needed. Currently, this means as the default font of a +// uiDrawTextLayout and as the data returned by uiFontButton. +// All the members operate like the respective uiAttributes. +typedef struct uiFontDescriptor uiFontDescriptor; + +struct uiDrawFontDescriptor { + // TODO const-correct this or figure out how to deal with this when getting a value + char *Family; + double Size; + uiTextWeight Weight; + uiTextItalic Italic; + uiTextStretch Stretch; +}; + +// uiDrawTextLayout is a concrete representation of a +// uiAttributedString that can be displayed in a uiDrawContext. +// It includes information important for the drawing of a block of +// text, including the bounding box to wrap the text within, the +// alignment of lines of text within that box, areas to mark as +// being selected, and other things. +// +// Unlike uiAttributedString, the content of a uiDrawTextLayout is +// immutable once it has been created. +// +// TODO talk about OS-specific differences with text drawing that libui can't account for... +typedef struct uiDrawTextLayout uiDrawTextLayout; + +// uiDrawTextAlign specifies the alignment of lines of text in a +// uiDrawTextLayout. +_UI_ENUM(uiDrawTextAlign) { + uiDrawTextAlignLeft, + uiDrawTextAlignCenter, + uiDrawTextAlignRight, +}; + +// uiDrawTextLayoutParams describes a uiDrawTextLayout. +// DefaultFont is used to render any text that is not attributed +// sufficiently in String. Width determines the width of the bounding +// box of the text; the height is determined automatically. +typedef struct uiDrawTextLayoutParams uiDrawTextLayoutParams; + +// TODO const-correct this somehow +struct uiDrawTextLayoutParams { + uiAttributedString *String; + uiDrawFontDescriptor *DefaultFont; + double Width; + uiDrawTextAlign Align; +}; + +// @role uiDrawTextLayout constructor +// uiDrawNewTextLayout() creates a new uiDrawTextLayout from +// the given parameters. +// +// TODO +// - allow creating a layout out of a substring +// - allow marking compositon strings +// - allow marking selections, even after creation +// - add the following functions: +// - uiDrawTextLayoutHeightForWidth() (returns the height that a layout would need to be to display the entire string at a given width) +// - uiDrawTextLayoutRangeForSize() (returns what substring would fit in a given size) +// - uiDrawTextLayoutNewWithHeight() (limits amount of string used by the height) +// - some function to fix up a range (for text editing) +_UI_EXTERN uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *params); + +// @role uiDrawFreeTextLayout destructor +// uiDrawFreeTextLayout() frees tl. The underlying +// uiAttributedString is not freed. +_UI_EXTERN void uiDrawFreeTextLayout(uiDrawTextLayout *tl); + +// uiDrawText() draws tl in c with the top-left point of tl at (x, y). +_UI_EXTERN void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y); + +// uiDrawTextLayoutExtents() returns the width and height of tl +// in width and height. The returned width may be smaller than +// the width passed into uiDrawNewTextLayout() depending on +// how the text in tl is wrapped. Therefore, you can use this +// function to get the actual size of the text layout. +_UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height); + +// uiDrawTextLayoutNumLines() returns the number of lines in tl. +// This number will always be greater than or equal to 1; a text +// layout with no text only has one line. +_UI_EXTERN int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl); + +// uiDrawTextLayoutLineByteRange() returns the byte indices of the +// text that falls into the given line of tl as [start, end). +_UI_EXTERN void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end); + +// TODO metrics functions + +// TODO number of lines visible for clipping rect, range visible for clipping rect? From eeb7717d88d3bd5a62946d30fb3c7c38f4a6c720 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 19 Feb 2018 01:52:38 -0500 Subject: [PATCH 0853/1329] Moved old attributed-string stuff out of the way for now. --- common/{attrlist.c => OLD_attrlist.c} | 0 common/{attrstr.c => OLD_attrstr.c} | 0 common/{drawtext.c => OLD_drawtext.c} | 0 common/OLD_uipriv_attrstr.h | 43 +++++++++++++++++++++++++++ common/uipriv.h | 43 --------------------------- 5 files changed, 43 insertions(+), 43 deletions(-) rename common/{attrlist.c => OLD_attrlist.c} (100%) rename common/{attrstr.c => OLD_attrstr.c} (100%) rename common/{drawtext.c => OLD_drawtext.c} (100%) create mode 100644 common/OLD_uipriv_attrstr.h diff --git a/common/attrlist.c b/common/OLD_attrlist.c similarity index 100% rename from common/attrlist.c rename to common/OLD_attrlist.c diff --git a/common/attrstr.c b/common/OLD_attrstr.c similarity index 100% rename from common/attrstr.c rename to common/OLD_attrstr.c diff --git a/common/drawtext.c b/common/OLD_drawtext.c similarity index 100% rename from common/drawtext.c rename to common/OLD_drawtext.c diff --git a/common/OLD_uipriv_attrstr.h b/common/OLD_uipriv_attrstr.h new file mode 100644 index 00000000..6a1a63ce --- /dev/null +++ b/common/OLD_uipriv_attrstr.h @@ -0,0 +1,43 @@ + +// for attrstr.c +struct graphemes { + size_t len; + size_t *pointsToGraphemes; + size_t *graphemesToPoints; +}; +extern int graphemesTakesUTF16(void); +extern struct graphemes *graphemes(void *s, size_t len); + +// TODO split these into a separate header file? + +// attrstr.c +extern const uint16_t *attrstrUTF16(uiAttributedString *s); +extern size_t attrstrUTF16Len(uiAttributedString *s); +extern size_t attrstrUTF8ToUTF16(uiAttributedString *s, size_t n); +extern size_t *attrstrCopyUTF8ToUTF16(uiAttributedString *s, size_t *n); +extern size_t *attrstrCopyUTF16ToUTF8(uiAttributedString *s, size_t *n); + +// attrlist.c +struct attrlist; +extern void attrlistInsertAttribute(struct attrlist *alist, uiAttributeSpec *spec, size_t start, size_t end); +extern void attrlistInsertCharactersUnattributed(struct attrlist *alist, size_t start, size_t count); +extern void attrlistInsertCharactersExtendingAttributes(struct attrlist *alist, size_t start, size_t count); +extern void attrlistRemoveAttribute(struct attrlist *alist, uiAttribute type, size_t start, size_t end); +extern void attrlistRemoveAttributes(struct attrlist *alist, size_t start, size_t end); +extern void attrlistRemoveCharacters(struct attrlist *alist, size_t start, size_t end); +extern void attrlistForEach(struct attrlist *alist, uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); +// TODO move these to the top like everythng else +extern struct attrlist *attrlistNew(void); +extern void attrlistFree(struct attrlist *alist); + +// drawtext.c +struct caretDrawParams { + double r; + double g; + double b; + double a; + double xoff; + double width; +}; +extern void caretDrawParams(uiDrawContext *c, double height, struct caretDrawParams *p); +extern void drawTextBackground(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t start, size_t end, uiDrawBrush *brush, int isSelection); diff --git a/common/uipriv.h b/common/uipriv.h index 553073f5..3cd23605 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -58,49 +58,6 @@ extern void fallbackSkew(uiDrawMatrix *, double, double, double, double); extern void scaleCenter(double, double, double *, double *); extern void fallbackTransformSize(uiDrawMatrix *, double *, double *); -// for attrstr.c -struct graphemes { - size_t len; - size_t *pointsToGraphemes; - size_t *graphemesToPoints; -}; -extern int graphemesTakesUTF16(void); -extern struct graphemes *graphemes(void *s, size_t len); - -// TODO split these into a separate header file? - -// attrstr.c -extern const uint16_t *attrstrUTF16(uiAttributedString *s); -extern size_t attrstrUTF16Len(uiAttributedString *s); -extern size_t attrstrUTF8ToUTF16(uiAttributedString *s, size_t n); -extern size_t *attrstrCopyUTF8ToUTF16(uiAttributedString *s, size_t *n); -extern size_t *attrstrCopyUTF16ToUTF8(uiAttributedString *s, size_t *n); - -// attrlist.c -struct attrlist; -extern void attrlistInsertAttribute(struct attrlist *alist, uiAttributeSpec *spec, size_t start, size_t end); -extern void attrlistInsertCharactersUnattributed(struct attrlist *alist, size_t start, size_t count); -extern void attrlistInsertCharactersExtendingAttributes(struct attrlist *alist, size_t start, size_t count); -extern void attrlistRemoveAttribute(struct attrlist *alist, uiAttribute type, size_t start, size_t end); -extern void attrlistRemoveAttributes(struct attrlist *alist, size_t start, size_t end); -extern void attrlistRemoveCharacters(struct attrlist *alist, size_t start, size_t end); -extern void attrlistForEach(struct attrlist *alist, uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); -// TODO move these to the top like everythng else -extern struct attrlist *attrlistNew(void); -extern void attrlistFree(struct attrlist *alist); - -// drawtext.c -struct caretDrawParams { - double r; - double g; - double b; - double a; - double xoff; - double width; -}; -extern void caretDrawParams(uiDrawContext *c, double height, struct caretDrawParams *p); -extern void drawTextBackground(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t start, size_t end, uiDrawBrush *brush, int isSelection); - #ifdef __cplusplus } #endif From 93bf0d403e2b4c378b6f9b110933944e5bd9b2f9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 19 Feb 2018 18:37:50 -0500 Subject: [PATCH 0854/1329] Wrote the new uiAttribute code in attribute.c. --- TODO.md | 3 + common/attribute.c | 235 +++++++++++++++++++++++++++++++++++++++++++++ common/attrstr.h | 4 + ui_attrstr.h | 2 +- 4 files changed, 243 insertions(+), 1 deletion(-) create mode 100644 common/attribute.c create mode 100644 common/attrstr.h diff --git a/TODO.md b/TODO.md index 6389f1f8..528a658d 100644 --- a/TODO.md +++ b/TODO.md @@ -257,3 +257,6 @@ mac os x accessibility uiEntry disabling bugs http://www.cocoabuilder.com/archive/cocoa/215525-nstextfield-bug-can-be.html uiMultilineEntry disabling https://developer.apple.com/library/content/qa/qa1461/_index.html + +more TODOs: +- make no guarantee about buildability of feature branches diff --git a/common/attribute.c b/common/attribute.c new file mode 100644 index 00000000..49dc860d --- /dev/null +++ b/common/attribute.c @@ -0,0 +1,235 @@ +// 19 february 2018 +#include "uipriv.h" +#include "attrstr.h" + +struct uiAttribute { + uiAttributeType type; + union { + const char *family; + double size; + uiTextWeight weight; + uiTextItalic italic; + uiTextStretch stretch; + struct { + double r; + double g; + double b; + double a; + // put this here so we can reuse this structure + uiUnderlineColor underlineColor; + } color; + uiUnderline underline; + uiOpenTypeFeatures *features; + } u; +}; + +static uiAttribute *newAttribute(uiAttributeType type) +{ + uiAttribute *a; + + a = uiprivNew(uiAttribute); + a->type = type; + return a; +} + +void uiFreeAttribute(uiAttribute *a) +[ + switch (a->type) { + case uiAttributeTypeFamily: + uiprivFree(a->u.family); + break; + case uiAttributeTypeFeatures: + uiFreeOpenTypeFeatures(a->u.features); + break; + } + uiprivFree(a); +} + +uiAttributeType uiAttributeGetType(const uiAttribute *a) +{ + return a->type; +} + +uiAttribute *uiNewFamilyAttribute(const char *family) +{ + uiAttribute *a; + + a = newAttribute(uiAttributeTypeFamily); + a->u.family = uiprivStrdup(family); + return a; +} + +const char *uiAttributeFamily(const uiAttribute *a) +{ + return a->u.family; +} + +uiAttribute *uiNewSizeAttribute(double size) +{ + uiAttribute *a; + + a = newAttribute(uiAttributeTypeSize); + a->u.size = size; + return a; +} + +double uiAttributeSize(const uiAttribute *a) +{ + return a->u.size; +} + +uiAttribute *uiNewWeightAttribute(uiTextWeight weight) +{ + uiAttribute *a; + + a = newAttribute(uiAttributeTypeWeight); + a->u.weight = weight; + return a; +} + +uiTextWeight uiAttributeWeight(const uiAttribute *a) +{ + return a->u.weight; +} + +uiAttribute *uiNewItalicAttribute(uiTextItalic italic) +{ + uiAttribute *a; + + a = newAttribute(uiAttributeTypeItalic); + a->u.italic = italic; + return a; +} + +uiTextItalic uiAttributeItalic(const uiAttribute *a) +{ + return a->u.italic; +} + +uiAttribute *uiNewStretchAttribute(uiTextStretch stretch) +{ + uiAttribute *a; + + a = newAttribute(uiAttributeTypeStretch); + a->u.stretch = stretch; + return a; +} + +uiTextStretch uiAttributeStretch(const uiAttribute *a) +{ + return a->u.stretch; +} + +uiAttribute *uiNewColorAttribute(double r, double g, double b, double a) +{ + uiAttribute *at; + + at = newAttribute(uiAttributeTypeColor); + at->u.color.r = r; + at->u.color.g = g; + at->u.color.b = b; + at->u.color.a = a; + return at; +} + +void uiAttributeColor(const uiAttribute *a, double *r, double *g, double *b, double *alpha) +{ + *r = a->u.color.r; + *g = a->u.color.g; + *b = a->u.color.b; + *alpha = a->u.color.a; +} + +uiAttribute *uiNewBackgroundAttribute(double r, double g, double b, double a) +{ + uiAttribute *at; + + at = newAttribute(uiAttributeTypeBackgroundColor); + at->u.color.r = r; + at->u.color.g = g; + at->u.color.b = b; + at->u.color.a = a; + return at; +} + +uiAttribute *uiNewUnderlineAttribute(uiUnderline u) +{ + uiAttribute *a; + + a = newAttribute(uiAttributeTypeUnderline); + a->u.underline = u; + return a; +} + +uiUnderline uiAttributeUnderline(const uiAttribute *a) +{ + return a->u.underline; +} + +uiAttribute *uiNewUnderlineColorAttribute(uiUnderlineColor u, double r, double g, double b, double a) +{ + uiAttribute *at; + + at = uiNewColorAttribute(r, g, b, a); + at->type = uiAttributeTypeUnderlineColor; + at->u.color.underlineColor = u; + return at; +} + +void uiAttributeUnderline(const uiAttribute *a, uiUnderlineColor *u, double *r, double *g, double *b, double *alpha) +{ + *u = a->u.color.underlineColor; + uiAttributeColor(a, r, g, b, alpha); +} + +uiAttribute *uiNewFeaturesAttribute(const uiOpenTypeFeatures *otf) +{ + uiAttribute *a; + + a = newAttribute(uiAttributeTypeFeatures); + a->u.features = uiOpenTypeFeaturesClone(otf); + return a; +} + +const uiOpenTypeFeatures *uiAttributeFeatures(const uiAttribute *a) +{ + return a->u.features; +} + +int uiprivAttributeEqual(const uiAttribute *a, const uiAttribute *b) +{ + if (a == b) + return 1; + if (a->type != b->type) + return 0; + switch (a->type) { + case uiAttributeTypeFamily: + return uiprivStricmp(a->u.family, b->u.family); + case uiAttributeTypeSize: + // TODO is the use of == correct? + return a->u.size == b->u.size; + case uiAttributeTypeWeight: + return a->u.weight == b->u.weight; + case uiAttributeTypeItalic: + return a->u.italic == b->u.italic; + case uiAttributeTypeStretch: + return a->u.stretch == b->u.stretch; + case uiAttributeTypeUnderline: + return a->u.underline == b->u.underline; + case uiAttributeTypeUnderlineColor: + if (a->u.color.underlineColor != b->u.color.underlineColor) + return 0; + // fall through + case uiAttributeTypeColor: + case uiAttributeTypeBackground: + // TODO is the use of == correct? + return (a->u.color.r == b->u.color.r) && + (a->u.color.g == b->u.color.g) && + (a->u.color.b == b->u.color.b) && + (a->u.color.a == b->u.color.a); + case uiAttributeTypeFeatures: + return uiprivOpenTypeFeaturesEqual(a->u.features, b->u.features); + } + // TODO should not be reached + return 0; +} diff --git a/common/attrstr.h b/common/attrstr.h new file mode 100644 index 00000000..297add10 --- /dev/null +++ b/common/attrstr.h @@ -0,0 +1,4 @@ +// 19 february 2018 + +// attribute.c +extern int uiprivAttributeEqual(const uiAttribute *a, const uiAttribute *b); diff --git a/ui_attrstr.h b/ui_attrstr.h index f180ad66..c2da9a4a 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -51,7 +51,7 @@ _UI_EXTERN const char *uiAttributeFamily(const uiAttribute *a); // uiNewSizeAttribute() creates a new uiAttribute that changes the // size of the text it is applied to, in typographical points. -_UI_EXTERN uiAttribute *uiNewFamilyAttribute(double size); +_UI_EXTERN uiAttribute *uiNewSizeAttribute(double size); // uiAttributeSize() returns the font size stored in a. It is an error to // call this on a uiAttribute that does not hold a font size. From 8b35d161449595403fe26240f84408e039700298 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 20 Feb 2018 21:05:08 -0500 Subject: [PATCH 0855/1329] More notes. Also fixed a typo (thanks Nicole from Jul). --- _notes/windowsUWPGlass | 25 +++++++++++++++++++++++++ common/attribute.c | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 _notes/windowsUWPGlass diff --git a/_notes/windowsUWPGlass b/_notes/windowsUWPGlass new file mode 100644 index 00000000..debe47cd --- /dev/null +++ b/_notes/windowsUWPGlass @@ -0,0 +1,25 @@ +https://twitter.com/omgubuntu/status/962765197109841922 +https://github.com/DominicMaas/SoundByte/blob/master/SoundByte.UWP/Resources/Brushes.xaml +https://docs.microsoft.com/en-us/windows/uwp/design/style/acrylic +https://www.google.com/search?q=uwp+acrylic+winapi&ie=utf-8&oe=utf-8&client=firefox-b-1 +https://stackoverflow.com/questions/43931709/acrylic-material-in-win32-app +https://stackoverflow.com/questions/44000217/mimicking-acrylic-in-a-win32-app +https://stackoverflow.com/questions/43699256/how-to-use-acrylic-accent-in-windows-10-creators-update +https://stackoverflow.com/questions/39135834/how-to-call-uwp-api-from-converted-win32-app-desktop-app-converter +https://stackoverflow.com/questions/32724187/how-do-you-set-the-glass-blend-colour-on-windows-10 +https://channel9.msdn.com/Events/Build/2017/B8034 +https://www.reddit.com/r/Windows10/comments/7lzyzc/since_the_taskbar_is_still_win32_and_it_can_have/ +https://thenextweb.com/microsoft/2017/05/15/microsoft-fluent-design-system-breaking-windows-10s-new-look/ +https://github.com/bbougot/AcrylicWPF +http://vhanla.codigobit.info/2015/07/enable-windows-10-aero-glass-aka-blur.html +http://undoc.airesoft.co.uk/user32.dll/SetWindowCompositionAttribute.php +http://undoc.airesoft.co.uk/user32.dll/GetWindowCompositionAttribute.php +https://msdn.microsoft.com/en-us/library/windows/desktop/aa969530(v=vs.85).aspx +https://github.com/bbougot/AcrylicWPF/blob/master/MainWindow.xaml.cs +https://www.google.com/search?q=%22WCA_ACCENT_POLICY%22&client=firefox-b-1&source=lnt&tbs=cdr%3A1%2Ccd_min%3A1%2F1%2F2001%2Ccd_max%3A12%2F31%2F2013&tbm= +https://github.com/sgothel/jogl/blob/master/src/nativewindow/native/win32/WindowsDWM.c +http://www.brandonfa.lk/win8/win8_devrel_head_x86/uxtheme.h +http://www.brandonfa.lk/win8/ +https://github.com/gamozolabs/pdblister +https://github.com/wbenny/pdbex +https://github.com/wbenny/pdbex/releases diff --git a/common/attribute.c b/common/attribute.c index 49dc860d..66120e99 100644 --- a/common/attribute.c +++ b/common/attribute.c @@ -33,7 +33,7 @@ static uiAttribute *newAttribute(uiAttributeType type) } void uiFreeAttribute(uiAttribute *a) -[ +{ switch (a->type) { case uiAttributeTypeFamily: uiprivFree(a->u.family); From 20a94937b31e412e115fe0e91f9184e5e45ef754 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 22 Feb 2018 20:58:40 -0500 Subject: [PATCH 0856/1329] More TODOs. --- ui_attrstr.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui_attrstr.h b/ui_attrstr.h index c2da9a4a..9b02035d 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -390,7 +390,8 @@ _UI_EXTERN void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttribut // uiAttributes in s. It is an error to modify s in f. Within f, s still // owns the attribute; you can neither free it nor save it for later // use. -// TODO reword the above for consistency +// TODO reword the above for consistency (TODO and find out what I meant by that) +// TODO define an enumeration order (or mark it as undefined); also define how consecutive runs of identical attributes are handled here and sync with the definition of uiAttributedString itself _UI_EXTERN void uiAttributedStringForEachAttribute(const uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); // TODO const correct this somehow (the implementation needs to mutate the structure) From dcaf69bc51ae6ec5c3b22e0699bf197b0029c81c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 25 Feb 2018 20:38:06 -0500 Subject: [PATCH 0857/1329] Added a single cross-platform implementation of uiOpenTypeFeatures. --- common/opentype.c | 147 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 common/opentype.c diff --git a/common/opentype.c b/common/opentype.c new file mode 100644 index 00000000..7ed1062c --- /dev/null +++ b/common/opentype.c @@ -0,0 +1,147 @@ +// 25 february 2018 +#include "uipriv.h" +#include "attrstr.h" + +struct feature { + char a; + char b; + char c; + char d; + uint32_t value; +}; + +struct uiOpenTypeFeatures { + struct feature *data; + size_t len; + size_t cap; +}; + +#define bytecount(n) ((n) * sizeof (struct feature)) + +uiOpenTypeFeatures *uiNewOpenTypeFeatures(void) +{ + uiOpenTypeFeatures *otf; + + otf = uiprivNew(uiOpenTypeFeatures); + otf->cap = 16; + otf->data = (struct feature *) uiprivAlloc(bytecount(otf->cap), "struct feature[]"); + otf->len = 0; + return otf; +} + +void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf) +{ + uiprivFree(otf->data); + uiprivFree(otf); +} + +// uiOpenTypeFeaturesClone() makes a copy of otf and returns it. +// Changing one will not affect the other. +_UI_EXTERN uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures *otf) +{ + uiOpenTypeFeatures *ret; + + ret = uiprivNew(uiOpenTypeFeatures); + ret->len = otf->len; + ret->cap = otf->cap; + ret->data = (struct feature *) uiprivAlloc(bytecount(ret->cap), "struct feature[]"); + memset(ret->data, 0, bytecount(ret->cap)); + memmove(ret->data, otf->data, bytecount(ret->len)); + return ret; +} + +#define intdiff(a, b) (((int) (a)) - ((int) (b))) + +static int featurecmp(const void *a, const void *b) +{ + const struct feature *f = (const struct feature *) a; + const struct feature *g = (const struct feature *) b; + + if (f->a != g->a) + return intdiff(f->a, g->a); + if (f->b != g->b) + return intdiff(f->b, g->b); + if (f->c != g->c) + return intdiff(f->c, g->c); + return intdiff(f->d, g->d); +} + +void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value) +{ + struct feature *f; + + // replace existing value if any + f = (struct feature *) bsearch(otf->data, otf->len, sizeof (struct feature), featurecmp); + if (f != NULL) { + f->value = value; + return; + } + + // if we got here, the tag is new + if (otf->len == otf->cap) { + otf->cap *= 2; + otf->data = (struct feature *) uiprivRealloc(otf->data, bytecount(otf->cap), "struct feature[]"); + } + f = otf->data + otf->len; + f->a = a; + f->b = b; + f->c = c; + f->d = d; + f->value = value; + // TODO qsort here is overkill + otf->len++; + qsort(otf->data, otf->len, sizeof (struct feature), featurecmp); +} + +_UI_EXTERN void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d) +{ + struct feature *f; + ptrdiff_t index; + size_t count; + + f = (struct feature *) bsearch(otf->data, otf->len, sizeof (struct feature), featurecmp); + if (f == NULL) + return; + + index = f - otf->data; + count = otf->len - index - 1; + memmove(f + 1, f, bytecount(count)); + otf->len--; +} + +int uiOpenTypeFeaturesGet(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value) +{ + const struct feature *f; + + f = (const struct feature *) bsearch(otf->data, otf->len, sizeof (struct feature), featurecmp); + if (f == NULL) + return 0; + *value = f->value; + return 1; +} + +void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data) +{ + size_t n; + const struct feature *p; + uiForEach ret; + + p = otf->data; + for (n = 0; n < otf->len; n++) { + ret = (*f)(cur->a, cur->b, cur->c, cur->d, + cur->value, data); + // TODO for all: require exact match? + if (ret == uiForEachStop) + return; + p++; + } +} + +int uiprivOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeatures *b) +{ + if (a == b) + return 1; + if (a->len != b->len) + return 0; + return memcmp(a->data, b->data, bytecount(a->len)) == 0; +} From 3f62cb5cee6eccb14c501c6ae42deee6edc3ab41 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 25 Feb 2018 20:40:23 -0500 Subject: [PATCH 0858/1329] Oops; forgot to update attrstr.h in the last commit. Also changed a TODO to a LONGTERM in opentype.c. --- common/attrstr.h | 3 +++ common/opentype.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/common/attrstr.h b/common/attrstr.h index 297add10..f835a8a4 100644 --- a/common/attrstr.h +++ b/common/attrstr.h @@ -2,3 +2,6 @@ // attribute.c extern int uiprivAttributeEqual(const uiAttribute *a, const uiAttribute *b); + +// opentype.c +extern int uiprivOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeatures *b); diff --git a/common/opentype.c b/common/opentype.c index 7ed1062c..335a2ad0 100644 --- a/common/opentype.c +++ b/common/opentype.c @@ -88,7 +88,7 @@ void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char f->c = c; f->d = d; f->value = value; - // TODO qsort here is overkill + // LONGTERM qsort here is overkill otf->len++; qsort(otf->data, otf->len, sizeof (struct feature), featurecmp); } From 70815d8d7b80966b68ff12c4d9db7004d27d0803 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 27 Feb 2018 23:44:50 -0500 Subject: [PATCH 0859/1329] Started writing a test suite for uiOpenTypeFeatures. So far it's just the test boilerplate. --- common/opentype_test.c | 114 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 common/opentype_test.c diff --git a/common/opentype_test.c b/common/opentype_test.c new file mode 100644 index 00000000..784073a8 --- /dev/null +++ b/common/opentype_test.c @@ -0,0 +1,114 @@ +xx 27 february 2018 +#ifndef TODO_TEST +#error TODO this is where libui itself goes +#endif + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(__GNUC__) +#define testingTest(Name) \ + void Test ## Name(testingT *t); \ + __attribute__((constructor)) static inline void testingprivCtorRegisterTest ## Name(void) { testingprivRegisterTest("Test" #Name, Test ## Name); } \ + void Test ## Name(testingT *t) +#else +#error unknown compiler; cannot continue +#endif + +extern int testingMain(void); + +typedef struct testingT testingT; +#define testingTErrorf(t, ...) testingprivTLogFull(t, __FILE__, __LINE__, __VA_ARGS__) +#define testingTErrorvf(t, format, ap) testingprivTLogFullv(t, __FILE__, __LINE__, format, ap) + +extern void testingprivRegisterTest(const char *, void (*)(testi +ngT *)); +extern void testingprivTLogFull(testingT *, const char *, int, const char *, ...); +extern void testingprivTLogFullv(testingT *, const char *, int, const char *, va_list); + +#ifdef __cplusplus +} +#endif + +testingTest(Name) +{ +} + +int main(void) +{ + return testingMain(); +} + +#include +#include + +#define testingprivNew(T) ((T *) malloc(sizeof (T))) + +struct testingT { + const char *name; + void (*f)(testingT *); + int failed; + struct testingprivTest *next; +}; + +static testingT *tests = NULL; + +void testingprivRegisterTest(const char *name, void (*f)(testingT *)) +{ + testingT *t; + + t = testingprivNew(testingT) + t->name = name; + t->f = f; + t->failed = 0; + t->next = tests; + tests = t; +} + +int testingMain(void) +{ + testingT *t; + int anyFailed; + const char *status; + + if (tests == NULL) { + fprintf(stderr, "warning: no tests to run\n"); + xx imitate Go here (TODO confirm this) + return 0; + } + + anyFailed = 0; + for (t = tests; t != NULL; t = t->next) { + printf("=== RUN %s\n", t->name); + (*(t->f))(t); + status = "PASS"; + if (t->failed) { + status = "FAIL"; + anyFailed = 1; + } + printf("--- %s: %s (%s)\n", status, t->name, "TODO"); + } + + if (anyFailed) { + printf("FAIL\n"); + return 1; + } + printf("PASS\n"); + return 0; +} + +extern void testingprivTLogFull(testingT *t, const char *file, int line, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + testingprivTLogFullv(t, file, line, format, ap); + va_end(ap); +} + +void testingprivTLogFullv(testingT *t, const char *file, int line, const char *format, va_list ap) +{ +} From 5ab1266b5d1ee2afb147560416d55dd4d05fdf7d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 28 Feb 2018 01:21:10 -0500 Subject: [PATCH 0860/1329] Finished the boilerplate for the uiOpenTypeFeatures test. --- common/opentype_test.c | 52 ++++++++++++++++++++++++++++++++---------- 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/common/opentype_test.c b/common/opentype_test.c index 784073a8..b494527b 100644 --- a/common/opentype_test.c +++ b/common/opentype_test.c @@ -1,10 +1,26 @@ -xx 27 february 2018 +// 27 february 2018 #ifndef TODO_TEST #error TODO this is where libui itself goes #endif #include +#undef testingprivBadLanguageVersion +#ifdef __cplusplus +// TODO https://stackoverflow.com/questions/2324658/how-to-determine-the-version-of-the-c-standard-used-by-the-compiler implies this won't do with C++0x-era compilers, and https://wiki.apache.org/stdcxx/C++0xCompilerSupport doesn't talk about va_copy() so a simple version check for the C99 preprocessor may be wrong... +// TODO what if __cplusplus is blank (maybe only in that case, since IIRC C++98 requires __cplusplus to have a value)? +#if __cplusplus < 201103L +#define testingprivBadLanguageVersion +#endif +#elif !defined(__STDC_VERSION__) +#define testingprivBadLanguageVersion +#elif __STDC_VERSION__ < 199901L +#define testingprivBadLanguageVersion +#endif +#ifdef testingprivBadLanguageVersion +#error sorry, TODO requires either C99 or TODO; cannot continue +#endif + #ifdef __cplusplus extern "C" { #endif @@ -21,13 +37,13 @@ extern "C" { extern int testingMain(void); typedef struct testingT testingT; -#define testingTErrorf(t, ...) testingprivTLogFull(t, __FILE__, __LINE__, __VA_ARGS__) -#define testingTErrorvf(t, format, ap) testingprivTLogFullv(t, __FILE__, __LINE__, format, ap) +#define testingTErrorf(t, ...) testingprivTErrorfFull(t, __FILE__, __LINE__, __VA_ARGS__) +#define testingTErrorvf(t, format, ap) testingprivTErrorvfFull(t, __FILE__, __LINE__, format, ap) -extern void testingprivRegisterTest(const char *, void (*)(testi -ngT *)); -extern void testingprivTLogFull(testingT *, const char *, int, const char *, ...); -extern void testingprivTLogFullv(testingT *, const char *, int, const char *, va_list); +// TODO should __LINE__ arguments use intmax_t or uintmax_t instead of int? +extern void testingprivRegisterTest(const char *, void (*)(testingT *)); +extern void testingprivTErrorfFull(testingT *, const char *, int, const char *, ...); +extern void testingprivTErrorvfFull(testingT *, const char *, int, const char *, va_list); #ifdef __cplusplus } @@ -35,6 +51,7 @@ extern void testingprivTLogFullv(testingT *, const char *, int, const char *, va testingTest(Name) { + printf("in the test!\n"); } int main(void) @@ -60,7 +77,7 @@ void testingprivRegisterTest(const char *name, void (*f)(testingT *)) { testingT *t; - t = testingprivNew(testingT) + t = testingprivNew(testingT); t->name = name; t->f = f; t->failed = 0; @@ -76,7 +93,7 @@ int testingMain(void) if (tests == NULL) { fprintf(stderr, "warning: no tests to run\n"); - xx imitate Go here (TODO confirm this) + // imitate Go here (TODO confirm this) return 0; } @@ -100,15 +117,26 @@ int testingMain(void) return 0; } -extern void testingprivTLogFull(testingT *t, const char *file, int line, const char *format, ...) +static void testingprivTDoLog(testingT *t, const char *file, int line, const char *format, va_list ap) +{ + // TODO extract filename from file + printf("\t%s:%d: ", file, line); + // TODO split into lines separated by \n\t\t and trimming trailing empty lines + vprintf(format, ap); + printf("\n"); +} + +void testingprivTErrorfFull(testingT *t, const char *file, int line, const char *format, ...) { va_list ap; va_start(ap, format); - testingprivTLogFullv(t, file, line, format, ap); + testingprivTErrorvfFull(t, file, line, format, ap); va_end(ap); } -void testingprivTLogFullv(testingT *t, const char *file, int line, const char *format, va_list ap) +void testingprivTErrorvfFull(testingT *t, const char *file, int line, const char *format, va_list ap) { + testingprivTDoLog(t, file, line, format, ap); + t->failed = 1; } From cf15dba2efb9d2d94f3214c47f850ed3e3495aee Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 28 Feb 2018 01:22:16 -0500 Subject: [PATCH 0861/1329] Oops, missed a spot when cleaning up the previous commit. Fixed. --- common/opentype_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/opentype_test.c b/common/opentype_test.c index b494527b..b883c53b 100644 --- a/common/opentype_test.c +++ b/common/opentype_test.c @@ -68,7 +68,7 @@ struct testingT { const char *name; void (*f)(testingT *); int failed; - struct testingprivTest *next; + testingT *next; }; static testingT *tests = NULL; From 010e8782861b8ac19be2e17cfbaabcfaa17373a5 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 28 Feb 2018 19:43:29 -0500 Subject: [PATCH 0862/1329] Wrote more of the testing framework, wrote the first test, and fixed compiler errors in opentype.c. --- common/opentype.c | 38 +++++++++++++++++++++++++++----------- common/opentype_test.c | 37 ++++++++++++++++++++++++++++++++++--- 2 files changed, 61 insertions(+), 14 deletions(-) diff --git a/common/opentype.c b/common/opentype.c index 335a2ad0..f27907f6 100644 --- a/common/opentype.c +++ b/common/opentype.c @@ -1,6 +1,6 @@ // 25 february 2018 -#include "uipriv.h" -#include "attrstr.h" +//TODO#include "uipriv.h" +//TODO#include "attrstr.h" struct feature { char a; @@ -35,9 +35,7 @@ void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf) uiprivFree(otf); } -// uiOpenTypeFeaturesClone() makes a copy of otf and returns it. -// Changing one will not affect the other. -_UI_EXTERN uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures *otf) +uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures *otf) { uiOpenTypeFeatures *ret; @@ -66,12 +64,27 @@ static int featurecmp(const void *a, const void *b) return intdiff(f->d, g->d); } +static struct feature mkkey(char a, char b, char c, char d) +{ + struct feature f; + + f.a = a; + f.b = b; + f.c = c; + f.d = d; + return f; +} + +#define find(pkey, otf) bsearch(pkey, otf->data, otf->len, sizeof (struct feature), featurecmp) + void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value) { struct feature *f; + struct feature key; // replace existing value if any - f = (struct feature *) bsearch(otf->data, otf->len, sizeof (struct feature), featurecmp); + key = mkkey(a, b, c, d); + f = (struct feature *) find(&key, otf); if (f != NULL) { f->value = value; return; @@ -93,13 +106,15 @@ void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char qsort(otf->data, otf->len, sizeof (struct feature), featurecmp); } -_UI_EXTERN void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d) +void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d) { struct feature *f; + struct feature key; ptrdiff_t index; size_t count; - f = (struct feature *) bsearch(otf->data, otf->len, sizeof (struct feature), featurecmp); + key = mkkey(a, b, c, d); + f = (struct feature *) find(&key, otf); if (f == NULL) return; @@ -112,8 +127,10 @@ _UI_EXTERN void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b int uiOpenTypeFeaturesGet(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value) { const struct feature *f; + struct feature key; - f = (const struct feature *) bsearch(otf->data, otf->len, sizeof (struct feature), featurecmp); + key = mkkey(a, b, c, d); + f = (const struct feature *) find(&key, otf); if (f == NULL) return 0; *value = f->value; @@ -128,8 +145,7 @@ void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeatures p = otf->data; for (n = 0; n < otf->len; n++) { - ret = (*f)(cur->a, cur->b, cur->c, cur->d, - cur->value, data); + ret = (*f)(otf, p->a, p->b, p->c, p->d, p->value, data); // TODO for all: require exact match? if (ret == uiForEachStop) return; diff --git a/common/opentype_test.c b/common/opentype_test.c index b883c53b..3fe00079 100644 --- a/common/opentype_test.c +++ b/common/opentype_test.c @@ -2,6 +2,7 @@ #ifndef TODO_TEST #error TODO this is where libui itself goes #endif +#include #include @@ -18,7 +19,7 @@ #define testingprivBadLanguageVersion #endif #ifdef testingprivBadLanguageVersion -#error sorry, TODO requires either C99 or TODO; cannot continue +#error sorry, TODO requires either C99 or C++11; cannot continue #endif #ifdef __cplusplus @@ -49,9 +50,39 @@ extern void testingprivTErrorvfFull(testingT *, const char *, int, const char *, } #endif -testingTest(Name) +#include +#include +#include +#include +typedef struct uiOpenTypeFeatures uiOpenTypeFeatures; +typedef int uiForEach; +enum { uiForEachContinue, uiForEachStop }; +typedef uiForEach (*uiOpenTypeFeaturesForEachFunc)(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data); +#define uiprivNew(x) ((x *) malloc(sizeof (x))) +#define uiprivAlloc(x,y) malloc(x) +#define uiprivRealloc(x,y,z) realloc(x,y) +#define uiprivFree free +#include "opentype.c" + +testingTest(OpenTypeFeaturesAddGet) { - printf("in the test!\n"); + uiOpenTypeFeatures *otf; + char a, b, c, d; + uint32_t value; + + otf = uiNewOpenTypeFeatures(); + uiOpenTypeFeaturesAdd(otf, 'a', 'b', 'c', 'd', 12345); + if (!uiOpenTypeFeaturesGet(otf, 'a', 'b', 'c', 'd', &value)) { + testingTErrorf(t, "uiOpenTypeFeaturesGet() failed to get feature we added"); + goto out; + } + if (value != 12345) { + testingTErrorf(t, "feature abcd: got %" PRIu32 ", want 12345", value); + goto out; + } + +out: + uiFreeOpenTypeFeatures(otf); } int main(void) From 8d92003426926ad9e29007b52abfa7cfc433a348 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 28 Feb 2018 22:07:06 -0500 Subject: [PATCH 0863/1329] Broke apart the testing implementation code into their own files. --- common/opentype_test.c | 129 +-------------------------------------- common/testing.h | 63 +++++++++++++++++++ common/testing_testing.c | 83 +++++++++++++++++++++++++ 3 files changed, 147 insertions(+), 128 deletions(-) create mode 100644 common/testing.h create mode 100644 common/testing_testing.c diff --git a/common/opentype_test.c b/common/opentype_test.c index 3fe00079..589353e8 100644 --- a/common/opentype_test.c +++ b/common/opentype_test.c @@ -3,52 +3,7 @@ #error TODO this is where libui itself goes #endif #include - -#include - -#undef testingprivBadLanguageVersion -#ifdef __cplusplus -// TODO https://stackoverflow.com/questions/2324658/how-to-determine-the-version-of-the-c-standard-used-by-the-compiler implies this won't do with C++0x-era compilers, and https://wiki.apache.org/stdcxx/C++0xCompilerSupport doesn't talk about va_copy() so a simple version check for the C99 preprocessor may be wrong... -// TODO what if __cplusplus is blank (maybe only in that case, since IIRC C++98 requires __cplusplus to have a value)? -#if __cplusplus < 201103L -#define testingprivBadLanguageVersion -#endif -#elif !defined(__STDC_VERSION__) -#define testingprivBadLanguageVersion -#elif __STDC_VERSION__ < 199901L -#define testingprivBadLanguageVersion -#endif -#ifdef testingprivBadLanguageVersion -#error sorry, TODO requires either C99 or C++11; cannot continue -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#if defined(__GNUC__) -#define testingTest(Name) \ - void Test ## Name(testingT *t); \ - __attribute__((constructor)) static inline void testingprivCtorRegisterTest ## Name(void) { testingprivRegisterTest("Test" #Name, Test ## Name); } \ - void Test ## Name(testingT *t) -#else -#error unknown compiler; cannot continue -#endif - -extern int testingMain(void); - -typedef struct testingT testingT; -#define testingTErrorf(t, ...) testingprivTErrorfFull(t, __FILE__, __LINE__, __VA_ARGS__) -#define testingTErrorvf(t, format, ap) testingprivTErrorvfFull(t, __FILE__, __LINE__, format, ap) - -// TODO should __LINE__ arguments use intmax_t or uintmax_t instead of int? -extern void testingprivRegisterTest(const char *, void (*)(testingT *)); -extern void testingprivTErrorfFull(testingT *, const char *, int, const char *, ...); -extern void testingprivTErrorvfFull(testingT *, const char *, int, const char *, va_list); - -#ifdef __cplusplus -} -#endif +#include "testing.h" #include #include @@ -89,85 +44,3 @@ int main(void) { return testingMain(); } - -#include -#include - -#define testingprivNew(T) ((T *) malloc(sizeof (T))) - -struct testingT { - const char *name; - void (*f)(testingT *); - int failed; - testingT *next; -}; - -static testingT *tests = NULL; - -void testingprivRegisterTest(const char *name, void (*f)(testingT *)) -{ - testingT *t; - - t = testingprivNew(testingT); - t->name = name; - t->f = f; - t->failed = 0; - t->next = tests; - tests = t; -} - -int testingMain(void) -{ - testingT *t; - int anyFailed; - const char *status; - - if (tests == NULL) { - fprintf(stderr, "warning: no tests to run\n"); - // imitate Go here (TODO confirm this) - return 0; - } - - anyFailed = 0; - for (t = tests; t != NULL; t = t->next) { - printf("=== RUN %s\n", t->name); - (*(t->f))(t); - status = "PASS"; - if (t->failed) { - status = "FAIL"; - anyFailed = 1; - } - printf("--- %s: %s (%s)\n", status, t->name, "TODO"); - } - - if (anyFailed) { - printf("FAIL\n"); - return 1; - } - printf("PASS\n"); - return 0; -} - -static void testingprivTDoLog(testingT *t, const char *file, int line, const char *format, va_list ap) -{ - // TODO extract filename from file - printf("\t%s:%d: ", file, line); - // TODO split into lines separated by \n\t\t and trimming trailing empty lines - vprintf(format, ap); - printf("\n"); -} - -void testingprivTErrorfFull(testingT *t, const char *file, int line, const char *format, ...) -{ - va_list ap; - - va_start(ap, format); - testingprivTErrorvfFull(t, file, line, format, ap); - va_end(ap); -} - -void testingprivTErrorvfFull(testingT *t, const char *file, int line, const char *format, va_list ap) -{ - testingprivTDoLog(t, file, line, format, ap); - t->failed = 1; -} diff --git a/common/testing.h b/common/testing.h new file mode 100644 index 00000000..9d8c8006 --- /dev/null +++ b/common/testing.h @@ -0,0 +1,63 @@ +// 27 february 2018 + +#ifndef testingprivIncludeGuard_testing_h +#define testingprivIncludeGuard_testing_h + +#include + +#undef testingprivBadLanguageVersion +#ifdef __cplusplus +// TODO https://stackoverflow.com/questions/2324658/how-to-determine-the-version-of-the-c-standard-used-by-the-compiler implies this won't do with C++0x-era compilers, and https://wiki.apache.org/stdcxx/C++0xCompilerSupport doesn't talk about va_copy() so a simple version check for the C99 preprocessor may be wrong... +// TODO what if __cplusplus is blank (maybe only in that case, since IIRC C++98 requires __cplusplus to have a value)? +#if __cplusplus < 201103L +#define testingprivBadLanguageVersion +#endif +#elif !defined(__STDC_VERSION__) +#define testingprivBadLanguageVersion +#elif __STDC_VERSION__ < 199901L +#define testingprivBadLanguageVersion +#endif +#ifdef testingprivBadLanguageVersion +#error sorry, TODO requires either C99 or C++11; cannot continue +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(__GNUC__) +#define testingprivMkCtor(name, reg) \ + __attribute__((constructor)) static void testingprivCtor ## name(void) { reg(#name, name); } +#elif defined(_MSC_VER) +#define testingprivMkCtorPrototype(name, reg) \ + static int name(void) testingprivCtor ## name(void) { reg(#name, name); return 0; } \ + __pragma(section(".CRT$XCU",read)) \ + __declspec(allocate(".CRT$XCU")) static int (*testingprivCtorPtr ## name)(void) = testingprivCtor ## name; +#elif defined(__SUNPRO_C) +#define testingprivMkCtor(name, reg) \ + _Pragma("init(testingprivCtor" #name ")") static void testingprivCtor ## name(void) { reg(#name, name); } +#else +#error unknown compiler; cannot continue +#endif + +#define testingTest(Name) \ + void Test ## Name(testingT *t); \ + testingprivMkCtor(Test ## Name, testingprivRegisterTest) \ + void Test ## Name(testingT *t) + +extern int testingMain(void); + +typedef struct testingT testingT; +#define testingTErrorf(t, ...) testingprivTErrorfFull(t, __FILE__, __LINE__, __VA_ARGS__) +#define testingTErrorvf(t, format, ap) testingprivTErrorvfFull(t, __FILE__, __LINE__, format, ap) + +// TODO should __LINE__ arguments use intmax_t or uintmax_t instead of int? +extern void testingprivRegisterTest(const char *, void (*)(testingT *)); +extern void testingprivTErrorfFull(testingT *, const char *, int, const char *, ...); +extern void testingprivTErrorvfFull(testingT *, const char *, int, const char *, va_list); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/common/testing_testing.c b/common/testing_testing.c new file mode 100644 index 00000000..c8d0d986 --- /dev/null +++ b/common/testing_testing.c @@ -0,0 +1,83 @@ +// 27 february 2018 +#include +#include +#include "testing.h" + +#define testingprivNew(T) ((T *) malloc(sizeof (T))) + +struct testingT { + const char *name; + void (*f)(testingT *); + int failed; + testingT *next; +}; + +static testingT *tests = NULL; + +void testingprivRegisterTest(const char *name, void (*f)(testingT *)) +{ + testingT *t; + + t = testingprivNew(testingT); + t->name = name; + t->f = f; + t->failed = 0; + t->next = tests; + tests = t; +} + +int testingMain(void) +{ + testingT *t; + int anyFailed; + const char *status; + + if (tests == NULL) { + fprintf(stderr, "warning: no tests to run\n"); + // imitate Go here (TODO confirm this) + return 0; + } + + anyFailed = 0; + for (t = tests; t != NULL; t = t->next) { + printf("=== RUN %s\n", t->name); + (*(t->f))(t); + status = "PASS"; + if (t->failed) { + status = "FAIL"; + anyFailed = 1; + } + printf("--- %s: %s (%s)\n", status, t->name, "TODO"); + } + + if (anyFailed) { + printf("FAIL\n"); + return 1; + } + printf("PASS\n"); + return 0; +} + +static void testingprivTDoLog(testingT *t, const char *file, int line, const char *format, va_list ap) +{ + // TODO extract filename from file + printf("\t%s:%d: ", file, line); + // TODO split into lines separated by \n\t\t and trimming trailing empty lines + vprintf(format, ap); + printf("\n"); +} + +void testingprivTErrorfFull(testingT *t, const char *file, int line, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + testingprivTErrorvfFull(t, file, line, format, ap); + va_end(ap); +} + +void testingprivTErrorvfFull(testingT *t, const char *file, int line, const char *format, va_list ap) +{ + testingprivTDoLog(t, file, line, format, ap); + t->failed = 1; +} From d0db6f95942cb4bd353835131f85247dd05c06c6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 1 Mar 2018 20:25:36 -0500 Subject: [PATCH 0864/1329] Added early termination mechanics to the testing framework. --- common/testing.h | 43 +++++++++++++++++++++++++++++++++++----- common/testing_testing.c | 11 +++++++++- 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/common/testing.h b/common/testing.h index 9d8c8006..10403c33 100644 --- a/common/testing.h +++ b/common/testing.h @@ -25,23 +25,43 @@ extern "C" { #endif -#if defined(__GNUC__) +#ifdef __cplusplus +#define testingprivMkScaffold(name) \ + static inline void testingprivScaffold ## name(testingT *t) \ + { \ + bool failedNow = false; \ + try { name(t); } \ + catch (testingprivFailNowException e) { failedNow = true; } \ + /* TODO see if we should catch other exceptions too */ \ + /* don't call this in the catch block as it calls longjmp() */ \ + if (failedNow) testingprivTDoFailNow(t); \ + } +#else +#define testingprivMkScaffold(name) \ + static inline void testingprivScaffold ## name(testingT *t) { name(t); } +#endif + +#if defined(__cplusplus) #define testingprivMkCtor(name, reg) \ - __attribute__((constructor)) static void testingprivCtor ## name(void) { reg(#name, name); } + static reg ## Class testingprivCtor ## name(#name, testingprivScaffold ## name); +#elif defined(__GNUC__) +#define testingprivMkCtor(name, reg) \ + __attribute__((constructor)) static void testingprivCtor ## name(void) { reg(#name, testingprivScaffold ## name); } #elif defined(_MSC_VER) #define testingprivMkCtorPrototype(name, reg) \ - static int name(void) testingprivCtor ## name(void) { reg(#name, name); return 0; } \ + static int name(void) testingprivCtor ## name(void) { reg(#name, testingprivScaffold ## name); return 0; } \ __pragma(section(".CRT$XCU",read)) \ __declspec(allocate(".CRT$XCU")) static int (*testingprivCtorPtr ## name)(void) = testingprivCtor ## name; #elif defined(__SUNPRO_C) #define testingprivMkCtor(name, reg) \ - _Pragma("init(testingprivCtor" #name ")") static void testingprivCtor ## name(void) { reg(#name, name); } + _Pragma("init(testingprivCtor" #name ")") static void testingprivCtor ## name(void) { reg(#name, testingprivScaffold ## name); } #else -#error unknown compiler; cannot continue +#error unknown compiler for making constructors in C; cannot continue #endif #define testingTest(Name) \ void Test ## Name(testingT *t); \ + testingprivMkScaffold(Test ## Name) \ testingprivMkCtor(Test ## Name, testingprivRegisterTest) \ void Test ## Name(testingT *t) @@ -50,14 +70,27 @@ extern int testingMain(void); typedef struct testingT testingT; #define testingTErrorf(t, ...) testingprivTErrorfFull(t, __FILE__, __LINE__, __VA_ARGS__) #define testingTErrorvf(t, format, ap) testingprivTErrorvfFull(t, __FILE__, __LINE__, format, ap) +#ifdef __cplusplus +#define testingTFailNow(t) (throw testingprivFailNowException()) +#else +#define testingTFailNow(t) testingprivTDoFailNow(t) +#endif // TODO should __LINE__ arguments use intmax_t or uintmax_t instead of int? extern void testingprivRegisterTest(const char *, void (*)(testingT *)); extern void testingprivTErrorfFull(testingT *, const char *, int, const char *, ...); extern void testingprivTErrorvfFull(testingT *, const char *, int, const char *, va_list); +extern void testingprivTDoFailNow(testingT *); #ifdef __cplusplus } +namespace { + class testingprivFailNowException {}; + class testingprivRegisterTestClass { + public: + testingprivRegisterTestClass(const char *name, void (*f)(testingT *)) { testingprivRegisterTest(name, f); } + }; +} #endif #endif diff --git a/common/testing_testing.c b/common/testing_testing.c index c8d0d986..33e19bbe 100644 --- a/common/testing_testing.c +++ b/common/testing_testing.c @@ -1,6 +1,7 @@ // 27 february 2018 #include #include +#include #include "testing.h" #define testingprivNew(T) ((T *) malloc(sizeof (T))) @@ -9,6 +10,7 @@ struct testingT { const char *name; void (*f)(testingT *); int failed; + jmp_buf failNowBuf; testingT *next; }; @@ -41,7 +43,8 @@ int testingMain(void) anyFailed = 0; for (t = tests; t != NULL; t = t->next) { printf("=== RUN %s\n", t->name); - (*(t->f))(t); + if (setjmp(t->failNowBuf) == 0) + (*(t->f))(t); status = "PASS"; if (t->failed) { status = "FAIL"; @@ -81,3 +84,9 @@ void testingprivTErrorvfFull(testingT *t, const char *file, int line, const char testingprivTDoLog(t, file, line, format, ap); t->failed = 1; } + +void testingprivTDoFailNow(testingT *t) +{ + t->failed = 1; + longjmp(t->failNowBuf, 1); +} From b5570040b0a859b479ba94241cf943fe778bbca3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 3 Mar 2018 14:08:17 -0500 Subject: [PATCH 0865/1329] Added skipping and fatal to testing.h. --- common/testing.h | 22 +++++++++++++++++----- common/testing_testing.c | 31 +++++++++++++++++++++++++------ 2 files changed, 42 insertions(+), 11 deletions(-) diff --git a/common/testing.h b/common/testing.h index 10403c33..6a20a0a7 100644 --- a/common/testing.h +++ b/common/testing.h @@ -29,12 +29,14 @@ extern "C" { #define testingprivMkScaffold(name) \ static inline void testingprivScaffold ## name(testingT *t) \ { \ - bool failedNow = false; \ + bool failedNow = false, skippedNow = false; \ try { name(t); } \ catch (testingprivFailNowException e) { failedNow = true; } \ + catch (testingprivSkipNowException e) { skippedNow = true; } \ /* TODO see if we should catch other exceptions too */ \ - /* don't call this in the catch block as it calls longjmp() */ \ + /* don't call these in the catch blocks as they call longjmp() */ \ if (failedNow) testingprivTDoFailNow(t); \ + if (skippedNow) testingprivTDoSkipNow(t); \ } #else #define testingprivMkScaffold(name) \ @@ -68,24 +70,34 @@ extern "C" { extern int testingMain(void); typedef struct testingT testingT; -#define testingTErrorf(t, ...) testingprivTErrorfFull(t, __FILE__, __LINE__, __VA_ARGS__) -#define testingTErrorvf(t, format, ap) testingprivTErrorvfFull(t, __FILE__, __LINE__, format, ap) +#define testingTLogf(t, ...) (testingprivTLogfFull(t, __FILE__, __LINE__, __VA_ARGS__)) +#define testingTLogvf(t, format, ap) (testingprivTLogvfFull(t, __FILE__, __LINE__, format, ap)) +#define testingTErrorf(t, ...) (testingprivTErrorfFull(t, __FILE__, __LINE__, __VA_ARGS__)) +#define testingTErrorvf(t, format, ap) (testingprivTErrorvfFull(t, __FILE__, __LINE__, format, ap)) +#define testingTFatalf(t, ...) ((testingprivTErrorfFull(t, __FILE__, __LINE__, __VA_ARGS__)), (testingTFailNow(t))) +#define testingTFatalvf(t, format, ap) ((testingprivTErrorvfFull(t, __FILE__, __LINE__, format, ap)), (testingTFailNow(t))) #ifdef __cplusplus #define testingTFailNow(t) (throw testingprivFailNowException()) +#define testingTSkipNow(t) (throw testingprivSkipNowException()) #else -#define testingTFailNow(t) testingprivTDoFailNow(t) +#define testingTFailNow(t) (testingprivTDoFailNow(t)) +#define testingTSkipNow(t) (testingprivTDoSkipNow(t)) #endif // TODO should __LINE__ arguments use intmax_t or uintmax_t instead of int? extern void testingprivRegisterTest(const char *, void (*)(testingT *)); +extern void testingprivTLogfFull(testingT *, const char *, int, const char *, ...); +extern void testingprivTLogvfFull(testingT *, const char *, int, const char *, va_list); extern void testingprivTErrorfFull(testingT *, const char *, int, const char *, ...); extern void testingprivTErrorvfFull(testingT *, const char *, int, const char *, va_list); extern void testingprivTDoFailNow(testingT *); +extern void testingprivTDoSkipNow(testingT *); #ifdef __cplusplus } namespace { class testingprivFailNowException {}; + class testingprivSkipNowException {}; class testingprivRegisterTestClass { public: testingprivRegisterTestClass(const char *name, void (*f)(testingT *)) { testingprivRegisterTest(name, f); } diff --git a/common/testing_testing.c b/common/testing_testing.c index 33e19bbe..24d22b0b 100644 --- a/common/testing_testing.c +++ b/common/testing_testing.c @@ -10,7 +10,8 @@ struct testingT { const char *name; void (*f)(testingT *); int failed; - jmp_buf failNowBuf; + int skipped; + jmp_buf returnNowBuf; testingT *next; }; @@ -34,6 +35,7 @@ int testingMain(void) int anyFailed; const char *status; + // TODO see if this should run if all tests are skipped if (tests == NULL) { fprintf(stderr, "warning: no tests to run\n"); // imitate Go here (TODO confirm this) @@ -43,13 +45,15 @@ int testingMain(void) anyFailed = 0; for (t = tests; t != NULL; t = t->next) { printf("=== RUN %s\n", t->name); - if (setjmp(t->failNowBuf) == 0) + if (setjmp(t->returnNowBuf) == 0) (*(t->f))(t); status = "PASS"; if (t->failed) { status = "FAIL"; anyFailed = 1; - } + } else if (t->skipped) + // note that failed overrides skipped + status = "SKIP"; printf("--- %s: %s (%s)\n", status, t->name, "TODO"); } @@ -61,7 +65,16 @@ int testingMain(void) return 0; } -static void testingprivTDoLog(testingT *t, const char *file, int line, const char *format, va_list ap) +void testingprivTLogfFull(testingT *t, const char *file, int line, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + testingprivTLogvfFull(t, file, line, format, ap); + va_end(ap); +} + +void testingprivTLogvfFull(testingT *t, const char *file, int line, const char *format, va_list ap) { // TODO extract filename from file printf("\t%s:%d: ", file, line); @@ -81,12 +94,18 @@ void testingprivTErrorfFull(testingT *t, const char *file, int line, const char void testingprivTErrorvfFull(testingT *t, const char *file, int line, const char *format, va_list ap) { - testingprivTDoLog(t, file, line, format, ap); + testingprivTLogvfFull(t, file, line, format, ap); t->failed = 1; } void testingprivTDoFailNow(testingT *t) { t->failed = 1; - longjmp(t->failNowBuf, 1); + longjmp(t->returnNowBuf, 1); +} + +void testingprivTDoSkipNow(testingT *t) +{ + t->skipped = 1; + longjmp(t->returnNowBuf, 1); } From 70db51d23bd90ab2a0474f265a0e770c6e5a4ee0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 3 Mar 2018 14:22:34 -0500 Subject: [PATCH 0866/1329] Added testingTFail() and simplified the implementation of the logging macros. --- common/testing.h | 29 +++++++++++++++++++++-------- common/testing_testing.c | 15 +++------------ 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/common/testing.h b/common/testing.h index 6a20a0a7..fe63197b 100644 --- a/common/testing.h +++ b/common/testing.h @@ -70,12 +70,23 @@ extern "C" { extern int testingMain(void); typedef struct testingT testingT; -#define testingTLogf(t, ...) (testingprivTLogfFull(t, __FILE__, __LINE__, __VA_ARGS__)) -#define testingTLogvf(t, format, ap) (testingprivTLogvfFull(t, __FILE__, __LINE__, format, ap)) -#define testingTErrorf(t, ...) (testingprivTErrorfFull(t, __FILE__, __LINE__, __VA_ARGS__)) -#define testingTErrorvf(t, format, ap) (testingprivTErrorvfFull(t, __FILE__, __LINE__, format, ap)) -#define testingTFatalf(t, ...) ((testingprivTErrorfFull(t, __FILE__, __LINE__, __VA_ARGS__)), (testingTFailNow(t))) -#define testingTFatalvf(t, format, ap) ((testingprivTErrorvfFull(t, __FILE__, __LINE__, format, ap)), (testingTFailNow(t))) +#define testingTLogf(t, ...) \ + testingprivExpand(testingprivTLogfThen((void), t, __VA_ARGS__)) +#define testingTLogvf(t, format, ap) \ + testingprivTLogvfThen((void), t, format, ap) +#define testingTErrorf(t, ...) \ + testingprivExpand(testingprivTLogfThen(testingTFail, t, __VA_ARGS__)) +#define testingTErrorvf(t, format, ap) \ + testingprivTLogvfThen(testingTFail, t, format, ap) +#define testingTFatalf(t, ...) \ + testingprivExpand(testingprivTLogfThen(testingTFailNow, t, __VA_ARGS__)) +#define testingTFatalvf(t, format, ap) \ + testingprivTLogvfThen(testingTFailNow, t, format, ap) +#define testingTSkipf(t, ...) \ + testingprivExpand(testingprivTLogfThen(testingTSkipNow, t, __VA_ARGS__)) +#define testingTSkipvf(t, format, ap) \ + testingprivTLogvfThen(testingTSkipNow, t, format, ap) +extern void testingTFail(testingT *t); #ifdef __cplusplus #define testingTFailNow(t) (throw testingprivFailNowException()) #define testingTSkipNow(t) (throw testingprivSkipNowException()) @@ -86,10 +97,12 @@ typedef struct testingT testingT; // TODO should __LINE__ arguments use intmax_t or uintmax_t instead of int? extern void testingprivRegisterTest(const char *, void (*)(testingT *)); +// see https://stackoverflow.com/questions/32399191/va-args-expansion-using-msvc +#define testingprivExpand(x) x +#define testingprivTLogfThen(then, t, ...) ((testingprivTLogfFull(t, __FILE__, __LINE__, __VA_ARGS__)), (then(t))) +#define testingprivTLogvfThen(then, t, format, ap) ((testingprivTLogvfFull(t, __FILE__, __LINE__, format, ap)), (then(t))) extern void testingprivTLogfFull(testingT *, const char *, int, const char *, ...); extern void testingprivTLogvfFull(testingT *, const char *, int, const char *, va_list); -extern void testingprivTErrorfFull(testingT *, const char *, int, const char *, ...); -extern void testingprivTErrorvfFull(testingT *, const char *, int, const char *, va_list); extern void testingprivTDoFailNow(testingT *); extern void testingprivTDoSkipNow(testingT *); diff --git a/common/testing_testing.c b/common/testing_testing.c index 24d22b0b..934daa5f 100644 --- a/common/testing_testing.c +++ b/common/testing_testing.c @@ -25,6 +25,7 @@ void testingprivRegisterTest(const char *name, void (*f)(testingT *)) t->name = name; t->f = f; t->failed = 0; + t->skipped = 0; t->next = tests; tests = t; } @@ -83,24 +84,14 @@ void testingprivTLogvfFull(testingT *t, const char *file, int line, const char * printf("\n"); } -void testingprivTErrorfFull(testingT *t, const char *file, int line, const char *format, ...) +void testingTFail(testingT *t) { - va_list ap; - - va_start(ap, format); - testingprivTErrorvfFull(t, file, line, format, ap); - va_end(ap); -} - -void testingprivTErrorvfFull(testingT *t, const char *file, int line, const char *format, va_list ap) -{ - testingprivTLogvfFull(t, file, line, format, ap); t->failed = 1; } void testingprivTDoFailNow(testingT *t) { - t->failed = 1; + testingTFail(t); longjmp(t->returnNowBuf, 1); } From 6c29932efe09a8cdfaaddeb028b87942b250eab6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 3 Mar 2018 16:24:10 -0500 Subject: [PATCH 0867/1329] Added testingTDefer(). Now we can actually write the rest of these tests. --- common/opentype_test.c | 22 +++++++++----------- common/testing.h | 2 ++ common/testing_testing.c | 44 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 54 insertions(+), 14 deletions(-) diff --git a/common/opentype_test.c b/common/opentype_test.c index 589353e8..c374154e 100644 --- a/common/opentype_test.c +++ b/common/opentype_test.c @@ -19,25 +19,23 @@ typedef uiForEach (*uiOpenTypeFeaturesForEachFunc)(const uiOpenTypeFeatures *otf #define uiprivFree free #include "opentype.c" +static void freeOpenType(void *otf) +{ + uiFreeOpenTypeFeatures((uiOpenTypeFeatures *) otf); +} + testingTest(OpenTypeFeaturesAddGet) { uiOpenTypeFeatures *otf; - char a, b, c, d; uint32_t value; otf = uiNewOpenTypeFeatures(); + testingTDefer(t, freeOpenType, otf); uiOpenTypeFeaturesAdd(otf, 'a', 'b', 'c', 'd', 12345); - if (!uiOpenTypeFeaturesGet(otf, 'a', 'b', 'c', 'd', &value)) { - testingTErrorf(t, "uiOpenTypeFeaturesGet() failed to get feature we added"); - goto out; - } - if (value != 12345) { - testingTErrorf(t, "feature abcd: got %" PRIu32 ", want 12345", value); - goto out; - } - -out: - uiFreeOpenTypeFeatures(otf); + if (!uiOpenTypeFeaturesGet(otf, 'a', 'b', 'c', 'd', &value)) + testingTFatalf(t, "uiOpenTypeFeaturesGet() failed to get feature we added"); + if (value != 12345) + testingTFatalf(t, "feature abcd: got %" PRIu32 ", want 12345", value); } int main(void) diff --git a/common/testing.h b/common/testing.h index fe63197b..15ea2629 100644 --- a/common/testing.h +++ b/common/testing.h @@ -94,6 +94,8 @@ extern void testingTFail(testingT *t); #define testingTFailNow(t) (testingprivTDoFailNow(t)) #define testingTSkipNow(t) (testingprivTDoSkipNow(t)) #endif +// TODO should the defered function also have t passed to it? +extern void testingTDefer(testingT *t, void (*f)(void *data), void *data); // TODO should __LINE__ arguments use intmax_t or uintmax_t instead of int? extern void testingprivRegisterTest(const char *, void (*)(testingT *)); diff --git a/common/testing_testing.c b/common/testing_testing.c index 934daa5f..6139ef75 100644 --- a/common/testing_testing.c +++ b/common/testing_testing.c @@ -6,12 +6,20 @@ #define testingprivNew(T) ((T *) malloc(sizeof (T))) +struct defer { + void (*f)(void *); + void *data; + struct defer *next; +}; + struct testingT { const char *name; void (*f)(testingT *); int failed; int skipped; jmp_buf returnNowBuf; + struct defer *defers; + int defersRun; testingT *next; }; @@ -26,10 +34,23 @@ void testingprivRegisterTest(const char *name, void (*f)(testingT *)) t->f = f; t->failed = 0; t->skipped = 0; + t->defers = NULL; + t->defersRun = 0; t->next = tests; tests = t; } +static void runDefers(testingT *t) +{ + struct defer *d; + + if (t->defersRun) + return; + t->defersRun = 1; + for (d = t->defers; d != NULL; d = d->next) + (*(d->f))(d->data); +} + int testingMain(void) { testingT *t; @@ -48,6 +69,7 @@ int testingMain(void) printf("=== RUN %s\n", t->name); if (setjmp(t->returnNowBuf) == 0) (*(t->f))(t); + runDefers(t); status = "PASS"; if (t->failed) { status = "FAIL"; @@ -89,14 +111,32 @@ void testingTFail(testingT *t) t->failed = 1; } +static void returnNow(testingT *t) +{ + // run defers before calling longjmp() just to be safe + runDefers(t); + longjmp(t->returnNowBuf, 1); +} + void testingprivTDoFailNow(testingT *t) { testingTFail(t); - longjmp(t->returnNowBuf, 1); + returnNow(t); } void testingprivTDoSkipNow(testingT *t) { t->skipped = 1; - longjmp(t->returnNowBuf, 1); + returnNow(t); +} + +void testingTDefer(testingT *t, void (*f)(void *data), void *data) +{ + struct defer *d; + + d = testingprivNew(struct defer); + d->f = f; + d->data = data; + d->next = t->defers; + t->defers = d; } From 4179ff86c2bb11009067c9c7168e696b2f84ce94 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 3 Mar 2018 17:01:40 -0500 Subject: [PATCH 0868/1329] Added more test cases. Also more TODOs in testing_testing.c. --- common/opentype_test.c | 77 ++++++++++++++++++++++++++++++++++++++-- common/testing_testing.c | 2 ++ 2 files changed, 76 insertions(+), 3 deletions(-) diff --git a/common/opentype_test.c b/common/opentype_test.c index c374154e..0c96108c 100644 --- a/common/opentype_test.c +++ b/common/opentype_test.c @@ -27,15 +27,86 @@ static void freeOpenType(void *otf) testingTest(OpenTypeFeaturesAddGet) { uiOpenTypeFeatures *otf; + int got; uint32_t value; otf = uiNewOpenTypeFeatures(); testingTDefer(t, freeOpenType, otf); uiOpenTypeFeaturesAdd(otf, 'a', 'b', 'c', 'd', 12345); - if (!uiOpenTypeFeaturesGet(otf, 'a', 'b', 'c', 'd', &value)) - testingTFatalf(t, "uiOpenTypeFeaturesGet() failed to get feature we added"); + got = uiOpenTypeFeaturesGet(otf, 'a', 'b', 'c', 'd', &value); + + if (!got) + testingTErrorf(t, "uiOpenTypeFeaturesGet() failed to get feature we added"); + else if (value != 12345) + testingTErrorf(t, "feature abcd: got %" PRIu32 ", want 12345", value); +} + +testingTest(OpenTypeFeaturesRemove) +{ + uiOpenTypeFeatures *otf; + uint32_t value; + + otf = uiNewOpenTypeFeatures(); + testingTDefer(t, freeOpenType, otf); + uiOpenTypeFeaturesAdd(otf, 'a', 'b', 'c', 'd', 12345); + uiOpenTypeFeaturesRemove(otf, 'a', 'b', 'c', 'd'); + + if (uiOpenTypeFeaturesGet(otf, 'a', 'b', 'c', 'd', &value)) + testingTErrorf(t, "uiOpenTypeFeaturesGet() succeeded in getting deleted feature; value %" PRIu32, value); +} + +testingTest(OpenTypeFeaturesCloneAdd) +{ + uiOpenTypeFeatures *otf, *otf2; + uint32_t value; + + otf = uiNewOpenTypeFeatures(); + testingTDefer(t, freeOpenType, otf); + uiOpenTypeFeaturesAdd(otf, 'a', 'b', 'c', 'd', 12345); + otf2 = uiOpenTypeFeaturesClone(otf); + testingTDefer(t, freeOpenType, otf2); + uiOpenTypeFeaturesAdd(otf2, 'q', 'w', 'e', 'r', 56789); + + if (uiOpenTypeFeaturesGet(otf, 'q', 'w', 'e', 'r', &value)) + testingTErrorf(t, "uiOpenTypeFeaturesGet() on original succeeded in getting feature added to clone; value %" PRIu32, value); +} + +testingTest(OpenTypeFeaturesCloneModify) +{ + uiOpenTypeFeatures *otf, *otf2; + uint32_t value; + + otf = uiNewOpenTypeFeatures(); + testingTDefer(t, freeOpenType, otf); + uiOpenTypeFeaturesAdd(otf, 'a', 'b', 'c', 'd', 12345); + otf2 = uiOpenTypeFeaturesClone(otf); + testingTDefer(t, freeOpenType, otf2); + uiOpenTypeFeaturesAdd(otf2, 'a', 'b', 'c', 'd', 56789); + + uiOpenTypeFeaturesGet(otf, 'a', 'b', 'c', 'd', &value); if (value != 12345) - testingTFatalf(t, "feature abcd: got %" PRIu32 ", want 12345", value); + testingTErrorf(t, "uiOpenTypeFeaturesGet() on original: got %" PRIu32 ", want 12345", value); + uiOpenTypeFeaturesGet(otf2, 'a', 'b', 'c', 'd', &value); + if (value != 56789) + testingTErrorf(t, "uiOpenTypeFeaturesGet() on clone: got %" PRIu32 ", want 56789", value); +} + +testingTest(OpenTypeFeaturesCloneRemove) +{ + uiOpenTypeFeatures *otf, *otf2; + uint32_t value; + + otf = uiNewOpenTypeFeatures(); + testingTDefer(t, freeOpenType, otf); + uiOpenTypeFeaturesAdd(otf, 'a', 'b', 'c', 'd', 12345); + otf2 = uiOpenTypeFeaturesClone(otf); + testingTDefer(t, freeOpenType, otf2); + uiOpenTypeFeaturesRemove(otf2, 'a', 'b', 'c', 'd'); + + if (uiOpenTypeFeaturesGet(otf2, 'a', 'b', 'c', 'd', &value)) + testingTErrorf(t, "uiOpenTypeFeaturesGet() on clone succeeded in getting feature removed from clone; value %" PRIu32, value); + if (!uiOpenTypeFeaturesGet(otf, 'a', 'b', 'c', 'd', &value)) + testingTErrorf(t, "uiOpenTypeFeaturesGet() on original failed to get feature removed from clone"); } int main(void) diff --git a/common/testing_testing.c b/common/testing_testing.c index 6139ef75..eb334648 100644 --- a/common/testing_testing.c +++ b/common/testing_testing.c @@ -36,6 +36,7 @@ void testingprivRegisterTest(const char *name, void (*f)(testingT *)) t->skipped = 0; t->defers = NULL; t->defersRun = 0; + // TODO add in the order called t->next = tests; tests = t; } @@ -137,6 +138,7 @@ void testingTDefer(testingT *t, void (*f)(void *data), void *data) d = testingprivNew(struct defer); d->f = f; d->data = data; + // add to the head of the list so defers are run in reverse order of how they were added d->next = t->defers; t->defers = d; } From 2822dbcebc8756c3699147a867fbd17cfcebf3d2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 3 Mar 2018 21:27:01 -0500 Subject: [PATCH 0869/1329] Decided to keep the existing attrlist.c, but updated it to the new attribute API and uipriv naming convention. Also resolved some TODOs along the way and decided that when in attribute lists, uiAttributes will be refcounted. --- common/OLD_uipriv_attrstr.h | 13 -- common/attribute.c | 31 ++++- common/{OLD_attrlist.c => attrlist.c} | 172 ++++++++------------------ common/attrstr.h | 14 +++ common/opentype.c | 5 +- 5 files changed, 98 insertions(+), 137 deletions(-) rename common/{OLD_attrlist.c => attrlist.c} (78%) diff --git a/common/OLD_uipriv_attrstr.h b/common/OLD_uipriv_attrstr.h index 6a1a63ce..cc67e276 100644 --- a/common/OLD_uipriv_attrstr.h +++ b/common/OLD_uipriv_attrstr.h @@ -17,19 +17,6 @@ extern size_t attrstrUTF8ToUTF16(uiAttributedString *s, size_t n); extern size_t *attrstrCopyUTF8ToUTF16(uiAttributedString *s, size_t *n); extern size_t *attrstrCopyUTF16ToUTF8(uiAttributedString *s, size_t *n); -// attrlist.c -struct attrlist; -extern void attrlistInsertAttribute(struct attrlist *alist, uiAttributeSpec *spec, size_t start, size_t end); -extern void attrlistInsertCharactersUnattributed(struct attrlist *alist, size_t start, size_t count); -extern void attrlistInsertCharactersExtendingAttributes(struct attrlist *alist, size_t start, size_t count); -extern void attrlistRemoveAttribute(struct attrlist *alist, uiAttribute type, size_t start, size_t end); -extern void attrlistRemoveAttributes(struct attrlist *alist, size_t start, size_t end); -extern void attrlistRemoveCharacters(struct attrlist *alist, size_t start, size_t end); -extern void attrlistForEach(struct attrlist *alist, uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); -// TODO move these to the top like everythng else -extern struct attrlist *attrlistNew(void); -extern void attrlistFree(struct attrlist *alist); - // drawtext.c struct caretDrawParams { double r; diff --git a/common/attribute.c b/common/attribute.c index 66120e99..da137349 100644 --- a/common/attribute.c +++ b/common/attribute.c @@ -1,8 +1,11 @@ // 19 february 2018 +#include "../ui.h" #include "uipriv.h" #include "attrstr.h" struct uiAttribute { + int owned; + size_t refcount; uiAttributeType type; union { const char *family; @@ -28,11 +31,21 @@ static uiAttribute *newAttribute(uiAttributeType type) uiAttribute *a; a = uiprivNew(uiAttribute); + a->ownedByUser = 1; + a->refcount = 0; a->type = type; return a; } -void uiFreeAttribute(uiAttribute *a) +// returns a to allow expressions like b = uiprivAttributeRetain(a) +uiAttribute *uiprivAttributeRetain(uiAttribute *a) +{ + a->ownedByUser = 0; + a->refcount++; + return a; +} + +static void destroy(uiAttribute *a) { switch (a->type) { case uiAttributeTypeFamily: @@ -45,6 +58,22 @@ void uiFreeAttribute(uiAttribute *a) uiprivFree(a); } +void uiprivAttributeRelease(uiAttribute *a) +{ + if (a->ownedByUser) + /* TODO implementation bug: we can't release an attribute we don't own */; + a->refcount--; + if (a->refcount == 0) + destroy(a); +} + +void uiFreeAttribute(uiAttribute *a) +{ + if (!a->ownedByUser) + /* TODO user bug: you can't free an attribute you don't own */; + destroy(a); +} + uiAttributeType uiAttributeGetType(const uiAttribute *a) { return a->type; diff --git a/common/OLD_attrlist.c b/common/attrlist.c similarity index 78% rename from common/OLD_attrlist.c rename to common/attrlist.c index 53217754..4fdb86f8 100644 --- a/common/OLD_attrlist.c +++ b/common/attrlist.c @@ -1,6 +1,7 @@ // 16 december 2016 #include "../ui.h" #include "uipriv.h" +#include "attrstr.h" /* An attribute list is a doubly linked list of attributes. @@ -12,62 +13,21 @@ The linked list is not a ring; alist->fist->prev == NULL and alist->last->next = TODO verify that this disallows attributes of length zero */ -struct attr { - uiAttributeSpec spec; +struct uiprivAttrList { + uiAttribute *val; size_t start; size_t end; struct attr *prev; struct attr *next; }; -struct attrlist { +uiprivAttrList { struct attr *first; struct attr *last; }; -// We need to make local copies of any pointers in uiAttributeSpec. -// If we don't do this, we'll wind up leaking stuff, or worse, prematurely freeing stuff. -// TODO ensure this is docmented -static void attrSetSpec(struct attr *a, uiAttributeSpec *spec) -{ - const char *family; - char *familyCopy; - - a->spec = *spec; - switch (a->spec.Type) { - case uiAttributeFamily: - // TODO UTF-8 validate this? - family = a->spec.Family; - familyCopy = (char *) uiAlloc((strlen(family) + 1) * sizeof (char), "char[] (uiAttributeSpec.Family copy)"); - strcpy(familyCopy, family); - a->spec.Family = familyCopy; - break; - case uiAttributeFeatures: - a->spec.Features = uiOpenTypeFeaturesClone(a->spec.Features); - break; - } -} - -static void attrCopySpec(struct attr *dest, struct attr *src) -{ - attrSetSpec(dest, &(src->spec)); -} - -// Likewise, this is needed to clean up a spec with a pointer when finished. -static void attrClearSpec(struct attr *a) -{ - switch (a->spec.Type) { - case uiAttributeFamily: - uiFree((char *) (a->spec.Family)); - break; - case uiAttributeFeatures: - uiFreeOpenTypeFeatures((uiOpenTypeFeatures *) (a->spec.Features)); - break; - } -} - // if before is NULL, add to the end of the list -static void attrInsertBefore(struct attrlist *alist, struct attr *a, struct attr *before) +static void attrInsertBefore(uiprivAttrList *alist, struct attr *a, struct attr *before) { // if the list is empty, this is the first item if (alist->first == NULL) { @@ -131,7 +91,7 @@ static int attrRangeIntersect(struct attr *a, size_t *start, size_t *end) } // returns the old a->next, for forward iteration -static struct attr *attrUnlink(struct attrlist *alist, struct attr *a) +static struct attr *attrUnlink(uiprivAttrList *alist, struct attr *a) { struct attr *p, *n; @@ -165,13 +125,13 @@ static struct attr *attrUnlink(struct attrlist *alist, struct attr *a) } // returns the old a->next, for forward iteration -static struct attr *attrDelete(struct attrlist *alist, struct attr *a) +static struct attr *attrDelete(uiprivAttrList *alist, struct attr *a) { struct attr *next; next = attrUnlink(alist, a); - attrClearSpec(a); - uiFree(a); + uiprivAttributeRelease(a->val); + uiprivFree(a); return next; } @@ -188,7 +148,7 @@ static struct attr *attrDelete(struct attrlist *alist, struct attr *a) // Otherwise, the attribute needs to be split. The existing attribute is adjusted to make the left half and a new attribute with the right half. This attribute is kept unlinked and returned in tail. // // In all cases, the return value is the next attribute to look at in a forward sequential loop. -static struct attr *attrDropRange(struct attrlist *alist, struct attr *a, size_t start, size_t end, struct attr **tail) +static struct attr *attrDropRange(uiprivAttrList *alist, struct attr *a, size_t start, size_t end, struct attr **tail) { struct attr *b; @@ -219,8 +179,8 @@ static struct attr *attrDropRange(struct attrlist *alist, struct attr *a, size_t } // we'll need to split the attribute into two - b = uiNew(struct attr); - attrCopySpec(b, a); + b = uiprivNew(struct attr); + b->val = uiprivAttributeRetain(a->val); b->start = end; b->end = a->end; *tail = b; @@ -229,7 +189,7 @@ static struct attr *attrDropRange(struct attrlist *alist, struct attr *a, size_t return a->next; } -static void attrGrow(struct attrlist *alist, struct attr *a, size_t start, size_t end) +static void attrGrow(uiprivAttrList *alist, struct attr *a, size_t start, size_t end) { struct attr *before; @@ -251,7 +211,7 @@ static void attrGrow(struct attrlist *alist, struct attr *a, size_t start, size_ } // returns the right side of the split, which is unlinked, or NULL if no split was done -static struct attr *attrSplitAt(struct attrlist *alist, struct attr *a, size_t at) +static struct attr *attrSplitAt(uiprivAttrList *alist, struct attr *a, size_t at) { struct attr *b; @@ -263,8 +223,8 @@ static struct attr *attrSplitAt(struct attrlist *alist, struct attr *a, size_t a if (at >= a->end) return NULL; - b = uiNew(struct attr); - attrCopySpec(b, a); + b = uiprivNew(struct attr); + b->val = uiprivAttributeRetain(a->val); b->start = at; b->end = a->end; @@ -282,7 +242,7 @@ static struct attr *attrSplitAt(struct attrlist *alist, struct attr *a, size_t a // // In all cases, the return value is the next attribute to look at in a forward sequential loop. // TODO rewrite this comment -static struct attr *attrDeleteRange(struct attrlist *alist, struct attr *a, size_t start, size_t end) +static struct attr *attrDeleteRange(uiprivAttrList *alist, struct attr *a, size_t start, size_t end) { size_t ostart, oend; size_t count; @@ -327,48 +287,37 @@ static struct attr *attrDeleteRange(struct attrlist *alist, struct attr *a, size return a->next; } -static int specsIdentical(struct attr *attr, uiAttributeSpec *spec) +uiprivAttrList *uiprivNewAttrList(void) { - if (attr->spec.Type != spec->Type) - return 0; - switch (attr->spec.Type) { - case uiAttributeFamily: - // TODO should this be case-insensitive? - return strcmp(attr->spec.Family, spec->Family) == 0; - case uiAttributeSize: - // TODO use a closest match? - return attr->spec.Double == spec->Double; - case uiAttributeUnderlineColor: - if (attr->spec.Value != spec->Value) - return 0; - if (attr->spec.Value != uiDrawUnderlineColorCustom) - return 1; - // otherwise fall through - case uiAttributeColor: - case uiAttributeBackground: - // TODO use a closest match? - return attr->spec.R == spec->R && - attr->spec.G == spec->G && - attr->spec.B == spec->B && - attr->spec.A == spec->A; - case uiAttributeFeatures: - // TODO rename it to uiAttributeOpenTypeFeatures? - return uiOpenTypeFeaturesEqual(attr->spec.Features, spec->Features); - } - // handles the rest - return attr->spec.Value == spec->Value; + return uiprivNew(uiprivAttrList); } -void attrlistInsertAttribute(struct attrlist *alist, uiAttributeSpec *spec, size_t start, size_t end) +void uiprivFreeAttrList(uiprivAttrList *alist) +{ + struct attr *a, *next; + + a = alist->first; + while (a != NULL) { + next = a->next; + uiprivAttributeRelease(a->val); + uiprivFree(a); + a = next; + } + uiprivFree(alist); +} + +void uiprivAttrListInsertAttribute(uiprivAttrList *alist, uiAttribute *val, size_t start, size_t end) { struct attr *a; struct attr *before; struct attr *tail = NULL; int split = 0; + uiAttributeType valtype; // first, figure out where in the list this should go // in addition, if this attribute overrides one that already exists, split that one apart so this one can take over before = alist->first; + valtype = uiAttributeGetType(val); while (before != NULL) { size_t lstart, lend; @@ -380,8 +329,8 @@ void attrlistInsertAttribute(struct attrlist *alist, uiAttributeSpec *spec, size if (split) goto next; - // should we split this? - if (before->spec.Type != spec->Type) + // should we split this attribute? + if (uiAttributeGetType(before->val) != valtype) goto next; lstart = start; lend = end; @@ -390,7 +339,7 @@ void attrlistInsertAttribute(struct attrlist *alist, uiAttributeSpec *spec, size // okay so this might conflict; if the val is the same as the one we want, we need to expand the existing attribute, not fragment anything // TODO will this reduce fragmentation if we first add from 0 to 2 and then from 2 to 4? or do we have to do that separately? - if (specsIdentical(before, spec)) { + if (uiprivAttributeEqual(before->val, val)) { attrGrow(alist, before, start, end); return; } @@ -404,8 +353,8 @@ void attrlistInsertAttribute(struct attrlist *alist, uiAttributeSpec *spec, size } // if we got here, we know we have to add the attribute before before - a = uiNew(struct attr); - attrSetSpec(a, spec); + a = uiprivNew(struct attr); + a->val = uiprivAttributeRetain(val); a->start = start; a->end = end; attrInsertBefore(alist, a, before); @@ -420,7 +369,7 @@ void attrlistInsertAttribute(struct attrlist *alist, uiAttributeSpec *spec, size attrInsertBefore(alist, tail, before); } -void attrlistInsertCharactersUnattributed(struct attrlist *alist, size_t start, size_t count) +void uiprivAttrListInsertCharactersUnattributed(uiprivAttrList *alist, size_t start, size_t count) { struct attr *a; struct attr *tails = NULL; @@ -533,7 +482,7 @@ which results in our algorithm: move end up */ // TODO does this ensure the list remains sorted? -void attrlistInsertCharactersExtendingAttributes(struct attrlist *alist, size_t start, size_t count) +void uiprivAttrListInsertCharactersExtendingAttributes(uiprivAttrList *alist, size_t start, size_t count) { struct attr *a; @@ -549,10 +498,10 @@ void attrlistInsertCharactersExtendingAttributes(struct attrlist *alist, size_t // TODO replace at point with — replaces with first character's attributes -void attrlistRemoveAttribute(struct attrlist *alist, uiAttribute type, size_t start, size_t end) +void uiprivAttrListRemoveAttribute(uiprivAttrList *alist, uiAttributeType type, size_t start, size_t end) { struct attr *a; - struct attr *tails = NULL; // see attrlistInsertCharactersUnattributed() above + struct attr *tails = NULL; // see uiprivAttrListInsertCharactersUnattributed() above struct attr *tailsAt = NULL; a = alist->first; @@ -567,7 +516,7 @@ void attrlistRemoveAttribute(struct attrlist *alist, uiAttribute type, size_t st // and at this point we're done, so break; } - if (a->spec.Type != type) + if (uiAttributeGetType(a->val) != type) goto next; lstart = start; lend = end; @@ -596,10 +545,10 @@ void attrlistRemoveAttribute(struct attrlist *alist, uiAttribute type, size_t st } // TODO merge this with the above -void attrlistRemoveAttributes(struct attrlist *alist, size_t start, size_t end) +void uiprivAttrListRemoveAttributes(uiprivAttrList *alist, size_t start, size_t end) { struct attr *a; - struct attr *tails = NULL; // see attrlistInsertCharactersUnattributed() above + struct attr *tails = NULL; // see uiprivAttrListInsertCharactersUnattributed() above struct attr *tailsAt = NULL; a = alist->first; @@ -640,7 +589,7 @@ void attrlistRemoveAttributes(struct attrlist *alist, size_t start, size_t end) } } -void attrlistRemoveCharacters(struct attrlist *alist, size_t start, size_t end) +void uiprivAttrListRemoveCharacters(uiprivAttrList *alist, size_t start, size_t end) { struct attr *a; @@ -649,34 +598,15 @@ void attrlistRemoveCharacters(struct attrlist *alist, size_t start, size_t end) a = attrDeleteRange(alist, a, start, end); } -void attrlistForEach(struct attrlist *alist, uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data) +void uiprivAttrListForEach(uiprivAttrList *alist, uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data) { struct attr *a; uiForEach ret; for (a = alist->first; a != NULL; a = a->next) { - ret = (*f)(s, &(a->spec), a->start, a->end, data); + ret = (*f)(s, a->val, a->start, a->end, data); if (ret == uiForEachStop) // TODO for all: break or return? break; } } - -struct attrlist *attrlistNew(void) -{ - return uiNew(struct attrlist); -} - -void attrlistFree(struct attrlist *alist) -{ - struct attr *a, *next; - - a = alist->first; - while (a != NULL) { - next = a->next; - attrClearSpec(a); - uiFree(a); - a = next; - } - uiFree(alist); -} diff --git a/common/attrstr.h b/common/attrstr.h index f835a8a4..3017111b 100644 --- a/common/attrstr.h +++ b/common/attrstr.h @@ -1,7 +1,21 @@ // 19 february 2018 // attribute.c +extern uiAttribute *uiprivAttributeRetain(uiAttribute *a); +extern void uiprivAttributeRelease(uiAttribute *a); extern int uiprivAttributeEqual(const uiAttribute *a, const uiAttribute *b); // opentype.c extern int uiprivOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeatures *b); + +// attrlist.c +typedef struct uiprivAttrList uiprivAttrList; +extern uiprivAttrList *uiprivNewAttrList(void); +extern void uiprivFreeAttrList(uiprivAttrList *alist); +extern void uiprivAttrListInsertAttribute(uiprivAttrList *alist, uiAttribute *val, size_t start, size_t end); +extern void uiprivAttrListInsertCharactersUnattributed(uiprivAttrList *alist, size_t start, size_t count); +extern void uiprivAttrListInsertCharactersExtendingAttributes(uiprivAttrList *alist, size_t start, size_t count); +extern void uiprivAttrListRemoveAttribute(uiprivAttrList *alist, uiAttribute type, size_t start, size_t end); +extern void uiprivAttrListRemoveAttributes(uiprivAttrList *alist, size_t start, size_t end); +extern void uiprivAttrListRemoveCharacters(uiprivAttrList *alist, size_t start, size_t end); +extern void uiprivAttrListForEach(uiprivAttrList *alist, uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); diff --git a/common/opentype.c b/common/opentype.c index f27907f6..625f5852 100644 --- a/common/opentype.c +++ b/common/opentype.c @@ -1,6 +1,7 @@ // 25 february 2018 -//TODO#include "uipriv.h" -//TODO#include "attrstr.h" +#include "../ui.h" +#include "uipriv.h" +#include "attrstr.h" struct feature { char a; From dcf67262396d2021c19b9b169dae4422c7e65631 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 3 Mar 2018 21:29:45 -0500 Subject: [PATCH 0870/1329] More TODOs. --- common/attribute.c | 1 + 1 file changed, 1 insertion(+) diff --git a/common/attribute.c b/common/attribute.c index da137349..79273bef 100644 --- a/common/attribute.c +++ b/common/attribute.c @@ -38,6 +38,7 @@ static uiAttribute *newAttribute(uiAttributeType type) } // returns a to allow expressions like b = uiprivAttributeRetain(a) +// TODO would this allow us to copy attributes between strings in a foreach func, and if so, should that be allowed? uiAttribute *uiprivAttributeRetain(uiAttribute *a) { a->ownedByUser = 0; From 3337f06e2e6b147daa949acfa7eb649d833f12a8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 3 Mar 2018 21:32:18 -0500 Subject: [PATCH 0871/1329] Oops. --- common/attrlist.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/attrlist.c b/common/attrlist.c index 4fdb86f8..7a85aff9 100644 --- a/common/attrlist.c +++ b/common/attrlist.c @@ -13,7 +13,7 @@ The linked list is not a ring; alist->fist->prev == NULL and alist->last->next = TODO verify that this disallows attributes of length zero */ -struct uiprivAttrList { +struct attr { uiAttribute *val; size_t start; size_t end; @@ -21,7 +21,7 @@ struct uiprivAttrList { struct attr *next; }; -uiprivAttrList { +struct uiprivAttrList { struct attr *first; struct attr *last; }; From 766f3a0cb2ac763754aaf3bb08aada8bc5641591 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 3 Mar 2018 21:35:29 -0500 Subject: [PATCH 0872/1329] Moved the unit tests out of the way for now. We'll fill them in later. --- {common => _future/unittest}/opentype_test.c | 0 {common => _future/unittest}/testing.h | 0 {common => _future/unittest}/testing_testing.c | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename {common => _future/unittest}/opentype_test.c (100%) rename {common => _future/unittest}/testing.h (100%) rename {common => _future/unittest}/testing_testing.c (100%) diff --git a/common/opentype_test.c b/_future/unittest/opentype_test.c similarity index 100% rename from common/opentype_test.c rename to _future/unittest/opentype_test.c diff --git a/common/testing.h b/_future/unittest/testing.h similarity index 100% rename from common/testing.h rename to _future/unittest/testing.h diff --git a/common/testing_testing.c b/_future/unittest/testing_testing.c similarity index 100% rename from common/testing_testing.c rename to _future/unittest/testing_testing.c From f025783632641d67af81728d6b964d1f54ef7496 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 3 Mar 2018 22:02:18 -0500 Subject: [PATCH 0873/1329] Migrated attrstr.c back like we just did to attrlist.c. RIP "graphemes()" --- common/OLD_uipriv_attrstr.h | 7 ----- common/{OLD_attrstr.c => attrstr.c} | 40 +++++++++++++++-------------- common/attrstr.h | 7 +++++ 3 files changed, 28 insertions(+), 26 deletions(-) rename common/{OLD_attrstr.c => attrstr.c} (87%) diff --git a/common/OLD_uipriv_attrstr.h b/common/OLD_uipriv_attrstr.h index cc67e276..f2a1b936 100644 --- a/common/OLD_uipriv_attrstr.h +++ b/common/OLD_uipriv_attrstr.h @@ -10,13 +10,6 @@ extern struct graphemes *graphemes(void *s, size_t len); // TODO split these into a separate header file? -// attrstr.c -extern const uint16_t *attrstrUTF16(uiAttributedString *s); -extern size_t attrstrUTF16Len(uiAttributedString *s); -extern size_t attrstrUTF8ToUTF16(uiAttributedString *s, size_t n); -extern size_t *attrstrCopyUTF8ToUTF16(uiAttributedString *s, size_t *n); -extern size_t *attrstrCopyUTF16ToUTF8(uiAttributedString *s, size_t *n); - // drawtext.c struct caretDrawParams { double r; diff --git a/common/OLD_attrstr.c b/common/attrstr.c similarity index 87% rename from common/OLD_attrstr.c rename to common/attrstr.c index e34b7d27..7b774cfc 100644 --- a/common/OLD_attrstr.c +++ b/common/attrstr.c @@ -1,12 +1,13 @@ // 3 december 2016 #include "../ui.h" #include "uipriv.h" +#include "attrstr.h" struct uiAttributedString { char *s; size_t len; - struct attrlist *attrs; + uiprivAttrList *attrs; // indiscriminately keep a UTF-16 copy of the string on all platforms so we can hand this off to the grapheme calculator // this ensures no one platform has a speed advantage (sorry GTK+) @@ -17,7 +18,7 @@ struct uiAttributedString { size_t *u16tou8; // this is lazily created to keep things from getting *too* slow - struct graphemes *graphemes; + uiprivGraphemes *graphemes; }; static void resize(uiAttributedString *s, size_t u8, size_t u16) @@ -35,21 +36,21 @@ uiAttributedString *uiNewAttributedString(const char *initialString) uiAttributedString *s; s = uiNew(uiAttributedString); - s->attrs = attrlistNew(); + s->attrs = uiprivNewAttrList(); uiAttributedStringAppendUnattributed(s, initialString); return s; } -// TODO make sure that all implementations of graphemes() work fine with empty strings; in particular, the Windows one might not +// TODO make sure that all implementations of uiprivNewGraphemes() work fine with empty strings; in particular, the Windows one might not static void recomputeGraphemes(uiAttributedString *s) { if (s->graphemes != NULL) return; - if (graphemesTakesUTF16()) { - s->graphemes = graphemes(s->u16, s->u16len); + if (uiprivGraphemesTakesUTF16()) { + s->graphemes = uiprivNewGraphemes(s->u16, s->u16len); return; } - s->graphemes = graphemes(s->s, s->len); + s->graphemes = uiprivNewGraphemes(s->s, s->len); } static void invalidateGraphemes(uiAttributedString *s) @@ -64,7 +65,7 @@ static void invalidateGraphemes(uiAttributedString *s) void uiFreeAttributedString(uiAttributedString *s) { - attrlistFree(s->attrs); + uiprivFreeAttrList(s->attrs); invalidateGraphemes(s); uiFree(s->u16tou8); uiFree(s->u8tou16); @@ -94,6 +95,7 @@ static void u8u16len(const char *str, size_t *n8, size_t *n16) while (*str) { str = utf8DecodeRune(str, 0, &rune); // TODO document the use of the function vs a pointer subtract here + // TODO also we need to consider namespace collision with utf.h... *n8 += utf8EncodeRune(rune, buf); *n16 += utf16EncodeRune(rune, buf16); } @@ -216,7 +218,7 @@ void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *s s->u16tou8[at16 + oldn16 + i] += s->len - oldlen; // and finally do the attributes - attrlistInsertCharactersUnattributed(s->attrs, at, n8); + uiprivAttrListInsertCharactersUnattributed(s->attrs, at, n8); } // TODO document that end is the first index that will be maintained @@ -271,7 +273,7 @@ void uiAttributedStringDelete(uiAttributedString *s, size_t start, size_t end) s->u16[start16 + count16] = 0; // fix up attributes - attrlistRemoveCharacters(s->attrs, start, end); + uiprivAttrListRemoveCharacters(s->attrs, start, end); // and finally resize resize(s, start + count, start16 + count16); @@ -287,7 +289,7 @@ size_t uiAttributedStringNumGraphemes(uiAttributedString *s) size_t uiAttributedStringByteIndexToGrapheme(uiAttributedString *s, size_t pos) { recomputeGraphemes(s); - if (graphemesTakesUTF16()) + if (uiprivGraphemesTakesUTF16()) pos = s->u8tou16[pos]; return s->graphemes->pointsToGraphemes[pos]; } @@ -296,41 +298,41 @@ size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos) { recomputeGraphemes(s); pos = s->graphemes->graphemesToPoints[pos]; - if (graphemesTakesUTF16()) + if (uiprivGraphemesTakesUTF16()) pos = s->u16tou8[pos]; return pos; } void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end) { - attrlistInsertAttribute(s->attrs, spec, start, end); + uiprivAttrListInsertAttribute(s->attrs, spec, start, end); } // LONGTERM introduce an iterator object instead? void uiAttributedStringForEachAttribute(uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data) { - attrlistForEach(s->attrs, s, f, data); + uiprivAttrListForEach(s->attrs, s, f, data); } // helpers for platform-specific code -const uint16_t *attrstrUTF16(uiAttributedString *s) +const uint16_t *uiprivAttributedStringUTF16String(uiAttributedString *s) { return s->u16; } -size_t attrstrUTF16Len(uiAttributedString *s) +size_t uiprivAttributedStringUTF16Len(uiAttributedString *s) { return s->u16len; } // TODO is this still needed given the below? -size_t attrstrUTF8ToUTF16(uiAttributedString *s, size_t n) +size_t uiprivAttributedStringUTF8ToUTF16(uiAttributedString *s, size_t n) { return s->u8tou16[n]; } -size_t *attrstrCopyUTF8ToUTF16(uiAttributedString *s, size_t *n) +size_t *uiprivAttributedStringCopyUTF8ToUTF16Table(uiAttributedString *s, size_t *n) { size_t *out; size_t nbytes; @@ -342,7 +344,7 @@ size_t *attrstrCopyUTF8ToUTF16(uiAttributedString *s, size_t *n) return out; } -size_t *attrstrCopyUTF16ToUTF8(uiAttributedString *s, size_t *n) +size_t *uiprivAttributedStringCopyUTF16ToUTF8Table(uiAttributedString *s, size_t *n) { size_t *out; size_t nbytes; diff --git a/common/attrstr.h b/common/attrstr.h index 3017111b..b71e1864 100644 --- a/common/attrstr.h +++ b/common/attrstr.h @@ -19,3 +19,10 @@ extern void uiprivAttrListRemoveAttribute(uiprivAttrList *alist, uiAttribute typ extern void uiprivAttrListRemoveAttributes(uiprivAttrList *alist, size_t start, size_t end); extern void uiprivAttrListRemoveCharacters(uiprivAttrList *alist, size_t start, size_t end); extern void uiprivAttrListForEach(uiprivAttrList *alist, uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); + +// attrstr.c +extern const uint16_t *uiprivAttributedStringUTF16String(uiAttributedString *s); +extern size_t uiprivAttributedStringUTF16Len(uiAttributedString *s); +extern size_t uiprivAttributedStringUTF8ToUTF16(uiAttributedString *s, size_t n); +extern size_t *uiprivAttributedStringCopyUTF8ToUTF16Table(uiAttributedString *s, size_t *n); +extern size_t *uiprivAttributedStringCopyUTF16ToUTF8Table(uiAttributedString *s, size_t *n); From 77c07075e336e793a5c7de651fa4a03cad78b829 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 4 Mar 2018 10:46:00 -0500 Subject: [PATCH 0874/1329] Updated grapheme function names in attrsr.h and updated allocator function names in attrstr.c. --- common/OLD_uipriv_attrstr.h | 8 -------- common/attrstr.c | 34 +++++++++++++++++----------------- common/attrstr.h | 9 +++++++++ 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/common/OLD_uipriv_attrstr.h b/common/OLD_uipriv_attrstr.h index f2a1b936..ec920954 100644 --- a/common/OLD_uipriv_attrstr.h +++ b/common/OLD_uipriv_attrstr.h @@ -1,12 +1,4 @@ -// for attrstr.c -struct graphemes { - size_t len; - size_t *pointsToGraphemes; - size_t *graphemesToPoints; -}; -extern int graphemesTakesUTF16(void); -extern struct graphemes *graphemes(void *s, size_t len); // TODO split these into a separate header file? diff --git a/common/attrstr.c b/common/attrstr.c index 7b774cfc..73246651 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -18,24 +18,24 @@ struct uiAttributedString { size_t *u16tou8; // this is lazily created to keep things from getting *too* slow - uiprivGraphemes *graphemes; + struct graphemes *graphemes; }; static void resize(uiAttributedString *s, size_t u8, size_t u16) { s->len = u8; - s->s = (char *) uiRealloc(s->s, (s->len + 1) * sizeof (char), "char[] (uiAttributedString)"); - s->u8tou16 = (size_t *) uiRealloc(s->u8tou16, (s->len + 1) * sizeof (size_t), "size_t[] (uiAttributedString)"); + s->s = (char *) uiprivRealloc(s->s, (s->len + 1) * sizeof (char), "char[] (uiAttributedString)"); + s->u8tou16 = (size_t *) uiprivRealloc(s->u8tou16, (s->len + 1) * sizeof (size_t), "size_t[] (uiAttributedString)"); s->u16len = u16; - s->u16 = (uint16_t *) uiRealloc(s->u16, (s->u16len + 1) * sizeof (uint16_t), "uint16_t[] (uiAttributedString)"); - s->u16tou8 = (size_t *) uiRealloc(s->u16tou8, (s->u16len + 1) * sizeof (size_t), "size_t[] (uiAttributedString)"); + s->u16 = (uint16_t *) uiprivRealloc(s->u16, (s->u16len + 1) * sizeof (uint16_t), "uint16_t[] (uiAttributedString)"); + s->u16tou8 = (size_t *) uiprivRealloc(s->u16tou8, (s->u16len + 1) * sizeof (size_t), "size_t[] (uiAttributedString)"); } uiAttributedString *uiNewAttributedString(const char *initialString) { uiAttributedString *s; - s = uiNew(uiAttributedString); + s = uiprivNew(uiAttributedString); s->attrs = uiprivNewAttrList(); uiAttributedStringAppendUnattributed(s, initialString); return s; @@ -57,9 +57,9 @@ static void invalidateGraphemes(uiAttributedString *s) { if (s->graphemes == NULL) return; - uiFree(s->graphemes->pointsToGraphemes); - uiFree(s->graphemes->graphemesToPoints); - uiFree(s->graphemes); + uiprivFree(s->graphemes->pointsToGraphemes); + uiprivFree(s->graphemes->graphemesToPoints); + uiprivFree(s->graphemes); s->graphemes = NULL; } @@ -67,11 +67,11 @@ void uiFreeAttributedString(uiAttributedString *s) { uiprivFreeAttrList(s->attrs); invalidateGraphemes(s); - uiFree(s->u16tou8); - uiFree(s->u8tou16); - uiFree(s->u16); - uiFree(s->s); - uiFree(s); + uiprivFree(s->u16tou8); + uiprivFree(s->u8tou16); + uiprivFree(s->u16); + uiprivFree(s->s); + uiprivFree(s); } const char *uiAttributedStringString(const uiAttributedString *s) @@ -106,7 +106,7 @@ void uiAttributedStringAppendUnattributed(uiAttributedString *s, const char *str uiAttributedStringInsertAtUnattributed(s, str, s->len); } -// this works (and returns true, which is what we want) at s->len too because s->s[s->len] is always going to be 0 due to us allocating s->len + 1 bytes and because uiRealloc() always zero-fills allocated memory +// this works (and returns true, which is what we want) at s->len too because s->s[s->len] is always going to be 0 due to us allocating s->len + 1 bytes and because uiprivRealloc() always zero-fills allocated memory static int onCodepointBoundary(uiAttributedString *s, size_t at) { uint8_t c; @@ -339,7 +339,7 @@ size_t *uiprivAttributedStringCopyUTF8ToUTF16Table(uiAttributedString *s, size_t nbytes = (s->len + 1) * sizeof (size_t); *n = s->len; - out = (size_t *) uiAlloc(nbytes, "size_t[] (uiAttributedString)"); + out = (size_t *) uiprivAlloc(nbytes, "size_t[] (uiAttributedString)"); memmove(out, s->u8tou16, nbytes); return out; } @@ -351,7 +351,7 @@ size_t *uiprivAttributedStringCopyUTF16ToUTF8Table(uiAttributedString *s, size_t nbytes = (s->u16len + 1) * sizeof (size_t); *n = s->u16len; - out = (size_t *) uiAlloc(nbytes, "size_t[] (uiAttributedString)"); + out = (size_t *) uiprivAlloc(nbytes, "size_t[] (uiAttributedString)"); memmove(out, s->u16tou8, nbytes); return out; } diff --git a/common/attrstr.h b/common/attrstr.h index b71e1864..b0192b92 100644 --- a/common/attrstr.h +++ b/common/attrstr.h @@ -26,3 +26,12 @@ extern size_t uiprivAttributedStringUTF16Len(uiAttributedString *s); extern size_t uiprivAttributedStringUTF8ToUTF16(uiAttributedString *s, size_t n); extern size_t *uiprivAttributedStringCopyUTF8ToUTF16Table(uiAttributedString *s, size_t *n); extern size_t *uiprivAttributedStringCopyUTF16ToUTF8Table(uiAttributedString *s, size_t *n); + +// per-OS graphemes.c/graphemes.cpp/graphemes.m/etc. +struct graphemes { + size_t len; + size_t *pointsToGraphemes; + size_t *graphemesToPoints; +}; +extern int uiprivGraphemesTakesUTF16(void); +extern struct graphemes *uiprivNewGraphemes(void *s, size_t len); From 9c8f6849c3580654490c5467bcb67b06e6cf514f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 4 Mar 2018 11:15:18 -0500 Subject: [PATCH 0875/1329] Synced const-correctness in ui_attrstr.h to attrstr.c, propagated that to the necessary private functions, and added some references to testing.h. --- _future/unittest/testing.h | 4 ++++ common/attrlist.c | 2 +- common/attrstr.c | 22 +++++++++++----------- common/attrstr.h | 2 +- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/_future/unittest/testing.h b/_future/unittest/testing.h index 15ea2629..c2d81690 100644 --- a/_future/unittest/testing.h +++ b/_future/unittest/testing.h @@ -43,6 +43,10 @@ extern "C" { static inline void testingprivScaffold ## name(testingT *t) { name(t); } #endif +// references: +// - https://gitlab.gnome.org/GNOME/glib/blob/master/glib/gconstructor.h +// - https://gitlab.gnome.org/GNOME/glib/blob/master/gio/glib-compile-resources.c +// - https://msdn.microsoft.com/en-us/library/bb918180.aspx #if defined(__cplusplus) #define testingprivMkCtor(name, reg) \ static reg ## Class testingprivCtor ## name(#name, testingprivScaffold ## name); diff --git a/common/attrlist.c b/common/attrlist.c index 7a85aff9..377778eb 100644 --- a/common/attrlist.c +++ b/common/attrlist.c @@ -598,7 +598,7 @@ void uiprivAttrListRemoveCharacters(uiprivAttrList *alist, size_t start, size_t a = attrDeleteRange(alist, a, start, end); } -void uiprivAttrListForEach(uiprivAttrList *alist, uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data) +void uiprivAttrListForEach(const uiprivAttrList *alist, const uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data) { struct attr *a; uiForEach ret; diff --git a/common/attrstr.c b/common/attrstr.c index 73246651..ab3f073a 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -279,6 +279,17 @@ void uiAttributedStringDelete(uiAttributedString *s, size_t start, size_t end) resize(s, start + count, start16 + count16); } +void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end) +{ + uiprivAttrListInsertAttribute(s->attrs, spec, start, end); +} + +// LONGTERM introduce an iterator object instead? +void uiAttributedStringForEachAttribute(const uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data) +{ + uiprivAttrListForEach(s->attrs, s, f, data); +} + // TODO figure out if we should count the grapheme past the end size_t uiAttributedStringNumGraphemes(uiAttributedString *s) { @@ -303,17 +314,6 @@ size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos) return pos; } -void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end) -{ - uiprivAttrListInsertAttribute(s->attrs, spec, start, end); -} - -// LONGTERM introduce an iterator object instead? -void uiAttributedStringForEachAttribute(uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data) -{ - uiprivAttrListForEach(s->attrs, s, f, data); -} - // helpers for platform-specific code const uint16_t *uiprivAttributedStringUTF16String(uiAttributedString *s) diff --git a/common/attrstr.h b/common/attrstr.h index b0192b92..32e632ed 100644 --- a/common/attrstr.h +++ b/common/attrstr.h @@ -18,7 +18,7 @@ extern void uiprivAttrListInsertCharactersExtendingAttributes(uiprivAttrList *al extern void uiprivAttrListRemoveAttribute(uiprivAttrList *alist, uiAttribute type, size_t start, size_t end); extern void uiprivAttrListRemoveAttributes(uiprivAttrList *alist, size_t start, size_t end); extern void uiprivAttrListRemoveCharacters(uiprivAttrList *alist, size_t start, size_t end); -extern void uiprivAttrListForEach(uiprivAttrList *alist, uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); +extern void uiprivAttrListForEach(const uiprivAttrList *alist, const uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); // attrstr.c extern const uint16_t *uiprivAttributedStringUTF16String(uiAttributedString *s); From 32041a2ecc770fb7cb60c6ad3bca49f0ddd94ed3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 4 Mar 2018 11:25:06 -0500 Subject: [PATCH 0876/1329] More TODOs. --- _future/unittest/testing.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/_future/unittest/testing.h b/_future/unittest/testing.h index c2d81690..550e5250 100644 --- a/_future/unittest/testing.h +++ b/_future/unittest/testing.h @@ -101,6 +101,12 @@ extern void testingTFail(testingT *t); // TODO should the defered function also have t passed to it? extern void testingTDefer(testingT *t, void (*f)(void *data), void *data); +// TODO IEEE 754 helpers +// references: +// - https://www.sourceware.org/ml/libc-alpha/2009-04/msg00005.html +// - https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html +// - https://stackoverflow.com/questions/5085533/is-a-c-preprocessor-identical-to-a-c-preprocessor + // TODO should __LINE__ arguments use intmax_t or uintmax_t instead of int? extern void testingprivRegisterTest(const char *, void (*)(testingT *)); // see https://stackoverflow.com/questions/32399191/va-args-expansion-using-msvc From 036c7c12e66dbca81e85e7e145a8126063fa9db0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 4 Mar 2018 13:52:33 -0500 Subject: [PATCH 0877/1329] Updated CONTRIBUTING.md and moved the old OS X draw text stuff out of the way. --- CONTRIBUTING.md | 6 ++++-- darwin/{_appkit_drawtext.m => OLD__appkit_drawtext.m} | 0 darwin/{_appkit_fontmatch.m => OLD__appkit_fontmatch.m} | 0 darwin/{_old_drawtext.m => OLD__old_drawtext.m} | 0 darwin/{aat.m => OLD_aat.m} | 0 darwin/{attrstr.m => OLD_attrstr.m} | 0 darwin/{drawtext.m => OLD_drawtext.m} | 0 darwin/{fontbutton.m => OLD_fontbutton.m} | 0 darwin/{fontmatch.m => OLD_fontmatch.m} | 0 darwin/{fontstyle.h => OLD_fontstyle.h} | 0 darwin/{fonttraits.m => OLD_fonttraits.m} | 0 darwin/{fontvariation.m => OLD_fontvariation.m} | 0 darwin/{graphemes.m => OLD_graphemes.m} | 0 darwin/{opentype.m => OLD_opentype.m} | 0 14 files changed, 4 insertions(+), 2 deletions(-) rename darwin/{_appkit_drawtext.m => OLD__appkit_drawtext.m} (100%) rename darwin/{_appkit_fontmatch.m => OLD__appkit_fontmatch.m} (100%) rename darwin/{_old_drawtext.m => OLD__old_drawtext.m} (100%) rename darwin/{aat.m => OLD_aat.m} (100%) rename darwin/{attrstr.m => OLD_attrstr.m} (100%) rename darwin/{drawtext.m => OLD_drawtext.m} (100%) rename darwin/{fontbutton.m => OLD_fontbutton.m} (100%) rename darwin/{fontmatch.m => OLD_fontmatch.m} (100%) rename darwin/{fontstyle.h => OLD_fontstyle.h} (100%) rename darwin/{fonttraits.m => OLD_fonttraits.m} (100%) rename darwin/{fontvariation.m => OLD_fontvariation.m} (100%) rename darwin/{graphemes.m => OLD_graphemes.m} (100%) rename darwin/{opentype.m => OLD_opentype.m} (100%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7147ff4e..94bd199b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -30,9 +30,11 @@ In the event you are unsure of something, refer to existing libui code for examp libui uses camel-case for naming, with a handful of very specific exceptions (namely GObject method names, where GObject itself enforces the naming convention). -All public API names should begin with `ui` and followed by a capital letter. All public struct names should begin with a capital letter. This is identical to the visibiilty rules of Go, assuming a package name of `ui`. +All public API names should begin with `ui` and followed by a capital letter. All public struct field names should begin with a capital letter. This is identical to the visibiilty rules of Go, assuming a package name of `ui`. -No private API name should begin with `ui` followed by a capital letter; in fact, I would ideally like to be able to get away with lax naming for private functions. I may have to change that if it becomes technically difficult to do in the case of static linking later on down the road. (`uiMalloc()`, `uiRealloc()`, and `uiFree()` are grandfathered-in exceptions.) +Private API names — specifcally those used by more than one source file — should begin with `uipriv` and be followed by a capital letter. This avoids namespace collisions in static libraries. + +Static functions and static objects do not have naming restrictions. Acronyms should **NOT** be mixed-case. `http` for the first word in a camel-case name, `HTTP` for all else, but **NEVER** `Http`. This is possibly the only aspect of the controversial nature of code style that I consider indefensibly stupid. diff --git a/darwin/_appkit_drawtext.m b/darwin/OLD__appkit_drawtext.m similarity index 100% rename from darwin/_appkit_drawtext.m rename to darwin/OLD__appkit_drawtext.m diff --git a/darwin/_appkit_fontmatch.m b/darwin/OLD__appkit_fontmatch.m similarity index 100% rename from darwin/_appkit_fontmatch.m rename to darwin/OLD__appkit_fontmatch.m diff --git a/darwin/_old_drawtext.m b/darwin/OLD__old_drawtext.m similarity index 100% rename from darwin/_old_drawtext.m rename to darwin/OLD__old_drawtext.m diff --git a/darwin/aat.m b/darwin/OLD_aat.m similarity index 100% rename from darwin/aat.m rename to darwin/OLD_aat.m diff --git a/darwin/attrstr.m b/darwin/OLD_attrstr.m similarity index 100% rename from darwin/attrstr.m rename to darwin/OLD_attrstr.m diff --git a/darwin/drawtext.m b/darwin/OLD_drawtext.m similarity index 100% rename from darwin/drawtext.m rename to darwin/OLD_drawtext.m diff --git a/darwin/fontbutton.m b/darwin/OLD_fontbutton.m similarity index 100% rename from darwin/fontbutton.m rename to darwin/OLD_fontbutton.m diff --git a/darwin/fontmatch.m b/darwin/OLD_fontmatch.m similarity index 100% rename from darwin/fontmatch.m rename to darwin/OLD_fontmatch.m diff --git a/darwin/fontstyle.h b/darwin/OLD_fontstyle.h similarity index 100% rename from darwin/fontstyle.h rename to darwin/OLD_fontstyle.h diff --git a/darwin/fonttraits.m b/darwin/OLD_fonttraits.m similarity index 100% rename from darwin/fonttraits.m rename to darwin/OLD_fonttraits.m diff --git a/darwin/fontvariation.m b/darwin/OLD_fontvariation.m similarity index 100% rename from darwin/fontvariation.m rename to darwin/OLD_fontvariation.m diff --git a/darwin/graphemes.m b/darwin/OLD_graphemes.m similarity index 100% rename from darwin/graphemes.m rename to darwin/OLD_graphemes.m diff --git a/darwin/opentype.m b/darwin/OLD_opentype.m similarity index 100% rename from darwin/opentype.m rename to darwin/OLD_opentype.m From 7fd012418dd22c9ed5d90a8fc069b963b69694a8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 4 Mar 2018 15:09:27 -0500 Subject: [PATCH 0878/1329] Migrated (and cleaned up) OS X opentype.m. --- darwin/OLD_attrstr.h | 26 ++++++ darwin/OLD_opentype.m | 191 ----------------------------------------- darwin/attrstr.h | 4 + darwin/opentype.m | 112 ++++++++++++++++++++++++ darwin/uipriv_darwin.h | 26 ------ 5 files changed, 142 insertions(+), 217 deletions(-) create mode 100644 darwin/OLD_attrstr.h delete mode 100644 darwin/OLD_opentype.m create mode 100644 darwin/attrstr.h create mode 100644 darwin/opentype.m diff --git a/darwin/OLD_attrstr.h b/darwin/OLD_attrstr.h new file mode 100644 index 00000000..abe80cee --- /dev/null +++ b/darwin/OLD_attrstr.h @@ -0,0 +1,26 @@ +// 4 march 2018 + +// fontmatch.m +extern CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd); +extern CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uiOpenTypeFeatures *otf); +extern void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescriptor *uidesc); + +// attrstr.m +extern void initUnderlineColors(void); +extern void uninitUnderlineColors(void); +typedef void (^backgroundBlock)(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y); +extern CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks); + +// aat.m +typedef void (^aatBlock)(uint16_t type, uint16_t selector); +extern void openTypeToAAT(char a, char b, char c, char d, uint32_t value, aatBlock f); + +// opentype.m +// TODO this is only used by opentype.m and aat.m; figure out some better way to handle this +// TODO remove x8tox32() +#define x8tox32(x) ((uint32_t) (((uint8_t) (x)) & 0xFF)) +#define mkTag(a, b, c, d) \ + ((x8tox32(a) << 24) | \ + (x8tox32(b) << 16) | \ + (x8tox32(c) << 8) | \ + x8tox32(d)) diff --git a/darwin/OLD_opentype.m b/darwin/OLD_opentype.m deleted file mode 100644 index 890aa02a..00000000 --- a/darwin/OLD_opentype.m +++ /dev/null @@ -1,191 +0,0 @@ -// 11 may 2017 -#import "uipriv_darwin.h" - -struct uiOpenTypeFeatures { - NSMutableDictionary *tags; -}; - -uiOpenTypeFeatures *uiNewOpenTypeFeatures(void) -{ - uiOpenTypeFeatures *otf; - - otf = uiNew(uiOpenTypeFeatures); - otf->tags = [NSMutableDictionary new]; - return otf; -} - -void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf) -{ - [otf->tags release]; - uiFree(otf); -} - -uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures *otf) -{ - uiOpenTypeFeatures *out; - - out = uiNew(uiOpenTypeFeatures); - out->tags = [otf->tags mutableCopy]; - return out; -} - -// why are there no NSNumber methods for stdint.h or the equivalent core foundation types?... -#define mkMapObject(tag) [NSNumber numberWithUnsignedLongLong:((unsigned long long) tag)] -#define mapObjectValue(num) ((uint32_t) [num unsignedLongLongValue]) - -void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value) -{ - NSNumber *tn, *vn; - - tn = mkMapObject(mkTag(a, b, c, d)); - vn = mkMapObject(value); - [otf->tags setObject:vn forKey:tn]; -} - -void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d) -{ - NSNumber *tn; - - tn = mkMapObject(mkTag(a, b, c, d)); - // documented as doing nothing if tn is not in otf->tags - [otf->tags removeObjectForKey:tn]; -} - -// TODO will the const wreck stuff up? -int uiOpenTypeFeaturesGet(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value) -{ - NSNumber *tn, *vn; - - tn = mkMapObject(mkTag(a, b, c, d)); - vn = (NSNumber *) [otf->tags objectForKey:tn]; - if (vn == nil) - return 0; - *value = mapObjectValue(vn); - // TODO release vn? - return 1; -} - -void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data) -{ - [otf->tags enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) { - NSNumber *tn = (NSNumber *) key; - NSNumber *vn = (NSNumber *) value; - uint32_t tag; - uint8_t a, b, c, d; - uiForEach ret; - - tag = mapObjectValue(tn); - a = (uint8_t) ((tag >> 24) & 0xFF); - b = (uint8_t) ((tag >> 16) & 0xFF); - c = (uint8_t) ((tag >> 8) & 0xFF); - d = (uint8_t) (tag & 0xFF); - ret = (*f)(otf, (char) a, (char) b, (char) c, (char) d, - mapObjectValue(vn), data); - // TODO for all: require exact match? - if (ret == uiForEachStop) - *stop = YES; - }]; -} - -int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeatures *b) -{ - if (a == NULL && b == NULL) - return 1; - if (a == NULL || b == NULL) - return 0; - return [a->tags isEqualToDictionary:b->tags]; -} - -// TODO explain all this -// TODO rename outerArray and innerDict (the names made sense when this was part of fontdescAppendFeatures(), but not here) -// TODO make all this use enumerateKeysAndObjects (which requires duplicating code)? -static uiForEach otfArrayForEachAAT(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data) -{ - CFMutableArrayRef outerArray = (CFMutableArrayRef) data; - - openTypeToAAT(a, b, c, d, value, ^(uint16_t type, uint16_t selector) { - CFDictionaryRef innerDict; - CFNumberRef numType, numSelector; - // not well documented, but fixed-size arrays don't support __block either (VLAs are documented as being unsupported) - const void *keys[2], *values[2]; - - keys[0] = kCTFontFeatureTypeIdentifierKey; - keys[1] = kCTFontFeatureSelectorIdentifierKey; - numType = CFNumberCreate(NULL, kCFNumberSInt16Type, - (const SInt16 *) (&type)); - numSelector = CFNumberCreate(NULL, kCFNumberSInt16Type, - (const SInt16 *) (&selector)); - values[0] = numType; - values[1] = numSelector; - innerDict = CFDictionaryCreate(NULL, - keys, values, 2, - // TODO are these correct? - &kCFCopyStringDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - if (innerDict == NULL) { - // TODO - } - CFArrayAppendValue(outerArray, innerDict); - CFRelease(innerDict); - CFRelease(numSelector); - CFRelease(numType); - }); - return uiForEachContinue; -} - -// TODO find out which fonts differ in AAT small caps and test them with this -static uiForEach otfArrayForEachOT(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data) -{ - CFMutableArrayRef outerArray = (CFMutableArrayRef) data; - CFDictionaryRef innerDict; - // TODO rename this to tagstr (and all the other variables likewise...) - CFStringRef strTag; - CFNumberRef numValue; - char tagcstr[5]; - const void *keys[2], *values[2]; - - tagcstr[0] = a; - tagcstr[1] = b; - tagcstr[2] = c; - tagcstr[3] = d; - tagcstr[4] = '\0'; - keys[0] = *FUTURE_kCTFontOpenTypeFeatureTag; - keys[1] = *FUTURE_kCTFontOpenTypeFeatureValue; - strTag = CFStringCreateWithCString(NULL, tagcstr, kCFStringEncodingUTF8); - if (strTag == NULL) { - // TODO - } - numValue = CFNumberCreate(NULL, kCFNumberSInt32Type, - (const SInt32 *) (&value)); - values[0] = strTag; - values[1] = numValue; - innerDict = CFDictionaryCreate(NULL, - keys, values, 2, - // TODO are these correct? - &kCFCopyStringDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - if (innerDict == NULL) { - // TODO - } - CFArrayAppendValue(outerArray, innerDict); - CFRelease(innerDict); - CFRelease(numValue); - CFRelease(strTag); - return uiForEachContinue; -} - -CFArrayRef otfToFeaturesArray(const uiOpenTypeFeatures *otf) -{ - CFMutableArrayRef outerArray; - uiOpenTypeFeaturesForEachFunc f; - - outerArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - if (outerArray == NULL) { - // TODO - } - f = otfArrayForEachAAT; - if (FUTURE_kCTFontOpenTypeFeatureTag != NULL && FUTURE_kCTFontOpenTypeFeatureValue != NULL) - f = otfArrayForEachOT; - uiOpenTypeFeaturesForEach(otf, f, outerArray); - return outerArray; -} diff --git a/darwin/attrstr.h b/darwin/attrstr.h new file mode 100644 index 00000000..8221b8b4 --- /dev/null +++ b/darwin/attrstr.h @@ -0,0 +1,4 @@ +// 4 march 2018 + +// opentype.m +extern CFArrayRef uiprivOpenTypeFeaturesToCTFeatures(const uiOpenTypeFeatures *otf); diff --git a/darwin/opentype.m b/darwin/opentype.m new file mode 100644 index 00000000..0b2647e9 --- /dev/null +++ b/darwin/opentype.m @@ -0,0 +1,112 @@ +// 11 may 2017 +#import "uipriv_darwin.h" + +struct addCTFeatureEntryParams { + CFMutableArrayRef array; + const void *tagKey; + BOOL tagIsNumber; + CFNumberType tagType; + const void *tagValue; + const void *valueKey; + CFNumberType valueType; + const void *valueValue; +}; + +static void addCTFeatureEntry(struct addCTFeatureEntryParams *p) +{ + CFDictionaryRef featureDict; + CFNumberRef tagNum, valueNum; + const void *keys[2], *values[2]; + + keys[0] = p->tagKey; + tagNum = NULL; + values[0] = p->tagValue; + if (p->tagIsNumber) { + tagNum = CFNumberCreate(NULL, p->tagType, p->tagValue); + values[0] = tagNum; + } + + keys[1] = p->valueKey; + valueNum = CFNumberCreate(NULL, p->valueType, p->valueValue); + values[1] = valueNum; + + featureDict = CFDictionaryCreate(NULL, + keys, values, 2, + // TODO are these correct? + &kCFCopyStringDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + if (featureDict == NULL) { + // TODO + } + CFArrayAppendValue(p->array, featureDict); + + CFRelease(featureDict); + CFRelease(valueNum); + if (p->tagIsNumber) + CFRelease(tagNum); +} + +static uiForEach otfArrayForEachAAT(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data) +{ + __block struct addCTFeatureEntryParams p; + + p.array = (CFMutableArrayRef) data; + p.tagIsNumber = YES; + openTypeToAAT(a, b, c, d, value, ^(uint16_t type, uint16_t selector) { + p.tagKey = kCTFontFeatureTypeIdentifierKey; + p.tagType = kCFNumberSInt16Type; + p.tagValue = (const SInt16 *) (&type); + p.valueKey = kCTFontFeatureSelectorIdentifierKey; + p.valueType = kCFNumberSInt16Type; + p.valueValue = (const SInt16 *) (&selector); + addCTFeatureEntry(&p); + }); + return uiForEachContinue; +} + +// TODO find out which fonts differ in AAT small caps and test them with this +static uiForEach otfArrayForEachOT(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data) +{ + struct addCTFeatureEntryParams p; + char tagcstr[5]; + CFStringRef tagstr; + + p.array = (CFMutableArrayRef) data; + + p.tagKey = *FUTURE_kCTFontOpenTypeFeatureTag; + p.tagIsNumber = NO; + tagcstr[0] = a; + tagcstr[1] = b; + tagcstr[2] = c; + tagcstr[3] = d; + tagcstr[4] = '\0'; + tagstr = CFStringCreateWithCString(NULL, tagcstr, kCFStringEncodingUTF8); + if (tagstr == NULL) { + // TODO + } + p.tagValue = tagstr; + + p.valueKey = *FUTURE_kCTFontOpenTypeFeatureValue; + p.valueType = kCFNumberSInt32Type; + p.valueValue = (const SInt32 *) (&value); + addCTFeatureEntry(&p); + + CFRelease(strTag); + return uiForEachContinue; +} + +CFArrayRef uiprivOpenTypeFeaturesToCTFeatures(const uiOpenTypeFeatures *otf) +{ + CFMutableArrayRef array; + uiOpenTypeFeaturesForEachFunc f; + + array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + if (array == NULL) { + // TODO + } + f = otfArrayForEachAAT; + if (FUTURE_kCTFontOpenTypeFeatureTag != NULL && FUTURE_kCTFontOpenTypeFeatureValue != NULL) + f = otfArrayForEachOT; + uiOpenTypeFeaturesForEach(otf, f, array); + return array; +} diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 0303c32c..4d5ebdc3 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -142,32 +142,6 @@ extern NSImage *imageImage(uiImage *); extern void doManualMove(NSWindow *w, NSEvent *initialEvent); extern void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdge edge); -// fontmatch.m -extern CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd); -extern CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uiOpenTypeFeatures *otf); -extern void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescriptor *uidesc); - -// attrstr.m -extern void initUnderlineColors(void); -extern void uninitUnderlineColors(void); -typedef void (^backgroundBlock)(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y); -extern CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks); - -// aat.m -typedef void (^aatBlock)(uint16_t type, uint16_t selector); -extern void openTypeToAAT(char a, char b, char c, char d, uint32_t value, aatBlock f); - -// opentype.m -// TODO this is only used by opentype.m and aat.m; figure out some better way to handle this -// TODO remove x8tox32() -#define x8tox32(x) ((uint32_t) (((uint8_t) (x)) & 0xFF)) -#define mkTag(a, b, c, d) \ - ((x8tox32(a) << 24) | \ - (x8tox32(b) << 16) | \ - (x8tox32(c) << 8) | \ - x8tox32(d)) -extern CFArrayRef otfToFeaturesArray(const uiOpenTypeFeatures *otf); - // future.m extern CFStringRef *FUTURE_kCTFontOpenTypeFeatureTag; extern CFStringRef *FUTURE_kCTFontOpenTypeFeatureValue; From 36567cc52235781a31f5ab3767b241c8d9e545e7 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 4 Mar 2018 15:48:45 -0500 Subject: [PATCH 0879/1329] Migrated OS X graphemes.m. --- darwin/attrstr.h | 1 + darwin/{OLD_graphemes.m => graphemes.m} | 11 ++++++----- darwin/opentype.m | 1 + 3 files changed, 8 insertions(+), 5 deletions(-) rename darwin/{OLD_graphemes.m => graphemes.m} (79%) diff --git a/darwin/attrstr.h b/darwin/attrstr.h index 8221b8b4..537243bb 100644 --- a/darwin/attrstr.h +++ b/darwin/attrstr.h @@ -1,4 +1,5 @@ // 4 march 2018 +#import "../common/attrstr.h" // opentype.m extern CFArrayRef uiprivOpenTypeFeaturesToCTFeatures(const uiOpenTypeFeatures *otf); diff --git a/darwin/OLD_graphemes.m b/darwin/graphemes.m similarity index 79% rename from darwin/OLD_graphemes.m rename to darwin/graphemes.m index e5ee816f..3fdbc17e 100644 --- a/darwin/OLD_graphemes.m +++ b/darwin/graphemes.m @@ -1,15 +1,16 @@ // 3 december 2016 #import "uipriv_darwin.h" +#import "attrstr.h" // CFStringGetRangeOfComposedCharactersAtIndex() is the function for grapheme clusters // https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Strings/Articles/stringsClusters.html says that this does work on all multi-codepoint graphemes (despite the name), and that this is the preferred function for this particular job anyway -int graphemesTakesUTF16(void) +int uiprivGraphemesTakesUTF16(void) { return 1; } -struct graphemes *graphemes(void *s, size_t len) +struct graphemes *uiprivNewGraphemes(void *s, size_t len) { struct graphemes *g; UniChar *str = (UniChar *) s; @@ -18,7 +19,7 @@ struct graphemes *graphemes(void *s, size_t len) CFRange range; size_t i; - g = uiNew(struct graphemes); + g = uiprivNew(struct graphemes); cfstr = CFStringCreateWithCharactersNoCopy(NULL, str, len, kCFAllocatorNull); if (cfstr == NULL) { @@ -34,8 +35,8 @@ struct graphemes *graphemes(void *s, size_t len) ppos = range.location + range.length; } - g->pointsToGraphemes = (size_t *) uiAlloc((len + 1) * sizeof (size_t), "size_t[] (graphemes)"); - g->graphemesToPoints = (size_t *) uiAlloc((g->len + 1) * sizeof (size_t), "size_t[] (graphemes)"); + g->pointsToGraphemes = (size_t *) uiprivAlloc((len + 1) * sizeof (size_t), "size_t[] (graphemes)"); + g->graphemesToPoints = (size_t *) uiprivAlloc((g->len + 1) * sizeof (size_t), "size_t[] (graphemes)"); // now calculate everything // fortunately due to the use of CFRange we can do this in one loop trivially! diff --git a/darwin/opentype.m b/darwin/opentype.m index 0b2647e9..de4bb093 100644 --- a/darwin/opentype.m +++ b/darwin/opentype.m @@ -1,5 +1,6 @@ // 11 may 2017 #import "uipriv_darwin.h" +#import "attrstr.h" struct addCTFeatureEntryParams { CFMutableArrayRef array; From d8ad3300c90964961ddc7a1d915b8998c4ee15ce Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 4 Mar 2018 15:53:46 -0500 Subject: [PATCH 0880/1329] Migrated aat.m back. --- darwin/OLD_attrstr.h | 14 -------------- darwin/{OLD_aat.m => aat.m} | 14 ++++++++++++-- darwin/attrstr.h | 4 ++++ darwin/opentype.m | 2 +- 4 files changed, 17 insertions(+), 17 deletions(-) rename darwin/{OLD_aat.m => aat.m} (96%) diff --git a/darwin/OLD_attrstr.h b/darwin/OLD_attrstr.h index abe80cee..2a637b84 100644 --- a/darwin/OLD_attrstr.h +++ b/darwin/OLD_attrstr.h @@ -10,17 +10,3 @@ extern void initUnderlineColors(void); extern void uninitUnderlineColors(void); typedef void (^backgroundBlock)(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y); extern CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks); - -// aat.m -typedef void (^aatBlock)(uint16_t type, uint16_t selector); -extern void openTypeToAAT(char a, char b, char c, char d, uint32_t value, aatBlock f); - -// opentype.m -// TODO this is only used by opentype.m and aat.m; figure out some better way to handle this -// TODO remove x8tox32() -#define x8tox32(x) ((uint32_t) (((uint8_t) (x)) & 0xFF)) -#define mkTag(a, b, c, d) \ - ((x8tox32(a) << 24) | \ - (x8tox32(b) << 16) | \ - (x8tox32(c) << 8) | \ - x8tox32(d)) diff --git a/darwin/OLD_aat.m b/darwin/aat.m similarity index 96% rename from darwin/OLD_aat.m rename to darwin/aat.m index 0c3fd5fa..05f0b268 100644 --- a/darwin/OLD_aat.m +++ b/darwin/aat.m @@ -1,9 +1,10 @@ // 14 february 2017 #import "uipriv_darwin.h" +#import "attrstr.h" // TODO explain the purpose of this file -static void boolspec(uint32_t value, uint16_t type, uint16_t ifTrue, uint16_t ifFalse, aatBlock f) +static void boolspec(uint32_t value, uint16_t type, uint16_t ifTrue, uint16_t ifFalse, uiprivAATBlock f) { // TODO are values other than 1 accepted for true by OpenType itself? (same for the rest of the file) if (value != 0) { @@ -13,8 +14,17 @@ static void boolspec(uint32_t value, uint16_t type, uint16_t ifTrue, uint16_t if f(type, ifFalse); } +// TODO remove the need for this +// TODO remove x8tox32() +#define x8tox32(x) ((uint32_t) (((uint8_t) (x)) & 0xFF)) +#define mkTag(a, b, c, d) \ + ((x8tox32(a) << 24) | \ + (x8tox32(b) << 16) | \ + (x8tox32(c) << 8) | \ + x8tox32(d)) + // TODO double-check drawtext example to make sure all of these are used properly (I already screwed dlig up by putting clig twice instead) -void openTypeToAAT(char a, char b, char c, char d, uint32_t value, aatBlock f) +void uiprivOpenTypeToAAT(char a, char b, char c, char d, uint32_t value, uiprivAATBlock f) { switch (mkTag(a, b, c, d)) { case mkTag('l', 'i', 'g', 'a'): diff --git a/darwin/attrstr.h b/darwin/attrstr.h index 537243bb..74be3a37 100644 --- a/darwin/attrstr.h +++ b/darwin/attrstr.h @@ -3,3 +3,7 @@ // opentype.m extern CFArrayRef uiprivOpenTypeFeaturesToCTFeatures(const uiOpenTypeFeatures *otf); + +// aat.m +typedef void (^uiprivAATBlock)(uint16_t type, uint16_t selector); +extern void uiprivOpenTypeToAAT(char a, char b, char c, char d, uint32_t value, uiprivAATBlock f); diff --git a/darwin/opentype.m b/darwin/opentype.m index de4bb093..079197ed 100644 --- a/darwin/opentype.m +++ b/darwin/opentype.m @@ -53,7 +53,7 @@ static uiForEach otfArrayForEachAAT(const uiOpenTypeFeatures *otf, char a, char p.array = (CFMutableArrayRef) data; p.tagIsNumber = YES; - openTypeToAAT(a, b, c, d, value, ^(uint16_t type, uint16_t selector) { + uiprivOpenTypeToAAT(a, b, c, d, value, ^(uint16_t type, uint16_t selector) { p.tagKey = kCTFontFeatureTypeIdentifierKey; p.tagType = kCFNumberSInt16Type; p.tagValue = (const SInt16 *) (&type); From 0b3176cead9471ef77baae90da411e5be07afb53 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 4 Mar 2018 18:01:08 -0500 Subject: [PATCH 0881/1329] Migrated the font matchng stuff on OS X. --- darwin/OLD_attrstr.h | 5 -- darwin/OLD_fontstyle.h | 61 ------------------ darwin/attrstr.h | 63 +++++++++++++++++++ darwin/{OLD_fontmatch.m => fontmatch.m} | 34 +++++----- darwin/{OLD_fonttraits.m => fonttraits.m} | 14 ++--- .../{OLD_fontvariation.m => fontvariation.m} | 7 ++- 6 files changed, 91 insertions(+), 93 deletions(-) delete mode 100644 darwin/OLD_fontstyle.h rename darwin/{OLD_fontmatch.m => fontmatch.m} (92%) rename darwin/{OLD_fonttraits.m => fonttraits.m} (92%) rename darwin/{OLD_fontvariation.m => fontvariation.m} (96%) diff --git a/darwin/OLD_attrstr.h b/darwin/OLD_attrstr.h index 2a637b84..49f5fabc 100644 --- a/darwin/OLD_attrstr.h +++ b/darwin/OLD_attrstr.h @@ -1,10 +1,5 @@ // 4 march 2018 -// fontmatch.m -extern CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd); -extern CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uiOpenTypeFeatures *otf); -extern void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescriptor *uidesc); - // attrstr.m extern void initUnderlineColors(void); extern void uninitUnderlineColors(void); diff --git a/darwin/OLD_fontstyle.h b/darwin/OLD_fontstyle.h deleted file mode 100644 index 8a27380c..00000000 --- a/darwin/OLD_fontstyle.h +++ /dev/null @@ -1,61 +0,0 @@ -// 3 november 2017 - -// fontmatch.m -@interface fontStyleData : NSObject { - CTFontRef font; - CTFontDescriptorRef desc; - CFDictionaryRef traits; - CTFontSymbolicTraits symbolic; - double weight; - double width; - BOOL didStyleName; - CFStringRef styleName; - BOOL didVariation; - CFDictionaryRef variation; - BOOL hasRegistrationScope; - CTFontManagerScope registrationScope; - BOOL didPostScriptName; - CFStringRef postScriptName; - CTFontFormat fontFormat; - BOOL didPreferredSubFamilyName; - CFStringRef preferredSubFamilyName; - BOOL didSubFamilyName; - CFStringRef subFamilyName; - BOOL didFullName; - CFStringRef fullName; - BOOL didPreferredFamilyName; - CFStringRef preferredFamilyName; - BOOL didFamilyName; - CFStringRef familyName; - BOOL didVariationAxes; - CFArrayRef variationAxes; -} -- (id)initWithFont:(CTFontRef)f; -- (id)initWithDescriptor:(CTFontDescriptorRef)d; -- (BOOL)prepare; -- (void)ensureFont; -- (CTFontSymbolicTraits)symbolicTraits; -- (double)weight; -- (double)width; -- (CFStringRef)styleName; -- (CFDictionaryRef)variation; -- (BOOL)hasRegistrationScope; -- (CTFontManagerScope)registrationScope; -- (CFStringRef)postScriptName; -- (CFDataRef)table:(CTFontTableTag)tag; -- (CTFontFormat)fontFormat; -- (CFStringRef)fontName:(CFStringRef)key; -- (CFStringRef)preferredSubFamilyName; -- (CFStringRef)subFamilyName; -- (CFStringRef)fullName; -- (CFStringRef)preferredFamilyName; -- (CFStringRef)familyName; -- (CFArrayRef)variationAxes; -@end - -// fonttraits.m -extern void processFontTraits(fontStyleData *d, uiDrawFontDescriptor *out); - -// fontvariation.m -extern NSDictionary *mkVariationAxisDict(CFArrayRef axes, CFDataRef avarTable); -extern void processFontVariation(fontStyleData *d, NSDictionary *axisDict, uiDrawFontDescriptor *out); diff --git a/darwin/attrstr.h b/darwin/attrstr.h index 74be3a37..7b1dbb74 100644 --- a/darwin/attrstr.h +++ b/darwin/attrstr.h @@ -7,3 +7,66 @@ extern CFArrayRef uiprivOpenTypeFeaturesToCTFeatures(const uiOpenTypeFeatures *o // aat.m typedef void (^uiprivAATBlock)(uint16_t type, uint16_t selector); extern void uiprivOpenTypeToAAT(char a, char b, char c, char d, uint32_t value, uiprivAATBlock f); + +// fontmatch.m +@interface uiprivFontStyleData : NSObject { + CTFontRef font; + CTFontDescriptorRef desc; + CFDictionaryRef traits; + CTFontSymbolicTraits symbolic; + double weight; + double width; + BOOL didStyleName; + CFStringRef styleName; + BOOL didVariation; + CFDictionaryRef variation; + BOOL hasRegistrationScope; + CTFontManagerScope registrationScope; + BOOL didPostScriptName; + CFStringRef postScriptName; + CTFontFormat fontFormat; + BOOL didPreferredSubFamilyName; + CFStringRef preferredSubFamilyName; + BOOL didSubFamilyName; + CFStringRef subFamilyName; + BOOL didFullName; + CFStringRef fullName; + BOOL didPreferredFamilyName; + CFStringRef preferredFamilyName; + BOOL didFamilyName; + CFStringRef familyName; + BOOL didVariationAxes; + CFArrayRef variationAxes; +} +- (id)initWithFont:(CTFontRef)f; +- (id)initWithDescriptor:(CTFontDescriptorRef)d; +- (BOOL)prepare; +- (void)ensureFont; +- (CTFontSymbolicTraits)symbolicTraits; +- (double)weight; +- (double)width; +- (CFStringRef)styleName; +- (CFDictionaryRef)variation; +- (BOOL)hasRegistrationScope; +- (CTFontManagerScope)registrationScope; +- (CFStringRef)postScriptName; +- (CFDataRef)table:(CTFontTableTag)tag; +- (CTFontFormat)fontFormat; +- (CFStringRef)fontName:(CFStringRef)key; +- (CFStringRef)preferredSubFamilyName; +- (CFStringRef)subFamilyName; +- (CFStringRef)fullName; +- (CFStringRef)preferredFamilyName; +- (CFStringRef)familyName; +- (CFArrayRef)variationAxes; +@end +extern CTFontDescriptorRef uiprivDrawFontDescriptorToCTFontDescriptor(uiDrawFontDescriptor *fd); +extern CTFontDescriptorRef uiprivCTFontDescriptorAppendFeatures(CTFontDescriptorRef desc, const uiOpenTypeFeatures *otf); +extern void uiprivDrawFontDescriptorFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescriptor *uidesc); + +// fonttraits.m +extern void uiprivProcessFontTraits(uiprivFontStyleData *d, uiDrawFontDescriptor *out); + +// fontvariation.m +extern NSDictionary *uiprivMakeVariationAxisDict(CFArrayRef axes, CFDataRef avarTable); +extern void uiprivProcessFontVariation(uiprivFontStyleData *d, NSDictionary *axisDict, uiDrawFontDescriptor *out); diff --git a/darwin/OLD_fontmatch.m b/darwin/fontmatch.m similarity index 92% rename from darwin/OLD_fontmatch.m rename to darwin/fontmatch.m index 25476900..6a9eb14e 100644 --- a/darwin/OLD_fontmatch.m +++ b/darwin/fontmatch.m @@ -1,6 +1,6 @@ // 3 january 2017 #import "uipriv_darwin.h" -#import "fontstyle.h" +#import "attrstr.h" // Core Text exposes font style info in two forms: // - Fonts with a QuickDraw GX font variation (fvar) table, a feature @@ -26,7 +26,7 @@ // because the descriptors returned by Core Text's own font // matching won't have any. -@implementation fontStyleData +@implementation uiprivFontStyleData - (id)initWithFont:(CTFontRef)f { @@ -294,7 +294,7 @@ static const double *italicClosenesses[] = { // Core Text doesn't seem to differentiate between Italic and Oblique. // Pango's Core Text code just does a g_strrstr() (backwards case-sensitive search) for "Oblique" in the font's style name (see https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c); let's do that too I guess -static uiDrawTextItalic guessItalicOblique(fontStyleData *d) +static uiDrawTextItalic guessItalicOblique(uiprivFontStyleData *d) { CFStringRef styleName; BOOL isOblique; @@ -318,20 +318,20 @@ static uiDrawTextItalic guessItalicOblique(fontStyleData *d) // However, Core Text does seem to guarantee (from experimentation; see below) that the slant will be nonzero if and only if the italic bit is set, so we don't need to use the slant value. // Core Text also seems to guarantee that if a font lists itself as Italic or Oblique by name (font subfamily name, font style name, whatever), it will also have that bit set, so testing this bit does cover all fonts that name themselves as Italic and Oblique. (Again, this is from the below experimentation.) // TODO there is still one catch that might matter from a user's POV: the reverse is not true — the italic bit can be set even if the style of the font face/subfamily/style isn't named as Italic (for example, script typefaces like Adobe's Palace Script MT Std); I don't know what to do about this... I know how to start: find a script font that has an italic form (Adobe's Palace Script MT Std does not; only Regular and Semibold) -static void setItalic(fontStyleData *d, uiDrawFontDescriptor *out) +static void setItalic(uiprivFontStyleData *d, uiDrawFontDescriptor *out) { out->Italic = uiDrawTextItalicNormal; if (([d symbolicTraits] & kCTFontItalicTrait) != 0) out->Italic = guessItalicOblique(d); } -static void fillDescStyleFields(fontStyleData *d, NSDictionary *axisDict, uiDrawFontDescriptor *out) +static void fillDescStyleFields(uiprivFontStyleData *d, NSDictionary *axisDict, uiDrawFontDescriptor *out) { setItalic(d, out); if (axisDict != nil) - processFontVariation(d, axisDict, out); + uiprivProcessFontVariation(d, axisDict, out); else - processFontTraits(d, out); + uiprivProcessFontTraits(d, out); } static CTFontDescriptorRef matchStyle(CTFontDescriptorRef against, uiDrawFontDescriptor *styles) @@ -341,7 +341,7 @@ static CTFontDescriptorRef matchStyle(CTFontDescriptorRef against, uiDrawFontDes struct closeness *closeness; CTFontDescriptorRef current; CTFontDescriptorRef out; - fontStyleData *d; + uiprivFontStyleData *d; NSDictionary *axisDict; matching = CTFontDescriptorCreateMatchingFontDescriptors(against, NULL); @@ -356,10 +356,10 @@ static CTFontDescriptorRef matchStyle(CTFontDescriptorRef against, uiDrawFontDes } current = (CTFontDescriptorRef) CFArrayGetValueAtIndex(matching, 0); - d = [[fontStyleData alloc] initWithDescriptor:current]; + d = [[uiprivFontStyleData alloc] initWithDescriptor:current]; axisDict = nil; if ([d variation] != NULL) - axisDict = mkVariationAxisDict([d variationAxes], [d table:kCTFontTableAvar]); + axisDict = uiprivMakeVariationAxisDict([d variationAxes], [d table:kCTFontTableAvar]); closeness = (struct closeness *) uiAlloc(n * sizeof (struct closeness), "struct closeness[]"); for (i = 0; i < n; i++) { @@ -368,7 +368,7 @@ static CTFontDescriptorRef matchStyle(CTFontDescriptorRef against, uiDrawFontDes closeness[i].index = i; if (i != 0) { current = (CTFontDescriptorRef) CFArrayGetValueAtIndex(matching, i); - d = [[fontStyleData alloc] initWithDescriptor:current]; + d = [[uiprivFontStyleData alloc] initWithDescriptor:current]; } fillDescStyleFields(d, axisDict, &fields); closeness[i].weight = fields.Weight - styles->Weight; @@ -413,7 +413,7 @@ static CTFontDescriptorRef matchStyle(CTFontDescriptorRef against, uiDrawFontDes return out; } -CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd) +CTFontDescriptorRef uiprivDrawFontDescriptorToCTFontDescriptor(uiDrawFontDescriptor *fd) { CFMutableDictionaryRef attrs; CFStringRef cffamily; @@ -443,7 +443,7 @@ CTFontDescriptorRef fontdescToCTFontDescriptor(uiDrawFontDescriptor *fd) } // fortunately features that aren't supported are simply ignored, so we can copy them all in -CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uiOpenTypeFeatures *otf) +CTFontDescriptorRef uiprivCTFontDescriptorAppendFeatures(CTFontDescriptorRef desc, const uiOpenTypeFeatures *otf) { CTFontDescriptorRef new; CFArrayRef featuresArray; @@ -465,10 +465,10 @@ CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uiOpe return new; } -void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescriptor *uidesc) +void uiprivDrawFontDescriptorFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescriptor *uidesc) { CFStringRef cffamily; - fontStyleData *d; + uiprivFontStyleData *d; NSDictionary *axisDict; cffamily = (CFStringRef) CTFontDescriptorCopyAttribute(ctdesc, kCTFontFamilyNameAttribute); @@ -479,10 +479,10 @@ void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescript uidesc->Family = uiDarwinNSStringToText((NSString *) cffamily); CFRelease(cffamily); - d = [[fontStyleData alloc] initWithDescriptor:ctdesc]; + d = [[uiprivFontStyleData alloc] initWithDescriptor:ctdesc]; axisDict = nil; if ([d variation] != NULL) - axisDict = mkVariationAxisDict([d variationAxes], [d table:kCTFontTableAvar]); + axisDict = uiprivMakeVariationAxisDict([d variationAxes], [d table:kCTFontTableAvar]); fillDescStyleFields(d, axisDict, uidesc); if (axisDict != nil) [axisDict release]; diff --git a/darwin/OLD_fonttraits.m b/darwin/fonttraits.m similarity index 92% rename from darwin/OLD_fonttraits.m rename to darwin/fonttraits.m index feb37b8b..b4b932c8 100644 --- a/darwin/OLD_fonttraits.m +++ b/darwin/fonttraits.m @@ -1,6 +1,6 @@ // 1 november 2017 #import "uipriv_darwin.h" -#import "fontstyle.h" +#import "attrstr.h" // This is the part of the font style matching and normalization code // that handles fonts that use a traits dictionary. @@ -19,7 +19,7 @@ // what. We'll just convert Core Text's values into libui constants // and use those for matching. -static BOOL fontRegistered(fontStyleData *d) +static BOOL fontRegistered(uiprivFontStyleData *d) { if (![d hasRegistrationScope]) // header says this should be treated as the same as not registered @@ -54,7 +54,7 @@ static const CFStringRef exceptions[] = { NULL, }; -static void trySecondaryOS2Values(fontStyleData *d, uiDrawFontDescriptor *out, BOOL *hasWeight, BOOL *hasWidth) +static void trySecondaryOS2Values(uiprivFontStyleData *d, uiDrawFontDescriptor *out, BOOL *hasWeight, BOOL *hasWidth) { CFDataRef os2; uint16_t usWeightClass, usWidthClass; @@ -125,7 +125,7 @@ static BOOL testTTFOTFSubfamilyName(CFStringRef name, CFStringRef want) (kCFCompareCaseInsensitive | kCFCompareBackwards | kCFCompareNonliteral), NULL) != false; } -static BOOL testTTFOTFSubfamilyNames(fontStyleData *d, CFStringRef want) +static BOOL testTTFOTFSubfamilyNames(uiprivFontStyleData *d, CFStringRef want) { switch ([d fontFormat]) { case kCTFontFormatOpenTypePostScript: @@ -148,18 +148,18 @@ static BOOL testTTFOTFSubfamilyNames(fontStyleData *d, CFStringRef want) } // work around a bug in libFontRegistry.dylib -static BOOL shouldReallyBeThin(fontStyleData *d) +static BOOL shouldReallyBeThin(uiprivFontStyleData *d) { return testTTFOTFSubfamilyNames(d, CFSTR("W1")); } // work around a bug in libFontRegistry.dylib -static BOOL shouldReallyBeSemiCondensed(fontStyleData *d) +static BOOL shouldReallyBeSemiCondensed(uiprivFontStyleData *d) { return testTTFOTFSubfamilyNames(d, CFSTR("Semi Condensed")); } -void processFontTraits(fontStyleData *d, uiDrawFontDescriptor *out) +void uiprivProcessFontTraits(uiprivFontStyleData *d, uiDrawFontDescriptor *out) { double weight, width; BOOL hasWeight, hasWidth; diff --git a/darwin/OLD_fontvariation.m b/darwin/fontvariation.m similarity index 96% rename from darwin/OLD_fontvariation.m rename to darwin/fontvariation.m index 3e22c710..9959b02b 100644 --- a/darwin/OLD_fontvariation.m +++ b/darwin/fontvariation.m @@ -1,6 +1,6 @@ // 2 november 2017 #import "uipriv_darwin.h" -#import "fontstyle.h" +#import "attrstr.h" // This is the part of the font style matching and normalization code // that handles fonts that use the fvar table. @@ -205,6 +205,7 @@ static BOOL extractAxisDictValue(CFDictionaryRef dict, CFStringRef key, fixed161 return YES; } +// TODO here and elsewhere: make sure all Objective-C classes and possibly also custom method names have uipriv prefixes @interface fvarAxis : NSObject { fixed1616 min; fixed1616 max; @@ -262,7 +263,7 @@ fail: @end -NSDictionary *mkVariationAxisDict(CFArrayRef axes, CFDataRef avarTable) +NSDictionary *uiprivMakeVariationAxisDict(CFArrayRef axes, CFDataRef avarTable) { CFDictionaryRef axis; CFIndex i, n; @@ -304,7 +305,7 @@ static BOOL tryAxis(NSDictionary *axisDict, CFDictionaryRef var, NSNumber *key, return YES; } -void processFontVariation(fontStyleData *d, NSDictionary *axisDict, uiDrawFontDescriptor *out) +void uiprivProcessFontVariation(uiprivFontStyleData *d, NSDictionary *axisDict, uiDrawFontDescriptor *out) { CFDictionaryRef var; double v; From 1fc9f137bc7ad54333e549f9db3872710c711752 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 4 Mar 2018 19:51:45 -0500 Subject: [PATCH 0882/1329] Migrated fontbutton.m back. --- darwin/{OLD_fontbutton.m => fontbutton.m} | 27 ++++++++++++----------- darwin/main.m | 6 ++--- darwin/uipriv_darwin.h | 6 ++--- 3 files changed, 20 insertions(+), 19 deletions(-) rename darwin/{OLD_fontbutton.m => fontbutton.m} (87%) diff --git a/darwin/OLD_fontbutton.m b/darwin/fontbutton.m similarity index 87% rename from darwin/OLD_fontbutton.m rename to darwin/fontbutton.m index 433f261f..38205135 100644 --- a/darwin/OLD_fontbutton.m +++ b/darwin/fontbutton.m @@ -1,7 +1,8 @@ // 14 april 2016 #import "uipriv_darwin.h" +#import "attrstr.h" -@interface fontButton : NSButton { +@interface uiprivFontButton : NSButton { uiFontButton *libui_b; NSFont *libui_font; } @@ -15,16 +16,16 @@ @end // only one may be active at one time -static fontButton *activeFontButton = nil; +static uiprivFontButton *activeFontButton = nil; struct uiFontButton { uiDarwinControl c; - fontButton *button; + uiprivFontButton *button; void (*onChanged)(uiFontButton *, void *); void *onChangedData; }; -@implementation fontButton +@implementation uiprivFontButton - (id)initWithFrame:(NSRect)frame libuiFontButton:(uiFontButton *)b { @@ -145,7 +146,7 @@ struct uiFontButton { ctfont = (CTFontRef) (self->libui_font); ctdesc = CTFontCopyFontDescriptor(ctfont); - fontdescFromCTFontDescriptor(ctdesc, uidesc); + uiprivDrawFontDescriptorFromCTFontDescriptor(ctdesc, uidesc); CFRelease(ctdesc); uidesc->Size = CTFontGetSize(ctfont); } @@ -156,16 +157,16 @@ uiDarwinControlAllDefaults(uiFontButton, button) // we do not want font change events to be sent to any controls other than the font buttons // see main.m for more details -BOOL fontButtonInhibitSendAction(SEL sel, id from, id to) +BOOL uiprivFontButtonInhibitSendAction(SEL sel, id from, id to) { if (sel != @selector(changeFont:)) return NO; - return ![to isKindOfClass:[fontButton class]]; + return ![to isKindOfClass:[uiprivFontButton class]]; } // we do not want NSFontPanelValidation messages to be sent to any controls other than the font buttons when a font button is active // see main.m for more details -BOOL fontButtonOverrideTargetForAction(SEL sel, id from, id to, id *override) +BOOL uiprivFontButtonOverrideTargetForAction(SEL sel, id from, id to, id *override) { if (activeFontButton == nil) return NO; @@ -177,10 +178,10 @@ BOOL fontButtonOverrideTargetForAction(SEL sel, id from, id to, id *override) // we also don't want the panel to be usable when there's a dialog running; see stddialogs.m for more details on that // unfortunately the panel seems to ignore -setWorksWhenModal: so we'll have to do things ourselves -@interface nonModalFontPanel : NSFontPanel +@interface uiprivNonModalFontPanel : NSFontPanel @end -@implementation nonModalFontPanel +@implementation uiprivNonModalFontPanel - (BOOL)worksWhenModal { @@ -189,9 +190,9 @@ BOOL fontButtonOverrideTargetForAction(SEL sel, id from, id to, id *override) @end -void setupFontPanel(void) +void uiprivSetupFontPanel(void) { - [NSFontManager setFontPanelFactory:[nonModalFontPanel class]]; + [NSFontManager setFontPanelFactory:[uiprivNonModalFontPanel class]]; } static void defaultOnChanged(uiFontButton *b, void *data) @@ -216,7 +217,7 @@ uiFontButton *uiNewFontButton(void) uiDarwinNewControl(uiFontButton, b); - b->button = [[fontButton alloc] initWithFrame:NSZeroRect libuiFontButton:b]; + b->button = [[uiprivFontButton alloc] initWithFrame:NSZeroRect libuiFontButton:b]; uiDarwinSetControlFont(b->button, NSRegularControlSize); uiFontButtonOnChanged(b, defaultOnChanged, NULL); diff --git a/darwin/main.m b/darwin/main.m index d85000dd..12c528ff 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -26,7 +26,7 @@ static BOOL stepsIsRunning; { if (colorButtonInhibitSendAction(sel, from, to)) return NO; - if (fontButtonInhibitSendAction(sel, from, to)) + if (uiprivFontButtonInhibitSendAction(sel, from, to)) return NO; return [super sendAction:sel to:to from:from]; } @@ -38,7 +38,7 @@ static BOOL stepsIsRunning; { id override; - if (fontButtonOverrideTargetForAction(sel, from, to, &override)) + if (uiprivFontButtonOverrideTargetForAction(sel, from, to, &override)) return override; return [super targetForAction:sel to:to from:from]; } @@ -126,7 +126,7 @@ const char *uiInit(uiInitOptions *o) appDelegate().menuManager = [[menuManager new] autorelease]; [realNSApp() setMainMenu:[appDelegate().menuManager makeMenubar]]; - setupFontPanel(); + uiprivSetupFontPanel(); initUnderlineColors(); } diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 4d5ebdc3..0711c68b 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -111,9 +111,9 @@ extern uiDrawContext *newContext(CGContextRef, CGFloat); extern void freeContext(uiDrawContext *); // fontbutton.m -extern BOOL fontButtonInhibitSendAction(SEL sel, id from, id to); -extern BOOL fontButtonOverrideTargetForAction(SEL sel, id from, id to, id *override); -extern void setupFontPanel(void); +extern BOOL uiprivFontButtonInhibitSendAction(SEL sel, id from, id to); +extern BOOL uiprivFontButtonOverrideTargetForAction(SEL sel, id from, id to, id *override); +extern void uiprivSetupFontPanel(void); // colorbutton.m extern BOOL colorButtonInhibitSendAction(SEL sel, id from, id to); From 232b14ccde057c1cb22931c4fce196206540ab44 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 4 Mar 2018 20:01:52 -0500 Subject: [PATCH 0883/1329] Migrated attrstr.m. This file needs to be cleaned up... --- darwin/OLD_attrstr.h | 7 ---- darwin/attrstr.h | 6 ++++ darwin/{OLD_attrstr.m => attrstr.m} | 50 ++++++++++++++++------------- 3 files changed, 33 insertions(+), 30 deletions(-) delete mode 100644 darwin/OLD_attrstr.h rename darwin/{OLD_attrstr.m => attrstr.m} (89%) diff --git a/darwin/OLD_attrstr.h b/darwin/OLD_attrstr.h deleted file mode 100644 index 49f5fabc..00000000 --- a/darwin/OLD_attrstr.h +++ /dev/null @@ -1,7 +0,0 @@ -// 4 march 2018 - -// attrstr.m -extern void initUnderlineColors(void); -extern void uninitUnderlineColors(void); -typedef void (^backgroundBlock)(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y); -extern CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks); diff --git a/darwin/attrstr.h b/darwin/attrstr.h index 7b1dbb74..14a1cbe5 100644 --- a/darwin/attrstr.h +++ b/darwin/attrstr.h @@ -70,3 +70,9 @@ extern void uiprivProcessFontTraits(uiprivFontStyleData *d, uiDrawFontDescriptor // fontvariation.m extern NSDictionary *uiprivMakeVariationAxisDict(CFArrayRef axes, CFDataRef avarTable); extern void uiprivProcessFontVariation(uiprivFontStyleData *d, NSDictionary *axisDict, uiDrawFontDescriptor *out); + +// attrstr.m +extern void uiprivInitUnderlineColors(void); +extern void uiprivUninitUnderlineColors(void); +typedef void (^backgroundBlock)(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y); +extern CFAttributedStringRef uiprivAttributedStringToCFAttributedString(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks); diff --git a/darwin/OLD_attrstr.m b/darwin/attrstr.m similarity index 89% rename from darwin/OLD_attrstr.m rename to darwin/attrstr.m index fd45ec25..4d20bc37 100644 --- a/darwin/OLD_attrstr.m +++ b/darwin/attrstr.m @@ -1,5 +1,6 @@ // 12 february 2017 #import "uipriv_darwin.h" +#import "attrstr.h" // this is what AppKit does internally // WebKit does this too; see https://github.com/adobe/webkit/blob/master/Source/WebCore/platform/graphics/mac/GraphicsContextMac.mm @@ -17,7 +18,7 @@ static NSColor *tryColorNamed(NSString *name) return [NSColor colorWithPatternImage:img]; } -void initUnderlineColors(void) +void uiprivInitUnderlineColors(void) { spellingColor = tryColorNamed(@"NSSpellingDot"); if (spellingColor == nil) { @@ -47,11 +48,14 @@ void initUnderlineColors(void) [auxiliaryColor retain]; // override autoreleasing } -void uninitUnderlineColors(void) +void uiprivUninitUnderlineColors(void) { [auxiliaryColor release]; + auxiliaryColor = nil; [grammarColor release]; + grammarColors = nil; [spellingColor release]; + spellingColor = nil; } // unlike the other systems, Core Text rolls family, size, weight, italic, width, AND opentype features into the "font" attribute @@ -62,12 +66,12 @@ void uninitUnderlineColors(void) // TODO in fact I should just write something to explain everything in this file... struct foreachParams { CFMutableAttributedStringRef mas; - NSMutableDictionary *combinedFontAttrs; // keys are CFIndex in mas, values are combinedFontAttr objects + NSMutableDictionary *combinedFontAttrs; // keys are CFIndex in mas, values are uiprivCombinedFontAttr objects uiDrawFontDescriptor *defaultFont; NSMutableArray *backgroundBlocks; }; -@interface combinedFontAttr : NSObject +@interface uiprivCombinedFontAttr : NSObject @property const char *family; @property double size; @property uiDrawTextWeight weight; @@ -75,11 +79,11 @@ struct foreachParams { @property uiDrawTextStretch stretch; @property const uiOpenTypeFeatures *features; - (id)initWithDefaultFont:(uiDrawFontDescriptor *)defaultFont; -- (BOOL)same:(combinedFontAttr *)b; +- (BOOL)same:(uiprivCombinedFontAttr *)b; - (CTFontRef)toCTFont; @end -@implementation combinedFontAttr +@implementation uiprivCombinedFontAttr - (id)initWithDefaultFont:(uiDrawFontDescriptor *)defaultFont { @@ -97,7 +101,7 @@ struct foreachParams { } // TODO deduplicate this with common/attrlist.c -- (BOOL)same:(combinedFontAttr *)b +- (BOOL)same:(uiprivCombinedFontAttr *)b { // TODO should this be case-insensitive? if (strcmp(self.family, b.family) != 0) @@ -144,26 +148,26 @@ static void ensureFontInRange(struct foreachParams *p, size_t start, size_t end) { size_t i; NSNumber *n; - combinedFontAttr *new; + uiprivCombinedFontAttr *new; for (i = start; i < end; i++) { n = [NSNumber numberWithInteger:i]; if ([p->combinedFontAttrs objectForKey:n] != nil) continue; - new = [[combinedFontAttr alloc] initWithDefaultFont:p->defaultFont]; + new = [[uiprivCombinedFontAttr alloc] initWithDefaultFont:p->defaultFont]; [p->combinedFontAttrs setObject:new forKey:n]; } } -static void adjustFontInRange(struct foreachParams *p, size_t start, size_t end, void (^adj)(combinedFontAttr *cfa)) +static void adjustFontInRange(struct foreachParams *p, size_t start, size_t end, void (^adj)(uiprivCombinedFontAttr *cfa)) { size_t i; NSNumber *n; - combinedFontAttr *cfa; + uiprivCombinedFontAttr *cfa; for (i = start; i < end; i++) { n = [NSNumber numberWithInteger:i]; - cfa = (combinedFontAttr *) [p->combinedFontAttrs objectForKey:n]; + cfa = (uiprivCombinedFontAttr *) [p->combinedFontAttrs objectForKey:n]; adj(cfa); } } @@ -220,31 +224,31 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute switch (spec->Type) { case uiAttributeFamily: ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(combinedFontAttr *cfa) { + adjustFontInRange(p, start, end, ^(uiprivCombinedFontAttr *cfa) { cfa.family = spec->Family; }); break; case uiAttributeSize: ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(combinedFontAttr *cfa) { + adjustFontInRange(p, start, end, ^(uiprivCombinedFontAttr *cfa) { cfa.size = spec->Double; }); break; case uiAttributeWeight: ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(combinedFontAttr *cfa) { + adjustFontInRange(p, start, end, ^(uiprivCombinedFontAttr *cfa) { cfa.weight = (uiDrawTextWeight) (spec->Value); }); break; case uiAttributeItalic: ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(combinedFontAttr *cfa) { + adjustFontInRange(p, start, end, ^(uiprivCombinedFontAttr *cfa) { cfa.italic = (uiDrawTextItalic) (spec->Value); }); break; case uiAttributeStretch: ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(combinedFontAttr *cfa) { + adjustFontInRange(p, start, end, ^(uiprivCombinedFontAttr *cfa) { cfa.stretch = (uiDrawTextStretch) (spec->Value); }); break; @@ -300,7 +304,7 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute break; case uiAttributeFeatures: ensureFontInRange(p, start, end); - adjustFontInRange(p, start, end, ^(combinedFontAttr *cfa) { + adjustFontInRange(p, start, end, ^(uiprivCombinedFontAttr *cfa) { cfa.features = spec->Features; }); break; @@ -311,7 +315,7 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute return uiForEachContinue; } -static BOOL cfaIsEqual(combinedFontAttr *a, combinedFontAttr *b) +static BOOL cfaIsEqual(uiprivCombinedFontAttr *a, uiprivCombinedFontAttr *b) { if (a == nil && b == nil) return YES; @@ -323,13 +327,13 @@ static BOOL cfaIsEqual(combinedFontAttr *a, combinedFontAttr *b) static void applyAndFreeFontAttributes(struct foreachParams *p) { CFIndex i, n; - combinedFontAttr *cfa, *cfab; + uiprivCombinedFontAttr *cfa, *cfab; CTFontRef defaultFont; CTFontRef font; CFRange range; // first get the default font as a CTFontRef - cfa = [[combinedFontAttr alloc] initWithDefaultFont:p->defaultFont]; + cfa = [[uiprivCombinedFontAttr alloc] initWithDefaultFont:p->defaultFont]; defaultFont = [cfa toCTFont]; [cfa release]; @@ -344,7 +348,7 @@ static void applyAndFreeFontAttributes(struct foreachParams *p) // TODO use NSValue or some other method of NSNumber nn = [NSNumber numberWithInteger:i]; - cfab = (combinedFontAttr *) [p->combinedFontAttrs objectForKey:nn]; + cfab = (uiprivCombinedFontAttr *) [p->combinedFontAttrs objectForKey:nn]; if (cfaIsEqual(cfa, cfab)) continue; @@ -403,7 +407,7 @@ static CTParagraphStyleRef mkParagraphStyle(uiDrawTextLayoutParams *p) return ps; } -CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks) +CFAttributedStringRef uiprivAttributedStringToCFAttributedString(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks) { CFStringRef cfstr; CFMutableDictionaryRef defaultAttrs; From 49c3f77d4625d25d27aa42cca6c2f9258305bab4 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 4 Mar 2018 20:45:05 -0500 Subject: [PATCH 0884/1329] Started cleaning up attrstr.m. This is gonna be fun... --- darwin/attrstr.m | 42 +++++++++++++----------------------------- 1 file changed, 13 insertions(+), 29 deletions(-) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 4d20bc37..1b2db2ea 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -59,7 +59,7 @@ void uiprivUninitUnderlineColors(void) } // unlike the other systems, Core Text rolls family, size, weight, italic, width, AND opentype features into the "font" attribute -// TODO opentype features are lost, so a handful of fonts in the font panel ("Titling" variants of some fonts and possibly others but those are the examples I know about) cannot be represented by uiDrawFontDescriptor; what *can* we do about this since this info is NOT part of the font on other platforms? +// TODO opentype features are lost when using uiDrawFontDescriptor, so a handful of fonts in the font panel ("Titling" variants of some fonts and possibly others but those are the examples I know about) cannot be represented by uiDrawFontDescriptor; what *can* we do about this since this info is NOT part of the font on other platforms? // TODO see if we could use NSAttributedString? // TODO consider renaming this struct and the fep variable(s) // TODO restructure all this so the important details at the top are below with the combined font attributes type? @@ -100,7 +100,7 @@ struct foreachParams { return self; } -// TODO deduplicate this with common/attrlist.c +// TODO deduplicate this with common/attribute.c - (BOOL)same:(uiprivCombinedFontAttr *)b { // TODO should this be case-insensitive? @@ -135,7 +135,7 @@ struct foreachParams { uidesc.Stretch = self.stretch; desc = fontdescToCTFontDescriptor(&uidesc); if (self.features != NULL) - desc = fontdescAppendFeatures(desc, self.features); + desc = uiprivCTFontDescriptorAppendFeatures(desc, self.features); font = CTFontCreateWithFontDescriptor(desc, self.size, NULL); CFRelease(desc); // TODO correct? return font; @@ -143,22 +143,6 @@ struct foreachParams { @end -// TODO merge this with adjustFontInRange() (and TODO figure out why they were separate in the first place; probably Windows?) -static void ensureFontInRange(struct foreachParams *p, size_t start, size_t end) -{ - size_t i; - NSNumber *n; - uiprivCombinedFontAttr *new; - - for (i = start; i < end; i++) { - n = [NSNumber numberWithInteger:i]; - if ([p->combinedFontAttrs objectForKey:n] != nil) - continue; - new = [[uiprivCombinedFontAttr alloc] initWithDefaultFont:p->defaultFont]; - [p->combinedFontAttrs setObject:new forKey:n]; - } -} - static void adjustFontInRange(struct foreachParams *p, size_t start, size_t end, void (^adj)(uiprivCombinedFontAttr *cfa)) { size_t i; @@ -168,6 +152,10 @@ static void adjustFontInRange(struct foreachParams *p, size_t start, size_t end, for (i = start; i < end; i++) { n = [NSNumber numberWithInteger:i]; cfa = (uiprivCombinedFontAttr *) [p->combinedFontAttrs objectForKey:n]; + if (cfa == nil) { + cfa = [[uiprivCombinedFontAttr alloc] initWithDefaultFont:p->defaultFont]; + [p->combinedFontAttrs setObject:cfa forKey:n]; + } adj(cfa); } } @@ -192,6 +180,7 @@ static CGColorRef mkcolor(uiAttributeSpec *spec) CGColorRef color; CGFloat components[4]; + // TODO we should probably just create this once and recycle it throughout program execution... colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); if (colorspace == NULL) { // TODO @@ -205,7 +194,7 @@ static CGColorRef mkcolor(uiAttributeSpec *spec) return color; } -static uiForEach processAttribute(const uiAttributedString *s, const uiAttributeSpec *spec, size_t start, size_t end, void *data) +static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute *attr, size_t start, size_t end, void *data) { struct foreachParams *p = (struct foreachParams *) data; CFRange range; @@ -217,37 +206,33 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute ostart = start; oend = end; - start = attrstrUTF8ToUTF16(s, start); - end = attrstrUTF8ToUTF16(s, end); + start = uiprivAttributedStringUTF8ToUTF16(s, start); + end = uiprivAttributedStringUTF8ToUTF16(s, end); range.location = start; range.length = end - start; - switch (spec->Type) { + switch (uiAttributeGetType(attr)) { +$$TODO_CONTINUE_HERE case uiAttributeFamily: - ensureFontInRange(p, start, end); adjustFontInRange(p, start, end, ^(uiprivCombinedFontAttr *cfa) { cfa.family = spec->Family; }); break; case uiAttributeSize: - ensureFontInRange(p, start, end); adjustFontInRange(p, start, end, ^(uiprivCombinedFontAttr *cfa) { cfa.size = spec->Double; }); break; case uiAttributeWeight: - ensureFontInRange(p, start, end); adjustFontInRange(p, start, end, ^(uiprivCombinedFontAttr *cfa) { cfa.weight = (uiDrawTextWeight) (spec->Value); }); break; case uiAttributeItalic: - ensureFontInRange(p, start, end); adjustFontInRange(p, start, end, ^(uiprivCombinedFontAttr *cfa) { cfa.italic = (uiDrawTextItalic) (spec->Value); }); break; case uiAttributeStretch: - ensureFontInRange(p, start, end); adjustFontInRange(p, start, end, ^(uiprivCombinedFontAttr *cfa) { cfa.stretch = (uiDrawTextStretch) (spec->Value); }); @@ -303,7 +288,6 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute CFRelease(color); break; case uiAttributeFeatures: - ensureFontInRange(p, start, end); adjustFontInRange(p, start, end, ^(uiprivCombinedFontAttr *cfa) { cfa.features = spec->Features; }); From 9e57c78fb35051c80e1e41524a46d71536b5199f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 4 Mar 2018 23:57:18 -0500 Subject: [PATCH 0885/1329] Started really refactoring attrstr.m by turning uiprivCombinedFontAttr into an actual attribute that CFAttributedString will manage itself; it will store uiAttributes (so we can use uiprivAttributeEqual() instead of duplicating its logic). Later, we will make it convert to CTFontRefs in place. --- darwin/attrstr.m | 147 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 100 insertions(+), 47 deletions(-) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 1b2db2ea..7b0dc562 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -71,72 +71,125 @@ struct foreachParams { NSMutableArray *backgroundBlocks; }; -@interface uiprivCombinedFontAttr : NSObject -@property const char *family; -@property double size; -@property uiDrawTextWeight weight; -@property uiDrawTextItalic italic; -@property uiDrawTextStretch stretch; -@property const uiOpenTypeFeatures *features; -- (id)initWithDefaultFont:(uiDrawFontDescriptor *)defaultFont; -- (BOOL)same:(uiprivCombinedFontAttr *)b; -- (CTFontRef)toCTFont; +// TODO what if this is NULL? +static const CFStringRef combinedFontAttrName = CFSTR("libuiCombinedFontAttribute"); + +enum { + cFamily, + cSize, + cWeight, + cItalic, + cStretch, + cFeatures, + nc, +}; + +static const int toc[] = { + [uiAttributeFamily] = cFamily, + [uiAttributeSize] = cSize, + [uiAttributeWeight] = cWeight, + [uiAttributeItalic] = cItalic, + [uiAttributeStretch] = cStretch, + [uiAttributeFeatures] = cFeatures, +}; + +@interface uiprivCombinedFontAttr : NSObject { + uiAttribute *attrs[nc]; +} +- (void)setAttribute:(uiAttribute *)attr; +- (CTFontRef)toCTFontWithDefaultFont:(uiDrawFontDescriptor *)defaultFont; @end @implementation uiprivCombinedFontAttr -- (id)initWithDefaultFont:(uiDrawFontDescriptor *)defaultFont +- (id)init { self = [super init]; - if (self) { - // TODO define behaviors if defaultFont->Family or any attribute Family is NULL, same with other invalid values - self.family = defaultFont->Family; - self.size = defaultFont->Size; - self.weight = defaultFont->Weight; - self.italic = defaultFont->Italic; - self.stretch = defaultFont->Stretch; - self.features = NULL; - } + if (self) + memset(self->attrs, 0, nc * sizeof (uiAttribute *)); return self; } -// TODO deduplicate this with common/attribute.c -- (BOOL)same:(uiprivCombinedFontAttr *)b +- (void)dealloc { - // TODO should this be case-insensitive? - if (strcmp(self.family, b.family) != 0) - return NO; - // TODO use a closest match? - if (self.size != b.size) - return NO; - if (self.weight != b.weight) - return NO; - if (self.italic != b.italic) - return NO; - if (self.stretch != b.stretch) - return NO; - // this also handles NULL cases - if (!uiOpenTypeFeaturesEqual(self.features, b.features)) + int i; + + for (i = 0; i < nc; i++) + if (self->attrs[i] != NULL) { + uiprivAttributeRelease(self->attrs[i]); + self->attrs[i] = NULL; + } + [super dealloc]; +} + +- (id)copyWithZone:(NSZone *)zone +{ + uiprivCombinedFontAttr *ret; + int i; + + ret = [[uiprivCombinedFontAttr allocWithZone:zone] init]; + for (i = 0; i < nc; i++) + if (self->attrs[i] != NULL) + ret->attrs[i] = uiprivAttributeRetain(self->attrs[i]); + return ret; +} + +- (void)setAttribute:(uiAttribute *)attr +{ + int index; + + index = toc[uiAttributeGetType(attr)]; + if (self->attrs[index] != NULL) + uiprivAttributeRelease(self->attrs[index]); + self->attrs[index] = uiprivAttributeRetain(attr); +} + +- (BOOL)isEqual:(id)bb +{ + uiprivCombinedFontAttr *b = (uiprivCombinedFontAttr *) bb; + int i; + + if (b == nil) return NO; + for (i = 0; i < nc; i++) { + if (self->attrs[i] == NULL && b->attrs[i] == NULL) + continue; + if (self->attrs[i] == NULL || b->attrs[i] == NULL) + return NO; + if (!uiprivAttributeEqual(self->attrs[i], b->attrs[i])) + return NO; + } return YES; } -- (CTFontRef)toCTFont +- (NSUInteger)hash +{ + // TODO implement this somehow; not quite sure how... + return [super hash]; +} + +- (CTFontRef)toCTFontWithDefaultFont:(uiDrawFontDescriptor *)defaultFont { uiDrawFontDescriptor uidesc; CTFontDescriptorRef desc; CTFontRef font; - // TODO const-correct uiDrawFontDescriptor or change this function below - uidesc.Family = (char *) (self.family); - uidesc.Size = self.size; - uidesc.Weight = self.weight; - uidesc.Italic = self.italic; - uidesc.Stretch = self.stretch; - desc = fontdescToCTFontDescriptor(&uidesc); - if (self.features != NULL) - desc = uiprivCTFontDescriptorAppendFeatures(desc, self.features); - font = CTFontCreateWithFontDescriptor(desc, self.size, NULL); + uidesc = *defaultFont; + if (self->attrs[cFamily] != NULL) + // TODO const-correct uiDrawFontDescriptor or change this function below + uidesc.Family = (char *) uiAttributeFamily(self->attrs[cFamily]); + if (self->attrs[cSize] != NULL) + uidesc.Size = uiAttributeSize(self->attrs[cSize]); + if (self->attrs[cWeight] != NULL) + uidesc.Weight = uiAttributeWeight(self->attrs[cWeight]); + if (self->attrs[cItalic] != NULL) + uidesc.Italic = uiAttributeItalic(self->attrs[cItalic]); + if (self->attrs[cStretch] != NULL) + uidesc.Stretch = uiAttributeStretch(self->attrs[cStretch]); + desc = uiprivDrawFontDescriptorToCTFontDescriptor(&uidesc); + if (self->attrs[cFeatures] != NULL) + desc = uiprivCTFontDescriptorAppendFeatures(desc, uiAttributeFeatures(self->attrs[cFeatures])); + font = CTFontCreateWithFontDescriptor(desc, uidesc.Size, NULL); CFRelease(desc); // TODO correct? return font; } From 93c375fd940feef55e71d74b6bc66719a6c51a70 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 5 Mar 2018 08:59:45 -0500 Subject: [PATCH 0886/1329] Implemented -[uiprivCombinedFontAttr hash]. --- darwin/attrstr.m | 64 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 9 deletions(-) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 7b0dc562..268ed00c 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -85,16 +85,32 @@ enum { }; static const int toc[] = { - [uiAttributeFamily] = cFamily, - [uiAttributeSize] = cSize, - [uiAttributeWeight] = cWeight, - [uiAttributeItalic] = cItalic, - [uiAttributeStretch] = cStretch, - [uiAttributeFeatures] = cFeatures, + [uiAttributeTypeFamily] = cFamily, + [uiAttributeTypeSize] = cSize, + [uiAttributeTypeWeight] = cWeight, + [uiAttributeTypeItalic] = cItalic, + [uiAttributeTypeStretch] = cStretch, + [uiAttributeTypeFeatures] = cFeatures, }; +static uiForEach featuresHash(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data) +{ + NSUInteger *hash = (NSUInteger *) data; + uint32_t tag; + + tag = (((uint32_t) a) & 0xFF) << 24; + tag |= (((uint32_t) b) & 0xFF) << 16; + tag |= (((uint32_t) c) & 0xFF) << 8; + tag |= ((uint32_t) d) & 0xFF; + *hash ^= tag; + *hash ^= value; + return uiForEachContinue; +} + @interface uiprivCombinedFontAttr : NSObject { uiAttribute *attrs[nc]; + BOOL hasHash; + NSUInteger hash; } - (void)setAttribute:(uiAttribute *)attr; - (CTFontRef)toCTFontWithDefaultFont:(uiDrawFontDescriptor *)defaultFont; @@ -105,8 +121,10 @@ static const int toc[] = { - (id)init { self = [super init]; - if (self) + if (self) { memset(self->attrs, 0, nc * sizeof (uiAttribute *)); + self->hasHash = NO; + } return self; } @@ -131,6 +149,8 @@ static const int toc[] = { for (i = 0; i < nc; i++) if (self->attrs[i] != NULL) ret->attrs[i] = uiprivAttributeRetain(self->attrs[i]); + ret->hasHash = self->hasHash; + ret->hash = self->hash; return ret; } @@ -142,6 +162,7 @@ static const int toc[] = { if (self->attrs[index] != NULL) uiprivAttributeRelease(self->attrs[index]); self->attrs[index] = uiprivAttributeRetain(attr); + self->hasHash = NO; } - (BOOL)isEqual:(id)bb @@ -164,8 +185,33 @@ static const int toc[] = { - (NSUInteger)hash { - // TODO implement this somehow; not quite sure how... - return [super hash]; + if (self->hasHash) + return self->hash; + @autoreleasepool { + NSString *family; + NSNumber *size; + + self->hash = 0; + if (self->attrs[cFamily] != NULL) { + family = [NSString stringWithUTF8String:uiAttributeFamily(self->attrs[cFamily])]; + // TODO make sure this aligns with case-insensitive compares when those are done in common/attribute.c + self->hash ^= [[family uppercaseString] hash]; + } + if (self->attrs[cSize] != NULL) { + size = [NSNumber numberWithDouble:uiAttributeSize(self->attrs[cSize])]; + self->hash ^= [size hash]; + } + if (self->attrs[cWeight] != NULL) + self->hash ^= (NSUInteger) uiAttributeWeight(self->attrs[cWeight]); + if (self->attrs[cItalic] != NULL) + self->hash ^= (NSUInteger) uiAttributeItalic(self->attrs[cItalic]); + if (self->attrs[cStretch] != NULL) + self->hash ^= (NSUInteger) uiAttributeStretch(self->attrs[cStretch]); + if (self->attrs[cFeatures] != NULL) + uiOpenTypeFeaturesForEach(uiAttributeFeatures(self->attrs[cFeatures]), featuresHash, &(self->hash)); + self->hasHash = YES; + } + return self->hash; } - (CTFontRef)toCTFontWithDefaultFont:(uiDrawFontDescriptor *)defaultFont From 00749b07ac073790ebd01d36ba379afeafac282c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 5 Mar 2018 23:15:31 -0500 Subject: [PATCH 0887/1329] And cleaned up all the font handling stuff in attrstr.m. A lot cleaner now! Not fully clean, but clean*er*, and probably more efficient, too... --- darwin/attrstr.m | 175 ++++++++++++++++++----------------------------- 1 file changed, 65 insertions(+), 110 deletions(-) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 268ed00c..e2a53e0d 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -58,7 +58,6 @@ void uiprivUninitUnderlineColors(void) spellingColor = nil; } -// unlike the other systems, Core Text rolls family, size, weight, italic, width, AND opentype features into the "font" attribute // TODO opentype features are lost when using uiDrawFontDescriptor, so a handful of fonts in the font panel ("Titling" variants of some fonts and possibly others but those are the examples I know about) cannot be represented by uiDrawFontDescriptor; what *can* we do about this since this info is NOT part of the font on other platforms? // TODO see if we could use NSAttributedString? // TODO consider renaming this struct and the fep variable(s) @@ -66,11 +65,13 @@ void uiprivUninitUnderlineColors(void) // TODO in fact I should just write something to explain everything in this file... struct foreachParams { CFMutableAttributedStringRef mas; - NSMutableDictionary *combinedFontAttrs; // keys are CFIndex in mas, values are uiprivCombinedFontAttr objects - uiDrawFontDescriptor *defaultFont; NSMutableArray *backgroundBlocks; }; +// unlike the other systems, Core Text rolls family, size, weight, italic, width, AND opentype features into the "font" attribute +// instead of incrementally adjusting CTFontRefs (which, judging from NSFontManager, seems finicky and UI-centric), we use a custom class to incrementally store attributes that go into a CTFontRef, and then convert everything to CTFonts en masse later +// https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/AttributedStrings/Tasks/ChangingAttrStrings.html#//apple_ref/doc/uid/20000162-BBCBGCDG says we must have -hash and -isEqual: workign properly for this to work, so we must do that too, using a basic xor-based hash and leveraging Cocoa -hash implementations where useful and feasible (if not necessary) +// TODO structure and rewrite this part // TODO what if this is NULL? static const CFStringRef combinedFontAttrName = CFSTR("libuiCombinedFontAttribute"); @@ -112,7 +113,7 @@ static uiForEach featuresHash(const uiOpenTypeFeatures *otf, char a, char b, cha BOOL hasHash; NSUInteger hash; } -- (void)setAttribute:(uiAttribute *)attr; +- (void)addAttribute:(uiAttribute *)attr; - (CTFontRef)toCTFontWithDefaultFont:(uiDrawFontDescriptor *)defaultFont; @end @@ -154,7 +155,7 @@ static uiForEach featuresHash(const uiOpenTypeFeatures *otf, char a, char b, cha return ret; } -- (void)setAttribute:(uiAttribute *)attr +- (void)addAttribute:(uiAttribute *)attr { int index; @@ -242,20 +243,30 @@ static uiForEach featuresHash(const uiOpenTypeFeatures *otf, char a, char b, cha @end -static void adjustFontInRange(struct foreachParams *p, size_t start, size_t end, void (^adj)(uiprivCombinedFontAttr *cfa)) +static void addFontAttributeToRange(struct foreachParams *p, size_t start, size_t end, uiAttribute *attr) { - size_t i; - NSNumber *n; uiprivCombinedFontAttr *cfa; + CFRange range; + size_t diff; - for (i = start; i < end; i++) { - n = [NSNumber numberWithInteger:i]; - cfa = (uiprivCombinedFontAttr *) [p->combinedFontAttrs objectForKey:n]; - if (cfa == nil) { - cfa = [[uiprivCombinedFontAttr alloc] initWithDefaultFont:p->defaultFont]; - [p->combinedFontAttrs setObject:cfa forKey:n]; + while (start <= end) { + cfa = (uiprivCombinedFontAttr *) CFAttributedStringGetAttribute(p->mas, start, combinedFontAttrName, &range); + if (cfa == nil) + cfa = [uiprivCombinedFontAttr new]; + else + cfa = [cfa copy]; + [cfa addAttribute:attr]; + // clamp effectiveRange within [start, end) + if (range.location < start) { + diff = start - range.location; + range.location = start; + range.length -= diff; } - adj(cfa); + if ((range.location + range.length) > end) + range.length = end - range.location; + CFAttributedStringSetAttribute(p->mas, range, combinedFontAttrName, cfa); + [cfa release]; + start += effectiveRange.length; } } @@ -310,32 +321,15 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute range.location = start; range.length = end - start; switch (uiAttributeGetType(attr)) { + case uiAttributeTypeFamily: + case uiAttributeTypeSize: + case uiAttributeTypeWeight: + case uiAttributeTypeItalic: + case uiAttributeTypeStretch: + case uiAttributeTypeFeatures: + addFontAttributeToRange(p, start, end, attr); + break; $$TODO_CONTINUE_HERE - case uiAttributeFamily: - adjustFontInRange(p, start, end, ^(uiprivCombinedFontAttr *cfa) { - cfa.family = spec->Family; - }); - break; - case uiAttributeSize: - adjustFontInRange(p, start, end, ^(uiprivCombinedFontAttr *cfa) { - cfa.size = spec->Double; - }); - break; - case uiAttributeWeight: - adjustFontInRange(p, start, end, ^(uiprivCombinedFontAttr *cfa) { - cfa.weight = (uiDrawTextWeight) (spec->Value); - }); - break; - case uiAttributeItalic: - adjustFontInRange(p, start, end, ^(uiprivCombinedFontAttr *cfa) { - cfa.italic = (uiDrawTextItalic) (spec->Value); - }); - break; - case uiAttributeStretch: - adjustFontInRange(p, start, end, ^(uiprivCombinedFontAttr *cfa) { - cfa.stretch = (uiDrawTextStretch) (spec->Value); - }); - break; case uiAttributeColor: color = mkcolor(spec); CFAttributedStringSetAttribute(p->mas, range, kCTForegroundColorAttributeName, color); @@ -347,6 +341,7 @@ $$TODO_CONTINUE_HERE [p->backgroundBlocks addObject:block]; Block_release(block); break; + // TODO turn into a class, like we did with the font attributes, or even integrate *into* the font attributes case uiAttributeUnderline: switch (spec->Value) { case uiDrawUnderlineStyleNone: @@ -386,84 +381,47 @@ $$TODO_CONTINUE_HERE if (spec->Value == uiDrawUnderlineColorCustom) CFRelease(color); break; - case uiAttributeFeatures: - adjustFontInRange(p, start, end, ^(uiprivCombinedFontAttr *cfa) { - cfa.features = spec->Features; - }); - break; - default: - // TODO complain - ; } return uiForEachContinue; } -static BOOL cfaIsEqual(uiprivCombinedFontAttr *a, uiprivCombinedFontAttr *b) +static void applyFontAttributes(CFMutableAttributedStringRef mas, uiDrawFontDescriptor *defaultFont) { - if (a == nil && b == nil) - return YES; - if (a == nil || b == nil) - return NO; - return [a same:b]; -} - -static void applyAndFreeFontAttributes(struct foreachParams *p) -{ - CFIndex i, n; - uiprivCombinedFontAttr *cfa, *cfab; - CTFontRef defaultFont; + uiprivCombinedFontAttr *cfa; CTFontRef font; CFRange range; + CFIndex n; - // first get the default font as a CTFontRef - cfa = [[uiprivCombinedFontAttr alloc] initWithDefaultFont:p->defaultFont]; - defaultFont = [cfa toCTFont]; + n = CFAttributedStringGetLength(mas); + + // first apply the default font to the entire string + // TODO is this necessary given the #if 0'd code in uiprivAttributedStringToCFAttributedString()? + cfa = [uiprivCombinedFontAttr new]; + font = [cfa toCTFontWithDefaultFont:defaultFont]; [cfa release]; - - // now go through, fililng in the font attribute for successive ranges of identical combinedFontAttrs - // we are best off treating series of identical fonts as single ranges ourselves for parity across platforms, even if OS X does something similar itself - // this also avoids breaking apart surrogate pairs (though IIRC OS X doing the something similar itself might make this a non-issue here) - cfa = nil; - n = CFAttributedStringGetLength(p->mas); range.location = 0; - for (i = 0; i < n; i++) { - NSNumber *nn; - - // TODO use NSValue or some other method of NSNumber - nn = [NSNumber numberWithInteger:i]; - cfab = (uiprivCombinedFontAttr *) [p->combinedFontAttrs objectForKey:nn]; - if (cfaIsEqual(cfa, cfab)) - continue; - - // the font has changed; write out the old one - range.length = i - range.location; - if (cfa == nil) { - // TODO this isn't necessary because of default font shenanigans below - font = defaultFont; - CFRetain(font); - } else - font = [cfa toCTFont]; - CFAttributedStringSetAttribute(p->mas, range, kCTFontAttributeName, font); - CFRelease(font); - // and start this run - cfa = cfab; - range.location = i; - } - - // and finally, write out the last range - range.length = i - range.location; - if (cfa == nil) { - // TODO likewise - font = defaultFont; - CFRetain(font); - } else - // note that this handles the difference between NULL and empty uiOpenTypeFeatures properly as far as conversion to native data formats is concerned (NULL does not generate a features dictionary; empty just produces an empty one) - // TODO but what about from the OS's perspective on all OSs? - font = [cfa toCTFont]; - CFAttributedStringSetAttribute(p->mas, range, kCTFontAttributeName, font); + range.length = n; + CFAttributedStringSetAttribute(mas, range, kCTFontAttributeName, font); CFRelease(font); - CFRelease(defaultFont); + // now go through, replacing every consecutive uiprivCombinedFontAttr with the proper CTFontRef + // we are best off treating series of identical fonts as single ranges ourselves for parity across platforms, even if OS X does something similar itself + range.location = 0; + while (range.location < n) { + // TODO consider seeing if CFAttributedStringGetAttributeAndLongestEffectiveRange() can make things faster by reducing the number of potential iterations, either here or above + cfa = (uiprivCombinedFontAttr *) CFAttributedStringSetAttribute(mas, range.location, combinedFontAttrName, &range); + if (cfa != nil) { + font = [cfa toCTFontWithDefaultFont:defaultFont]; + CFAttributedStringSetAttribute(mas, range, kCTFontAttributeName, font); + CFRelease(font); + } + range.location += range.length; + } + + // and finally, get rid of all the uiprivCombinedFontAttrs as we won't need them anymore + range.location = 0; + range.length = 0; + CFAttributedStringRemoveAttribute(mas, range, combinedFontAttrName); } static const CTTextAlignment ctaligns[] = { @@ -531,12 +489,9 @@ CFAttributedStringRef uiprivAttributedStringToCFAttributedString(uiDrawTextLayou CFAttributedStringBeginEditing(mas); fep.mas = mas; - fep.combinedFontAttrs = [NSMutableDictionary new]; - fep.defaultFont = p->DefaultFont; fep.backgroundBlocks = [NSMutableArray new]; uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); - applyAndFreeFontAttributes(&fep); - [fep.combinedFontAttrs release]; + applyFontAttributes(mas, p->DefaultFont); CFAttributedStringEndEditing(mas); *backgroundBlocks = fep.backgroundBlocks; From bf58601ff8ab6468ef8a0dfe62bcc1505a83ab41 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 5 Mar 2018 23:16:43 -0500 Subject: [PATCH 0888/1329] More TODOs. --- darwin/attrstr.m | 1 + 1 file changed, 1 insertion(+) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index e2a53e0d..6d443648 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -72,6 +72,7 @@ struct foreachParams { // instead of incrementally adjusting CTFontRefs (which, judging from NSFontManager, seems finicky and UI-centric), we use a custom class to incrementally store attributes that go into a CTFontRef, and then convert everything to CTFonts en masse later // https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/AttributedStrings/Tasks/ChangingAttrStrings.html#//apple_ref/doc/uid/20000162-BBCBGCDG says we must have -hash and -isEqual: workign properly for this to work, so we must do that too, using a basic xor-based hash and leveraging Cocoa -hash implementations where useful and feasible (if not necessary) // TODO structure and rewrite this part +// TODO re-find sources proving support of custom attributes // TODO what if this is NULL? static const CFStringRef combinedFontAttrName = CFSTR("libuiCombinedFontAttribute"); From 7451d455e5fa830f8cdd6d211218593f8588536b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 7 Mar 2018 23:53:36 -0500 Subject: [PATCH 0889/1329] Started a new drawtext.m with a different way to handle the empty-string crash problem. --- darwin/attrstr.m | 1 + darwin/drawtext.m | 127 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 darwin/drawtext.m diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 6d443648..439da2c9 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -449,6 +449,7 @@ static CTParagraphStyleRef mkParagraphStyle(uiDrawTextLayoutParams *p) return ps; } +// TODO either rename this to uiprivDrawTextLayoutParams... or rename this file or both or split the struct or something else... CFAttributedStringRef uiprivAttributedStringToCFAttributedString(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks) { CFStringRef cfstr; diff --git a/darwin/drawtext.m b/darwin/drawtext.m new file mode 100644 index 00000000..e3cc328f --- /dev/null +++ b/darwin/drawtext.m @@ -0,0 +1,127 @@ +// 7 march 2018 +#import "uipriv_darwin.h" +#import "draw.h" +#import "attrstr.h" + +// problem: for a CTFrame made from an empty string, the CTLine array will be empty, and we will crash when gathering metrics or hit-testing +// solution: for those cases, maintain a separate framesetter just for computing those things +// in the usual case, the separate copy will just be identical to the regular one, with extra references to everything within +struct frame { + CFAttributedStringRef attrstr; + NSArray *backgroundBlocks; + CTFramesetterRef framesetter; + CGSize size; + CGPathRef path; + CTFrameRef frame; +}; + +struct uiDrawTextLayout { + struct frame forDrawing; + struct frame forMetrics; +}; + +static void paramsToFrame(uiDrawTextLayoutParams *params, struct frame *frame) +{ + CFRange range; + CGFloat width; + CFRange unused; + CGRect rect; + + frame->attrstr = uiprivAttributedStringToCFAttributedString(p, &(frame->backgroundBlocks)); + // TODO kCTParagraphStyleSpecifierMaximumLineSpacing, kCTParagraphStyleSpecifierMinimumLineSpacing, kCTParagraphStyleSpecifierLineSpacingAdjustment for line spacing + frame->framesetter = CTFramesetterCreateWithAttributedString(tl->attrstr); + if (frame->framesetter == NULL) { + // TODO + } + + range.location = 0; + range.length = CFAttributedStringGetLength(tl->attrstr); + + cgwidth = (CGFloat) (frame->width); + if (cgwidth < 0) + cgwidth = CGFLOAT_MAX; + frame->size = CTFramesetterSuggestFrameSizeWithConstraints(frame->framesetter, + range, + // TODO kCTFramePathWidthAttributeName? + NULL, + CGSizeMake(cgwidth, CGFLOAT_MAX), + &unused); // not documented as accepting NULL (TODO really?) + + rect.origin = CGPointZero; + rect.size = frame->size; + frame->path = CGPathCreateWithRect(rect, NULL); + frame->frame = CTFramesetterCreateFrame(tl->framesetter, + range, + tl->path, + // TODO kCTFramePathWidthAttributeName? + NULL); + if (frame->frame == NULL) { + // TODO + } +} + +static void freeFrame(struct frame *frame) +{ + CFRelease(frame->frame); + CFRelease(frame->path); + CFRelease(frame->framesetter); + [frame->backgroundBlocks release]; + CFRelease(frame->attrstr); +} + +static void retainFrameCopy(struct frame *out, const struct frame *frame) +{ + memcpy(out, frame, sizeof (struct frame)); + CFRetain(out->attrstr); + [out->backgroundBlocks retain]; + CFRetain(out->framesetter); + CFRetain(out->path); + CFRetain(out->frame); +} + +uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) +{ + uiDrawTextLayout *tl; + + tl = uiprivNew(uiDrawTextLayout); + paramsToFrame(p, &(tl->forDrawing)); + if (uiAttributedStringLength(p->String) != 0) + retainFrameCopy(&(tl->forMetrics), &(tl->forDrawing)); + else { + uiAttributedString *space; + uiDrawTextLayoutParams p2; + + space = uiNewAttributedString(" "); + p2 = *p; + p2.String = space; + paramsToFrame(&p2, &(tl->forMetrics)); + uiFreeAttributedString(space); + } + return tl; +} + +void uiDrawFreeTextLayout(uiDrawTextLayout *tl) +{ + freeFrame(&(tl->forMetrics)); + freeFrame(&(tl->forDrawing)); + uiprivFree(tl); +} + +// uiDrawText() draws tl in c with the top-left point of tl at (x, y). +_UI_EXTERN void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y); + +// uiDrawTextLayoutExtents() returns the width and height of tl +// in width and height. The returned width may be smaller than +// the width passed into uiDrawNewTextLayout() depending on +// how the text in tl is wrapped. Therefore, you can use this +// function to get the actual size of the text layout. +_UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height); + +// uiDrawTextLayoutNumLines() returns the number of lines in tl. +// This number will always be greater than or equal to 1; a text +// layout with no text only has one line. +_UI_EXTERN int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl); + +// uiDrawTextLayoutLineByteRange() returns the byte indices of the +// text that falls into the given line of tl as [start, end). +_UI_EXTERN void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end); From 5535c43bd8297ccbbdff4f2ef23067e24989c4bc Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 8 Mar 2018 22:38:53 -0500 Subject: [PATCH 0890/1329] And finished up drawtext.m for now. --- darwin/drawtext.m | 225 +++++++++++++++++++++++++++++++--------------- 1 file changed, 154 insertions(+), 71 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index e3cc328f..ed200f76 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -3,125 +3,208 @@ #import "draw.h" #import "attrstr.h" -// problem: for a CTFrame made from an empty string, the CTLine array will be empty, and we will crash when gathering metrics or hit-testing +// problem: for a CTFrame made from an empty string, the CTLine array will be empty, and we will crash when doing anything requiring a CTLine // solution: for those cases, maintain a separate framesetter just for computing those things // in the usual case, the separate copy will just be identical to the regular one, with extra references to everything within -struct frame { +@interface uiprivTextFrame { CFAttributedStringRef attrstr; NSArray *backgroundBlocks; CTFramesetterRef framesetter; CGSize size; CGPathRef path; CTFrameRef frame; -}; +} +- (id)initWithLayoutParams:(uiDrawTextLayoutParams *)p; +- (void)draw:(uiDrawContext *)c textLayout:(uiDrawTextLayout *)tl at:(double)x y:(double)y; +- (void)returnWidth:(double *)width height:(double *)height; +- (CFArrayRef)lines; +@end -struct uiDrawTextLayout { - struct frame forDrawing; - struct frame forMetrics; -}; +@implementation uiprivTextFrame -static void paramsToFrame(uiDrawTextLayoutParams *params, struct frame *frame) +- (id)initWithLayoutParams:(uiDrawTextLayoutParams *)p { CFRange range; CGFloat width; CFRange unused; CGRect rect; - frame->attrstr = uiprivAttributedStringToCFAttributedString(p, &(frame->backgroundBlocks)); - // TODO kCTParagraphStyleSpecifierMaximumLineSpacing, kCTParagraphStyleSpecifierMinimumLineSpacing, kCTParagraphStyleSpecifierLineSpacingAdjustment for line spacing - frame->framesetter = CTFramesetterCreateWithAttributedString(tl->attrstr); - if (frame->framesetter == NULL) { - // TODO - } - - range.location = 0; - range.length = CFAttributedStringGetLength(tl->attrstr); - - cgwidth = (CGFloat) (frame->width); - if (cgwidth < 0) - cgwidth = CGFLOAT_MAX; - frame->size = CTFramesetterSuggestFrameSizeWithConstraints(frame->framesetter, - range, - // TODO kCTFramePathWidthAttributeName? - NULL, - CGSizeMake(cgwidth, CGFLOAT_MAX), - &unused); // not documented as accepting NULL (TODO really?) - - rect.origin = CGPointZero; - rect.size = frame->size; - frame->path = CGPathCreateWithRect(rect, NULL); - frame->frame = CTFramesetterCreateFrame(tl->framesetter, - range, - tl->path, - // TODO kCTFramePathWidthAttributeName? - NULL); - if (frame->frame == NULL) { - // TODO + self = [super init]; + if (self) { + self->attrstr = uiprivAttributedStringToCFAttributedString(p, &(self->backgroundBlocks)); + // TODO kCTParagraphStyleSpecifierMaximumLineSpacing, kCTParagraphStyleSpecifierMinimumLineSpacing, kCTParagraphStyleSpecifierLineSpacingAdjustment for line spacing + self->framesetter = CTFramesetterCreateWithAttributedString(self->attrstr); + if (self->framesetter == NULL) { + // TODO + } + + range.location = 0; + range.length = CFAttributedStringGetLength(self->attrstr); + + cgwidth = (CGFloat) (p->Width); + if (cgwidth < 0) + cgwidth = CGFLOAT_MAX; + self->size = CTFramesetterSuggestFrameSizeWithConstraints(self->framesetter, + range, + // TODO kCTFramePathWidthAttributeName? + NULL, + CGSizeMake(cgwidth, CGFLOAT_MAX), + &unused); // not documented as accepting NULL (TODO really?) + + rect.origin = CGPointZero; + rect.size = self->size; + self->path = CGPathCreateWithRect(rect, NULL); + self->frame = CTFramesetterCreateFrame(tl->framesetter, + range, + self->path, + // TODO kCTFramePathWidthAttributeName? + NULL); + if (self->frame == NULL) { + // TODO + } } + return self; } -static void freeFrame(struct frame *frame) +- (void)dealloc { - CFRelease(frame->frame); - CFRelease(frame->path); - CFRelease(frame->framesetter); - [frame->backgroundBlocks release]; - CFRelease(frame->attrstr); + CFRelease(self->frame); + CFRelease(self->path); + CFRelease(self->framesetter); + [self->backgroundBlocks release]; + CFRelease(self->attrstr); + [super dealloc]; } -static void retainFrameCopy(struct frame *out, const struct frame *frame) +- (void)draw:(uiDrawContext *)c textLayout:(uiDrawTextLayout *)tl at:(double)x y:(double)y { - memcpy(out, frame, sizeof (struct frame)); - CFRetain(out->attrstr); - [out->backgroundBlocks retain]; - CFRetain(out->framesetter); - CFRetain(out->path); - CFRetain(out->frame); + backgroundBlock b; + CGAffineTransform textMatrix; + + CGContextSaveGState(c->c); + // save the text matrix because it's not part of the graphics state + textMatrix = CGContextGetTextMatrix(c->c); + + for (b in self->backgroundBlocks) + b(c, tl, x, y); + + // Core Text doesn't draw onto a flipped view correctly; we have to pretend it was unflipped + // see the iOS bits of the first example at https://developer.apple.com/library/mac/documentation/StringsTextFonts/Conceptual/CoreText_Programming/LayoutOperations/LayoutOperations.html#//apple_ref/doc/uid/TP40005533-CH12-SW1 (iOS is naturally flipped) + // TODO how is this affected by a non-identity CTM? + CGContextTranslateCTM(c->c, 0, c->height); + CGContextScaleCTM(c->c, 1.0, -1.0); + CGContextSetTextMatrix(c->c, CGAffineTransformIdentity); + + // wait, that's not enough; we need to offset y values to account for our new flipping + // TODO explain this calculation + y = c->height - self->size.height - y; + + // CTFrameDraw() draws in the path we specified when creating the frame + // this means that in our usage, CTFrameDraw() will draw at (0,0) + // so move the origin to be at (x,y) instead + // TODO are the signs correct? + CGContextTranslateCTM(c->c, x, y); + + CTFrameDraw(self->frame, c->c); + + CGContextSetTextMatrix(c->c, textMatrix); + CGContextRestoreGState(c->c); } +- (void)returnWidth:(double *)width height:(double *)height +{ + if (width != NULL) + *width = self->size.width; + if (height != NULL) + *height = self->size.height; +} + +- (CFArrayRef)lines +{ + return CTFrameGetLines(self->frame); +} + +@end + +struct uiDrawTextLayout { + uiprivTextFrame *frame; + uiprivTextFrame *forLines; + BOOL empty; + + // for converting CFAttributedString indices from/to byte offsets + size_t *u8tou16; + size_t nUTF8; + size_t *u16tou8; + size_t nUTF16; +}; + uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) { uiDrawTextLayout *tl; tl = uiprivNew(uiDrawTextLayout); - paramsToFrame(p, &(tl->forDrawing)); + tl->frame = [[uiprivTextFrame alloc] initWithLayoutParams:p]; if (uiAttributedStringLength(p->String) != 0) - retainFrameCopy(&(tl->forMetrics), &(tl->forDrawing)); + tl->forLines = [tl->frame retain]; else { uiAttributedString *space; uiDrawTextLayoutParams p2; + tl->empty = YES; space = uiNewAttributedString(" "); p2 = *p; p2.String = space; - paramsToFrame(&p2, &(tl->forMetrics)); + tl->forLines = [[uiprivTextFrame alloc] initWithLayoutParams:&p2]; uiFreeAttributedString(space); } + + // and finally copy the UTF-8/UTF-16 conversion tables + tl->u8tou16 = uiprivAttributedStringCopyUTF8ToUTF16Table(p->String, &(tl->nUTF8)); + tl->u16tou8 = uiprivAttributedStringCopyUTF16ToUTF8Table(p->String, &(tl->nUTF16)); return tl; } void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { - freeFrame(&(tl->forMetrics)); - freeFrame(&(tl->forDrawing)); + uiprivFree(tl->u16tou8); + uiprivFree(tl->u8tou16); + [tl->forLines release]; + [tl->frame release]; uiprivFree(tl); } -// uiDrawText() draws tl in c with the top-left point of tl at (x, y). -_UI_EXTERN void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y); +// TODO document that (x,y) is the top-left corner of the *entire frame* +void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) +{ + [tl->frame draw:c textLayout:tl at:x y:y]; +} -// uiDrawTextLayoutExtents() returns the width and height of tl -// in width and height. The returned width may be smaller than -// the width passed into uiDrawNewTextLayout() depending on -// how the text in tl is wrapped. Therefore, you can use this -// function to get the actual size of the text layout. -_UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height); +// TODO document that the width and height of a layout is not necessarily the sum of the widths and heights of its constituent lines +// TODO width doesn't include trailing whitespace... +// TODO figure out how paragraph spacing should play into this +// TODO standardize and document the behavior of this on an empty layout +void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height) +{ + // TODO explain this, given the above + [tl->frame returnWidth:width height:NULL]; + [tl->forLines returnWidth:NULL height:height]; +} -// uiDrawTextLayoutNumLines() returns the number of lines in tl. -// This number will always be greater than or equal to 1; a text -// layout with no text only has one line. -_UI_EXTERN int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl); +int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) +{ + return CFArrayGetCount([tl->forLines lines]); +} -// uiDrawTextLayoutLineByteRange() returns the byte indices of the -// text that falls into the given line of tl as [start, end). -_UI_EXTERN void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end); +void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end) +{ + CTLineRef lr; + CFRange range; + + lr = (CTLineRef) CFArrayGetValueAtIndex([tl->forLines lines], line); + range = CTLineGetStringRange(lr); + *start = tl->u16tou8[range.location]; + if (tl->empty) + *end = *start; + else + *end = tl->u16tou8[range.location + range.length]; +} From 9661d142625a0340160ad139e39cb6f73a8c2bab Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 8 Mar 2018 22:44:35 -0500 Subject: [PATCH 0891/1329] And fixed up loose ends preventing a build. Let's build! --- common/CMakeLists.txt | 4 +++- common/attrstr.h | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 8ecff8b3..2d7d08d9 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -1,13 +1,15 @@ # 3 june 2016 list(APPEND _LIBUI_SOURCES + common/attribute.c common/attrlist.c common/attrstr.c common/areaevents.c common/control.c common/debug.c - common/drawtext.c +# common/drawtext.c common/matrix.c + common/opentype.c common/shouldquit.c common/userbugs.c common/utf.c diff --git a/common/attrstr.h b/common/attrstr.h index 32e632ed..fb96c761 100644 --- a/common/attrstr.h +++ b/common/attrstr.h @@ -1,5 +1,11 @@ // 19 february 2018 +// TODO remove when done migrating these functions +#define uiprivNew(x) uiNew(x) +#define uiprivAlloc(x, y) uiAlloc(x, y) +#define uiprivRealloc(x, y) uiRealloc(x, y) +#define uiprivFree(x) uiFree(x) + // attribute.c extern uiAttribute *uiprivAttributeRetain(uiAttribute *a); extern void uiprivAttributeRelease(uiAttribute *a); From e9a62461c2770c24ad7a174fab015d8a6ff7c8ca Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 8 Mar 2018 23:04:45 -0500 Subject: [PATCH 0892/1329] Started fixing compile errors. I forgot I renamed uiDrawFontDescriptor to uiFontDescriptor! --- common/attribute.c | 2 +- darwin/attrstr.h | 8 ++++---- darwin/attrstr.m | 14 +++++++------- darwin/fontbutton.m | 8 ++++---- darwin/fontmatch.m | 14 +++++++------- darwin/fonttraits.m | 4 ++-- darwin/fontvariation.m | 2 +- ui_attrstr.h | 6 +++--- 8 files changed, 29 insertions(+), 29 deletions(-) diff --git a/common/attribute.c b/common/attribute.c index 79273bef..a6c0b4c0 100644 --- a/common/attribute.c +++ b/common/attribute.c @@ -206,7 +206,7 @@ uiAttribute *uiNewUnderlineColorAttribute(uiUnderlineColor u, double r, double g return at; } -void uiAttributeUnderline(const uiAttribute *a, uiUnderlineColor *u, double *r, double *g, double *b, double *alpha) +void uiAttributeUnderlineColor(const uiAttribute *a, uiUnderlineColor *u, double *r, double *g, double *b, double *alpha) { *u = a->u.color.underlineColor; uiAttributeColor(a, r, g, b, alpha); diff --git a/darwin/attrstr.h b/darwin/attrstr.h index 14a1cbe5..384a607b 100644 --- a/darwin/attrstr.h +++ b/darwin/attrstr.h @@ -60,16 +60,16 @@ extern void uiprivOpenTypeToAAT(char a, char b, char c, char d, uint32_t value, - (CFStringRef)familyName; - (CFArrayRef)variationAxes; @end -extern CTFontDescriptorRef uiprivDrawFontDescriptorToCTFontDescriptor(uiDrawFontDescriptor *fd); +extern CTFontDescriptorRef uiprivFontDescriptorToCTFontDescriptor(uiFontDescriptor *fd); extern CTFontDescriptorRef uiprivCTFontDescriptorAppendFeatures(CTFontDescriptorRef desc, const uiOpenTypeFeatures *otf); -extern void uiprivDrawFontDescriptorFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescriptor *uidesc); +extern void uiprivFontDescriptorFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiFontDescriptor *uidesc); // fonttraits.m -extern void uiprivProcessFontTraits(uiprivFontStyleData *d, uiDrawFontDescriptor *out); +extern void uiprivProcessFontTraits(uiprivFontStyleData *d, uiFontDescriptor *out); // fontvariation.m extern NSDictionary *uiprivMakeVariationAxisDict(CFArrayRef axes, CFDataRef avarTable); -extern void uiprivProcessFontVariation(uiprivFontStyleData *d, NSDictionary *axisDict, uiDrawFontDescriptor *out); +extern void uiprivProcessFontVariation(uiprivFontStyleData *d, NSDictionary *axisDict, uiFontDescriptor *out); // attrstr.m extern void uiprivInitUnderlineColors(void); diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 439da2c9..ad2dfeda 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -58,7 +58,7 @@ void uiprivUninitUnderlineColors(void) spellingColor = nil; } -// TODO opentype features are lost when using uiDrawFontDescriptor, so a handful of fonts in the font panel ("Titling" variants of some fonts and possibly others but those are the examples I know about) cannot be represented by uiDrawFontDescriptor; what *can* we do about this since this info is NOT part of the font on other platforms? +// TODO opentype features are lost when using uiFontDescriptor, so a handful of fonts in the font panel ("Titling" variants of some fonts and possibly others but those are the examples I know about) cannot be represented by uiFontDescriptor; what *can* we do about this since this info is NOT part of the font on other platforms? // TODO see if we could use NSAttributedString? // TODO consider renaming this struct and the fep variable(s) // TODO restructure all this so the important details at the top are below with the combined font attributes type? @@ -115,7 +115,7 @@ static uiForEach featuresHash(const uiOpenTypeFeatures *otf, char a, char b, cha NSUInteger hash; } - (void)addAttribute:(uiAttribute *)attr; -- (CTFontRef)toCTFontWithDefaultFont:(uiDrawFontDescriptor *)defaultFont; +- (CTFontRef)toCTFontWithDefaultFont:(uiFontDescriptor *)defaultFont; @end @implementation uiprivCombinedFontAttr @@ -216,15 +216,15 @@ static uiForEach featuresHash(const uiOpenTypeFeatures *otf, char a, char b, cha return self->hash; } -- (CTFontRef)toCTFontWithDefaultFont:(uiDrawFontDescriptor *)defaultFont +- (CTFontRef)toCTFontWithDefaultFont:(uiFontDescriptor *)defaultFont { - uiDrawFontDescriptor uidesc; + uiFontDescriptor uidesc; CTFontDescriptorRef desc; CTFontRef font; uidesc = *defaultFont; if (self->attrs[cFamily] != NULL) - // TODO const-correct uiDrawFontDescriptor or change this function below + // TODO const-correct uiFontDescriptor or change this function below uidesc.Family = (char *) uiAttributeFamily(self->attrs[cFamily]); if (self->attrs[cSize] != NULL) uidesc.Size = uiAttributeSize(self->attrs[cSize]); @@ -234,7 +234,7 @@ static uiForEach featuresHash(const uiOpenTypeFeatures *otf, char a, char b, cha uidesc.Italic = uiAttributeItalic(self->attrs[cItalic]); if (self->attrs[cStretch] != NULL) uidesc.Stretch = uiAttributeStretch(self->attrs[cStretch]); - desc = uiprivDrawFontDescriptorToCTFontDescriptor(&uidesc); + desc = uiprivFontDescriptorToCTFontDescriptor(&uidesc); if (self->attrs[cFeatures] != NULL) desc = uiprivCTFontDescriptorAppendFeatures(desc, uiAttributeFeatures(self->attrs[cFeatures])); font = CTFontCreateWithFontDescriptor(desc, uidesc.Size, NULL); @@ -386,7 +386,7 @@ $$TODO_CONTINUE_HERE return uiForEachContinue; } -static void applyFontAttributes(CFMutableAttributedStringRef mas, uiDrawFontDescriptor *defaultFont) +static void applyFontAttributes(CFMutableAttributedStringRef mas, uiFontDescriptor *defaultFont) { uiprivCombinedFontAttr *cfa; CTFontRef font; diff --git a/darwin/fontbutton.m b/darwin/fontbutton.m index 38205135..86f1c140 100644 --- a/darwin/fontbutton.m +++ b/darwin/fontbutton.m @@ -12,7 +12,7 @@ - (void)activateFontButton; - (void)deactivateFontButton:(BOOL)activatingAnother; - (void)deactivateOnClose:(NSNotification *)note; -- (void)getfontdesc:(uiDrawFontDescriptor *)uidesc; +- (void)getfontdesc:(uiFontDescriptor *)uidesc; @end // only one may be active at one time @@ -139,14 +139,14 @@ struct uiFontButton { NSFontPanelCollectionModeMask; } -- (void)getfontdesc:(uiDrawFontDescriptor *)uidesc +- (void)getfontdesc:(uiFontDescriptor *)uidesc { CTFontRef ctfont; CTFontDescriptorRef ctdesc; ctfont = (CTFontRef) (self->libui_font); ctdesc = CTFontCopyFontDescriptor(ctfont); - uiprivDrawFontDescriptorFromCTFontDescriptor(ctdesc, uidesc); + uiprivFontDescriptorFromCTFontDescriptor(ctdesc, uidesc); CFRelease(ctdesc); uidesc->Size = CTFontGetSize(ctfont); } @@ -200,7 +200,7 @@ static void defaultOnChanged(uiFontButton *b, void *data) // do nothing } -void uiFontButtonFont(uiFontButton *b, uiDrawFontDescriptor *desc) +void uiFontButtonFont(uiFontButton *b, uiFontDescriptor *desc) { [b->button getfontdesc:desc]; } diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index 6a9eb14e..2c3de4bc 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -18,7 +18,7 @@ // // To make things easier for us, we'll match by converting Core // Text's values back into libui values. This allows us to also use the -// normalization code for filling in uiDrawFontDescriptors from +// normalization code for filling in uiFontDescriptors from // Core Text fonts and font descriptors. // // Style matching needs to be done early in the font loading process; @@ -318,14 +318,14 @@ static uiDrawTextItalic guessItalicOblique(uiprivFontStyleData *d) // However, Core Text does seem to guarantee (from experimentation; see below) that the slant will be nonzero if and only if the italic bit is set, so we don't need to use the slant value. // Core Text also seems to guarantee that if a font lists itself as Italic or Oblique by name (font subfamily name, font style name, whatever), it will also have that bit set, so testing this bit does cover all fonts that name themselves as Italic and Oblique. (Again, this is from the below experimentation.) // TODO there is still one catch that might matter from a user's POV: the reverse is not true — the italic bit can be set even if the style of the font face/subfamily/style isn't named as Italic (for example, script typefaces like Adobe's Palace Script MT Std); I don't know what to do about this... I know how to start: find a script font that has an italic form (Adobe's Palace Script MT Std does not; only Regular and Semibold) -static void setItalic(uiprivFontStyleData *d, uiDrawFontDescriptor *out) +static void setItalic(uiprivFontStyleData *d, uiFontDescriptor *out) { out->Italic = uiDrawTextItalicNormal; if (([d symbolicTraits] & kCTFontItalicTrait) != 0) out->Italic = guessItalicOblique(d); } -static void fillDescStyleFields(uiprivFontStyleData *d, NSDictionary *axisDict, uiDrawFontDescriptor *out) +static void fillDescStyleFields(uiprivFontStyleData *d, NSDictionary *axisDict, uiFontDescriptor *out) { setItalic(d, out); if (axisDict != nil) @@ -334,7 +334,7 @@ static void fillDescStyleFields(uiprivFontStyleData *d, NSDictionary *axisDict, uiprivProcessFontTraits(d, out); } -static CTFontDescriptorRef matchStyle(CTFontDescriptorRef against, uiDrawFontDescriptor *styles) +static CTFontDescriptorRef matchStyle(CTFontDescriptorRef against, uiFontDescriptor *styles) { CFArrayRef matching; CFIndex i, n; @@ -363,7 +363,7 @@ static CTFontDescriptorRef matchStyle(CTFontDescriptorRef against, uiDrawFontDes closeness = (struct closeness *) uiAlloc(n * sizeof (struct closeness), "struct closeness[]"); for (i = 0; i < n; i++) { - uiDrawFontDescriptor fields; + uiFontDescriptor fields; closeness[i].index = i; if (i != 0) { @@ -413,7 +413,7 @@ static CTFontDescriptorRef matchStyle(CTFontDescriptorRef against, uiDrawFontDes return out; } -CTFontDescriptorRef uiprivDrawFontDescriptorToCTFontDescriptor(uiDrawFontDescriptor *fd) +CTFontDescriptorRef uiprivFontDescriptorToCTFontDescriptor(uiFontDescriptor *fd) { CFMutableDictionaryRef attrs; CFStringRef cffamily; @@ -465,7 +465,7 @@ CTFontDescriptorRef uiprivCTFontDescriptorAppendFeatures(CTFontDescriptorRef des return new; } -void uiprivDrawFontDescriptorFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontDescriptor *uidesc) +void uiprivFontDescriptorFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiFontDescriptor *uidesc) { CFStringRef cffamily; uiprivFontStyleData *d; diff --git a/darwin/fonttraits.m b/darwin/fonttraits.m index b4b932c8..b3989c37 100644 --- a/darwin/fonttraits.m +++ b/darwin/fonttraits.m @@ -54,7 +54,7 @@ static const CFStringRef exceptions[] = { NULL, }; -static void trySecondaryOS2Values(uiprivFontStyleData *d, uiDrawFontDescriptor *out, BOOL *hasWeight, BOOL *hasWidth) +static void trySecondaryOS2Values(uiprivFontStyleData *d, uiFontDescriptor *out, BOOL *hasWeight, BOOL *hasWidth) { CFDataRef os2; uint16_t usWeightClass, usWidthClass; @@ -159,7 +159,7 @@ static BOOL shouldReallyBeSemiCondensed(uiprivFontStyleData *d) return testTTFOTFSubfamilyNames(d, CFSTR("Semi Condensed")); } -void uiprivProcessFontTraits(uiprivFontStyleData *d, uiDrawFontDescriptor *out) +void uiprivProcessFontTraits(uiprivFontStyleData *d, uiFontDescriptor *out) { double weight, width; BOOL hasWeight, hasWidth; diff --git a/darwin/fontvariation.m b/darwin/fontvariation.m index 9959b02b..d59f0c7a 100644 --- a/darwin/fontvariation.m +++ b/darwin/fontvariation.m @@ -305,7 +305,7 @@ static BOOL tryAxis(NSDictionary *axisDict, CFDictionaryRef var, NSNumber *key, return YES; } -void uiprivProcessFontVariation(uiprivFontStyleData *d, NSDictionary *axisDict, uiDrawFontDescriptor *out) +void uiprivProcessFontVariation(uiprivFontStyleData *d, NSDictionary *axisDict, uiFontDescriptor *out) { CFDictionaryRef var; double v; diff --git a/ui_attrstr.h b/ui_attrstr.h index 9b02035d..3a22e6ed 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -214,7 +214,7 @@ _UI_EXTERN uiAttribute *uiNewUnderlineColorAttribute(uiUnderlineColor u, double // uiAttributeUnderlineColor() returns the underline color stored in // a. It is an error to call this on a uiAttribute that does not hold an // underline color. -_UI_EXTERN void uiAttributeUnderline(const uiAttribute *a, uiUnderlineColor *u, double *r, double *g, double *b, double *alpha); +_UI_EXTERN void uiAttributeUnderlineColor(const uiAttribute *a, uiUnderlineColor *u, double *r, double *g, double *b, double *alpha); // uiOpenTypeFeatures represents a set of OpenType feature // tag-value pairs, for applying OpenType features to text. @@ -409,7 +409,7 @@ _UI_EXTERN size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, s // All the members operate like the respective uiAttributes. typedef struct uiFontDescriptor uiFontDescriptor; -struct uiDrawFontDescriptor { +struct uiFontDescriptor { // TODO const-correct this or figure out how to deal with this when getting a value char *Family; double Size; @@ -448,7 +448,7 @@ typedef struct uiDrawTextLayoutParams uiDrawTextLayoutParams; // TODO const-correct this somehow struct uiDrawTextLayoutParams { uiAttributedString *String; - uiDrawFontDescriptor *DefaultFont; + uiFontDescriptor *DefaultFont; double Width; uiDrawTextAlign Align; }; From 82d3de7c31ec37fa5a0c9a525612b4a6bb7b0a84 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 8 Mar 2018 23:27:04 -0500 Subject: [PATCH 0893/1329] More compiler error fixes. Oops, I forgot to finish attrstr.m! --- common/attribute.c | 4 ++-- common/attrstr.c | 4 ++-- common/attrstr.h | 4 ++-- darwin/attrstr.m | 59 ++++++++++++++++++++++++---------------------- 4 files changed, 37 insertions(+), 34 deletions(-) diff --git a/common/attribute.c b/common/attribute.c index a6c0b4c0..0e30cb2e 100644 --- a/common/attribute.c +++ b/common/attribute.c @@ -4,7 +4,7 @@ #include "attrstr.h" struct uiAttribute { - int owned; + int ownedByUser; size_t refcount; uiAttributeType type; union { @@ -174,7 +174,7 @@ uiAttribute *uiNewBackgroundAttribute(double r, double g, double b, double a) { uiAttribute *at; - at = newAttribute(uiAttributeTypeBackgroundColor); + at = newAttribute(uiAttributeTypeBackground); at->u.color.r = r; at->u.color.g = g; at->u.color.b = b; diff --git a/common/attrstr.c b/common/attrstr.c index ab3f073a..1a0f6d9f 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -279,9 +279,9 @@ void uiAttributedStringDelete(uiAttributedString *s, size_t start, size_t end) resize(s, start + count, start16 + count16); } -void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t start, size_t end) +void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttribute *a, size_t start, size_t end) { - uiprivAttrListInsertAttribute(s->attrs, spec, start, end); + uiprivAttrListInsertAttribute(s->attrs, a, start, end); } // LONGTERM introduce an iterator object instead? diff --git a/common/attrstr.h b/common/attrstr.h index fb96c761..1dae7d63 100644 --- a/common/attrstr.h +++ b/common/attrstr.h @@ -3,7 +3,7 @@ // TODO remove when done migrating these functions #define uiprivNew(x) uiNew(x) #define uiprivAlloc(x, y) uiAlloc(x, y) -#define uiprivRealloc(x, y) uiRealloc(x, y) +#define uiprivRealloc(x, y, z) uiRealloc(x, y, z) #define uiprivFree(x) uiFree(x) // attribute.c @@ -21,7 +21,7 @@ extern void uiprivFreeAttrList(uiprivAttrList *alist); extern void uiprivAttrListInsertAttribute(uiprivAttrList *alist, uiAttribute *val, size_t start, size_t end); extern void uiprivAttrListInsertCharactersUnattributed(uiprivAttrList *alist, size_t start, size_t count); extern void uiprivAttrListInsertCharactersExtendingAttributes(uiprivAttrList *alist, size_t start, size_t count); -extern void uiprivAttrListRemoveAttribute(uiprivAttrList *alist, uiAttribute type, size_t start, size_t end); +extern void uiprivAttrListRemoveAttribute(uiprivAttrList *alist, uiAttributeType type, size_t start, size_t end); extern void uiprivAttrListRemoveAttributes(uiprivAttrList *alist, size_t start, size_t end); extern void uiprivAttrListRemoveCharacters(uiprivAttrList *alist, size_t start, size_t end); extern void uiprivAttrListForEach(const uiprivAttrList *alist, const uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); diff --git a/darwin/attrstr.m b/darwin/attrstr.m index ad2dfeda..f067956a 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -53,7 +53,7 @@ void uiprivUninitUnderlineColors(void) [auxiliaryColor release]; auxiliaryColor = nil; [grammarColor release]; - grammarColors = nil; + grammarColor = nil; [spellingColor release]; spellingColor = nil; } @@ -267,7 +267,7 @@ static void addFontAttributeToRange(struct foreachParams *p, size_t start, size_ range.length = end - range.location; CFAttributedStringSetAttribute(p->mas, range, combinedFontAttrName, cfa); [cfa release]; - start += effectiveRange.length; + start += range.length; } } @@ -285,7 +285,7 @@ static backgroundBlock mkBackgroundBlock(size_t start, size_t end, double r, dou }); } -static CGColorRef mkcolor(uiAttributeSpec *spec) +static CGColorRef mkcolor(double r, double g, double b, double a) { CGColorSpaceRef colorspace; CGColorRef color; @@ -296,10 +296,10 @@ static CGColorRef mkcolor(uiAttributeSpec *spec) if (colorspace == NULL) { // TODO } - components[0] = spec->R; - components[1] = spec->G; - components[2] = spec->B; - components[3] = spec->A; + components[0] = r; + components[1] = g; + components[2] = b; + components[3] = a; color = CGColorCreate(colorspace, components); CFRelease(colorspace); return color; @@ -314,6 +314,8 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute backgroundBlock block; int32_t us; CFNumberRef num; + double r, g, b, a; + uiUnderlineColor colorType; ostart = start; oend = end; @@ -330,31 +332,31 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute case uiAttributeTypeFeatures: addFontAttributeToRange(p, start, end, attr); break; -$$TODO_CONTINUE_HERE - case uiAttributeColor: - color = mkcolor(spec); + case uiAttributeTypeColor: + uiAttributeColor(attr, &r, &g, &b, &a); + color = mkcolor(r, g, b, a); CFAttributedStringSetAttribute(p->mas, range, kCTForegroundColorAttributeName, color); CFRelease(color); break; - case uiAttributeBackground: - block = mkBackgroundBlock(ostart, oend, - spec->R, spec->G, spec->B, spec->A); + case uiAttributeTypeBackground: + uiAttributeColor(attr, &r, &g, &b, &a); + block = mkBackgroundBlock(ostart, oend, r, g, b, a); [p->backgroundBlocks addObject:block]; Block_release(block); break; // TODO turn into a class, like we did with the font attributes, or even integrate *into* the font attributes - case uiAttributeUnderline: - switch (spec->Value) { - case uiDrawUnderlineStyleNone: + case uiAttributeTypeUnderline: + switch (uiAttributeUnderline(attr)) { + case uiUnderlineNone: us = kCTUnderlineStyleNone; break; - case uiDrawUnderlineStyleSingle: + case uiUnderlineSingle: us = kCTUnderlineStyleSingle; break; - case uiDrawUnderlineStyleDouble: + case uiUnderlineDouble: us = kCTUnderlineStyleDouble; break; - case uiDrawUnderlineStyleSuggestion: + case uiUnderlineSuggestion: // TODO incorrect if a solid color us = kCTUnderlineStyleThick; break; @@ -363,23 +365,24 @@ $$TODO_CONTINUE_HERE CFAttributedStringSetAttribute(p->mas, range, kCTUnderlineStyleAttributeName, num); CFRelease(num); break; - case uiAttributeUnderlineColor: - switch (spec->Value) { - case uiDrawUnderlineColorCustom: - color = mkcolor(spec); + case uiAttributeTypeUnderlineColor: + uiAttributeUnderlineColor(attr, &colorType, &r, &g, &b, &a); + switch (colorType) { + case uiUnderlineColorCustom: + color = mkcolor(r, g, b, a); break; - case uiDrawUnderlineColorSpelling: + case uiUnderlineColorSpelling: color = [spellingColor CGColor]; break; - case uiDrawUnderlineColorGrammar: + case uiUnderlineColorGrammar: color = [grammarColor CGColor]; break; - case uiDrawUnderlineColorAuxiliary: + case uiUnderlineColorAuxiliary: color = [auxiliaryColor CGColor]; break; } CFAttributedStringSetAttribute(p->mas, range, kCTUnderlineColorAttributeName, color); - if (spec->Value == uiDrawUnderlineColorCustom) + if (colorType == uiUnderlineColorCustom) CFRelease(color); break; } @@ -410,7 +413,7 @@ static void applyFontAttributes(CFMutableAttributedStringRef mas, uiFontDescript range.location = 0; while (range.location < n) { // TODO consider seeing if CFAttributedStringGetAttributeAndLongestEffectiveRange() can make things faster by reducing the number of potential iterations, either here or above - cfa = (uiprivCombinedFontAttr *) CFAttributedStringSetAttribute(mas, range.location, combinedFontAttrName, &range); + cfa = (uiprivCombinedFontAttr *) CFAttributedStringGetAttribute(mas, range.location, combinedFontAttrName, &range); if (cfa != nil) { font = [cfa toCTFontWithDefaultFont:defaultFont]; CFAttributedStringSetAttribute(mas, range, kCTFontAttributeName, font); From c82197f4089a9e1c9f46ddd411d8029d7de2c309 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 9 Mar 2018 18:01:23 -0500 Subject: [PATCH 0894/1329] And fixed other compile errors. Of course I forgot to add uiFontButton back in (I wanted to properly comment that first :/ ). Now for linker errors, which may result in some warning fixes along the way. --- darwin/drawtext.m | 8 ++--- darwin/fontmatch.m | 18 +++++------ darwin/fonttraits.m | 68 +++++++++++++++++++++--------------------- darwin/fontvariation.m | 10 +++---- darwin/opentype.m | 2 +- old_ui_attrstr.h | 8 ----- ui_attrstr.h | 8 +++++ 7 files changed, 61 insertions(+), 61 deletions(-) diff --git a/darwin/drawtext.m b/darwin/drawtext.m index ed200f76..21c6e45e 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -6,7 +6,7 @@ // problem: for a CTFrame made from an empty string, the CTLine array will be empty, and we will crash when doing anything requiring a CTLine // solution: for those cases, maintain a separate framesetter just for computing those things // in the usual case, the separate copy will just be identical to the regular one, with extra references to everything within -@interface uiprivTextFrame { +@interface uiprivTextFrame : NSObject { CFAttributedStringRef attrstr; NSArray *backgroundBlocks; CTFramesetterRef framesetter; @@ -25,7 +25,7 @@ - (id)initWithLayoutParams:(uiDrawTextLayoutParams *)p { CFRange range; - CGFloat width; + CGFloat cgwidth; CFRange unused; CGRect rect; @@ -54,7 +54,7 @@ rect.origin = CGPointZero; rect.size = self->size; self->path = CGPathCreateWithRect(rect, NULL); - self->frame = CTFramesetterCreateFrame(tl->framesetter, + self->frame = CTFramesetterCreateFrame(self->framesetter, range, self->path, // TODO kCTFramePathWidthAttributeName? @@ -144,7 +144,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) tl = uiprivNew(uiDrawTextLayout); tl->frame = [[uiprivTextFrame alloc] initWithLayoutParams:p]; - if (uiAttributedStringLength(p->String) != 0) + if (uiAttributedStringLen(p->String) != 0) tl->forLines = [tl->frame retain]; else { uiAttributedString *space; diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index 2c3de4bc..9645662f 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -275,9 +275,9 @@ FONTNAME(familyName, struct closeness { CFIndex index; - uiDrawTextWeight weight; + uiTextWeight weight; double italic; - uiDrawTextStretch stretch; + uiTextStretch stretch; double distance; }; @@ -287,14 +287,14 @@ static const double italicClosenessNormal[] = { 0, 1, 1 }; static const double italicClosenessOblique[] = { 1, 0, 0.5 }; static const double italicClosenessItalic[] = { 1, 0.5, 0 }; static const double *italicClosenesses[] = { - [uiDrawTextItalicNormal] = italicClosenessNormal, - [uiDrawTextItalicOblique] = italicClosenessOblique, - [uiDrawTextItalicItalic] = italicClosenessItalic, + [uiTextItalicNormal] = italicClosenessNormal, + [uiTextItalicOblique] = italicClosenessOblique, + [uiTextItalicItalic] = italicClosenessItalic, }; // Core Text doesn't seem to differentiate between Italic and Oblique. // Pango's Core Text code just does a g_strrstr() (backwards case-sensitive search) for "Oblique" in the font's style name (see https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c); let's do that too I guess -static uiDrawTextItalic guessItalicOblique(uiprivFontStyleData *d) +static uiTextItalic guessItalicOblique(uiprivFontStyleData *d) { CFStringRef styleName; BOOL isOblique; @@ -309,8 +309,8 @@ static uiDrawTextItalic guessItalicOblique(uiprivFontStyleData *d) isOblique = YES; } if (isOblique) - return uiDrawTextItalicOblique; - return uiDrawTextItalicItalic; + return uiTextItalicOblique; + return uiTextItalicItalic; } // Italics are hard because Core Text does NOT distinguish between italic and oblique. @@ -320,7 +320,7 @@ static uiDrawTextItalic guessItalicOblique(uiprivFontStyleData *d) // TODO there is still one catch that might matter from a user's POV: the reverse is not true — the italic bit can be set even if the style of the font face/subfamily/style isn't named as Italic (for example, script typefaces like Adobe's Palace Script MT Std); I don't know what to do about this... I know how to start: find a script font that has an italic form (Adobe's Palace Script MT Std does not; only Regular and Semibold) static void setItalic(uiprivFontStyleData *d, uiFontDescriptor *out) { - out->Italic = uiDrawTextItalicNormal; + out->Italic = uiTextItalicNormal; if (([d symbolicTraits] & kCTFontItalicTrait) != 0) out->Italic = guessItalicOblique(d); } diff --git a/darwin/fonttraits.m b/darwin/fonttraits.m index b3989c37..a51492c9 100644 --- a/darwin/fonttraits.m +++ b/darwin/fonttraits.m @@ -31,18 +31,18 @@ static BOOL fontRegistered(uiprivFontStyleData *d) // Core Text does (usWidthClass / 10) - 0.5 here. // This roughly maps to our values with increments of 0.1, except for the fact 0 and 10 are allowed by Core Text, despite being banned by TrueType and OpenType themselves. // We'll just treat them as identical to 1 and 9, respectively. -static const uiDrawTextStretch os2WidthsToStretches[] = { - uiDrawTextStretchUltraCondensed, - uiDrawTextStretchUltraCondensed, - uiDrawTextStretchExtraCondensed, - uiDrawTextStretchCondensed, - uiDrawTextStretchSemiCondensed, - uiDrawTextStretchNormal, - uiDrawTextStretchSemiExpanded, - uiDrawTextStretchExpanded, - uiDrawTextStretchExtraExpanded, - uiDrawTextStretchUltraExpanded, - uiDrawTextStretchUltraExpanded, +static const uiTextStretch os2WidthsToStretches[] = { + uiTextStretchUltraCondensed, + uiTextStretchUltraCondensed, + uiTextStretchExtraCondensed, + uiTextStretchCondensed, + uiTextStretchSemiCondensed, + uiTextStretchNormal, + uiTextStretchSemiExpanded, + uiTextStretchExpanded, + uiTextStretchExtraExpanded, + uiTextStretchUltraExpanded, + uiTextStretchUltraExpanded, }; static const CFStringRef exceptions[] = { @@ -174,50 +174,50 @@ void uiprivProcessFontTraits(uiprivFontStyleData *d, uiFontDescriptor *out) if (!hasWeight) // TODO this scale is a bit lopsided if (weight <= -0.7) - out->Weight = uiDrawTextWeightThin; + out->Weight = uiTextWeightThin; else if (weight <= -0.5) - out->Weight = uiDrawTextWeightUltraLight; + out->Weight = uiTextWeightUltraLight; else if (weight <= -0.3) - out->Weight = uiDrawTextWeightLight; + out->Weight = uiTextWeightLight; else if (weight <= -0.23) { - out->Weight = uiDrawTextWeightBook; + out->Weight = uiTextWeightBook; if (shouldReallyBeThin(d)) - out->Weight = uiDrawTextWeightThin; + out->Weight = uiTextWeightThin; } else if (weight <= 0.0) - out->Weight = uiDrawTextWeightNormal; + out->Weight = uiTextWeightNormal; else if (weight <= 0.23) - out->Weight = uiDrawTextWeightMedium; + out->Weight = uiTextWeightMedium; else if (weight <= 0.3) - out->Weight = uiDrawTextWeightSemiBold; + out->Weight = uiTextWeightSemiBold; else if (weight <= 0.4) - out->Weight = uiDrawTextWeightBold; + out->Weight = uiTextWeightBold; else if (weight <= 0.5) - out->Weight = uiDrawTextWeightUltraBold; + out->Weight = uiTextWeightUltraBold; else if (weight <= 0.7) - out->Weight = uiDrawTextWeightHeavy; + out->Weight = uiTextWeightHeavy; else - out->Weight = uiDrawTextWeightUltraHeavy; + out->Weight = uiTextWeightUltraHeavy; if (!hasWidth) // TODO this scale is a bit lopsided if (width <= -0.7) { - out->Stretch = uiDrawTextStretchUltraCondensed; + out->Stretch = uiTextStretchUltraCondensed; if (shouldReallyBeSemiCondensed(d)) - out->Stretch = uiDrawTextStretchSemiCondensed; + out->Stretch = uiTextStretchSemiCondensed; } else if (width <= -0.5) - out->Stretch = uiDrawTextStretchExtraCondensed; + out->Stretch = uiTextStretchExtraCondensed; else if (width <= -0.2) - out->Stretch = uiDrawTextStretchCondensed; + out->Stretch = uiTextStretchCondensed; else if (width <= -0.1) - out->Stretch = uiDrawTextStretchSemiCondensed; + out->Stretch = uiTextStretchSemiCondensed; else if (width <= 0.0) - out->Stretch = uiDrawTextStretchNormal; + out->Stretch = uiTextStretchNormal; else if (width <= 0.1) - out->Stretch = uiDrawTextStretchSemiExpanded; + out->Stretch = uiTextStretchSemiExpanded; else if (width <= 0.2) - out->Stretch = uiDrawTextStretchExpanded; + out->Stretch = uiTextStretchExpanded; else if (width <= 0.6) - out->Stretch = uiDrawTextStretchExtraExpanded; + out->Stretch = uiTextStretchExtraExpanded; else - out->Stretch = uiDrawTextStretchUltraExpanded; + out->Stretch = uiTextStretchUltraExpanded; } diff --git a/darwin/fontvariation.m b/darwin/fontvariation.m index d59f0c7a..60a124bd 100644 --- a/darwin/fontvariation.m +++ b/darwin/fontvariation.m @@ -310,8 +310,8 @@ void uiprivProcessFontVariation(uiprivFontStyleData *d, NSDictionary *axisDict, CFDictionaryRef var; double v; - out->Weight = uiDrawTextWeightNormal; - out->Stretch = uiDrawTextStretchNormal; + out->Weight = uiTextWeightNormal; + out->Stretch = uiTextStretchNormal; var = [d variation]; @@ -320,14 +320,14 @@ void uiprivProcessFontVariation(uiprivFontStyleData *d, NSDictionary *axisDict, // we want a linear value between 0 and 1000 with 400 being normal if (v < 0) { v += 1; - out->Weight = (uiDrawTextWeight) (v * 400); + out->Weight = (uiTextWeight) (v * 400); } else if (v > 0) - out->Weight += (uiDrawTextWeight) (v * 600); + out->Weight += (uiTextWeight) (v * 600); } if (tryAxis(axisDict, var, fvarAxisKey(fvarWidth), &v)) { // likewise, but with stretches, we go from 0 to 8 with 4 being directly between the two, so this is sufficient v += 1; - out->Stretch = (uiDrawTextStretch) (v * 4); + out->Stretch = (uiTextStretch) (v * 4); } } diff --git a/darwin/opentype.m b/darwin/opentype.m index 079197ed..84dbde36 100644 --- a/darwin/opentype.m +++ b/darwin/opentype.m @@ -92,7 +92,7 @@ static uiForEach otfArrayForEachOT(const uiOpenTypeFeatures *otf, char a, char b p.valueValue = (const SInt32 *) (&value); addCTFeatureEntry(&p); - CFRelease(strTag); + CFRelease(tagstr); return uiForEachContinue; } diff --git a/old_ui_attrstr.h b/old_ui_attrstr.h index 5dfc921c..75df996d 100644 --- a/old_ui_attrstr.h +++ b/old_ui_attrstr.h @@ -61,11 +61,3 @@ _UI_EXTERN double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_ _UI_EXTERN void uiDrawCaret(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t pos, int *line); // TODO allow blinking // TODO allow secondary carets - -typedef struct uiFontButton uiFontButton; -#define uiFontButton(this) ((uiFontButton *) (this)) -// TODO have a function that sets an entire font descriptor to a range in a uiAttributedString at once, for SetFont? -_UI_EXTERN void uiFontButtonFont(uiFontButton *b, uiDrawFontDescriptor *desc); -// TOOD SetFont, mechanics -_UI_EXTERN void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data); -_UI_EXTERN uiFontButton *uiNewFontButton(void); diff --git a/ui_attrstr.h b/ui_attrstr.h index 3a22e6ed..6dd738ee 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -495,3 +495,11 @@ _UI_EXTERN void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, si // TODO metrics functions // TODO number of lines visible for clipping rect, range visible for clipping rect? + +typedef struct uiFontButton uiFontButton; +#define uiFontButton(this) ((uiFontButton *) (this)) +// TODO have a function that sets an entire font descriptor to a range in a uiAttributedString at once, for SetFont? +_UI_EXTERN void uiFontButtonFont(uiFontButton *b, uiFontDescriptor *desc); +// TOOD SetFont, mechanics +_UI_EXTERN void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data); +_UI_EXTERN uiFontButton *uiNewFontButton(void); From 9194ba29fe2d7dfddfdb1b828e426ceb751af46c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 10 Mar 2018 19:02:10 -0500 Subject: [PATCH 0895/1329] And fixed build errors. Now I'm sure there are warnings that are real bugs, so let's handle those next. --- common/attrstr.h | 2 ++ darwin/attrstr.m | 4 ++-- darwin/fontmatch.m | 2 +- darwin/main.m | 5 +++-- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/common/attrstr.h b/common/attrstr.h index 1dae7d63..d0ff049b 100644 --- a/common/attrstr.h +++ b/common/attrstr.h @@ -5,6 +5,8 @@ #define uiprivAlloc(x, y) uiAlloc(x, y) #define uiprivRealloc(x, y, z) uiRealloc(x, y, z) #define uiprivFree(x) uiFree(x) +#define uiprivStrdup(x) strdup(x) +#define uiprivStricmp(x, y) strcasecmp(x, y) // attribute.c extern uiAttribute *uiprivAttributeRetain(uiAttribute *a); diff --git a/darwin/attrstr.m b/darwin/attrstr.m index f067956a..cc42d4c6 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -281,7 +281,7 @@ static backgroundBlock mkBackgroundBlock(size_t start, size_t end, double r, dou brush.G = g; brush.B = b; brush.A = a; - drawTextBackground(c, x, y, layout, start, end, &brush, 0); +//TODO drawTextBackground(c, x, y, layout, start, end, &brush, 0); }); } @@ -463,7 +463,7 @@ CFAttributedStringRef uiprivAttributedStringToCFAttributedString(uiDrawTextLayou CFMutableAttributedStringRef mas; struct foreachParams fep; - cfstr = CFStringCreateWithCharacters(NULL, attrstrUTF16(p->String), attrstrUTF16Len(p->String)); + cfstr = CFStringCreateWithCharacters(NULL, uiprivAttributedStringUTF16String(p->String), uiprivAttributedStringUTF16Len(p->String)); if (cfstr == NULL) { // TODO } diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index 9645662f..f459a165 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -450,7 +450,7 @@ CTFontDescriptorRef uiprivCTFontDescriptorAppendFeatures(CTFontDescriptorRef des CFDictionaryRef attrs; const void *keys[1], *values[1]; - featuresArray = otfToFeaturesArray(otf); + featuresArray = uiprivOpenTypeFeaturesToCTFeatures(otf); keys[0] = kCTFontFeatureSettingsAttribute; values[0] = featuresArray; attrs = CFDictionaryCreate(NULL, diff --git a/darwin/main.m b/darwin/main.m index 12c528ff..865fb942 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -1,5 +1,6 @@ // 6 april 2015 #import "uipriv_darwin.h" +#import "attrstr.h" static BOOL canQuit = NO; static NSAutoreleasePool *globalPool; @@ -128,7 +129,7 @@ const char *uiInit(uiInitOptions *o) uiprivSetupFontPanel(); - initUnderlineColors(); + uiprivInitUnderlineColors(); } globalPool = [[NSAutoreleasePool alloc] init]; @@ -144,7 +145,7 @@ void uiUninit(void) [globalPool release]; @autoreleasepool { - uninitUnderlineColors(); + uiprivUninitUnderlineColors(); [delegate release]; [realNSApp() setDelegate:nil]; [app release]; From 115a60c950e9223a01bd5e5a0cf2a2205fe6910b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 10 Mar 2018 19:13:20 -0500 Subject: [PATCH 0896/1329] uiAttribute copies family names, so its internal copy doesn't need to be const. Fix this. --- common/attribute.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/attribute.c b/common/attribute.c index 0e30cb2e..de0bdc13 100644 --- a/common/attribute.c +++ b/common/attribute.c @@ -8,7 +8,7 @@ struct uiAttribute { size_t refcount; uiAttributeType type; union { - const char *family; + char *family; double size; uiTextWeight weight; uiTextItalic italic; From 4bb6a56c99cd2c558c26c766b9b6aae228863b90 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 10 Mar 2018 19:21:39 -0500 Subject: [PATCH 0897/1329] More warning fixes. Made private uiAttributeString functions const-correct to expose more potential issues later. Const-correctness and uiAttributeRetain() are going to be an issue... --- common/attrstr.c | 10 +++++----- common/attrstr.h | 10 +++++----- common/opentype.c | 1 + darwin/attrstr.m | 1 - 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/common/attrstr.c b/common/attrstr.c index 1a0f6d9f..ee2b1616 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -316,23 +316,23 @@ size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos) // helpers for platform-specific code -const uint16_t *uiprivAttributedStringUTF16String(uiAttributedString *s) +const uint16_t *uiprivAttributedStringUTF16String(const uiAttributedString *s) { return s->u16; } -size_t uiprivAttributedStringUTF16Len(uiAttributedString *s) +size_t uiprivAttributedStringUTF16Len(const uiAttributedString *s) { return s->u16len; } // TODO is this still needed given the below? -size_t uiprivAttributedStringUTF8ToUTF16(uiAttributedString *s, size_t n) +size_t uiprivAttributedStringUTF8ToUTF16(const uiAttributedString *s, size_t n) { return s->u8tou16[n]; } -size_t *uiprivAttributedStringCopyUTF8ToUTF16Table(uiAttributedString *s, size_t *n) +size_t *uiprivAttributedStringCopyUTF8ToUTF16Table(const uiAttributedString *s, size_t *n) { size_t *out; size_t nbytes; @@ -344,7 +344,7 @@ size_t *uiprivAttributedStringCopyUTF8ToUTF16Table(uiAttributedString *s, size_t return out; } -size_t *uiprivAttributedStringCopyUTF16ToUTF8Table(uiAttributedString *s, size_t *n) +size_t *uiprivAttributedStringCopyUTF16ToUTF8Table(const uiAttributedString *s, size_t *n) { size_t *out; size_t nbytes; diff --git a/common/attrstr.h b/common/attrstr.h index d0ff049b..94fe934f 100644 --- a/common/attrstr.h +++ b/common/attrstr.h @@ -29,11 +29,11 @@ extern void uiprivAttrListRemoveCharacters(uiprivAttrList *alist, size_t start, extern void uiprivAttrListForEach(const uiprivAttrList *alist, const uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); // attrstr.c -extern const uint16_t *uiprivAttributedStringUTF16String(uiAttributedString *s); -extern size_t uiprivAttributedStringUTF16Len(uiAttributedString *s); -extern size_t uiprivAttributedStringUTF8ToUTF16(uiAttributedString *s, size_t n); -extern size_t *uiprivAttributedStringCopyUTF8ToUTF16Table(uiAttributedString *s, size_t *n); -extern size_t *uiprivAttributedStringCopyUTF16ToUTF8Table(uiAttributedString *s, size_t *n); +extern const uint16_t *uiprivAttributedStringUTF16String(const uiAttributedString *s); +extern size_t uiprivAttributedStringUTF16Len(const uiAttributedString *s); +extern size_t uiprivAttributedStringUTF8ToUTF16(const uiAttributedString *s, size_t n); +extern size_t *uiprivAttributedStringCopyUTF8ToUTF16Table(const uiAttributedString *s, size_t *n); +extern size_t *uiprivAttributedStringCopyUTF16ToUTF8Table(const uiAttributedString *s, size_t *n); // per-OS graphemes.c/graphemes.cpp/graphemes.m/etc. struct graphemes { diff --git a/common/opentype.c b/common/opentype.c index 625f5852..e579b612 100644 --- a/common/opentype.c +++ b/common/opentype.c @@ -1,4 +1,5 @@ // 25 february 2018 +#include #include "../ui.h" #include "uipriv.h" #include "attrstr.h" diff --git a/darwin/attrstr.m b/darwin/attrstr.m index cc42d4c6..c53daae7 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -457,7 +457,6 @@ CFAttributedStringRef uiprivAttributedStringToCFAttributedString(uiDrawTextLayou { CFStringRef cfstr; CFMutableDictionaryRef defaultAttrs; - CTFontRef defaultCTFont; CTParagraphStyleRef ps; CFAttributedStringRef base; CFMutableAttributedStringRef mas; From 8fda4071834ccecc7c07b95780a7500fd66b0c51 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 10 Mar 2018 19:24:20 -0500 Subject: [PATCH 0898/1329] Moved the old drawtext example out of the way. --- .../examples_drawtext}/attributes.c | 0 .../examples_drawtext}/basic.c | 0 .../examples_drawtext}/drawtext.h | 0 .../examples_drawtext}/emptystr_hittest.c | 0 .../examples_drawtext}/hittest.c | 0 .../examples_drawtext}/httext.gz | Bin .../examples_drawtext}/main.c | 0 _wip/examples_drawtext_CMakeLists.txt | 58 ++++++++++++++++++ examples/CMakeLists.txt | 11 ---- 9 files changed, 58 insertions(+), 11 deletions(-) rename {examples/drawtext => _wip/examples_drawtext}/attributes.c (100%) rename {examples/drawtext => _wip/examples_drawtext}/basic.c (100%) rename {examples/drawtext => _wip/examples_drawtext}/drawtext.h (100%) rename {examples/drawtext => _wip/examples_drawtext}/emptystr_hittest.c (100%) rename {examples/drawtext => _wip/examples_drawtext}/hittest.c (100%) rename {examples/drawtext => _wip/examples_drawtext}/httext.gz (100%) rename {examples/drawtext => _wip/examples_drawtext}/main.c (100%) create mode 100644 _wip/examples_drawtext_CMakeLists.txt diff --git a/examples/drawtext/attributes.c b/_wip/examples_drawtext/attributes.c similarity index 100% rename from examples/drawtext/attributes.c rename to _wip/examples_drawtext/attributes.c diff --git a/examples/drawtext/basic.c b/_wip/examples_drawtext/basic.c similarity index 100% rename from examples/drawtext/basic.c rename to _wip/examples_drawtext/basic.c diff --git a/examples/drawtext/drawtext.h b/_wip/examples_drawtext/drawtext.h similarity index 100% rename from examples/drawtext/drawtext.h rename to _wip/examples_drawtext/drawtext.h diff --git a/examples/drawtext/emptystr_hittest.c b/_wip/examples_drawtext/emptystr_hittest.c similarity index 100% rename from examples/drawtext/emptystr_hittest.c rename to _wip/examples_drawtext/emptystr_hittest.c diff --git a/examples/drawtext/hittest.c b/_wip/examples_drawtext/hittest.c similarity index 100% rename from examples/drawtext/hittest.c rename to _wip/examples_drawtext/hittest.c diff --git a/examples/drawtext/httext.gz b/_wip/examples_drawtext/httext.gz similarity index 100% rename from examples/drawtext/httext.gz rename to _wip/examples_drawtext/httext.gz diff --git a/examples/drawtext/main.c b/_wip/examples_drawtext/main.c similarity index 100% rename from examples/drawtext/main.c rename to _wip/examples_drawtext/main.c diff --git a/_wip/examples_drawtext_CMakeLists.txt b/_wip/examples_drawtext_CMakeLists.txt new file mode 100644 index 00000000..f2289d21 --- /dev/null +++ b/_wip/examples_drawtext_CMakeLists.txt @@ -0,0 +1,58 @@ +# 3 june 2016 + +if(WIN32) + set(_EXAMPLE_RESOURCES_RC resources.rc) +endif() + +macro(_add_example _name) + _add_exec(${_name} ${ARGN}) + # because Microsoft's toolchain is dumb + if(MSVC) + set_property(TARGET ${_name} APPEND_STRING PROPERTY + LINK_FLAGS " /ENTRY:mainCRTStartup") + endif() +endmacro() + +_add_example(controlgallery + controlgallery/main.c + ${_EXAMPLE_RESOURCES_RC} +) + +_add_example(histogram + histogram/main.c + ${_EXAMPLE_RESOURCES_RC} +) + +_add_example(cpp-multithread + cpp-multithread/main.cpp + ${_EXAMPLE_RESOURCES_RC} +) +if(NOT WIN32) + target_link_libraries(cpp-multithread pthread) +endif() + +_add_example(drawtext + drawtext/attributes.c + drawtext/basic.c + drawtext/emptystr_hittest.c + drawtext/hittest.c + drawtext/main.c + ${_EXAMPLE_RESOURCES_RC} +) +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 + opentype) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index f2289d21..b820df8b 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -31,17 +31,6 @@ if(NOT WIN32) target_link_libraries(cpp-multithread pthread) endif() -_add_example(drawtext - drawtext/attributes.c - drawtext/basic.c - drawtext/emptystr_hittest.c - drawtext/hittest.c - drawtext/main.c - ${_EXAMPLE_RESOURCES_RC} -) -target_include_directories(drawtext - PRIVATE drawtext) - _add_example(opentype opentype/main.c ${_EXAMPLE_RESOURCES_RC} From 427e013d78b1fd9708dae7b4763a746ae29a0475 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 10 Mar 2018 19:42:50 -0500 Subject: [PATCH 0899/1329] And moved the OpenType example out of the way too. --- {examples/opentype => _wip/examples_opentype}/main.c | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {examples/opentype => _wip/examples_opentype}/main.c (100%) diff --git a/examples/opentype/main.c b/_wip/examples_opentype/main.c similarity index 100% rename from examples/opentype/main.c rename to _wip/examples_opentype/main.c From 1fae3eea02de828f281156f899a0f89528004d40 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 10 Mar 2018 21:57:45 -0500 Subject: [PATCH 0900/1329] And wrote a new, simpler drawtext example. Now to debug run-time issues in the new attributed string code! First up: some infinite loop. --- examples/CMakeLists.txt | 6 +- examples/drawtext/main.c | 158 +++++++++++++++++++++++++++++++++++++++ ui_attrstr.h | 1 + 3 files changed, 161 insertions(+), 4 deletions(-) create mode 100644 examples/drawtext/main.c diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index b820df8b..cdedb039 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -31,12 +31,10 @@ if(NOT WIN32) target_link_libraries(cpp-multithread pthread) endif() -_add_example(opentype - opentype/main.c +_add_example(drawtext + drawtext/main.c ${_EXAMPLE_RESOURCES_RC} ) -target_include_directories(opentype - PRIVATE opentype) add_custom_target(examples DEPENDS diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c new file mode 100644 index 00000000..13c7c659 --- /dev/null +++ b/examples/drawtext/main.c @@ -0,0 +1,158 @@ +// 10 march 2018 +#include +#include +#include "../../ui.h" + +uiWindow *mainwin; +uiArea *area; +uiAreaHandler handler; +uiFontButton *fontButton; + +uiAttributedString *attrstr; + +#define margins 20 + +static void appendWithAttribute(const char *what, uiAttribute *attr) +{ + size_t start, end; + + start = uiAttributedStringLen(attrstr); + end = start + strlen(what); + uiAttributedStringAppendUnattributed(attrstr, what); + uiAttributedStringSetAttribute(attrstr, attr, start, end); +} + +static void makeAttributedString(void) +{ + uiAttribute *attr; + + attrstr = uiNewAttributedString( + "Drawing strings with libui is done with the uiAttributedString and uiDrawTextLayout obects.\n" + "uiAttributedString lets you have a variety of attributes: "); + + attr = uiNewFamilyAttribute("Courier New"); + appendWithAttribute("font family", attr); + uiAttributedStringAppendUnattributed(attrstr, ", "); + + attr = uiNewSizeAttribute(18); + appendWithAttribute("font size", attr); + uiAttributedStringAppendUnattributed(attrstr, ", "); + + attr = uiNewWeightAttribute(uiTextWeightBold); + appendWithAttribute("font weight", attr); + uiAttributedStringAppendUnattributed(attrstr, ", "); + + attr = uiNewItalicAttribute(uiTextItalicItalic); + appendWithAttribute("font italicness", attr); + uiAttributedStringAppendUnattributed(attrstr, ", "); + + attr = uiNewStretchAttribute(uiTextStretchCondensed); + appendWithAttribute("font stretch", attr); + uiAttributedStringAppendUnattributed(attrstr, ", "); + + attr = uiNewColorAttribute(0.75, 0.25, 0.5, 0.75); + appendWithAttribute("text color", attr); + uiAttributedStringAppendUnattributed(attrstr, ", "); + + attr = uiNewBackgroundAttribute(0.5, 0.5, 0.25, 0.5); + appendWithAttribute("text background color", attr); + uiAttributedStringAppendUnattributed(attrstr, ", "); +} + +static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *p) +{ + uiDrawTextLayout *textLayout; + uiFontDescriptor fontdesc; + uiDrawTextLayoutParams params; + + params.String = attrstr; + uiFontButtonFont(fontButton, &fontdesc); + params.Width = p->AreaWidth - (2 * margins); + params.Align = uiDrawTextAlignLeft; + textLayout = uiDrawNewTextLayout(¶ms); + // TODO clip to margins + uiDrawText(p->Context, textLayout, margins, margins); + uiDrawFreeTextLayout(textLayout); +} + +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) +{ + uiControlDestroy(uiControl(mainwin)); + uiQuit(); + return 0; +} + +static int shouldQuit(void *data) +{ + uiControlDestroy(uiControl(mainwin)); + return 1; +} + +int main(void) +{ + uiInitOptions o; + const char *err; + uiBox *hbox, *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); + + makeAttributedString(); + + mainwin = uiNewWindow("libui Histogram Example", 640, 480, 1); + uiWindowSetMargined(mainwin, 1); + uiWindowOnClosing(mainwin, onClosing, NULL); + + hbox = uiNewHorizontalBox(); + uiBoxSetPadded(hbox, 1); + uiWindowSetChild(mainwin, uiControl(hbox)); + + vbox = uiNewVerticalBox(); + uiBoxSetPadded(vbox, 1); + uiBoxAppend(hbox, uiControl(vbox), 0); + + fontButton = uiNewFontButton(); + uiBoxAppend(vbox, uiControl(fontButton), 0); + + area = uiNewArea(&handler); + uiBoxAppend(hbox, uiControl(area), 1); + + uiControlShow(uiControl(mainwin)); + uiMain(); + uiUninit(); + return 0; +} diff --git a/ui_attrstr.h b/ui_attrstr.h index 6dd738ee..66b0fb4b 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -433,6 +433,7 @@ typedef struct uiDrawTextLayout uiDrawTextLayout; // uiDrawTextAlign specifies the alignment of lines of text in a // uiDrawTextLayout. +// TODO should this really have Draw in the name? _UI_ENUM(uiDrawTextAlign) { uiDrawTextAlignLeft, uiDrawTextAlignCenter, From b15f88412bab17d947347fc968616b33d3131719 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 10 Mar 2018 22:04:07 -0500 Subject: [PATCH 0901/1329] Fixed the infinite loop: end is exclusive, and my code was correct in handling that, so <= (which i though was needed there because I thought the code wouldn't handle it) is wrong. Now for a segfault. --- darwin/attrstr.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index c53daae7..2c27f12c 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -250,7 +250,7 @@ static void addFontAttributeToRange(struct foreachParams *p, size_t start, size_ CFRange range; size_t diff; - while (start <= end) { + while (start < end) { cfa = (uiprivCombinedFontAttr *) CFAttributedStringGetAttribute(p->mas, start, combinedFontAttrName, &range); if (cfa == nil) cfa = [uiprivCombinedFontAttr new]; From 5ef04f26756c8294e9e233ef7945b0915a9f7015 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 10 Mar 2018 22:16:43 -0500 Subject: [PATCH 0902/1329] And fixed the other issue: didn't fully set uiDrawTextLayoutParams. Oops =P It works! Also did proper memory management before uiUninit()... but it's crashing for other reasons now (using strdup() instead of uiAlloc()). And Skia doesn't map correctly... --- examples/drawtext/main.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c index 13c7c659..fb9ee778 100644 --- a/examples/drawtext/main.c +++ b/examples/drawtext/main.c @@ -62,11 +62,12 @@ static void makeAttributedString(void) static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *p) { uiDrawTextLayout *textLayout; - uiFontDescriptor fontdesc; + uiFontDescriptor defaultFont; uiDrawTextLayoutParams params; params.String = attrstr; - uiFontButtonFont(fontButton, &fontdesc); + uiFontButtonFont(fontButton, &defaultFont); + params.DefaultFont = &defaultFont; params.Width = p->AreaWidth - (2 * margins); params.Align = uiDrawTextAlignLeft; textLayout = uiDrawNewTextLayout(¶ms); @@ -153,6 +154,7 @@ int main(void) uiControlShow(uiControl(mainwin)); uiMain(); + uiFreeAttributedString(attrstr); uiUninit(); return 0; } From 6643a148e019124ca92eac97382edee5279863c4 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 10 Mar 2018 22:18:48 -0500 Subject: [PATCH 0903/1329] More TODOs. --- darwin/fontvariation.m | 3 +++ 1 file changed, 3 insertions(+) diff --git a/darwin/fontvariation.m b/darwin/fontvariation.m index 60a124bd..b5a3591a 100644 --- a/darwin/fontvariation.m +++ b/darwin/fontvariation.m @@ -29,6 +29,9 @@ // - https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6.html#Types // - https://www.microsoft.com/typography/otspec/avar.htm +// TODO Skia doesn't quite map correctly; notice what passes for condensed in the drawtext example +// TODO also investigate Marker Felt not working right in Thin and Wide modes (but that's probably the other file, putting it here just so I don't forget) + #define fvarWeight 0x77676874 #define fvarWidth 0x77647468 From 6ba2d3606d0f65c9d8cabb42d31854ea491f4c37 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 10 Mar 2018 22:51:39 -0500 Subject: [PATCH 0904/1329] Removed uiprivStrdup() (we'll just uiprivAlloc()+strcpy() instead) and fleshed out the drawtext example's text a bit more. --- common/attribute.c | 3 ++- common/attrstr.h | 1 - examples/drawtext/main.c | 50 ++++++++++++++++++++++++++++++++-------- 3 files changed, 43 insertions(+), 11 deletions(-) diff --git a/common/attribute.c b/common/attribute.c index de0bdc13..5054d1c1 100644 --- a/common/attribute.c +++ b/common/attribute.c @@ -85,7 +85,8 @@ uiAttribute *uiNewFamilyAttribute(const char *family) uiAttribute *a; a = newAttribute(uiAttributeTypeFamily); - a->u.family = uiprivStrdup(family); + a->u.family = (char *) uiAlloc((strlen(family) + 1) * sizeof (char), "char[] (uiAttribute)"); + strcpy(a->u.family, family); return a; } diff --git a/common/attrstr.h b/common/attrstr.h index 94fe934f..643c8c3c 100644 --- a/common/attrstr.h +++ b/common/attrstr.h @@ -5,7 +5,6 @@ #define uiprivAlloc(x, y) uiAlloc(x, y) #define uiprivRealloc(x, y, z) uiRealloc(x, y, z) #define uiprivFree(x) uiFree(x) -#define uiprivStrdup(x) strdup(x) #define uiprivStricmp(x, y) strcasecmp(x, y) // attribute.c diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c index fb9ee778..c3b2d968 100644 --- a/examples/drawtext/main.c +++ b/examples/drawtext/main.c @@ -12,7 +12,7 @@ uiAttributedString *attrstr; #define margins 20 -static void appendWithAttribute(const char *what, uiAttribute *attr) +static void appendWithAttribute(const char *what, uiAttribute *attr, uiAttribute *attr2) { size_t start, end; @@ -20,43 +20,75 @@ static void appendWithAttribute(const char *what, uiAttribute *attr) end = start + strlen(what); uiAttributedStringAppendUnattributed(attrstr, what); uiAttributedStringSetAttribute(attrstr, attr, start, end); + if (attr2 != NULL) + uiAttributedStringSetAttribute(attrstr, attr2, start, end); } static void makeAttributedString(void) { - uiAttribute *attr; + uiAttribute *attr, *attr2; + uiOpenTypeFeatures *otf; attrstr = uiNewAttributedString( "Drawing strings with libui is done with the uiAttributedString and uiDrawTextLayout obects.\n" "uiAttributedString lets you have a variety of attributes: "); attr = uiNewFamilyAttribute("Courier New"); - appendWithAttribute("font family", attr); + appendWithAttribute("font family", attr, NULL); uiAttributedStringAppendUnattributed(attrstr, ", "); attr = uiNewSizeAttribute(18); - appendWithAttribute("font size", attr); + appendWithAttribute("font size", attr, NULL); uiAttributedStringAppendUnattributed(attrstr, ", "); attr = uiNewWeightAttribute(uiTextWeightBold); - appendWithAttribute("font weight", attr); + appendWithAttribute("font weight", attr, NULL); uiAttributedStringAppendUnattributed(attrstr, ", "); attr = uiNewItalicAttribute(uiTextItalicItalic); - appendWithAttribute("font italicness", attr); + appendWithAttribute("font italicness", attr, NULL); uiAttributedStringAppendUnattributed(attrstr, ", "); attr = uiNewStretchAttribute(uiTextStretchCondensed); - appendWithAttribute("font stretch", attr); + appendWithAttribute("font stretch", attr, NULL); uiAttributedStringAppendUnattributed(attrstr, ", "); attr = uiNewColorAttribute(0.75, 0.25, 0.5, 0.75); - appendWithAttribute("text color", attr); + appendWithAttribute("text color", attr, NULL); uiAttributedStringAppendUnattributed(attrstr, ", "); attr = uiNewBackgroundAttribute(0.5, 0.5, 0.25, 0.5); - appendWithAttribute("text background color", attr); + appendWithAttribute("text background color", attr, NULL); uiAttributedStringAppendUnattributed(attrstr, ", "); + + + attr = uiNewUnderlineAttribute(uiUnderlineSingle); + appendWithAttribute("underline style", attr, NULL); + uiAttributedStringAppendUnattributed(attrstr, ", "); + + uiAttributedStringAppendUnattributed(attrstr, "and "); + attr = uiNewUnderlineAttribute(uiUnderlineDouble); + attr2 = uiNewUnderlineColorAttribute(uiUnderlineColorCustom, 1.0, 0.0, 0.5, 1.0); + appendWithAttribute("underline color", attr, attr2); + uiAttributedStringAppendUnattributed(attrstr, ". "); + + uiAttributedStringAppendUnattributed(attrstr, "Furthermore, there are attributes allowing for "); + attr = uiNewUnderlineAttribute(uiUnderlineSuggestion); + attr2 = uiNewUnderlineColorAttribute(uiUnderlineColorSpelling, 0, 0, 0, 0); + appendWithAttribute("special underlines for indicating spelling errors", attr, attr2); + uiAttributedStringAppendUnattributed(attrstr, " (and other types of errors) "); + + uiAttributedStringAppendUnattributed(attrstr, "and control over OpenType features such as ligatures (for instance, "); + otf = uiNewOpenTypeFeatures(); + uiOpenTypeFeaturesAdd(otf, 'l', 'i', 'g', 'a', 0); + attr = uiNewFeaturesAttribute(otf); + appendWithAttribute("afford", attr, NULL); + uiAttributedStringAppendUnattributed(attrstr, " vs. "); + uiOpenTypeFeaturesAdd(otf, 'l', 'i', 'g', 'a', 1); + attr = uiNewFeaturesAttribute(otf); + appendWithAttribute("afford", attr, NULL); + uiFreeOpenTypeFeatures(otf); + uiAttributedStringAppendUnattributed(attrstr, ")."); } static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *p) From acbe7c3149947fe07dee3c2ff0f25e7a16c3a676 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 11 Mar 2018 03:26:32 -0400 Subject: [PATCH 0905/1329] Implemented uiAttributeTypeBackground for OS X 10.12 and newer using the attribute included with Core Text. Also laid out a non-block-based API for drawing backgrounds on older versions of OS X (not implemented here yet, however; that will require bringing back the old metrics code). --- darwin/attrstr.h | 16 +++++++++++-- darwin/attrstr.m | 52 ++++++++++++++++++++++-------------------- darwin/drawtext.m | 35 +++++++++++++++++++++++----- darwin/future.m | 4 ++++ darwin/uipriv_darwin.h | 1 + 5 files changed, 75 insertions(+), 33 deletions(-) diff --git a/darwin/attrstr.h b/darwin/attrstr.h index 384a607b..f77b4d86 100644 --- a/darwin/attrstr.h +++ b/darwin/attrstr.h @@ -74,5 +74,17 @@ extern void uiprivProcessFontVariation(uiprivFontStyleData *d, NSDictionary *axi // attrstr.m extern void uiprivInitUnderlineColors(void); extern void uiprivUninitUnderlineColors(void); -typedef void (^backgroundBlock)(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y); -extern CFAttributedStringRef uiprivAttributedStringToCFAttributedString(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks); +extern CFAttributedStringRef uiprivAttributedStringToCFAttributedString(uiDrawTextLayoutParams *p, NSArray **backgroundParams); + +// drawtext.m +@interface uiprivDrawTextBackgroundParams : NSObject { + size_t start; + size_t end; + double r; + double g; + double b; + double a; +} +- (id)initWithStart:(size_t)s end:(size_t)e r:(double)red g:(double)green b:(double)blue a:(double)alpha; +- (void)draw:(CGContextRef)c layout:(uiDrawTextLayout *)layout at:(double)x y:(double)y utf8Mapping:(const size_t *)u16tou8; +@end diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 2c27f12c..530e6a22 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -65,7 +65,7 @@ void uiprivUninitUnderlineColors(void) // TODO in fact I should just write something to explain everything in this file... struct foreachParams { CFMutableAttributedStringRef mas; - NSMutableArray *backgroundBlocks; + NSMutableArray *backgroundParams; }; // unlike the other systems, Core Text rolls family, size, weight, italic, width, AND opentype features into the "font" attribute @@ -271,20 +271,6 @@ static void addFontAttributeToRange(struct foreachParams *p, size_t start, size_ } } -static backgroundBlock mkBackgroundBlock(size_t start, size_t end, double r, double g, double b, double a) -{ - return Block_copy(^(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y) { - uiDrawBrush brush; - - brush.Type = uiDrawBrushTypeSolid; - brush.R = r; - brush.G = g; - brush.B = b; - brush.A = a; -//TODO drawTextBackground(c, x, y, layout, start, end, &brush, 0); - }); -} - static CGColorRef mkcolor(double r, double g, double b, double a) { CGColorSpaceRef colorspace; @@ -305,20 +291,38 @@ static CGColorRef mkcolor(double r, double g, double b, double a) return color; } +static void addBackgroundAttribute(struct foreachParams *p, size_t start, size_t end, double r, double g, double b, double a) +{ + uiprivDrawTextBackgroundParams *dtb; + + // TODO make sure this works properly with line paragraph spacings (after figuring out what that means, of course) + if (FUTURE_kCTBackgroundColorAttributeName != NULL) { + CGColorRef color; + CFRange range; + + color = mkcolor(r, g, b, a); + range.location = start; + range.length = end - start; + CFAttributedStringSetAttribute(p->mas, range, *FUTURE_kCTBackgroundColorAttributeName, color); + CFRelease(color); + return; + } + + dtb = [[uiprivDrawTextBackgroundParams alloc] initWithStart:start end:end r:r g:g b:b a:a]; + [p->backgroundParams addObject:dtb]; + [dtb release]; +} + static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute *attr, size_t start, size_t end, void *data) { struct foreachParams *p = (struct foreachParams *) data; CFRange range; CGColorRef color; - size_t ostart, oend; - backgroundBlock block; int32_t us; CFNumberRef num; double r, g, b, a; uiUnderlineColor colorType; - ostart = start; - oend = end; start = uiprivAttributedStringUTF8ToUTF16(s, start); end = uiprivAttributedStringUTF8ToUTF16(s, end); range.location = start; @@ -340,9 +344,7 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute break; case uiAttributeTypeBackground: uiAttributeColor(attr, &r, &g, &b, &a); - block = mkBackgroundBlock(ostart, oend, r, g, b, a); - [p->backgroundBlocks addObject:block]; - Block_release(block); + addBackgroundAttribute(p, start, end, r, g, b, a); break; // TODO turn into a class, like we did with the font attributes, or even integrate *into* the font attributes case uiAttributeTypeUnderline: @@ -453,7 +455,7 @@ static CTParagraphStyleRef mkParagraphStyle(uiDrawTextLayoutParams *p) } // TODO either rename this to uiprivDrawTextLayoutParams... or rename this file or both or split the struct or something else... -CFAttributedStringRef uiprivAttributedStringToCFAttributedString(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks) +CFAttributedStringRef uiprivAttributedStringToCFAttributedString(uiDrawTextLayoutParams *p, NSArray **backgroundParams) { CFStringRef cfstr; CFMutableDictionaryRef defaultAttrs; @@ -493,11 +495,11 @@ CFAttributedStringRef uiprivAttributedStringToCFAttributedString(uiDrawTextLayou CFAttributedStringBeginEditing(mas); fep.mas = mas; - fep.backgroundBlocks = [NSMutableArray new]; + fep.backgroundParams = [NSMutableArray new]; uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); applyFontAttributes(mas, p->DefaultFont); CFAttributedStringEndEditing(mas); - *backgroundBlocks = fep.backgroundBlocks; + *backgroundParams = fep.backgroundParams; return mas; } diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 21c6e45e..5406617c 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -8,7 +8,7 @@ // in the usual case, the separate copy will just be identical to the regular one, with extra references to everything within @interface uiprivTextFrame : NSObject { CFAttributedStringRef attrstr; - NSArray *backgroundBlocks; + NSArray *backgroundParams; CTFramesetterRef framesetter; CGSize size; CGPathRef path; @@ -20,6 +20,29 @@ - (CFArrayRef)lines; @end +@implementation uiprivDrawTextBackgroundParams + +- (id)initWithStart:(size_t)s end:(size_t)e r:(double)red g:(double)green b:(double)blue a:(double)alpha +{ + self = [super init]; + if (self) { + self->start = s; + self->end = e; + self->r = red; + self->g = green; + self->b = blue; + self->a = alpha; + } + return self; +} + +- (void)draw:(CGContextRef)c layout:(uiDrawTextLayout *)layout at:(double)x y:(double)y utf8Mapping:(const size_t *)u16tou8 +{ + // TODO +} + +@end + @implementation uiprivTextFrame - (id)initWithLayoutParams:(uiDrawTextLayoutParams *)p @@ -31,7 +54,7 @@ self = [super init]; if (self) { - self->attrstr = uiprivAttributedStringToCFAttributedString(p, &(self->backgroundBlocks)); + self->attrstr = uiprivAttributedStringToCFAttributedString(p, &(self->backgroundParams)); // TODO kCTParagraphStyleSpecifierMaximumLineSpacing, kCTParagraphStyleSpecifierMinimumLineSpacing, kCTParagraphStyleSpecifierLineSpacingAdjustment for line spacing self->framesetter = CTFramesetterCreateWithAttributedString(self->attrstr); if (self->framesetter == NULL) { @@ -71,22 +94,22 @@ CFRelease(self->frame); CFRelease(self->path); CFRelease(self->framesetter); - [self->backgroundBlocks release]; + [self->backgroundParams release]; CFRelease(self->attrstr); [super dealloc]; } - (void)draw:(uiDrawContext *)c textLayout:(uiDrawTextLayout *)tl at:(double)x y:(double)y { - backgroundBlock b; + uiprivDrawTextBackgroundParams *dtb; CGAffineTransform textMatrix; CGContextSaveGState(c->c); // save the text matrix because it's not part of the graphics state textMatrix = CGContextGetTextMatrix(c->c); - for (b in self->backgroundBlocks) - b(c, tl, x, y); + for (dtb in self->backgroundParams) + /* TODO */; // Core Text doesn't draw onto a flipped view correctly; we have to pretend it was unflipped // see the iOS bits of the first example at https://developer.apple.com/library/mac/documentation/StringsTextFonts/Conceptual/CoreText_Programming/LayoutOperations/LayoutOperations.html#//apple_ref/doc/uid/TP40005533-CH12-SW1 (iOS is naturally flipped) diff --git a/darwin/future.m b/darwin/future.m index 60936f40..a262d009 100644 --- a/darwin/future.m +++ b/darwin/future.m @@ -8,6 +8,9 @@ CFStringRef *FUTURE_kCTFontOpenTypeFeatureTag = NULL; CFStringRef *FUTURE_kCTFontOpenTypeFeatureValue = NULL; +// added in OS X 10.12; we need 10.8 +CFStringRef *FUTURE_kCTBackgroundColorAttributeName = NULL; + // note that we treat any error as "the symbols aren't there" (and don't care if dlclose() failed) void loadFutures(void) { @@ -20,6 +23,7 @@ void loadFutures(void) #define GET(var, fn) *((void **) (&var)) = dlsym(handle, #fn) GET(FUTURE_kCTFontOpenTypeFeatureTag, kCTFontOpenTypeFeatureTag); GET(FUTURE_kCTFontOpenTypeFeatureValue, kCTFontOpenTypeFeatureValue); + GET(FUTURE_kCTBackgroundColorAttributeName, kCTBackgroundColorAttributeName); dlclose(handle); } diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 0711c68b..8b95e315 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -145,6 +145,7 @@ extern void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdg // future.m extern CFStringRef *FUTURE_kCTFontOpenTypeFeatureTag; extern CFStringRef *FUTURE_kCTFontOpenTypeFeatureValue; +extern CFStringRef *FUTURE_kCTBackgroundColorAttributeName; extern void loadFutures(void); extern void FUTURE_NSLayoutConstraint_setIdentifier(NSLayoutConstraint *constraint, NSString *identifier); extern BOOL FUTURE_NSWindow_performWindowDragWithEvent(NSWindow *w, NSEvent *initialEvent); From 008be9b6d8889cbf711b8ebe919cbcdd401fd06e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 11 Mar 2018 15:55:28 -0400 Subject: [PATCH 0906/1329] Began migrating the Unix draw text code by moving the existing files out of the way. --- unix/{_old_drawtext.c => OLD__old_drawtext.c} | 0 unix/{attrstr.c => OLD_attrstr.c} | 0 unix/{drawtext.c => OLD_drawtext.c} | 0 unix/{fontbutton.c => OLD_fontbutton.c} | 0 unix/{graphemes.c => OLD_graphemes.c} | 0 unix/{opentype.c => OLD_opentype.c} | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename unix/{_old_drawtext.c => OLD__old_drawtext.c} (100%) rename unix/{attrstr.c => OLD_attrstr.c} (100%) rename unix/{drawtext.c => OLD_drawtext.c} (100%) rename unix/{fontbutton.c => OLD_fontbutton.c} (100%) rename unix/{graphemes.c => OLD_graphemes.c} (100%) rename unix/{opentype.c => OLD_opentype.c} (100%) diff --git a/unix/_old_drawtext.c b/unix/OLD__old_drawtext.c similarity index 100% rename from unix/_old_drawtext.c rename to unix/OLD__old_drawtext.c diff --git a/unix/attrstr.c b/unix/OLD_attrstr.c similarity index 100% rename from unix/attrstr.c rename to unix/OLD_attrstr.c diff --git a/unix/drawtext.c b/unix/OLD_drawtext.c similarity index 100% rename from unix/drawtext.c rename to unix/OLD_drawtext.c diff --git a/unix/fontbutton.c b/unix/OLD_fontbutton.c similarity index 100% rename from unix/fontbutton.c rename to unix/OLD_fontbutton.c diff --git a/unix/graphemes.c b/unix/OLD_graphemes.c similarity index 100% rename from unix/graphemes.c rename to unix/OLD_graphemes.c diff --git a/unix/opentype.c b/unix/OLD_opentype.c similarity index 100% rename from unix/opentype.c rename to unix/OLD_opentype.c From 1cce6dc70416de048201ecde9513c138adce0afc Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 11 Mar 2018 16:15:28 -0400 Subject: [PATCH 0907/1329] Migrated opentype.c and graphemes.c back. --- unix/OLD_opentype.c | 208 -------------------------- unix/attrstr.h | 4 + unix/{OLD_graphemes.c => graphemes.c} | 5 +- unix/opentype.c | 26 ++++ 4 files changed, 33 insertions(+), 210 deletions(-) delete mode 100644 unix/OLD_opentype.c create mode 100644 unix/attrstr.h rename unix/{OLD_graphemes.c => graphemes.c} (92%) create mode 100644 unix/opentype.c diff --git a/unix/OLD_opentype.c b/unix/OLD_opentype.c deleted file mode 100644 index 30933ea8..00000000 --- a/unix/OLD_opentype.c +++ /dev/null @@ -1,208 +0,0 @@ -// 11 may 2017 -#include "uipriv_unix.h" - -// TODO switch from GINT_TO_POINTER() and so to a fake GUINT_TO_POINTER()? - -struct uiOpenTypeFeatures { - GHashTable *tags; -}; - -uiOpenTypeFeatures *uiNewOpenTypeFeatures(void) -{ - uiOpenTypeFeatures *otf; - - otf = uiNew(uiOpenTypeFeatures); - otf->tags = g_hash_table_new(g_direct_hash, g_direct_equal); - return otf; -} - -void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf) -{ - g_hash_table_destroy(otf->tags); - uiFree(otf); -} - -static void cloneTags(gpointer key, gpointer value, gpointer data) -{ - // TODO is there a G_HASH_TABLE()? - g_hash_table_replace((GHashTable *) data, key, value); -} - -uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures *otf) -{ - uiOpenTypeFeatures *out; - - // TODO switch the windows one to use this - out = uiNewOpenTypeFeatures(); - g_hash_table_foreach(otf->tags, cloneTags, out->tags); - return out; -} - -static gpointer mkTag(char a, char b, char c, char d) -{ - uint32_t tag; - - tag = (((uint32_t) a) & 0xFF) << 24; - tag |= (((uint32_t) b) & 0xFF) << 16; - tag |= (((uint32_t) c) & 0xFF) << 8; - tag |= ((uint32_t) d) & 0xFF; - return GINT_TO_POINTER(tag); -} - -void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value) -{ - g_hash_table_replace(otf->tags, mkTag(a, b, c, d), GINT_TO_POINTER(value)); -} - -void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d) -{ - // will just return FALSE if the tag is not in otf->tags (half-documented as such), so we can use it safely - g_hash_table_remove(otf->tags, mkTag(a, b, c, d)); -} - -// TODO will the const wreck stuff up? -int uiOpenTypeFeaturesGet(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value) -{ - gboolean found; - gpointer gv; - - found = g_hash_table_lookup_extended(otf->tags, - mkTag(a, b, c, d), - NULL, &gv); - if (!found) - return 0; - *value = GPOINTER_TO_INT(gv); - return 1; -} - -struct otfForEach { - const uiOpenTypeFeatures *otf; - uiOpenTypeFeaturesForEachFunc f; - void *data; - uiForEach ret; -}; - -static void foreach(gpointer key, gpointer value, gpointer data) -{ - struct otfForEach *ofe = (struct otfForEach *) data; - uint32_t tag; - uint8_t a, b, c, d; - - // we can't stop early, so just ignore the rest if we have to - if (ofe->ret == uiForEachStop) - return; - tag = GPOINTER_TO_INT(key); - a = (uint8_t) ((tag >> 24) & 0xFF); - b = (uint8_t) ((tag >> 16) & 0xFF); - c = (uint8_t) ((tag >> 8) & 0xFF); - d = (uint8_t) (tag & 0xFF); - ofe->ret = (*(ofe->f))(ofe->otf, (char) a, (char) b, (char) c, (char) d, GPOINTER_TO_INT(value), ofe->data); -} - -void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data) -{ - struct otfForEach ofe; - - memset(&ofe, 0, sizeof (struct otfForEach)); - ofe.otf = otf; - ofe.f = f; - ofe.data = data; - g_hash_table_foreach(otf->tags, foreach, &ofe); -} - -static gint tagcmp(gconstpointer a, gconstpointer b) -{ - return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b); -} - -static GList *copySortedKeys(GHashTable *tags) -{ - GList *k, *copy; - - k = g_hash_table_get_keys(tags); - copy = g_list_copy(k); - copy = g_list_sort(copy, tagcmp); - // TODO do we free k? the docs contradict themselves - // TODO I already forgot, does g_list_sort() copy, or just change the head? - return copy; -} - -int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeatures *b) -{ - GList *ak, *bk; - GList *ai, *bi; - guint na, nb; - guint i; - int equal = 0; - - if (a == NULL && b == NULL) - return 1; - if (a == NULL || b == NULL) - return 0; - - ak = copySortedKeys(a->tags); - bk = copySortedKeys(b->tags); - - na = g_list_length(ak); - nb = g_list_length(bk); - if (na != nb) { - equal = 0; - goto out; - } - - // TODO use GPOINTER_TO_INT() in these? - ai = ak; - bi = bk; - for (i = 0; i < na; i++) { - gpointer av, bv; - - // compare keys - // this is why we needed to sort earlier - if (ai->data != bi->data) { - equal = 0; - goto out; - } - // and compare values - av = g_hash_table_lookup(a->tags, ai->data); - bv = g_hash_table_lookup(b->tags, bi->data); - if (av != bv) { - equal = 0; - goto out; - } - ai = ai->next; - bi = bi->next; - } - - // all good - equal = 1; - -out: - g_list_free(bk); - g_list_free(ak); - return equal; -} - -// see https://developer.mozilla.org/en/docs/Web/CSS/font-feature-settings -// TODO make this a g_hash_table_foreach() function (which requires duplicating code)? -static uiForEach toCSS(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data) -{ - // TODO is there a G_STRING()? - GString *s = (GString *) data; - - // the last trailing comma is removed after foreach is done - g_string_append_printf(s, "\"%c%c%c%c\" %" PRIu32 ", ", - a, b, c, d, value); - return uiForEachContinue; -} - -gchar *otfToPangoCSSString(const uiOpenTypeFeatures *otf) -{ - GString *s; - - s = g_string_new(""); - uiOpenTypeFeaturesForEach(otf, toCSS, s); - if (s->len != 0) - // and remove the last comma - g_string_truncate(s, s->len - 2); - return g_string_free(s, FALSE); -} diff --git a/unix/attrstr.h b/unix/attrstr.h new file mode 100644 index 00000000..3c3e49b3 --- /dev/null +++ b/unix/attrstr.h @@ -0,0 +1,4 @@ +// 11 march 2018 + +// opentype.c +extern GString *uiprivOpenTypeFeaturesToPangoCSSFeaturesString(const uiOpenTypeFeatures *otf); diff --git a/unix/OLD_graphemes.c b/unix/graphemes.c similarity index 92% rename from unix/OLD_graphemes.c rename to unix/graphemes.c index 4f957352..296e655f 100644 --- a/unix/OLD_graphemes.c +++ b/unix/graphemes.c @@ -1,12 +1,13 @@ // 25 may 2016 #include "uipriv_unix.h" +#include "attrstr.h" -int graphemesTakesUTF16(void) +int uiprivGraphemesTakesUTF16(void) { return 0; } -struct graphemes *graphemes(void *s, size_t len) +struct graphemes *uiprivNewGraphemes(void *s, size_t len) { struct graphemes *g; char *text = (char *) s; diff --git a/unix/opentype.c b/unix/opentype.c new file mode 100644 index 00000000..281acda7 --- /dev/null +++ b/unix/opentype.c @@ -0,0 +1,26 @@ +// 11 may 2017 +#include "uipriv_unix.h" +#include "attrstr.h" + +// see https://developer.mozilla.org/en/docs/Web/CSS/font-feature-settings +static uiForEach toCSS(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data) +{ + GString *s = (GString *) data; + + // the last trailing comma is removed after foreach is done + g_string_append_printf(s, "\"%c%c%c%c\" %" PRIu32 ", ", + a, b, c, d, value); + return uiForEachContinue; +} + +GString *uiprivOpenTypeFeaturesToPangoCSSFeaturesString(const uiOpenTypeFeatures *otf) +{ + GString *s; + + s = g_string_new(""); + uiOpenTypeFeaturesForEach(otf, toCSS, s); + if (s->len != 0) + // and remove the last comma + g_string_truncate(s, s->len - 2); + return s; +} From 7dc5c6d940800cfb3b9e29891909ec8d510a605a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 11 Mar 2018 17:36:22 -0400 Subject: [PATCH 0908/1329] Migrated attrstr.c back. --- darwin/attrstr.m | 2 +- unix/OLD_attrstr.c | 211 --------------------------------------------- unix/attrstr.c | 173 +++++++++++++++++++++++++++++++++++++ unix/attrstr.h | 16 ++++ 4 files changed, 190 insertions(+), 212 deletions(-) delete mode 100644 unix/OLD_attrstr.c create mode 100644 unix/attrstr.c diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 530e6a22..180bf272 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -454,7 +454,7 @@ static CTParagraphStyleRef mkParagraphStyle(uiDrawTextLayoutParams *p) return ps; } -// TODO either rename this to uiprivDrawTextLayoutParams... or rename this file or both or split the struct or something else... +// TODO either rename this (on all platforms) to uiprivDrawTextLayoutParams... or rename this file or both or split the struct or something else... CFAttributedStringRef uiprivAttributedStringToCFAttributedString(uiDrawTextLayoutParams *p, NSArray **backgroundParams) { CFStringRef cfstr; diff --git a/unix/OLD_attrstr.c b/unix/OLD_attrstr.c deleted file mode 100644 index 36141b7c..00000000 --- a/unix/OLD_attrstr.c +++ /dev/null @@ -1,211 +0,0 @@ -// 12 february 2017 -#include "uipriv_unix.h" - -// TODO pango alpha attributes turn 0 into 65535 :| - -// TODO make this name less generic? -struct foreachParams { - PangoAttrList *attrs; - // TODO use pango's built-in background attribute? - GPtrArray *backgroundClosures; -}; - -struct closureParams { - size_t start; - size_t end; - double r; - double g; - double b; - double a; -}; - -static void backgroundClosure(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y, gpointer data) -{ - struct closureParams *p = (struct closureParams *) data; - uiDrawBrush brush; - - brush.Type = uiDrawBrushTypeSolid; - brush.R = p->r; - brush.G = p->g; - brush.B = p->b; - brush.A = p->a; - drawTextBackground(c, x, y, layout, p->start, p->end, &brush, 0); -} - -static void freeClosureParams(gpointer data, GClosure *closure) -{ - uiFree((struct closureParams *) data); -} - -static GClosure *mkBackgroundClosure(size_t start, size_t end, double r, double g, double b, double a) -{ - struct closureParams *p; - GClosure *closure; - - p = uiNew(struct closureParams); - p->start = start; - p->end = end; - p->r = r; - p->g = g; - p->b = b; - p->a = a; - closure = g_cclosure_new(G_CALLBACK(backgroundClosure), p, freeClosureParams); - // TODO write a specific marshaler - // TODO or drop the closure stuff entirely - g_closure_set_marshal(closure, g_cclosure_marshal_generic); - return closure; -} - -static void addattr(struct foreachParams *p, size_t start, size_t end, PangoAttribute *attr) -{ - if (attr == NULL) // in case of a future attribute - return; - attr->start_index = start; - attr->end_index = end; - pango_attr_list_insert(p->attrs, attr); -} - -static uiForEach processAttribute(const uiAttributedString *s, const uiAttributeSpec *spec, size_t start, size_t end, void *data) -{ - struct foreachParams *p = (struct foreachParams *) data; - GClosure *closure; - PangoUnderline underline; - char *featurestr; - - switch (spec->Type) { - case uiAttributeFamily: - addattr(p, start, end, - pango_attr_family_new(spec->Family)); - break; - case uiAttributeSize: - addattr(p, start, end, - pango_attr_size_new(cairoToPango(spec->Double))); - break; - case uiAttributeWeight: - // TODO reverse the misalignment from drawtext.c if it is corrected - addattr(p, start, end, - pango_attr_weight_new((PangoWeight) (spec->Value))); - break; - case uiAttributeItalic: - addattr(p, start, end, - pango_attr_style_new(pangoItalics[(uiDrawTextItalic) (spec->Value)])); - break; - case uiAttributeStretch: - addattr(p, start, end, - pango_attr_stretch_new(pangoStretches[(uiDrawTextStretch) (spec->Value)])); - break; - case uiAttributeColor: - addattr(p, start, end, - pango_attr_foreground_new( - (guint16) (spec->R * 65535.0), - (guint16) (spec->G * 65535.0), - (guint16) (spec->B * 65535.0))); - addattr(p, start, end, - FUTURE_pango_attr_foreground_alpha_new( - (guint16) (spec->A * 65535.0))); - break; - case uiAttributeBackground: - closure = mkBackgroundClosure(start, end, - spec->R, spec->G, spec->B, spec->A); - g_ptr_array_add(p->backgroundClosures, closure); - break; - case uiAttributeUnderline: - switch (spec->Value) { - case uiDrawUnderlineStyleNone: - underline = PANGO_UNDERLINE_NONE; - break; - case uiDrawUnderlineStyleSingle: - underline = PANGO_UNDERLINE_SINGLE; - break; - case uiDrawUnderlineStyleDouble: - underline = PANGO_UNDERLINE_DOUBLE; - break; - case uiDrawUnderlineStyleSuggestion: - underline = PANGO_UNDERLINE_ERROR; - break; - } - addattr(p, start, end, - pango_attr_underline_new(underline)); - break; - case uiAttributeUnderlineColor: - switch (spec->Value) { - case uiDrawUnderlineColorCustom: - addattr(p, start, end, - pango_attr_underline_color_new( - (guint16) (spec->R * 65535.0), - (guint16) (spec->G * 65535.0), - (guint16) (spec->B * 65535.0))); - break; - case uiDrawUnderlineColorSpelling: - // TODO GtkTextView style property error-underline-color - addattr(p, start, end, - pango_attr_underline_color_new(65535, 0, 0)); - break; - case uiDrawUnderlineColorGrammar: - // TODO find a more appropriate color - addattr(p, start, end, - pango_attr_underline_color_new(0, 65535, 0)); - break; - case uiDrawUnderlineColorAuxiliary: - // TODO find a more appropriate color - addattr(p, start, end, - pango_attr_underline_color_new(0, 0, 65535)); - break; - } - break; - case uiAttributeFeatures: - // only generate an attribute if spec->Features is not NULL - // TODO state that this is allowed - if (spec->Features == NULL) - break; - featurestr = otfToPangoCSSString(spec->Features); - addattr(p, start, end, - FUTURE_pango_attr_font_features_new(featurestr)); - g_free(featurestr); - break; - default: - // TODO complain - ; - } - return uiForEachContinue; -} - -static void unrefClosure(gpointer data) -{ - g_closure_unref((GClosure *) data); -} - -PangoAttrList *attrstrToPangoAttrList(uiDrawTextLayoutParams *p, GPtrArray **backgroundClosures) -{ - struct foreachParams fep; - - fep.attrs = pango_attr_list_new(); - fep.backgroundClosures = g_ptr_array_new_with_free_func(unrefClosure); - uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); - *backgroundClosures = fep.backgroundClosures; - return fep.attrs; -} - -void invokeBackgroundClosure(GClosure *closure, uiDrawContext *c, uiDrawTextLayout *layout, double x, double y) -{ - GValue values[4] = { - // the zero-initialization is needed for g_value_init() to work - G_VALUE_INIT, - G_VALUE_INIT, - G_VALUE_INIT, - G_VALUE_INIT, - }; - - g_value_init(values + 0, G_TYPE_POINTER); - g_value_set_pointer(values + 0, c); - g_value_init(values + 1, G_TYPE_POINTER); - g_value_set_pointer(values + 1, layout); - g_value_init(values + 2, G_TYPE_DOUBLE); - g_value_set_double(values + 2, x); - g_value_init(values + 3, G_TYPE_DOUBLE); - g_value_set_double(values + 3, y); - g_closure_invoke(closure, - NULL, - 4, values, - NULL); -} diff --git a/unix/attrstr.c b/unix/attrstr.c new file mode 100644 index 00000000..0ffb0b56 --- /dev/null +++ b/unix/attrstr.c @@ -0,0 +1,173 @@ +// 12 february 2017 +#include "uipriv_unix.h" +#include "attrstr.h" + +// TODO pango alpha attributes turn 0 into 65535 :| + +// TODO make this name less generic? +struct foreachParams { + PangoAttrList *attrs; + // TODO use pango's built-in background attribute + GPtrArray *backgroundParams; +}; + +static void backgroundClosure(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y, gpointer data) +{ + struct closureParams *p = (struct closureParams *) data; + uiDrawBrush brush; + + brush.Type = uiDrawBrushTypeSolid; + brush.R = p->r; + brush.G = p->g; + brush.B = p->b; + brush.A = p->a; +//TODO drawTextBackground(c, x, y, layout, p->start, p->end, &brush, 0); +} + +static void addBackgroundAttribute(struct foreachParams *p, size_t start, size_t end, double r, double g, double b, double a) +{ + uiprivDrawTextBackgroundParams *dtb; + + dtb = uiprivNew(uiprivDrawTextBackgroundParams); + dtb->start = start; + dtb->end = end; + dtb->r = r; + dtb->g = g; + dtb->b = b; + dtb->a = a; + g_ptr_array_add(p->backgroundParams, dtb); +} + +static void addattr(struct foreachParams *p, size_t start, size_t end, PangoAttribute *attr) +{ + if (attr == NULL) // in case of a future attribute + return; + attr->start_index = start; + attr->end_index = end; + pango_attr_list_insert(p->attrs, attr); +} + +static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute *attr, size_t start, size_t end, void *data) +{ + struct foreachParams *p = (struct foreachParams *) data; + double r, g, b, a; + PangoUnderline underline; + uiUnderlineColor colorType; + const uiOpenTypeFeatures *features; + GString *featurestr; + + switch (uiAttributeGetType(attr)) { + case uiAttributeTypeFamily: + addattr(p, start, end, + pango_attr_family_new(uiAttributeFamily(attr))); + break; + case uiAttributeTypeSize: + addattr(p, start, end, + pango_attr_size_new(cairoToPango(uiAttributeSize(attr)))); + break; + case uiAttributeTypeWeight: + // TODO reverse the misalignment from drawtext.c if it is corrected + addattr(p, start, end, + pango_attr_weight_new((PangoWeight) uiAttributeWeight(attr))); + break; + case uiAttributeTypeItalic: + addattr(p, start, end, + pango_attr_style_new(pangoItalics[uiAttributeItalic(attr)])); + break; + case uiAttributeTypeStretch: + addattr(p, start, end, + pango_attr_stretch_new(pangoStretches[uiAttributeStretch(attr)])); + break; + case uiAttributeTypeColor: + uiAttributeColor(attr, &r, &g, &b, &a); + addattr(p, start, end, + pango_attr_foreground_new( + (guint16) (r * 65535.0), + (guint16) (g * 65535.0), + (guint16) (b * 65535.0))); + addattr(p, start, end, + FUTURE_pango_attr_foreground_alpha_new( + (guint16) (a * 65535.0))); + break; + case uiAttributeTypeBackground: + uiAttributeColor(attr, &r, &g, &b, &a); + addBackgroundAttribute(p, start, end, r, g, b, a); + break; + case uiAttributeTypeUnderline: + switch (uiAttributeUnderline(attr)) { + case uiUnderlineNone: + underline = PANGO_UNDERLINE_NONE; + break; + case uiUnderlineSingle: + underline = PANGO_UNDERLINE_SINGLE; + break; + case uiUnderlineDouble: + underline = PANGO_UNDERLINE_DOUBLE; + break; + case uiUnderlineSuggestion: + underline = PANGO_UNDERLINE_ERROR; + break; + } + addattr(p, start, end, + pango_attr_underline_new(underline)); + break; + case uiAttributeTypeUnderlineColor: + uiAttributeUnderlineColor(attr, &colorType, &r, &g, &b, &a); + switch (colorType) { + case uiUnderlineColorCustom: + addattr(p, start, end, + pango_attr_underline_color_new( + (guint16) (r * 65535.0), + (guint16) (g * 65535.0), + (guint16) (b * 65535.0))); + break; + case uiUnderlineColorSpelling: + // TODO GtkTextView style property error-underline-color + addattr(p, start, end, + pango_attr_underline_color_new(65535, 0, 0)); + break; + case uiUnderlineColorGrammar: + // TODO find a more appropriate color + addattr(p, start, end, + pango_attr_underline_color_new(0, 65535, 0)); + break; + case uiUnderlineColorAuxiliary: + // TODO find a more appropriate color + addattr(p, start, end, + pango_attr_underline_color_new(0, 0, 65535)); + break; + } + break; + case uiAttributeTypeFeatures: + // only generate an attribute if the features object is not NULL + // TODO state that this is allowed + features = uiAttributeFeatures(attr); + if (features == NULL) + break; + featurestr = uiprivOpenTypeFeaturesToPangoCSSFeaturesString(features); + addattr(p, start, end, + FUTURE_pango_attr_font_features_new(featurestr->str)); + g_string_free(featurestr, TRUE); + break; + default: + // TODO complain + ; + } + return uiForEachContinue; +} + +static void freeBackgroundParams(gpointer data) +{ + uiprivFree((uiprivDrawTextBackgroundParams *) data); +} + +PangoAttrList *uiprivAttributedStringToPangoAttrList(uiDrawTextLayoutParams *p, GPtrArray **backgroundParams) +{ + struct foreachParams fep; + + fep.attrs = pango_attr_list_new(); + fep.backgroundParams = g_ptr_array_new_with_free_func(freeBackgroundParams); + uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); + *backgroundParams = fep.backgroundParams; + return fep.attrs; +} diff --git a/unix/attrstr.h b/unix/attrstr.h index 3c3e49b3..c2632786 100644 --- a/unix/attrstr.h +++ b/unix/attrstr.h @@ -1,4 +1,20 @@ // 11 march 2018 +#import "../common/attrstr.h" // opentype.c extern GString *uiprivOpenTypeFeaturesToPangoCSSFeaturesString(const uiOpenTypeFeatures *otf); + +// attrstr.c +extern PangoAttrList *uiprivAttributedStringToPangoAttrList(uiDrawTextLayoutParams *p, GPtrArray **backgroundParams); + +// drawtext.c +// TODO figure out where this type should *really* go in all the headers... +typedef struct uiprivDrawTextBackgroundParams uiprivDrawTextBackgroundParams; +struct uiprivDrawTextBackgroundParams { + size_t start; + size_t end; + double r; + double g; + double b; + double a; +}; From 24d2220fe5f2ff5b2fef67cb469371fbc71921c3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 11 Mar 2018 18:12:15 -0400 Subject: [PATCH 0909/1329] Migrated drawtext.c. --- unix/OLD_drawtext.c | 82 --------------------------- unix/attrstr.h | 8 +++ unix/drawtext.c | 132 ++++++++++++++++++++++++++++++++++++++++++++ unix/uipriv_unix.h | 13 ----- 4 files changed, 140 insertions(+), 95 deletions(-) create mode 100644 unix/drawtext.c diff --git a/unix/OLD_drawtext.c b/unix/OLD_drawtext.c index 373702cd..0626b6be 100644 --- a/unix/OLD_drawtext.c +++ b/unix/OLD_drawtext.c @@ -15,35 +15,6 @@ struct uiDrawTextLayout { int nLines; }; -// See https://developer.gnome.org/pango/1.30/pango-Cairo-Rendering.html#pango-Cairo-Rendering.description -// For the conversion, see https://developer.gnome.org/pango/1.30/pango-Glyph-Storage.html#pango-units-to-double and https://developer.gnome.org/pango/1.30/pango-Glyph-Storage.html#pango-units-from-double -#define pangoToCairo(pango) (pango_units_to_double(pango)) -// cairoToPango() is in uipriv_unix.h because attrstr.c needs it - -// we need a context for a few things -// the documentation suggests creating cairo_t-specific, GdkScreen-specific, or even GtkWidget-specific contexts, but we can't really do that because we want our uiDrawTextFonts and uiDrawTextLayouts to be context-independent -// we could use pango_font_map_create_context(pango_cairo_font_map_get_default()) but that will ignore GDK-specific settings -// so let's use gdk_pango_context_get() instead; even though it's for the default screen only, it's good enough for us -#define mkGenericPangoCairoContext() (gdk_pango_context_get()) - -const PangoStyle pangoItalics[] = { - [uiDrawTextItalicNormal] = PANGO_STYLE_NORMAL, - [uiDrawTextItalicOblique] = PANGO_STYLE_OBLIQUE, - [uiDrawTextItalicItalic] = PANGO_STYLE_ITALIC, -}; - -const PangoStretch pangoStretches[] = { - [uiDrawTextStretchUltraCondensed] = PANGO_STRETCH_ULTRA_CONDENSED, - [uiDrawTextStretchExtraCondensed] = PANGO_STRETCH_EXTRA_CONDENSED, - [uiDrawTextStretchCondensed] = PANGO_STRETCH_CONDENSED, - [uiDrawTextStretchSemiCondensed] = PANGO_STRETCH_SEMI_CONDENSED, - [uiDrawTextStretchNormal] = PANGO_STRETCH_NORMAL, - [uiDrawTextStretchSemiExpanded] = PANGO_STRETCH_SEMI_EXPANDED, - [uiDrawTextStretchExpanded] = PANGO_STRETCH_EXPANDED, - [uiDrawTextStretchExtraExpanded] = PANGO_STRETCH_EXTRA_EXPANDED, - [uiDrawTextStretchUltraExpanded] = PANGO_STRETCH_ULTRA_EXPANDED, -}; - // TODO neither these nor the overall extents seem to include trailing whitespace... we need to figure that out too static void computeLineMetrics(uiDrawTextLayout *tl) { @@ -94,12 +65,6 @@ static void computeLineMetrics(uiDrawTextLayout *tl) pango_layout_iter_free(iter); } -static const PangoAlignment pangoAligns[] = { - [uiDrawTextAlignLeft] = PANGO_ALIGN_LEFT, - [uiDrawTextAlignCenter] = PANGO_ALIGN_CENTER, - [uiDrawTextAlignRight] = PANGO_ALIGN_RIGHT, -}; - uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) { uiDrawTextLayout *tl; @@ -152,53 +117,6 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) return tl; } -void uiDrawFreeTextLayout(uiDrawTextLayout *tl) -{ - uiFree(tl->lineMetrics); - g_ptr_array_unref(tl->backgroundClosures); - g_object_unref(tl->layout); - uiFree(tl); -} - -void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) -{ - guint i; - GClosure *closure; - - for (i = 0; i < tl->backgroundClosures->len; i++) { - closure = (GClosure *) g_ptr_array_index(tl->backgroundClosures, i); - invokeBackgroundClosure(closure, c, tl, x, y); - } - // TODO have an implicit save/restore on each drawing functions instead? and is this correct? - cairo_set_source_rgb(c->cr, 0.0, 0.0, 0.0); - cairo_move_to(c->cr, x, y); - pango_cairo_show_layout(c->cr, tl->layout); -} - -void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height) -{ - PangoRectangle logical; - - pango_layout_get_extents(tl->layout, NULL, &logical); - *width = pangoToCairo(logical.width); - *height = pangoToCairo(logical.height); -} - -int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) -{ - return pango_layout_get_line_count(tl->layout); -} - -void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end) -{ - PangoLayoutLine *pll; - - pll = pango_layout_get_line_readonly(tl->layout, line); - *start = pll->start_index; - *end = pll->start_index + pll->length; - // TODO unref pll? -} - void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m) { *m = tl->lineMetrics[line]; diff --git a/unix/attrstr.h b/unix/attrstr.h index c2632786..53acc496 100644 --- a/unix/attrstr.h +++ b/unix/attrstr.h @@ -1,6 +1,11 @@ // 11 march 2018 #import "../common/attrstr.h" +// See https://developer.gnome.org/pango/1.30/pango-Cairo-Rendering.html#pango-Cairo-Rendering.description +// For the conversion, see https://developer.gnome.org/pango/1.30/pango-Glyph-Storage.html#pango-units-to-double and https://developer.gnome.org/pango/1.30/pango-Glyph-Storage.html#pango-units-from-double +#define pangoToCairo(pango) (pango_units_to_double(pango)) +#define cairoToPango(cairo) (pango_units_from_double(cairo)) + // opentype.c extern GString *uiprivOpenTypeFeaturesToPangoCSSFeaturesString(const uiOpenTypeFeatures *otf); @@ -18,3 +23,6 @@ struct uiprivDrawTextBackgroundParams { double b; double a; }; +// TODO move this to a fontmatch.c +extern const PangoStyle uiprivPangoItalics[]; +extern const PangoStretch uiprivPangoStretches[]; diff --git a/unix/drawtext.c b/unix/drawtext.c new file mode 100644 index 00000000..d0866929 --- /dev/null +++ b/unix/drawtext.c @@ -0,0 +1,132 @@ +xx 11 march 2018 +#import "uipriv_unix.h" +#import "draw.h" +#import "attrstr.h" + +struct uiDrawTextLayout { + PangoLayout *layout; + GPtrArray *backgroundParams; +}; + +// we need a context for a few things +// the documentation suggests creating cairo_t-specific, GdkScreen-specific, or even GtkWidget-specific contexts, but we can't really do that because we want our uiDrawTextFonts and uiDrawTextLayouts to be context-independent +// we could use pango_font_map_create_context(pango_cairo_font_map_get_default()) but that will ignore GDK-specific settings +// so let's use gdk_pango_context_get() instead; even though it's for the default screen only, it's good enough for us +#define mkGenericPangoCairoContext() (gdk_pango_context_get()) + +const PangoStyle uiprivPangoItalics[] = { + [uiTextItalicNormal] = PANGO_STYLE_NORMAL, + [uiTextItalicOblique] = PANGO_STYLE_OBLIQUE, + [uiTextItalicItalic] = PANGO_STYLE_ITALIC, +}; + +const PangoStretch uiprivPangoStretches[] = { + [uiTextStretchUltraCondensed] = PANGO_STRETCH_ULTRA_CONDENSED, + [uiTextStretchExtraCondensed] = PANGO_STRETCH_EXTRA_CONDENSED, + [uiTextStretchCondensed] = PANGO_STRETCH_CONDENSED, + [uiTextStretchSemiCondensed] = PANGO_STRETCH_SEMI_CONDENSED, + [uiTextStretchNormal] = PANGO_STRETCH_NORMAL, + [uiTextStretchSemiExpanded] = PANGO_STRETCH_SEMI_EXPANDED, + [uiTextStretchExpanded] = PANGO_STRETCH_EXPANDED, + [uiTextStretchExtraExpanded] = PANGO_STRETCH_EXTRA_EXPANDED, + [uiTextStretchUltraExpanded] = PANGO_STRETCH_ULTRA_EXPANDED, +}; + +static const PangoAlignment pangoAligns[] = { + [uiDrawTextAlignLeft] = PANGO_ALIGN_LEFT, + [uiDrawTextAlignCenter] = PANGO_ALIGN_CENTER, + [uiDrawTextAlignRight] = PANGO_ALIGN_RIGHT, +}; + +uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) +{ + uiDrawTextLayout *tl; + PangoContext *context; + PangoFontDescription *desc; + PangoAttrList *attrs; + int pangoWidth; + + tl = uiprivNew(uiDrawTextLayout); + + // in this case, the context is necessary to create the layout + // the layout takes a ref on the context so we can unref it afterward + context = mkGenericPangoCairoContext(); + tl->layout = pango_layout_new(context); + g_object_unref(context); + + // this is safe; pango_layout_set_text() copies the string + pango_layout_set_text(tl->layout, uiAttributedStringString(p->String), -1); + + desc = pango_font_description_new(); + pango_font_description_set_family(desc, p->DefaultFont->Family); + pango_font_description_set_style(desc, uiprivPangoItalics[p->DefaultFont->Italic]); + // for the most part, pango weights correlate to ours + // the differences: + // - Book — libui: 350, Pango: 380 + // - Ultra Heavy — libui: 950, Pango: 1000 + // TODO figure out what to do about this misalignment + pango_font_description_set_weight(desc, p->DefaultFont->Weight); + pango_font_description_set_stretch(desc, uiprivPangoStretches[p->DefaultFont->Stretch]); + // see https://developer.gnome.org/pango/1.30/pango-Fonts.html#pango-font-description-set-size and https://developer.gnome.org/pango/1.30/pango-Glyph-Storage.html#pango-units-from-double + pango_font_description_set_size(desc, pango_units_from_double(p->DefaultFont->Size)); + pango_layout_set_font_description(tl->layout, desc); + // this is safe; the description is copied + pango_font_description_free(desc); + + pangoWidth = cairoToPango(p->Width); + if (p->Width < 0) + pangoWidth = -1; + pango_layout_set_width(tl->layout, pangoWidth); + + pango_layout_set_alignment(tl->layout, pangoAligns[p->Align]); + + attrs = uiprivAttributedStringToPangoAttrList(p, &(tl->backgroundFeatures)); + pango_layout_set_attributes(tl->layout, attrs); + pango_attr_list_unref(attrs); + + return tl; +} + +void uiDrawFreeTextLayout(uiDrawTextLayout *tl) +{ + g_ptr_array_unref(tl->backgroundFeatures); + g_object_unref(tl->layout); + uiprivFree(tl); +} + +void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) +{ + guint i; + + for (i = 0; i < tl->backgroundFeatures->len; i++) { + // TODO + } + // TODO have an implicit save/restore on each drawing functions instead? and is this correct? + cairo_set_source_rgb(c->cr, 0.0, 0.0, 0.0); + cairo_move_to(c->cr, x, y); + pango_cairo_show_layout(c->cr, tl->layout); +} + +void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height) +{ + PangoRectangle logical; + + pango_layout_get_extents(tl->layout, NULL, &logical); + *width = pangoToCairo(logical.width); + *height = pangoToCairo(logical.height); +} + +int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) +{ + return pango_layout_get_line_count(tl->layout); +} + +void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end) +{ + PangoLayoutLine *pll; + + pll = pango_layout_get_line_readonly(tl->layout, line); + *start = pll->start_index; + *end = pll->start_index + pll->length; + // TODO unref pll? +} diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index 46a2426f..531e1c05 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -61,16 +61,3 @@ extern gboolean FUTURE_gtk_widget_path_iter_set_object_name(GtkWidgetPath *path, // drawtext.c extern void fontdescFromPangoFontDescription(PangoFontDescription *pdesc, uiDrawFontDescriptor *uidesc); - -// attrstr.c -extern PangoAttrList *attrstrToPangoAttrList(uiDrawTextLayoutParams *p, GPtrArray **backgroundClosures); -extern void invokeBackgroundClosure(GClosure *closure, uiDrawContext *c, uiDrawTextLayout *layout, double x, double y); - -// drawtext.c -// TODO get rid of these (for attrstr.c) -#define cairoToPango(cairo) (pango_units_from_double(cairo)) -extern const PangoStyle pangoItalics[]; -extern const PangoStretch pangoStretches[]; - -// opentype.c -extern gchar *otfToPangoCSSString(const uiOpenTypeFeatures *otf); From 5939c3203d50dba8f7cd60a798fa818f5953a444 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 11 Mar 2018 19:32:08 -0400 Subject: [PATCH 0910/1329] Created a new file for the font matching code. --- unix/CMakeLists.txt | 1 + unix/attrstr.c | 6 ++--- unix/attrstr.h | 9 +++++--- unix/drawtext.c | 33 ++------------------------- unix/fontmatch.c | 55 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 67 insertions(+), 37 deletions(-) create mode 100644 unix/fontmatch.c diff --git a/unix/CMakeLists.txt b/unix/CMakeLists.txt index c20cf266..eba09ad9 100644 --- a/unix/CMakeLists.txt +++ b/unix/CMakeLists.txt @@ -24,6 +24,7 @@ list(APPEND _LIBUI_SOURCES unix/editablecombo.c unix/entry.c unix/fontbutton.c + unix/fontmatch.c unix/form.c unix/future.c unix/graphemes.c diff --git a/unix/attrstr.c b/unix/attrstr.c index 0ffb0b56..0fc9de89 100644 --- a/unix/attrstr.c +++ b/unix/attrstr.c @@ -68,15 +68,15 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute case uiAttributeTypeWeight: // TODO reverse the misalignment from drawtext.c if it is corrected addattr(p, start, end, - pango_attr_weight_new((PangoWeight) uiAttributeWeight(attr))); + pango_attr_weight_new(uiprivWeightToPangoWeight(uiAttributeWeight(attr)))); break; case uiAttributeTypeItalic: addattr(p, start, end, - pango_attr_style_new(pangoItalics[uiAttributeItalic(attr)])); + pango_attr_style_new(uiprivItalicToPangoStyle(uiAttributeItalic(attr)))); break; case uiAttributeTypeStretch: addattr(p, start, end, - pango_attr_stretch_new(pangoStretches[uiAttributeStretch(attr)])); + pango_attr_stretch_new(uiprivStretchToPangoStretch(uiAttributeStretch(attr)))); break; case uiAttributeTypeColor: uiAttributeColor(attr, &r, &g, &b, &a); diff --git a/unix/attrstr.h b/unix/attrstr.h index 53acc496..5fd072c2 100644 --- a/unix/attrstr.h +++ b/unix/attrstr.h @@ -9,6 +9,12 @@ // opentype.c extern GString *uiprivOpenTypeFeaturesToPangoCSSFeaturesString(const uiOpenTypeFeatures *otf); +// fontmatch.c +extern PangoWeight uiprivWeightToPangoWeight(uiTextWeight w); +extern PangoStyle uiprivItalicToPangoStyle(uiTextItalic i); +extern PangoStretch uiprivStretchToPangoStretch(uiTextStretch s); +extern PangoFontDescription *uiprivFontDescriptorToPangoFontDescription(const uiFontDescriptor *uidesc); + // attrstr.c extern PangoAttrList *uiprivAttributedStringToPangoAttrList(uiDrawTextLayoutParams *p, GPtrArray **backgroundParams); @@ -23,6 +29,3 @@ struct uiprivDrawTextBackgroundParams { double b; double a; }; -// TODO move this to a fontmatch.c -extern const PangoStyle uiprivPangoItalics[]; -extern const PangoStretch uiprivPangoStretches[]; diff --git a/unix/drawtext.c b/unix/drawtext.c index d0866929..47d4362d 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -1,4 +1,4 @@ -xx 11 march 2018 +// 11 march 2018 #import "uipriv_unix.h" #import "draw.h" #import "attrstr.h" @@ -14,24 +14,6 @@ struct uiDrawTextLayout { // so let's use gdk_pango_context_get() instead; even though it's for the default screen only, it's good enough for us #define mkGenericPangoCairoContext() (gdk_pango_context_get()) -const PangoStyle uiprivPangoItalics[] = { - [uiTextItalicNormal] = PANGO_STYLE_NORMAL, - [uiTextItalicOblique] = PANGO_STYLE_OBLIQUE, - [uiTextItalicItalic] = PANGO_STYLE_ITALIC, -}; - -const PangoStretch uiprivPangoStretches[] = { - [uiTextStretchUltraCondensed] = PANGO_STRETCH_ULTRA_CONDENSED, - [uiTextStretchExtraCondensed] = PANGO_STRETCH_EXTRA_CONDENSED, - [uiTextStretchCondensed] = PANGO_STRETCH_CONDENSED, - [uiTextStretchSemiCondensed] = PANGO_STRETCH_SEMI_CONDENSED, - [uiTextStretchNormal] = PANGO_STRETCH_NORMAL, - [uiTextStretchSemiExpanded] = PANGO_STRETCH_SEMI_EXPANDED, - [uiTextStretchExpanded] = PANGO_STRETCH_EXPANDED, - [uiTextStretchExtraExpanded] = PANGO_STRETCH_EXTRA_EXPANDED, - [uiTextStretchUltraExpanded] = PANGO_STRETCH_ULTRA_EXPANDED, -}; - static const PangoAlignment pangoAligns[] = { [uiDrawTextAlignLeft] = PANGO_ALIGN_LEFT, [uiDrawTextAlignCenter] = PANGO_ALIGN_CENTER, @@ -57,18 +39,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) // this is safe; pango_layout_set_text() copies the string pango_layout_set_text(tl->layout, uiAttributedStringString(p->String), -1); - desc = pango_font_description_new(); - pango_font_description_set_family(desc, p->DefaultFont->Family); - pango_font_description_set_style(desc, uiprivPangoItalics[p->DefaultFont->Italic]); - // for the most part, pango weights correlate to ours - // the differences: - // - Book — libui: 350, Pango: 380 - // - Ultra Heavy — libui: 950, Pango: 1000 - // TODO figure out what to do about this misalignment - pango_font_description_set_weight(desc, p->DefaultFont->Weight); - pango_font_description_set_stretch(desc, uiprivPangoStretches[p->DefaultFont->Stretch]); - // see https://developer.gnome.org/pango/1.30/pango-Fonts.html#pango-font-description-set-size and https://developer.gnome.org/pango/1.30/pango-Glyph-Storage.html#pango-units-from-double - pango_font_description_set_size(desc, pango_units_from_double(p->DefaultFont->Size)); + desc = uiprivFontDescriptorToPangoFontDescription(p->DefaultFont); pango_layout_set_font_description(tl->layout, desc); // this is safe; the description is copied pango_font_description_free(desc); diff --git a/unix/fontmatch.c b/unix/fontmatch.c new file mode 100644 index 00000000..05523c24 --- /dev/null +++ b/unix/fontmatch.c @@ -0,0 +1,55 @@ +// 11 march 2018 +#include "uipriv_unix.h" +#include "attrstr.h" + +static const PangoStyle pangoItalics[] = { + [uiTextItalicNormal] = PANGO_STYLE_NORMAL, + [uiTextItalicOblique] = PANGO_STYLE_OBLIQUE, + [uiTextItalicItalic] = PANGO_STYLE_ITALIC, +}; + +static const PangoStretch pangoStretches[] = { + [uiTextStretchUltraCondensed] = PANGO_STRETCH_ULTRA_CONDENSED, + [uiTextStretchExtraCondensed] = PANGO_STRETCH_EXTRA_CONDENSED, + [uiTextStretchCondensed] = PANGO_STRETCH_CONDENSED, + [uiTextStretchSemiCondensed] = PANGO_STRETCH_SEMI_CONDENSED, + [uiTextStretchNormal] = PANGO_STRETCH_NORMAL, + [uiTextStretchSemiExpanded] = PANGO_STRETCH_SEMI_EXPANDED, + [uiTextStretchExpanded] = PANGO_STRETCH_EXPANDED, + [uiTextStretchExtraExpanded] = PANGO_STRETCH_EXTRA_EXPANDED, + [uiTextStretchUltraExpanded] = PANGO_STRETCH_ULTRA_EXPANDED, +}; + +// for the most part, pango weights correlate to ours +// the differences: +// - Book — libui: 350, Pango: 380 +// - Ultra Heavy — libui: 950, Pango: 1000 +// TODO figure out what to do about this misalignment +PangoWeight uiprivWeightToPangoWeight(uiTextWeight w) +{ + return (PangoWeight) w; +} + +PangoStyle uiprivItalicToPangoStyle(uiTextItalic i) +{ + return pangoItalics[i]; +} + +PangoStretch uiprivStretchToPangoStretch(uiTextStretch s) +{ + return pangoStretches[s]; +} + +PangoFontDescription *uiprivFontDescriptorToPangoFontDescription(const uiFontDescriptor *uidesc) +{ + PangoFontDescriptor *desc; + + desc = pango_font_description_new(); + pango_font_description_set_family(desc, uidesc->Family); + // see https://developer.gnome.org/pango/1.30/pango-Fonts.html#pango-font-description-set-size and https://developer.gnome.org/pango/1.30/pango-Glyph-Storage.html#pango-units-from-double + pango_font_description_set_size(desc, pango_units_from_double(uidesc->Size)); + pango_font_description_set_weight(desc, uiprivWeightToPangoWeight(uidesc->Weight)); + pango_font_description_set_style(desc, uiprivItalicToPangoStyle(uidesc->Italic)); + pango_font_description_set_stretch(desc, uiprivStretchToPangoStretch(uidesc->Stretch)); + return desc; +} From 697c926c92893b833651db667ab8ae862ec4f765 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 11 Mar 2018 19:37:30 -0400 Subject: [PATCH 0911/1329] And migrated fontmatch.c back. Let's test. --- unix/OLD_drawtext.c | 22 ---------------------- unix/attrstr.h | 1 + unix/{OLD_fontbutton.c => fontbutton.c} | 5 +++-- unix/fontmatch.c | 21 +++++++++++++++++++++ 4 files changed, 25 insertions(+), 24 deletions(-) rename unix/{OLD_fontbutton.c => fontbutton.c} (92%) diff --git a/unix/OLD_drawtext.c b/unix/OLD_drawtext.c index 0626b6be..dbd2d709 100644 --- a/unix/OLD_drawtext.c +++ b/unix/OLD_drawtext.c @@ -225,25 +225,3 @@ void caretDrawParams(uiDrawContext *c, double height, struct caretDrawParams *p) p->xoff = xoff; p->width = width; } - -// TODO split this and the other font description stuff into their own file? -void fontdescFromPangoFontDescription(PangoFontDescription *pdesc, uiDrawFontDescriptor *uidesc) -{ - PangoStyle pitalic; - PangoStretch pstretch; - - uidesc->Family = uiUnixStrdupText(pango_font_description_get_family(pdesc)); - pitalic = pango_font_description_get_style(pdesc); - // TODO reverse the above misalignment if it is corrected - uidesc->Weight = pango_font_description_get_weight(pdesc); - pstretch = pango_font_description_get_stretch(pdesc); - // absolute size does not matter because, as above, 1 device unit == 1 cairo point - uidesc->Size = pango_units_to_double(pango_font_description_get_size(pdesc)); - - for (uidesc->Italic = uiDrawTextItalicNormal; uidesc->Italic < uiDrawTextItalicItalic; uidesc->Italic++) - if (pangoItalics[uidesc->Italic] == pitalic) - break; - for (uidesc->Stretch = uiDrawTextStretchUltraCondensed; uidesc->Stretch < uiDrawTextStretchUltraExpanded; uidesc->Stretch++) - if (pangoStretches[uidesc->Stretch] == pstretch) - break; -} diff --git a/unix/attrstr.h b/unix/attrstr.h index 5fd072c2..1988fe86 100644 --- a/unix/attrstr.h +++ b/unix/attrstr.h @@ -14,6 +14,7 @@ extern PangoWeight uiprivWeightToPangoWeight(uiTextWeight w); extern PangoStyle uiprivItalicToPangoStyle(uiTextItalic i); extern PangoStretch uiprivStretchToPangoStretch(uiTextStretch s); extern PangoFontDescription *uiprivFontDescriptorToPangoFontDescription(const uiFontDescriptor *uidesc); +extern void uiprivFontDescriptorFromPangoFontDescription(PangoFontDescription *pdesc, uiDrawFontDescriptor *uidesc); // attrstr.c extern PangoAttrList *uiprivAttributedStringToPangoAttrList(uiDrawTextLayoutParams *p, GPtrArray **backgroundParams); diff --git a/unix/OLD_fontbutton.c b/unix/fontbutton.c similarity index 92% rename from unix/OLD_fontbutton.c rename to unix/fontbutton.c index 9a2552b1..3b4a0425 100644 --- a/unix/OLD_fontbutton.c +++ b/unix/fontbutton.c @@ -1,5 +1,6 @@ // 14 april 2016 #include "uipriv_unix.h" +#include "attrstr.h" struct uiFontButton { uiUnixControl c; @@ -31,8 +32,8 @@ void uiFontButtonFont(uiFontButton *b, uiDrawFontDescriptor *desc) PangoFontDescription *pdesc; pdesc = gtk_font_chooser_get_font_desc(b->fc); - fontdescFromPangoFontDescription(pdesc, desc); - // desc is transfer-full and thus is a copy + uiprivFontDescriptorFromPangoFontDescription(pdesc, desc); + // pdesc is transfer-full and thus is a copy pango_font_description_free(pdesc); } diff --git a/unix/fontmatch.c b/unix/fontmatch.c index 05523c24..c55c77a2 100644 --- a/unix/fontmatch.c +++ b/unix/fontmatch.c @@ -53,3 +53,24 @@ PangoFontDescription *uiprivFontDescriptorToPangoFontDescription(const uiFontDes pango_font_description_set_stretch(desc, uiprivStretchToPangoStretch(uidesc->Stretch)); return desc; } + +void uiprivFontDescriptorFromPangoFontDescription(PangoFontDescription *pdesc, uiDrawFontDescriptor *uidesc) +{ + PangoStyle pitalic; + PangoStretch pstretch; + + uidesc->Family = uiUnixStrdupText(pango_font_description_get_family(pdesc)); + pitalic = pango_font_description_get_style(pdesc); + // TODO reverse the above misalignment if it is corrected + uidesc->Weight = pango_font_description_get_weight(pdesc); + pstretch = pango_font_description_get_stretch(pdesc); + // absolute size does not matter because, as above, 1 device unit == 1 cairo point + uidesc->Size = pango_units_to_double(pango_font_description_get_size(pdesc)); + + for (uidesc->Italic = uiDrawTextItalicNormal; uidesc->Italic < uiDrawTextItalicItalic; uidesc->Italic++) + if (pangoItalics[uidesc->Italic] == pitalic) + break; + for (uidesc->Stretch = uiDrawTextStretchUltraCondensed; uidesc->Stretch < uiDrawTextStretchUltraExpanded; uidesc->Stretch++) + if (pangoStretches[uidesc->Stretch] == pstretch) + break; +} From 602060a673e6c34a3f31951cb6281071c20b643a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 11 Mar 2018 19:59:11 -0400 Subject: [PATCH 0912/1329] Fixed build errors, some of which were overisghts and others were habits from the OS X code. --- unix/attrstr.c | 13 ------------- unix/attrstr.h | 4 ++-- unix/drawtext.c | 12 ++++++------ unix/fontbutton.c | 2 +- unix/fontmatch.c | 8 ++++---- unix/uipriv_unix.h | 3 --- 6 files changed, 13 insertions(+), 29 deletions(-) diff --git a/unix/attrstr.c b/unix/attrstr.c index 0fc9de89..fe554c48 100644 --- a/unix/attrstr.c +++ b/unix/attrstr.c @@ -11,19 +11,6 @@ struct foreachParams { GPtrArray *backgroundParams; }; -static void backgroundClosure(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y, gpointer data) -{ - struct closureParams *p = (struct closureParams *) data; - uiDrawBrush brush; - - brush.Type = uiDrawBrushTypeSolid; - brush.R = p->r; - brush.G = p->g; - brush.B = p->b; - brush.A = p->a; -//TODO drawTextBackground(c, x, y, layout, p->start, p->end, &brush, 0); -} - static void addBackgroundAttribute(struct foreachParams *p, size_t start, size_t end, double r, double g, double b, double a) { uiprivDrawTextBackgroundParams *dtb; diff --git a/unix/attrstr.h b/unix/attrstr.h index 1988fe86..5dff27b3 100644 --- a/unix/attrstr.h +++ b/unix/attrstr.h @@ -1,5 +1,5 @@ // 11 march 2018 -#import "../common/attrstr.h" +#include "../common/attrstr.h" // See https://developer.gnome.org/pango/1.30/pango-Cairo-Rendering.html#pango-Cairo-Rendering.description // For the conversion, see https://developer.gnome.org/pango/1.30/pango-Glyph-Storage.html#pango-units-to-double and https://developer.gnome.org/pango/1.30/pango-Glyph-Storage.html#pango-units-from-double @@ -14,7 +14,7 @@ extern PangoWeight uiprivWeightToPangoWeight(uiTextWeight w); extern PangoStyle uiprivItalicToPangoStyle(uiTextItalic i); extern PangoStretch uiprivStretchToPangoStretch(uiTextStretch s); extern PangoFontDescription *uiprivFontDescriptorToPangoFontDescription(const uiFontDescriptor *uidesc); -extern void uiprivFontDescriptorFromPangoFontDescription(PangoFontDescription *pdesc, uiDrawFontDescriptor *uidesc); +extern void uiprivFontDescriptorFromPangoFontDescription(PangoFontDescription *pdesc, uiFontDescriptor *uidesc); // attrstr.c extern PangoAttrList *uiprivAttributedStringToPangoAttrList(uiDrawTextLayoutParams *p, GPtrArray **backgroundParams); diff --git a/unix/drawtext.c b/unix/drawtext.c index 47d4362d..b64a9999 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -1,7 +1,7 @@ // 11 march 2018 -#import "uipriv_unix.h" -#import "draw.h" -#import "attrstr.h" +#include "uipriv_unix.h" +#include "draw.h" +#include "attrstr.h" struct uiDrawTextLayout { PangoLayout *layout; @@ -51,7 +51,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) pango_layout_set_alignment(tl->layout, pangoAligns[p->Align]); - attrs = uiprivAttributedStringToPangoAttrList(p, &(tl->backgroundFeatures)); + attrs = uiprivAttributedStringToPangoAttrList(p, &(tl->backgroundParams)); pango_layout_set_attributes(tl->layout, attrs); pango_attr_list_unref(attrs); @@ -60,7 +60,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { - g_ptr_array_unref(tl->backgroundFeatures); + g_ptr_array_unref(tl->backgroundParams); g_object_unref(tl->layout); uiprivFree(tl); } @@ -69,7 +69,7 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) { guint i; - for (i = 0; i < tl->backgroundFeatures->len; i++) { + for (i = 0; i < tl->backgroundParams->len; i++) { // TODO } // TODO have an implicit save/restore on each drawing functions instead? and is this correct? diff --git a/unix/fontbutton.c b/unix/fontbutton.c index 3b4a0425..b19cd310 100644 --- a/unix/fontbutton.c +++ b/unix/fontbutton.c @@ -27,7 +27,7 @@ static void defaultOnChanged(uiFontButton *b, void *data) // do nothing } -void uiFontButtonFont(uiFontButton *b, uiDrawFontDescriptor *desc) +void uiFontButtonFont(uiFontButton *b, uiFontDescriptor *desc) { PangoFontDescription *pdesc; diff --git a/unix/fontmatch.c b/unix/fontmatch.c index c55c77a2..95f4fdd7 100644 --- a/unix/fontmatch.c +++ b/unix/fontmatch.c @@ -42,7 +42,7 @@ PangoStretch uiprivStretchToPangoStretch(uiTextStretch s) PangoFontDescription *uiprivFontDescriptorToPangoFontDescription(const uiFontDescriptor *uidesc) { - PangoFontDescriptor *desc; + PangoFontDescription *desc; desc = pango_font_description_new(); pango_font_description_set_family(desc, uidesc->Family); @@ -54,7 +54,7 @@ PangoFontDescription *uiprivFontDescriptorToPangoFontDescription(const uiFontDes return desc; } -void uiprivFontDescriptorFromPangoFontDescription(PangoFontDescription *pdesc, uiDrawFontDescriptor *uidesc) +void uiprivFontDescriptorFromPangoFontDescription(PangoFontDescription *pdesc, uiFontDescriptor *uidesc) { PangoStyle pitalic; PangoStretch pstretch; @@ -67,10 +67,10 @@ void uiprivFontDescriptorFromPangoFontDescription(PangoFontDescription *pdesc, u // absolute size does not matter because, as above, 1 device unit == 1 cairo point uidesc->Size = pango_units_to_double(pango_font_description_get_size(pdesc)); - for (uidesc->Italic = uiDrawTextItalicNormal; uidesc->Italic < uiDrawTextItalicItalic; uidesc->Italic++) + for (uidesc->Italic = uiTextItalicNormal; uidesc->Italic < uiTextItalicItalic; uidesc->Italic++) if (pangoItalics[uidesc->Italic] == pitalic) break; - for (uidesc->Stretch = uiDrawTextStretchUltraCondensed; uidesc->Stretch < uiDrawTextStretchUltraExpanded; uidesc->Stretch++) + for (uidesc->Stretch = uiTextStretchUltraCondensed; uidesc->Stretch < uiTextStretchUltraExpanded; uidesc->Stretch++) if (pangoStretches[uidesc->Stretch] == pstretch) break; } diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index 531e1c05..debcb0b2 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -58,6 +58,3 @@ extern void loadFutures(void); extern PangoAttribute *FUTURE_pango_attr_font_features_new(const gchar *features); extern PangoAttribute *FUTURE_pango_attr_foreground_alpha_new(guint16 alpha); extern gboolean FUTURE_gtk_widget_path_iter_set_object_name(GtkWidgetPath *path, gint pos, const char *name); - -// drawtext.c -extern void fontdescFromPangoFontDescription(PangoFontDescription *pdesc, uiDrawFontDescriptor *uidesc); From bffe311afe126d335b3efe6fdbfdc95dde9eb079 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 11 Mar 2018 20:23:18 -0400 Subject: [PATCH 0913/1329] Switched to using Pango background color attributes. Unix code done for now. --- darwin/attrstr.h | 1 + unix/attrstr.c | 35 ++++++++++------------------------- unix/attrstr.h | 14 +------------- unix/drawtext.c | 9 +-------- unix/future.c | 9 +++++++++ unix/uipriv_unix.h | 1 + 6 files changed, 23 insertions(+), 46 deletions(-) diff --git a/darwin/attrstr.h b/darwin/attrstr.h index f77b4d86..02a3418d 100644 --- a/darwin/attrstr.h +++ b/darwin/attrstr.h @@ -77,6 +77,7 @@ extern void uiprivUninitUnderlineColors(void); extern CFAttributedStringRef uiprivAttributedStringToCFAttributedString(uiDrawTextLayoutParams *p, NSArray **backgroundParams); // drawtext.m +// TODO figure out where this type should *really* go in all the headers... @interface uiprivDrawTextBackgroundParams : NSObject { size_t start; size_t end; diff --git a/unix/attrstr.c b/unix/attrstr.c index fe554c48..a378e452 100644 --- a/unix/attrstr.c +++ b/unix/attrstr.c @@ -7,24 +7,8 @@ // TODO make this name less generic? struct foreachParams { PangoAttrList *attrs; - // TODO use pango's built-in background attribute - GPtrArray *backgroundParams; }; -static void addBackgroundAttribute(struct foreachParams *p, size_t start, size_t end, double r, double g, double b, double a) -{ - uiprivDrawTextBackgroundParams *dtb; - - dtb = uiprivNew(uiprivDrawTextBackgroundParams); - dtb->start = start; - dtb->end = end; - dtb->r = r; - dtb->g = g; - dtb->b = b; - dtb->a = a; - g_ptr_array_add(p->backgroundParams, dtb); -} - static void addattr(struct foreachParams *p, size_t start, size_t end, PangoAttribute *attr) { if (attr == NULL) // in case of a future attribute @@ -77,8 +61,16 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute (guint16) (a * 65535.0))); break; case uiAttributeTypeBackground: + // TODO make sure this works properly with line paragraph spacings (after figuring out what that means, of course) uiAttributeColor(attr, &r, &g, &b, &a); - addBackgroundAttribute(p, start, end, r, g, b, a); + addattr(p, start, end, + pango_attr_background_new( + (guint16) (r * 65535.0), + (guint16) (g * 65535.0), + (guint16) (b * 65535.0))); + addattr(p, start, end, + FUTURE_pango_attr_background_alpha_new( + (guint16) (a * 65535.0))); break; case uiAttributeTypeUnderline: switch (uiAttributeUnderline(attr)) { @@ -143,18 +135,11 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute return uiForEachContinue; } -static void freeBackgroundParams(gpointer data) -{ - uiprivFree((uiprivDrawTextBackgroundParams *) data); -} - -PangoAttrList *uiprivAttributedStringToPangoAttrList(uiDrawTextLayoutParams *p, GPtrArray **backgroundParams) +PangoAttrList *uiprivAttributedStringToPangoAttrList(uiDrawTextLayoutParams *p) { struct foreachParams fep; fep.attrs = pango_attr_list_new(); - fep.backgroundParams = g_ptr_array_new_with_free_func(freeBackgroundParams); uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); - *backgroundParams = fep.backgroundParams; return fep.attrs; } diff --git a/unix/attrstr.h b/unix/attrstr.h index 5dff27b3..984ac1f5 100644 --- a/unix/attrstr.h +++ b/unix/attrstr.h @@ -17,16 +17,4 @@ extern PangoFontDescription *uiprivFontDescriptorToPangoFontDescription(const ui extern void uiprivFontDescriptorFromPangoFontDescription(PangoFontDescription *pdesc, uiFontDescriptor *uidesc); // attrstr.c -extern PangoAttrList *uiprivAttributedStringToPangoAttrList(uiDrawTextLayoutParams *p, GPtrArray **backgroundParams); - -// drawtext.c -// TODO figure out where this type should *really* go in all the headers... -typedef struct uiprivDrawTextBackgroundParams uiprivDrawTextBackgroundParams; -struct uiprivDrawTextBackgroundParams { - size_t start; - size_t end; - double r; - double g; - double b; - double a; -}; +extern PangoAttrList *uiprivAttributedStringToPangoAttrList(uiDrawTextLayoutParams *p); diff --git a/unix/drawtext.c b/unix/drawtext.c index b64a9999..5792f9e0 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -5,7 +5,6 @@ struct uiDrawTextLayout { PangoLayout *layout; - GPtrArray *backgroundParams; }; // we need a context for a few things @@ -51,7 +50,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) pango_layout_set_alignment(tl->layout, pangoAligns[p->Align]); - attrs = uiprivAttributedStringToPangoAttrList(p, &(tl->backgroundParams)); + attrs = uiprivAttributedStringToPangoAttrList(p); pango_layout_set_attributes(tl->layout, attrs); pango_attr_list_unref(attrs); @@ -60,18 +59,12 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { - g_ptr_array_unref(tl->backgroundParams); g_object_unref(tl->layout); uiprivFree(tl); } void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) { - guint i; - - for (i = 0; i < tl->backgroundParams->len; i++) { - // TODO - } // TODO have an implicit save/restore on each drawing functions instead? and is this correct? cairo_set_source_rgb(c->cr, 0.0, 0.0, 0.0); cairo_move_to(c->cr, x, y); diff --git a/unix/future.c b/unix/future.c index 3d63e9e0..68730ead 100644 --- a/unix/future.c +++ b/unix/future.c @@ -8,6 +8,7 @@ // added in pango 1.38; we need 1.36 static PangoAttribute *(*newFeaturesAttr)(const gchar *features) = NULL; static PangoAttribute *(*newFGAlphaAttr)(guint16 alpha) = NULL; +static PangoAttribute *(*newBGAlphaAttr)(guint16 alpha) = NULL; // added in GTK+ 3.20; we need 3.10 static void (*gwpIterSetObjectName)(GtkWidgetPath *path, gint pos, const char *name) = NULL; @@ -24,6 +25,7 @@ void loadFutures(void) #define GET(var, fn) *((void **) (&var)) = dlsym(handle, #fn) GET(newFeaturesAttr, pango_attr_font_features_new); GET(newFGAlphaAttr, pango_attr_foreground_alpha_new); + GET(newBGAlphaAttr, pango_attr_background_alpha_new); GET(gwpIterSetObjectName, gtk_widget_path_iter_set_object_name); dlclose(handle); } @@ -42,6 +44,13 @@ PangoAttribute *FUTURE_pango_attr_foreground_alpha_new(guint16 alpha) return (*newFGAlphaAttr)(alpha); } +PangoAttribute *FUTURE_pango_attr_background_alpha_new(guint16 alpha) +{ + if (newBGAlphaAttr == NULL) + return NULL; + return (*newBGAlphaAttr)(alpha); +} + gboolean FUTURE_gtk_widget_path_iter_set_object_name(GtkWidgetPath *path, gint pos, const char *name) { if (gwpIterSetObjectName == NULL) diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index debcb0b2..43ed144b 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -57,4 +57,5 @@ extern GtkCellRenderer *newCellRendererButton(void); extern void loadFutures(void); extern PangoAttribute *FUTURE_pango_attr_font_features_new(const gchar *features); extern PangoAttribute *FUTURE_pango_attr_foreground_alpha_new(guint16 alpha); +extern PangoAttribute *FUTURE_pango_attr_background_alpha_new(guint16 alpha); extern gboolean FUTURE_gtk_widget_path_iter_set_object_name(GtkWidgetPath *path, gint pos, const char *name); From e020ba465addecf8f19e490242e3290a34ff6219 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 11 Mar 2018 21:04:38 -0400 Subject: [PATCH 0914/1329] Moved the old Windows text code out of the way. --- ...old_drawtext.cpp => OLD__old_drawtext.cpp} | 0 windows/{attrstr.cpp => OLD_attrstr.cpp} | 0 windows/{drawtext.cpp => OLD_drawtext.cpp} | 0 windows/{dwrite.cpp => OLD_dwrite.cpp} | 0 .../{fontbutton.cpp => OLD_fontbutton.cpp} | 0 .../{fontdialog.cpp => OLD_fontdialog.cpp} | 0 windows/{graphemes.cpp => OLD_graphemes.cpp} | 0 windows/{opentype.cpp => OLD_opentype.cpp} | 0 windows/OLD_uipriv_attrstr.h | 142 ++++++++++++++++++ windows/_uipriv_migrate.hpp | 26 ---- windows/draw.hpp | 111 -------------- windows/uipriv_windows.hpp | 9 -- 12 files changed, 142 insertions(+), 146 deletions(-) rename windows/{_old_drawtext.cpp => OLD__old_drawtext.cpp} (100%) rename windows/{attrstr.cpp => OLD_attrstr.cpp} (100%) rename windows/{drawtext.cpp => OLD_drawtext.cpp} (100%) rename windows/{dwrite.cpp => OLD_dwrite.cpp} (100%) rename windows/{fontbutton.cpp => OLD_fontbutton.cpp} (100%) rename windows/{fontdialog.cpp => OLD_fontdialog.cpp} (100%) rename windows/{graphemes.cpp => OLD_graphemes.cpp} (100%) rename windows/{opentype.cpp => OLD_opentype.cpp} (100%) create mode 100644 windows/OLD_uipriv_attrstr.h diff --git a/windows/_old_drawtext.cpp b/windows/OLD__old_drawtext.cpp similarity index 100% rename from windows/_old_drawtext.cpp rename to windows/OLD__old_drawtext.cpp diff --git a/windows/attrstr.cpp b/windows/OLD_attrstr.cpp similarity index 100% rename from windows/attrstr.cpp rename to windows/OLD_attrstr.cpp diff --git a/windows/drawtext.cpp b/windows/OLD_drawtext.cpp similarity index 100% rename from windows/drawtext.cpp rename to windows/OLD_drawtext.cpp diff --git a/windows/dwrite.cpp b/windows/OLD_dwrite.cpp similarity index 100% rename from windows/dwrite.cpp rename to windows/OLD_dwrite.cpp diff --git a/windows/fontbutton.cpp b/windows/OLD_fontbutton.cpp similarity index 100% rename from windows/fontbutton.cpp rename to windows/OLD_fontbutton.cpp diff --git a/windows/fontdialog.cpp b/windows/OLD_fontdialog.cpp similarity index 100% rename from windows/fontdialog.cpp rename to windows/OLD_fontdialog.cpp diff --git a/windows/graphemes.cpp b/windows/OLD_graphemes.cpp similarity index 100% rename from windows/graphemes.cpp rename to windows/OLD_graphemes.cpp diff --git a/windows/opentype.cpp b/windows/OLD_opentype.cpp similarity index 100% rename from windows/opentype.cpp rename to windows/OLD_opentype.cpp diff --git a/windows/OLD_uipriv_attrstr.h b/windows/OLD_uipriv_attrstr.h new file mode 100644 index 00000000..1ef2b71f --- /dev/null +++ b/windows/OLD_uipriv_attrstr.h @@ -0,0 +1,142 @@ +// dwrite.cpp +extern IDWriteFactory *dwfactory; +extern HRESULT initDrawText(void); +extern void uninitDrawText(void); +struct fontCollection { + IDWriteFontCollection *fonts; + WCHAR userLocale[LOCALE_NAME_MAX_LENGTH]; + int userLocaleSuccess; +}; +extern fontCollection *loadFontCollection(void); +extern WCHAR *fontCollectionFamilyName(fontCollection *fc, IDWriteFontFamily *family); +extern void fontCollectionFree(fontCollection *fc); +extern WCHAR *fontCollectionCorrectString(fontCollection *fc, IDWriteLocalizedStrings *names); + +// fontdialog.cpp +struct fontDialogParams { + IDWriteFont *font; + double size; + WCHAR *familyName; + WCHAR *styleName; +}; +extern BOOL showFontDialog(HWND parent, struct fontDialogParams *params); +extern void loadInitialFontDialogParams(struct fontDialogParams *params); +extern void destroyFontDialogParams(struct fontDialogParams *params); +extern WCHAR *fontDialogParamsToString(struct fontDialogParams *params); + +// attrstr.cpp +typedef std::function backgroundFunc; +extern void attrstrToIDWriteTextLayoutAttrs(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout, std::vector **backgroundFuncs); + +// drawtext.cpp +// TODO reconcile this with attrstr.cpp +class textDrawingEffect : public IUnknown { + ULONG refcount; +public: + bool hasColor; + double r; + double g; + double b; + double a; + + bool hasUnderline; + uiDrawUnderlineStyle u; + + bool hasUnderlineColor; + double ur; + double ug; + double ub; + double ua; + + textDrawingEffect() + { + this->refcount = 1; + this->hasColor = false; + this->hasUnderline = false; + this->hasUnderlineColor = false; + } + + // IUnknown + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) + { + if (ppvObject == NULL) + return E_POINTER; + if (riid == IID_IUnknown) { + this->AddRef(); + *ppvObject = this; + return S_OK; + } + *ppvObject = NULL; + return E_NOINTERFACE; + } + + virtual ULONG STDMETHODCALLTYPE AddRef(void) + { + this->refcount++; + return this->refcount; + } + + virtual ULONG STDMETHODCALLTYPE Release(void) + { + this->refcount--; + if (this->refcount == 0) { + delete this; + return 0; + } + return this->refcount; + } + + // TODO deduplicate this with common/attrlist.c + bool same(textDrawingEffect *b) + { + static auto boolsDiffer = [](bool a, bool b) -> bool { + if (a && b) + return false; + if (!a && !b) + return false; + return true; + }; + + if (boolsDiffer(this->hasColor, b->hasColor)) + return false; + if (this->hasColor) { + // TODO use a closest match? + if (this->r != b->r) + return false; + if (this->g != b->g) + return false; + if (this->b != b->b) + return false; + if (this->a != b->a) + return false; + } + if (boolsDiffer(this->hasUnderline, b->hasUnderline)) + return false; + if (this->hasUnderline) + if (this->u != b->u) + return false; + if (boolsDiffer(this->hasUnderlineColor, b->hasUnderlineColor)) + return false; + if (this->hasUnderlineColor) { + // TODO use a closest match? + if (this->ur != b->ur) + return false; + if (this->ug != b->ug) + return false; + if (this->ub != b->ub) + return false; + if (this->ua != b->ua) + return false; + } + return true; + } +}; +// TODO these should not be exported +extern std::map dwriteItalics; +extern std::map dwriteStretches; + +// drawtext.cpp +extern void fontdescFromIDWriteFont(IDWriteFont *font, uiDrawFontDescriptor *uidesc); + +// opentype.cpp +extern IDWriteTypography *otfToDirectWrite(const uiOpenTypeFeatures *otf); diff --git a/windows/_uipriv_migrate.hpp b/windows/_uipriv_migrate.hpp index b9c365cc..96a67089 100644 --- a/windows/_uipriv_migrate.hpp +++ b/windows/_uipriv_migrate.hpp @@ -12,29 +12,3 @@ extern void uninitDraw(void); extern ID2D1HwndRenderTarget *makeHWNDRenderTarget(HWND hwnd); extern uiDrawContext *newContext(ID2D1RenderTarget *); extern void freeContext(uiDrawContext *); - -// dwrite.cpp -extern IDWriteFactory *dwfactory; -extern HRESULT initDrawText(void); -extern void uninitDrawText(void); -struct fontCollection { - IDWriteFontCollection *fonts; - WCHAR userLocale[LOCALE_NAME_MAX_LENGTH]; - int userLocaleSuccess; -}; -extern fontCollection *loadFontCollection(void); -extern WCHAR *fontCollectionFamilyName(fontCollection *fc, IDWriteFontFamily *family); -extern void fontCollectionFree(fontCollection *fc); -extern WCHAR *fontCollectionCorrectString(fontCollection *fc, IDWriteLocalizedStrings *names); - -// fontdialog.cpp -struct fontDialogParams { - IDWriteFont *font; - double size; - WCHAR *familyName; - WCHAR *styleName; -}; -extern BOOL showFontDialog(HWND parent, struct fontDialogParams *params); -extern void loadInitialFontDialogParams(struct fontDialogParams *params); -extern void destroyFontDialogParams(struct fontDialogParams *params); -extern WCHAR *fontDialogParamsToString(struct fontDialogParams *params); diff --git a/windows/draw.hpp b/windows/draw.hpp index 8cfbbf72..c271e4db 100644 --- a/windows/draw.hpp +++ b/windows/draw.hpp @@ -16,114 +16,3 @@ extern ID2D1PathGeometry *pathGeometry(uiDrawPath *p); // drawmatrix.cpp extern void m2d(uiDrawMatrix *m, D2D1_MATRIX_3X2_F *d); - -// attrstr.cpp -typedef std::function backgroundFunc; -extern void attrstrToIDWriteTextLayoutAttrs(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout, std::vector **backgroundFuncs); - -// drawtext.cpp -// TODO reconcile this with attrstr.cpp -class textDrawingEffect : public IUnknown { - ULONG refcount; -public: - bool hasColor; - double r; - double g; - double b; - double a; - - bool hasUnderline; - uiDrawUnderlineStyle u; - - bool hasUnderlineColor; - double ur; - double ug; - double ub; - double ua; - - textDrawingEffect() - { - this->refcount = 1; - this->hasColor = false; - this->hasUnderline = false; - this->hasUnderlineColor = false; - } - - // IUnknown - virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) - { - if (ppvObject == NULL) - return E_POINTER; - if (riid == IID_IUnknown) { - this->AddRef(); - *ppvObject = this; - return S_OK; - } - *ppvObject = NULL; - return E_NOINTERFACE; - } - - virtual ULONG STDMETHODCALLTYPE AddRef(void) - { - this->refcount++; - return this->refcount; - } - - virtual ULONG STDMETHODCALLTYPE Release(void) - { - this->refcount--; - if (this->refcount == 0) { - delete this; - return 0; - } - return this->refcount; - } - - // TODO deduplicate this with common/attrlist.c - bool same(textDrawingEffect *b) - { - static auto boolsDiffer = [](bool a, bool b) -> bool { - if (a && b) - return false; - if (!a && !b) - return false; - return true; - }; - - if (boolsDiffer(this->hasColor, b->hasColor)) - return false; - if (this->hasColor) { - // TODO use a closest match? - if (this->r != b->r) - return false; - if (this->g != b->g) - return false; - if (this->b != b->b) - return false; - if (this->a != b->a) - return false; - } - if (boolsDiffer(this->hasUnderline, b->hasUnderline)) - return false; - if (this->hasUnderline) - if (this->u != b->u) - return false; - if (boolsDiffer(this->hasUnderlineColor, b->hasUnderlineColor)) - return false; - if (this->hasUnderlineColor) { - // TODO use a closest match? - if (this->ur != b->ur) - return false; - if (this->ug != b->ug) - return false; - if (this->ub != b->ub) - return false; - if (this->ua != b->ua) - return false; - } - return true; - } -}; -// TODO these should not be exported -extern std::map dwriteItalics; -extern std::map dwriteStretches; diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index 3f199cbc..3244b7b4 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -152,17 +152,8 @@ extern void getSizing(HWND hwnd, uiWindowsSizing *sizing, HFONT font); // TODO move into a dedicated file abibugs.cpp when we rewrite the drawing code extern D2D1_SIZE_F realGetSize(ID2D1RenderTarget *rt); - - - // TODO #include "_uipriv_migrate.hpp" // draw.cpp extern ID2D1DCRenderTarget *makeHDCRenderTarget(HDC dc, RECT *r); - -// drawtext.cpp -extern void fontdescFromIDWriteFont(IDWriteFont *font, uiDrawFontDescriptor *uidesc); - -// opentype.cpp -extern IDWriteTypography *otfToDirectWrite(const uiOpenTypeFeatures *otf); From 6c95ce849ae232b543df826073edab1a47ee3f0b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 11 Mar 2018 21:17:39 -0400 Subject: [PATCH 0915/1329] Migrated graphemes.cpp and opentype.cpp back. --- windows/OLD_opentype.cpp | 110 ------------------- windows/attrstr.hpp | 7 ++ windows/{OLD_graphemes.cpp => graphemes.cpp} | 5 +- windows/opentype.cpp | 32 ++++++ 4 files changed, 42 insertions(+), 112 deletions(-) delete mode 100644 windows/OLD_opentype.cpp create mode 100644 windows/attrstr.hpp rename windows/{OLD_graphemes.cpp => graphemes.cpp} (93%) create mode 100644 windows/opentype.cpp diff --git a/windows/OLD_opentype.cpp b/windows/OLD_opentype.cpp deleted file mode 100644 index 235f0388..00000000 --- a/windows/OLD_opentype.cpp +++ /dev/null @@ -1,110 +0,0 @@ -// 11 may 2017 -#include "uipriv_windows.hpp" - -typedef std::map tagmap; - -struct uiOpenTypeFeatures { - tagmap *tags; -}; - -uiOpenTypeFeatures *uiNewOpenTypeFeatures(void) -{ - uiOpenTypeFeatures *otf; - - otf = uiNew(uiOpenTypeFeatures); - otf->tags = new tagmap; - return otf; -} - -void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf) -{ - delete otf->tags; - uiFree(otf); -} - -uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures *otf) -{ - uiOpenTypeFeatures *out; - - out = uiNew(uiOpenTypeFeatures); - out->tags = new tagmap; - *(out->tags) = *(otf->tags); - return out; -} - -#define mktag(a, b, c, d) ((uint32_t) DWRITE_MAKE_OPENTYPE_TAG(a, b, c, d)) - -void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value) -{ - (*(otf->tags))[mktag(a, b, c, d)] = value; -} - -void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d) -{ - // this will just return 0 if nothing was removed (if I'm reading the help pages I've found correctly) - otf->tags->erase(mktag(a, b, c, d)); -} - -// TODO will the const wreck stuff up? -int uiOpenTypeFeaturesGet(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value) -{ - tagmap::const_iterator iter; - - iter = otf->tags->find(mktag(a, b, c, d)); - if (iter == otf->tags->end()) - return 0; - *value = iter->second; - return 1; -} - -void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data) -{ - tagmap::const_iterator iter, end; - - end = otf->tags->end(); - for (iter = otf->tags->begin(); iter != end; iter++) { - uint8_t a, b, c, d; - uiForEach ret; - - a = (uint8_t) (iter->first & 0xFF); - b = (uint8_t) ((iter->first >> 8) & 0xFF); - c = (uint8_t) ((iter->first >> 16) & 0xFF); - d = (uint8_t) ((iter->first >> 24) & 0xFF); - ret = (*f)(otf, (char) a, (char) b, (char) c, (char) d, - iter->second, data); - if (ret == uiForEachStop) - return; - } -} - -int uiOpenTypeFeaturesEqual(const uiOpenTypeFeatures *a, const uiOpenTypeFeatures *b) -{ - if (a == NULL && b == NULL) - return 1; - if (a == NULL || b == NULL) - return 0; - // TODO make sure this is correct - return *(a->tags) == *(b->tags); -} - -IDWriteTypography *otfToDirectWrite(const uiOpenTypeFeatures *otf) -{ - IDWriteTypography *dt; - tagmap::const_iterator iter, end; - DWRITE_FONT_FEATURE dff; - HRESULT hr; - - hr = dwfactory->CreateTypography(&dt); - if (hr != S_OK) - logHRESULT(L"error creating IDWriteTypography", hr); - end = otf->tags->end(); - for (iter = otf->tags->begin(); iter != end; iter++) { - ZeroMemory(&dff, sizeof (DWRITE_FONT_FEATURE)); - dff.nameTag = (DWRITE_FONT_FEATURE_TAG) (iter->first); - dff.parameter = (UINT32) (iter->second); - hr = dt->AddFontFeature(dff); - if (hr != S_OK) - logHRESULT(L"error adding OpenType feature to IDWriteTypography", hr); - } - return dt; -} diff --git a/windows/attrstr.hpp b/windows/attrstr.hpp new file mode 100644 index 00000000..0b1d9031 --- /dev/null +++ b/windows/attrstr.hpp @@ -0,0 +1,7 @@ +// 11 march 2018 +extern "C" { +#include "../common/attrstr.h" +} + +// opentype.cpp +extern IDWriteTypography *uiprivOpenTypeFeaturesToIDWriteTypography(const uiOpenTypeFeatures *otf); diff --git a/windows/OLD_graphemes.cpp b/windows/graphemes.cpp similarity index 93% rename from windows/OLD_graphemes.cpp rename to windows/graphemes.cpp index 256c3a07..63e4882f 100644 --- a/windows/OLD_graphemes.cpp +++ b/windows/graphemes.cpp @@ -1,16 +1,17 @@ // 25 may 2016 #include "uipriv_windows.hpp" +#include "attrstr.hpp" // We could use CharNextW() to generate grapheme cluster boundaries, but it doesn't handle surrogate pairs properly (see http://archives.miloush.net/michkap/archive/2008/12/16/9223301.html). // We could also use Uniscribe (see http://archives.miloush.net/michkap/archive/2005/01/14/352802.html, http://www.catch22.net/tuts/uniscribe-mysteries, http://www.catch22.net/tuts/keyboard-navigation, and https://maxradi.us/documents/uniscribe/), but its rules for buffer sizes is convoluted. // Let's just deal with the CharNextW() bug. -int graphemesTakesUTF16(void) +int uiprivGraphemesTakesUTF16(void) { return 1; } -struct graphemes *graphemes(void *s, size_t len) +struct graphemes *uiprivNewGraphemes(void *s, size_t len) { struct graphemes *g; WCHAR *str; diff --git a/windows/opentype.cpp b/windows/opentype.cpp new file mode 100644 index 00000000..0a96cdc4 --- /dev/null +++ b/windows/opentype.cpp @@ -0,0 +1,32 @@ +// 11 may 2017 +#include "uipriv_windows.hpp" +#include "attrstr.hpp" + +// TODO pull out my decision for empty uiOpenTypeFeatures, assuming that it isn't in another file or that I even made one + +static uiForEach addToTypography(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data) +{ + IDWriteTypography *dt = (IDWriteTypography *) data; + DWRITE_FONT_FEATURE dff; + HRESULT hr; + + ZeroMemory(&dff, sizeof (DWRITE_FONT_FEATURE)); + dff.nameTag = /*(DWRITE_FONT_FEATURE_TAG)*/ DWRITE_MAKE_OPENTYPE_TAG(a, b, c, d); + dff.parameter = (UINT32) value; + hr = dt->AddFontFeature(dff); + if (hr != S_OK) + logHRESULT(L"error adding OpenType feature to IDWriteTypography", hr); + return uiForEachContinue; +} + +IDWriteTypography *uiprivOpenTypeFeaturesToIDWriteTypography(const uiOpenTypeFeatures *otf) +{ + IDWriteTypography *dt; + HRESULT hr; + + hr = dwfactory->CreateTypography(&dt); + if (hr != S_OK) + logHRESULT(L"error creating IDWriteTypography", hr); + uiOpenTypeFeaturesForEach(otf, addToTypography, dt); + return dt; +} From 1f61fb30de59f362f65af3f9545c9861a168358d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 11 Mar 2018 22:11:19 -0400 Subject: [PATCH 0916/1329] Wrote a fontmatch.cpp. --- windows/OLD_drawtext.cpp | 31 +++-------------------------- windows/attrstr.hpp | 5 +++++ windows/fontmatch.cpp | 43 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 28 deletions(-) create mode 100644 windows/fontmatch.cpp diff --git a/windows/OLD_drawtext.cpp b/windows/OLD_drawtext.cpp index 84a8874e..0db8fc9b 100644 --- a/windows/OLD_drawtext.cpp +++ b/windows/OLD_drawtext.cpp @@ -30,26 +30,6 @@ struct uiDrawTextLayout { // fortunately Microsoft does this too, in https://msdn.microsoft.com/en-us/library/windows/desktop/dd371554%28v=vs.85%29.aspx #define pointSizeToDWriteSize(size) (size * (96.0 / 72.0)) -// TODO should be const but then I can't operator[] on it; the real solution is to find a way to do designated array initializers in C++11 but I do not know enough C++ voodoo to make it work (it is possible but no one else has actually done it before) -std::map dwriteItalics = { - { uiDrawTextItalicNormal, DWRITE_FONT_STYLE_NORMAL }, - { uiDrawTextItalicOblique, DWRITE_FONT_STYLE_OBLIQUE }, - { uiDrawTextItalicItalic, DWRITE_FONT_STYLE_ITALIC }, -}; - -// TODO should be const but then I can't operator[] on it; the real solution is to find a way to do designated array initializers in C++11 but I do not know enough C++ voodoo to make it work (it is possible but no one else has actually done it before) -std::map dwriteStretches = { - { uiDrawTextStretchUltraCondensed, DWRITE_FONT_STRETCH_ULTRA_CONDENSED }, - { uiDrawTextStretchExtraCondensed, DWRITE_FONT_STRETCH_EXTRA_CONDENSED }, - { uiDrawTextStretchCondensed, DWRITE_FONT_STRETCH_CONDENSED }, - { uiDrawTextStretchSemiCondensed, DWRITE_FONT_STRETCH_SEMI_CONDENSED }, - { uiDrawTextStretchNormal, DWRITE_FONT_STRETCH_NORMAL }, - { uiDrawTextStretchSemiExpanded, DWRITE_FONT_STRETCH_SEMI_EXPANDED }, - { uiDrawTextStretchExpanded, DWRITE_FONT_STRETCH_EXPANDED }, - { uiDrawTextStretchExtraExpanded, DWRITE_FONT_STRETCH_EXTRA_EXPANDED }, - { uiDrawTextStretchUltraExpanded, DWRITE_FONT_STRETCH_ULTRA_EXPANDED }, -}; - struct lineInfo { size_t startPos; // in UTF-16 points size_t endPos; @@ -154,14 +134,9 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) wDefaultFamily = toUTF16(p->DefaultFont->Family); hr = dwfactory->CreateTextFormat( wDefaultFamily, NULL, - // for the most part, DirectWrite weights correlate to ours - // the differences: - // - Minimum — libui: 0, DirectWrite: 1 - // - Maximum — libui: 1000, DirectWrite: 999 - // TODO figure out what to do about this shorter range (the actual major values are the same (but with different names), so it's just a range issue) - (DWRITE_FONT_WEIGHT) (p->DefaultFont->Weight), - dwriteItalics[p->DefaultFont->Italic], - dwriteStretches[p->DefaultFont->Stretch], + uiprivWeightToDWriteWeight(p->DefaultFont->Weight), + uiprivItalicToDWriteStyle(p->DefaultFont->Italic), + uiprivStretchToDWriteStretch(p->DefaultFont->Stretch), pointSizeToDWriteSize(p->DefaultFont->Size), // see http://stackoverflow.com/questions/28397971/idwritefactorycreatetextformat-failing and https://msdn.microsoft.com/en-us/library/windows/desktop/dd368203.aspx // TODO use the current locale? diff --git a/windows/attrstr.hpp b/windows/attrstr.hpp index 0b1d9031..ca1226c5 100644 --- a/windows/attrstr.hpp +++ b/windows/attrstr.hpp @@ -5,3 +5,8 @@ extern "C" { // opentype.cpp extern IDWriteTypography *uiprivOpenTypeFeaturesToIDWriteTypography(const uiOpenTypeFeatures *otf); + +// fontmatch.cpp +extern DWRITE_FONT_WEIGHT uiprivWeightToDWriteWeight(uiTextWeight w); +extern DWRITE_FONT_STYLE uiprivItalicToDWriteStyle(uiTextItalic i); +extern DWRITE_FONT_STRETCH uiprivStretchToDWriteStretch(uiTextStretch s); diff --git a/windows/fontmatch.cpp b/windows/fontmatch.cpp new file mode 100644 index 00000000..05986dc1 --- /dev/null +++ b/windows/fontmatch.cpp @@ -0,0 +1,43 @@ +// 11 march 2018 +#include "uipriv_windows.hpp" +#include "attrstr.hpp" + +// TODO should be const but then I can't operator[] on it; the real solution is to find a way to do designated array initializers in C++11 but I do not know enough C++ voodoo to make it work (it is possible but no one else has actually done it before) +static std::map dwriteItalics = { + { uiTextItalicNormal, DWRITE_FONT_STYLE_NORMAL }, + { uiTextItalicOblique, DWRITE_FONT_STYLE_OBLIQUE }, + { uiTextItalicItalic, DWRITE_FONT_STYLE_ITALIC }, +}; + +// TODO should be const but then I can't operator[] on it; the real solution is to find a way to do designated array initializers in C++11 but I do not know enough C++ voodoo to make it work (it is possible but no one else has actually done it before) +static std::map dwriteStretches = { + { uiTextStretchUltraCondensed, DWRITE_FONT_STRETCH_ULTRA_CONDENSED }, + { uiTextStretchExtraCondensed, DWRITE_FONT_STRETCH_EXTRA_CONDENSED }, + { uiTextStretchCondensed, DWRITE_FONT_STRETCH_CONDENSED }, + { uiTextStretchSemiCondensed, DWRITE_FONT_STRETCH_SEMI_CONDENSED }, + { uiTextStretchNormal, DWRITE_FONT_STRETCH_NORMAL }, + { uiTextStretchSemiExpanded, DWRITE_FONT_STRETCH_SEMI_EXPANDED }, + { uiTextStretchExpanded, DWRITE_FONT_STRETCH_EXPANDED }, + { uiTextStretchExtraExpanded, DWRITE_FONT_STRETCH_EXTRA_EXPANDED }, + { uiTextStretchUltraExpanded, DWRITE_FONT_STRETCH_ULTRA_EXPANDED }, +}; + +// for the most part, DirectWrite weights correlate to ours +// the differences: +// - Minimum — libui: 0, DirectWrite: 1 +// - Maximum — libui: 1000, DirectWrite: 999 +// TODO figure out what to do about this shorter range (the actual major values are the same (but with different names), so it's just a range issue) +DWRITE_FONT_WEIGHT uiprivWeightToDWriteWeight(uiTextWeight w) +{ + return (DWRITE_FONT_WEIGHT) w; +} + +DWRITE_FONT_STYLE uiprivItalicToDWriteStyle(uiTextItalic i) +{ + return dwriteItalics[i]; +} + +DWRITE_FONT_STRETCH uiprivStretchToDWriteStretch(uiTextStretch s) +{ + return dwriteStretches[s]; +} From 86264d32a01f7ae3538646e6845c9f1c05e90732 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 11 Mar 2018 22:17:16 -0400 Subject: [PATCH 0917/1329] And migrated the IDWriteFont -> uiFontDescriptor code. --- windows/OLD_drawtext.cpp | 19 ------------------- windows/OLD_uipriv_attrstr.h | 9 --------- windows/attrstr.hpp | 1 + windows/fontmatch.cpp | 18 ++++++++++++++++++ 4 files changed, 19 insertions(+), 28 deletions(-) diff --git a/windows/OLD_drawtext.cpp b/windows/OLD_drawtext.cpp index 0db8fc9b..201d3a1d 100644 --- a/windows/OLD_drawtext.cpp +++ b/windows/OLD_drawtext.cpp @@ -665,22 +665,3 @@ void caretDrawParams(uiDrawContext *c, double height, struct caretDrawParams *p) // and there doesn't seem to be this either... (TODO check what PadWrite does?) p->xoff = 0; } - -// TODO split this and the above related font matching code into a separate file? -void fontdescFromIDWriteFont(IDWriteFont *font, uiDrawFontDescriptor *uidesc) -{ - DWRITE_FONT_STYLE dwitalic; - DWRITE_FONT_STRETCH dwstretch; - - dwitalic = font->GetStyle(); - // TODO reverse the above misalignment if it is corrected - uidesc->Weight = (uiDrawTextWeight) (font->GetWeight()); - dwstretch = font->GetStretch(); - - for (uidesc->Italic = uiDrawTextItalicNormal; uidesc->Italic < uiDrawTextItalicItalic; uidesc->Italic++) - if (dwriteItalics[uidesc->Italic] == dwitalic) - break; - for (uidesc->Stretch = uiDrawTextStretchUltraCondensed; uidesc->Stretch < uiDrawTextStretchUltraExpanded; uidesc->Stretch++) - if (dwriteStretches[uidesc->Stretch] == dwstretch) - break; -} diff --git a/windows/OLD_uipriv_attrstr.h b/windows/OLD_uipriv_attrstr.h index 1ef2b71f..360ec9e2 100644 --- a/windows/OLD_uipriv_attrstr.h +++ b/windows/OLD_uipriv_attrstr.h @@ -131,12 +131,3 @@ public: return true; } }; -// TODO these should not be exported -extern std::map dwriteItalics; -extern std::map dwriteStretches; - -// drawtext.cpp -extern void fontdescFromIDWriteFont(IDWriteFont *font, uiDrawFontDescriptor *uidesc); - -// opentype.cpp -extern IDWriteTypography *otfToDirectWrite(const uiOpenTypeFeatures *otf); diff --git a/windows/attrstr.hpp b/windows/attrstr.hpp index ca1226c5..56523aa5 100644 --- a/windows/attrstr.hpp +++ b/windows/attrstr.hpp @@ -10,3 +10,4 @@ extern IDWriteTypography *uiprivOpenTypeFeaturesToIDWriteTypography(const uiOpen extern DWRITE_FONT_WEIGHT uiprivWeightToDWriteWeight(uiTextWeight w); extern DWRITE_FONT_STYLE uiprivItalicToDWriteStyle(uiTextItalic i); extern DWRITE_FONT_STRETCH uiprivStretchToDWriteStretch(uiTextStretch s); +extern void uiprivFontDescriptorFromIDWriteFont(IDWriteFont *font, uiDrawFontDescriptor *uidesc); diff --git a/windows/fontmatch.cpp b/windows/fontmatch.cpp index 05986dc1..034b9907 100644 --- a/windows/fontmatch.cpp +++ b/windows/fontmatch.cpp @@ -41,3 +41,21 @@ DWRITE_FONT_STRETCH uiprivStretchToDWriteStretch(uiTextStretch s) { return dwriteStretches[s]; } + +void uiprivFontDescriptorFromIDWriteFont(IDWriteFont *font, uiDrawFontDescriptor *uidesc) +{ + DWRITE_FONT_STYLE dwitalic; + DWRITE_FONT_STRETCH dwstretch; + + dwitalic = font->GetStyle(); + // TODO reverse the above misalignment if it is corrected + uidesc->Weight = (uiDrawTextWeight) (font->GetWeight()); + dwstretch = font->GetStretch(); + + for (uidesc->Italic = uiDrawTextItalicNormal; uidesc->Italic < uiDrawTextItalicItalic; uidesc->Italic++) + if (dwriteItalics[uidesc->Italic] == dwitalic) + break; + for (uidesc->Stretch = uiDrawTextStretchUltraCondensed; uidesc->Stretch < uiDrawTextStretchUltraExpanded; uidesc->Stretch++) + if (dwriteStretches[uidesc->Stretch] == dwstretch) + break; +} From abc6fd282513ca4b4577736ce3d341a55e0dbaae Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 11 Mar 2018 22:36:31 -0400 Subject: [PATCH 0918/1329] uiDrawFontDescriptor -> uiFontDescriptor. --- windows/attrstr.hpp | 2 +- windows/fontmatch.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/windows/attrstr.hpp b/windows/attrstr.hpp index 56523aa5..074a8830 100644 --- a/windows/attrstr.hpp +++ b/windows/attrstr.hpp @@ -10,4 +10,4 @@ extern IDWriteTypography *uiprivOpenTypeFeaturesToIDWriteTypography(const uiOpen extern DWRITE_FONT_WEIGHT uiprivWeightToDWriteWeight(uiTextWeight w); extern DWRITE_FONT_STYLE uiprivItalicToDWriteStyle(uiTextItalic i); extern DWRITE_FONT_STRETCH uiprivStretchToDWriteStretch(uiTextStretch s); -extern void uiprivFontDescriptorFromIDWriteFont(IDWriteFont *font, uiDrawFontDescriptor *uidesc); +extern void uiprivFontDescriptorFromIDWriteFont(IDWriteFont *font, uiFontDescriptor *uidesc); diff --git a/windows/fontmatch.cpp b/windows/fontmatch.cpp index 034b9907..28b4c84a 100644 --- a/windows/fontmatch.cpp +++ b/windows/fontmatch.cpp @@ -42,7 +42,7 @@ DWRITE_FONT_STRETCH uiprivStretchToDWriteStretch(uiTextStretch s) return dwriteStretches[s]; } -void uiprivFontDescriptorFromIDWriteFont(IDWriteFont *font, uiDrawFontDescriptor *uidesc) +void uiprivFontDescriptorFromIDWriteFont(IDWriteFont *font, uiFontDescriptor *uidesc) { DWRITE_FONT_STYLE dwitalic; DWRITE_FONT_STRETCH dwstretch; From 12e97a1b29a6c2caf1e5b247844b8f393746b1ad Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 13 Mar 2018 18:43:32 -0400 Subject: [PATCH 0919/1329] Started migrating attrstr.cpp, using the same techniques as attrstr.m. --- darwin/attrstr.m | 2 +- windows/{OLD_attrstr.cpp => attrstr.cpp} | 235 ++++++++++++++++------- 2 files changed, 170 insertions(+), 67 deletions(-) rename windows/{OLD_attrstr.cpp => attrstr.cpp} (52%) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 180bf272..ca32216c 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -257,7 +257,7 @@ static void addFontAttributeToRange(struct foreachParams *p, size_t start, size_ else cfa = [cfa copy]; [cfa addAttribute:attr]; - // clamp effectiveRange within [start, end) + // clamp range within [start, end) if (range.location < start) { diff = start - range.location; range.location = start; diff --git a/windows/OLD_attrstr.cpp b/windows/attrstr.cpp similarity index 52% rename from windows/OLD_attrstr.cpp rename to windows/attrstr.cpp index c87804b5..92c39b9a 100644 --- a/windows/OLD_attrstr.cpp +++ b/windows/attrstr.cpp @@ -1,38 +1,147 @@ // 12 february 2017 #include "uipriv_windows.hpp" -#include "draw.hpp" +#include "attrstr.hpp" // TODO this whole file needs cleanup -// we need to combine color and underline style into one unit for IDWriteLayout::SetDrawingEffect() -// we also need to collect all the background blocks and add them all at once +// we need to collect all the background blocks and add them all at once // TODO contextual alternates override ligatures? // TODO rename this struct to something that isn't exclusively foreach-ing? struct foreachParams { const uint16_t *s; size_t len; IDWriteTextLayout *layout; - std::map *effects; std::vector *backgroundFuncs; }; -static void ensureEffectsInRange(struct foreachParams *p, size_t start, size_t end, std::function f) -{ - size_t i; - size_t *key; - textDrawingEffect *t; +// we need to combine color and underline style into one unit for IDWriteLayout::SetDrawingEffect() +// we also want to combine identical effects, which DirectWrite doesn't seem to provide a way to do +// we can at least try to goad it into doing so if we can deduplicate effects once they're all computed +// so what we do is use this class to store in-progress effects, much like uiprivCombinedFontAttr on the OS X code +// we then deduplicate them later while converting them into a form suitable for drawing with; see applyEffectsAttributes() below +class combinedEffectsAttr : public IUnknown { + ULONG refcount; + uiAttribute *colorAttr; + uiAttribute *underlineAttr; + uiAttribute *underlineColorAttr; - // TODO explain why we make one for every character - for (i = start; i < end; i++) { - t = (*(p->effects))[i]; - if (t != NULL) { - f(t); - continue; + void setAttribute(uiAttribute *a) + { + if (a == NULL) + return; + switch (uiAttributeGetType(a)) { + case uiAttributeTypeColor: + if (this->colorAttr != NULL) + uiprivAttributeRelease(this->colorAttr); + this->colorAttr = uiprivAttributeRetain(a); + break; + case uiAttributeTypeUnderline: + if (this->underlineAttr != NULL) + uiprivAttributeRelease(this->underlineAttr); + this->underlineAttr = uiprivAttributeRetain(a); + break; + case uiAttributeTypeUnderlineColor: + if (this->underlineAttr != NULL) + uiprivAttributeRelease(this->underlineAttr); + this->underlineColorAttr = uiprivAttributeRetain(a); + break; } - t = new textDrawingEffect; - f(t); - (*(p->effects))[i] = t; } +public: + combinedEffectsAttr(uiAttribute *a) + { + this->refcount = 1; + this->colorAttr = NULL; + this->underlineAttr = NULL; + this->underlineColorAttr = NULL; + this->setAttribute(a); + } + + ~combinedEffectsAttr() + { + if (this->colorAttr != NULL) + uiprivAttributeRelease(this->colorAttr); + if (this->underlineAttr != NULL) + uiprivAttributeRelease(this->underlineAttr); + if (this->underlineColorAttr != NULL) + uiprivAttributeRelease(this->underlineColorAttr); + } + + // IUnknown + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) + { + if (ppvObject == NULL) + return E_POINTER; + if (riid == IID_IUnknown) { + this->AddRef(); + *ppvObject = this; + return S_OK; + } + *ppvObject = NULL; + return E_NOINTERFACE; + } + + virtual ULONG STDMETHODCALLTYPE AddRef(void) + { + this->refcount++; + return this->refcount; + } + + virtual ULONG STDMETHODCALLTYPE Release(void) + { + this->refcount--; + if (this->refcount == 0) { + delete this; + return 0; + } + return this->refcount; + } + + combinedEffectsAttr *cloneWith(uiAttribute *a) + { + combinedEffectsAttr *b; + + b = new combinedEffectsAttr(this->colorAttr); + b->setAttribute(this->underlineAttr); + b->setAttribute(this->underlineColorAttr); + b->setAttribute(a); + return b; + } +}; + +static HRESULT addEffectAttributeToRange(struct foreachParams *p, size_t start, size_t end, uiAttribute *attr) +{ + IUnknown *u; + combinedEffectsAttr *cea; + DWRITE_TEXT_RANGE range; + size_t diff; + HRESULT hr; + + while (start < end) { + hr = p->layout->GetDrawingEffect(start, &u, &range); + if (hr != S_OK) +{logHRESULT(L"HELP", hr); + return hr; +} cea = (combinedEffectsAttr *) u; + if (cea == NULL) + cea = new combinedEffectsAttr(attr); + else + cea = cea->cloneWith(attr); + // clamp range within [start, end) + if (range.startPosition < start) { + diff = start - range.startPosition; + range.startPosition = start; + range.length -= diff; + } + if ((range.startPosition + range.length) > end) + range.length = end - range.startPosition; + hr = p->layout->SetDrawingEffect(cea, range); + if (hr != S_OK) + return hr; + // TODO figure out what and when needs to be released + start += range.length; + } + return S_OK; } static backgroundFunc mkBackgroundFunc(size_t start, size_t end, double r, double g, double b, double a) @@ -49,7 +158,7 @@ static backgroundFunc mkBackgroundFunc(size_t start, size_t end, double r, doubl }; } -static uiForEach processAttribute(const uiAttributedString *s, const uiAttributeSpec *spec, size_t start, size_t end, void *data) +static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute *attr, size_t start, size_t end, void *data) { struct foreachParams *p = (struct foreachParams *) data; DWRITE_TEXT_RANGE range; @@ -66,76 +175,69 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute end = attrstrUTF8ToUTF16((uiAttributedString *) s, end); range.startPosition = start; range.length = end - start; - switch (spec->Type) { - case uiAttributeFamily: - wfamily = toUTF16(spec->Family); + switch (uiAttributeGetType(attr)) { + case uiAttributeTypeFamily: + wfamily = toUTF16(uiAttributeFamily(attr)); hr = p->layout->SetFontFamilyName(wfamily, range); if (hr != S_OK) logHRESULT(L"error applying family name attribute", hr); uiFree(wfamily); break; - case uiAttributeSize: + case uiAttributeTypeSize: hr = p->layout->SetFontSize( -// TODO unify with drawtext.cpp +// TODO unify with fontmatch.cpp and/or attrstr.hpp #define pointSizeToDWriteSize(size) (size * (96.0 / 72.0)) - pointSizeToDWriteSize(spec->Double), + pointSizeToDWriteSize(uiAttributeSize(attr)), range); if (hr != S_OK) logHRESULT(L"error applying size attribute", hr); break; - case uiAttributeWeight: - // TODO reverse the misalignment from drawtext.cpp if it is corrected + case uiAttributeTypeWeight: hr = p->layout->SetFontWeight( - (DWRITE_FONT_WEIGHT) (spec->Value), + uiprivWeightToDWriteWeight(uiAttributeWeight(attr)), range); if (hr != S_OK) logHRESULT(L"error applying weight attribute", hr); break; - case uiAttributeItalic: + case uiAttributeTypeItalic: hr = p->layout->SetFontStyle( - dwriteItalics[(uiDrawTextItalic) (spec->Value)], + uiprivItalicToDWriteStyle(uiAttributeItalic(attr)), range); if (hr != S_OK) logHRESULT(L"error applying italic attribute", hr); break; - case uiAttributeStretch: + case uiAttributeTypeStretch: hr = p->layout->SetFontStretch( - dwriteStretches[(uiDrawTextStretch) (spec->Value)], + uiprivStretchToDWriteStretch(uiAttributeStretch(attr)), range); if (hr != S_OK) logHRESULT(L"error applying stretch attribute", hr); break; - case uiAttributeColor: - ensureEffectsInRange(p, start, end, [=](textDrawingEffect *t) { - t->hasColor = true; - t->r = spec->R; - t->g = spec->G; - t->b = spec->B; - t->a = spec->A; - }); + case uiAttributeTypeUnderline: + // mark that we have an underline; otherwise, DirectWrite will never call our custom renderer's DrawUnderline() method + hasUnderline = FALSE; + if (uiAttributeUnderline(attr) != uiUnderlineNone) + hasUnderline = TRUE; + hr = p->layout->SetUnderline(hasUnderline, range); + if (hr != S_OK) + logHRESULT(L"error applying underline attribute", hr); + // and fall through to set the underline style through the drawing effect + case uiAttributeTypeColor: + case uiAttributeTypeUnderlineColor: + hr = addEffectAttributeToRange(p, start, end, attr); + if (hr != S_OK) + logHRESULT(L"error applying effect (color, underline, or underline color) attribute", hr); break; case uiAttributeBackground: p->backgroundFuncs->push_back( mkBackgroundFunc(ostart, oend, spec->R, spec->G, spec->B, spec->A)); break; - case uiAttributeUnderline: - ensureEffectsInRange(p, start, end, [=](textDrawingEffect *t) { - t->hasUnderline = true; - t->u = (uiDrawUnderlineStyle) (spec->Value); - }); - // mark that we have an underline; otherwise, DirectWrite will never call our custom renderer's DrawUnderline() method - hasUnderline = FALSE; - if ((uiDrawUnderlineStyle) (spec->Value) != uiDrawUnderlineStyleNone) - hasUnderline = TRUE; - hr = p->layout->SetUnderline(hasUnderline, range); - if (hr != S_OK) - logHRESULT(L"error applying underline attribute", hr); - break; - case uiAttributeUnderlineColor: +#if 0 +TODO switch (spec->Value) { case uiDrawUnderlineColorCustom: - ensureEffectsInRange(p, start, end, [=](textDrawingEffect *t) { + x(p, start, end, [=](textDrawingEffect *t) { t->hasUnderlineColor = true; t->ur = spec->R; t->ug = spec->G; @@ -146,7 +248,7 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute // TODO see if Microsoft has any standard colors for this case uiDrawUnderlineColorSpelling: // TODO GtkTextView style property error-underline-color - ensureEffectsInRange(p, start, end, [=](textDrawingEffect *t) { + x(p, start, end, [=](textDrawingEffect *t) { t->hasUnderlineColor = true; t->ur = 1.0; t->ug = 0.0; @@ -155,7 +257,7 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute }); break; case uiDrawUnderlineColorGrammar: - ensureEffectsInRange(p, start, end, [=](textDrawingEffect *t) { + x(p, start, end, [=](textDrawingEffect *t) { t->hasUnderlineColor = true; t->ur = 0.0; t->ug = 1.0; @@ -164,7 +266,7 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute }); break; case uiDrawUnderlineColorAuxiliary: - ensureEffectsInRange(p, start, end, [=](textDrawingEffect *t) { + x(p, start, end, [=](textDrawingEffect *t) { t->hasUnderlineColor = true; t->ur = 0.0; t->ug = 0.0; @@ -174,24 +276,25 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute break; } break; - case uiAttributeFeatures: - // only generate an attribute if spec->Features is not NULL - if (spec->Features == NULL) +#endif + case uiAttributeTypeFeatures: + // only generate an attribute if not NULL + // TODO do we still need to do this or not... + if (uiAttributeFeatures(attr) == NULL) break; - dt = otfToDirectWrite(spec->Features); + dt = uiprivOpenTypeFeaturesToIDWriteTypography(uiAttributeFeatures(attr)); hr = p->layout->SetTypography(dt, range); if (hr != S_OK) logHRESULT(L"error applying features attribute", hr); dt->Release(); break; - default: - // TODO complain - ; } return uiForEachContinue; } -static void applyAndFreeEffectsAttributes(struct foreachParams *p) +$$$$TODO CONTINUE HERE + +static void applyEffectsAttributes(struct foreachParams *p) { size_t i, n; textDrawingEffect *effect, *effectb; From f25b8dce37dec5bcbb6ba62f77f6f0d54b5fd449 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 13 Mar 2018 22:01:15 -0400 Subject: [PATCH 0920/1329] Finished migrating attrstr.cpp. --- darwin/attrstr.m | 2 +- windows/OLD_uipriv_attrstr.h | 79 +----------- windows/attrstr.cpp | 243 ++++++++++++++++++++++------------- windows/attrstr.hpp | 35 +++++ windows/winapi.hpp | 1 + 5 files changed, 199 insertions(+), 161 deletions(-) diff --git a/darwin/attrstr.m b/darwin/attrstr.m index ca32216c..7d62bcfe 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -410,7 +410,7 @@ static void applyFontAttributes(CFMutableAttributedStringRef mas, uiFontDescript CFAttributedStringSetAttribute(mas, range, kCTFontAttributeName, font); CFRelease(font); - // now go through, replacing every consecutive uiprivCombinedFontAttr with the proper CTFontRef + // now go through, replacing every uiprivCombinedFontAttr with the proper CTFontRef // we are best off treating series of identical fonts as single ranges ourselves for parity across platforms, even if OS X does something similar itself range.location = 0; while (range.location < n) { diff --git a/windows/OLD_uipriv_attrstr.h b/windows/OLD_uipriv_attrstr.h index 360ec9e2..b50a7fe2 100644 --- a/windows/OLD_uipriv_attrstr.h +++ b/windows/OLD_uipriv_attrstr.h @@ -29,34 +29,14 @@ typedef std::function **backgroundFuncs); // drawtext.cpp -// TODO reconcile this with attrstr.cpp -class textDrawingEffect : public IUnknown { - ULONG refcount; -public: - bool hasColor; - double r; - double g; - double b; - double a; +textDrawingEffect:textDrawingEffect(void) +{ + this->refcount = 1; + this->hasColor = false; + this->hasUnderline = false; + this->hasUnderlineColor = false; +} - bool hasUnderline; - uiDrawUnderlineStyle u; - - bool hasUnderlineColor; - double ur; - double ug; - double ub; - double ua; - - textDrawingEffect() - { - this->refcount = 1; - this->hasColor = false; - this->hasUnderline = false; - this->hasUnderlineColor = false; - } - - // IUnknown virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) { if (ppvObject == NULL) @@ -85,49 +65,4 @@ public: } return this->refcount; } - - // TODO deduplicate this with common/attrlist.c - bool same(textDrawingEffect *b) - { - static auto boolsDiffer = [](bool a, bool b) -> bool { - if (a && b) - return false; - if (!a && !b) - return false; - return true; - }; - - if (boolsDiffer(this->hasColor, b->hasColor)) - return false; - if (this->hasColor) { - // TODO use a closest match? - if (this->r != b->r) - return false; - if (this->g != b->g) - return false; - if (this->b != b->b) - return false; - if (this->a != b->a) - return false; - } - if (boolsDiffer(this->hasUnderline, b->hasUnderline)) - return false; - if (this->hasUnderline) - if (this->u != b->u) - return false; - if (boolsDiffer(this->hasUnderlineColor, b->hasUnderlineColor)) - return false; - if (this->hasUnderlineColor) { - // TODO use a closest match? - if (this->ur != b->ur) - return false; - if (this->ug != b->ug) - return false; - if (this->ub != b->ub) - return false; - if (this->ua != b->ua) - return false; - } - return true; - } }; diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index 92c39b9a..ad16a70b 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -14,6 +14,8 @@ struct foreachParams { std::vector *backgroundFuncs; }; +static std::hash doubleHash; + // we need to combine color and underline style into one unit for IDWriteLayout::SetDrawingEffect() // we also want to combine identical effects, which DirectWrite doesn't seem to provide a way to do // we can at least try to goad it into doing so if we can deduplicate effects once they're all computed @@ -47,6 +49,17 @@ class combinedEffectsAttr : public IUnknown { break; } } + + // this is needed by applyEffectsAttributes() below + // TODO doesn't uiprivAttributeEqual() already do this; if it doesn't, make it so; if (or when) it does, fix all platforms to avoid this extra check + static bool attrEqual(uiAttribute *a, uiAttribute *b) const + { + if (a == NULL && b == NULL) + return true; + if (a == NULL || b == NULL) + return false; + return uiprivAttributeEqual(a, b); + } public: combinedEffectsAttr(uiAttribute *a) { @@ -107,6 +120,102 @@ public: b->setAttribute(a); return b; } + + // and these are also needed by applyEffectsAttributes() below + size_t hash(void) const noexcept + { + size_t ret = 0; + double r, g, b, a; + + if (self->colorAttr != NULL) { + uiAttributeColor(self->colorAttr, &r, &g, &b, &a); + ret ^= doubleHash(r); + ret ^= doubleHash(g); + ret ^= doubleHash(b); + ret ^= doubleHash(a); + } + if (self->underlineAttr != NULL) + ret ^= (size_t) uiAttributeUnderline(self->underlineAttr); + if (self->underlineColorAttr != NULL) { + uiAttributeUnderlineColor(self->underlineColorAttr, &colorType, &r, &g, &b, &a); + ret ^= (size_t) colorType; + ret ^= doubleHash(r); + ret ^= doubleHash(g); + ret ^= doubleHash(b); + ret ^= doubleHash(a); + } + return ret; + } + + bool equals(const combinedEffectsAttr *b) const + { + if (b == NULL) + return false; + return combinedEffectsAttr::attrEqual(a->colorAttr, b->colorAttr) && + combinedEffectsAttr::attrEqual(a->underilneAttr, b->underlineAttr) && + combinedEffectsAttr::attrEqual(a->underlineColorAttr, b->underlineColorAttr); + } + + drawingEffectsAttr *toDrawingEffectsAttr(void) + { + drawingEffectsAttr *dea; + double r, g, b, a; + uiUnderlineColor colorType; + + dea = new drawingEffectsAttr; + if (self->colorAttr != NULL) { + uiAttributeColor(self->colorAttr, &r, &g, &b, &a); + dea->addColor(r, g, b, a); + } + if (self->underlineAttr != NULL) + dea->addUnderline(uiAttributeUnderline(self->underlineAttr)); + if (self->underlineColorAttr != NULL) { + uiAttributeUnderlineColor(self->underlineColor, &colorType, &r, &g, &b, &a); + // TODO see if Microsoft has any standard colors for these + switch (colorType) { + case uiUnderlineColorSpelling: + // TODO consider using the GtkTextView style property error-underline-color here if Microsoft has no preference + r = 1.0; + g = 0.0; + b = 0.0; + a = 1.0; + break; + case uiUnderlineColorGrammar: + r = 0.0; + g = 1.0; + b = 0.0; + a = 1.0; + break; + case uiUnderlineColorAlternate: + r = 0.0; + g = 0.0; + b = 1.0; + a = 1.0; + break; + } + dea->addUnderlineColor(r, g, b, a); + } + return dea; + } +}; + +// also needed by applyEffectsAttributes() below +class applyEffectsHash { +public: + typedef combinedEffectsAttr *ceaptr; + size_t operator()(applyEffectsHash::ceaptr const &cea) const noexcept + { + return cea->hash(); + } +}; + +class applyEffectsEqualTo { +public: + typedef combinedEffectsAttr *ceaptr; + bool operator()(const applyEffectsEqualTo::ceaptr &a, const applyEffectsEqualTo::ceaptr &b) const + { + return a->equals(b); + } }; static HRESULT addEffectAttributeToRange(struct foreachParams *p, size_t start, size_t end, uiAttribute *attr) @@ -121,6 +230,7 @@ static HRESULT addEffectAttributeToRange(struct foreachParams *p, size_t start, hr = p->layout->GetDrawingEffect(start, &u, &range); if (hr != S_OK) {logHRESULT(L"HELP", hr); + // TODO proper cleanup somehow return hr; } cea = (combinedEffectsAttr *) u; if (cea == NULL) @@ -137,6 +247,7 @@ static HRESULT addEffectAttributeToRange(struct foreachParams *p, size_t start, range.length = end - range.startPosition; hr = p->layout->SetDrawingEffect(cea, range); if (hr != S_OK) + // TODO proper cleanup somehow return hr; // TODO figure out what and when needs to be released start += range.length; @@ -233,50 +344,6 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute mkBackgroundFunc(ostart, oend, spec->R, spec->G, spec->B, spec->A)); break; -#if 0 -TODO - switch (spec->Value) { - case uiDrawUnderlineColorCustom: - x(p, start, end, [=](textDrawingEffect *t) { - t->hasUnderlineColor = true; - t->ur = spec->R; - t->ug = spec->G; - t->ub = spec->B; - t->ua = spec->A; - }); - break; - // TODO see if Microsoft has any standard colors for this - case uiDrawUnderlineColorSpelling: - // TODO GtkTextView style property error-underline-color - x(p, start, end, [=](textDrawingEffect *t) { - t->hasUnderlineColor = true; - t->ur = 1.0; - t->ug = 0.0; - t->ub = 0.0; - t->ua = 1.0; - }); - break; - case uiDrawUnderlineColorGrammar: - x(p, start, end, [=](textDrawingEffect *t) { - t->hasUnderlineColor = true; - t->ur = 0.0; - t->ug = 1.0; - t->ub = 0.0; - t->ua = 1.0; - }); - break; - case uiDrawUnderlineColorAuxiliary: - x(p, start, end, [=](textDrawingEffect *t) { - t->hasUnderlineColor = true; - t->ur = 0.0; - t->ug = 0.0; - t->ub = 1.0; - t->ua = 1.0; - }); - break; - } - break; -#endif case uiAttributeTypeFeatures: // only generate an attribute if not NULL // TODO do we still need to do this or not... @@ -292,66 +359,66 @@ TODO return uiForEachContinue; } -$$$$TODO CONTINUE HERE - -static void applyEffectsAttributes(struct foreachParams *p) +static HRESULT applyEffectsAttributes(struct foreachParams *p) { - size_t i, n; - textDrawingEffect *effect, *effectb; + IUnknown *u; + combinedEffectsAttr *cea; + drawingEffectsAttr *dea; DWRITE_TEXT_RANGE range; - static auto apply = [](IDWriteTextLayout *layout, textDrawingEffect *effect, DWRITE_TEXT_RANGE range) { - HRESULT hr; + // here's the magic: this std::unordered_map will deduplicate all of our combinedEffectsAttrs, mapping all identical ones to a single drawingEffectsAttr + // because drawingEffectsAttr is the *actual* drawing effect we want for rendering, we also replace the combinedEffectsAttrs with them in the IDWriteTextLayout at the same time + // note the use of our custom hash and equal_to implementations + std::unordered_map effects; + HRESULT hr; - if (effect == NULL) - return; - hr = layout->SetDrawingEffect(effect, range); - if (hr != S_OK) - logHRESULT(L"error applying drawing effects attributes", hr); - effect->Release(); - }; - - // go through, fililng in the effect attribute for successive ranges of identical textDrawingEffects - // we are best off treating series of identical effects as single ranges ourselves for parity across platforms, even if Windows does something similar itself - // this also avoids breaking apart surrogate pairs (though IIRC Windows doing the something similar itself might make this a non-issue here) - effect = NULL; - n = p->len; + // go through, replacing every combinedEffectsAttr with the proper drawingEffectsAttr range.startPosition = 0; - for (i = 0; i < n; i++) { - effectb = (*(p->effects))[i]; - // run of no effect? - if (effect == NULL && effectb == NULL) - continue; - // run of the same effect? - if (effect != NULL && effectb != NULL) - if (effect->same(effectb)) { - effectb->Release(); - continue; + while (range.startPosition < p->len) { + hr = p->layout->GetDrawingEffect(range.startPosition, &u, &range); + if (hr != S_OK) + // TODO proper cleanup somehow + return hr; + cea = (combinedEffectsAttr *) cea; + if (cea != NULL) { + auto diter = effects.find(cea); + if (diter != effects.end()) + dea = diter->second; + else { + dea = cea->toDrawingEffectsAttr(); + effects.insert(cea, dea); } - - // the effect has changed; commit the old effect - range.length = i - range.startPosition; - apply(p->layout, effect, range); - - range.startPosition = i; - effect = effectb; + hr = p->layout->SetDrawingEffect(dea, range); + if (hr != S_OK) + // TODO proper cleanup somehow + return hr; + } + range.startPosition += range.length; } - // and apply the last effect - range.length = i - range.startPosition; - apply(p->layout, effect, range); - delete p->effects; + // and clean up, finally destroying the combinedEffectAttrs too +#if 0 +TODO + for (auto iter = effects.begin(); iter != effects.end(); iter++) { + iter->first->Release(); + iter->second->Release(); + } +#endif + return S_OK; } -void attrstrToIDWriteTextLayoutAttrs(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout, std::vector **backgroundFuncs) +void uiprivAttributedStringApplyAttributesToDWriteTextLayout(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout, std::vector **backgroundFuncs) { struct foreachParams fep; + HRESULT hr; fep.s = attrstrUTF16(p->String); fep.len = attrstrUTF16Len(p->String); fep.layout = layout; - fep.effects = new std::map; fep.backgroundFuncs = new std::vector; uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); - applyAndFreeEffectsAttributes(&fep); + hr = applyEffectsAttributes(&fep); + if (hr != S_OK) + logHRESULT(L"error applying effects attributes", hr); *backgroundFuncs = fep.backgroundFuncs; } diff --git a/windows/attrstr.hpp b/windows/attrstr.hpp index 074a8830..3137d75a 100644 --- a/windows/attrstr.hpp +++ b/windows/attrstr.hpp @@ -11,3 +11,38 @@ extern DWRITE_FONT_WEIGHT uiprivWeightToDWriteWeight(uiTextWeight w); extern DWRITE_FONT_STYLE uiprivItalicToDWriteStyle(uiTextItalic i); extern DWRITE_FONT_STRETCH uiprivStretchToDWriteStretch(uiTextStretch s); extern void uiprivFontDescriptorFromIDWriteFont(IDWriteFont *font, uiFontDescriptor *uidesc); + +// attrstr.cpp +extern void uiprivAttributedStringApplyAttributesToDWriteTextLayout(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout, std::vector **backgroundFuncs); + +// drawtext.cpp +class drawingEffectsAttr : public IUnknown { + ULONG refcount; + + bool hasColor; + double r; + double g; + double b; + double a; + + bool hasUnderline; + uiUnderline u; + + bool hasUnderlineColor; + double ur; + double ug; + double ub; + double ua; +public: + textDrawingEffect(); + + // IUnknown + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject); + virtual ULONG STDMETHODCALLTYPE AddRef(void); + virtual ULONG STDMETHODCALLTYPE Release(void); + + void setColor(double r, double g, double b, double a); + void setUnderline(uiUnderline u); + void setUnderlineColor(double r, double g, double b, double a); + HRESULT draw(TODO); +}; diff --git a/windows/winapi.hpp b/windows/winapi.hpp index c5796faf..1b2ab000 100644 --- a/windows/winapi.hpp +++ b/windows/winapi.hpp @@ -46,6 +46,7 @@ #include #include +#include #include #include #endif From 528295168122ecade75465b17ba9d462de80b1f5 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 13 Mar 2018 22:06:33 -0400 Subject: [PATCH 0921/1329] Oops, self -> this. --- windows/attrstr.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index ad16a70b..a1455b71 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -127,17 +127,17 @@ public: size_t ret = 0; double r, g, b, a; - if (self->colorAttr != NULL) { - uiAttributeColor(self->colorAttr, &r, &g, &b, &a); + if (this->colorAttr != NULL) { + uiAttributeColor(this->colorAttr, &r, &g, &b, &a); ret ^= doubleHash(r); ret ^= doubleHash(g); ret ^= doubleHash(b); ret ^= doubleHash(a); } - if (self->underlineAttr != NULL) - ret ^= (size_t) uiAttributeUnderline(self->underlineAttr); - if (self->underlineColorAttr != NULL) { - uiAttributeUnderlineColor(self->underlineColorAttr, &colorType, &r, &g, &b, &a); + if (this->underlineAttr != NULL) + ret ^= (size_t) uiAttributeUnderline(this->underlineAttr); + if (this->underlineColorAttr != NULL) { + uiAttributeUnderlineColor(this->underlineColorAttr, &colorType, &r, &g, &b, &a); ret ^= (size_t) colorType; ret ^= doubleHash(r); ret ^= doubleHash(g); @@ -163,14 +163,14 @@ public: uiUnderlineColor colorType; dea = new drawingEffectsAttr; - if (self->colorAttr != NULL) { - uiAttributeColor(self->colorAttr, &r, &g, &b, &a); + if (this->colorAttr != NULL) { + uiAttributeColor(this->colorAttr, &r, &g, &b, &a); dea->addColor(r, g, b, a); } - if (self->underlineAttr != NULL) - dea->addUnderline(uiAttributeUnderline(self->underlineAttr)); - if (self->underlineColorAttr != NULL) { - uiAttributeUnderlineColor(self->underlineColor, &colorType, &r, &g, &b, &a); + if (this->underlineAttr != NULL) + dea->addUnderline(uiAttributeUnderline(this->underlineAttr)); + if (this->underlineColorAttr != NULL) { + uiAttributeUnderlineColor(this->underlineColor, &colorType, &r, &g, &b, &a); // TODO see if Microsoft has any standard colors for these switch (colorType) { case uiUnderlineColorSpelling: From 5314295e4cfe88838671273537a588daab52f438 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 13 Mar 2018 22:56:30 -0400 Subject: [PATCH 0922/1329] Started migrating drawtext.cpp. This is a bigger mess than I was hoping for... --- windows/OLD_uipriv_attrstr.h | 39 -- windows/attrstr.hpp | 6 +- windows/drawtext.cpp | 669 +++++++++++++++++++++++++++++++++++ 3 files changed, 673 insertions(+), 41 deletions(-) create mode 100644 windows/drawtext.cpp diff --git a/windows/OLD_uipriv_attrstr.h b/windows/OLD_uipriv_attrstr.h index b50a7fe2..a183b3b0 100644 --- a/windows/OLD_uipriv_attrstr.h +++ b/windows/OLD_uipriv_attrstr.h @@ -27,42 +27,3 @@ extern WCHAR *fontDialogParamsToString(struct fontDialogParams *params); // attrstr.cpp typedef std::function backgroundFunc; extern void attrstrToIDWriteTextLayoutAttrs(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout, std::vector **backgroundFuncs); - -// drawtext.cpp -textDrawingEffect:textDrawingEffect(void) -{ - this->refcount = 1; - this->hasColor = false; - this->hasUnderline = false; - this->hasUnderlineColor = false; -} - - virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) - { - if (ppvObject == NULL) - return E_POINTER; - if (riid == IID_IUnknown) { - this->AddRef(); - *ppvObject = this; - return S_OK; - } - *ppvObject = NULL; - return E_NOINTERFACE; - } - - virtual ULONG STDMETHODCALLTYPE AddRef(void) - { - this->refcount++; - return this->refcount; - } - - virtual ULONG STDMETHODCALLTYPE Release(void) - { - this->refcount--; - if (this->refcount == 0) { - delete this; - return 0; - } - return this->refcount; - } -}; diff --git a/windows/attrstr.hpp b/windows/attrstr.hpp index 3137d75a..6c0bce7f 100644 --- a/windows/attrstr.hpp +++ b/windows/attrstr.hpp @@ -34,7 +34,7 @@ class drawingEffectsAttr : public IUnknown { double ub; double ua; public: - textDrawingEffect(); + drawingEffectsAttr(void); // IUnknown virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject); @@ -44,5 +44,7 @@ public: void setColor(double r, double g, double b, double a); void setUnderline(uiUnderline u); void setUnderlineColor(double r, double g, double b, double a); - HRESULT draw(TODO); + HRESULT mkColorBrush(ID2D1RenderTarget *rt, ID2D1SolidColorBrush **b); + HRESULT underline(uiUnderline *u); + HRESULT mkUnderlineBrush(ID2D1RenderTarget *rt, ID2D1SolidColorBrush **b); }; diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp new file mode 100644 index 00000000..c9baef49 --- /dev/null +++ b/windows/drawtext.cpp @@ -0,0 +1,669 @@ +// 17 january 2017 +#include "uipriv_windows.hpp" +#include "draw.hpp" +#include "attrstr.hpp" + +// TODO verify our renderer is correct, especially with regards to snapping + +struct uiDrawTextLayout { + IDWriteTextFormat *format; + IDWriteTextLayout *layout; + std::vector *backgroundFuncs; + // for converting DirectWrite indices from/to byte offsets + size_t *u8tou16; + size_t nUTF8; + size_t *u16tou8; + size_t nUTF16; +}; + +// TODO copy notes about DirectWrite DIPs being equal to Direct2D DIPs here + +// typographic points are 1/72 inch; this parameter is 1/96 inch +// fortunately Microsoft does this too, in https://msdn.microsoft.com/en-us/library/windows/desktop/dd371554%28v=vs.85%29.aspx +#define pointSizeToDWriteSize(size) (size * (96.0 / 72.0)) + +// TODO move this and the layout creation stuff to attrstr.cpp like the other ports, or move the other ports into their drawtext.* files +// TODO should be const but then I can't operator[] on it; the real solution is to find a way to do designated array initializers in C++11 but I do not know enough C++ voodoo to make it work (it is possible but no one else has actually done it before) +static std::map dwriteAligns = { + { uiDrawTextAlignLeft, DWRITE_TEXT_ALIGNMENT_LEADING }, + { uiDrawTextAlignCenter, DWRITE_TEXT_ALIGNMENT_CENTER }, + { uiDrawTextAlignRight, DWRITE_TEXT_ALIGNMENT_TRAILING }, +}; + +uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) +{ + uiDrawTextLayout *tl; + WCHAR *wDefaultFamily; + DWRITE_WORD_WRAPPING wrap; + FLOAT maxWidth; + HRESULT hr; + + tl = uiNew(uiDrawTextLayout); + + wDefaultFamily = toUTF16(p->DefaultFont->Family); + hr = dwfactory->CreateTextFormat( + wDefaultFamily, NULL, + uiprivWeightToDWriteWeight(p->DefaultFont->Weight), + uiprivItalicToDWriteStyle(p->DefaultFont->Italic), + uiprivStretchToDWriteStretch(p->DefaultFont->Stretch), + pointSizeToDWriteSize(p->DefaultFont->Size), + // see http://stackoverflow.com/questions/28397971/idwritefactorycreatetextformat-failing and https://msdn.microsoft.com/en-us/library/windows/desktop/dd368203.aspx + // TODO use the current locale? + L"", + &(tl->format)); + uiFree(wDefaultFamily); + if (hr != S_OK) + logHRESULT(L"error creating IDWriteTextFormat", hr); + hr = tl->format->SetTextAlignment(dwriteAligns[p->Align]); + if (hr != S_OK) + logHRESULT(L"error applying text layout alignment", hr); + + hr = dwfactory->CreateTextLayout( + (const WCHAR *) attrstrUTF16(p->String), attrstrUTF16Len(p->String), + tl->format, + // FLOAT is float, not double, so this should work... TODO + FLT_MAX, FLT_MAX, + &(tl->layout)); + if (hr != S_OK) + logHRESULT(L"error creating IDWriteTextLayout", hr); + + // and set the width + // this is the only wrapping mode (apart from "no wrap") available prior to Windows 8.1 (TODO verify this fact) (TODO this should be the default anyway) + wrap = DWRITE_WORD_WRAPPING_WRAP; + maxWidth = (FLOAT) (p->Width); + if (p->Width < 0) { + // TODO is this wrapping juggling even necessary? + wrap = DWRITE_WORD_WRAPPING_NO_WRAP; + // setting the max width in this case technically isn't needed since the wrap mode will simply ignore the max width, but let's do it just to be safe + maxWidth = FLT_MAX; // see TODO above + } + hr = tl->layout->SetWordWrapping(wrap); + if (hr != S_OK) + logHRESULT(L"error setting IDWriteTextLayout word wrapping mode", hr); + hr = tl->layout->SetMaxWidth(maxWidth); + if (hr != S_OK) + logHRESULT(L"error setting IDWriteTextLayout max layout width", hr); + + attrstrToIDWriteTextLayoutAttrs(p, tl->layout, &(tl->backgroundFuncs)); + + // and finally copy the UTF-8/UTF-16 index conversion tables + tl->u8tou16 = attrstrCopyUTF8ToUTF16(p->String, &(tl->nUTF8)); + tl->u16tou8 = attrstrCopyUTF16ToUTF8(p->String, &(tl->nUTF16)); + + return tl; +} + +void uiDrawFreeTextLayout(uiDrawTextLayout *tl) +{ + uiFree(tl->u16tou8); + uiFree(tl->u8tou16); + delete tl->backgroundFuncs; + tl->layout->Release(); + tl->format->Release(); + uiFree(tl); +} + +static HRESULT mkSolidBrush(ID2D1RenderTarget *rt, double r, double g, double b, double a, ID2D1SolidColorBrush **brush) +{ + D2D1_BRUSH_PROPERTIES props; + D2D1_COLOR_F color; + + ZeroMemory(&props, sizeof (D2D1_BRUSH_PROPERTIES)); + props.opacity = 1.0; + // identity matrix + props.transform._11 = 1; + props.transform._22 = 1; + color.r = r; + color.g = g; + color.b = b; + color.a = a; + return rt->CreateSolidColorBrush( + &color, + &props, + brush); +} + +static ID2D1SolidColorBrush *mustMakeSolidBrush(ID2D1RenderTarget *rt, double r, double g, double b, double a) +{ + ID2D1SolidColorBrush *brush; + HRESULT hr; + + hr = mkSolidBrush(rt, r, g, b, a, &brush); + if (hr != S_OK) + logHRESULT(L"error creating solid brush", hr); + return brush; +} + +// some of the stuff we want to do isn't possible with what DirectWrite provides itself; we need to do it ourselves + +drawingEffectsAttr::drawingEffectsAttr(void) +{ + this->refcount = 1; + this->hasColor = false; + this->hasUnderline = false; + this->hasUnderlineColor = false; +} + +virtual HRESULT STDMETHODCALLTYPE drawingEffectsAttr::QueryInterface(REFIID riid, void **ppvObject) +{ + if (ppvObject == NULL) + return E_POINTER; + if (riid == IID_IUnknown) { + this->AddRef(); + *ppvObject = this; + return S_OK; + } + *ppvObject = NULL; + return E_NOINTERFACE; +} + +virtual ULONG STDMETHODCALLTYPE drawingEffectsAttr::AddRef(void) +{ + this->refcount++; + return this->refcount; +} + +virtual ULONG STDMETHODCALLTYPE drawingEffectsAttr::Release(void) +{ + this->refcount--; + if (this->refcount == 0) { + delete this; + return 0; + } + return this->refcount; +} + +void drawingEffectsAttr::setColor(double r, double g, double b, double a) +{ + this->hasColor = true; + this->r = r; + this->g = g; + this->b = b; + this->a = a; +} + +void drawingEffectsAttr::setUnderline(uiUnderline u) +{ + this->hasUnderline = true; + this->u = u; +} + +void drawingEffectsAttr::setUnderlineColor(double r, double g, double b, double a) +{ + this->hasUnderlineColor = true; + this->ur = r; + this->ug = g; + this->ub = b; + this->ua = a; +} + +HRESULT drawingEffectsAttr::mkColorBrush(ID2D1RenderTarget *rt, ID2D1SolidColorBrush **b) +{ + if (!this->hasColor) { + *b = NULL; + return S_OK; + } + return mkSolidBrush(rt, this->r, this->g, this->b, this->a, b); +} + +HRESULT drawingEffectsAttr::underline(uiUnderline *u) +{ + if (u == NULL) + return E_POINTER; + if (!this->hasUnderline) + return E_UNEXPECTED; + *u = this->u; + return S_OK; +} + +HRESULT drawingEffectsAttr::mkUnderlineBrush(ID2D1RenderTarget *rt, ID2D1SolidColorBrush **b) +{ + if (!this->hasUnderlineColor) { + *b = NULL; + return S_OK; + } + return mkSolidBrush(rt, this->ur, this->ug, this->ub, this->ua, b); +} + +// this is based on http://www.charlespetzold.com/blog/2014/01/Character-Formatting-Extensions-with-DirectWrite.html +class textRenderer : public IDWriteTextRenderer { + ULONG refcount; + ID2D1RenderTarget *rt; + BOOL snap; + ID2D1SolidColorBrush *black; +public: + textRenderer(ID2D1RenderTarget *rt, BOOL snap, ID2D1SolidColorBrush *black) + { + this->refcount = 1; + this->rt = rt; + this->snap = snap; + this->black = black; + } + + // IUnknown + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) + { + if (ppvObject == NULL) + return E_POINTER; + if (riid == IID_IUnknown || + riid == __uuidof (IDWritePixelSnapping) || + riid == __uuidof (IDWriteTextRenderer)) { + this->AddRef(); + *ppvObject = this; + return S_OK; + } + *ppvObject = NULL; + return E_NOINTERFACE; + } + + virtual ULONG STDMETHODCALLTYPE AddRef(void) + { + this->refcount++; + return this->refcount; + } + + virtual ULONG STDMETHODCALLTYPE Release(void) + { + this->refcount--; + if (this->refcount == 0) { + delete this; + return 0; + } + return this->refcount; + } + + // IDWritePixelSnapping + virtual HRESULT STDMETHODCALLTYPE GetCurrentTransform(void *clientDrawingContext, DWRITE_MATRIX *transform) + { + D2D1_MATRIX_3X2_F d2dtf; + + if (transform == NULL) + return E_POINTER; + this->rt->GetTransform(&d2dtf); + transform->m11 = d2dtf._11; + transform->m12 = d2dtf._12; + transform->m21 = d2dtf._21; + transform->m22 = d2dtf._22; + transform->dx = d2dtf._31; + transform->dy = d2dtf._32; + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE GetPixelsPerDip(void *clientDrawingContext, FLOAT *pixelsPerDip) + { + FLOAT dpix, dpiy; + + if (pixelsPerDip == NULL) + return E_POINTER; + this->rt->GetDpi(&dpix, &dpiy); + *pixelsPerDip = dpix / 96; + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE IsPixelSnappingDisabled(void *clientDrawingContext, BOOL *isDisabled) + { + if (isDisabled == NULL) + return E_POINTER; + *isDisabled = !this->snap; + return S_OK; + } + + // IDWriteTextRenderer + virtual HRESULT STDMETHODCALLTYPE DrawGlyphRun(void *clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, DWRITE_MEASURING_MODE measuringMode, const DWRITE_GLYPH_RUN *glyphRun, const DWRITE_GLYPH_RUN_DESCRIPTION *glyphRunDescription, IUnknown *clientDrawingEffect) + { + D2D1_POINT_2F baseline; + drawingEffecsAttr *dea = (drawingEffectAttrs *) clientDrawingEffect; + ID2D1SolidColorBrush *brush; + + baseline.x = baselineOriginX; + baseline.y = baselineOriginY; + brush = NULL; + if (dea != NULL) { + HRESULT hr; + + hr = dea->mkColorBrush(this->rt, &brush); + if (hr != S_OK) + return hr; + } + if (brush == NULL) { + brush = this->black; + brush->Retain(); + } + this->rt->DrawGlyphRun( + baseline, + glyphRun, + brush, + measuringMode); + brush->Release(); + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE DrawInlineObject(void *clientDrawingContext, FLOAT originX, FLOAT originY, IDWriteInlineObject *inlineObject, BOOL isSideways, BOOL isRightToLeft, IUnknown *clientDrawingEffect) + { + if (inlineObject == NULL) + return E_POINTER; + return inlineObject->Draw(clientDrawingContext, this, + originX, originY, + isSideways, isRightToLeft, + clientDrawingEffect); + } + + virtual HRESULT STDMETHODCALLTYPE DrawStrikethrough(void *clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, const DWRITE_STRIKETHROUGH *strikethrough, IUnknown *clientDrawingEffect) + { + // we don't support strikethrough + return E_UNEXPECTED; + } + + virtual HRESULT STDMETHODCALLTYPE DrawUnderline(void *clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, const DWRITE_UNDERLINE *underline, IUnknown *clientDrawingEffect) + { + drawingEffectsAttr *dea = (drawingEffectsAttr *) clientDrawingEffect; + uiUnderline utype; + ID2D1SolidColorBrush *brush; + D2D1_RECT_F rect; + D2D1::Matrix3x2F pixeltf; + FLOAT dpix, dpiy; + D2D1_POINT_2F pt; + HRESULT hr; + + if (underline == NULL) + return E_POINTER; + if (dea == NULL) // we can only get here through an underline + return E_UNEXPECTED; + hr = dea->underline(&utype); + if (hr != S_OK) // we *should* only get here through an underline that's actually set... + return hr; + hr = dea->mkUnderlineBrush(this->rt, &brush); + if (hr != S_OK) + return hr; + if (brush == NULL) { + // TODO document this rule if not already done + hr = dea->mkColorBrush(this->rt, &brush); + if (hr != S_OK) + return hr; + } + if (brush == NULL) { + brush = this->black; + brush->Retain(); + } + rect.left = baselineOriginX; + rect.top = baselineOriginY + underline->offset; + rect.right = rect.left + underline->width; + rect.bottom = rect.top + underline->thickness; + switch (utype) { + case uiDrawUnderlineStyleSingle: + this->rt->FillRectangle(&rect, brush); + break; + case uiDrawUnderlineStyleDouble: + // TODO do any of the matrix methods return errors? + // TODO standardize double-underline shape across platforms? wavy underline shape? + this->rt->GetTransform(&pixeltf); + this->rt->GetDpi(&dpix, &dpiy); + pixeltf = pixeltf * D2D1::Matrix3x2F::Scale(dpix / 96, dpiy / 96); + pt.x = 0; + pt.y = rect.top; + pt = pixeltf.TransformPoint(pt); + rect.top = (FLOAT) ((int) (pt.y + 0.5)); + pixeltf.Invert(); + pt = pixeltf.TransformPoint(pt); + rect.top = pt.y; + // first line + rect.top -= underline->thickness; + // and it seems we need to recompute this + rect.bottom = rect.top + underline->thickness; + this->rt->FillRectangle(&rect, brush); + // second line + rect.top += 2 * underline->thickness; + rect.bottom = rect.top + underline->thickness; + this->rt->FillRectangle(&rect, brush); + break; + case uiDrawUnderlineStyleSuggestion: + { // TODO get rid of the extra block + // TODO properly clean resources on failure + // TODO use fully qualified C overloads for all methods + // TODO ensure all methods properly have errors handled + ID2D1PathGeometry *path; + ID2D1GeometrySink *sink; + double amplitude, period, xOffset, yOffset; + double t; + bool first = true; + HRESULT hr; + + hr = d2dfactory->CreatePathGeometry(&path); + if (hr != S_OK) + return hr; + hr = path->Open(&sink); + if (hr != S_OK) + return hr; + amplitude = underline->thickness; + period = 5 * underline->thickness; + xOffset = baselineOriginX; + yOffset = baselineOriginY + underline->offset; + for (t = 0; t < underline->width; t++) { + double x, angle, y; + D2D1_POINT_2F pt; + + x = t + xOffset; + angle = 2 * uiPi * fmod(x, period) / period; + y = amplitude * sin(angle) + yOffset; + pt.x = x; + pt.y = y; + if (first) { + sink->BeginFigure(pt, D2D1_FIGURE_BEGIN_HOLLOW); + first = false; + } else + sink->AddLine(pt); + } + sink->EndFigure(D2D1_FIGURE_END_OPEN); + hr = sink->Close(); + if (hr != S_OK) + return hr; + sink->Release(); + this->rt->DrawGeometry(path, brush, underline->thickness); + path->Release(); + } + break; + } + brush->Release(); + return S_OK; + } +}; + +$$$$ TODO continue here + +// TODO this ignores clipping? +void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) +{ + D2D1_POINT_2F pt; + ID2D1SolidColorBrush *black; + textRenderer *renderer; + HRESULT hr; + + for (const auto &f : *(tl->backgroundFuncs)) + f(c, tl, x, y); + + // TODO document that fully opaque black is the default text color; figure out whether this is upheld in various scenarios on other platforms + // TODO figure out if this needs to be cleaned out + black = mustMakeSolidBrush(c->rt, 0.0, 0.0, 0.0, 1.0); + +#define renderD2D 0 +#define renderOur 1 +#if renderD2D + pt.x = x; + pt.y = y; + // TODO D2D1_DRAW_TEXT_OPTIONS_NO_SNAP? + // TODO D2D1_DRAW_TEXT_OPTIONS_CLIP? + // TODO LONGTERM when setting 8.1 as minimum (TODO verify), D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT? + // TODO what is our pixel snapping setting related to the OPTIONS enum values? + c->rt->DrawTextLayout(pt, tl->layout, black, D2D1_DRAW_TEXT_OPTIONS_NONE); +#endif +#if renderD2D && renderOur + // draw ours semitransparent so we can check + // TODO get the actual color Charles Petzold uses and use that + black->Release(); + black = mustMakeSolidBrush(c->rt, 1.0, 0.0, 0.0, 0.75); +#endif +#if renderOur + renderer = new textRenderer(c->rt, + TRUE, // TODO FALSE for no-snap? + black); + hr = tl->layout->Draw(NULL, + renderer, + x, y); + if (hr != S_OK) + logHRESULT(L"error drawing IDWriteTextLayout", hr); + renderer->Release(); +#endif + + black->Release(); +} + +// TODO for a single line the height includes the leading; should it? TextEdit on OS X always includes the leading and/or paragraph spacing, otherwise Klee won't work... +// TODO width does not include trailing whitespace +void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height) +{ + DWRITE_TEXT_METRICS metrics; + HRESULT hr; + + hr = tl->layout->GetMetrics(&metrics); + if (hr != S_OK) + logHRESULT(L"error getting IDWriteTextLayout layout metrics", hr); + *width = metrics.width; + // TODO make sure the behavior of this on empty strings is the same on all platforms (ideally should be 0-width, line height-height; TODO note this in the docs too) + *height = metrics.height; +} + +int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) +{ + return tl->nLines; +} + +// DirectWrite doesn't provide a direct way to do this, so we have to do this manually +// TODO does that comment still apply here or to the code at the top of this file? +void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end) +{ + *start = tl->lineInfo[line].startPos; + *start = tl->u16tou8[*start]; + *end = tl->lineInfo[line].endPos - tl->lineInfo[line].newlineCount; + *end = tl->u16tou8[*end]; +} + +void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m) +{ + m->X = tl->lineInfo[line].x; + m->Y = tl->lineInfo[line].y; + m->Width = tl->lineInfo[line].width; + m->Height = tl->lineInfo[line].height; + + // TODO rename tl->lineInfo[line].baseline to .baselineOffset or something of the sort to make its meaning more clear + m->BaselineY = tl->lineInfo[line].y + tl->lineInfo[line].baseline; + m->Ascent = tl->lineInfo[line].baseline; + m->Descent = tl->lineInfo[line].height - tl->lineInfo[line].baseline; + m->Leading = 0; // TODO + + m->ParagraphSpacingBefore = 0; // TODO + m->LineHeightSpace = 0; // TODO + m->LineSpacing = 0; // TODO + m->ParagraphSpacing = 0; // TODO +} + +// this algorithm comes from Microsoft's PadWrite sample, following TextEditor::SetSelectionFromPoint() +// TODO go back through all of these and make sure we convert coordinates properly +// TODO same for OS X +void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *pos, int *line) +{ + DWRITE_HIT_TEST_METRICS m; + BOOL trailing, inside; + size_t p; + UINT32 i; + HRESULT hr; + + hr = tl->layout->HitTestPoint(x, y, + &trailing, &inside, + &m); + if (hr != S_OK) + logHRESULT(L"error hit-testing IDWriteTextLayout", hr); + p = m.textPosition; + // on a trailing hit, align to the nearest cluster + if (trailing) { + DWRITE_HIT_TEST_METRICS m2; + FLOAT x, y; // crashes if I skip these :/ + + hr = tl->layout->HitTestTextPosition(m.textPosition, trailing, + &x, &y, &m2); + if (hr != S_OK) + logHRESULT(L"error aligning trailing hit to nearest cluster", hr); + p = m2.textPosition + m2.length; + } + *pos = tl->u16tou8[p]; + + for (i = 0; i < tl->nLines; i++) { + double ltop, lbottom; + + ltop = tl->lineInfo[i].y; + lbottom = ltop + tl->lineInfo[i].height; + // y will already >= ltop at this point since the past lbottom should == ltop + if (y < lbottom) + break; + } + if (i == tl->nLines) + i--; + *line = i; +} + +double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line) +{ + BOOL trailing; + DWRITE_HIT_TEST_METRICS m; + FLOAT x, y; + HRESULT hr; + + if (line < 0 || line >= tl->nLines) + return -1; + pos = tl->u8tou16[pos]; + // note: >, not >=, because the position at endPos is valid! + if (pos < tl->lineInfo[line].startPos || pos > tl->lineInfo[line].endPos) + return -1; + // this behavior seems correct + // there's also PadWrite's TextEditor::GetCaretRect() but that requires state... + // TODO where does this fail? + trailing = FALSE; + if (pos != 0 && pos != tl->nUTF16 && pos == tl->lineInfo[line].endPos) { + pos--; + trailing = TRUE; + } + hr = tl->layout->HitTestTextPosition(pos, trailing, + &x, &y, &m); + if (hr != S_OK) + logHRESULT(L"error calling IDWriteTextLayout::HitTestTextPosition()", hr); + return x; +} + +void caretDrawParams(uiDrawContext *c, double height, struct caretDrawParams *p) +{ + DWORD caretWidth; + + // there seems to be no defined caret color + // the best I can come up with is "inverts colors underneath" (according to https://msdn.microsoft.com/en-us/library/windows/desktop/ms648397(v=vs.85).aspx) which I have no idea how to do (TODO) + // just return black for now + p->r = 0.0; + p->g = 0.0; + p->b = 0.0; + p->a = 1.0; + + if (SystemParametersInfoW(SPI_GETCARETWIDTH, 0, &caretWidth, 0) == 0) + // don't log the failure, fall back gracefully + // the instruction to use this comes from https://msdn.microsoft.com/en-us/library/windows/desktop/ms648399(v=vs.85).aspx + // and we have to assume GetSystemMetrics() always succeeds, so + caretWidth = GetSystemMetrics(SM_CXBORDER); + // TODO make this a function and split it out of areautil.cpp + { + FLOAT dpix, dpiy; + + // TODO can we pass NULL for dpiy? + c->rt->GetDpi(&dpix, &dpiy); + // see https://msdn.microsoft.com/en-us/library/windows/desktop/dd756649%28v=vs.85%29.aspx (and others; search "direct2d mouse") + p->width = ((double) (caretWidth * 96)) / dpix; + } + // and there doesn't seem to be this either... (TODO check what PadWrite does?) + p->xoff = 0; +} From 1b1d609c88cff15fd9febd49a9ffe5823e2ffb39 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 14 Mar 2018 21:08:19 -0400 Subject: [PATCH 0923/1329] More migrations of attrstr.cpp and drawtext.cpp. My this is a mess :D --- windows/OLD_uipriv_attrstr.h | 4 - windows/attrstr.cpp | 40 ++++------ windows/attrstr.hpp | 13 +++- windows/drawtext.cpp | 145 ++++------------------------------- 4 files changed, 45 insertions(+), 157 deletions(-) diff --git a/windows/OLD_uipriv_attrstr.h b/windows/OLD_uipriv_attrstr.h index a183b3b0..c494a59e 100644 --- a/windows/OLD_uipriv_attrstr.h +++ b/windows/OLD_uipriv_attrstr.h @@ -23,7 +23,3 @@ extern BOOL showFontDialog(HWND parent, struct fontDialogParams *params); extern void loadInitialFontDialogParams(struct fontDialogParams *params); extern void destroyFontDialogParams(struct fontDialogParams *params); extern WCHAR *fontDialogParamsToString(struct fontDialogParams *params); - -// attrstr.cpp -typedef std::function backgroundFunc; -extern void attrstrToIDWriteTextLayoutAttrs(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout, std::vector **backgroundFuncs); diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index a1455b71..586b1f4d 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -4,14 +4,15 @@ // TODO this whole file needs cleanup -// we need to collect all the background blocks and add them all at once +// we need to collect all the background parameters and add them all at once +// TODO consider having background parameters in the drawing effects // TODO contextual alternates override ligatures? // TODO rename this struct to something that isn't exclusively foreach-ing? struct foreachParams { const uint16_t *s; size_t len; IDWriteTextLayout *layout; - std::vector *backgroundFuncs; + std::vector *backgroundParams; }; static std::hash doubleHash; @@ -255,18 +256,15 @@ static HRESULT addEffectAttributeToRange(struct foreachParams *p, size_t start, return S_OK; } -static backgroundFunc mkBackgroundFunc(size_t start, size_t end, double r, double g, double b, double a) +static void addBackgroundParams(struct foreachParams *p, size_t start, size_t end, const uiAttribute *attr) { - return [=](uiDrawContext *c, uiDrawTextLayout *layout, double x, double y) { - uiDrawBrush brush; + struct drawTextBackgroundParams *params; - brush.Type = uiDrawBrushTypeSolid; - brush.R = r; - brush.G = g; - brush.B = b; - brush.A = a; - drawTextBackground(c, x, y, layout, start, end, &brush, 0); - }; + params = uiprivNew(struct drawTextBackgroundParams); + params->start = start; + params->end = end; + uiAttributeColor(attr, &(params->r), &(params->g), &(params->b), &(params->a)); + p->backgroundParams->push_back(params); } static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute *attr, size_t start, size_t end, void *data) @@ -274,16 +272,12 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute struct foreachParams *p = (struct foreachParams *) data; DWRITE_TEXT_RANGE range; WCHAR *wfamily; - size_t ostart, oend; BOOL hasUnderline; IDWriteTypography *dt; HRESULT hr; - ostart = start; - oend = end; - // TODO fix const correctness - start = attrstrUTF8ToUTF16((uiAttributedString *) s, start); - end = attrstrUTF8ToUTF16((uiAttributedString *) s, end); + start = uiprivAttributedStringUTF8ToUTF16(s, start); + end = uiprivAttributedStringUTF8ToUTF16(s, end); range.startPosition = start; range.length = end - start; switch (uiAttributeGetType(attr)) { @@ -340,9 +334,7 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute logHRESULT(L"error applying effect (color, underline, or underline color) attribute", hr); break; case uiAttributeBackground: - p->backgroundFuncs->push_back( - mkBackgroundFunc(ostart, oend, - spec->R, spec->G, spec->B, spec->A)); + addBackgroundParams(p, start, end, attr); break; case uiAttributeTypeFeatures: // only generate an attribute if not NULL @@ -407,7 +399,7 @@ TODO return S_OK; } -void uiprivAttributedStringApplyAttributesToDWriteTextLayout(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout, std::vector **backgroundFuncs) +void uiprivAttributedStringApplyAttributesToDWriteTextLayout(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout, std::vector **backgroundParams) { struct foreachParams fep; HRESULT hr; @@ -415,10 +407,10 @@ void uiprivAttributedStringApplyAttributesToDWriteTextLayout(uiDrawTextLayoutPar fep.s = attrstrUTF16(p->String); fep.len = attrstrUTF16Len(p->String); fep.layout = layout; - fep.backgroundFuncs = new std::vector; + fep.backgroundParams = new std::vector; uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); hr = applyEffectsAttributes(&fep); if (hr != S_OK) logHRESULT(L"error applying effects attributes", hr); - *backgroundFuncs = fep.backgroundFuncs; + *backgroundParams = fep.backgroundParams; } diff --git a/windows/attrstr.hpp b/windows/attrstr.hpp index 6c0bce7f..828223eb 100644 --- a/windows/attrstr.hpp +++ b/windows/attrstr.hpp @@ -13,7 +13,9 @@ extern DWRITE_FONT_STRETCH uiprivStretchToDWriteStretch(uiTextStretch s); extern void uiprivFontDescriptorFromIDWriteFont(IDWriteFont *font, uiFontDescriptor *uidesc); // attrstr.cpp -extern void uiprivAttributedStringApplyAttributesToDWriteTextLayout(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout, std::vector **backgroundFuncs); +// TODO +struct drawTextBackgroundParams; +extern void uiprivAttributedStringApplyAttributesToDWriteTextLayout(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout, std::vector **backgroundFuncs); // drawtext.cpp class drawingEffectsAttr : public IUnknown { @@ -48,3 +50,12 @@ public: HRESULT underline(uiUnderline *u); HRESULT mkUnderlineBrush(ID2D1RenderTarget *rt, ID2D1SolidColorBrush **b); }; +// TODO figure out where this type should *really* go in all the headers... +struct drawTextBackgroundParams { + size_t start; + size_t end; + double r; + double g; + double b; + double a; +}; diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index c9baef49..c1de9b2e 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -8,7 +8,7 @@ struct uiDrawTextLayout { IDWriteTextFormat *format; IDWriteTextLayout *layout; - std::vector *backgroundFuncs; + std::vector *backgroundParams; // for converting DirectWrite indices from/to byte offsets size_t *u8tou16; size_t nUTF8; @@ -84,7 +84,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) if (hr != S_OK) logHRESULT(L"error setting IDWriteTextLayout max layout width", hr); - attrstrToIDWriteTextLayoutAttrs(p, tl->layout, &(tl->backgroundFuncs)); + attrstrToIDWriteTextLayoutAttrs(p, tl->layout, &(tl->backgroundParams)); // and finally copy the UTF-8/UTF-16 index conversion tables tl->u8tou16 = attrstrCopyUTF8ToUTF16(p->String, &(tl->nUTF8)); @@ -97,12 +97,15 @@ void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { uiFree(tl->u16tou8); uiFree(tl->u8tou16); - delete tl->backgroundFuncs; + for (auto p in *(tl->backgroundParams)) + uiprivFree(p); + delete tl->backgroundParams; tl->layout->Release(); tl->format->Release(); uiFree(tl); } +// TODO make this shared code somehow static HRESULT mkSolidBrush(ID2D1RenderTarget *rt, double r, double g, double b, double a, ID2D1SolidColorBrush **brush) { D2D1_BRUSH_PROPERTIES props; @@ -354,6 +357,7 @@ public: return E_UNEXPECTED; } + // TODO clean this function up virtual HRESULT STDMETHODCALLTYPE DrawUnderline(void *clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, const DWRITE_UNDERLINE *underline, IUnknown *clientDrawingEffect) { drawingEffectsAttr *dea = (drawingEffectsAttr *) clientDrawingEffect; @@ -468,8 +472,6 @@ public: } }; -$$$$ TODO continue here - // TODO this ignores clipping? void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) { @@ -478,8 +480,9 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) textRenderer *renderer; HRESULT hr; - for (const auto &f : *(tl->backgroundFuncs)) - f(c, tl, x, y); + for (auto p : *(tl->backgroundParams)) { + // TODO + } // TODO document that fully opaque black is the default text color; figure out whether this is upheld in various scenarios on other platforms // TODO figure out if this needs to be cleaned out @@ -534,136 +537,22 @@ void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) { +return 0; +#if 0 +TODO return tl->nLines; +#endif } // DirectWrite doesn't provide a direct way to do this, so we have to do this manually // TODO does that comment still apply here or to the code at the top of this file? void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end) { +#if 0 +TODO *start = tl->lineInfo[line].startPos; *start = tl->u16tou8[*start]; *end = tl->lineInfo[line].endPos - tl->lineInfo[line].newlineCount; *end = tl->u16tou8[*end]; -} - -void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m) -{ - m->X = tl->lineInfo[line].x; - m->Y = tl->lineInfo[line].y; - m->Width = tl->lineInfo[line].width; - m->Height = tl->lineInfo[line].height; - - // TODO rename tl->lineInfo[line].baseline to .baselineOffset or something of the sort to make its meaning more clear - m->BaselineY = tl->lineInfo[line].y + tl->lineInfo[line].baseline; - m->Ascent = tl->lineInfo[line].baseline; - m->Descent = tl->lineInfo[line].height - tl->lineInfo[line].baseline; - m->Leading = 0; // TODO - - m->ParagraphSpacingBefore = 0; // TODO - m->LineHeightSpace = 0; // TODO - m->LineSpacing = 0; // TODO - m->ParagraphSpacing = 0; // TODO -} - -// this algorithm comes from Microsoft's PadWrite sample, following TextEditor::SetSelectionFromPoint() -// TODO go back through all of these and make sure we convert coordinates properly -// TODO same for OS X -void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *pos, int *line) -{ - DWRITE_HIT_TEST_METRICS m; - BOOL trailing, inside; - size_t p; - UINT32 i; - HRESULT hr; - - hr = tl->layout->HitTestPoint(x, y, - &trailing, &inside, - &m); - if (hr != S_OK) - logHRESULT(L"error hit-testing IDWriteTextLayout", hr); - p = m.textPosition; - // on a trailing hit, align to the nearest cluster - if (trailing) { - DWRITE_HIT_TEST_METRICS m2; - FLOAT x, y; // crashes if I skip these :/ - - hr = tl->layout->HitTestTextPosition(m.textPosition, trailing, - &x, &y, &m2); - if (hr != S_OK) - logHRESULT(L"error aligning trailing hit to nearest cluster", hr); - p = m2.textPosition + m2.length; - } - *pos = tl->u16tou8[p]; - - for (i = 0; i < tl->nLines; i++) { - double ltop, lbottom; - - ltop = tl->lineInfo[i].y; - lbottom = ltop + tl->lineInfo[i].height; - // y will already >= ltop at this point since the past lbottom should == ltop - if (y < lbottom) - break; - } - if (i == tl->nLines) - i--; - *line = i; -} - -double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line) -{ - BOOL trailing; - DWRITE_HIT_TEST_METRICS m; - FLOAT x, y; - HRESULT hr; - - if (line < 0 || line >= tl->nLines) - return -1; - pos = tl->u8tou16[pos]; - // note: >, not >=, because the position at endPos is valid! - if (pos < tl->lineInfo[line].startPos || pos > tl->lineInfo[line].endPos) - return -1; - // this behavior seems correct - // there's also PadWrite's TextEditor::GetCaretRect() but that requires state... - // TODO where does this fail? - trailing = FALSE; - if (pos != 0 && pos != tl->nUTF16 && pos == tl->lineInfo[line].endPos) { - pos--; - trailing = TRUE; - } - hr = tl->layout->HitTestTextPosition(pos, trailing, - &x, &y, &m); - if (hr != S_OK) - logHRESULT(L"error calling IDWriteTextLayout::HitTestTextPosition()", hr); - return x; -} - -void caretDrawParams(uiDrawContext *c, double height, struct caretDrawParams *p) -{ - DWORD caretWidth; - - // there seems to be no defined caret color - // the best I can come up with is "inverts colors underneath" (according to https://msdn.microsoft.com/en-us/library/windows/desktop/ms648397(v=vs.85).aspx) which I have no idea how to do (TODO) - // just return black for now - p->r = 0.0; - p->g = 0.0; - p->b = 0.0; - p->a = 1.0; - - if (SystemParametersInfoW(SPI_GETCARETWIDTH, 0, &caretWidth, 0) == 0) - // don't log the failure, fall back gracefully - // the instruction to use this comes from https://msdn.microsoft.com/en-us/library/windows/desktop/ms648399(v=vs.85).aspx - // and we have to assume GetSystemMetrics() always succeeds, so - caretWidth = GetSystemMetrics(SM_CXBORDER); - // TODO make this a function and split it out of areautil.cpp - { - FLOAT dpix, dpiy; - - // TODO can we pass NULL for dpiy? - c->rt->GetDpi(&dpix, &dpiy); - // see https://msdn.microsoft.com/en-us/library/windows/desktop/dd756649%28v=vs.85%29.aspx (and others; search "direct2d mouse") - p->width = ((double) (caretWidth * 96)) / dpix; - } - // and there doesn't seem to be this either... (TODO check what PadWrite does?) - p->xoff = 0; +#endif } From 1d1b6c316290bb70474e4017d6f57bff250efa7f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 16 Mar 2018 20:06:23 -0400 Subject: [PATCH 0924/1329] Migrated fontbutton.cpp back. --- windows/OLD_uipriv_attrstr.h | 11 ----------- windows/attrstr.hpp | 12 ++++++++++++ windows/{OLD_fontbutton.cpp => fontbutton.cpp} | 13 +++++++------ 3 files changed, 19 insertions(+), 17 deletions(-) rename windows/{OLD_fontbutton.cpp => fontbutton.cpp} (90%) diff --git a/windows/OLD_uipriv_attrstr.h b/windows/OLD_uipriv_attrstr.h index c494a59e..83c73b1f 100644 --- a/windows/OLD_uipriv_attrstr.h +++ b/windows/OLD_uipriv_attrstr.h @@ -12,14 +12,3 @@ extern WCHAR *fontCollectionFamilyName(fontCollection *fc, IDWriteFontFamily *fa extern void fontCollectionFree(fontCollection *fc); extern WCHAR *fontCollectionCorrectString(fontCollection *fc, IDWriteLocalizedStrings *names); -// fontdialog.cpp -struct fontDialogParams { - IDWriteFont *font; - double size; - WCHAR *familyName; - WCHAR *styleName; -}; -extern BOOL showFontDialog(HWND parent, struct fontDialogParams *params); -extern void loadInitialFontDialogParams(struct fontDialogParams *params); -extern void destroyFontDialogParams(struct fontDialogParams *params); -extern WCHAR *fontDialogParamsToString(struct fontDialogParams *params); diff --git a/windows/attrstr.hpp b/windows/attrstr.hpp index 828223eb..df143613 100644 --- a/windows/attrstr.hpp +++ b/windows/attrstr.hpp @@ -59,3 +59,15 @@ struct drawTextBackgroundParams { double b; double a; }; + +// fontdialog.cpp +struct fontDialogParams { + IDWriteFont *font; + double size; + WCHAR *familyName; + WCHAR *styleName; +}; +extern BOOL uiprivShowFontDialog(HWND parent, struct fontDialogParams *params); +extern void uiprivLoadInitialFontDialogParams(struct fontDialogParams *params); +extern void uiprivDestroyFontDialogParams(struct fontDialogParams *params); +extern WCHAR *uiprivFontDialogParamsToString(struct fontDialogParams *params); diff --git a/windows/OLD_fontbutton.cpp b/windows/fontbutton.cpp similarity index 90% rename from windows/OLD_fontbutton.cpp rename to windows/fontbutton.cpp index ab9fbe73..4f1ef594 100644 --- a/windows/OLD_fontbutton.cpp +++ b/windows/fontbutton.cpp @@ -1,5 +1,6 @@ // 14 april 2016 #include "uipriv_windows.hpp" +#include "attrstr.hpp" struct uiFontButton { uiWindowsControl c; @@ -15,7 +16,7 @@ static void uiFontButtonDestroy(uiControl *c) uiFontButton *b = uiFontButton(c); uiWindowsUnregisterWM_COMMANDHandler(b->hwnd); - destroyFontDialogParams(&(b->params)); + uiprivDestroyFontDialogParams(&(b->params)); uiWindowsEnsureDestroyWindow(b->hwnd); uiFreeControl(uiControl(b)); } @@ -24,9 +25,9 @@ static void updateFontButtonLabel(uiFontButton *b) { WCHAR *text; - text = fontDialogParamsToString(&(b->params)); + text = uiprivFontDialogParamsToString(&(b->params)); setWindowText(b->hwnd, text); - uiFree(text); + uiprivFree(text); // changing the text might necessitate a change in the button's size uiWindowsControlMinimumSizeChanged(uiWindowsControl(b)); @@ -41,7 +42,7 @@ static BOOL onWM_COMMAND(uiControl *c, HWND hwnd, WORD code, LRESULT *lResult) return FALSE; parent = parentToplevel(b->hwnd); - if (showFontDialog(parent, &(b->params))) { + if (uiprivShowFontDialog(parent, &(b->params))) { updateFontButtonLabel(b); (*(b->onChanged))(b, b->onChangedData); } @@ -88,7 +89,7 @@ static void defaultOnChanged(uiFontButton *b, void *data) void uiFontButtonFont(uiFontButton *b, uiDrawFontDescriptor *desc) { - fontdescFromIDWriteFont(b->params.font, desc); + uiprivFontDescriptorFromIDWriteFont(b->params.font, desc); desc->Family = toUTF8(b->params.familyName); desc->Size = b->params.size; } @@ -111,7 +112,7 @@ uiFontButton *uiNewFontButton(void) hInstance, NULL, TRUE); - loadInitialFontDialogParams(&(b->params)); + uiprivLoadInitialFontDialogParams(&(b->params)); uiWindowsRegisterWM_COMMANDHandler(b->hwnd, onWM_COMMAND, uiControl(b)); uiFontButtonOnChanged(b, defaultOnChanged, NULL); From 8352cd72b8ae3b35fc1d70d873a821e8cd9362ee Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Mar 2018 14:44:38 -0400 Subject: [PATCH 0925/1329] "Migrated" dwrite.cpp back. Not much to say here; it'll do for now. --- windows/OLD_uipriv_attrstr.h | 13 ------------- windows/attrstr.hpp | 14 +++++++++++++ windows/{OLD_dwrite.cpp => dwrite.cpp} | 27 +++++++++++++------------- 3 files changed, 28 insertions(+), 26 deletions(-) rename windows/{OLD_dwrite.cpp => dwrite.cpp} (84%) diff --git a/windows/OLD_uipriv_attrstr.h b/windows/OLD_uipriv_attrstr.h index 83c73b1f..8b137891 100644 --- a/windows/OLD_uipriv_attrstr.h +++ b/windows/OLD_uipriv_attrstr.h @@ -1,14 +1 @@ -// dwrite.cpp -extern IDWriteFactory *dwfactory; -extern HRESULT initDrawText(void); -extern void uninitDrawText(void); -struct fontCollection { - IDWriteFontCollection *fonts; - WCHAR userLocale[LOCALE_NAME_MAX_LENGTH]; - int userLocaleSuccess; -}; -extern fontCollection *loadFontCollection(void); -extern WCHAR *fontCollectionFamilyName(fontCollection *fc, IDWriteFontFamily *family); -extern void fontCollectionFree(fontCollection *fc); -extern WCHAR *fontCollectionCorrectString(fontCollection *fc, IDWriteLocalizedStrings *names); diff --git a/windows/attrstr.hpp b/windows/attrstr.hpp index df143613..449000ed 100644 --- a/windows/attrstr.hpp +++ b/windows/attrstr.hpp @@ -3,6 +3,20 @@ extern "C" { #include "../common/attrstr.h" } +// dwrite.cpp +extern IDWriteFactory *dwfactory; +extern HRESULT uiprivInitDrawText(void); +extern void uiprivUninitDrawText(void); +struct fontCollection { + IDWriteFontCollection *fonts; + WCHAR userLocale[LOCALE_NAME_MAX_LENGTH]; + int userLocaleSuccess; +}; +extern fontCollection *uiprivLoadFontCollection(void); +extern WCHAR *uiprivFontCollectionFamilyName(fontCollection *fc, IDWriteFontFamily *family); +extern void uiprivFontCollectionFree(fontCollection *fc); +extern WCHAR *uiprivFontCollectionCorrectString(fontCollection *fc, IDWriteLocalizedStrings *names); + // opentype.cpp extern IDWriteTypography *uiprivOpenTypeFeaturesToIDWriteTypography(const uiOpenTypeFeatures *otf); diff --git a/windows/OLD_dwrite.cpp b/windows/dwrite.cpp similarity index 84% rename from windows/OLD_dwrite.cpp rename to windows/dwrite.cpp index 498ef376..3aeedfdb 100644 --- a/windows/OLD_dwrite.cpp +++ b/windows/dwrite.cpp @@ -1,10 +1,11 @@ // 14 april 2016 #include "uipriv_windows.hpp" -// TODO really migrate? (TODO what did I mean by this?) +#include "attrstr.hpp" IDWriteFactory *dwfactory = NULL; -HRESULT initDrawText(void) +// TOOD rename to something else, maybe +HRESULT uiprivInitDrawText(void) { // TOOD use DWRITE_FACTORY_TYPE_ISOLATED instead? return DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, @@ -12,17 +13,17 @@ HRESULT initDrawText(void) (IUnknown **) (&dwfactory)); } -void uninitDrawText(void) +void uiprivUninitDrawText(void) { dwfactory->Release(); } -fontCollection *loadFontCollection(void) +fontCollection *uiprivLoadFontCollection(void) { fontCollection *fc; HRESULT hr; - fc = uiNew(fontCollection); + fc = uiprivNew(fontCollection); // always get the latest available font information hr = dwfactory->GetSystemFontCollection(&(fc->fonts), TRUE); if (hr != S_OK) @@ -31,7 +32,13 @@ fontCollection *loadFontCollection(void) return fc; } -WCHAR *fontCollectionFamilyName(fontCollection *fc, IDWriteFontFamily *family) +void uiprivFontCollectionFree(fontCollection *fc) +{ + fc->fonts->Release(); + uiprivFree(fc); +} + +WCHAR *uiprivFontCollectionFamilyName(fontCollection *fc, IDWriteFontFamily *family) { IDWriteLocalizedStrings *names; WCHAR *str; @@ -45,7 +52,7 @@ WCHAR *fontCollectionFamilyName(fontCollection *fc, IDWriteFontFamily *family) return str; } -WCHAR *fontCollectionCorrectString(fontCollection *fc, IDWriteLocalizedStrings *names) +WCHAR *uiprivFontCollectionCorrectString(fontCollection *fc, IDWriteLocalizedStrings *names) { UINT32 index; BOOL exists; @@ -81,9 +88,3 @@ WCHAR *fontCollectionCorrectString(fontCollection *fc, IDWriteLocalizedStrings * return wname; } - -void fontCollectionFree(fontCollection *fc) -{ - fc->fonts->Release(); - uiFree(fc); -} From e08460adc3bfa1c4b9fea0249f285ff3014d1ea2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Mar 2018 14:45:45 -0400 Subject: [PATCH 0926/1329] Cleanup from the previous commit. --- windows/OLD_uipriv_attrstr.h | 1 - windows/init.cpp | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) delete mode 100644 windows/OLD_uipriv_attrstr.h diff --git a/windows/OLD_uipriv_attrstr.h b/windows/OLD_uipriv_attrstr.h deleted file mode 100644 index 8b137891..00000000 --- a/windows/OLD_uipriv_attrstr.h +++ /dev/null @@ -1 +0,0 @@ - diff --git a/windows/init.cpp b/windows/init.cpp index 22874165..cfe63b9a 100644 --- a/windows/init.cpp +++ b/windows/init.cpp @@ -1,5 +1,6 @@ // 6 april 2015 #include "uipriv_windows.hpp" +#include "attrstr.hpp" HINSTANCE hInstance; int nCmdShow; @@ -117,7 +118,7 @@ const char *uiInit(uiInitOptions *o) if (hr != S_OK) return ieHRESULT("initializing Direct2D", hr); - hr = initDrawText(); + hr = uiprivInitDrawText(); if (hr != S_OK) return ieHRESULT("initializing DirectWrite", hr); @@ -139,7 +140,7 @@ void uiUninit(void) unregisterD2DScratchClass(); unregisterMessageFilter(); unregisterArea(); - uninitDrawText(); + uiprivUninitDrawText(); uninitDraw(); CoUninitialize(); if (DeleteObject(hollowBrush) == 0) From cdaf49ec3017bdd58d7c9c5537606768ccb1328b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Mar 2018 14:47:20 -0400 Subject: [PATCH 0927/1329] And quick and dirty migration of fontdialog.cpp back. Okay, after fixing the CMakeLists.txt, let's see how bad this is. --- windows/{OLD_fontdialog.cpp => fontdialog.cpp} | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) rename windows/{OLD_fontdialog.cpp => fontdialog.cpp} (98%) diff --git a/windows/OLD_fontdialog.cpp b/windows/fontdialog.cpp similarity index 98% rename from windows/OLD_fontdialog.cpp rename to windows/fontdialog.cpp index 6096d442..6a137de1 100644 --- a/windows/OLD_fontdialog.cpp +++ b/windows/fontdialog.cpp @@ -1,5 +1,6 @@ // 14 april 2016 #include "uipriv_windows.hpp" +#include "attrstr.hpp" // TODOs // - quote the Choose Font sample here for reference @@ -590,7 +591,7 @@ static INT_PTR CALLBACK fontDialogDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, L return FALSE; } -BOOL showFontDialog(HWND parent, struct fontDialogParams *params) +BOOL uiprivShowFontDialog(HWND parent, struct fontDialogParams *params) { switch (DialogBoxParamW(hInstance, MAKEINTRESOURCE(rcFontDialog), parent, fontDialogDlgProc, (LPARAM) params)) { case 1: // cancel @@ -622,7 +623,7 @@ static IDWriteFontFamily *tryFindFamily(IDWriteFontCollection *fc, const WCHAR * return family; } -void loadInitialFontDialogParams(struct fontDialogParams *params) +void uiprivLoadInitialFontDialogParams(struct fontDialogParams *params) { struct fontCollection *fc; IDWriteFontFamily *family; @@ -668,14 +669,14 @@ void loadInitialFontDialogParams(struct fontDialogParams *params) fontCollectionFree(fc); } -void destroyFontDialogParams(struct fontDialogParams *params) +void uiprivDestroyFontDialogParams(struct fontDialogParams *params) { params->font->Release(); - uiFree(params->familyName); - uiFree(params->styleName); + uiprivFree(params->familyName); + uiprivFree(params->styleName); } -WCHAR *fontDialogParamsToString(struct fontDialogParams *params) +WCHAR *uiprivFontDialogParamsToString(struct fontDialogParams *params) { WCHAR *text; From 51952b45994fa882b079d2f70622b78bbf62c2e0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Mar 2018 14:50:10 -0400 Subject: [PATCH 0928/1329] And fixed the CMakeLists.txt. Now to build. --- windows/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 739c1300..eab9ca90 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -29,6 +29,7 @@ list(APPEND _LIBUI_SOURCES windows/events.cpp windows/fontbutton.cpp windows/fontdialog.cpp + windows/fontmatch.cpp windows/form.cpp windows/graphemes.cpp windows/grid.cpp From 2f0283618109b967d91179c85788649495fdca34 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Mar 2018 15:29:06 -0400 Subject: [PATCH 0929/1329] Fixed attrstr.cpp. Wow, that unordered_map custom hash and equal_to actually compiles! --- windows/attrstr.cpp | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index 586b1f4d..c39a259e 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -4,6 +4,14 @@ // TODO this whole file needs cleanup +// yep, even when it supports C++11, it doesn't support C++11 +// we require MSVC 2013; this was added in MSVC 2015 (https://msdn.microsoft.com/en-us/library/wfa0edys.aspx) +#ifdef _MSC_VER +#if _MSC_VER < 1900 +#define noexcept +#endif +#endif + // we need to collect all the background parameters and add them all at once // TODO consider having background parameters in the drawing effects // TODO contextual alternates override ligatures? @@ -53,7 +61,7 @@ class combinedEffectsAttr : public IUnknown { // this is needed by applyEffectsAttributes() below // TODO doesn't uiprivAttributeEqual() already do this; if it doesn't, make it so; if (or when) it does, fix all platforms to avoid this extra check - static bool attrEqual(uiAttribute *a, uiAttribute *b) const + static bool attrEqual(uiAttribute *a, uiAttribute *b) { if (a == NULL && b == NULL) return true; @@ -127,6 +135,7 @@ public: { size_t ret = 0; double r, g, b, a; + uiUnderlineColor colorType; if (this->colorAttr != NULL) { uiAttributeColor(this->colorAttr, &r, &g, &b, &a); @@ -152,9 +161,9 @@ public: { if (b == NULL) return false; - return combinedEffectsAttr::attrEqual(a->colorAttr, b->colorAttr) && - combinedEffectsAttr::attrEqual(a->underilneAttr, b->underlineAttr) && - combinedEffectsAttr::attrEqual(a->underlineColorAttr, b->underlineColorAttr); + return combinedEffectsAttr::attrEqual(this->colorAttr, b->colorAttr) && + combinedEffectsAttr::attrEqual(this->underlineAttr, b->underlineAttr) && + combinedEffectsAttr::attrEqual(this->underlineColorAttr, b->underlineColorAttr); } drawingEffectsAttr *toDrawingEffectsAttr(void) @@ -166,12 +175,12 @@ public: dea = new drawingEffectsAttr; if (this->colorAttr != NULL) { uiAttributeColor(this->colorAttr, &r, &g, &b, &a); - dea->addColor(r, g, b, a); + dea->setColor(r, g, b, a); } if (this->underlineAttr != NULL) - dea->addUnderline(uiAttributeUnderline(this->underlineAttr)); + dea->setUnderline(uiAttributeUnderline(this->underlineAttr)); if (this->underlineColorAttr != NULL) { - uiAttributeUnderlineColor(this->underlineColor, &colorType, &r, &g, &b, &a); + uiAttributeUnderlineColor(this->underlineColorAttr, &colorType, &r, &g, &b, &a); // TODO see if Microsoft has any standard colors for these switch (colorType) { case uiUnderlineColorSpelling: @@ -187,14 +196,14 @@ public: b = 0.0; a = 1.0; break; - case uiUnderlineColorAlternate: + case uiUnderlineColorAuxiliary: r = 0.0; g = 0.0; b = 1.0; a = 1.0; break; } - dea->addUnderlineColor(r, g, b, a); + dea->setUnderlineColor(r, g, b, a); } return dea; } @@ -329,11 +338,12 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute // and fall through to set the underline style through the drawing effect case uiAttributeTypeColor: case uiAttributeTypeUnderlineColor: - hr = addEffectAttributeToRange(p, start, end, attr); + // TODO const-correct this properly + hr = addEffectAttributeToRange(p, start, end, (uiAttribute *) attr); if (hr != S_OK) logHRESULT(L"error applying effect (color, underline, or underline color) attribute", hr); break; - case uiAttributeBackground: + case uiAttributeTypeBackground: addBackgroundParams(p, start, end, attr); break; case uiAttributeTypeFeatures: @@ -360,7 +370,7 @@ static HRESULT applyEffectsAttributes(struct foreachParams *p) // here's the magic: this std::unordered_map will deduplicate all of our combinedEffectsAttrs, mapping all identical ones to a single drawingEffectsAttr // because drawingEffectsAttr is the *actual* drawing effect we want for rendering, we also replace the combinedEffectsAttrs with them in the IDWriteTextLayout at the same time // note the use of our custom hash and equal_to implementations - std::unordered_map effects; HRESULT hr; @@ -378,7 +388,7 @@ static HRESULT applyEffectsAttributes(struct foreachParams *p) dea = diter->second; else { dea = cea->toDrawingEffectsAttr(); - effects.insert(cea, dea); + effects.insert({cea, dea}); } hr = p->layout->SetDrawingEffect(dea, range); if (hr != S_OK) @@ -404,8 +414,8 @@ void uiprivAttributedStringApplyAttributesToDWriteTextLayout(uiDrawTextLayoutPar struct foreachParams fep; HRESULT hr; - fep.s = attrstrUTF16(p->String); - fep.len = attrstrUTF16Len(p->String); + fep.s = uiprivAttributedStringUTF16String(p->String); + fep.len = uiprivAttributedStringUTF16Len(p->String); fep.layout = layout; fep.backgroundParams = new std::vector; uiAttributedStringForEachAttribute(p->String, processAttribute, &fep); From a5bbc83359f92fbbc86f219a8a8bcf7b9e2f7eb2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Mar 2018 15:49:00 -0400 Subject: [PATCH 0930/1329] Fixed build errors. Now to test. --- common/attrstr.h | 4 ++++ windows/attrstr.cpp | 1 + windows/drawtext.cpp | 28 ++++++++++++++-------------- windows/dwrite.cpp | 2 +- windows/fontbutton.cpp | 2 +- windows/fontdialog.cpp | 22 +++++++++++----------- windows/fontmatch.cpp | 6 +++--- windows/opentype.cpp | 3 ++- 8 files changed, 37 insertions(+), 31 deletions(-) diff --git a/common/attrstr.h b/common/attrstr.h index 643c8c3c..fb2346d1 100644 --- a/common/attrstr.h +++ b/common/attrstr.h @@ -5,7 +5,11 @@ #define uiprivAlloc(x, y) uiAlloc(x, y) #define uiprivRealloc(x, y, z) uiRealloc(x, y, z) #define uiprivFree(x) uiFree(x) +#ifndef _WIN32 #define uiprivStricmp(x, y) strcasecmp(x, y) +#else +#define uiprivStricmp(x, y) stricmp(x, y) +#endif // attribute.c extern uiAttribute *uiprivAttributeRetain(uiAttribute *a); diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index c39a259e..feaaa61e 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -210,6 +210,7 @@ public: }; // also needed by applyEffectsAttributes() below +// TODO provide all the fields of std::hash and std::equal_to? class applyEffectsHash { public: typedef combinedEffectsAttr *ceaptr; diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index c1de9b2e..56a7dfbb 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -59,7 +59,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) logHRESULT(L"error applying text layout alignment", hr); hr = dwfactory->CreateTextLayout( - (const WCHAR *) attrstrUTF16(p->String), attrstrUTF16Len(p->String), + (const WCHAR *) uiprivAttributedStringUTF16String(p->String), uiprivAttributedStringUTF16Len(p->String), tl->format, // FLOAT is float, not double, so this should work... TODO FLT_MAX, FLT_MAX, @@ -84,11 +84,11 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) if (hr != S_OK) logHRESULT(L"error setting IDWriteTextLayout max layout width", hr); - attrstrToIDWriteTextLayoutAttrs(p, tl->layout, &(tl->backgroundParams)); + uiprivAttributedStringApplyAttributesToDWriteTextLayout(p, tl->layout, &(tl->backgroundParams)); // and finally copy the UTF-8/UTF-16 index conversion tables - tl->u8tou16 = attrstrCopyUTF8ToUTF16(p->String, &(tl->nUTF8)); - tl->u16tou8 = attrstrCopyUTF16ToUTF8(p->String, &(tl->nUTF16)); + tl->u8tou16 = uiprivAttributedStringCopyUTF8ToUTF16Table(p->String, &(tl->nUTF8)); + tl->u16tou8 = uiprivAttributedStringCopyUTF16ToUTF8Table(p->String, &(tl->nUTF16)); return tl; } @@ -97,7 +97,7 @@ void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { uiFree(tl->u16tou8); uiFree(tl->u8tou16); - for (auto p in *(tl->backgroundParams)) + for (auto p : *(tl->backgroundParams)) uiprivFree(p); delete tl->backgroundParams; tl->layout->Release(); @@ -147,7 +147,7 @@ drawingEffectsAttr::drawingEffectsAttr(void) this->hasUnderlineColor = false; } -virtual HRESULT STDMETHODCALLTYPE drawingEffectsAttr::QueryInterface(REFIID riid, void **ppvObject) +HRESULT STDMETHODCALLTYPE drawingEffectsAttr::QueryInterface(REFIID riid, void **ppvObject) { if (ppvObject == NULL) return E_POINTER; @@ -160,13 +160,13 @@ virtual HRESULT STDMETHODCALLTYPE drawingEffectsAttr::QueryInterface(REFIID riid return E_NOINTERFACE; } -virtual ULONG STDMETHODCALLTYPE drawingEffectsAttr::AddRef(void) +ULONG STDMETHODCALLTYPE drawingEffectsAttr::AddRef(void) { this->refcount++; return this->refcount; } -virtual ULONG STDMETHODCALLTYPE drawingEffectsAttr::Release(void) +ULONG STDMETHODCALLTYPE drawingEffectsAttr::Release(void) { this->refcount--; if (this->refcount == 0) { @@ -315,7 +315,7 @@ public: virtual HRESULT STDMETHODCALLTYPE DrawGlyphRun(void *clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, DWRITE_MEASURING_MODE measuringMode, const DWRITE_GLYPH_RUN *glyphRun, const DWRITE_GLYPH_RUN_DESCRIPTION *glyphRunDescription, IUnknown *clientDrawingEffect) { D2D1_POINT_2F baseline; - drawingEffecsAttr *dea = (drawingEffectAttrs *) clientDrawingEffect; + drawingEffectsAttr *dea = (drawingEffectsAttr *) clientDrawingEffect; ID2D1SolidColorBrush *brush; baseline.x = baselineOriginX; @@ -330,7 +330,7 @@ public: } if (brush == NULL) { brush = this->black; - brush->Retain(); + brush->AddRef(); } this->rt->DrawGlyphRun( baseline, @@ -387,17 +387,17 @@ public: } if (brush == NULL) { brush = this->black; - brush->Retain(); + brush->AddRef(); } rect.left = baselineOriginX; rect.top = baselineOriginY + underline->offset; rect.right = rect.left + underline->width; rect.bottom = rect.top + underline->thickness; switch (utype) { - case uiDrawUnderlineStyleSingle: + case uiUnderlineSingle: this->rt->FillRectangle(&rect, brush); break; - case uiDrawUnderlineStyleDouble: + case uiUnderlineDouble: // TODO do any of the matrix methods return errors? // TODO standardize double-underline shape across platforms? wavy underline shape? this->rt->GetTransform(&pixeltf); @@ -420,7 +420,7 @@ public: rect.bottom = rect.top + underline->thickness; this->rt->FillRectangle(&rect, brush); break; - case uiDrawUnderlineStyleSuggestion: + case uiUnderlineSuggestion: { // TODO get rid of the extra block // TODO properly clean resources on failure // TODO use fully qualified C overloads for all methods diff --git a/windows/dwrite.cpp b/windows/dwrite.cpp index 3aeedfdb..24a4aa3a 100644 --- a/windows/dwrite.cpp +++ b/windows/dwrite.cpp @@ -47,7 +47,7 @@ WCHAR *uiprivFontCollectionFamilyName(fontCollection *fc, IDWriteFontFamily *fam hr = family->GetFamilyNames(&names); if (hr != S_OK) logHRESULT(L"error getting names of font out", hr); - str = fontCollectionCorrectString(fc, names); + str = uiprivFontCollectionCorrectString(fc, names); names->Release(); return str; } diff --git a/windows/fontbutton.cpp b/windows/fontbutton.cpp index 4f1ef594..29de64b6 100644 --- a/windows/fontbutton.cpp +++ b/windows/fontbutton.cpp @@ -87,7 +87,7 @@ static void defaultOnChanged(uiFontButton *b, void *data) // do nothing } -void uiFontButtonFont(uiFontButton *b, uiDrawFontDescriptor *desc) +void uiFontButtonFont(uiFontButton *b, uiFontDescriptor *desc) { uiprivFontDescriptorFromIDWriteFont(b->params.font, desc); desc->Family = toUTF8(b->params.familyName); diff --git a/windows/fontdialog.cpp b/windows/fontdialog.cpp index 6a137de1..2d10a1ae 100644 --- a/windows/fontdialog.cpp +++ b/windows/fontdialog.cpp @@ -18,7 +18,7 @@ struct fontDialog { struct fontDialogParams *params; - fontCollection *fc; + struct fontCollection *fc; RECT sampleRect; HWND sampleBox; @@ -169,7 +169,7 @@ static WCHAR *fontStyleName(struct fontCollection *fc, IDWriteFont *font) hr = font->GetFaceNames(&str); if (hr != S_OK) logHRESULT(L"error getting font style name for font dialog", hr); - wstr = fontCollectionCorrectString(fc, str); + wstr = uiprivFontCollectionCorrectString(fc, str); str->Release(); return wstr; } @@ -365,7 +365,7 @@ static void fontDialogDrawSampleText(struct fontDialog *f, ID2D1RenderTarget *rt if (hr != S_OK) exists = FALSE; if (exists) { - sample = fontCollectionCorrectString(f->fc, sampleStrings); + sample = uiprivFontCollectionCorrectString(f->fc, sampleStrings); sampleStrings->Release(); } else sample = L"The quick brown fox jumps over the lazy dog."; @@ -474,13 +474,13 @@ static struct fontDialog *beginFontDialog(HWND hwnd, LPARAM lParam) f->styleCombobox = getDlgItem(f->hwnd, rcFontStyleCombobox); f->sizeCombobox = getDlgItem(f->hwnd, rcFontSizeCombobox); - f->fc = loadFontCollection(); + f->fc = uiprivLoadFontCollection(); nFamilies = f->fc->fonts->GetFontFamilyCount(); for (i = 0; i < nFamilies; i++) { hr = f->fc->fonts->GetFontFamily(i, &family); if (hr != S_OK) logHRESULT(L"error getting font family", hr); - wname = fontCollectionFamilyName(f->fc, family); + wname = uiprivFontCollectionFamilyName(f->fc, family); pos = cbAddString(f->familyCombobox, wname); uiFree(wname); cbSetItemData(f->familyCombobox, (WPARAM) pos, (LPARAM) family); @@ -503,7 +503,7 @@ static void endFontDialog(struct fontDialog *f, INT_PTR code) { wipeStylesBox(f); cbWipeAndReleaseData(f->familyCombobox); - fontCollectionFree(f->fc); + uiprivFontCollectionFree(f->fc); if (EndDialog(f->hwnd, code) == 0) logLastError(L"error ending font dialog"); uiFree(f); @@ -520,13 +520,13 @@ static INT_PTR tryFinishDialog(struct fontDialog *f, WPARAM wParam) } // OK - destroyFontDialogParams(f->params); + uiprivDestroyFontDialogParams(f->params); f->params->font = (IDWriteFont *) cbGetItemData(f->styleCombobox, f->curStyle); // we need to save font from being destroyed with the combobox f->params->font->AddRef(); f->params->size = f->curSize; family = (IDWriteFontFamily *) cbGetItemData(f->familyCombobox, f->curFamily); - f->params->familyName = fontCollectionFamilyName(f->fc, family); + f->params->familyName = uiprivFontCollectionFamilyName(f->fc, family); f->params->styleName = fontStyleName(f->fc, f->params->font); endFontDialog(f, 2); return TRUE; @@ -636,7 +636,7 @@ void uiprivLoadInitialFontDialogParams(struct fontDialogParams *params) // If Arial isn't found, we'll use Helvetica and then MS Sans Serif as fallbacks, and if not, we'll just grab the first font family in the collection. // We need the correct localized name for Regular (and possibly Arial too? let's say yes to be safe), so let's grab the strings from DirectWrite instead of hardcoding them. - fc = loadFontCollection(); + fc = uiprivLoadFontCollection(); family = tryFindFamily(fc->fonts, L"Arial"); if (family == NULL) { family = tryFindFamily(fc->fonts, L"Helvetica"); @@ -661,12 +661,12 @@ void uiprivLoadInitialFontDialogParams(struct fontDialogParams *params) params->font = font; params->size = 10; - params->familyName = fontCollectionFamilyName(fc, family); + params->familyName = uiprivFontCollectionFamilyName(fc, family); params->styleName = fontStyleName(fc, font); // don't release font; we still need it family->Release(); - fontCollectionFree(fc); + uiprivFontCollectionFree(fc); } void uiprivDestroyFontDialogParams(struct fontDialogParams *params) diff --git a/windows/fontmatch.cpp b/windows/fontmatch.cpp index 28b4c84a..73f29543 100644 --- a/windows/fontmatch.cpp +++ b/windows/fontmatch.cpp @@ -49,13 +49,13 @@ void uiprivFontDescriptorFromIDWriteFont(IDWriteFont *font, uiFontDescriptor *ui dwitalic = font->GetStyle(); // TODO reverse the above misalignment if it is corrected - uidesc->Weight = (uiDrawTextWeight) (font->GetWeight()); + uidesc->Weight = (uiTextWeight) (font->GetWeight()); dwstretch = font->GetStretch(); - for (uidesc->Italic = uiDrawTextItalicNormal; uidesc->Italic < uiDrawTextItalicItalic; uidesc->Italic++) + for (uidesc->Italic = uiTextItalicNormal; uidesc->Italic < uiTextItalicItalic; uidesc->Italic++) if (dwriteItalics[uidesc->Italic] == dwitalic) break; - for (uidesc->Stretch = uiDrawTextStretchUltraCondensed; uidesc->Stretch < uiDrawTextStretchUltraExpanded; uidesc->Stretch++) + for (uidesc->Stretch = uiTextStretchUltraCondensed; uidesc->Stretch < uiTextStretchUltraExpanded; uidesc->Stretch++) if (dwriteStretches[uidesc->Stretch] == dwstretch) break; } diff --git a/windows/opentype.cpp b/windows/opentype.cpp index 0a96cdc4..777f30d6 100644 --- a/windows/opentype.cpp +++ b/windows/opentype.cpp @@ -11,7 +11,8 @@ static uiForEach addToTypography(const uiOpenTypeFeatures *otf, char a, char b, HRESULT hr; ZeroMemory(&dff, sizeof (DWRITE_FONT_FEATURE)); - dff.nameTag = /*(DWRITE_FONT_FEATURE_TAG)*/ DWRITE_MAKE_OPENTYPE_TAG(a, b, c, d); + // yes, the cast here is necessary (the compiler will complain otherwise)... + dff.nameTag = (DWRITE_FONT_FEATURE_TAG) DWRITE_MAKE_OPENTYPE_TAG(a, b, c, d); dff.parameter = (UINT32) value; hr = dt->AddFontFeature(dff); if (hr != S_OK) From 45d11962b10133f9df6842b24ad7d09e05474d17 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Mar 2018 16:10:11 -0400 Subject: [PATCH 0931/1329] Turns out there was only one real runtime bug (a bad castee). It works! Now to clean up. --- windows/attrstr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index feaaa61e..79406fa6 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -382,7 +382,7 @@ static HRESULT applyEffectsAttributes(struct foreachParams *p) if (hr != S_OK) // TODO proper cleanup somehow return hr; - cea = (combinedEffectsAttr *) cea; + cea = (combinedEffectsAttr *) u; if (cea != NULL) { auto diter = effects.find(cea); if (diter != effects.end()) From 5d116d87fedfb2e8a9e05ea752673c7d22786790 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Mar 2018 16:14:50 -0400 Subject: [PATCH 0932/1329] Fixed a typo in the drawtext example and made it redraw immediately on a font change. --- examples/drawtext/main.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c index c3b2d968..98d206f9 100644 --- a/examples/drawtext/main.c +++ b/examples/drawtext/main.c @@ -30,7 +30,7 @@ static void makeAttributedString(void) uiOpenTypeFeatures *otf; attrstr = uiNewAttributedString( - "Drawing strings with libui is done with the uiAttributedString and uiDrawTextLayout obects.\n" + "Drawing strings with libui is done with the uiAttributedString and uiDrawTextLayout objects.\n" "uiAttributedString lets you have a variety of attributes: "); attr = uiNewFamilyAttribute("Courier New"); @@ -129,6 +129,11 @@ static int handlerKeyEvent(uiAreaHandler *ah, uiArea *a, uiAreaKeyEvent *e) return 0; } +static void onFontChanged(uiFontButton *b, void *data) +{ + uiAreaQueueRedrawAll(area); +} + static int onClosing(uiWindow *w, void *data) { uiControlDestroy(uiControl(mainwin)); @@ -179,6 +184,7 @@ int main(void) uiBoxAppend(hbox, uiControl(vbox), 0); fontButton = uiNewFontButton(); + uiFontButtonOnChanged(fontButton, onFontChanged, NULL); uiBoxAppend(vbox, uiControl(fontButton), 0); area = uiNewArea(&handler); From d358e875838ba00314e0161e235bdc210620c50c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Mar 2018 21:00:11 -0400 Subject: [PATCH 0933/1329] Moved the old metrics attrstr code out of the way. --- .../OLD_drawtext.c => _wip/attrstr_metrics/common_OLD_drawtext.c | 0 .../attrstr_metrics/common_OLD_uipriv_attrstr.h | 0 .../attrstr_metrics/darwin_OLD__appkit_drawtext.m | 0 .../attrstr_metrics/darwin_OLD__appkit_fontmatch.m | 0 .../attrstr_metrics/darwin_OLD__old_drawtext.m | 0 .../OLD_drawtext.m => _wip/attrstr_metrics/darwin_OLD_drawtext.m | 0 .../attrstr_metrics/unix_OLD__old_drawtext.c | 0 unix/OLD_drawtext.c => _wip/attrstr_metrics/unix_OLD_drawtext.c | 0 .../attrstr_metrics/windows_OLD__old_drawtext.cpp | 0 .../attrstr_metrics/windows_OLD_drawtext.cpp | 0 10 files changed, 0 insertions(+), 0 deletions(-) rename common/OLD_drawtext.c => _wip/attrstr_metrics/common_OLD_drawtext.c (100%) rename common/OLD_uipriv_attrstr.h => _wip/attrstr_metrics/common_OLD_uipriv_attrstr.h (100%) rename darwin/OLD__appkit_drawtext.m => _wip/attrstr_metrics/darwin_OLD__appkit_drawtext.m (100%) rename darwin/OLD__appkit_fontmatch.m => _wip/attrstr_metrics/darwin_OLD__appkit_fontmatch.m (100%) rename darwin/OLD__old_drawtext.m => _wip/attrstr_metrics/darwin_OLD__old_drawtext.m (100%) rename darwin/OLD_drawtext.m => _wip/attrstr_metrics/darwin_OLD_drawtext.m (100%) rename unix/OLD__old_drawtext.c => _wip/attrstr_metrics/unix_OLD__old_drawtext.c (100%) rename unix/OLD_drawtext.c => _wip/attrstr_metrics/unix_OLD_drawtext.c (100%) rename windows/OLD__old_drawtext.cpp => _wip/attrstr_metrics/windows_OLD__old_drawtext.cpp (100%) rename windows/OLD_drawtext.cpp => _wip/attrstr_metrics/windows_OLD_drawtext.cpp (100%) diff --git a/common/OLD_drawtext.c b/_wip/attrstr_metrics/common_OLD_drawtext.c similarity index 100% rename from common/OLD_drawtext.c rename to _wip/attrstr_metrics/common_OLD_drawtext.c diff --git a/common/OLD_uipriv_attrstr.h b/_wip/attrstr_metrics/common_OLD_uipriv_attrstr.h similarity index 100% rename from common/OLD_uipriv_attrstr.h rename to _wip/attrstr_metrics/common_OLD_uipriv_attrstr.h diff --git a/darwin/OLD__appkit_drawtext.m b/_wip/attrstr_metrics/darwin_OLD__appkit_drawtext.m similarity index 100% rename from darwin/OLD__appkit_drawtext.m rename to _wip/attrstr_metrics/darwin_OLD__appkit_drawtext.m diff --git a/darwin/OLD__appkit_fontmatch.m b/_wip/attrstr_metrics/darwin_OLD__appkit_fontmatch.m similarity index 100% rename from darwin/OLD__appkit_fontmatch.m rename to _wip/attrstr_metrics/darwin_OLD__appkit_fontmatch.m diff --git a/darwin/OLD__old_drawtext.m b/_wip/attrstr_metrics/darwin_OLD__old_drawtext.m similarity index 100% rename from darwin/OLD__old_drawtext.m rename to _wip/attrstr_metrics/darwin_OLD__old_drawtext.m diff --git a/darwin/OLD_drawtext.m b/_wip/attrstr_metrics/darwin_OLD_drawtext.m similarity index 100% rename from darwin/OLD_drawtext.m rename to _wip/attrstr_metrics/darwin_OLD_drawtext.m diff --git a/unix/OLD__old_drawtext.c b/_wip/attrstr_metrics/unix_OLD__old_drawtext.c similarity index 100% rename from unix/OLD__old_drawtext.c rename to _wip/attrstr_metrics/unix_OLD__old_drawtext.c diff --git a/unix/OLD_drawtext.c b/_wip/attrstr_metrics/unix_OLD_drawtext.c similarity index 100% rename from unix/OLD_drawtext.c rename to _wip/attrstr_metrics/unix_OLD_drawtext.c diff --git a/windows/OLD__old_drawtext.cpp b/_wip/attrstr_metrics/windows_OLD__old_drawtext.cpp similarity index 100% rename from windows/OLD__old_drawtext.cpp rename to _wip/attrstr_metrics/windows_OLD__old_drawtext.cpp diff --git a/windows/OLD_drawtext.cpp b/_wip/attrstr_metrics/windows_OLD_drawtext.cpp similarity index 100% rename from windows/OLD_drawtext.cpp rename to _wip/attrstr_metrics/windows_OLD_drawtext.cpp From fa293717af8dec5087ad3c9fc112b2939d969011 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Mar 2018 21:05:05 -0400 Subject: [PATCH 0934/1329] Removed the NumLines and LineByteRange functions for now; I'll count them under extents. --- _wip/attrstr_metrics/numlinesbyterange | 60 +++++++++++++++++++ .../attrstr_metrics/old_ui_attrstr.h | 9 +++ darwin/drawtext.m | 19 ------ ui_attrstr.h | 9 --- unix/drawtext.c | 15 ----- windows/drawtext.cpp | 22 ------- 6 files changed, 69 insertions(+), 65 deletions(-) create mode 100644 _wip/attrstr_metrics/numlinesbyterange rename old_ui_attrstr.h => _wip/attrstr_metrics/old_ui_attrstr.h (85%) diff --git a/_wip/attrstr_metrics/numlinesbyterange b/_wip/attrstr_metrics/numlinesbyterange new file mode 100644 index 00000000..46a7ba21 --- /dev/null +++ b/_wip/attrstr_metrics/numlinesbyterange @@ -0,0 +1,60 @@ +darwin +int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) +{ + return CFArrayGetCount([tl->forLines lines]); +} + +void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end) +{ + CTLineRef lr; + CFRange range; + + lr = (CTLineRef) CFArrayGetValueAtIndex([tl->forLines lines], line); + range = CTLineGetStringRange(lr); + *start = tl->u16tou8[range.location]; + if (tl->empty) + *end = *start; + else + *end = tl->u16tou8[range.location + range.length]; +} + + +unix +int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) +{ + return pango_layout_get_line_count(tl->layout); +} + +void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end) +{ + PangoLayoutLine *pll; + + pll = pango_layout_get_line_readonly(tl->layout, line); + *start = pll->start_index; + *end = pll->start_index + pll->length; + // TODO unref pll? +} + + +windows +int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) +{ +return 0; +#if 0 +TODO + return tl->nLines; +#endif +} + +// DirectWrite doesn't provide a direct way to do this, so we have to do this manually +// TODO does that comment still apply here or to the code at the top of this file? +void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end) +{ +#if 0 +TODO + *start = tl->lineInfo[line].startPos; + *start = tl->u16tou8[*start]; + *end = tl->lineInfo[line].endPos - tl->lineInfo[line].newlineCount; + *end = tl->u16tou8[*end]; +#endif +} diff --git a/old_ui_attrstr.h b/_wip/attrstr_metrics/old_ui_attrstr.h similarity index 85% rename from old_ui_attrstr.h rename to _wip/attrstr_metrics/old_ui_attrstr.h index 75df996d..ebe9308d 100644 --- a/old_ui_attrstr.h +++ b/_wip/attrstr_metrics/old_ui_attrstr.h @@ -31,6 +31,15 @@ struct uiDrawTextLayoutLineMetrics { // TODO trailing whitespace? }; +// uiDrawTextLayoutNumLines() returns the number of lines in tl. +// This number will always be greater than or equal to 1; a text +// layout with no text only has one line. +_UI_EXTERN int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl); + +// uiDrawTextLayoutLineByteRange() returns the byte indices of the +// text that falls into the given line of tl as [start, end). +_UI_EXTERN void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end); + _UI_EXTERN void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m); // TODO rewrite this documentation diff --git a/darwin/drawtext.m b/darwin/drawtext.m index 5406617c..c04b402b 100644 --- a/darwin/drawtext.m +++ b/darwin/drawtext.m @@ -212,22 +212,3 @@ void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height [tl->frame returnWidth:width height:NULL]; [tl->forLines returnWidth:NULL height:height]; } - -int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) -{ - return CFArrayGetCount([tl->forLines lines]); -} - -void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end) -{ - CTLineRef lr; - CFRange range; - - lr = (CTLineRef) CFArrayGetValueAtIndex([tl->forLines lines], line); - range = CTLineGetStringRange(lr); - *start = tl->u16tou8[range.location]; - if (tl->empty) - *end = *start; - else - *end = tl->u16tou8[range.location + range.length]; -} diff --git a/ui_attrstr.h b/ui_attrstr.h index 66b0fb4b..f4302cd5 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -484,15 +484,6 @@ _UI_EXTERN void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, dou // function to get the actual size of the text layout. _UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height); -// uiDrawTextLayoutNumLines() returns the number of lines in tl. -// This number will always be greater than or equal to 1; a text -// layout with no text only has one line. -_UI_EXTERN int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl); - -// uiDrawTextLayoutLineByteRange() returns the byte indices of the -// text that falls into the given line of tl as [start, end). -_UI_EXTERN void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end); - // TODO metrics functions // TODO number of lines visible for clipping rect, range visible for clipping rect? diff --git a/unix/drawtext.c b/unix/drawtext.c index 5792f9e0..477e9ca3 100644 --- a/unix/drawtext.c +++ b/unix/drawtext.c @@ -79,18 +79,3 @@ void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height *width = pangoToCairo(logical.width); *height = pangoToCairo(logical.height); } - -int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) -{ - return pango_layout_get_line_count(tl->layout); -} - -void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end) -{ - PangoLayoutLine *pll; - - pll = pango_layout_get_line_readonly(tl->layout, line); - *start = pll->start_index; - *end = pll->start_index + pll->length; - // TODO unref pll? -} diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 56a7dfbb..85accab1 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -534,25 +534,3 @@ void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height // TODO make sure the behavior of this on empty strings is the same on all platforms (ideally should be 0-width, line height-height; TODO note this in the docs too) *height = metrics.height; } - -int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl) -{ -return 0; -#if 0 -TODO - return tl->nLines; -#endif -} - -// DirectWrite doesn't provide a direct way to do this, so we have to do this manually -// TODO does that comment still apply here or to the code at the top of this file? -void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end) -{ -#if 0 -TODO - *start = tl->lineInfo[line].startPos; - *start = tl->u16tou8[*start]; - *end = tl->lineInfo[line].endPos - tl->lineInfo[line].newlineCount; - *end = tl->u16tou8[*end]; -#endif -} From 70321353a13ba63af3f3f546053dec679da66493 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Mar 2018 21:06:45 -0400 Subject: [PATCH 0935/1329] Moved a few more future files out of the way. --- checklist_attrstr => _future/unittest/checklist_attrstr | 0 .../attrstr_metrics/textDarwinEmptyLayout.diff | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename checklist_attrstr => _future/unittest/checklist_attrstr (100%) rename textDarwinEmptyLayout.diff => _wip/attrstr_metrics/textDarwinEmptyLayout.diff (100%) diff --git a/checklist_attrstr b/_future/unittest/checklist_attrstr similarity index 100% rename from checklist_attrstr rename to _future/unittest/checklist_attrstr diff --git a/textDarwinEmptyLayout.diff b/_wip/attrstr_metrics/textDarwinEmptyLayout.diff similarity index 100% rename from textDarwinEmptyLayout.diff rename to _wip/attrstr_metrics/textDarwinEmptyLayout.diff From 78e06844356ebae83de048035e98a7c36c1cd10d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Mar 2018 21:26:34 -0400 Subject: [PATCH 0936/1329] Cleaned up memory management in windows attrstr.cpp; also got rid of the logHRESULT(HELP) I was using in case DirectWrite doesn't return NULL+S_OK on a nonexistent drawing effect (thankfully it does). --- windows/attrstr.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index 79406fa6..9ea0f755 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -240,10 +240,8 @@ static HRESULT addEffectAttributeToRange(struct foreachParams *p, size_t start, while (start < end) { hr = p->layout->GetDrawingEffect(start, &u, &range); if (hr != S_OK) -{logHRESULT(L"HELP", hr); - // TODO proper cleanup somehow return hr; -} cea = (combinedEffectsAttr *) u; + cea = (combinedEffectsAttr *) u; if (cea == NULL) cea = new combinedEffectsAttr(attr); else @@ -257,10 +255,11 @@ static HRESULT addEffectAttributeToRange(struct foreachParams *p, size_t start, if ((range.startPosition + range.length) > end) range.length = end - range.startPosition; hr = p->layout->SetDrawingEffect(cea, range); + // SetDrawingEffect will AddRef(), so Release() our copy + // (and we're abandoning early if that failed, so this will make sure things are cleaned up in that case) + cea->Release(); if (hr != S_OK) - // TODO proper cleanup somehow return hr; - // TODO figure out what and when needs to be released start += range.length; } return S_OK; @@ -377,11 +376,13 @@ static HRESULT applyEffectsAttributes(struct foreachParams *p) // go through, replacing every combinedEffectsAttr with the proper drawingEffectsAttr range.startPosition = 0; + // and in case this while loop never runs, make hr valid to start with + hr = S_OK; while (range.startPosition < p->len) { hr = p->layout->GetDrawingEffect(range.startPosition, &u, &range); if (hr != S_OK) - // TODO proper cleanup somehow - return hr; + // note that we are breaking instead of returning; this allows us to clean up on failure + break; cea = (combinedEffectsAttr *) u; if (cea != NULL) { auto diter = effects.find(cea); @@ -392,22 +393,21 @@ static HRESULT applyEffectsAttributes(struct foreachParams *p) effects.insert({cea, dea}); } hr = p->layout->SetDrawingEffect(dea, range); + // don't release dea; we need the reference that's inside the map + // (we don't take extra references on lookup, so this will be fine) if (hr != S_OK) - // TODO proper cleanup somehow - return hr; + break; } range.startPosition += range.length; } // and clean up, finally destroying the combinedEffectAttrs too -#if 0 -TODO + // we do this in the case of failure as well, to make sure everything is properly cleaned up for (auto iter = effects.begin(); iter != effects.end(); iter++) { iter->first->Release(); iter->second->Release(); } -#endif - return S_OK; + return hr; } void uiprivAttributedStringApplyAttributesToDWriteTextLayout(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout, std::vector **backgroundParams) From 8709838a8f409919c5008d9a0bd1b7b726f661b1 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Mar 2018 23:07:40 -0400 Subject: [PATCH 0937/1329] Added a uiFreeFontButtonFont() function to free resources allocated by uiFontButtonFont(), implemented it on Windows, and integrated it into the drawtext example. I'm going to continue chipping away at the Windows code for a bit longer, so I'll add this to the other platforms later. --- examples/drawtext/main.c | 1 + ui_attrstr.h | 1 + windows/fontbutton.cpp | 5 +++++ 3 files changed, 7 insertions(+) diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c index 98d206f9..615340e7 100644 --- a/examples/drawtext/main.c +++ b/examples/drawtext/main.c @@ -106,6 +106,7 @@ static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *p) // TODO clip to margins uiDrawText(p->Context, textLayout, margins, margins); uiDrawFreeTextLayout(textLayout); + uiFreeFontButtonFont(&defaultFont); } static void handlerMouseEvent(uiAreaHandler *a, uiArea *area, uiAreaMouseEvent *e) diff --git a/ui_attrstr.h b/ui_attrstr.h index f4302cd5..3486b4e9 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -495,3 +495,4 @@ _UI_EXTERN void uiFontButtonFont(uiFontButton *b, uiFontDescriptor *desc); // TOOD SetFont, mechanics _UI_EXTERN void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data); _UI_EXTERN uiFontButton *uiNewFontButton(void); +_UI_EXTERN void uiFreeFontButtonFont(uiFontDescriptor *desc); diff --git a/windows/fontbutton.cpp b/windows/fontbutton.cpp index 29de64b6..d6e5e0d8 100644 --- a/windows/fontbutton.cpp +++ b/windows/fontbutton.cpp @@ -121,3 +121,8 @@ uiFontButton *uiNewFontButton(void) return b; } + +void uiFreeFontButtonFont(uiFontDescriptor *desc) +{ + uiprivFree((char *) (desc->Family)); +} From 93f0eea140914fb7bc1d4458a8d7bba5bd59a6f2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Mar 2018 23:21:54 -0400 Subject: [PATCH 0938/1329] Likewise codified uiprivStricmp(). Honestly this will probably do for the Windows code for now... --- common/attrstr.h | 5 ----- common/uipriv.h | 3 +++ windows/text.cpp | 13 +++++++++++++ 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/common/attrstr.h b/common/attrstr.h index fb2346d1..2f4e42b4 100644 --- a/common/attrstr.h +++ b/common/attrstr.h @@ -5,11 +5,6 @@ #define uiprivAlloc(x, y) uiAlloc(x, y) #define uiprivRealloc(x, y, z) uiRealloc(x, y, z) #define uiprivFree(x) uiFree(x) -#ifndef _WIN32 -#define uiprivStricmp(x, y) strcasecmp(x, y) -#else -#define uiprivStricmp(x, y) stricmp(x, y) -#endif // attribute.c extern uiAttribute *uiprivAttributeRetain(uiAttribute *a); diff --git a/common/uipriv.h b/common/uipriv.h index 3cd23605..02676a16 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -58,6 +58,9 @@ extern void fallbackSkew(uiDrawMatrix *, double, double, double, double); extern void scaleCenter(double, double, double *, double *); extern void fallbackTransformSize(uiDrawMatrix *, double *, double *); +// OS-specific text.* files +extern int uiprivStricmp(const char *a, const char *b); + #ifdef __cplusplus } #endif diff --git a/windows/text.cpp b/windows/text.cpp index af79fb80..e3a23570 100644 --- a/windows/text.cpp +++ b/windows/text.cpp @@ -105,3 +105,16 @@ void uiWindowsSetWindowText(HWND hwnd, const char *text) setWindowText(hwnd, wtext); uiFree(wtext); } + +int uiprivStricmp(const char *a, const char *b) +{ + WCHAR *wa, *wb; + int ret; + + wa = toUTF16(a); + wb = toUTF16(b); + ret = _wcsicmp(wa, wb); + uiFree(wb); + uiFree(wa); + return ret; +} From 0125e33720b9616bdd3a69da18b6b155b17ce4dd Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Mar 2018 23:42:54 -0400 Subject: [PATCH 0939/1329] Made the likewise changes on Unix. --- unix/fontbutton.c | 6 ++++++ unix/text.c | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/unix/fontbutton.c b/unix/fontbutton.c index b19cd310..d239952b 100644 --- a/unix/fontbutton.c +++ b/unix/fontbutton.c @@ -67,3 +67,9 @@ uiFontButton *uiNewFontButton(void) return b; } + +void uiFreeFontButtonFont(uiFontDescriptor *desc) +{ + // TODO ensure this is synchronized with fontmatch.c + uiFreeText((char *) (desc->Family)); +} diff --git a/unix/text.c b/unix/text.c index ad92738d..9a2a9dc0 100644 --- a/unix/text.c +++ b/unix/text.c @@ -10,3 +10,8 @@ void uiFreeText(char *t) { g_free(t); } + +int uiprivStricmp(const char *a, const char *b) +{ + return strcasecmp(a, b); +} From bc895d67078e52743b706bb2932ad81d8f9f4cd8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 17 Mar 2018 23:55:33 -0400 Subject: [PATCH 0940/1329] And added the necessary functions on OS X. Now to do some final cleanup before merging back in (at long last). --- darwin/fontbutton.m | 6 ++++++ darwin/text.m | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/darwin/fontbutton.m b/darwin/fontbutton.m index 86f1c140..0ef57d81 100644 --- a/darwin/fontbutton.m +++ b/darwin/fontbutton.m @@ -224,3 +224,9 @@ uiFontButton *uiNewFontButton(void) return b; } + +void uiFreeFontButtonFont(uiFontDescriptor *desc) +{ + // TODO ensure this is synchronized with fontmatch.m + uiFreeText((char *) (desc->Family)); +} diff --git a/darwin/text.m b/darwin/text.m index f0d3dab6..8efd36fe 100644 --- a/darwin/text.m +++ b/darwin/text.m @@ -17,3 +17,8 @@ void uiFreeText(char *s) { free(s); } + +int uiprivStricmp(const char *a, const char *b) +{ + return strcasecmp(a, b); +} From 9aea7fa62e6d1880f4e4a80b501ac25d2a47c3c8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 18 Mar 2018 11:24:09 -0400 Subject: [PATCH 0941/1329] Polished up the drawtext demo a bit (such as finally fixing that titlebar). Also more crash-related TODOs. --- darwin/fontmatch.m | 3 +++ examples/drawtext/main.c | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index f459a165..5b56f258 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -2,6 +2,9 @@ #import "uipriv_darwin.h" #import "attrstr.h" +// TODOs: +// - switching from Skia to a non-fvar-based font crashes because the CTFontDescriptorRef we get has an empty variation dictionary for some reason... + // Core Text exposes font style info in two forms: // - Fonts with a QuickDraw GX font variation (fvar) table, a feature // adopted by OpenType, expose variations directly. diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c index 615340e7..45549dff 100644 --- a/examples/drawtext/main.c +++ b/examples/drawtext/main.c @@ -88,7 +88,9 @@ static void makeAttributedString(void) attr = uiNewFeaturesAttribute(otf); appendWithAttribute("afford", attr, NULL); uiFreeOpenTypeFeatures(otf); - uiAttributedStringAppendUnattributed(attrstr, ")."); + uiAttributedStringAppendUnattributed(attrstr, ").\n"); + + uiAttributedStringAppendUnattributed(attrstr, "Use the controls opposite to the text to control properties of the text."); } static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *p) @@ -172,7 +174,7 @@ int main(void) makeAttributedString(); - mainwin = uiNewWindow("libui Histogram Example", 640, 480, 1); + mainwin = uiNewWindow("libui Text-Drawing Example", 640, 480, 1); uiWindowSetMargined(mainwin, 1); uiWindowOnClosing(mainwin, onClosing, NULL); From 8944a3fc5528445b9027b1294b6c86bae03eeb89 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 18 Mar 2018 15:11:03 -0400 Subject: [PATCH 0942/1329] Finally documented the remaining functions in ui_attrstr.h. --- _future/unittest/checklist_attrstr | 1 + ui_attrstr.h | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/_future/unittest/checklist_attrstr b/_future/unittest/checklist_attrstr index 47117def..7ba0f244 100644 --- a/_future/unittest/checklist_attrstr +++ b/_future/unittest/checklist_attrstr @@ -22,3 +22,4 @@ should uiNewFamilyAttribute() accept NULL it is an error in ForEach too invalid values for uiDrawTextAlign empty text layouts have one line +TODO figure out what to do if any field (particularly the font family name) in uiFontDescriptor is unset diff --git a/ui_attrstr.h b/ui_attrstr.h index 3486b4e9..66eaf1ce 100644 --- a/ui_attrstr.h +++ b/ui_attrstr.h @@ -488,11 +488,20 @@ _UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, dou // TODO number of lines visible for clipping rect, range visible for clipping rect? +// uiFontButton is a button that allows users to choose a font when they click on it. typedef struct uiFontButton uiFontButton; #define uiFontButton(this) ((uiFontButton *) (this)) +// uiFontButtonFont() returns the font currently selected in the uiFontButton in desc. +// uiFontButtonFont() allocates resources in desc; when you are done with the font, call uiFreeFontButtonFont() to release them. +// uiFontButtonFont() does not allocate desc itself; you must do so. // TODO have a function that sets an entire font descriptor to a range in a uiAttributedString at once, for SetFont? _UI_EXTERN void uiFontButtonFont(uiFontButton *b, uiFontDescriptor *desc); // TOOD SetFont, mechanics +// uiFontButtonOnChanged() sets the function that is called when the font in the uiFontButton is changed. _UI_EXTERN void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data); +// uiNewFontButton() creates a new uiFontButton. The default font selected into the uiFontButton is OS-defined. _UI_EXTERN uiFontButton *uiNewFontButton(void); +// uiFreeFontButtonFont() frees resources allocated in desc by uiFontButtonFont(). +// After calling uiFreeFontButtonFont(), the contents of desc should be assumed to be undefined (though since you allocate desc itself, you can safely reuse desc for other font descriptors). +// Calling uiFreeFontButtonFont() on a uiFontDescriptor not returned by uiFontButtonFont() results in undefined behavior. _UI_EXTERN void uiFreeFontButtonFont(uiFontDescriptor *desc); From a0d2d6a1f87a536580cc2a6b8915c90383d7f22a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 18 Mar 2018 15:30:50 -0400 Subject: [PATCH 0943/1329] Added alignment to the drawtext example. --- examples/drawtext/main.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c index 45549dff..3f6e403e 100644 --- a/examples/drawtext/main.c +++ b/examples/drawtext/main.c @@ -7,6 +7,7 @@ uiWindow *mainwin; uiArea *area; uiAreaHandler handler; uiFontButton *fontButton; +uiCombobox *alignment; uiAttributedString *attrstr; @@ -103,7 +104,7 @@ static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *p) uiFontButtonFont(fontButton, &defaultFont); params.DefaultFont = &defaultFont; params.Width = p->AreaWidth - (2 * margins); - params.Align = uiDrawTextAlignLeft; + params.Align = (uiDrawTextAlign) uiComboboxSelected(alignment); textLayout = uiDrawNewTextLayout(¶ms); // TODO clip to margins uiDrawText(p->Context, textLayout, margins, margins); @@ -137,6 +138,11 @@ static void onFontChanged(uiFontButton *b, void *data) uiAreaQueueRedrawAll(area); } +static void onComboboxSelected(uiCombobox *b, void *data) +{ + uiAreaQueueRedrawAll(area); +} + static int onClosing(uiWindow *w, void *data) { uiControlDestroy(uiControl(mainwin)); @@ -155,6 +161,7 @@ int main(void) uiInitOptions o; const char *err; uiBox *hbox, *vbox; + uiForm *form; handler.Draw = handlerDraw; handler.MouseEvent = handlerMouseEvent; @@ -190,6 +197,20 @@ int main(void) uiFontButtonOnChanged(fontButton, onFontChanged, NULL); uiBoxAppend(vbox, uiControl(fontButton), 0); + form = uiNewForm(); + uiFormSetPadded(form, 1); + // TODO on OS X if this is set to 1 then the window can't resize; does the form not have the concept of stretchy trailing space? + uiBoxAppend(vbox, uiControl(form), 0); + + alignment = uiNewCombobox(); + // note that the items match with the values of the uiDrawTextAlign values + uiComboboxAppend(alignment, "Left"); + uiComboboxAppend(alignment, "Center"); + uiComboboxAppend(alignment, "Right"); + uiComboboxSetSelected(alignment, 0); // start with left alignment + uiComboboxOnSelected(alignment, onComboboxSelected, NULL); + uiFormAppend(form, "Alignment", uiControl(alignment), 0); + area = uiNewArea(&handler); uiBoxAppend(hbox, uiControl(area), 1); From d788d86239a8b09d1040ccaa831ead03000b4166 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 18 Mar 2018 15:39:44 -0400 Subject: [PATCH 0944/1329] Removed the margins from the drawtext example. It looks better this way. --- examples/drawtext/main.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/examples/drawtext/main.c b/examples/drawtext/main.c index 3f6e403e..d94d2572 100644 --- a/examples/drawtext/main.c +++ b/examples/drawtext/main.c @@ -11,8 +11,6 @@ uiCombobox *alignment; uiAttributedString *attrstr; -#define margins 20 - static void appendWithAttribute(const char *what, uiAttribute *attr, uiAttribute *attr2) { size_t start, end; @@ -103,11 +101,10 @@ static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *p) params.String = attrstr; uiFontButtonFont(fontButton, &defaultFont); params.DefaultFont = &defaultFont; - params.Width = p->AreaWidth - (2 * margins); + params.Width = p->AreaWidth; params.Align = (uiDrawTextAlign) uiComboboxSelected(alignment); textLayout = uiDrawNewTextLayout(¶ms); - // TODO clip to margins - uiDrawText(p->Context, textLayout, margins, margins); + uiDrawText(p->Context, textLayout, 0, 0); uiDrawFreeTextLayout(textLayout); uiFreeFontButtonFont(&defaultFont); } From df03c09a9cab88f253b3bc4dccf8a6ceb879cf02 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 18 Mar 2018 15:40:45 -0400 Subject: [PATCH 0945/1329] More TODOs. --- darwin/fontmatch.m | 1 + 1 file changed, 1 insertion(+) diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index 5b56f258..ea075e53 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -4,6 +4,7 @@ // TODOs: // - switching from Skia to a non-fvar-based font crashes because the CTFontDescriptorRef we get has an empty variation dictionary for some reason... +// - Futura causes the Courier New in the drawtext example to be bold for some reason... // Core Text exposes font style info in two forms: // - Fonts with a QuickDraw GX font variation (fvar) table, a feature From c402dcac30eb8d8d11453b2601955796fc5fde5d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 18 Mar 2018 15:49:44 -0400 Subject: [PATCH 0946/1329] And finally got rid of ui_attrstr.h (it's now all in ui.h) and updated the README. Time to FINALLY merge this back in! --- README.md | 6 +- ui.h | 509 ++++++++++++++++++++++++++++++++++++++++++++++++++- ui_attrstr.h | 507 -------------------------------------------------- 3 files changed, 510 insertions(+), 512 deletions(-) delete mode 100644 ui_attrstr.h diff --git a/README.md b/README.md index a1fd57ad..41edf3c1 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,9 @@ 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). 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. +* **18 March 2018** + * 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](https://github.com/andlabs/libui/blob/8944a3fc5528445b9027b1294b6c86bae03eeb89/ui_attrstr.h). There is also a new examples for it: `drawtext`, which shows the whole API at a glance. It doesn't yet support measuring or manipulating text, nor does it currently support functions that would be necessary for things like text editors; all of this will be added back later. + * Also introduced a formal set of contribution guidelines, see `CONTRIBUTING.md` for details. They are still WIP. * **17 February 2018** * The longstanding Enter+Escape crashes on Windows have finally been fixed (thanks to @lxn). diff --git a/ui.h b/ui.h index 0f7dce64..e3f0c908 100644 --- a/ui.h +++ b/ui.h @@ -486,8 +486,513 @@ _UI_EXTERN void uiDrawClip(uiDrawContext *c, uiDrawPath *path); _UI_EXTERN void uiDrawSave(uiDrawContext *c); _UI_EXTERN void uiDrawRestore(uiDrawContext *c); -// TODO merge back in -#include "ui_attrstr.h" +// uiAttribute stores information about an attribute in a +// uiAttributedString. +// +// You do not create uiAttributes directly; instead, you create a +// uiAttribute of a given type using the specialized constructor +// functions. For every Unicode codepoint in the uiAttributedString, +// at most one value of each attribute type can be applied. +// +// uiAttributes are immutable and the uiAttributedString takes +// ownership of the uiAttribute object once assigned, copying its +// contents as necessary. +typedef struct uiAttribute uiAttribute; + +// uiFreeAttribute() frees a uiAttribute. You generally do not need to +// call this yourself, as uiAttributedString does this for you. In fact, +// it is an error to call this function on a uiAttribute that has been +// given to a uiAttributedString. You can call this, however, if you +// created a uiAttribute that you aren't going to use later. +_UI_EXTERN void uiFreeAttribute(uiAttribute *a); + +// uiAttributeType holds the possible uiAttribute types that may be +// returned by uiAttributeGetType(). Refer to the documentation for +// each type's constructor function for details on each type. +_UI_ENUM(uiAttributeType) { + uiAttributeTypeFamily, + uiAttributeTypeSize, + uiAttributeTypeWeight, + uiAttributeTypeItalic, + uiAttributeTypeStretch, + uiAttributeTypeColor, + uiAttributeTypeBackground, + uiAttributeTypeUnderline, + uiAttributeTypeUnderlineColor, + uiAttributeTypeFeatures, +}; + +// uiAttributeGetType() returns the type of a. +// TODO I don't like this name +_UI_EXTERN uiAttributeType uiAttributeGetType(const uiAttribute *a); + +// uiNewFamilyAttribute() creates a new uiAttribute that changes the +// font family of the text it is applied to. family is copied; you do not +// need to keep it alive after uiNewFamilyAttribute() returns. Font +// family names are case-insensitive. +_UI_EXTERN uiAttribute *uiNewFamilyAttribute(const char *family); + +// uiAttributeFamily() returns the font family stored in a. The +// returned string is owned by a. It is an error to call this on a +// uiAttribute that does not hold a font family. +_UI_EXTERN const char *uiAttributeFamily(const uiAttribute *a); + +// uiNewSizeAttribute() creates a new uiAttribute that changes the +// size of the text it is applied to, in typographical points. +_UI_EXTERN uiAttribute *uiNewSizeAttribute(double size); + +// uiAttributeSize() returns the font size stored in a. It is an error to +// call this on a uiAttribute that does not hold a font size. +_UI_EXTERN double uiAttributeSize(const uiAttribute *a); + +// uiTextWeight represents possible text weights. These roughly +// map to the OSx2 text weight field of TrueType and OpenType +// fonts, or to CSS weight numbers. The named constants are +// nominal values; the actual values may vary by font and by OS, +// though this isn't particularly likely. Any value between +// uiTextWeightMinimum and uiDrawTextWeightMaximum, +// inclusive, is allowed. +// +// Note that due to restrictions in early versions of Windows, some +// fonts have "special" weights be exposed in many programs as +// separate font families. This is perhaps most notable with +// Arial Black. libui does not do this, even on Windows (because the +// DirectWrite API libui uses on Windows does not do this); to +// specify Arial Black, use family Arial and weight uiTextWeightBlack. +_UI_ENUM(uiTextWeight) { + uiTextWeightMinimum = 0, + uiTextWeightThin = 100, + uiTextWeightUltraLight = 200, + uiTextWeightLight = 300, + uiTextWeightBook = 350, + uiTextWeightNormal = 400, + uiTextWeightMedium = 500, + uiTextWeightSemiBold = 600, + uiTextWeightBold = 700, + uiTextWeightUltraBold = 800, + uiTextWeightHeavy = 900, + uiTextWeightUltraHeavy = 950, + uiTextWeightMaximum = 1000, +}; + +// uiNewWeightAttribute() creates a new uiAttribute that changes the +// weight of the text it is applied to. It is an error to specify a weight +// outside the range [uiTextWeightMinimum, +// uiTextWeightMaximum]. +_UI_EXTERN uiAttribute *uiNewWeightAttribute(uiTextWeight weight); + +// uiAttributeWeight() returns the font weight stored in a. It is an error +// to call this on a uiAttribute that does not hold a font weight. +_UI_EXTERN uiTextWeight uiAttributeWeight(const uiAttribute *a); + +// uiTextItalic represents possible italic modes for a font. Italic +// represents "true" italics where the slanted glyphs have custom +// shapes, whereas oblique represents italics that are merely slanted +// versions of the normal glyphs. Most fonts usually have one or the +// other. +_UI_ENUM(uiTextItalic) { + uiTextItalicNormal, + uiTextItalicOblique, + uiTextItalicItalic, +}; + +// uiNewItalicAttribute() creates a new uiAttribute that changes the +// italic mode of the text it is applied to. It is an error to specify an +// italic mode not specified in uiTextItalic. +_UI_EXTERN uiAttribute *uiNewItalicAttribute(uiTextItalic italic); + +// uiAttributeItalic() returns the font italic mode stored in a. It is an +// error to call this on a uiAttribute that does not hold a font italic +// mode. +_UI_EXTERN uiTextItalic uiAttributeItalic(const uiAttribute *a); + +// uiTextStretch represents possible stretches (also called "widths") +// of a font. +// +// Note that due to restrictions in early versions of Windows, some +// fonts have "special" stretches be exposed in many programs as +// separate font families. This is perhaps most notable with +// Arial Condensed. libui does not do this, even on Windows (because +// the DirectWrite API libui uses on Windows does not do this); to +// specify Arial Condensed, use family Arial and stretch +// uiTextStretchCondensed. +_UI_ENUM(uiTextStretch) { + uiTextStretchUltraCondensed, + uiTextStretchExtraCondensed, + uiTextStretchCondensed, + uiTextStretchSemiCondensed, + uiTextStretchNormal, + uiTextStretchSemiExpanded, + uiTextStretchExpanded, + uiTextStretchExtraExpanded, + uiTextStretchUltraExpanded, +}; + +// uiNewStretchAttribute() creates a new uiAttribute that changes the +// stretch of the text it is applied to. It is an error to specify a strech +// not specified in uiTextStretch. +_UI_EXTERN uiAttribute *uiNewStretchAttribute(uiTextStretch stretch); + +// uiAttributeStretch() returns the font stretch stored in a. It is an +// error to call this on a uiAttribute that does not hold a font stretch. +_UI_EXTERN uiTextStretch uiAttributeStretch(const uiAttribute *a); + +// uiNewColorAttribute() creates a new uiAttribute that changes the +// color of the text it is applied to. It is an error to specify an invalid +// color. +_UI_EXTERN uiAttribute *uiNewColorAttribute(double r, double g, double b, double a); + +// uiAttributeColor() returns the text color stored in a. It is an +// error to call this on a uiAttribute that does not hold a text color. +_UI_EXTERN void uiAttributeColor(const uiAttribute *a, double *r, double *g, double *b, double *alpha); + +// uiNewBackgroundAttribute() creates a new uiAttribute that +// changes the background color of the text it is applied to. It is an +// error to specify an invalid color. +_UI_EXTERN uiAttribute *uiNewBackgroundAttribute(double r, double g, double b, double a); + +// TODO reuse uiAttributeColor() for background colors, or make a new function... + +// uiUnderline specifies a type of underline to use on text. +_UI_ENUM(uiUnderline) { + uiUnderlineNone, + uiUnderlineSingle, + uiUnderlineDouble, + uiUnderlineSuggestion, // wavy or dotted underlines used for spelling/grammar checkers +}; + +// uiNewUnderlineAttribute() creates a new uiAttribute that changes +// the type of underline on the text it is applied to. It is an error to +// specify an underline type not specified in uiUnderline. +_UI_EXTERN uiAttribute *uiNewUnderlineAttribute(uiUnderline u); + +// uiAttributeUnderline() returns the underline type stored in a. It is +// an error to call this on a uiAttribute that does not hold an underline +// style. +_UI_EXTERN uiUnderline uiAttributeUnderline(const uiAttribute *a); + +// uiUnderlineColor specifies the color of any underline on the text it +// is applied to, regardless of the type of underline. In addition to +// being able to specify a custom color, you can explicitly specify +// platform-specific colors for suggestion underlines; to use them +// correctly, pair them with uiUnderlineSuggestion (though they can +// be used on other types of underline as well). +// +// If an underline type is applied but no underline color is +// specified, the text color is used instead. If an underline color +// is specified without an underline type, the underline color +// attribute is ignored, but not removed from the uiAttributedString. +_UI_ENUM(uiUnderlineColor) { + uiUnderlineColorCustom, + uiUnderlineColorSpelling, + uiUnderlineColorGrammar, + uiUnderlineColorAuxiliary, // for instance, the color used by smart replacements on macOS or in Microsoft Office +}; + +// uiNewUnderlineColorAttribute() creates a new uiAttribute that +// changes the color of the underline on the text it is applied to. +// It is an error to specify an underline color not specified in +// uiUnderlineColor. +// +// If the specified color type is uiUnderlineColorCustom, it is an +// error to specify an invalid color value. Otherwise, the color values +// are ignored and should be specified as zero. +_UI_EXTERN uiAttribute *uiNewUnderlineColorAttribute(uiUnderlineColor u, double r, double g, double b, double a); + +// uiAttributeUnderlineColor() returns the underline color stored in +// a. It is an error to call this on a uiAttribute that does not hold an +// underline color. +_UI_EXTERN void uiAttributeUnderlineColor(const uiAttribute *a, uiUnderlineColor *u, double *r, double *g, double *b, double *alpha); + +// uiOpenTypeFeatures represents a set of OpenType feature +// tag-value pairs, for applying OpenType features to text. +// OpenType feature tags are four-character codes defined by +// OpenType that cover things from design features like small +// caps and swashes to language-specific glyph shapes and +// beyond. Each tag may only appear once in any given +// uiOpenTypeFeatures instance. Each value is a 32-bit integer, +// often used as a Boolean flag, but sometimes as an index to choose +// a glyph shape to use. +// +// If a font does not support a certain feature, that feature will be +// ignored. (TODO verify this on all OSs) +// +// See the OpenType specification at +// https://www.microsoft.com/typography/otspec/featuretags.htm +// for the complete list of available features, information on specific +// features, and how to use them. +// TODO invalid features +typedef struct uiOpenTypeFeatures uiOpenTypeFeatures; + +// uiOpenTypeFeaturesForEachFunc is the type of the function +// invoked by uiOpenTypeFeaturesForEach() for every OpenType +// feature in otf. Refer to that function's documentation for more +// details. +typedef uiForEach (*uiOpenTypeFeaturesForEachFunc)(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data); + +// @role uiOpenTypeFeatures constructor +// uiNewOpenTypeFeatures() returns a new uiOpenTypeFeatures +// instance, with no tags yet added. +_UI_EXTERN uiOpenTypeFeatures *uiNewOpenTypeFeatures(void); + +// @role uiOpenTypeFeatures destructor +// uiFreeOpenTypeFeatures() frees otf. +_UI_EXTERN void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf); + +// uiOpenTypeFeaturesClone() makes a copy of otf and returns it. +// Changing one will not affect the other. +_UI_EXTERN uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures *otf); + +// uiOpenTypeFeaturesAdd() adds the given feature tag and value +// to otf. The feature tag is specified by a, b, c, and d. If there is +// already a value associated with the specified tag in otf, the old +// value is removed. +_UI_EXTERN void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value); + +// uiOpenTypeFeaturesRemove() removes the given feature tag +// and value from otf. If the tag is not present in otf, +// uiOpenTypeFeaturesRemove() does nothing. +_UI_EXTERN void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d); + +// uiOpenTypeFeaturesGet() determines whether the given feature +// tag is present in otf. If it is, *value is set to the tag's value and +// nonzero is returned. Otherwise, zero is returned. +// +// Note that if uiOpenTypeFeaturesGet() returns zero, value isn't +// changed. This is important: if a feature is not present in a +// uiOpenTypeFeatures, the feature is NOT treated as if its +// value was zero anyway. Script-specific font shaping rules and +// font-specific feature settings may use a different default value +// for a feature. You should likewise not treat a missing feature as +// having a value of zero either. Instead, a missing feature should +// be treated as having some unspecified default value. +_UI_EXTERN int uiOpenTypeFeaturesGet(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value); + +// uiOpenTypeFeaturesForEach() executes f for every tag-value +// pair in otf. The enumeration order is unspecified. You cannot +// modify otf while uiOpenTypeFeaturesForEach() is running. +_UI_EXTERN void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data); + +// uiNewFeaturesAttribute() creates a new uiAttribute that changes +// the font family of the text it is applied to. otf is copied; you may +// free it after uiNewFeaturesAttribute() returns. +_UI_EXTERN uiAttribute *uiNewFeaturesAttribute(const uiOpenTypeFeatures *otf); + +// uiAttributeFeatures() returns the OpenType features stored in a. +// The returned uiOpenTypeFeatures object is owned by a. It is an +// error to call this on a uiAttribute that does not hold OpenType +// features. +_UI_EXTERN const uiOpenTypeFeatures *uiAttributeFeatures(const uiAttribute *a); + +// uiAttributedString represents a string of UTF-8 text that can +// optionally be embellished with formatting attributes. libui +// provides the list of formatting attributes, which cover common +// formatting traits like boldface and color as well as advanced +// typographical features provided by OpenType like superscripts +// and small caps. These attributes can be combined in a variety of +// ways. +// +// Attributes are applied to runs of Unicode codepoints in the string. +// Zero-length runs are elided. Consecutive runs that have the same +// attribute type and value are merged. Each attribute is independent +// of each other attribute; overlapping attributes of different types +// do not split each other apart, but different values of the same +// attribute type do. +// +// The empty string can also be represented by uiAttributedString, +// but because of the no-zero-length-attribute rule, it will not have +// attributes. +// +// A uiAttributedString takes ownership of all attributes given to +// it, as it may need to duplicate or delete uiAttribute objects at +// any time. By extension, when you free a uiAttributedString, +// all uiAttributes within will also be freed. Each method will +// describe its own rules in more details. +// +// In addition, uiAttributedString provides facilities for moving +// between grapheme clusters, which represent a character +// from the point of view of the end user. The cursor of a text editor +// is always placed on a grapheme boundary, so you can use these +// features to move the cursor left or right by one "character". +// TODO does uiAttributedString itself need this +// +// uiAttributedString does not provide enough information to be able +// to draw itself onto a uiDrawContext or respond to user actions. +// In order to do that, you'll need to use a uiDrawTextLayout, which +// is built from the combination of a uiAttributedString and a set of +// layout-specific properties. +typedef struct uiAttributedString uiAttributedString; + +// uiAttributedStringForEachAttributeFunc is the type of the function +// invoked by uiAttributedStringForEachAttribute() for every +// attribute in s. Refer to that function's documentation for more +// details. +typedef uiForEach (*uiAttributedStringForEachAttributeFunc)(const uiAttributedString *s, const uiAttribute *a, size_t start, size_t end, void *data); + +// @role uiAttributedString constructor +// uiNewAttributedString() creates a new uiAttributedString from +// initialString. The string will be entirely unattributed. +_UI_EXTERN uiAttributedString *uiNewAttributedString(const char *initialString); + +// @role uiAttributedString destructor +// uiFreeAttributedString() destroys the uiAttributedString s. +// It will also free all uiAttributes within. +_UI_EXTERN void uiFreeAttributedString(uiAttributedString *s); + +// uiAttributedStringString() returns the textual content of s as a +// '\0'-terminated UTF-8 string. The returned pointer is valid until +// the next change to the textual content of s. +_UI_EXTERN const char *uiAttributedStringString(const uiAttributedString *s); + +// uiAttributedStringLength() returns the number of UTF-8 bytes in +// the textual content of s, excluding the terminating '\0'. +_UI_EXTERN size_t uiAttributedStringLen(const uiAttributedString *s); + +// uiAttributedStringAppendUnattributed() adds the '\0'-terminated +// UTF-8 string str to the end of s. The new substring will be +// unattributed. +_UI_EXTERN void uiAttributedStringAppendUnattributed(uiAttributedString *s, const char *str); + +// uiAttributedStringInsertAtUnattributed() adds the '\0'-terminated +// UTF-8 string str to s at the byte position specified by at. The new +// substring will be unattributed; existing attributes will be moved +// along with their text. +_UI_EXTERN void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *str, size_t at); + +// TODO add the Append and InsertAtExtendingAttributes functions +// TODO and add functions that take a string + length + +// uiAttributedStringDelete() deletes the characters and attributes of +// s in the byte range [start, end). +_UI_EXTERN void uiAttributedStringDelete(uiAttributedString *s, size_t start, size_t end); + +// TODO add a function to uiAttributedString to get an attribute's value at a specific index or in a specific range, so we can edit + +// uiAttributedStringSetAttribute() sets a in the byte range [start, end) +// of s. Any existing attributes in that byte range of the same type are +// removed. s takes ownership of a; you should not use it after +// uiAttributedStringSetAttribute() returns. +_UI_EXTERN void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttribute *a, size_t start, size_t end); + +// uiAttributedStringForEachAttribute() enumerates all the +// uiAttributes in s. It is an error to modify s in f. Within f, s still +// owns the attribute; you can neither free it nor save it for later +// use. +// TODO reword the above for consistency (TODO and find out what I meant by that) +// TODO define an enumeration order (or mark it as undefined); also define how consecutive runs of identical attributes are handled here and sync with the definition of uiAttributedString itself +_UI_EXTERN void uiAttributedStringForEachAttribute(const uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); + +// TODO const correct this somehow (the implementation needs to mutate the structure) +_UI_EXTERN size_t uiAttributedStringNumGraphemes(uiAttributedString *s); + +// TODO const correct this somehow (the implementation needs to mutate the structure) +_UI_EXTERN size_t uiAttributedStringByteIndexToGrapheme(uiAttributedString *s, size_t pos); + +// TODO const correct this somehow (the implementation needs to mutate the structure) +_UI_EXTERN size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos); + +// uiFontDescriptor provides a complete description of a font where +// one is needed. Currently, this means as the default font of a +// uiDrawTextLayout and as the data returned by uiFontButton. +// All the members operate like the respective uiAttributes. +typedef struct uiFontDescriptor uiFontDescriptor; + +struct uiFontDescriptor { + // TODO const-correct this or figure out how to deal with this when getting a value + char *Family; + double Size; + uiTextWeight Weight; + uiTextItalic Italic; + uiTextStretch Stretch; +}; + +// uiDrawTextLayout is a concrete representation of a +// uiAttributedString that can be displayed in a uiDrawContext. +// It includes information important for the drawing of a block of +// text, including the bounding box to wrap the text within, the +// alignment of lines of text within that box, areas to mark as +// being selected, and other things. +// +// Unlike uiAttributedString, the content of a uiDrawTextLayout is +// immutable once it has been created. +// +// TODO talk about OS-specific differences with text drawing that libui can't account for... +typedef struct uiDrawTextLayout uiDrawTextLayout; + +// uiDrawTextAlign specifies the alignment of lines of text in a +// uiDrawTextLayout. +// TODO should this really have Draw in the name? +_UI_ENUM(uiDrawTextAlign) { + uiDrawTextAlignLeft, + uiDrawTextAlignCenter, + uiDrawTextAlignRight, +}; + +// uiDrawTextLayoutParams describes a uiDrawTextLayout. +// DefaultFont is used to render any text that is not attributed +// sufficiently in String. Width determines the width of the bounding +// box of the text; the height is determined automatically. +typedef struct uiDrawTextLayoutParams uiDrawTextLayoutParams; + +// TODO const-correct this somehow +struct uiDrawTextLayoutParams { + uiAttributedString *String; + uiFontDescriptor *DefaultFont; + double Width; + uiDrawTextAlign Align; +}; + +// @role uiDrawTextLayout constructor +// uiDrawNewTextLayout() creates a new uiDrawTextLayout from +// the given parameters. +// +// TODO +// - allow creating a layout out of a substring +// - allow marking compositon strings +// - allow marking selections, even after creation +// - add the following functions: +// - uiDrawTextLayoutHeightForWidth() (returns the height that a layout would need to be to display the entire string at a given width) +// - uiDrawTextLayoutRangeForSize() (returns what substring would fit in a given size) +// - uiDrawTextLayoutNewWithHeight() (limits amount of string used by the height) +// - some function to fix up a range (for text editing) +_UI_EXTERN uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *params); + +// @role uiDrawFreeTextLayout destructor +// uiDrawFreeTextLayout() frees tl. The underlying +// uiAttributedString is not freed. +_UI_EXTERN void uiDrawFreeTextLayout(uiDrawTextLayout *tl); + +// uiDrawText() draws tl in c with the top-left point of tl at (x, y). +_UI_EXTERN void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y); + +// uiDrawTextLayoutExtents() returns the width and height of tl +// in width and height. The returned width may be smaller than +// the width passed into uiDrawNewTextLayout() depending on +// how the text in tl is wrapped. Therefore, you can use this +// function to get the actual size of the text layout. +_UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height); + +// TODO metrics functions + +// TODO number of lines visible for clipping rect, range visible for clipping rect? + +// uiFontButton is a button that allows users to choose a font when they click on it. +typedef struct uiFontButton uiFontButton; +#define uiFontButton(this) ((uiFontButton *) (this)) +// uiFontButtonFont() returns the font currently selected in the uiFontButton in desc. +// uiFontButtonFont() allocates resources in desc; when you are done with the font, call uiFreeFontButtonFont() to release them. +// uiFontButtonFont() does not allocate desc itself; you must do so. +// TODO have a function that sets an entire font descriptor to a range in a uiAttributedString at once, for SetFont? +_UI_EXTERN void uiFontButtonFont(uiFontButton *b, uiFontDescriptor *desc); +// TOOD SetFont, mechanics +// uiFontButtonOnChanged() sets the function that is called when the font in the uiFontButton is changed. +_UI_EXTERN void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data); +// uiNewFontButton() creates a new uiFontButton. The default font selected into the uiFontButton is OS-defined. +_UI_EXTERN uiFontButton *uiNewFontButton(void); +// uiFreeFontButtonFont() frees resources allocated in desc by uiFontButtonFont(). +// After calling uiFreeFontButtonFont(), the contents of desc should be assumed to be undefined (though since you allocate desc itself, you can safely reuse desc for other font descriptors). +// Calling uiFreeFontButtonFont() on a uiFontDescriptor not returned by uiFontButtonFont() results in undefined behavior. +_UI_EXTERN void uiFreeFontButtonFont(uiFontDescriptor *desc); _UI_ENUM(uiModifiers) { uiModifierCtrl = 1 << 0, diff --git a/ui_attrstr.h b/ui_attrstr.h deleted file mode 100644 index 66eaf1ce..00000000 --- a/ui_attrstr.h +++ /dev/null @@ -1,507 +0,0 @@ -// uiAttribute stores information about an attribute in a -// uiAttributedString. -// -// You do not create uiAttributes directly; instead, you create a -// uiAttribute of a given type using the specialized constructor -// functions. For every Unicode codepoint in the uiAttributedString, -// at most one value of each attribute type can be applied. -// -// uiAttributes are immutable and the uiAttributedString takes -// ownership of the uiAttribute object once assigned, copying its -// contents as necessary. -typedef struct uiAttribute uiAttribute; - -// uiFreeAttribute() frees a uiAttribute. You generally do not need to -// call this yourself, as uiAttributedString does this for you. In fact, -// it is an error to call this function on a uiAttribute that has been -// given to a uiAttributedString. You can call this, however, if you -// created a uiAttribute that you aren't going to use later. -_UI_EXTERN void uiFreeAttribute(uiAttribute *a); - -// uiAttributeType holds the possible uiAttribute types that may be -// returned by uiAttributeGetType(). Refer to the documentation for -// each type's constructor function for details on each type. -_UI_ENUM(uiAttributeType) { - uiAttributeTypeFamily, - uiAttributeTypeSize, - uiAttributeTypeWeight, - uiAttributeTypeItalic, - uiAttributeTypeStretch, - uiAttributeTypeColor, - uiAttributeTypeBackground, - uiAttributeTypeUnderline, - uiAttributeTypeUnderlineColor, - uiAttributeTypeFeatures, -}; - -// uiAttributeGetType() returns the type of a. -// TODO I don't like this name -_UI_EXTERN uiAttributeType uiAttributeGetType(const uiAttribute *a); - -// uiNewFamilyAttribute() creates a new uiAttribute that changes the -// font family of the text it is applied to. family is copied; you do not -// need to keep it alive after uiNewFamilyAttribute() returns. Font -// family names are case-insensitive. -_UI_EXTERN uiAttribute *uiNewFamilyAttribute(const char *family); - -// uiAttributeFamily() returns the font family stored in a. The -// returned string is owned by a. It is an error to call this on a -// uiAttribute that does not hold a font family. -_UI_EXTERN const char *uiAttributeFamily(const uiAttribute *a); - -// uiNewSizeAttribute() creates a new uiAttribute that changes the -// size of the text it is applied to, in typographical points. -_UI_EXTERN uiAttribute *uiNewSizeAttribute(double size); - -// uiAttributeSize() returns the font size stored in a. It is an error to -// call this on a uiAttribute that does not hold a font size. -_UI_EXTERN double uiAttributeSize(const uiAttribute *a); - -// uiTextWeight represents possible text weights. These roughly -// map to the OSx2 text weight field of TrueType and OpenType -// fonts, or to CSS weight numbers. The named constants are -// nominal values; the actual values may vary by font and by OS, -// though this isn't particularly likely. Any value between -// uiTextWeightMinimum and uiDrawTextWeightMaximum, -// inclusive, is allowed. -// -// Note that due to restrictions in early versions of Windows, some -// fonts have "special" weights be exposed in many programs as -// separate font families. This is perhaps most notable with -// Arial Black. libui does not do this, even on Windows (because the -// DirectWrite API libui uses on Windows does not do this); to -// specify Arial Black, use family Arial and weight uiTextWeightBlack. -_UI_ENUM(uiTextWeight) { - uiTextWeightMinimum = 0, - uiTextWeightThin = 100, - uiTextWeightUltraLight = 200, - uiTextWeightLight = 300, - uiTextWeightBook = 350, - uiTextWeightNormal = 400, - uiTextWeightMedium = 500, - uiTextWeightSemiBold = 600, - uiTextWeightBold = 700, - uiTextWeightUltraBold = 800, - uiTextWeightHeavy = 900, - uiTextWeightUltraHeavy = 950, - uiTextWeightMaximum = 1000, -}; - -// uiNewWeightAttribute() creates a new uiAttribute that changes the -// weight of the text it is applied to. It is an error to specify a weight -// outside the range [uiTextWeightMinimum, -// uiTextWeightMaximum]. -_UI_EXTERN uiAttribute *uiNewWeightAttribute(uiTextWeight weight); - -// uiAttributeWeight() returns the font weight stored in a. It is an error -// to call this on a uiAttribute that does not hold a font weight. -_UI_EXTERN uiTextWeight uiAttributeWeight(const uiAttribute *a); - -// uiTextItalic represents possible italic modes for a font. Italic -// represents "true" italics where the slanted glyphs have custom -// shapes, whereas oblique represents italics that are merely slanted -// versions of the normal glyphs. Most fonts usually have one or the -// other. -_UI_ENUM(uiTextItalic) { - uiTextItalicNormal, - uiTextItalicOblique, - uiTextItalicItalic, -}; - -// uiNewItalicAttribute() creates a new uiAttribute that changes the -// italic mode of the text it is applied to. It is an error to specify an -// italic mode not specified in uiTextItalic. -_UI_EXTERN uiAttribute *uiNewItalicAttribute(uiTextItalic italic); - -// uiAttributeItalic() returns the font italic mode stored in a. It is an -// error to call this on a uiAttribute that does not hold a font italic -// mode. -_UI_EXTERN uiTextItalic uiAttributeItalic(const uiAttribute *a); - -// uiTextStretch represents possible stretches (also called "widths") -// of a font. -// -// Note that due to restrictions in early versions of Windows, some -// fonts have "special" stretches be exposed in many programs as -// separate font families. This is perhaps most notable with -// Arial Condensed. libui does not do this, even on Windows (because -// the DirectWrite API libui uses on Windows does not do this); to -// specify Arial Condensed, use family Arial and stretch -// uiTextStretchCondensed. -_UI_ENUM(uiTextStretch) { - uiTextStretchUltraCondensed, - uiTextStretchExtraCondensed, - uiTextStretchCondensed, - uiTextStretchSemiCondensed, - uiTextStretchNormal, - uiTextStretchSemiExpanded, - uiTextStretchExpanded, - uiTextStretchExtraExpanded, - uiTextStretchUltraExpanded, -}; - -// uiNewStretchAttribute() creates a new uiAttribute that changes the -// stretch of the text it is applied to. It is an error to specify a strech -// not specified in uiTextStretch. -_UI_EXTERN uiAttribute *uiNewStretchAttribute(uiTextStretch stretch); - -// uiAttributeStretch() returns the font stretch stored in a. It is an -// error to call this on a uiAttribute that does not hold a font stretch. -_UI_EXTERN uiTextStretch uiAttributeStretch(const uiAttribute *a); - -// uiNewColorAttribute() creates a new uiAttribute that changes the -// color of the text it is applied to. It is an error to specify an invalid -// color. -_UI_EXTERN uiAttribute *uiNewColorAttribute(double r, double g, double b, double a); - -// uiAttributeColor() returns the text color stored in a. It is an -// error to call this on a uiAttribute that does not hold a text color. -_UI_EXTERN void uiAttributeColor(const uiAttribute *a, double *r, double *g, double *b, double *alpha); - -// uiNewBackgroundAttribute() creates a new uiAttribute that -// changes the background color of the text it is applied to. It is an -// error to specify an invalid color. -_UI_EXTERN uiAttribute *uiNewBackgroundAttribute(double r, double g, double b, double a); - -// TODO reuse uiAttributeColor() for background colors, or make a new function... - -// uiUnderline specifies a type of underline to use on text. -_UI_ENUM(uiUnderline) { - uiUnderlineNone, - uiUnderlineSingle, - uiUnderlineDouble, - uiUnderlineSuggestion, // wavy or dotted underlines used for spelling/grammar checkers -}; - -// uiNewUnderlineAttribute() creates a new uiAttribute that changes -// the type of underline on the text it is applied to. It is an error to -// specify an underline type not specified in uiUnderline. -_UI_EXTERN uiAttribute *uiNewUnderlineAttribute(uiUnderline u); - -// uiAttributeUnderline() returns the underline type stored in a. It is -// an error to call this on a uiAttribute that does not hold an underline -// style. -_UI_EXTERN uiUnderline uiAttributeUnderline(const uiAttribute *a); - -// uiUnderlineColor specifies the color of any underline on the text it -// is applied to, regardless of the type of underline. In addition to -// being able to specify a custom color, you can explicitly specify -// platform-specific colors for suggestion underlines; to use them -// correctly, pair them with uiUnderlineSuggestion (though they can -// be used on other types of underline as well). -// -// If an underline type is applied but no underline color is -// specified, the text color is used instead. If an underline color -// is specified without an underline type, the underline color -// attribute is ignored, but not removed from the uiAttributedString. -_UI_ENUM(uiUnderlineColor) { - uiUnderlineColorCustom, - uiUnderlineColorSpelling, - uiUnderlineColorGrammar, - uiUnderlineColorAuxiliary, // for instance, the color used by smart replacements on macOS or in Microsoft Office -}; - -// uiNewUnderlineColorAttribute() creates a new uiAttribute that -// changes the color of the underline on the text it is applied to. -// It is an error to specify an underline color not specified in -// uiUnderlineColor. -// -// If the specified color type is uiUnderlineColorCustom, it is an -// error to specify an invalid color value. Otherwise, the color values -// are ignored and should be specified as zero. -_UI_EXTERN uiAttribute *uiNewUnderlineColorAttribute(uiUnderlineColor u, double r, double g, double b, double a); - -// uiAttributeUnderlineColor() returns the underline color stored in -// a. It is an error to call this on a uiAttribute that does not hold an -// underline color. -_UI_EXTERN void uiAttributeUnderlineColor(const uiAttribute *a, uiUnderlineColor *u, double *r, double *g, double *b, double *alpha); - -// uiOpenTypeFeatures represents a set of OpenType feature -// tag-value pairs, for applying OpenType features to text. -// OpenType feature tags are four-character codes defined by -// OpenType that cover things from design features like small -// caps and swashes to language-specific glyph shapes and -// beyond. Each tag may only appear once in any given -// uiOpenTypeFeatures instance. Each value is a 32-bit integer, -// often used as a Boolean flag, but sometimes as an index to choose -// a glyph shape to use. -// -// If a font does not support a certain feature, that feature will be -// ignored. (TODO verify this on all OSs) -// -// See the OpenType specification at -// https://www.microsoft.com/typography/otspec/featuretags.htm -// for the complete list of available features, information on specific -// features, and how to use them. -// TODO invalid features -typedef struct uiOpenTypeFeatures uiOpenTypeFeatures; - -// uiOpenTypeFeaturesForEachFunc is the type of the function -// invoked by uiOpenTypeFeaturesForEach() for every OpenType -// feature in otf. Refer to that function's documentation for more -// details. -typedef uiForEach (*uiOpenTypeFeaturesForEachFunc)(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data); - -// @role uiOpenTypeFeatures constructor -// uiNewOpenTypeFeatures() returns a new uiOpenTypeFeatures -// instance, with no tags yet added. -_UI_EXTERN uiOpenTypeFeatures *uiNewOpenTypeFeatures(void); - -// @role uiOpenTypeFeatures destructor -// uiFreeOpenTypeFeatures() frees otf. -_UI_EXTERN void uiFreeOpenTypeFeatures(uiOpenTypeFeatures *otf); - -// uiOpenTypeFeaturesClone() makes a copy of otf and returns it. -// Changing one will not affect the other. -_UI_EXTERN uiOpenTypeFeatures *uiOpenTypeFeaturesClone(const uiOpenTypeFeatures *otf); - -// uiOpenTypeFeaturesAdd() adds the given feature tag and value -// to otf. The feature tag is specified by a, b, c, and d. If there is -// already a value associated with the specified tag in otf, the old -// value is removed. -_UI_EXTERN void uiOpenTypeFeaturesAdd(uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value); - -// uiOpenTypeFeaturesRemove() removes the given feature tag -// and value from otf. If the tag is not present in otf, -// uiOpenTypeFeaturesRemove() does nothing. -_UI_EXTERN void uiOpenTypeFeaturesRemove(uiOpenTypeFeatures *otf, char a, char b, char c, char d); - -// uiOpenTypeFeaturesGet() determines whether the given feature -// tag is present in otf. If it is, *value is set to the tag's value and -// nonzero is returned. Otherwise, zero is returned. -// -// Note that if uiOpenTypeFeaturesGet() returns zero, value isn't -// changed. This is important: if a feature is not present in a -// uiOpenTypeFeatures, the feature is NOT treated as if its -// value was zero anyway. Script-specific font shaping rules and -// font-specific feature settings may use a different default value -// for a feature. You should likewise not treat a missing feature as -// having a value of zero either. Instead, a missing feature should -// be treated as having some unspecified default value. -_UI_EXTERN int uiOpenTypeFeaturesGet(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t *value); - -// uiOpenTypeFeaturesForEach() executes f for every tag-value -// pair in otf. The enumeration order is unspecified. You cannot -// modify otf while uiOpenTypeFeaturesForEach() is running. -_UI_EXTERN void uiOpenTypeFeaturesForEach(const uiOpenTypeFeatures *otf, uiOpenTypeFeaturesForEachFunc f, void *data); - -// uiNewFeaturesAttribute() creates a new uiAttribute that changes -// the font family of the text it is applied to. otf is copied; you may -// free it after uiNewFeaturesAttribute() returns. -_UI_EXTERN uiAttribute *uiNewFeaturesAttribute(const uiOpenTypeFeatures *otf); - -// uiAttributeFeatures() returns the OpenType features stored in a. -// The returned uiOpenTypeFeatures object is owned by a. It is an -// error to call this on a uiAttribute that does not hold OpenType -// features. -_UI_EXTERN const uiOpenTypeFeatures *uiAttributeFeatures(const uiAttribute *a); - -// uiAttributedString represents a string of UTF-8 text that can -// optionally be embellished with formatting attributes. libui -// provides the list of formatting attributes, which cover common -// formatting traits like boldface and color as well as advanced -// typographical features provided by OpenType like superscripts -// and small caps. These attributes can be combined in a variety of -// ways. -// -// Attributes are applied to runs of Unicode codepoints in the string. -// Zero-length runs are elided. Consecutive runs that have the same -// attribute type and value are merged. Each attribute is independent -// of each other attribute; overlapping attributes of different types -// do not split each other apart, but different values of the same -// attribute type do. -// -// The empty string can also be represented by uiAttributedString, -// but because of the no-zero-length-attribute rule, it will not have -// attributes. -// -// A uiAttributedString takes ownership of all attributes given to -// it, as it may need to duplicate or delete uiAttribute objects at -// any time. By extension, when you free a uiAttributedString, -// all uiAttributes within will also be freed. Each method will -// describe its own rules in more details. -// -// In addition, uiAttributedString provides facilities for moving -// between grapheme clusters, which represent a character -// from the point of view of the end user. The cursor of a text editor -// is always placed on a grapheme boundary, so you can use these -// features to move the cursor left or right by one "character". -// TODO does uiAttributedString itself need this -// -// uiAttributedString does not provide enough information to be able -// to draw itself onto a uiDrawContext or respond to user actions. -// In order to do that, you'll need to use a uiDrawTextLayout, which -// is built from the combination of a uiAttributedString and a set of -// layout-specific properties. -typedef struct uiAttributedString uiAttributedString; - -// uiAttributedStringForEachAttributeFunc is the type of the function -// invoked by uiAttributedStringForEachAttribute() for every -// attribute in s. Refer to that function's documentation for more -// details. -typedef uiForEach (*uiAttributedStringForEachAttributeFunc)(const uiAttributedString *s, const uiAttribute *a, size_t start, size_t end, void *data); - -// @role uiAttributedString constructor -// uiNewAttributedString() creates a new uiAttributedString from -// initialString. The string will be entirely unattributed. -_UI_EXTERN uiAttributedString *uiNewAttributedString(const char *initialString); - -// @role uiAttributedString destructor -// uiFreeAttributedString() destroys the uiAttributedString s. -// It will also free all uiAttributes within. -_UI_EXTERN void uiFreeAttributedString(uiAttributedString *s); - -// uiAttributedStringString() returns the textual content of s as a -// '\0'-terminated UTF-8 string. The returned pointer is valid until -// the next change to the textual content of s. -_UI_EXTERN const char *uiAttributedStringString(const uiAttributedString *s); - -// uiAttributedStringLength() returns the number of UTF-8 bytes in -// the textual content of s, excluding the terminating '\0'. -_UI_EXTERN size_t uiAttributedStringLen(const uiAttributedString *s); - -// uiAttributedStringAppendUnattributed() adds the '\0'-terminated -// UTF-8 string str to the end of s. The new substring will be -// unattributed. -_UI_EXTERN void uiAttributedStringAppendUnattributed(uiAttributedString *s, const char *str); - -// uiAttributedStringInsertAtUnattributed() adds the '\0'-terminated -// UTF-8 string str to s at the byte position specified by at. The new -// substring will be unattributed; existing attributes will be moved -// along with their text. -_UI_EXTERN void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *str, size_t at); - -// TODO add the Append and InsertAtExtendingAttributes functions -// TODO and add functions that take a string + length - -// uiAttributedStringDelete() deletes the characters and attributes of -// s in the byte range [start, end). -_UI_EXTERN void uiAttributedStringDelete(uiAttributedString *s, size_t start, size_t end); - -// TODO add a function to uiAttributedString to get an attribute's value at a specific index or in a specific range, so we can edit - -// uiAttributedStringSetAttribute() sets a in the byte range [start, end) -// of s. Any existing attributes in that byte range of the same type are -// removed. s takes ownership of a; you should not use it after -// uiAttributedStringSetAttribute() returns. -_UI_EXTERN void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttribute *a, size_t start, size_t end); - -// uiAttributedStringForEachAttribute() enumerates all the -// uiAttributes in s. It is an error to modify s in f. Within f, s still -// owns the attribute; you can neither free it nor save it for later -// use. -// TODO reword the above for consistency (TODO and find out what I meant by that) -// TODO define an enumeration order (or mark it as undefined); also define how consecutive runs of identical attributes are handled here and sync with the definition of uiAttributedString itself -_UI_EXTERN void uiAttributedStringForEachAttribute(const uiAttributedString *s, uiAttributedStringForEachAttributeFunc f, void *data); - -// TODO const correct this somehow (the implementation needs to mutate the structure) -_UI_EXTERN size_t uiAttributedStringNumGraphemes(uiAttributedString *s); - -// TODO const correct this somehow (the implementation needs to mutate the structure) -_UI_EXTERN size_t uiAttributedStringByteIndexToGrapheme(uiAttributedString *s, size_t pos); - -// TODO const correct this somehow (the implementation needs to mutate the structure) -_UI_EXTERN size_t uiAttributedStringGraphemeToByteIndex(uiAttributedString *s, size_t pos); - -// uiFontDescriptor provides a complete description of a font where -// one is needed. Currently, this means as the default font of a -// uiDrawTextLayout and as the data returned by uiFontButton. -// All the members operate like the respective uiAttributes. -typedef struct uiFontDescriptor uiFontDescriptor; - -struct uiFontDescriptor { - // TODO const-correct this or figure out how to deal with this when getting a value - char *Family; - double Size; - uiTextWeight Weight; - uiTextItalic Italic; - uiTextStretch Stretch; -}; - -// uiDrawTextLayout is a concrete representation of a -// uiAttributedString that can be displayed in a uiDrawContext. -// It includes information important for the drawing of a block of -// text, including the bounding box to wrap the text within, the -// alignment of lines of text within that box, areas to mark as -// being selected, and other things. -// -// Unlike uiAttributedString, the content of a uiDrawTextLayout is -// immutable once it has been created. -// -// TODO talk about OS-specific differences with text drawing that libui can't account for... -typedef struct uiDrawTextLayout uiDrawTextLayout; - -// uiDrawTextAlign specifies the alignment of lines of text in a -// uiDrawTextLayout. -// TODO should this really have Draw in the name? -_UI_ENUM(uiDrawTextAlign) { - uiDrawTextAlignLeft, - uiDrawTextAlignCenter, - uiDrawTextAlignRight, -}; - -// uiDrawTextLayoutParams describes a uiDrawTextLayout. -// DefaultFont is used to render any text that is not attributed -// sufficiently in String. Width determines the width of the bounding -// box of the text; the height is determined automatically. -typedef struct uiDrawTextLayoutParams uiDrawTextLayoutParams; - -// TODO const-correct this somehow -struct uiDrawTextLayoutParams { - uiAttributedString *String; - uiFontDescriptor *DefaultFont; - double Width; - uiDrawTextAlign Align; -}; - -// @role uiDrawTextLayout constructor -// uiDrawNewTextLayout() creates a new uiDrawTextLayout from -// the given parameters. -// -// TODO -// - allow creating a layout out of a substring -// - allow marking compositon strings -// - allow marking selections, even after creation -// - add the following functions: -// - uiDrawTextLayoutHeightForWidth() (returns the height that a layout would need to be to display the entire string at a given width) -// - uiDrawTextLayoutRangeForSize() (returns what substring would fit in a given size) -// - uiDrawTextLayoutNewWithHeight() (limits amount of string used by the height) -// - some function to fix up a range (for text editing) -_UI_EXTERN uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *params); - -// @role uiDrawFreeTextLayout destructor -// uiDrawFreeTextLayout() frees tl. The underlying -// uiAttributedString is not freed. -_UI_EXTERN void uiDrawFreeTextLayout(uiDrawTextLayout *tl); - -// uiDrawText() draws tl in c with the top-left point of tl at (x, y). -_UI_EXTERN void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y); - -// uiDrawTextLayoutExtents() returns the width and height of tl -// in width and height. The returned width may be smaller than -// the width passed into uiDrawNewTextLayout() depending on -// how the text in tl is wrapped. Therefore, you can use this -// function to get the actual size of the text layout. -_UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height); - -// TODO metrics functions - -// TODO number of lines visible for clipping rect, range visible for clipping rect? - -// uiFontButton is a button that allows users to choose a font when they click on it. -typedef struct uiFontButton uiFontButton; -#define uiFontButton(this) ((uiFontButton *) (this)) -// uiFontButtonFont() returns the font currently selected in the uiFontButton in desc. -// uiFontButtonFont() allocates resources in desc; when you are done with the font, call uiFreeFontButtonFont() to release them. -// uiFontButtonFont() does not allocate desc itself; you must do so. -// TODO have a function that sets an entire font descriptor to a range in a uiAttributedString at once, for SetFont? -_UI_EXTERN void uiFontButtonFont(uiFontButton *b, uiFontDescriptor *desc); -// TOOD SetFont, mechanics -// uiFontButtonOnChanged() sets the function that is called when the font in the uiFontButton is changed. -_UI_EXTERN void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data); -// uiNewFontButton() creates a new uiFontButton. The default font selected into the uiFontButton is OS-defined. -_UI_EXTERN uiFontButton *uiNewFontButton(void); -// uiFreeFontButtonFont() frees resources allocated in desc by uiFontButtonFont(). -// After calling uiFreeFontButtonFont(), the contents of desc should be assumed to be undefined (though since you allocate desc itself, you can safely reuse desc for other font descriptors). -// Calling uiFreeFontButtonFont() on a uiFontDescriptor not returned by uiFontButtonFont() results in undefined behavior. -_UI_EXTERN void uiFreeFontButtonFont(uiFontDescriptor *desc); From 8d6e41e1998e500bc0f581abd66648e5028de7c5 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 18 Mar 2018 15:54:55 -0400 Subject: [PATCH 0947/1329] Oops, missed a spot --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 41edf3c1..472781db 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ This README is being written.
* **18 March 2018** * 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](https://github.com/andlabs/libui/blob/8944a3fc5528445b9027b1294b6c86bae03eeb89/ui_attrstr.h). There is also a new examples for it: `drawtext`, which shows the whole API at a glance. It doesn't yet support measuring or manipulating text, nor does it currently support functions that would be necessary for things like text editors; all of this will be added back later. + * libui also now uses my [utf library](https://github.com/andlabs/utf) for UTF-8 and UTF-16 processing, to allow consistent behavior across platforms. This usage is not completely propagated throughout libui, but the Windows port uses it in most places now, and eventually this will become what libui will use throughout. * Also introduced a formal set of contribution guidelines, see `CONTRIBUTING.md` for details. They are still WIP. * **17 February 2018** From 3e76d799b18933226b511a8941fb9589cb873c88 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 20 Mar 2018 01:58:34 -0400 Subject: [PATCH 0948/1329] Fixed leftovers from utflib-and-attrstr that broke builds of things I didn't fully update yet, since people want to build them anyway (for testing in a CI environment, I suppose; a real unit test suite would be better for this, though, which is one of the reasons for the _future/unittest stuff...) Updates #302. --- examples/CMakeLists.txt | 3 +-- test/CMakeLists.txt | 6 +++--- test/main.c | 4 ++-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index cdedb039..75b7f9d1 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -41,5 +41,4 @@ add_custom_target(examples controlgallery histogram cpp-multithread - drawtext - opentype) + drawtext) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e4924bb3..c2f9c1a9 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -18,9 +18,9 @@ _add_exec(tester page7a.c page7b.c page7c.c - page8.c - page9.c - page10.c +# page8.c +# page9.c +# page10.c page11.c page12.c page13.c diff --git a/test/main.c b/test/main.c index 18774dcd..aa38784f 100644 --- a/test/main.c +++ b/test/main.c @@ -129,7 +129,7 @@ int main(int argc, char *argv[]) page7 = makePage7(); uiTabAppend(innerTab, "Page 7", uiControl(page7)); - page8 = makePage8(); +/* page8 = makePage8(); uiTabAppend(innerTab, "Page 8", uiControl(page8)); page9 = makePage9(); @@ -137,7 +137,7 @@ int main(int argc, char *argv[]) page10 = makePage10(); uiTabAppend(innerTab, "Page 10", uiControl(page10)); - +*/ innerTab = newTab(); uiTabAppend(outerTab, "Pages 11-15", uiControl(innerTab)); From 242620ff0f111898e61f84b1dc758cc0be67c28d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 20 Mar 2018 02:10:48 -0400 Subject: [PATCH 0949/1329] Fixing deployment target issues on OS X broke cpp-multithread due to deployment target libc++ issues. Fixed. Fixes #302. --- examples/CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 75b7f9d1..61ed7b82 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -30,6 +30,12 @@ _add_example(cpp-multithread if(NOT WIN32) target_link_libraries(cpp-multithread pthread) endif() +if(APPLE) + # since we use a deployment target of 10.8, the non-C++11-compliant libstdc++ is chosen by default; we need C++11 + # see issue #302 for more details + target_compile_options(cpp-multithread PRIVATE --stdlib=libc++) + target_link_libraries(cpp-multithread --stdlib=libc++) +endif() _add_example(drawtext drawtext/main.c From 93789560ceae9fcaa44635d10a6167184b350f76 Mon Sep 17 00:00:00 2001 From: marcotrosi Date: Thu, 22 Mar 2018 18:37:07 +0100 Subject: [PATCH 0950/1329] added link to Lua binding library lui by Gunnar Z. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index aa62632e..ad56dffc 100644 --- a/README.md +++ b/README.md @@ -164,7 +164,7 @@ Harbour | [HBUI](https://github.com/RJopek/HBUI) Haskell | [libui-haskell](https://github.com/ajnsit/libui-haskell), [beijaflor-io/haskell-libui (complete FFI bindings, extensions and higher-level API)](https://github.com/beijaflor-io/haskell-libui) JavaScript | [libui.js (merged into libui-node?)](https://github.com/mavenave/libui.js) Julia | [Libui.jl](https://github.com/joa-quim/Libui.jl) -Lua | [libuilua](https://github.com/zevv/libuilua), [libui-lua](https://github.com/mdombroski/libui-lua) +Lua | [libuilua](https://github.com/zevv/libuilua), [libui-lua](https://github.com/mdombroski/libui-lua), [lui](http://tset.de/lui/index.html) Nim | [ui](https://github.com/nim-lang/ui) Node.js | [libui-node](https://github.com/parro-it/libui-node) PHP | [ui](https://github.com/krakjoe/ui) From 51f72abba84e3941deba1c8f699046a48d1f7fb9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 23 Mar 2018 08:25:41 -0400 Subject: [PATCH 0951/1329] Updated the list of bindings, adding new ones and removing some deleted ones. Fixed #244. FIxed #217. Updates #206. --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ad56dffc..e94195b4 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,7 @@ Other people have made bindings to other languages: Language | Bindings --- | --- +C++ | [libui-cpp](https://github.com/billyquith/libui-cpp), [cpp-libui-qtlike](https://github.com/aoloe/cpp-libui-qtlike) C# / .NET Framework | [LibUI.Binding](https://github.com/NattyNarwhal/LibUI.Binding) C# / .NET Core | [DevZH.UI](https://github.com/noliar/DevZH.UI), [SharpUI](https://github.com/benpye/sharpui/) CHICKEN Scheme | [wasamasa/libui](https://github.com/wasamasa/libui) @@ -161,16 +162,17 @@ Crystal | [libui.cr](https://github.com/Fusion/libui.cr) D | [DerelictLibui (flat API)](https://github.com/Extrawurst/DerelictLibui), [libuid (object-oriented)](https://github.com/mogud/libuid) Euphoria | [libui-euphoria](https://github.com/ghaberek/libui-euphoria) Harbour | [HBUI](https://github.com/RJopek/HBUI) -Haskell | [libui-haskell](https://github.com/ajnsit/libui-haskell), [beijaflor-io/haskell-libui (complete FFI bindings, extensions and higher-level API)](https://github.com/beijaflor-io/haskell-libui) -JavaScript | [libui.js (merged into libui-node?)](https://github.com/mavenave/libui.js) +Haskell | [haskell-libui](https://github.com/beijaflor-io/haskell-libui) +JavaScript | [libui.js (merged into libui-node?)](https://github.com/mavenave/libui.js), [proton-native](https://github.com/kusti8/proton-native) Julia | [Libui.jl](https://github.com/joa-quim/Libui.jl) Lua | [libuilua](https://github.com/zevv/libuilua), [libui-lua](https://github.com/mdombroski/libui-lua), [lui](http://tset.de/lui/index.html) Nim | [ui](https://github.com/nim-lang/ui) Node.js | [libui-node](https://github.com/parro-it/libui-node) PHP | [ui](https://github.com/krakjoe/ui) -Python | [pylibui](https://github.com/joaoventura/pylibui) +Python | [pylibui](https://github.com/joaoventura/pylibui), [pylibui-cffi](https://github.com/Yardanico/pylibui-cffi) Ruby | [libui-ruby](https://github.com/jamescook/libui-ruby) Rust | [libui-rs](https://github.com/pcwalton/libui-rs) +Scala | [scalaui](https://github.com/lolgab/scalaui) Swift | [libui-swift](https://github.com/sclukey/libui-swift) ## Frequently Asked Questions From 84ff1890c5d9da5db181eeba33a8c0ccccd5cce3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 24 Mar 2018 21:16:58 -0400 Subject: [PATCH 0952/1329] More notes. --- _notes/textSelections | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 _notes/textSelections diff --git a/_notes/textSelections b/_notes/textSelections new file mode 100644 index 00000000..a1bccba6 --- /dev/null +++ b/_notes/textSelections @@ -0,0 +1,3 @@ +http://www.catch22.net/tuts/transparent-text +https://msdn.microsoft.com/en-us/library/windows/desktop/ms724371(v=vs.85).aspx + TODO which color did I want from this list From a7c58c2c2effb6f38255af83fe211f26d9c16a8e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 25 Mar 2018 02:01:37 -0400 Subject: [PATCH 0953/1329] More notes. --- _notes/misc | 100 ++++++++++++++++++++++++++++++++++++++++++ _notes/windowsHighDPI | 2 + 2 files changed, 102 insertions(+) diff --git a/_notes/misc b/_notes/misc index 72381721..714a401e 100644 --- a/_notes/misc +++ b/_notes/misc @@ -1,2 +1,102 @@ windows data types, "open specifications" on msdn https://msdn.microsoft.com/en-us/library/cc230321.aspx (I usually use https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx for windows data types) + +windows platform update for windows 7 +https://msdn.microsoft.com/en-us/library/windows/desktop/jj863687(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/hh802478(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/hh802480(v=vs.85).aspx + +DWM, header bars, toolbars +https://stackoverflow.com/questions/41106347/why-is-my-dwmextendframeintoclientaread-window-not-drawing-the-dwm-borders/41125616#41125616 +https://developer.gnome.org/hig/stable/header-bars.html.en +https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/OSXHIGuidelines/WindowTitleBarToolbar.html#//apple_ref/doc/uid/20000957-CH39-SW1 +https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/OSXHIGuidelines/ControlsAll.html#//apple_ref/doc/uid/20000957-CH46-SW2 +https://msdn.microsoft.com/en-us/library/windows/desktop/bb787329(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/bb787334(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/bb787337(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/cc835034(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/bb787345(v=vs.85).aspx + +rendering +https://www.youtube.com/watch?v=UUfXWzp0-DU + +text input on windows +https://blogs.msdn.microsoft.com/oldnewthing/20121025-00/?p=6253 +https://www.google.com/search?q=translatemessage+site%3Ahttp%3A%2F%2Farchives.miloush.net%2Fmichkap%2Farchive%2F&ie=utf-8&oe=utf-8 +http://archives.miloush.net/michkap/archive/2008/04/22/8415843.html +http://archives.miloush.net/michkap/archive/2007/03/25/1948887.html +http://archives.miloush.net/michkap/archive/2006/09/10/748775.html +http://archives.miloush.net/michkap/archive/2004/11/27/270931.html +https://stackoverflow.com/questions/41334851/since-translatemessage-returns-nonzero-unconditionally-how-can-i-tell-either +http://stackoverflow.com/questions/41334851/since-translatemessage-returns-nonzero-unconditionally-how-can-i-tell-either?noredirect=1#comment70107257_41334851 + +text layouts +https://developer.apple.com/reference/coretext/2110184-ctframe?language=objc +https://msdn.microsoft.com/en-us/library/windows/desktop/dd368203(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/dd368205(v=vs.85).aspx +https://developer.gnome.org/pango/1.30/pango-Layout-Objects.html#pango-layout-set-font-description +https://developer.gnome.org/pango/1.30/pango-Fonts.html#PangoWeight-enum +https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c (code for small caps) +https://bugzilla.gnome.org/show_bug.cgi?id=766148 +https://developer.apple.com/documentation/coretext/ctline?preferredLanguage=occ +https://developer.apple.com/reference/coretext/1508876-ctlinegetstringindexforposition?language=objc +https://developer.apple.com/library/content/documentation/StringsTextFonts/Conceptual/CoreText_Programming/Introduction/Introduction.html#//apple_ref/doc/uid/TP40005533-CH1-SW1 +https://developer.apple.com/reference/coretext/2110184-ctframe?language=objc +https://developer.apple.com/reference/coretext/2168885-ctline?language=objc +https://developer.apple.com/library/content/documentation/StringsTextFonts/Conceptual/CoreText_Programming/LayoutOperations/LayoutOperations.html +http://asciiwwdc.com/2013/sessions/205 +https://developer.apple.com/reference/coretext/1511332-ctlinegetboundswithoptions?language=objc +https://stackoverflow.com/questions/19709751/ctlinegetboundswithoptions-what-does-the-returned-frame-origin-y-value-mean?rq=1 +https://imgur.com/a/lqhzC +https://imgur.com/a/hYeOL +https://www.google.com/search?q=ctline+%22paragraph+spacing%22&ie=utf-8&oe=utf-8 +http://stackoverflow.com/questions/8010615/how-do-i-determine-the-of-a-ctline-following-a-ctline +https://imgur.com/a/dlpm2 +https://stackoverflow.com/questions/41601845/am-i-missing-something-in-core-text-that-will-let-me-see-which-line-a-certain-po +https://www.google.com/search?q=%22core+text%22+%22combining+character%22&oq=%22core+text%22+%22combining+character%22&gs_l=serp.3...2526898.2528158.0.2528485.21.10.0.0.0.0.216.997.5j3j1.9.0....0...1c.1.64.serp..12.8.904...33i21k1j33i160k1.J69jsB9g0N0 +https://github.com/macvim-dev/macvim/issues/400#issuecomment-274292877 +https://bugs.webkit.org/show_bug.cgi?id=68287 +https://trac.webkit.org/changeset/95391 +https://trac.webkit.org/changeset/95391/trunk/Source/WebCore/platform/graphics/mac/ComplexTextControllerCoreText.cpp +http://stackoverflow.com/questions/41857213/is-there-any-way-i-can-get-precise-metrics-line-ascent-line-descent-etc-of +http://pawlan.com/monica/articles/texttutorial/int.html + +enter in entry fields, possibly other text layout issues, possibly keyboard shortcuts +https://developer.gnome.org/gtk3/3.10/GtkEntry.html "activate" signal +https://gitlab.gnome.org/GNOME/gtk/blob/gtk-3-10/gtk/gtkentry.c (not sure where) +https://developer.gnome.org/gtk3/3.10/GtkTextView.html signals section of contents +https://gitlab.gnome.org/GNOME/gtk/blob/gtk-3-10/gtk/gtktextview.c (not sure where; closed before I could find out again though gitlab might wreck it anyway) +https://developer.gnome.org/gtk3/3.10/gtk3-Bindings.html +https://gitlab.gnome.org/GNOME/gtk/blob/gtk-3-10/gtk/gtkwidget.c (not sure where) +https://gitlab.gnome.org/GNOME/gtk/blob/gtk-3-10/gtk/gtkbindings.c (not sure where) +https://gitlab.gnome.org/GNOME/gnome-builder/tree/master/libide/keybindings/ide-keybindings.c 404; TODO find the original cgit link in the chat logs to see what hergertme wanted me to see +https://gitlab.gnome.org/GNOME/gnome-builder/tree/master/libide/sourceview/ide-source-view.c likewise +https://gitlab.gnome.org/GNOME/gnome-builder/tree/master/libide/sourceview/ide-source-view-mode.c#n323 likewise + +rgb int->float conversion +https://stackoverflow.com/questions/41348339/how-to-convert-rgb-to-hexadecimal-using-gtk?noredirect=1#comment69903262_41348339 + +windows fonts, hi-dpi +https://stackoverflow.com/questions/41448320/dlgtemplateex-and-ds-shellfont-what-about-point-size + +windows default fonts +https://stackoverflow.com/questions/41505151/how-to-draw-text-with-the-default-ui-font-in-directwrite#41505750 + +uwp stuff +https://docs.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/inking-controls +https://msdn.microsoft.com/en-us/windows/uwp/controls-and-patterns/master-details + +cmake stuff +https://public.kitware.com/pipermail/cmake/2010-January/034669.html +https://cmake.org/cmake/help/v3.0/command/string.html +https://cmake.org/pipermail/cmake/2003-April/003599.html + +opentype features and locales +https://www.microsoft.com/typography/otspec/featurelist.htm +https://en.wikipedia.org/wiki/List_of_typographic_features +https://www.microsoft.com/typography/otspec160/scripttags.htm +https://msdn.microsoft.com/en-us/library/windows/desktop/dd371503(v=vs.85).aspx + +gspell (TODO figure out what I wanted from this; possibly spelling checking) +https://git.gnome.org/browse/gspell/tree/gspell/gspell-inline-checker-text-buffer.c +https://git.gnome.org/browse/gtk+/tree/gtk/gtktextview.c?h=gtk-3-10 diff --git a/_notes/windowsHighDPI b/_notes/windowsHighDPI index bfec3e7c..14397b67 100644 --- a/_notes/windowsHighDPI +++ b/_notes/windowsHighDPI @@ -7,3 +7,5 @@ https://blogs.windows.com/buildingapps/2017/04/04/high-dpi-scaling-improvements- https://channel9.msdn.com/Events/Windows/Windows-Developer-Day-Creators-Update/High-DPI-Improvements-for-Desktop-Developers https://social.msdn.microsoft.com/Forums/vstudio/en-US/31d2a89f-3518-403e-b1e4-bbe37ac1b0f0/per-monitor-high-dpi-awareness-wmdpichanged?forum=vcgeneral https://stackoverflow.com/questions/36864894/scaling-the-non-client-area-title-bar-menu-bar-for-per-monitor-high-dpi-suppo/37624363 +https://stackoverflow.com/questions/41448320/dlgtemplateex-and-ds-shellfont-what-about-point-size +http://stackoverflow.com/questions/41917279/do-child-windows-have-the-same-dpi-as-their-parents-in-a-per-monitor-aware-appli From e244a0edf4799df3df4b5137fde3821ff3dc0870 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 25 Mar 2018 13:13:31 -0400 Subject: [PATCH 0954/1329] More notes. --- _notes/misc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/_notes/misc b/_notes/misc index 714a401e..697b2978 100644 --- a/_notes/misc +++ b/_notes/misc @@ -100,3 +100,10 @@ https://msdn.microsoft.com/en-us/library/windows/desktop/dd371503(v=vs.85).aspx gspell (TODO figure out what I wanted from this; possibly spelling checking) https://git.gnome.org/browse/gspell/tree/gspell/gspell-inline-checker-text-buffer.c https://git.gnome.org/browse/gtk+/tree/gtk/gtktextview.c?h=gtk-3-10 + +other assorted notes on text +https://mail.gnome.org/archives/gtk-devel-list/2016-March/msg00037.html +https://mail.gnome.org/archives/gtk-devel-list/2016-June/msg00007.html +https://blogs.msdn.microsoft.com/tsfaware/ +https://stackoverflow.com/questions/40453186/what-is-the-correct-modern-way-to-handle-arbitrary-text-input-in-a-custom-contr +https://www.microsoft.com/typography/fonts/family.aspx From 696459be852f08e93e7ee647e5040c36c54da381 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 26 Mar 2018 22:03:17 -0400 Subject: [PATCH 0955/1329] More notes. --- _notes/misc | 3 +++ _notes/winARM64 | 3 +++ 2 files changed, 6 insertions(+) create mode 100644 _notes/winARM64 diff --git a/_notes/misc b/_notes/misc index 697b2978..0b246a11 100644 --- a/_notes/misc +++ b/_notes/misc @@ -107,3 +107,6 @@ https://mail.gnome.org/archives/gtk-devel-list/2016-June/msg00007.html https://blogs.msdn.microsoft.com/tsfaware/ https://stackoverflow.com/questions/40453186/what-is-the-correct-modern-way-to-handle-arbitrary-text-input-in-a-custom-contr https://www.microsoft.com/typography/fonts/family.aspx + +windows ribbon +https://twitter.com/_Ninji/status/814918189708668929 (C08rw9TWgAMpLkC.jpg:large) diff --git a/_notes/winARM64 b/_notes/winARM64 new file mode 100644 index 00000000..c22d5593 --- /dev/null +++ b/_notes/winARM64 @@ -0,0 +1,3 @@ +http://www.os2museum.com/wp/windows-10-arm64-on-qemu/ +https://docs.microsoft.com/en-us/windows/uwp/porting/apps-on-arm +http://pete.akeo.ie/2017/05/compiling-desktop-arm-applications-with.html From 177527c959f771a2ceb7d13bc449e8b3fac98b53 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 29 Mar 2018 23:36:53 -0400 Subject: [PATCH 0956/1329] More notes. --- _notes/misc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/_notes/misc b/_notes/misc index 0b246a11..3b1f4c00 100644 --- a/_notes/misc +++ b/_notes/misc @@ -110,3 +110,7 @@ https://www.microsoft.com/typography/fonts/family.aspx windows ribbon https://twitter.com/_Ninji/status/814918189708668929 (C08rw9TWgAMpLkC.jpg:large) + +https://developer.apple.com/library/content/samplecode/CocoaTipsAndTricks/Introduction/Intro.html#//apple_ref/doc/uid/DTS40010039 BlurryView and StaticObjcMethods in particular +printing https://developer.apple.com/library/content/samplecode/PMPrinterPrintWithFile/Introduction/Intro.html#//apple_ref/doc/uid/DTS10003958 +auto layout https://developer.apple.com/library/content/releasenotes/UserExperience/RNAutomaticLayout/index.html#//apple_ref/doc/uid/TP40010631 From e45c7b4d8750162bbfe23fd27c0fc8cb77171a01 Mon Sep 17 00:00:00 2001 From: Thomas Corwin Date: Mon, 2 Apr 2018 12:47:23 -0400 Subject: [PATCH 0957/1329] Added the LibUISharp project to the list of bindings. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e94195b4..3de973d8 100644 --- a/README.md +++ b/README.md @@ -156,7 +156,7 @@ Language | Bindings --- | --- C++ | [libui-cpp](https://github.com/billyquith/libui-cpp), [cpp-libui-qtlike](https://github.com/aoloe/cpp-libui-qtlike) C# / .NET Framework | [LibUI.Binding](https://github.com/NattyNarwhal/LibUI.Binding) -C# / .NET Core | [DevZH.UI](https://github.com/noliar/DevZH.UI), [SharpUI](https://github.com/benpye/sharpui/) +C# / .NET Core | [DevZH.UI](https://github.com/noliar/DevZH.UI), [SharpUI](https://github.com/benpye/sharpui/), [LibUISharp](https://github.com/tom-corwin/LibUISharp) CHICKEN Scheme | [wasamasa/libui](https://github.com/wasamasa/libui) Crystal | [libui.cr](https://github.com/Fusion/libui.cr) D | [DerelictLibui (flat API)](https://github.com/Extrawurst/DerelictLibui), [libuid (object-oriented)](https://github.com/mogud/libuid) From d53ab65589e126bb9bf40c99d67ca1d94bbff738 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 4 Apr 2018 11:27:59 -0400 Subject: [PATCH 0958/1329] More notes. --- _notes/tableNotes | 4 ++++ _notes/windowsHighDPI | 7 +++++++ 2 files changed, 11 insertions(+) create mode 100644 _notes/tableNotes diff --git a/_notes/tableNotes b/_notes/tableNotes new file mode 100644 index 00000000..b894d5a3 --- /dev/null +++ b/_notes/tableNotes @@ -0,0 +1,4 @@ +https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Protocols/NSOutlineViewDataSource_Protocol/index.html#//apple_ref/occ/intfm/NSOutlineViewDataSource/outlineView:child:ofItem: +https://developer.apple.com/library/mac/documentation/Cocoa/Reference/NSOutlineViewDelegate_Protocol/index.html#//apple_ref/occ/intfm/NSOutlineViewDelegate/outlineView:didAddRowView:forRow: + https://developer.apple.com/documentation/appkit/nsoutlineviewdelegate/1528320-outlineview?language=objc +https://github.com/mity/mctrl/blob/master/mctrl/treelist.c around treelist_set_subitem() diff --git a/_notes/windowsHighDPI b/_notes/windowsHighDPI index 14397b67..0f69a507 100644 --- a/_notes/windowsHighDPI +++ b/_notes/windowsHighDPI @@ -9,3 +9,10 @@ https://social.msdn.microsoft.com/Forums/vstudio/en-US/31d2a89f-3518-403e-b1e4-b https://stackoverflow.com/questions/36864894/scaling-the-non-client-area-title-bar-menu-bar-for-per-monitor-high-dpi-suppo/37624363 https://stackoverflow.com/questions/41448320/dlgtemplateex-and-ds-shellfont-what-about-point-size http://stackoverflow.com/questions/41917279/do-child-windows-have-the-same-dpi-as-their-parents-in-a-per-monitor-aware-appli + +https://msdn.microsoft.com/en-us/library/windows/desktop/dn469266(v=vs.85).aspx + https://msdn.microsoft.com/library/windows/desktop/mt843498(v=vs.85).aspx(d=robot) +https://msdn.microsoft.com/en-us/library/windows/desktop/dn469266(v=vs.85).aspx#appendix_c_common_high_dpi_issues + https://msdn.microsoft.com/library/windows/desktop/mt843498(v=vs.85).aspx(d=robot)#appendix_c_common_high_dpi_issues +https://msdn.microsoft.com/en-us/library/windows/desktop/dn469266(v=vs.85).aspx#addressing_high_dpi_issues + https://msdn.microsoft.com/library/windows/desktop/mt843498(v=vs.85).aspx(d=robot)#addressing_high_dpi_issues From 1266a77e61d973cc0693054af88fcabd9339ba4a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 7 Apr 2018 23:39:41 -0400 Subject: [PATCH 0959/1329] More notes. --- _notes/darwinAutoLayout | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 _notes/darwinAutoLayout diff --git a/_notes/darwinAutoLayout b/_notes/darwinAutoLayout new file mode 100644 index 00000000..5a521fc0 --- /dev/null +++ b/_notes/darwinAutoLayout @@ -0,0 +1,6 @@ +https://developer.apple.com/library/mac/documentation/AppKit/Reference/NSLayoutConstraint_Class/ + https://developer.apple.com/documentation/uikit/nslayoutconstraint?language=objc +https://developer.apple.com/library/mac/documentation/UserExperience/Conceptual/AutolayoutPG/ProgrammaticallyCreatingConstraints.html#//apple_ref/doc/uid/TP40010853-CH16-SW1 + https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/ProgrammaticallyCreatingConstraints.html#//apple_ref/doc/uid/TP40010853-CH16-SW1 (Listing 13-3) +https://developer.apple.com/library/mac/documentation/UserExperience/Conceptual/AutolayoutPG/WorkingwithScrollViews.html#//apple_ref/doc/uid/TP40010853-CH24-SW1 + https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/WorkingwithScrollViews.html#//apple_ref/doc/uid/TP40010853-CH24-SW1 ("IMPORTANT Your layout must fully define..." large box) From ceec25f0613422dd29ef6be6bd136496ab61e899 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 8 Apr 2018 21:26:16 -0400 Subject: [PATCH 0960/1329] More notes. --- _notes/misc | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/_notes/misc b/_notes/misc index 3b1f4c00..2d13d6b8 100644 --- a/_notes/misc +++ b/_notes/misc @@ -114,3 +114,35 @@ https://twitter.com/_Ninji/status/814918189708668929 (C08rw9TWgAMpLkC.jpg:large) https://developer.apple.com/library/content/samplecode/CocoaTipsAndTricks/Introduction/Intro.html#//apple_ref/doc/uid/DTS40010039 BlurryView and StaticObjcMethods in particular printing https://developer.apple.com/library/content/samplecode/PMPrinterPrintWithFile/Introduction/Intro.html#//apple_ref/doc/uid/DTS10003958 auto layout https://developer.apple.com/library/content/releasenotes/UserExperience/RNAutomaticLayout/index.html#//apple_ref/doc/uid/TP40010631 + +other stuff, mostly custom draw on windows +https://github.com/pauldotknopf/WindowsSDK7-Samples/blob/master/multimedia/DirectWrite/ChooseFont/ChooseFont.cpp at ChooseFontDialog::OnFontSizeNameEdit() definition +https://developer.gnome.org/gtk3/3.10/GtkColorButton.html +https://msdn.microsoft.com/en-us/library/windows/desktop/bb775951(v=vs.85).aspx + https://msdn.microsoft.com/en-us/library/windows/desktop/bb775951(v=vs.85).aspx between BS_LEFTTEXT and BS_RADIOBUTTON +https://msdn.microsoft.com/en-us/library/windows/desktop/bb775923(v=vs.85).aspx + https://msdn.microsoft.com/en-us/library/windows/desktop/bb775923(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/bb775802(v=vs.85).aspx + https://msdn.microsoft.com/en-us/library/windows/desktop/bb775802(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/bb761837(v=vs.85).aspx + https://msdn.microsoft.com/en-us/library/windows/desktop/bb761837(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/bb761847(v=vs.85).aspx + https://msdn.microsoft.com/en-us/library/windows/desktop/bb761847(v=vs.85).aspx description + parameters +https://msdn.microsoft.com/en-us/library/windows/desktop/bb775483(v=vs.85).aspx + https://msdn.microsoft.com/en-us/library/windows/desktop/bb775483(v=vs.85).aspx dwItemSpec parameter +https://msdn.microsoft.com/en-us/library/windows/desktop/bb775951(v=vs.85).aspx#BS_NOTIFY + https://msdn.microsoft.com/en-us/library/windows/desktop/bb775951(v=vs.85).aspx#BS_NOTIFY BS_NOTIFY to BS_RIGHT +http://stackoverflow.com/questions/17678261/how-to-change-color-of-a-button-while-keeping-buttons-functions-in-win32 +https://msdn.microsoft.com/en-us/library/windows/desktop/ff919569(v=vs.85).aspx + https://msdn.microsoft.com/en-us/library/windows/desktop/ff919569(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/ff919573(v=vs.85).aspx + https://msdn.microsoft.com/en-us/library/windows/desktop/ff919573(v=vs.85).aspx switch (pnm->hdr.code){ +http://stackoverflow.com/questions/36756094/will-the-device-context-of-a-child-window-have-the-same-text-metrics-and-text-ex +actually I think most if not all of these were from when I was trying to implement uiColorButton on Windows, so I'm not sure if I still need these, but meh... + +more miscellaneous +https://blogs.msdn.microsoft.com/oldnewthing/20160328-00/?p=93204 from "One is virtual memory" to "occured to them that their" +https://blogs.msdn.microsoft.com/oldnewthing/20160331-00/?p=93231 + https://msdn.microsoft.com/en-us/library/windows/desktop/aa373631(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/ms648395(v=vs.85).aspx + https://msdn.microsoft.com/en-us/library/windows/desktop/ms648395(v=vs.85).aspx From 34fd48ae0d251e38198b83670f1c1b6b73636b94 Mon Sep 17 00:00:00 2001 From: Hanyuan Li Date: Mon, 9 Apr 2018 18:29:46 +1000 Subject: [PATCH 0961/1329] Added link to Hedron --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e94195b4..8132289a 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ C++ | [libui-cpp](https://github.com/billyquith/libui-cpp), [cpp-libui-qtlike](h C# / .NET Framework | [LibUI.Binding](https://github.com/NattyNarwhal/LibUI.Binding) C# / .NET Core | [DevZH.UI](https://github.com/noliar/DevZH.UI), [SharpUI](https://github.com/benpye/sharpui/) CHICKEN Scheme | [wasamasa/libui](https://github.com/wasamasa/libui) -Crystal | [libui.cr](https://github.com/Fusion/libui.cr) +Crystal | [libui.cr](https://github.com/Fusion/libui.cr), [hedron](https://github.com/Qwerp-Derp/hedron) D | [DerelictLibui (flat API)](https://github.com/Extrawurst/DerelictLibui), [libuid (object-oriented)](https://github.com/mogud/libuid) Euphoria | [libui-euphoria](https://github.com/ghaberek/libui-euphoria) Harbour | [HBUI](https://github.com/RJopek/HBUI) From a098f7f78bdfc4262d66b3404636fd06e9a70da3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 9 Apr 2018 21:32:50 -0400 Subject: [PATCH 0962/1329] More notes. --- _notes/misc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/_notes/misc b/_notes/misc index 2d13d6b8..4f8c1b08 100644 --- a/_notes/misc +++ b/_notes/misc @@ -146,3 +146,8 @@ https://blogs.msdn.microsoft.com/oldnewthing/20160331-00/?p=93231 https://msdn.microsoft.com/en-us/library/windows/desktop/aa373631(v=vs.85).aspx https://msdn.microsoft.com/en-us/library/windows/desktop/ms648395(v=vs.85).aspx https://msdn.microsoft.com/en-us/library/windows/desktop/ms648395(v=vs.85).aspx +https://blogs.msdn.microsoft.com/oldnewthing/20101118-00/?p=12253#comment-875273 "MTA implies poor service and fare hikes." + +from #gtk+ +[18:06:48] if i am doing an animation that needs to be updated at every duration(say every 50ms), should i use get_frame_time() from the frameclock to do that? (from add_tick_callback()) +[18:12:47] <~Company> zorcon: yes From 8e7bd22d6c93ad74644cc4b6aafb3585e8394a40 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 12 Apr 2018 21:55:57 -0400 Subject: [PATCH 0963/1329] More notes. --- _notes/OS2 | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 _notes/OS2 diff --git a/_notes/OS2 b/_notes/OS2 new file mode 100644 index 00000000..e5aa04e4 --- /dev/null +++ b/_notes/OS2 @@ -0,0 +1,25 @@ +https://twitter.com/OS2World/status/983822011389620224 + Hi. I recommend you today to start with OS/2 Warp 4.52 or ArcaOS ( The tools are almost the same of Warp 3). EDM/2 is a good place to start: http://www.edm2.com +https://twitter.com/OS2World/status/983822594465034240 + There is also an RPM with OS/2 Software (https://www.arcanoae.com/resources/downloadables/arca-noae-package-manager/ …), and you can get from it some parts of the OS/2 Toolkit. If you want to develop drivers you require the OS/2 Device Driver Kit. (that is more complex). You can also develop in Qt4 and compile things with gcc. +http://www.edm2.com/index.php/Main_Page +https://www.arcanoae.com/resources/downloadables/arca-noae-package-manager/ +http://www.edm2.com/index.php/IBM_OS/2_Toolkit_Documentation +https://www.os2world.com/forum/index.php?topic=953.0 +https://www.google.com/search?client=firefox-b-1&ei=QIPPWurPLs7OwAK65K24BA&q=%22OS%2F2+Toolkit%22+site%3Aamazon.com&oq=%22OS%2F2+Toolkit%22+site%3AAmazon.com&gs_l=psy-ab.3...160814.161293.0.161540.2.2.0.0.0.0.128.252.0j2.2.0....0...1c.1.64.psy-ab..0.0.0....0.itT9Og6hC5c +http://www.edm2.com/index.php/List_of_Presentation_Manager_Articles +https://www.ecsoft2.org/ +http://www.edm2.com/index.php/Cairo + http://www.edm2.com/index.php/Doodle +http://www.edm2.com/index.php/Workplace_Shell_Toolkit +http://wpstk.netlabs.org/en/site/index.xml +https://en.wikipedia.org/wiki/Workplace_Shell +https://www.google.com/search?q=OS2+alphablending&ie=utf-8&oe=utf-8&client=firefox-b-1 +http://www.edm2.com/index.php/List_of_Multimedia_Articles +alphablending: + http://www.osnews.com/story/369/Review-eComStation-OS2-1.0/page3/ + http://halfos.ru/documentation/33-os2-api-documentation/67-opengl-os2-developer-reference-guide.html + http://www.altools.com/ALTools/ALSee/ALSee-Image-Viewer.aspx + http://www.os2voice.org/vnewsarc/bn2007122.html + http://www.mozillazine.org/talkback.html?article=194 + https://books.google.com/books?id=9cpU5uYCzq4C&pg=PA202&lpg=PA202&dq=%22OS/2%22+alphablending&source=bl&ots=uatEop2jAL&sig=HAa_ofQSKsk6-8tBR6YZ6MRJG_0&hl=en&sa=X&ved=0ahUKEwiDq5HukLbaAhUk8IMKHR7aCw4Q6AEIWTAI#v=onepage&q=%22OS%2F2%22%20alphablending&f=false From a24085bd5efb2379cb70581be03d2de3b91f1f35 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 13 Apr 2018 22:30:21 -0400 Subject: [PATCH 0964/1329] More notes. --- _notes/misc | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/_notes/misc b/_notes/misc index 4f8c1b08..413fcdc3 100644 --- a/_notes/misc +++ b/_notes/misc @@ -151,3 +151,19 @@ https://blogs.msdn.microsoft.com/oldnewthing/20101118-00/?p=12253#comment-875273 from #gtk+ [18:06:48] if i am doing an animation that needs to be updated at every duration(say every 50ms), should i use get_frame_time() from the frameclock to do that? (from add_tick_callback()) [18:12:47] <~Company> zorcon: yes + +windows stuff: mostly aero tricks, sdk version macro notes, ribbon stuff, scrollbar stuff too +https://tools.stefankueng.com/SendMessage.html +https://sourceforge.net/p/sendmessage/code/HEAD/tree/trunk/src/SendMessage.vcxproj +https://sourceforge.net/p/sendmessage/code/HEAD/tree/trunk/src/stdafx.h +https://sourceforge.net/p/sendmessage/code/HEAD/tree/trunk/src/MainDlg.h +https://sourceforge.net/u/steveking/profile/ +https://github.com/stefankueng/BowPad/tree/master/src + sourceforge original +https://github.com/stefankueng/sktoolslib/blob/master/AeroControls.cpp + sourceforge original +https://tools.stefankueng.com/BowPad.html +https://www.codeproject.com/Articles/1084/Custom-Scrollbar-Library-version-1-1#_articleTop +https://www.google.com/search?client=firefox-b-1&biw=1080&bih=600&ei=gUHRWsrdMYayggf30bfoAw&q=%22aerocontrols.h%22&oq=%22aerocontrols.h%22&gs_l=psy-ab.3..35i39k1l6.1816.3367.0.3413.4.2.0.0.0.0.0.0..1.0....0...1c.1.64.psy-ab..3.1.211.6...211.0Ppw_OREAk0 + https://searchcode.com/codesearch/view/7295148/ + https://github.com/TortoiseGit/tortoisesvn/blob/master/src/Utils/MiscUI/AeroBaseDlg.h + https://github.com/TortoiseGit/tortoisesvn/blob/master/src/Utils/MiscUI/AeroControls.cpp + https://github.com/gavioto/stexbar/blob/master/Misc/FileTool/src/CleanVerifyDlg.h From 03d0d4074a0d43d987b915f0f93173986b4ab02c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 14 Apr 2018 12:36:52 -0400 Subject: [PATCH 0965/1329] More notes. --- _notes/misc | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/_notes/misc b/_notes/misc index 413fcdc3..5d40d908 100644 --- a/_notes/misc +++ b/_notes/misc @@ -167,3 +167,20 @@ https://www.google.com/search?client=firefox-b-1&biw=1080&bih=600&ei=gUHRWsrdMYa https://github.com/TortoiseGit/tortoisesvn/blob/master/src/Utils/MiscUI/AeroBaseDlg.h https://github.com/TortoiseGit/tortoisesvn/blob/master/src/Utils/MiscUI/AeroControls.cpp https://github.com/gavioto/stexbar/blob/master/Misc/FileTool/src/CleanVerifyDlg.h + +windows ribbon colors and dwm customization +https://github.com/stefankueng/BowPad/blob/master/src/MainWindow.cpp +https://msdn.microsoft.com/en-us/library/windows/desktop/dd940487(v=vs.85).aspx +https://github.com/yohei-yoshihara/GameOfLife3D/blob/master/GameOfLife3DLib/gameoflife3d/Ribbon.cpp +https://msdn.microsoft.com/en-us/library/windows/desktop/dd940404(v=vs.85).aspx +https://github.com/stefankueng/sktoolslib/blob/master/AeroColors.cpp +https://gist.github.com/emoacht/bfa852ccc16bdb5465bd +https://stackoverflow.com/questions/4258295/aero-how-to-draw-solid-opaque-colors-on-glass +https://github.com/ComponentFactory/Krypton +https://www.codeproject.com/Articles/620045/Custom-Controls-in-Win-API-Visual-Styles +https://blogs.msdn.microsoft.com/wpf/2010/11/30/systemcolors-reference/ +https://stackoverflow.com/questions/25639621/check-when-a-user-changes-windows-glass-brush-theme-color +https://msdn.microsoft.com/en-us/library/windows/desktop/aa969513(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/aa969527(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/dd388198(v=vs.85).aspx +I hope the MFC ribbon can have customizable colors too, otherwise I'll have to do some serious image processing... From e0f800d5eb016c4c1a128b081a2eef092ce50a07 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 Apr 2018 15:39:28 -0400 Subject: [PATCH 0966/1329] Started cleaning up the common/ folder. Backed up the current uipriv.h. --- common/OLD_uipriv.h | 66 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 common/OLD_uipriv.h diff --git a/common/OLD_uipriv.h b/common/OLD_uipriv.h new file mode 100644 index 00000000..02676a16 --- /dev/null +++ b/common/OLD_uipriv.h @@ -0,0 +1,66 @@ +// 6 april 2015 + +// this must go outside other extern "C" blocks, otherwise we'll get double-declaration errors +#include "utf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "controlsigs.h" + +extern uiInitOptions options; + +extern void *uiAlloc(size_t, const char *); +#define uiNew(T) ((T *) uiAlloc(sizeof (T), #T)) +extern void *uiRealloc(void *, size_t, const char *); +extern void uiFree(void *); + +// ugh, this was only introduced in MSVC 2015... +#ifdef _MSC_VER +#define __func__ __FUNCTION__ +#endif +extern void realbug(const char *file, const char *line, const char *func, const char *prefix, const char *format, va_list ap); +#define _ns2(s) #s +#define _ns(s) _ns2(s) +extern void _implbug(const char *file, const char *line, const char *func, const char *format, ...); +#define implbug(...) _implbug(__FILE__, _ns(__LINE__), __func__, __VA_ARGS__) +extern void _userbug(const char *file, const char *line, const char *func, const char *format, ...); +#define userbug(...) _userbug(__FILE__, _ns(__LINE__), __func__, __VA_ARGS__) + +// control.c +extern uiControl *newControl(size_t size, uint32_t OSsig, uint32_t typesig, const char *typenamestr); + +// shouldquit.c +extern int shouldQuit(void); + +// areaevents.c +typedef struct clickCounter clickCounter; +// you should call Reset() to zero-initialize a new instance +// it doesn't matter that all the non-count fields are zero: the first click will fail the curButton test straightaway, so it'll return 1 and set the rest of the structure accordingly +struct clickCounter { + int curButton; + int rectX0; + int rectY0; + int rectX1; + int rectY1; + uintptr_t prevTime; + int count; +}; +int clickCounterClick(clickCounter *c, int button, int x, int y, uintptr_t time, uintptr_t maxTime, int32_t xdist, int32_t ydist); +extern void clickCounterReset(clickCounter *); +extern int fromScancode(uintptr_t, uiAreaKeyEvent *); + +// matrix.c +extern void fallbackSkew(uiDrawMatrix *, double, double, double, double); +extern void scaleCenter(double, double, double *, double *); +extern void fallbackTransformSize(uiDrawMatrix *, double *, double *); + +// OS-specific text.* files +extern int uiprivStricmp(const char *a, const char *b); + +#ifdef __cplusplus +} +#endif From 4a57b15d09179baf3192467c1b50bdf42f68b3ed Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 Apr 2018 15:49:45 -0400 Subject: [PATCH 0967/1329] Renamed the common options variable to uiprivOptions. --- common/OLD_uipriv.h | 18 ------------------ common/uipriv.h | 11 ++++------- darwin/main.m | 4 ++-- unix/main.c | 4 ++-- windows/init.cpp | 6 +++--- 5 files changed, 11 insertions(+), 32 deletions(-) diff --git a/common/OLD_uipriv.h b/common/OLD_uipriv.h index 02676a16..453871d7 100644 --- a/common/OLD_uipriv.h +++ b/common/OLD_uipriv.h @@ -1,17 +1,3 @@ -// 6 april 2015 - -// this must go outside other extern "C" blocks, otherwise we'll get double-declaration errors -#include "utf.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include "controlsigs.h" - -extern uiInitOptions options; extern void *uiAlloc(size_t, const char *); #define uiNew(T) ((T *) uiAlloc(sizeof (T), #T)) @@ -60,7 +46,3 @@ extern void fallbackTransformSize(uiDrawMatrix *, double *, double *); // OS-specific text.* files extern int uiprivStricmp(const char *a, const char *b); - -#ifdef __cplusplus -} -#endif diff --git a/common/uipriv.h b/common/uipriv.h index 02676a16..5f0f287d 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -1,17 +1,14 @@ // 6 april 2015 - -// this must go outside other extern "C" blocks, otherwise we'll get double-declaration errors +#include +#include +#include "controlsigs.h" #include "utf.h" #ifdef __cplusplus extern "C" { #endif -#include -#include -#include "controlsigs.h" - -extern uiInitOptions options; +extern uiInitOptions uiprivOptions; extern void *uiAlloc(size_t, const char *); #define uiNew(T) ((T *) uiAlloc(sizeof (T), #T)) diff --git a/darwin/main.m b/darwin/main.m index 865fb942..1bc6bfc3 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -106,12 +106,12 @@ static BOOL stepsIsRunning; @end -uiInitOptions options; +uiInitOptions uiprivOptions; const char *uiInit(uiInitOptions *o) { @autoreleasepool { - options = *o; + uiprivOptions = *o; app = [[applicationClass sharedApplication] retain]; // don't check for a NO return; something (launch services?) causes running from application bundles to always return NO when asking to change activation policy, even if the change is to the same activation policy! // see https://github.com/andlabs/ui/issues/6 diff --git a/unix/main.c b/unix/main.c index 2998bf31..1d7e7a76 100644 --- a/unix/main.c +++ b/unix/main.c @@ -1,14 +1,14 @@ // 6 april 2015 #include "uipriv_unix.h" -uiInitOptions options; +uiInitOptions uiprivOptions; const char *uiInit(uiInitOptions *o) { GError *err = NULL; const char *msg; - options = *o; + uiprivOptions = *o; if (gtk_init_with_args(NULL, NULL, NULL, NULL, NULL, &err) == FALSE) { msg = g_strdup(err->message); g_error_free(err); diff --git a/windows/init.cpp b/windows/init.cpp index cfe63b9a..2c25d3d4 100644 --- a/windows/init.cpp +++ b/windows/init.cpp @@ -39,8 +39,8 @@ static const char *initerr(const char *message, const WCHAR *label, DWORD value) #define ieLastErr(msg) initerr("=" msg, L"GetLastError() ==", GetLastError()) #define ieHRESULT(msg, hr) initerr("=" msg, L"HRESULT", (DWORD) hr) -// LONGTERM make common -uiInitOptions options; +// LONGTERM put this declaration in a common file +uiInitOptions uiprivOptions; #define wantedICCClasses ( \ ICC_STANDARD_CLASSES | /* user32.dll controls */ \ @@ -62,7 +62,7 @@ const char *uiInit(uiInitOptions *o) INITCOMMONCONTROLSEX icc; HRESULT hr; - options = *o; + uiprivOptions = *o; initAlloc(); From 72e8b9a1983801fb7ab65de35011de85ecd734ca Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 Apr 2018 15:53:27 -0400 Subject: [PATCH 0968/1329] Started FINALLY renaming uiAlloc(), uiNew(), uiRealloc(), and uiFree() into uipriv* forms. This handles the common folder. --- common/OLD_uipriv.h | 5 ----- common/attribute.c | 2 +- common/attrstr.h | 6 ------ common/control.c | 4 ++-- common/uipriv.h | 8 ++++---- 5 files changed, 7 insertions(+), 18 deletions(-) diff --git a/common/OLD_uipriv.h b/common/OLD_uipriv.h index 453871d7..64d82ad1 100644 --- a/common/OLD_uipriv.h +++ b/common/OLD_uipriv.h @@ -1,9 +1,4 @@ -extern void *uiAlloc(size_t, const char *); -#define uiNew(T) ((T *) uiAlloc(sizeof (T), #T)) -extern void *uiRealloc(void *, size_t, const char *); -extern void uiFree(void *); - // ugh, this was only introduced in MSVC 2015... #ifdef _MSC_VER #define __func__ __FUNCTION__ diff --git a/common/attribute.c b/common/attribute.c index 5054d1c1..4a8f0160 100644 --- a/common/attribute.c +++ b/common/attribute.c @@ -85,7 +85,7 @@ uiAttribute *uiNewFamilyAttribute(const char *family) uiAttribute *a; a = newAttribute(uiAttributeTypeFamily); - a->u.family = (char *) uiAlloc((strlen(family) + 1) * sizeof (char), "char[] (uiAttribute)"); + a->u.family = (char *) uiprivAlloc((strlen(family) + 1) * sizeof (char), "char[] (uiAttribute)"); strcpy(a->u.family, family); return a; } diff --git a/common/attrstr.h b/common/attrstr.h index 2f4e42b4..54e43feb 100644 --- a/common/attrstr.h +++ b/common/attrstr.h @@ -1,11 +1,5 @@ // 19 february 2018 -// TODO remove when done migrating these functions -#define uiprivNew(x) uiNew(x) -#define uiprivAlloc(x, y) uiAlloc(x, y) -#define uiprivRealloc(x, y, z) uiRealloc(x, y, z) -#define uiprivFree(x) uiFree(x) - // attribute.c extern uiAttribute *uiprivAttributeRetain(uiAttribute *a); extern void uiprivAttributeRelease(uiAttribute *a); diff --git a/common/control.c b/common/control.c index 28066461..e46e322c 100644 --- a/common/control.c +++ b/common/control.c @@ -63,7 +63,7 @@ uiControl *uiAllocControl(size_t size, uint32_t OSsig, uint32_t typesig, const c { uiControl *c; - c = (uiControl *) uiAlloc(size, typenamestr); + c = (uiControl *) uiprivAlloc(size, typenamestr); c->Signature = uiControlSignature; c->OSSignature = OSsig; c->TypeSignature = typesig; @@ -74,7 +74,7 @@ void uiFreeControl(uiControl *c) { if (uiControlParent(c) != NULL) userbug("You cannot destroy a uiControl while it still has a parent. (control: %p)", c); - uiFree(c); + uiprivFree(c); } void uiControlVerifySetParent(uiControl *c, uiControl *parent) diff --git a/common/uipriv.h b/common/uipriv.h index 5f0f287d..69062c87 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -10,10 +10,10 @@ extern "C" { extern uiInitOptions uiprivOptions; -extern void *uiAlloc(size_t, const char *); -#define uiNew(T) ((T *) uiAlloc(sizeof (T), #T)) -extern void *uiRealloc(void *, size_t, const char *); -extern void uiFree(void *); +extern void *uiprivAlloc(size_t, const char *); +#define uiprivNew(T) ((T *) uiprivAlloc(sizeof (T), #T)) +extern void *uiprivRealloc(void *, size_t, const char *); +extern void uiprivFree(void *); // ugh, this was only introduced in MSVC 2015... #ifdef _MSC_VER From 8ca32f098fffd17e3209bb6cfb239432fcd6238e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 Apr 2018 16:05:24 -0400 Subject: [PATCH 0969/1329] uiAlloc() et al -> uiprivAlloc() et al, OS X code. --- darwin/alloc.m | 12 ++++++------ darwin/draw.m | 20 ++++++++++---------- darwin/fontmatch.m | 4 ++-- darwin/fontvariation.m | 4 ++-- darwin/grid.m | 32 ++++++++++++++++---------------- darwin/image.m | 10 +++++----- darwin/map.m | 4 ++-- darwin/menu.m | 8 ++++---- darwin/scrollview.m | 4 ++-- 9 files changed, 49 insertions(+), 49 deletions(-) diff --git a/darwin/alloc.m b/darwin/alloc.m index 0bbb8996..0012c853 100644 --- a/darwin/alloc.m +++ b/darwin/alloc.m @@ -41,7 +41,7 @@ void uninitAlloc(void) [str release]; } -void *uiAlloc(size_t size, const char *type) +void *uiprivAlloc(size_t size, const char *type) { void *out; @@ -57,17 +57,17 @@ void *uiAlloc(size_t size, const char *type) return DATA(out); } -void *uiRealloc(void *p, size_t new, const char *type) +void *uiprivRealloc(void *p, size_t new, const char *type) { void *out; size_t *s; if (p == NULL) - return uiAlloc(new, type); + return uiprivAlloc(new, type); p = BASE(p); out = realloc(p, EXTRA + new); if (out == NULL) { - fprintf(stderr, "memory exhausted in uiRealloc()\n"); + fprintf(stderr, "memory exhausted in uiprivRealloc()\n"); abort(); } s = SIZE(out); @@ -79,10 +79,10 @@ void *uiRealloc(void *p, size_t new, const char *type) return DATA(out); } -void uiFree(void *p) +void uiprivFree(void *p) { if (p == NULL) - implbug("attempt to uiFree(NULL)"); + implbug("attempt to uiprivFree(NULL)"); p = BASE(p); free(p); [allocations removeObject:[NSValue valueWithPointer:p]]; diff --git a/darwin/draw.m b/darwin/draw.m index b4be6e7f..b52b5a57 100644 --- a/darwin/draw.m +++ b/darwin/draw.m @@ -12,7 +12,7 @@ uiDrawPath *uiDrawNewPath(uiDrawFillMode mode) { uiDrawPath *p; - p = uiNew(uiDrawPath); + p = uiprivNew(uiDrawPath); p->path = CGPathCreateMutable(); p->fillMode = mode; return p; @@ -21,7 +21,7 @@ uiDrawPath *uiDrawNewPath(uiDrawFillMode mode) void uiDrawFreePath(uiDrawPath *p) { CGPathRelease((CGPathRef) (p->path)); - uiFree(p); + uiprivFree(p); } void uiDrawPathNewFigure(uiDrawPath *p, double x, double y) @@ -108,7 +108,7 @@ uiDrawContext *newContext(CGContextRef ctxt, CGFloat height) { uiDrawContext *c; - c = uiNew(uiDrawContext); + c = uiprivNew(uiDrawContext); c->c = ctxt; c->height = height; return c; @@ -116,7 +116,7 @@ uiDrawContext *newContext(CGContextRef ctxt, CGFloat height) void freeContext(uiDrawContext *c) { - uiFree(c); + uiprivFree(c); } // a stroke is identical to a fill of a stroked path @@ -160,7 +160,7 @@ void uiDrawStroke(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b, uiDrawStro // create a temporary path identical to the previous one dashPath = (CGPathRef) path->path; if (p->NumDashes != 0) { - dashes = (CGFloat *) uiAlloc(p->NumDashes * sizeof (CGFloat), "CGFloat[]"); + dashes = (CGFloat *) uiprivAlloc(p->NumDashes * sizeof (CGFloat), "CGFloat[]"); for (i = 0; i < p->NumDashes; i++) dashes[i] = p->Dashes[i]; dashPath = CGPathCreateCopyByDashingPath(path->path, @@ -168,7 +168,7 @@ void uiDrawStroke(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b, uiDrawStro p->DashPhase, dashes, p->NumDashes); - uiFree(dashes); + uiprivFree(dashes); } // the documentation is wrong: this produces a path suitable for calling CGPathCreateCopyByStrokingPath(), not for filling directly // the cast is safe; we never modify the CGPathRef and always cast it back to a CGPathRef anyway @@ -224,8 +224,8 @@ static void fillGradient(CGContextRef ctxt, uiDrawPath *p, uiDrawBrush *b) // TODO add NULL check to other uses of CGColorSpace // make the gradient - colors = uiAlloc(b->NumStops * 4 * sizeof (CGFloat), "CGFloat[]"); - locations = uiAlloc(b->NumStops * sizeof (CGFloat), "CGFloat[]"); + colors = uiprivAlloc(b->NumStops * 4 * sizeof (CGFloat), "CGFloat[]"); + locations = uiprivAlloc(b->NumStops * sizeof (CGFloat), "CGFloat[]"); for (i = 0; i < b->NumStops; i++) { colors[i * 4 + 0] = b->Stops[i].R; colors[i * 4 + 1] = b->Stops[i].G; @@ -234,8 +234,8 @@ static void fillGradient(CGContextRef ctxt, uiDrawPath *p, uiDrawBrush *b) locations[i] = b->Stops[i].Pos; } gradient = CGGradientCreateWithColorComponents(colorspace, colors, locations, b->NumStops); - uiFree(locations); - uiFree(colors); + uiprivFree(locations); + uiprivFree(colors); // because we're mucking with clipping, we need to save the graphics state and restore it later CGContextSaveGState(ctxt); diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index ea075e53..2c240b95 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -365,7 +365,7 @@ static CTFontDescriptorRef matchStyle(CTFontDescriptorRef against, uiFontDescrip if ([d variation] != NULL) axisDict = uiprivMakeVariationAxisDict([d variationAxes], [d table:kCTFontTableAvar]); - closeness = (struct closeness *) uiAlloc(n * sizeof (struct closeness), "struct closeness[]"); + closeness = (struct closeness *) uiprivAlloc(n * sizeof (struct closeness), "struct closeness[]"); for (i = 0; i < n; i++) { uiFontDescriptor fields; @@ -409,7 +409,7 @@ static CTFontDescriptorRef matchStyle(CTFontDescriptorRef against, uiFontDescrip // release everything if (axisDict != nil) [axisDict release]; - uiFree(closeness); + uiprivFree(closeness); CFRelease(matching); // and release the original descriptor since we no longer need it CFRelease(against); diff --git a/darwin/fontvariation.m b/darwin/fontvariation.m index b5a3591a..6dfb3ab1 100644 --- a/darwin/fontvariation.m +++ b/darwin/fontvariation.m @@ -187,7 +187,7 @@ static fixed1616 *avarExtract(CFDataRef table, CFIndex index, size_t *n) } nEntries = nextuint16be(); *n = nEntries * 2; - entries = (fixed1616 *) uiAlloc(*n * sizeof (fixed1616), "fixed1616[]"); + entries = (fixed1616 *) uiprivAlloc(*n * sizeof (fixed1616), "fixed1616[]"); p = entries; for (i = 0; i < *n; i++) { *p++ = fixed214ToFixed1616((fixed214) nextuint16be()); @@ -247,7 +247,7 @@ fail: - (void)dealloc { if (self->avarMappings != NULL) { - uiFree(self->avarMappings); + uiprivFree(self->avarMappings); self->avarMappings = NULL; } [super dealloc]; diff --git a/darwin/grid.m b/darwin/grid.m index d5c5fb1e..fc98cc9f 100644 --- a/darwin/grid.m +++ b/darwin/grid.m @@ -288,11 +288,11 @@ struct uiGrid { // now build a topological map of the grid gg[y][x] // also figure out which cells contain spanned views so they can be ignored later // treat hidden controls by keeping the indices -1 - gg = (int **) uiAlloc(ycount * sizeof (int *), "int[][]"); - gspan = (BOOL **) uiAlloc(ycount * sizeof (BOOL *), "BOOL[][]"); + gg = (int **) uiprivAlloc(ycount * sizeof (int *), "int[][]"); + gspan = (BOOL **) uiprivAlloc(ycount * sizeof (BOOL *), "BOOL[][]"); for (y = 0; y < ycount; y++) { - gg[y] = (int *) uiAlloc(xcount * sizeof (int), "int[]"); - gspan[y] = (BOOL *) uiAlloc(xcount * sizeof (BOOL), "BOOL[]"); + gg[y] = (int *) uiprivAlloc(xcount * sizeof (int), "int[]"); + gspan[y] = (BOOL *) uiprivAlloc(xcount * sizeof (BOOL), "BOOL[]"); for (x = 0; x < xcount; x++) gg[y][x] = -1; // empty } @@ -344,9 +344,9 @@ struct uiGrid { // now build a topological map of the grid's views gv[y][x] // for any empty cell, create a dummy view - gv = (NSView ***) uiAlloc(ycount * sizeof (NSView **), "NSView *[][]"); + gv = (NSView ***) uiprivAlloc(ycount * sizeof (NSView **), "NSView *[][]"); for (y = 0; y < ycount; y++) { - gv[y] = (NSView **) uiAlloc(xcount * sizeof (NSView *), "NSView *[]"); + gv[y] = (NSView **) uiprivAlloc(xcount * sizeof (NSView *), "NSView *[]"); for (x = 0; x < xcount; x++) if (gg[y][x] == -1) { gv[y][x] = [[NSView alloc] initWithFrame:NSZeroRect]; @@ -360,8 +360,8 @@ struct uiGrid { } // now figure out which rows and columns really expand - hexpand = (BOOL *) uiAlloc(xcount * sizeof (BOOL), "BOOL[]"); - vexpand = (BOOL *) uiAlloc(ycount * sizeof (BOOL), "BOOL[]"); + hexpand = (BOOL *) uiprivAlloc(xcount * sizeof (BOOL), "BOOL[]"); + vexpand = (BOOL *) uiprivAlloc(ycount * sizeof (BOOL), "BOOL[]"); // first, which don't span for (gc in self->children) { if (!uiControlVisible(gc.c)) @@ -522,16 +522,16 @@ struct uiGrid { // TODO make all expanding rows/columns the same height/width // and finally clean up - uiFree(hexpand); - uiFree(vexpand); + uiprivFree(hexpand); + uiprivFree(vexpand); for (y = 0; y < ycount; y++) { - uiFree(gg[y]); - uiFree(gv[y]); - uiFree(gspan[y]); + uiprivFree(gg[y]); + uiprivFree(gv[y]); + uiprivFree(gspan[y]); } - uiFree(gg); - uiFree(gv); - uiFree(gspan); + uiprivFree(gg); + uiprivFree(gv); + uiprivFree(gspan); } - (void)append:(gridChild *)gc diff --git a/darwin/image.m b/darwin/image.m index b62de31d..aa1b945b 100644 --- a/darwin/image.m +++ b/darwin/image.m @@ -11,23 +11,23 @@ uiImage *uiNewImage(double width, double height) { uiImage *i; - i = uiNew(uiImage); + i = uiprivNew(uiImage); i->size = NSMakeSize(width, height); i->i = [[NSImage alloc] initWithSize:i->size]; i->swizzled = [NSMutableArray new]; return i; } -void uiFreeImage(uiImage *i) +void Image(uiImage *i) { NSValue *v; [i->i release]; // to be safe, do this after releasing the image for (v in i->swizzled) - uiFree([v pointerValue]); + uiprivFree([v pointerValue]); [i->swizzled release]; - uiFree(i); + uiprivFree(i); } void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int pixelStride) @@ -40,7 +40,7 @@ void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, in // OS X demands that R and B are in the opposite order from what we expect // we must swizzle :( // LONGTERM test on a big-endian system - swizzled = (uint8_t *) uiAlloc((pixelStride * pixelHeight * 4) * sizeof (uint8_t), "uint8_t[]"); + swizzled = (uint8_t *) uiprivAlloc((pixelStride * pixelHeight * 4) * sizeof (uint8_t), "uint8_t[]"); bp = (uint8_t *) pixels; sp = swizzled; for (y = 0; y < pixelHeight * pixelStride; y += pixelStride) diff --git a/darwin/map.m b/darwin/map.m index 46a7b8d2..4eaa057d 100644 --- a/darwin/map.m +++ b/darwin/map.m @@ -12,7 +12,7 @@ struct mapTable *newMap(void) { struct mapTable *m; - m = uiNew(struct mapTable); + m = uiprivNew(struct mapTable); m->m = [[NSMapTable alloc] initWithKeyOptions:(NSPointerFunctionsOpaqueMemory | NSPointerFunctionsOpaquePersonality) valueOptions:(NSPointerFunctionsOpaqueMemory | NSPointerFunctionsOpaquePersonality) capacity:0]; @@ -24,7 +24,7 @@ void mapDestroy(struct mapTable *m) if ([m->m count] != 0) implbug("attempt to destroy map with items inside"); [m->m release]; - uiFree(m); + uiprivFree(m); } void *mapGet(struct mapTable *m, void *key) diff --git a/darwin/menu.m b/darwin/menu.m index 735cac50..11a98e63 100644 --- a/darwin/menu.m +++ b/darwin/menu.m @@ -241,7 +241,7 @@ static uiMenuItem *newItem(uiMenu *m, int type, const char *name) if (menusFinalized) userbug("You can't create a new menu item after menus have been finalized."); - item = uiNew(uiMenuItem); + item = uiprivNew(uiMenuItem); item->type = type; switch (item->type) { @@ -319,7 +319,7 @@ uiMenu *uiNewMenu(const char *name) if (menus == nil) menus = [NSMutableArray new]; - m = uiNew(uiMenu); + m = uiprivNew(uiMenu); m->menu = [[NSMenu alloc] initWithTitle:toNSString(name)]; // use automatic menu item enabling for all menus for consistency's sake @@ -359,10 +359,10 @@ void uninitMenus(void) v = (NSValue *) obj; mi = (uiMenuItem *) [v pointerValue]; - uiFree(mi); + uiprivFree(mi); }]; [m->items release]; - uiFree(m); + uiprivFree(m); }]; [menus release]; } diff --git a/darwin/scrollview.m b/darwin/scrollview.m index b0b4040c..b583a00f 100644 --- a/darwin/scrollview.m +++ b/darwin/scrollview.m @@ -39,7 +39,7 @@ NSScrollView *mkScrollView(struct scrollViewCreateParams *p, struct scrollViewDa [sv setAllowsMagnification:NO]; [sv setDocumentView:p->DocumentView]; - d = uiNew(struct scrollViewData); + d = uiprivNew(struct scrollViewData); scrollViewSetScrolling(sv, d, p->HScroll, p->VScroll); *dout = d; @@ -57,5 +57,5 @@ void scrollViewSetScrolling(NSScrollView *sv, struct scrollViewData *d, BOOL hsc void scrollViewFreeData(NSScrollView *sv, struct scrollViewData *d) { - uiFree(d); + uiprivFree(d); } From 099c4ff631911cacc435dd1972561ef847562f3e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 Apr 2018 16:36:03 -0400 Subject: [PATCH 0970/1329] uiAlloc() et al -> uiprivAlloc() et al, GTK+ code. --- unix/alloc.c | 14 +++++++------- unix/child.c | 4 ++-- unix/draw.c | 4 ++-- unix/drawpath.c | 4 ++-- unix/graphemes.c | 10 +++++----- unix/image.c | 8 ++++---- unix/main.c | 2 +- unix/menu.c | 12 ++++++------ 8 files changed, 29 insertions(+), 29 deletions(-) diff --git a/unix/alloc.c b/unix/alloc.c index 0291bd49..98eb0e32 100644 --- a/unix/alloc.c +++ b/unix/alloc.c @@ -43,7 +43,7 @@ void uninitAlloc(void) g_free(str); } -void *uiAlloc(size_t size, const char *type) +void *uiprivAlloc(size_t size, const char *type) { void *out; @@ -54,13 +54,13 @@ void *uiAlloc(size_t size, const char *type) return DATA(out); } -void *uiRealloc(void *p, size_t new, const char *type) +void *uiprivRealloc(void *p, size_t new, const char *type) { void *out; size_t *s; if (p == NULL) - return uiAlloc(new, type); + return uiprivAlloc(new, type); p = BASE(p); out = g_realloc(p, EXTRA + new); s = SIZE(out); @@ -68,17 +68,17 @@ void *uiRealloc(void *p, size_t new, const char *type) memset(((uint8_t *) DATA(out)) + *s, 0, new - *s); *s = new; if (g_ptr_array_remove(allocations, p) == FALSE) - implbug("%p not found in allocations array in uiRealloc()", p); + implbug("%p not found in allocations array in uiprivRealloc()", p); g_ptr_array_add(allocations, out); return DATA(out); } -void uiFree(void *p) +void uiprivFree(void *p) { if (p == NULL) - implbug("attempt to uiFree(NULL)"); + implbug("attempt to uiprivFree(NULL)"); p = BASE(p); g_free(p); if (g_ptr_array_remove(allocations, p) == FALSE) - implbug("%p not found in allocations array in uiFree()", p); + implbug("%p not found in allocations array in uiprivFree()", p); } diff --git a/unix/child.c b/unix/child.c index b4a09677..b6e48807 100644 --- a/unix/child.c +++ b/unix/child.c @@ -33,7 +33,7 @@ struct child *newChild(uiControl *child, uiControl *parent, GtkContainer *parent if (child == NULL) return NULL; - c = uiNew(struct child); + c = uiprivNew(struct child); c->c = child; c->widget = GTK_WIDGET(uiControlHandle(c->c)); @@ -82,7 +82,7 @@ void childRemove(struct child *c) if (c->box != NULL) gtk_widget_destroy(c->box); - uiFree(c); + uiprivFree(c); } void childDestroy(struct child *c) diff --git a/unix/draw.c b/unix/draw.c index b4d18049..1eacee33 100644 --- a/unix/draw.c +++ b/unix/draw.c @@ -6,7 +6,7 @@ uiDrawContext *newContext(cairo_t *cr, GtkStyleContext *style) { uiDrawContext *c; - c = uiNew(uiDrawContext); + c = uiprivNew(uiDrawContext); c->cr = cr; c->style = style; return c; @@ -15,7 +15,7 @@ uiDrawContext *newContext(cairo_t *cr, GtkStyleContext *style) void freeContext(uiDrawContext *c) { // free neither cr nor style; we own neither - uiFree(c); + uiprivFree(c); } static cairo_pattern_t *mkbrush(uiDrawBrush *b) diff --git a/unix/drawpath.c b/unix/drawpath.c index a0165fb8..384743dc 100644 --- a/unix/drawpath.c +++ b/unix/drawpath.c @@ -28,7 +28,7 @@ uiDrawPath *uiDrawNewPath(uiDrawFillMode mode) { uiDrawPath *p; - p = uiNew(uiDrawPath); + p = uiprivNew(uiDrawPath); p->pieces = g_array_new(FALSE, TRUE, sizeof (struct piece)); p->fillMode = mode; return p; @@ -37,7 +37,7 @@ uiDrawPath *uiDrawNewPath(uiDrawFillMode mode) void uiDrawFreePath(uiDrawPath *p) { g_array_free(p->pieces, TRUE); - uiFree(p); + uiprivFree(p); } static void add(uiDrawPath *p, struct piece *piece) diff --git a/unix/graphemes.c b/unix/graphemes.c index 296e655f..b5edb95a 100644 --- a/unix/graphemes.c +++ b/unix/graphemes.c @@ -16,11 +16,11 @@ struct graphemes *uiprivNewGraphemes(void *s, size_t len) size_t i; size_t *op; - g = uiNew(struct graphemes); + g = uiprivNew(struct graphemes); // TODO see if we can use the utf routines lenchars = g_utf8_strlen(text, -1); - logattrs = (PangoLogAttr *) uiAlloc((lenchars + 1) * sizeof (PangoLogAttr), "PangoLogAttr[] (graphemes)"); + logattrs = (PangoLogAttr *) uiprivAlloc((lenchars + 1) * sizeof (PangoLogAttr), "PangoLogAttr[] (graphemes)"); pango_get_log_attrs(text, len, -1, NULL, logattrs, lenchars + 1); @@ -31,8 +31,8 @@ struct graphemes *uiprivNewGraphemes(void *s, size_t len) if (logattrs[i].is_cursor_position != 0) g->len++; - g->pointsToGraphemes = (size_t *) uiAlloc((len + 1) * sizeof (size_t), "size_t[] (graphemes)"); - g->graphemesToPoints = (size_t *) uiAlloc((g->len + 1) * sizeof (size_t), "size_t[] (graphemes)"); + g->pointsToGraphemes = (size_t *) uiprivAlloc((len + 1) * sizeof (size_t), "size_t[] (graphemes)"); + g->graphemesToPoints = (size_t *) uiprivAlloc((g->len + 1) * sizeof (size_t), "size_t[] (graphemes)"); // compute the graphemesToPoints array // TODO merge with the next for loop somehow? @@ -58,6 +58,6 @@ struct graphemes *uiprivNewGraphemes(void *s, size_t len) // and do the last one *op++ = i; - uiFree(logattrs); + uiprivFree(logattrs); return g; } diff --git a/unix/image.c b/unix/image.c index a79e550f..1bdf0d64 100644 --- a/unix/image.c +++ b/unix/image.c @@ -14,14 +14,14 @@ static void freeImageRep(gpointer item) buf = cairo_image_surface_get_data(cs); cairo_surface_destroy(cs); - uiFree(buf); + uiprivFree(buf); } uiImage *uiNewImage(double width, double height) { uiImage *i; - i = uiNew(uiImage); + i = uiprivNew(uiImage); i->width = width; i->height = height; i->images = g_ptr_array_new_with_free_func(freeImageRep); @@ -31,7 +31,7 @@ uiImage *uiNewImage(double width, double height) void uiFreeImage(uiImage *i) { g_ptr_array_free(i->images, TRUE); - uiFree(i); + uiprivFree(i); } void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int pixelStride) @@ -43,7 +43,7 @@ void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, in int y; cstride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, pixelWidth); - buf = (unsigned char *) uiAlloc((cstride * pixelHeight * 4) * sizeof (unsigned char), "unsigned char[]"); + buf = (unsigned char *) uiprivAlloc((cstride * pixelHeight * 4) * sizeof (unsigned char), "unsigned char[]"); p = buf; for (y = 0; y < pixelStride * pixelHeight; y += pixelStride) { memmove(p, src + y, cstride); diff --git a/unix/main.c b/unix/main.c index 1d7e7a76..a0cd7835 100644 --- a/unix/main.c +++ b/unix/main.c @@ -99,7 +99,7 @@ void uiQueueMain(void (*f)(void *data), void *data) { struct queued *q; - // we have to use g_new0()/g_free() because uiAlloc() is only safe to call on the main thread + // we have to use g_new0()/g_free() because uiprivAlloc() is only safe to call on the main thread // for some reason it didn't affect me, but it did affect krakjoe q = g_new0(struct queued, 1); q->f = f; diff --git a/unix/menu.c b/unix/menu.c index 5ccb4a51..1d950c5f 100644 --- a/unix/menu.c +++ b/unix/menu.c @@ -137,7 +137,7 @@ static uiMenuItem *newItem(uiMenu *m, int type, const char *name) if (menusFinalized) userbug("You cannot create a new menu item after menus have been finalized."); - item = uiNew(uiMenuItem); + item = uiprivNew(uiMenuItem); g_array_append_val(m->items, item); @@ -234,7 +234,7 @@ uiMenu *uiNewMenu(const char *name) if (menus == NULL) menus = g_array_new(FALSE, TRUE, sizeof (uiMenu *)); - m = uiNew(uiMenu); + m = uiprivNew(uiMenu); g_array_append_val(menus, m); @@ -260,7 +260,7 @@ static void appendMenuItem(GtkMenuShell *submenu, uiMenuItem *item, uiWindow *w) singleSetChecked(GTK_CHECK_MENU_ITEM(menuitem), item->checked, signal); } gtk_menu_shell_append(submenu, menuitem); - ww = uiNew(struct menuItemWindow); + ww = uiprivNew(struct menuItemWindow); ww->w = w; ww->signal = signal; g_hash_table_insert(item->windows, menuitem, ww); @@ -309,7 +309,7 @@ static void freeMenuItem(GtkWidget *widget, gpointer data) w = (struct menuItemWindow *) g_hash_table_lookup(item->windows, widget); if (g_hash_table_remove(item->windows, widget) == FALSE) implbug("GtkMenuItem %p not in menu item's item/window map", widget); - uiFree(w); + uiprivFree(w); fmi->i++; } @@ -357,10 +357,10 @@ void uninitMenus(void) implbug("menu item %p (%s) still has uiWindows attached; did you forget to destroy some windows?", item, item->name); g_free(item->name); g_hash_table_destroy(item->windows); - uiFree(item); + uiprivFree(item); } g_array_free(m->items, TRUE); - uiFree(m); + uiprivFree(m); } g_array_free(menus, TRUE); } From c6bb4636929f146b557521e22854c3f86e683f88 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 Apr 2018 18:12:58 -0400 Subject: [PATCH 0971/1329] uiAlloc() et al -> uiprivAlloc() et al, Windows code. --- windows/alloc.cpp | 10 +++++----- windows/attrstr.cpp | 2 +- windows/button.cpp | 2 +- windows/checkbox.cpp | 2 +- windows/colordialog.cpp | 20 ++++++++++---------- windows/combobox.cpp | 2 +- windows/datetimepicker.cpp | 14 +++++++------- windows/debug.cpp | 10 +++++----- windows/draw.cpp | 12 ++++++------ windows/drawpath.cpp | 4 ++-- windows/drawtext.cpp | 10 +++++----- windows/dwrite.cpp | 2 +- windows/editablecombo.cpp | 2 +- windows/fontdialog.cpp | 20 ++++++++++---------- windows/form.cpp | 2 +- windows/graphemes.cpp | 6 +++--- windows/grid.cpp | 4 ++-- windows/group.cpp | 2 +- windows/init.cpp | 6 +++--- windows/label.cpp | 2 +- windows/menu.cpp | 24 ++++++++++++------------ windows/multilineentry.cpp | 6 +++--- windows/radiobuttons.cpp | 2 +- windows/spinbox.cpp | 4 ++-- windows/stddialogs.cpp | 4 ++-- windows/tab.cpp | 2 +- windows/tabpage.cpp | 4 ++-- windows/text.cpp | 16 ++++++++-------- windows/uipriv_windows.hpp | 4 ++-- windows/utf16.cpp | 10 +++++----- windows/window.cpp | 2 +- 31 files changed, 106 insertions(+), 106 deletions(-) diff --git a/windows/alloc.cpp b/windows/alloc.cpp index 23201f75..244b2380 100644 --- a/windows/alloc.cpp +++ b/windows/alloc.cpp @@ -27,7 +27,7 @@ void uninitAlloc(void) #define rawBytes(pa) (&((*pa)[0])) -void *uiAlloc(size_t size, const char *type) +void *uiprivAlloc(size_t size, const char *type) { byteArray *out; @@ -37,13 +37,13 @@ void *uiAlloc(size_t size, const char *type) return rawBytes(out); } -void *uiRealloc(void *_p, size_t size, const char *type) +void *uiprivRealloc(void *_p, size_t size, const char *type) { uint8_t *p = (uint8_t *) _p; byteArray *arr; if (p == NULL) - return uiAlloc(size, type); + return uiprivAlloc(size, type); arr = heap[p]; // TODO does this fill in? arr->resize(size, 0); @@ -52,12 +52,12 @@ void *uiRealloc(void *_p, size_t size, const char *type) return rawBytes(arr); } -void uiFree(void *_p) +void uiprivFree(void *_p) { uint8_t *p = (uint8_t *) _p; if (p == NULL) - implbug("attempt to uiFree(NULL)"); + implbug("attempt to uiprivFree(NULL)"); types.erase(heap[p]); delete heap[p]; heap.erase(p); diff --git a/windows/attrstr.cpp b/windows/attrstr.cpp index 9ea0f755..740ac43e 100644 --- a/windows/attrstr.cpp +++ b/windows/attrstr.cpp @@ -295,7 +295,7 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute hr = p->layout->SetFontFamilyName(wfamily, range); if (hr != S_OK) logHRESULT(L"error applying family name attribute", hr); - uiFree(wfamily); + uiprivFree(wfamily); break; case uiAttributeTypeSize: hr = p->layout->SetFontSize( diff --git a/windows/button.cpp b/windows/button.cpp index 3b12e726..d8913ec7 100644 --- a/windows/button.cpp +++ b/windows/button.cpp @@ -95,7 +95,7 @@ uiButton *uiNewButton(const char *text) BS_PUSHBUTTON | WS_TABSTOP, hInstance, NULL, TRUE); - uiFree(wtext); + uiprivFree(wtext); uiWindowsRegisterWM_COMMANDHandler(b->hwnd, onWM_COMMAND, uiControl(b)); uiButtonOnClicked(b, defaultOnClicked, NULL); diff --git a/windows/checkbox.cpp b/windows/checkbox.cpp index be425c00..3d8c92e3 100644 --- a/windows/checkbox.cpp +++ b/windows/checkbox.cpp @@ -108,7 +108,7 @@ uiCheckbox *uiNewCheckbox(const char *text) BS_CHECKBOX | WS_TABSTOP, hInstance, NULL, TRUE); - uiFree(wtext); + uiprivFree(wtext); uiWindowsRegisterWM_COMMANDHandler(c->hwnd, onWM_COMMAND, uiControl(c)); uiCheckboxOnToggled(c, defaultOnToggled, NULL); diff --git a/windows/colordialog.cpp b/windows/colordialog.cpp index 2efe72c8..d3d9bde9 100644 --- a/windows/colordialog.cpp +++ b/windows/colordialog.cpp @@ -228,7 +228,7 @@ static void updateDouble(HWND hwnd, double d, HWND whichChanged) return; str = ftoutf16(d); setWindowText(hwnd, str); - uiFree(str); + uiprivFree(str); } static void updateDialog(struct colorDialog *c, HWND whichChanged) @@ -259,22 +259,22 @@ static void updateDialog(struct colorDialog *c, HWND whichChanged) if (whichChanged != c->editRInt) { str = itoutf16(rb); setWindowText(c->editRInt, str); - uiFree(str); + uiprivFree(str); } if (whichChanged != c->editGInt) { str = itoutf16(gb); setWindowText(c->editGInt, str); - uiFree(str); + uiprivFree(str); } if (whichChanged != c->editBInt) { str = itoutf16(bb); setWindowText(c->editBInt, str); - uiFree(str); + uiprivFree(str); } if (whichChanged != c->editAInt) { str = itoutf16(ab); setWindowText(c->editAInt, str); - uiFree(str); + uiprivFree(str); } if (whichChanged != c->editHex) { @@ -956,7 +956,7 @@ static struct colorDialog *beginColorDialog(HWND hwnd, LPARAM lParam) { struct colorDialog *c; - c = uiNew(struct colorDialog); + c = uiprivNew(struct colorDialog); c->hwnd = hwnd; c->out = (struct colorDialogRGBA *) lParam; // load initial values now @@ -995,7 +995,7 @@ static void endColorDialog(struct colorDialog *c, INT_PTR code) { if (EndDialog(c->hwnd, code) == 0) logLastError(L"error ending color dialog"); - uiFree(c); + uiprivFree(c); } // TODO make this void on the font dialog too @@ -1020,7 +1020,7 @@ static double editDouble(HWND hwnd) s = windowText(hwnd); d = _wtof(s); - uiFree(s); + uiprivFree(s); return d; } @@ -1111,7 +1111,7 @@ static int editInt(HWND hwnd) s = windowText(hwnd); i = _wtoi(s); - uiFree(s); + uiprivFree(s); return i; } @@ -1176,7 +1176,7 @@ static void hexChanged(struct colorDialog *c) buf = windowText(c->editHex); is = hex2RGBA(buf, &r, &g, &b, &a); - uiFree(buf); + uiprivFree(buf); if (!is) return; rgb2HSV(r, g, b, &(c->h), &(c->s), &(c->v)); diff --git a/windows/combobox.cpp b/windows/combobox.cpp index 87c999ea..50f49dd7 100644 --- a/windows/combobox.cpp +++ b/windows/combobox.cpp @@ -66,7 +66,7 @@ void uiComboboxAppend(uiCombobox *c, const char *text) logLastError(L"error appending item to uiCombobox"); else if (res == (LRESULT) CB_ERRSPACE) logLastError(L"memory exhausted appending item to uiCombobox"); - uiFree(wtext); + uiprivFree(wtext); } int uiComboboxSelected(uiCombobox *c) diff --git a/windows/datetimepicker.cpp b/windows/datetimepicker.cpp index e105c2fd..342eb256 100644 --- a/windows/datetimepicker.cpp +++ b/windows/datetimepicker.cpp @@ -19,7 +19,7 @@ static WCHAR *expandYear(WCHAR *dts, int n) int ny = 0; // allocate more than we need to be safe - out = (WCHAR *) uiAlloc((n * 3) * sizeof (WCHAR), "WCHAR[]"); + out = (WCHAR *) uiprivAlloc((n * 3) * sizeof (WCHAR), "WCHAR[]"); q = out; for (p = dts; *p != L'\0'; p++) { // first, if the current character is a y, increment the number of consecutive ys @@ -73,17 +73,17 @@ static void setDateTimeFormat(HWND hwnd) ndate = GLI(LOCALE_SSHORTDATE, NULL, 0); if (ndate == 0) logLastError(L"error getting date string length"); - date = (WCHAR *) uiAlloc(ndate * sizeof (WCHAR), "WCHAR[]"); + date = (WCHAR *) uiprivAlloc(ndate * sizeof (WCHAR), "WCHAR[]"); if (GLI(LOCALE_SSHORTDATE, date, ndate) == 0) logLastError(L"error geting date string"); unexpandedDate = date; // so we can free it date = expandYear(unexpandedDate, ndate); - uiFree(unexpandedDate); + uiprivFree(unexpandedDate); ntime = GLI(LOCALE_STIMEFORMAT, NULL, 0); if (ndate == 0) logLastError(L"error getting time string length"); - time = (WCHAR *) uiAlloc(ntime * sizeof (WCHAR), "WCHAR[]"); + time = (WCHAR *) uiprivAlloc(ntime * sizeof (WCHAR), "WCHAR[]"); if (GLI(LOCALE_STIMEFORMAT, time, ntime) == 0) logLastError(L"error geting time string"); @@ -91,9 +91,9 @@ static void setDateTimeFormat(HWND hwnd) if (SendMessageW(hwnd, DTM_SETFORMAT, 0, (LPARAM) datetime) == 0) logLastError(L"error applying format string to date/time picker"); - uiFree(datetime); - uiFree(time); - uiFree(date); + uiprivFree(datetime); + uiprivFree(time); + uiprivFree(date); } // control implementation diff --git a/windows/debug.cpp b/windows/debug.cpp index cfafffdc..9bfbbf0f 100644 --- a/windows/debug.cpp +++ b/windows/debug.cpp @@ -26,7 +26,7 @@ HRESULT _logLastError(debugargs, const WCHAR *s) if (useFormatted) LocalFree(formatted); // ignore error printDebug(msg); - uiFree(msg); + uiprivFree(msg); DebugBreak(); SetLastError(le); @@ -53,7 +53,7 @@ HRESULT _logHRESULT(debugargs, const WCHAR *s, HRESULT hr) if (useFormatted) LocalFree(formatted); // ignore error printDebug(msg); - uiFree(msg); + uiprivFree(msg); DebugBreak(); return hr; @@ -71,14 +71,14 @@ void realbug(const char *file, const char *line, const char *func, const char *p va_end(ap2); n++; // terminating '\0' - msg = (char *) uiAlloc(n * sizeof (char), "char[]"); + msg = (char *) uiprivAlloc(n * sizeof (char), "char[]"); // includes terminating '\0' according to example in https://msdn.microsoft.com/en-us/library/xa1a1a6z.aspx vsprintf_s(msg, n, format, ap); final = strf(L"[libui] %hs:%hs:%hs() %hs%hs\n", file, line, func, prefix, msg); - uiFree(msg); + uiprivFree(msg); printDebug(final); - uiFree(final); + uiprivFree(final); DebugBreak(); } diff --git a/windows/draw.cpp b/windows/draw.cpp index 5f4d29f1..2fc9eed9 100644 --- a/windows/draw.cpp +++ b/windows/draw.cpp @@ -107,7 +107,7 @@ uiDrawContext *newContext(ID2D1RenderTarget *rt) { uiDrawContext *c; - c = uiNew(uiDrawContext); + c = uiprivNew(uiDrawContext); c->rt = rt; c->states = new std::vector; resetTarget(c->rt); @@ -122,7 +122,7 @@ void freeContext(uiDrawContext *c) // TODO do this on other platforms userbug("You did not balance uiDrawSave() and uiDrawRestore() calls."); delete c->states; - uiFree(c); + uiprivFree(c); } static ID2D1Brush *makeSolidBrush(uiDrawBrush *b, ID2D1RenderTarget *rt, D2D1_BRUSH_PROPERTIES *props) @@ -152,7 +152,7 @@ static ID2D1GradientStopCollection *mkstops(uiDrawBrush *b, ID2D1RenderTarget *r size_t i; HRESULT hr; - stops = (D2D1_GRADIENT_STOP *) uiAlloc(b->NumStops * sizeof (D2D1_GRADIENT_STOP), "D2D1_GRADIENT_STOP[]"); + stops = (D2D1_GRADIENT_STOP *) uiprivAlloc(b->NumStops * sizeof (D2D1_GRADIENT_STOP), "D2D1_GRADIENT_STOP[]"); for (i = 0; i < b->NumStops; i++) { stops[i].position = b->Stops[i].Pos; stops[i].color.r = b->Stops[i].R; @@ -170,7 +170,7 @@ static ID2D1GradientStopCollection *mkstops(uiDrawBrush *b, ID2D1RenderTarget *r if (hr != S_OK) logHRESULT(L"error creating stop collection", hr); - uiFree(stops); + uiprivFree(stops); return s; } @@ -365,7 +365,7 @@ void uiDrawStroke(uiDrawContext *c, uiDrawPath *p, uiDrawBrush *b, uiDrawStrokeP // TODO be sure to formally document this if (sp->NumDashes != 0) { dsp.dashStyle = D2D1_DASH_STYLE_CUSTOM; - dashes = (FLOAT *) uiAlloc(sp->NumDashes * sizeof (FLOAT), "FLOAT[]"); + dashes = (FLOAT *) uiprivAlloc(sp->NumDashes * sizeof (FLOAT), "FLOAT[]"); for (i = 0; i < sp->NumDashes; i++) dashes[i] = sp->Dashes[i] / sp->Thickness; } @@ -378,7 +378,7 @@ void uiDrawStroke(uiDrawContext *c, uiDrawPath *p, uiDrawBrush *b, uiDrawStrokeP if (hr != S_OK) logHRESULT(L"error creating stroke style", hr); if (sp->NumDashes != 0) - uiFree(dashes); + uiprivFree(dashes); cliplayer = applyClip(c); c->rt->DrawGeometry( diff --git a/windows/drawpath.cpp b/windows/drawpath.cpp index 045d11c0..8baa75a7 100644 --- a/windows/drawpath.cpp +++ b/windows/drawpath.cpp @@ -17,7 +17,7 @@ uiDrawPath *uiDrawNewPath(uiDrawFillMode fillmode) uiDrawPath *p; HRESULT hr; - p = uiNew(uiDrawPath); + p = uiprivNew(uiDrawPath); hr = d2dfactory->CreatePathGeometry(&(p->path)); if (hr != S_OK) logHRESULT(L"error creating path", hr); @@ -43,7 +43,7 @@ void uiDrawFreePath(uiDrawPath *p) // TODO close sink first? p->sink->Release(); p->path->Release(); - uiFree(p); + uiprivFree(p); } void uiDrawPathNewFigure(uiDrawPath *p, double x, double y) diff --git a/windows/drawtext.cpp b/windows/drawtext.cpp index 85accab1..ec2ae152 100644 --- a/windows/drawtext.cpp +++ b/windows/drawtext.cpp @@ -38,7 +38,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) FLOAT maxWidth; HRESULT hr; - tl = uiNew(uiDrawTextLayout); + tl = uiprivNew(uiDrawTextLayout); wDefaultFamily = toUTF16(p->DefaultFont->Family); hr = dwfactory->CreateTextFormat( @@ -51,7 +51,7 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) // TODO use the current locale? L"", &(tl->format)); - uiFree(wDefaultFamily); + uiprivFree(wDefaultFamily); if (hr != S_OK) logHRESULT(L"error creating IDWriteTextFormat", hr); hr = tl->format->SetTextAlignment(dwriteAligns[p->Align]); @@ -95,14 +95,14 @@ uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p) void uiDrawFreeTextLayout(uiDrawTextLayout *tl) { - uiFree(tl->u16tou8); - uiFree(tl->u8tou16); + uiprivFree(tl->u16tou8); + uiprivFree(tl->u8tou16); for (auto p : *(tl->backgroundParams)) uiprivFree(p); delete tl->backgroundParams; tl->layout->Release(); tl->format->Release(); - uiFree(tl); + uiprivFree(tl); } // TODO make this shared code somehow diff --git a/windows/dwrite.cpp b/windows/dwrite.cpp index 24a4aa3a..4d6b6741 100644 --- a/windows/dwrite.cpp +++ b/windows/dwrite.cpp @@ -81,7 +81,7 @@ WCHAR *uiprivFontCollectionCorrectString(fontCollection *fc, IDWriteLocalizedStr if (hr != S_OK) logHRESULT(L"error getting length of font name", hr); // GetStringLength() does not include the null terminator, but GetString() does - wname = (WCHAR *) uiAlloc((length + 1) * sizeof (WCHAR), "WCHAR[]"); + wname = (WCHAR *) uiprivAlloc((length + 1) * sizeof (WCHAR), "WCHAR[]"); hr = names->GetString(index, wname, length + 1); if (hr != S_OK) logHRESULT(L"error getting font name", hr); diff --git a/windows/editablecombo.cpp b/windows/editablecombo.cpp index 9e1fdbfb..138618d7 100644 --- a/windows/editablecombo.cpp +++ b/windows/editablecombo.cpp @@ -76,7 +76,7 @@ void uiEditableComboboxAppend(uiEditableCombobox *c, const char *text) logLastError(L"error appending item to uiEditableCombobox"); else if (res == (LRESULT) CB_ERRSPACE) logLastError(L"memory exhausted appending item to uiEditableCombobox"); - uiFree(wtext); + uiprivFree(wtext); } char *uiEditableComboboxText(uiEditableCombobox *c) diff --git a/windows/fontdialog.cpp b/windows/fontdialog.cpp index 2d10a1ae..ea02a416 100644 --- a/windows/fontdialog.cpp +++ b/windows/fontdialog.cpp @@ -121,7 +121,7 @@ static WCHAR *cbGetItemText(HWND cb, WPARAM item) len = SendMessageW(cb, CB_GETLBTEXTLEN, item, 0); if (len == (LRESULT) CB_ERR) logLastError(L"error getting item text length from combobox"); - text = (WCHAR *) uiAlloc((len + 1) * sizeof (WCHAR), "WCHAR[]"); + text = (WCHAR *) uiprivAlloc((len + 1) * sizeof (WCHAR), "WCHAR[]"); if (SendMessageW(cb, CB_GETLBTEXT, item, (LPARAM) text) != len) logLastError(L"error getting item text from combobox"); return text; @@ -138,7 +138,7 @@ static BOOL cbTypeToSelect(HWND cb, LRESULT *posOut, BOOL restoreAfter) text = windowText(cb); pos = SendMessageW(cb, CB_FINDSTRINGEXACT, (WPARAM) (-1), (LPARAM) text); if (pos == (LRESULT) CB_ERR) { - uiFree(text); + uiprivFree(text); return FALSE; } cbSetCurSel(cb, (WPARAM) pos); @@ -147,7 +147,7 @@ static BOOL cbTypeToSelect(HWND cb, LRESULT *posOut, BOOL restoreAfter) if (restoreAfter) if (SendMessageW(cb, WM_SETTEXT, 0, (LPARAM) text) != (LRESULT) TRUE) logLastError(L"error restoring old combobox text"); - uiFree(text); + uiprivFree(text); // and restore the selection like above // TODO isn't there a 32-bit version of this if (SendMessageW(cb, CB_SETEDITSEL, 0, MAKELPARAM(selStart, selEnd)) != (LRESULT) TRUE) @@ -254,7 +254,7 @@ static void familyChanged(struct fontDialog *f) logHRESULT(L"error getting font for filling styles box", hr); label = fontStyleName(f->fc, font); pos = cbAddString(f->styleCombobox, label); - uiFree(label); + uiprivFree(label); cbSetItemData(f->styleCombobox, (WPARAM) pos, (LPARAM) font); if (font->GetWeight() == weight && font->GetStyle() == style && @@ -386,7 +386,7 @@ static void fontDialogDrawSampleText(struct fontDialog *f, ID2D1RenderTarget *rt &format); if (hr != S_OK) logHRESULT(L"error creating IDWriteTextFormat", hr); - uiFree(family); + uiprivFree(family); rect.left = 0; rect.top = 0; @@ -402,7 +402,7 @@ static void fontDialogDrawSampleText(struct fontDialog *f, ID2D1RenderTarget *rt format->Release(); if (exists) - uiFree(sample); + uiprivFree(sample); black->Release(); } @@ -466,7 +466,7 @@ static struct fontDialog *beginFontDialog(HWND hwnd, LPARAM lParam) HWND samplePlacement; HRESULT hr; - f = uiNew(struct fontDialog); + f = uiprivNew(struct fontDialog); f->hwnd = hwnd; f->params = (struct fontDialogParams *) lParam; @@ -482,7 +482,7 @@ static struct fontDialog *beginFontDialog(HWND hwnd, LPARAM lParam) logHRESULT(L"error getting font family", hr); wname = uiprivFontCollectionFamilyName(f->fc, family); pos = cbAddString(f->familyCombobox, wname); - uiFree(wname); + uiprivFree(wname); cbSetItemData(f->familyCombobox, (WPARAM) pos, (LPARAM) family); } @@ -506,7 +506,7 @@ static void endFontDialog(struct fontDialog *f, INT_PTR code) uiprivFontCollectionFree(f->fc); if (EndDialog(f->hwnd, code) == 0) logLastError(L"error ending font dialog"); - uiFree(f); + uiprivFree(f); } static INT_PTR tryFinishDialog(struct fontDialog *f, WPARAM wParam) @@ -681,7 +681,7 @@ WCHAR *uiprivFontDialogParamsToString(struct fontDialogParams *params) WCHAR *text; // TODO dynamically allocate - text = (WCHAR *) uiAlloc(512 * sizeof (WCHAR), "WCHAR[]"); + text = (WCHAR *) uiprivAlloc(512 * sizeof (WCHAR), "WCHAR[]"); _snwprintf(text, 512, L"%s %s %g", params->familyName, params->styleName, diff --git a/windows/form.cpp b/windows/form.cpp index febcc693..ed194671 100644 --- a/windows/form.cpp +++ b/windows/form.cpp @@ -266,7 +266,7 @@ void uiFormAppend(uiForm *f, const char *label, uiControl *c, int stretchy) SS_LEFT | SS_NOPREFIX, hInstance, NULL, TRUE); - uiFree(wlabel); + uiprivFree(wlabel); uiWindowsEnsureSetParentHWND(fc.label, f->hwnd); fc.stretchy = stretchy; uiControlSetParent(fc.c, uiControl(f)); diff --git a/windows/graphemes.cpp b/windows/graphemes.cpp index 63e4882f..266cf97f 100644 --- a/windows/graphemes.cpp +++ b/windows/graphemes.cpp @@ -17,7 +17,7 @@ struct graphemes *uiprivNewGraphemes(void *s, size_t len) WCHAR *str; size_t *pPTG, *pGTP; - g = uiNew(struct graphemes); + g = uiprivNew(struct graphemes); g->len = 0; str = (WCHAR *) s; @@ -27,8 +27,8 @@ struct graphemes *uiprivNewGraphemes(void *s, size_t len) // no need to worry about surrogates if we're just counting } - g->pointsToGraphemes = (size_t *) uiAlloc((len + 1) * sizeof (size_t), "size_t[] (graphemes)"); - g->graphemesToPoints = (size_t *) uiAlloc((g->len + 1) * sizeof (size_t), "size_t[] (graphemes)"); + g->pointsToGraphemes = (size_t *) uiprivAlloc((len + 1) * sizeof (size_t), "size_t[] (graphemes)"); + g->graphemesToPoints = (size_t *) uiprivAlloc((g->len + 1) * sizeof (size_t), "size_t[] (graphemes)"); pPTG = g->pointsToGraphemes; pGTP = g->graphemesToPoints; diff --git a/windows/grid.cpp b/windows/grid.cpp index c63cd1e4..61e78543 100644 --- a/windows/grid.cpp +++ b/windows/grid.cpp @@ -418,7 +418,7 @@ static void uiGridDestroy(uiControl *c) for (struct gridChild *gc : *(g->children)) { uiControlSetParent(gc->c, NULL); uiControlDestroy(gc->c); - uiFree(gc); + uiprivFree(gc); } delete g->indexof; delete g->children; @@ -565,7 +565,7 @@ static struct gridChild *toChild(uiControl *c, int xspan, int yspan, int hexpand userbug("You cannot have a negative xspan in a uiGrid cell."); if (yspan < 0) userbug("You cannot have a negative yspan in a uiGrid cell."); - gc = uiNew(struct gridChild); + gc = uiprivNew(struct gridChild); gc->c = c; gc->xspan = xspan; gc->yspan = yspan; diff --git a/windows/group.cpp b/windows/group.cpp index 8824c5a4..1a2cc6ed 100644 --- a/windows/group.cpp +++ b/windows/group.cpp @@ -208,7 +208,7 @@ uiGroup *uiNewGroup(const char *text) BS_GROUPBOX, hInstance, NULL, TRUE); - uiFree(wtext); + uiprivFree(wtext); if (SetWindowSubclass(g->hwnd, groupSubProc, 0, (DWORD_PTR) g) == FALSE) logLastError(L"error subclassing groupbox to handle parent messages"); diff --git a/windows/init.cpp b/windows/init.cpp index 2c25d3d4..24831143 100644 --- a/windows/init.cpp +++ b/windows/init.cpp @@ -28,11 +28,11 @@ static const char *initerr(const char *message, const WCHAR *label, DWORD value) wmessage, value, value, sysmsg); - uiFree(wmessage); + uiprivFree(wmessage); if (hassysmsg) LocalFree(sysmsg); // ignore error out = toUTF8(wout); - uiFree(wout); + uiprivFree(wout); return out + 1; } @@ -157,7 +157,7 @@ void uiUninit(void) void uiFreeInitError(const char *err) { if (*(err - 1) == '-') - uiFree((void *) (err - 1)); + uiprivFree((void *) (err - 1)); } BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) diff --git a/windows/label.cpp b/windows/label.cpp index d74b7d18..94a0d88e 100644 --- a/windows/label.cpp +++ b/windows/label.cpp @@ -51,7 +51,7 @@ uiLabel *uiNewLabel(const char *text) SS_LEFTNOWORDWRAP | SS_NOPREFIX, hInstance, NULL, TRUE); - uiFree(wtext); + uiprivFree(wtext); return l; } diff --git a/windows/menu.cpp b/windows/menu.cpp index 6112fc13..46405faf 100644 --- a/windows/menu.cpp +++ b/windows/menu.cpp @@ -115,10 +115,10 @@ static uiMenuItem *newItem(uiMenu *m, int type, const char *name) if (m->len >= m->cap) { m->cap += grow; - m->items = (uiMenuItem **) uiRealloc(m->items, m->cap * sizeof (uiMenuItem *), "uiMenuitem *[]"); + m->items = (uiMenuItem **) uiprivRealloc(m->items, m->cap * sizeof (uiMenuItem *), "uiMenuitem *[]"); } - item = uiNew(uiMenuItem); + item = uiprivNew(uiMenuItem); m->items[m->len] = item; m->len++; @@ -207,10 +207,10 @@ uiMenu *uiNewMenu(const char *name) userbug("You can not create a new menu after menus have been finalized."); if (len >= cap) { cap += grow; - menus = (uiMenu **) uiRealloc(menus, cap * sizeof (uiMenu *), "uiMenu *[]"); + menus = (uiMenu **) uiprivRealloc(menus, cap * sizeof (uiMenu *), "uiMenu *[]"); } - m = uiNew(uiMenu); + m = uiprivNew(uiMenu); menus[len] = m; len++; @@ -237,7 +237,7 @@ static void appendMenuItem(HMENU menu, uiMenuItem *item) if (item->len >= item->cap) { item->cap += grow; - item->hmenus = (HMENU *) uiRealloc(item->hmenus, item->cap * sizeof (HMENU), "HMENU[]"); + item->hmenus = (HMENU *) uiprivRealloc(item->hmenus, item->cap * sizeof (HMENU), "HMENU[]"); } item->hmenus[item->len] = menu; item->len++; @@ -348,22 +348,22 @@ void uninitMenus(void) for (i = 0; i < len; i++) { m = menus[i]; - uiFree(m->name); + uiprivFree(m->name); for (j = 0; j < m->len; j++) { item = m->items[j]; if (item->len != 0) // LONGTERM userbug()? implbug("menu item %p (%ws) still has uiWindows attached; did you forget to destroy some windows?", item, item->name); if (item->name != NULL) - uiFree(item->name); + uiprivFree(item->name); if (item->hmenus != NULL) - uiFree(item->hmenus); - uiFree(item); + uiprivFree(item->hmenus); + uiprivFree(item); } if (m->items != NULL) - uiFree(m->items); - uiFree(m); + uiprivFree(m->items); + uiprivFree(m); } if (menus != NULL) - uiFree(menus); + uiprivFree(menus); } diff --git a/windows/multilineentry.cpp b/windows/multilineentry.cpp index a32960cb..391f4855 100644 --- a/windows/multilineentry.cpp +++ b/windows/multilineentry.cpp @@ -76,7 +76,7 @@ void uiMultilineEntrySetText(uiMultilineEntry *e, const char *text) e->inhibitChanged = TRUE; crlf = LFtoCRLF(text); uiWindowsSetWindowText(e->hwnd, text); - uiFree(crlf); + uiprivFree(crlf); e->inhibitChanged = FALSE; // don't queue the control for resize; entry sizes are independent of their contents } @@ -95,9 +95,9 @@ void uiMultilineEntryAppend(uiMultilineEntry *e, const char *text) SendMessageW(e->hwnd, EM_SETSEL, n, n); crlf = LFtoCRLF(text); wtext = toUTF16(crlf); - uiFree(crlf); + uiprivFree(crlf); SendMessageW(e->hwnd, EM_REPLACESEL, FALSE, (LPARAM) wtext); - uiFree(wtext); + uiprivFree(wtext); e->inhibitChanged = FALSE; } diff --git a/windows/radiobuttons.cpp b/windows/radiobuttons.cpp index 29cd2e66..0684a270 100644 --- a/windows/radiobuttons.cpp +++ b/windows/radiobuttons.cpp @@ -140,7 +140,7 @@ void uiRadioButtonsAppend(uiRadioButtons *r, const char *text) BS_RADIOBUTTON | groupTabStop, hInstance, NULL, TRUE); - uiFree(wtext); + uiprivFree(wtext); uiWindowsEnsureSetParentHWND(hwnd, r->hwnd); uiWindowsRegisterWM_COMMANDHandler(hwnd, onWM_COMMAND, uiControl(r)); r->hwnds->push_back(hwnd); diff --git a/windows/spinbox.cpp b/windows/spinbox.cpp index 8c4b666a..57fb8d64 100644 --- a/windows/spinbox.cpp +++ b/windows/spinbox.cpp @@ -48,10 +48,10 @@ static BOOL onWM_COMMAND(uiControl *c, HWND hwnd, WORD code, LRESULT *lResult) // This won't handle leading spaces, but spaces aren't allowed *anyway*. wtext = windowText(s->edit); if (wcscmp(wtext, L"-") == 0) { - uiFree(wtext); + uiprivFree(wtext); return TRUE; } - uiFree(wtext); + uiprivFree(wtext); // value() does the work for us value(s); (*(s->onChanged))(s, s->onChangedData); diff --git a/windows/stddialogs.cpp b/windows/stddialogs.cpp index 89d26bac..c6ac4557 100644 --- a/windows/stddialogs.cpp +++ b/windows/stddialogs.cpp @@ -114,8 +114,8 @@ static void msgbox(HWND parent, const char *title, const char *description, TASK if (hr != S_OK) logHRESULT(L"error showing task dialog", hr); - uiFree(wdescription); - uiFree(wtitle); + uiprivFree(wdescription); + uiprivFree(wtitle); } void uiMsgBox(uiWindow *parent, const char *title, const char *description) diff --git a/windows/tab.cpp b/windows/tab.cpp index 365f5a1f..e7239585 100644 --- a/windows/tab.cpp +++ b/windows/tab.cpp @@ -212,7 +212,7 @@ void uiTabInsertAt(uiTab *t, const char *name, int n, uiControl *child) item.pszText = wname; if (SendMessageW(t->tabHWND, TCM_INSERTITEM, (WPARAM) n, (LPARAM) (&item)) == (LRESULT) -1) logLastError(L"error adding tab to uiTab"); - uiFree(wname); + uiprivFree(wname); // we need to do this because adding the first tab doesn't send a TCN_SELCHANGE; it just shows the page show = curpage(t); diff --git a/windows/tabpage.cpp b/windows/tabpage.cpp index 77df9a18..dc98fb88 100644 --- a/windows/tabpage.cpp +++ b/windows/tabpage.cpp @@ -83,7 +83,7 @@ struct tabPage *newTabPage(uiControl *child) struct tabPage *tp; HRESULT hr; - tp = uiNew(struct tabPage); + tp = uiprivNew(struct tabPage); // unfortunately this needs to be a proper dialog for EnableThemeDialogTexture() to work; CreateWindowExW() won't suffice if (CreateDialogParamW(hInstance, MAKEINTRESOURCE(rcTabPageDialog), @@ -114,7 +114,7 @@ void tabPageDestroy(struct tabPage *tp) uiWindowsControlSetParentHWND(uiWindowsControl(tp->child), NULL); // don't call EndDialog(); that's for the DialogBox() family of functions instead of CreateDialog() uiWindowsEnsureDestroyWindow(tp->hwnd); - uiFree(tp); + uiprivFree(tp); } void tabPageMinimumSize(struct tabPage *tp, int *width, int *height) diff --git a/windows/text.cpp b/windows/text.cpp index e3a23570..eafbe714 100644 --- a/windows/text.cpp +++ b/windows/text.cpp @@ -10,7 +10,7 @@ WCHAR *windowTextAndLen(HWND hwnd, LRESULT *len) if (len != NULL) *len = n; // WM_GETTEXTLENGTH does not include the null terminator - text = (WCHAR *) uiAlloc((n + 1) * sizeof (WCHAR), "WCHAR[]"); + text = (WCHAR *) uiprivAlloc((n + 1) * sizeof (WCHAR), "WCHAR[]"); // note the comparison: the size includes the null terminator, but the return does not if (GetWindowTextW(hwnd, text, n + 1) != n) { logLastError(L"error getting window text"); @@ -35,7 +35,7 @@ void setWindowText(HWND hwnd, WCHAR *wtext) void uiFreeText(char *text) { - uiFree(text); + uiprivFree(text); } int uiWindowsWindowTextWidth(HWND hwnd) @@ -78,11 +78,11 @@ int uiWindowsWindowTextWidth(HWND hwnd) if (ReleaseDC(hwnd, dc) == 0) logLastError(L"error releasing DC"); - uiFree(text); + uiprivFree(text); return size.cx; noTextOrError: - uiFree(text); + uiprivFree(text); return 0; } @@ -93,7 +93,7 @@ char *uiWindowsWindowText(HWND hwnd) wtext = windowText(hwnd); text = toUTF8(wtext); - uiFree(wtext); + uiprivFree(wtext); return text; } @@ -103,7 +103,7 @@ void uiWindowsSetWindowText(HWND hwnd, const char *text) wtext = toUTF16(text); setWindowText(hwnd, wtext); - uiFree(wtext); + uiprivFree(wtext); } int uiprivStricmp(const char *a, const char *b) @@ -114,7 +114,7 @@ int uiprivStricmp(const char *a, const char *b) wa = toUTF16(a); wb = toUTF16(b); ret = _wcsicmp(wa, wb); - uiFree(wb); - uiFree(wa); + uiprivFree(wb); + uiprivFree(wa); return ret; } diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index 3244b7b4..7fb00efa 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -29,8 +29,8 @@ extern BOOL runWM_HSCROLL(WPARAM wParam, LPARAM lParam, LRESULT *lResult); extern void issueWM_WININICHANGE(WPARAM wParam, LPARAM lParam); // utf16.cpp -#define emptyUTF16() ((WCHAR *) uiAlloc(1 * sizeof (WCHAR), "WCHAR[]")) -#define emptyUTF8() ((char *) uiAlloc(1 * sizeof (char), "char[]")) +#define emptyUTF16() ((WCHAR *) uiprivAlloc(1 * sizeof (WCHAR), "WCHAR[]")) +#define emptyUTF8() ((char *) uiprivAlloc(1 * sizeof (char), "char[]")) extern WCHAR *toUTF16(const char *str); extern char *toUTF8(const WCHAR *wstr); extern WCHAR *utf16dup(const WCHAR *orig); diff --git a/windows/utf16.cpp b/windows/utf16.cpp index 21d2f8a5..6afd0b0e 100644 --- a/windows/utf16.cpp +++ b/windows/utf16.cpp @@ -13,7 +13,7 @@ WCHAR *toUTF16(const char *str) if (*str == '\0') // empty string return emptyUTF16(); n = utf8UTF16Count(str, 0); - wstr = (WCHAR *) uiAlloc((n + 1) * sizeof (WCHAR), "WCHAR[]"); + wstr = (WCHAR *) uiprivAlloc((n + 1) * sizeof (WCHAR), "WCHAR[]"); wp = wstr; while (*str) { str = utf8DecodeRune(str, 0, &rune); @@ -33,7 +33,7 @@ char *toUTF8(const WCHAR *wstr) if (*wstr == L'\0') // empty string return emptyUTF8(); n = utf16RuneCount(wstr, 0); - str = (char *) uiAlloc((n + 1) * sizeof (char), "char[]"); + str = (char *) uiprivAlloc((n + 1) * sizeof (char), "char[]"); sp = str; while (*wstr) { wstr = utf16DecodeRune(wstr, 0, &rune); @@ -49,7 +49,7 @@ WCHAR *utf16dup(const WCHAR *orig) size_t len; len = wcslen(orig); - out = (WCHAR *) uiAlloc((len + 1) * sizeof (WCHAR), "WCHAR[]"); + out = (WCHAR *) uiprivAlloc((len + 1) * sizeof (WCHAR), "WCHAR[]"); wcscpy_s(out, len + 1, orig); return out; } @@ -79,7 +79,7 @@ WCHAR *vstrf(const WCHAR *format, va_list ap) va_end(ap2); n++; // terminating L'\0' - buf = (WCHAR *) uiAlloc(n * sizeof (WCHAR), "WCHAR[]"); + buf = (WCHAR *) uiprivAlloc(n * sizeof (WCHAR), "WCHAR[]"); // includes terminating L'\0' according to example in https://msdn.microsoft.com/en-us/library/xa1a1a6z.aspx vswprintf_s(buf, n, format, ap); @@ -97,7 +97,7 @@ char *LFtoCRLF(const char *lfonly) char *out; len = strlen(lfonly); - crlf = (char *) uiAlloc((len * 2 + 1) * sizeof (char), "char[]"); + crlf = (char *) uiprivAlloc((len * 2 + 1) * sizeof (char), "char[]"); out = crlf; for (i = 0; i < len; i++) { if (*lfonly == '\n') diff --git a/windows/window.cpp b/windows/window.cpp index 34baf545..2ea5b7ce 100644 --- a/windows/window.cpp +++ b/windows/window.cpp @@ -480,7 +480,7 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) NULL, NULL, hInstance, w); if (w->hwnd == NULL) logLastError(L"error creating window"); - uiFree(wtitle); + uiprivFree(wtitle); if (hasMenubar) { w->menubar = makeMenubar(); From f93973d3cbe4ecb1cd3f484227dee74c742b791f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 Apr 2018 21:46:08 -0400 Subject: [PATCH 0972/1329] Migrated implbug() and userbug() to uipriv forms. --- common/OLD_uipriv.h | 12 ------------ common/control.c | 8 ++++---- common/debug.c | 8 ++++---- common/uipriv.h | 24 +++++++++++++++--------- common/userbugs.c | 2 +- darwin/alloc.m | 4 ++-- darwin/area.m | 4 ++-- darwin/box.m | 2 +- darwin/debug.m | 2 +- darwin/draw.m | 22 +++++++++++----------- darwin/form.m | 2 +- darwin/grid.m | 8 ++++---- darwin/main.m | 7 +++---- darwin/map.m | 2 +- darwin/menu.m | 12 ++++++------ darwin/progressbar.m | 2 +- unix/alloc.c | 8 ++++---- unix/area.c | 6 +++--- unix/debug.c | 2 +- unix/draw.c | 2 +- unix/drawpath.c | 4 ++-- unix/menu.c | 18 +++++++++--------- unix/progressbar.c | 2 +- windows/alloc.cpp | 4 ++-- windows/datetimepicker.cpp | 2 +- windows/debug.cpp | 2 +- windows/draw.cpp | 4 ++-- windows/drawpath.cpp | 2 +- windows/events.cpp | 16 ++++++++-------- windows/grid.cpp | 4 ++-- windows/menu.cpp | 22 +++++++++++----------- windows/progressbar.cpp | 2 +- 32 files changed, 107 insertions(+), 114 deletions(-) diff --git a/common/OLD_uipriv.h b/common/OLD_uipriv.h index 64d82ad1..ac8ec0cc 100644 --- a/common/OLD_uipriv.h +++ b/common/OLD_uipriv.h @@ -1,16 +1,4 @@ -// ugh, this was only introduced in MSVC 2015... -#ifdef _MSC_VER -#define __func__ __FUNCTION__ -#endif -extern void realbug(const char *file, const char *line, const char *func, const char *prefix, const char *format, va_list ap); -#define _ns2(s) #s -#define _ns(s) _ns2(s) -extern void _implbug(const char *file, const char *line, const char *func, const char *format, ...); -#define implbug(...) _implbug(__FILE__, _ns(__LINE__), __func__, __VA_ARGS__) -extern void _userbug(const char *file, const char *line, const char *func, const char *format, ...); -#define userbug(...) _userbug(__FILE__, _ns(__LINE__), __func__, __VA_ARGS__) - // control.c extern uiControl *newControl(size_t size, uint32_t OSsig, uint32_t typesig, const char *typenamestr); diff --git a/common/control.c b/common/control.c index e46e322c..3b5b8286 100644 --- a/common/control.c +++ b/common/control.c @@ -73,7 +73,7 @@ uiControl *uiAllocControl(size_t size, uint32_t OSsig, uint32_t typesig, const c void uiFreeControl(uiControl *c) { if (uiControlParent(c) != NULL) - userbug("You cannot destroy a uiControl while it still has a parent. (control: %p)", c); + uiprivUserBug("You cannot destroy a uiControl while it still has a parent. (control: %p)", c); uiprivFree(c); } @@ -82,12 +82,12 @@ void uiControlVerifySetParent(uiControl *c, uiControl *parent) uiControl *curParent; if (uiControlToplevel(c)) - userbug("You cannot give a toplevel uiControl a parent. (control: %p)", c); + uiprivUserBug("You cannot give a toplevel uiControl a parent. (control: %p)", c); curParent = uiControlParent(c); if (parent != NULL && curParent != NULL) - userbug("You cannot give a uiControl a parent while it already has one. (control: %p; current parent: %p; new parent: %p)", c, curParent, parent); + uiprivUserBug("You cannot give a uiControl a parent while it already has one. (control: %p; current parent: %p; new parent: %p)", c, curParent, parent); if (parent == NULL && curParent == NULL) - implbug("attempt to double unparent uiControl %p", c); + uiprivImplBug("attempt to double unparent uiControl %p", c); } int uiControlEnabledToUser(uiControl *c) diff --git a/common/debug.c b/common/debug.c index 97280b47..aa24e29f 100644 --- a/common/debug.c +++ b/common/debug.c @@ -2,20 +2,20 @@ #include "../ui.h" #include "uipriv.h" -void _implbug(const char *file, const char *line, const char *func, const char *format, ...) +void uiprivDoImplBug(const char *file, const char *line, const char *func, const char *format, ...) { va_list ap; va_start(ap, format); - realbug(file, line, func, "POSSIBLE IMPLEMENTATION BUG; CONTACT ANDLABS:\n", format, ap); + uiprivRealBug(file, line, func, "POSSIBLE IMPLEMENTATION BUG; CONTACT ANDLABS:\n", format, ap); va_end(ap); } -void _userbug(const char *file, const char *line, const char *func, const char *format, ...) +void uiprivDoUserBug(const char *file, const char *line, const char *func, const char *format, ...) { va_list ap; va_start(ap, format); - realbug(file, line, func, "You have a bug: ", format, ap); + uiprivRealBug(file, line, func, "You have a bug: ", format, ap); va_end(ap); } diff --git a/common/uipriv.h b/common/uipriv.h index 69062c87..530a9794 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -8,24 +8,30 @@ extern "C" { #endif +// OS-specific init.* or main.* files extern uiInitOptions uiprivOptions; +// OS-specific alloc.* files extern void *uiprivAlloc(size_t, const char *); #define uiprivNew(T) ((T *) uiprivAlloc(sizeof (T), #T)) extern void *uiprivRealloc(void *, size_t, const char *); extern void uiprivFree(void *); -// ugh, this was only introduced in MSVC 2015... +// debug.c and OS-specific debug.* files +// TODO get rid of this mess... +// ugh, __func__ was only introduced in MSVC 2015... #ifdef _MSC_VER -#define __func__ __FUNCTION__ +#define uiprivMacro__func__ __FUNCTION__ +#else +#define uiprivMacro__func__ __func__ #endif -extern void realbug(const char *file, const char *line, const char *func, const char *prefix, const char *format, va_list ap); -#define _ns2(s) #s -#define _ns(s) _ns2(s) -extern void _implbug(const char *file, const char *line, const char *func, const char *format, ...); -#define implbug(...) _implbug(__FILE__, _ns(__LINE__), __func__, __VA_ARGS__) -extern void _userbug(const char *file, const char *line, const char *func, const char *format, ...); -#define userbug(...) _userbug(__FILE__, _ns(__LINE__), __func__, __VA_ARGS__) +extern void uiprivRealBug(const char *file, const char *line, const char *func, const char *prefix, const char *format, va_list ap); +#define uiprivMacro_ns2(s) #s +#define uiprivMacro_ns(s) uiprivMacro_ns2(s) +extern void uiprivDoImplBug(const char *file, const char *line, const char *func, const char *format, ...); +#define uiprivImplBug(...) uiprivDoImplBug(__FILE__, uiprivMacro_ns(__LINE__), uiprivMacro__func__, __VA_ARGS__) +extern void uiprivDoUserBug(const char *file, const char *line, const char *func, const char *format, ...); +#define uiprivUserBug(...) uiprivDoUserBug(__FILE__, uiprivMacro_ns(__LINE__), uiprivMacro__func__, __VA_ARGS__) // control.c extern uiControl *newControl(size_t size, uint32_t OSsig, uint32_t typesig, const char *typenamestr); diff --git a/common/userbugs.c b/common/userbugs.c index 0a85874c..09cc703d 100644 --- a/common/userbugs.c +++ b/common/userbugs.c @@ -4,5 +4,5 @@ void uiUserBugCannotSetParentOnToplevel(const char *type) { - userbug("You cannot make a %s a child of another uiControl,", type); + uiprivUserBug("You cannot make a %s a child of another uiControl,", type); } diff --git a/darwin/alloc.m b/darwin/alloc.m index 0012c853..fbafc153 100644 --- a/darwin/alloc.m +++ b/darwin/alloc.m @@ -37,7 +37,7 @@ void uninitAlloc(void) ptr = [v pointerValue]; [str appendString:[NSString stringWithFormat:@"%p %s\n", ptr, *TYPE(ptr)]]; } - userbug("Some data was leaked; either you left a uiControl lying around or there's a bug in libui itself. Leaked data:\n%s", [str UTF8String]); + uiprivUserBug("Some data was leaked; either you left a uiControl lying around or there's a bug in libui itself. Leaked data:\n%s", [str UTF8String]); [str release]; } @@ -82,7 +82,7 @@ void *uiprivRealloc(void *p, size_t new, const char *type) void uiprivFree(void *p) { if (p == NULL) - implbug("attempt to uiprivFree(NULL)"); + uiprivImplBug("attempt to uiprivFree(NULL)"); p = BASE(p); free(p); [allocations removeObject:[NSValue valueWithPointer:p]]; diff --git a/darwin/area.m b/darwin/area.m index 23162e6c..1442479a 100644 --- a/darwin/area.m +++ b/darwin/area.m @@ -390,7 +390,7 @@ int sendAreaEvents(NSEvent *e) void uiAreaSetSize(uiArea *a, int width, int height) { if (!a->scrolling) - userbug("You cannot call uiAreaSetSize() on a non-scrolling uiArea. (area: %p)", a); + uiprivUserBug("You cannot call uiAreaSetSize() on a non-scrolling uiArea. (area: %p)", a); [a->area setScrollingSize:NSMakeSize(width, height)]; } @@ -402,7 +402,7 @@ void uiAreaQueueRedrawAll(uiArea *a) void uiAreaScrollTo(uiArea *a, double x, double y, double width, double height) { if (!a->scrolling) - userbug("You cannot call uiAreaScrollTo() on a non-scrolling uiArea. (area: %p)", a); + uiprivUserBug("You cannot call uiAreaScrollTo() on a non-scrolling uiArea. (area: %p)", a); [a->area scrollRectToVisible:NSMakeRect(x, y, width, height)]; // don't worry about the return value; it just says whether scrolling was needed } diff --git a/darwin/box.m b/darwin/box.m index 18d536d5..6a1941ea 100644 --- a/darwin/box.m +++ b/darwin/box.m @@ -428,7 +428,7 @@ void uiBoxAppend(uiBox *b, uiControl *c, int stretchy) // LONGTERM on other platforms // or at leat allow this and implicitly turn it into a spacer if (c == NULL) - userbug("You cannot add NULL to a uiBox."); + uiprivUserBug("You cannot add NULL to a uiBox."); [b->view append:c stretchy:stretchy]; } diff --git a/darwin/debug.m b/darwin/debug.m index c91c6a73..aff66e0d 100644 --- a/darwin/debug.m +++ b/darwin/debug.m @@ -3,7 +3,7 @@ // LONGTERM don't halt on release builds -void realbug(const char *file, const char *line, const char *func, const char *prefix, const char *format, va_list ap) +void uiprivRealBug(const char *file, const char *line, const char *func, const char *prefix, const char *format, va_list ap) { NSMutableString *str; NSString *formatted; diff --git a/darwin/draw.m b/darwin/draw.m index b52b5a57..7ca854d2 100644 --- a/darwin/draw.m +++ b/darwin/draw.m @@ -27,7 +27,7 @@ void uiDrawFreePath(uiDrawPath *p) void uiDrawPathNewFigure(uiDrawPath *p, double x, double y) { if (p->ended) - userbug("You cannot call uiDrawPathNewFigure() on a uiDrawPath that has already been ended. (path; %p)", p); + uiprivUserBug("You cannot call uiDrawPathNewFigure() on a uiDrawPath that has already been ended. (path; %p)", p); CGPathMoveToPoint(p->path, NULL, x, y); } @@ -37,7 +37,7 @@ void uiDrawPathNewFigureWithArc(uiDrawPath *p, double xCenter, double yCenter, d double startx, starty; if (p->ended) - userbug("You cannot call uiDrawPathNewFigureWithArc() on a uiDrawPath that has already been ended. (path; %p)", p); + uiprivUserBug("You cannot call uiDrawPathNewFigureWithArc() on a uiDrawPath that has already been ended. (path; %p)", p); sinStart = sin(startAngle); cosStart = cos(startAngle); startx = xCenter + radius * cosStart; @@ -50,7 +50,7 @@ void uiDrawPathLineTo(uiDrawPath *p, double x, double y) { // TODO refine this to require being in a path if (p->ended) - implbug("attempt to add line to ended path in uiDrawPathLineTo()"); + uiprivImplBug("attempt to add line to ended path in uiDrawPathLineTo()"); CGPathAddLineToPoint(p->path, NULL, x, y); } @@ -60,7 +60,7 @@ void uiDrawPathArcTo(uiDrawPath *p, double xCenter, double yCenter, double radiu // TODO likewise if (p->ended) - implbug("attempt to add arc to ended path in uiDrawPathArcTo()"); + uiprivImplBug("attempt to add arc to ended path in uiDrawPathArcTo()"); if (sweep > 2 * uiPi) sweep = 2 * uiPi; cw = false; @@ -77,7 +77,7 @@ void uiDrawPathBezierTo(uiDrawPath *p, double c1x, double c1y, double c2x, doubl { // TODO likewise if (p->ended) - implbug("attempt to add bezier to ended path in uiDrawPathBezierTo()"); + uiprivImplBug("attempt to add bezier to ended path in uiDrawPathBezierTo()"); CGPathAddCurveToPoint(p->path, NULL, c1x, c1y, c2x, c2y, @@ -88,14 +88,14 @@ void uiDrawPathCloseFigure(uiDrawPath *p) { // TODO likewise if (p->ended) - implbug("attempt to close figure of ended path in uiDrawPathCloseFigure()"); + uiprivImplBug("attempt to close figure of ended path in uiDrawPathCloseFigure()"); CGPathCloseSubpath(p->path); } void uiDrawPathAddRectangle(uiDrawPath *p, double x, double y, double width, double height) { if (p->ended) - userbug("You cannot call uiDrawPathAddRectangle() on a uiDrawPath that has already been ended. (path; %p)", p); + uiprivUserBug("You cannot call uiDrawPathAddRectangle() on a uiDrawPath that has already been ended. (path; %p)", p); CGPathAddRect(p->path, NULL, CGRectMake(x, y, width, height)); } @@ -132,7 +132,7 @@ void uiDrawStroke(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b, uiDrawStro uiDrawPath p2; if (!path->ended) - userbug("You cannot call uiDrawStroke() on a uiDrawPath that has not been ended. (path: %p)", path); + uiprivUserBug("You cannot call uiDrawStroke() on a uiDrawPath that has not been ended. (path: %p)", path); switch (p->Cap) { case uiDrawLineCapFlat: @@ -280,7 +280,7 @@ static void fillGradient(CGContextRef ctxt, uiDrawPath *p, uiDrawBrush *b) void uiDrawFill(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b) { if (!path->ended) - userbug("You cannot call uiDrawStroke() on a uiDrawPath that has not been ended. (path: %p)", path); + uiprivUserBug("You cannot call uiDrawStroke() on a uiDrawPath that has not been ended. (path: %p)", path); CGContextAddPath(c->c, (CGPathRef) (path->path)); switch (b->Type) { case uiDrawBrushTypeSolid: @@ -294,7 +294,7 @@ void uiDrawFill(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b) // TODO return; } - userbug("Unknown brush type %d passed to uiDrawFill().", b->Type); + uiprivUserBug("Unknown brush type %d passed to uiDrawFill().", b->Type); } static void m2c(uiDrawMatrix *m, CGAffineTransform *c) @@ -425,7 +425,7 @@ void uiDrawTransform(uiDrawContext *c, uiDrawMatrix *m) void uiDrawClip(uiDrawContext *c, uiDrawPath *path) { if (!path->ended) - userbug("You cannot call uiDrawCilp() on a uiDrawPath that has not been ended. (path: %p)", path); + uiprivUserBug("You cannot call uiDrawCilp() on a uiDrawPath that has not been ended. (path: %p)", path); CGContextAddPath(c->c, (CGPathRef) (path->path)); switch (path->fillMode) { case uiDrawFillModeWinding: diff --git a/darwin/form.m b/darwin/form.m index 7cdb965a..613818a9 100644 --- a/darwin/form.m +++ b/darwin/form.m @@ -530,7 +530,7 @@ void uiFormAppend(uiForm *f, const char *label, uiControl *c, int stretchy) // LONGTERM on other platforms // or at leat allow this and implicitly turn it into a spacer if (c == NULL) - userbug("You cannot add NULL to a uiForm."); + uiprivUserBug("You cannot add NULL to a uiForm."); [f->view append:toNSString(label) c:c stretchy:stretchy]; } diff --git a/darwin/grid.m b/darwin/grid.m index fc98cc9f..218a5f63 100644 --- a/darwin/grid.m +++ b/darwin/grid.m @@ -574,7 +574,7 @@ struct uiGrid { break; } if (!found) - userbug("Existing control %p is not in grid %p; you cannot add other controls next to it", c, self->g); + uiprivUserBug("Existing control %p is not in grid %p; you cannot add other controls next to it", c, self->g); switch (at) { case uiAtLeading: @@ -742,9 +742,9 @@ static gridChild *toChild(uiControl *c, int xspan, int yspan, int hexpand, uiAli gridChild *gc; if (xspan < 0) - userbug("You cannot have a negative xspan in a uiGrid cell."); + uiprivUserBug("You cannot have a negative xspan in a uiGrid cell."); if (yspan < 0) - userbug("You cannot have a negative yspan in a uiGrid cell."); + uiprivUserBug("You cannot have a negative yspan in a uiGrid cell."); gc = [gridChild new]; gc.xspan = xspan; gc.yspan = yspan; @@ -763,7 +763,7 @@ void uiGridAppend(uiGrid *g, uiControl *c, int left, int top, int xspan, int ysp // LONGTERM on other platforms // or at leat allow this and implicitly turn it into a spacer if (c == NULL) - userbug("You cannot add NULL to a uiGrid."); + uiprivUserBug("You cannot add NULL to a uiGrid."); gc = toChild(c, xspan, yspan, hexpand, halign, vexpand, valign, g); gc.left = left; gc.top = top; diff --git a/darwin/main.m b/darwin/main.m index 1bc6bfc3..f3392ce6 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -57,7 +57,7 @@ static BOOL stepsIsRunning; NSEvent *e; if (!canQuit) - implbug("call to [NSApp terminate:] when not ready to terminate; definitely contact andlabs"); + uiprivImplBug("call to [NSApp terminate:] when not ready to terminate; definitely contact andlabs"); [realNSApp() stop:realNSApp()]; // stop: won't register until another event has passed; let's synthesize one @@ -139,9 +139,8 @@ const char *uiInit(uiInitOptions *o) void uiUninit(void) { - if (!globalPool) { - userbug("You must call uiInit() first!"); - } + if (!globalPool) + uiprivUserBug("You must call uiInit() first!"); [globalPool release]; @autoreleasepool { diff --git a/darwin/map.m b/darwin/map.m index 4eaa057d..190218a1 100644 --- a/darwin/map.m +++ b/darwin/map.m @@ -22,7 +22,7 @@ struct mapTable *newMap(void) void mapDestroy(struct mapTable *m) { if ([m->m count] != 0) - implbug("attempt to destroy map with items inside"); + uiprivImplBug("attempt to destroy map with items inside"); [m->m release]; uiprivFree(m); } diff --git a/darwin/menu.m b/darwin/menu.m index 11a98e63..ca6cce4b 100644 --- a/darwin/menu.m +++ b/darwin/menu.m @@ -80,17 +80,17 @@ static void mapItemReleaser(void *key, void *value) switch (smi->type) { case typeQuit: if (self->hasQuit) - userbug("You can't have multiple Quit menu items in one program."); + uiprivUserBug("You can't have multiple Quit menu items in one program."); self->hasQuit = YES; break; case typePreferences: if (self->hasPreferences) - userbug("You can't have multiple Preferences menu items in one program."); + uiprivUserBug("You can't have multiple Preferences menu items in one program."); self->hasPreferences = YES; break; case typeAbout: if (self->hasAbout) - userbug("You can't have multiple About menu items in one program."); + uiprivUserBug("You can't have multiple About menu items in one program."); self->hasAbout = YES; break; } @@ -212,7 +212,7 @@ void uiMenuItemDisable(uiMenuItem *item) void uiMenuItemOnClicked(uiMenuItem *item, void (*f)(uiMenuItem *, uiWindow *, void *), void *data) { if (item->type == typeQuit) - userbug("You can't call uiMenuItemOnClicked() on a Quit item; use uiOnShouldQuit() instead."); + uiprivUserBug("You can't call uiMenuItemOnClicked() on a Quit item; use uiOnShouldQuit() instead."); item->onClicked = f; item->onClickedData = data; } @@ -239,7 +239,7 @@ static uiMenuItem *newItem(uiMenu *m, int type, const char *name) uiMenuItem *item; if (menusFinalized) - userbug("You can't create a new menu item after menus have been finalized."); + uiprivUserBug("You can't create a new menu item after menus have been finalized."); item = uiprivNew(uiMenuItem); @@ -315,7 +315,7 @@ uiMenu *uiNewMenu(const char *name) uiMenu *m; if (menusFinalized) - userbug("You can't create a new menu after menus have been finalized."); + uiprivUserBug("You can't create a new menu after menus have been finalized."); if (menus == nil) menus = [NSMutableArray new]; diff --git a/darwin/progressbar.m b/darwin/progressbar.m index b5382281..1f5390ff 100644 --- a/darwin/progressbar.m +++ b/darwin/progressbar.m @@ -48,7 +48,7 @@ void uiProgressBarSetValue(uiProgressBar *p, int value) } if (value < 0 || value > 100) - userbug("Value %d out of range for a uiProgressBar.", value); + uiprivUserBug("Value %d out of range for a uiProgressBar.", value); // on 10.8 there's an animation when the progress bar increases, just like with Aero if (value == 100) { diff --git a/unix/alloc.c b/unix/alloc.c index 98eb0e32..2fdd2052 100644 --- a/unix/alloc.c +++ b/unix/alloc.c @@ -39,7 +39,7 @@ void uninitAlloc(void) return; } g_ptr_array_foreach(allocations, uninitComplain, &str); - userbug("Some data was leaked; either you left a uiControl lying around or there's a bug in libui itself. Leaked data:\n%s", str); + uiprivUserBug("Some data was leaked; either you left a uiControl lying around or there's a bug in libui itself. Leaked data:\n%s", str); g_free(str); } @@ -68,7 +68,7 @@ void *uiprivRealloc(void *p, size_t new, const char *type) memset(((uint8_t *) DATA(out)) + *s, 0, new - *s); *s = new; if (g_ptr_array_remove(allocations, p) == FALSE) - implbug("%p not found in allocations array in uiprivRealloc()", p); + uiprivImplBug("%p not found in allocations array in uiprivRealloc()", p); g_ptr_array_add(allocations, out); return DATA(out); } @@ -76,9 +76,9 @@ void *uiprivRealloc(void *p, size_t new, const char *type) void uiprivFree(void *p) { if (p == NULL) - implbug("attempt to uiprivFree(NULL)"); + uiprivImplBug("attempt to uiprivFree(NULL)"); p = BASE(p); g_free(p); if (g_ptr_array_remove(allocations, p) == FALSE) - implbug("%p not found in allocations array in uiprivFree()", p); + uiprivImplBug("%p not found in allocations array in uiprivFree()", p); } diff --git a/unix/area.c b/unix/area.c index 24cd9513..abf868ca 100644 --- a/unix/area.c +++ b/unix/area.c @@ -499,7 +499,7 @@ uiUnixControlAllDefaults(uiArea) void uiAreaSetSize(uiArea *a, int width, int height) { if (!a->scrolling) - userbug("You cannot call uiAreaSetSize() on a non-scrolling uiArea. (area: %p)", a); + uiprivUserBug("You cannot call uiAreaSetSize() on a non-scrolling uiArea. (area: %p)", a); a->scrollWidth = width; a->scrollHeight = height; gtk_widget_queue_resize(a->areaWidget); @@ -521,7 +521,7 @@ void uiAreaBeginUserWindowMove(uiArea *a) GtkWidget *toplevel; if (a->dragevent == NULL) - userbug("cannot call uiAreaBeginUserWindowMove() outside of a Mouse() with Down != 0"); + uiprivUserBug("cannot call uiAreaBeginUserWindowMove() outside of a Mouse() with Down != 0"); // TODO don't we have a libui function for this? did I scrap it? // TODO widget or areaWidget? toplevel = gtk_widget_get_toplevel(a->widget); @@ -561,7 +561,7 @@ void uiAreaBeginUserWindowResize(uiArea *a, uiWindowResizeEdge edge) GtkWidget *toplevel; if (a->dragevent == NULL) - userbug("cannot call uiAreaBeginUserWindowResize() outside of a Mouse() with Down != 0"); + uiprivUserBug("cannot call uiAreaBeginUserWindowResize() outside of a Mouse() with Down != 0"); // TODO don't we have a libui function for this? did I scrap it? // TODO widget or areaWidget? toplevel = gtk_widget_get_toplevel(a->widget); diff --git a/unix/debug.c b/unix/debug.c index c948db62..fd97c9ed 100644 --- a/unix/debug.c +++ b/unix/debug.c @@ -3,7 +3,7 @@ // LONGTERM don't halt on release builds -void realbug(const char *file, const char *line, const char *func, const char *prefix, const char *format, va_list ap) +void uiprivRealBug(const char *file, const char *line, const char *func, const char *prefix, const char *format, va_list ap) { char *a, *b; diff --git a/unix/draw.c b/unix/draw.c index 1eacee33..15abb611 100644 --- a/unix/draw.c +++ b/unix/draw.c @@ -39,7 +39,7 @@ static cairo_pattern_t *mkbrush(uiDrawBrush *b) // case uiDrawBrushTypeImage: } if (cairo_pattern_status(pat) != CAIRO_STATUS_SUCCESS) - implbug("error creating pattern in mkbrush(): %s", + uiprivImplBug("error creating pattern in mkbrush(): %s", cairo_status_to_string(cairo_pattern_status(pat))); switch (b->Type) { case uiDrawBrushTypeLinearGradient: diff --git a/unix/drawpath.c b/unix/drawpath.c index 384743dc..28eeb981 100644 --- a/unix/drawpath.c +++ b/unix/drawpath.c @@ -43,7 +43,7 @@ void uiDrawFreePath(uiDrawPath *p) static void add(uiDrawPath *p, struct piece *piece) { if (p->ended) - userbug("You cannot modify a uiDrawPath that has been ended. (path: %p)", p); + uiprivUserBug("You cannot modify a uiDrawPath that has been ended. (path: %p)", p); g_array_append_vals(p->pieces, piece, 1); } @@ -145,7 +145,7 @@ void runPath(uiDrawPath *p, cairo_t *cr) void (*arc)(cairo_t *, double, double, double, double, double); if (!p->ended) - userbug("You cannot draw with a uiDrawPath that has not been ended. (path: %p)", p); + uiprivUserBug("You cannot draw with a uiDrawPath that has not been ended. (path: %p)", p); cairo_new_path(cr); for (i = 0; i < p->pieces->len; i++) { piece = &g_array_index(p->pieces, struct piece, i); diff --git a/unix/menu.c b/unix/menu.c index 1d950c5f..3d02e939 100644 --- a/unix/menu.c +++ b/unix/menu.c @@ -109,7 +109,7 @@ void uiMenuItemDisable(uiMenuItem *item) void uiMenuItemOnClicked(uiMenuItem *item, void (*f)(uiMenuItem *, uiWindow *, void *), void *data) { if (item->type == typeQuit) - userbug("You cannot call uiMenuItemOnClicked() on a Quit item; use uiOnShouldQuit() instead."); + uiprivUserBug("You cannot call uiMenuItemOnClicked() on a Quit item; use uiOnShouldQuit() instead."); item->onClicked = f; item->onClickedData = data; } @@ -135,7 +135,7 @@ static uiMenuItem *newItem(uiMenu *m, int type, const char *name) uiMenuItem *item; if (menusFinalized) - userbug("You cannot create a new menu item after menus have been finalized."); + uiprivUserBug("You cannot create a new menu item after menus have been finalized."); item = uiprivNew(uiMenuItem); @@ -196,7 +196,7 @@ uiMenuItem *uiMenuAppendCheckItem(uiMenu *m, const char *name) uiMenuItem *uiMenuAppendQuitItem(uiMenu *m) { if (hasQuit) - userbug("You cannot have multiple Quit menu items in the same program."); + uiprivUserBug("You cannot have multiple Quit menu items in the same program."); hasQuit = TRUE; newItem(m, typeSeparator, NULL); return newItem(m, typeQuit, NULL); @@ -205,7 +205,7 @@ uiMenuItem *uiMenuAppendQuitItem(uiMenu *m) uiMenuItem *uiMenuAppendPreferencesItem(uiMenu *m) { if (hasPreferences) - userbug("You cannot have multiple Preferences menu items in the same program."); + uiprivUserBug("You cannot have multiple Preferences menu items in the same program."); hasPreferences = TRUE; newItem(m, typeSeparator, NULL); return newItem(m, typePreferences, NULL); @@ -214,7 +214,7 @@ uiMenuItem *uiMenuAppendPreferencesItem(uiMenu *m) uiMenuItem *uiMenuAppendAboutItem(uiMenu *m) { if (hasAbout) - userbug("You cannot have multiple About menu items in the same program."); + uiprivUserBug("You cannot have multiple About menu items in the same program."); hasAbout = TRUE; newItem(m, typeSeparator, NULL); return newItem(m, typeAbout, NULL); @@ -230,7 +230,7 @@ uiMenu *uiNewMenu(const char *name) uiMenu *m; if (menusFinalized) - userbug("You cannot create a new menu after menus have been finalized."); + uiprivUserBug("You cannot create a new menu after menus have been finalized."); if (menus == NULL) menus = g_array_new(FALSE, TRUE, sizeof (uiMenu *)); @@ -308,7 +308,7 @@ static void freeMenuItem(GtkWidget *widget, gpointer data) item = g_array_index(fmi->items, uiMenuItem *, fmi->i); w = (struct menuItemWindow *) g_hash_table_lookup(item->windows, widget); if (g_hash_table_remove(item->windows, widget) == FALSE) - implbug("GtkMenuItem %p not in menu item's item/window map", widget); + uiprivImplBug("GtkMenuItem %p not in menu item's item/window map", widget); uiprivFree(w); fmi->i++; } @@ -353,8 +353,8 @@ void uninitMenus(void) for (j = 0; j < m->items->len; j++) { item = g_array_index(m->items, uiMenuItem *, j); if (g_hash_table_size(item->windows) != 0) - // TODO is this really a userbug()? - implbug("menu item %p (%s) still has uiWindows attached; did you forget to destroy some windows?", item, item->name); + // TODO is this really a uiprivUserBug()? + uiprivImplBug("menu item %p (%s) still has uiWindows attached; did you forget to destroy some windows?", item, item->name); g_free(item->name); g_hash_table_destroy(item->windows); uiprivFree(item); diff --git a/unix/progressbar.c b/unix/progressbar.c index 9b543b04..b3681a6f 100644 --- a/unix/progressbar.c +++ b/unix/progressbar.c @@ -53,7 +53,7 @@ void uiProgressBarSetValue(uiProgressBar *p, int value) } if (value < 0 || value > 100) - userbug("Value %d is out of range for a uiProgressBar.", value); + uiprivUserBug("Value %d is out of range for a uiProgressBar.", value); gtk_progress_bar_set_fraction(p->pbar, ((gdouble) value) / 100); } diff --git a/windows/alloc.cpp b/windows/alloc.cpp index 244b2380..321cca03 100644 --- a/windows/alloc.cpp +++ b/windows/alloc.cpp @@ -22,7 +22,7 @@ void uninitAlloc(void) // note the void * cast; otherwise it'll be treated as a string oss << (void *) (alloc.first) << " " << types[alloc.second] << "\n"; ossstr = oss.str(); - userbug("Some data was leaked; either you left a uiControl lying around or there's a bug in libui itself. Leaked data:\n%s", ossstr.c_str()); + uiprivUserBug("Some data was leaked; either you left a uiControl lying around or there's a bug in libui itself. Leaked data:\n%s", ossstr.c_str()); } #define rawBytes(pa) (&((*pa)[0])) @@ -57,7 +57,7 @@ void uiprivFree(void *_p) uint8_t *p = (uint8_t *) _p; if (p == NULL) - implbug("attempt to uiprivFree(NULL)"); + uiprivImplBug("attempt to uiprivFree(NULL)"); types.erase(heap[p]); delete heap[p]; heap.erase(p); diff --git a/windows/datetimepicker.cpp b/windows/datetimepicker.cpp index 342eb256..6784fec2 100644 --- a/windows/datetimepicker.cpp +++ b/windows/datetimepicker.cpp @@ -44,7 +44,7 @@ static WCHAR *expandYear(WCHAR *dts, int n) if (*p == L'\'') break; if (*p == L'\0') - implbug("unterminated quote in system-provided locale date string in expandYear()"); + uiprivImplBug("unterminated quote in system-provided locale date string in expandYear()"); *q++ = *p; } // and fall through to copy the closing quote diff --git a/windows/debug.cpp b/windows/debug.cpp index 9bfbbf0f..bd512743 100644 --- a/windows/debug.cpp +++ b/windows/debug.cpp @@ -59,7 +59,7 @@ HRESULT _logHRESULT(debugargs, const WCHAR *s, HRESULT hr) return hr; } -void realbug(const char *file, const char *line, const char *func, const char *prefix, const char *format, va_list ap) +void uiprivRealBug(const char *file, const char *line, const char *func, const char *prefix, const char *format, va_list ap) { va_list ap2; char *msg; diff --git a/windows/draw.cpp b/windows/draw.cpp index 2fc9eed9..a5e5033a 100644 --- a/windows/draw.cpp +++ b/windows/draw.cpp @@ -120,7 +120,7 @@ void freeContext(uiDrawContext *c) c->currentClip->Release(); if (c->states->size() != 0) // TODO do this on other platforms - userbug("You did not balance uiDrawSave() and uiDrawRestore() calls."); + uiprivUserBug("You did not balance uiDrawSave() and uiDrawRestore() calls."); delete c->states; uiprivFree(c); } @@ -253,7 +253,7 @@ static ID2D1Brush *makeBrush(uiDrawBrush *b, ID2D1RenderTarget *rt) } // TODO do this on all platforms - userbug("Invalid brush type %d given to drawing operation.", b->Type); + uiprivUserBug("Invalid brush type %d given to drawing operation.", b->Type); // TODO dummy brush? return NULL; // make compiler happy } diff --git a/windows/drawpath.cpp b/windows/drawpath.cpp index 8baa75a7..34b15466 100644 --- a/windows/drawpath.cpp +++ b/windows/drawpath.cpp @@ -242,6 +242,6 @@ void uiDrawPathEnd(uiDrawPath *p) ID2D1PathGeometry *pathGeometry(uiDrawPath *p) { if (p->sink != NULL) - userbug("You cannot draw with a uiDrawPath that was not ended. (path: %p)", p); + uiprivUserBug("You cannot draw with a uiDrawPath that was not ended. (path: %p)", p); return p->path; } diff --git a/windows/events.cpp b/windows/events.cpp index 45e8d43d..c13d6d00 100644 --- a/windows/events.cpp +++ b/windows/events.cpp @@ -23,7 +23,7 @@ static std::map handlers; void uiWindowsRegisterWM_COMMANDHandler(HWND hwnd, BOOL (*handler)(uiControl *, HWND, WORD, LRESULT *), uiControl *c) { if (handlers[hwnd].commandHandler != NULL) - implbug("already registered a WM_COMMAND handler to window handle %p", hwnd); + uiprivImplBug("already registered a WM_COMMAND handler to window handle %p", hwnd); handlers[hwnd].commandHandler = handler; handlers[hwnd].c = c; } @@ -31,7 +31,7 @@ void uiWindowsRegisterWM_COMMANDHandler(HWND hwnd, BOOL (*handler)(uiControl *, void uiWindowsRegisterWM_NOTIFYHandler(HWND hwnd, BOOL (*handler)(uiControl *, HWND, NMHDR *, LRESULT *), uiControl *c) { if (handlers[hwnd].notifyHandler != NULL) - implbug("already registered a WM_NOTIFY handler to window handle %p", hwnd); + uiprivImplBug("already registered a WM_NOTIFY handler to window handle %p", hwnd); handlers[hwnd].notifyHandler = handler; handlers[hwnd].c = c; } @@ -39,7 +39,7 @@ void uiWindowsRegisterWM_NOTIFYHandler(HWND hwnd, BOOL (*handler)(uiControl *, H void uiWindowsRegisterWM_HSCROLLHandler(HWND hwnd, BOOL (*handler)(uiControl *, HWND, WORD, LRESULT *), uiControl *c) { if (handlers[hwnd].hscrollHandler != NULL) - implbug("already registered a WM_HSCROLL handler to window handle %p", hwnd); + uiprivImplBug("already registered a WM_HSCROLL handler to window handle %p", hwnd); handlers[hwnd].hscrollHandler = handler; handlers[hwnd].c = c; } @@ -47,21 +47,21 @@ void uiWindowsRegisterWM_HSCROLLHandler(HWND hwnd, BOOL (*handler)(uiControl *, void uiWindowsUnregisterWM_COMMANDHandler(HWND hwnd) { if (handlers[hwnd].commandHandler == NULL) - implbug("window handle %p not registered to receive WM_COMMAND events", hwnd); + uiprivImplBug("window handle %p not registered to receive WM_COMMAND events", hwnd); handlers[hwnd].commandHandler = NULL; } void uiWindowsUnregisterWM_NOTIFYHandler(HWND hwnd) { if (handlers[hwnd].notifyHandler == NULL) - implbug("window handle %p not registered to receive WM_NOTIFY events", hwnd); + uiprivImplBug("window handle %p not registered to receive WM_NOTIFY events", hwnd); handlers[hwnd].notifyHandler = NULL; } void uiWindowsUnregisterWM_HSCROLLHandler(HWND hwnd) { if (handlers[hwnd].hscrollHandler == NULL) - implbug("window handle %p not registered to receive WM_HSCROLL events", hwnd); + uiprivImplBug("window handle %p not registered to receive WM_HSCROLL events", hwnd); handlers[hwnd].hscrollHandler = NULL; } @@ -131,14 +131,14 @@ static std::map wininichanges; void uiWindowsRegisterReceiveWM_WININICHANGE(HWND hwnd) { if (wininichanges[hwnd]) - implbug("window handle %p already subscribed to receive WM_WINICHANGEs", hwnd); + uiprivImplBug("window handle %p already subscribed to receive WM_WINICHANGEs", hwnd); wininichanges[hwnd] = true; } void uiWindowsUnregisterReceiveWM_WININICHANGE(HWND hwnd) { if (!wininichanges[hwnd]) - implbug("window handle %p not registered to receive WM_WININICHANGEs", hwnd); + uiprivImplBug("window handle %p not registered to receive WM_WININICHANGEs", hwnd); wininichanges[hwnd] = false; } diff --git a/windows/grid.cpp b/windows/grid.cpp index 61e78543..cac87aff 100644 --- a/windows/grid.cpp +++ b/windows/grid.cpp @@ -562,9 +562,9 @@ static struct gridChild *toChild(uiControl *c, int xspan, int yspan, int hexpand struct gridChild *gc; if (xspan < 0) - userbug("You cannot have a negative xspan in a uiGrid cell."); + uiprivUserBug("You cannot have a negative xspan in a uiGrid cell."); if (yspan < 0) - userbug("You cannot have a negative yspan in a uiGrid cell."); + uiprivUserBug("You cannot have a negative yspan in a uiGrid cell."); gc = uiprivNew(struct gridChild); gc->c = c; gc->xspan = xspan; diff --git a/windows/menu.cpp b/windows/menu.cpp index 46405faf..09dfcf30 100644 --- a/windows/menu.cpp +++ b/windows/menu.cpp @@ -87,7 +87,7 @@ void uiMenuItemDisable(uiMenuItem *i) void uiMenuItemOnClicked(uiMenuItem *i, void (*f)(uiMenuItem *, uiWindow *, void *), void *data) { if (i->type == typeQuit) - userbug("You can not call uiMenuItemOnClicked() on a Quit item; use uiOnShouldQuit() instead."); + uiprivUserBug("You can not call uiMenuItemOnClicked() on a Quit item; use uiOnShouldQuit() instead."); i->onClicked = f; i->onClickedData = data; } @@ -111,7 +111,7 @@ static uiMenuItem *newItem(uiMenu *m, int type, const char *name) uiMenuItem *item; if (menusFinalized) - userbug("You can not create a new menu item after menus have been finalized."); + uiprivUserBug("You can not create a new menu item after menus have been finalized."); if (m->len >= m->cap) { m->cap += grow; @@ -169,7 +169,7 @@ uiMenuItem *uiMenuAppendCheckItem(uiMenu *m, const char *name) uiMenuItem *uiMenuAppendQuitItem(uiMenu *m) { if (hasQuit) - userbug("You can not have multiple Quit menu items in a program."); + uiprivUserBug("You can not have multiple Quit menu items in a program."); hasQuit = TRUE; newItem(m, typeSeparator, NULL); return newItem(m, typeQuit, NULL); @@ -178,7 +178,7 @@ uiMenuItem *uiMenuAppendQuitItem(uiMenu *m) uiMenuItem *uiMenuAppendPreferencesItem(uiMenu *m) { if (hasPreferences) - userbug("You can not have multiple Preferences menu items in a program."); + uiprivUserBug("You can not have multiple Preferences menu items in a program."); hasPreferences = TRUE; newItem(m, typeSeparator, NULL); return newItem(m, typePreferences, NULL); @@ -187,8 +187,8 @@ uiMenuItem *uiMenuAppendPreferencesItem(uiMenu *m) uiMenuItem *uiMenuAppendAboutItem(uiMenu *m) { if (hasAbout) - // TODO place these userbug strings in a header - userbug("You can not have multiple About menu items in a program."); + // TODO place these uiprivImplBug() and uiprivUserBug() strings in a header + uiprivUserBug("You can not have multiple About menu items in a program."); hasAbout = TRUE; newItem(m, typeSeparator, NULL); return newItem(m, typeAbout, NULL); @@ -204,7 +204,7 @@ uiMenu *uiNewMenu(const char *name) uiMenu *m; if (menusFinalized) - userbug("You can not create a new menu after menus have been finalized."); + uiprivUserBug("You can not create a new menu after menus have been finalized."); if (len >= cap) { cap += grow; menus = (uiMenu **) uiprivRealloc(menus, cap * sizeof (uiMenu *), "uiMenu *[]"); @@ -293,7 +293,7 @@ void runMenuEvent(WORD id, uiWindow *w) } } // no match - implbug("unknown menu ID %hu in runMenuEvent()", id); + uiprivImplBug("unknown menu ID %hu in runMenuEvent()", id); found: // first toggle checkboxes, if any @@ -316,7 +316,7 @@ static void freeMenu(uiMenu *m, HMENU submenu) if (item->hmenus[j] == submenu) break; if (j >= item->len) - implbug("submenu handle %p not found in freeMenu()", submenu); + uiprivImplBug("submenu handle %p not found in freeMenu()", submenu); for (; j < item->len - 1; j++) item->hmenus[j] = item->hmenus[j + 1]; item->hmenus[j] = NULL; @@ -352,8 +352,8 @@ void uninitMenus(void) for (j = 0; j < m->len; j++) { item = m->items[j]; if (item->len != 0) - // LONGTERM userbug()? - implbug("menu item %p (%ws) still has uiWindows attached; did you forget to destroy some windows?", item, item->name); + // LONGTERM uiprivUserBug()? + uiprivImplBug("menu item %p (%ws) still has uiWindows attached; did you forget to destroy some windows?", item, item->name); if (item->name != NULL) uiprivFree(item->name); if (item->hmenus != NULL) diff --git a/windows/progressbar.cpp b/windows/progressbar.cpp index 3750eb6a..c3a67dd3 100644 --- a/windows/progressbar.cpp +++ b/windows/progressbar.cpp @@ -54,7 +54,7 @@ void uiProgressBarSetValue(uiProgressBar *p, int value) } if (value < 0 || value > 100) - userbug("Value %d is out of range for uiProgressBars.", value); + uiprivUserBug("Value %d is out of range for uiProgressBars.", value); if (value == 100) { // because we can't 101 SendMessageW(p->hwnd, PBM_SETRANGE32, 0, 101); From 59835a9bae97bbf8728b780ce5c2678a4e9ceb03 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 Apr 2018 21:49:58 -0400 Subject: [PATCH 0973/1329] Removed the declaration of newControl(): it was completely unused, as it was superceded by uiAllocControl() long ago. --- common/OLD_uipriv.h | 3 --- common/uipriv.h | 3 --- 2 files changed, 6 deletions(-) diff --git a/common/OLD_uipriv.h b/common/OLD_uipriv.h index ac8ec0cc..f42baf56 100644 --- a/common/OLD_uipriv.h +++ b/common/OLD_uipriv.h @@ -1,7 +1,4 @@ -// control.c -extern uiControl *newControl(size_t size, uint32_t OSsig, uint32_t typesig, const char *typenamestr); - // shouldquit.c extern int shouldQuit(void); diff --git a/common/uipriv.h b/common/uipriv.h index 530a9794..4c179c47 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -33,9 +33,6 @@ extern void uiprivDoImplBug(const char *file, const char *line, const char *func extern void uiprivDoUserBug(const char *file, const char *line, const char *func, const char *format, ...); #define uiprivUserBug(...) uiprivDoUserBug(__FILE__, uiprivMacro_ns(__LINE__), uiprivMacro__func__, __VA_ARGS__) -// control.c -extern uiControl *newControl(size_t size, uint32_t OSsig, uint32_t typesig, const char *typenamestr); - // shouldquit.c extern int shouldQuit(void); From f5be05f1437e777d50c9f8e35d5150aa6467e90b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 Apr 2018 21:54:46 -0400 Subject: [PATCH 0974/1329] shouldQuit() -> uiprivShouldQuit(). --- common/OLD_uipriv.h | 3 --- common/shouldquit.c | 2 +- common/uipriv.h | 2 +- darwin/main.m | 2 +- darwin/menu.m | 2 +- unix/menu.c | 2 +- windows/menu.cpp | 2 +- windows/utilwin.cpp | 4 ++-- 8 files changed, 8 insertions(+), 11 deletions(-) diff --git a/common/OLD_uipriv.h b/common/OLD_uipriv.h index f42baf56..b6e0581d 100644 --- a/common/OLD_uipriv.h +++ b/common/OLD_uipriv.h @@ -1,7 +1,4 @@ -// shouldquit.c -extern int shouldQuit(void); - // areaevents.c typedef struct clickCounter clickCounter; // you should call Reset() to zero-initialize a new instance diff --git a/common/shouldquit.c b/common/shouldquit.c index 4e7aa5c3..dddd879c 100644 --- a/common/shouldquit.c +++ b/common/shouldquit.c @@ -16,7 +16,7 @@ void uiOnShouldQuit(int (*f)(void *), void *data) onShouldQuitData = data; } -int shouldQuit(void) +int uiprivShouldQuit(void) { return (*onShouldQuit)(onShouldQuitData); } diff --git a/common/uipriv.h b/common/uipriv.h index 4c179c47..7d3ff297 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -34,7 +34,7 @@ extern void uiprivDoUserBug(const char *file, const char *line, const char *func #define uiprivUserBug(...) uiprivDoUserBug(__FILE__, uiprivMacro_ns(__LINE__), uiprivMacro__func__, __VA_ARGS__) // shouldquit.c -extern int shouldQuit(void); +extern int uiprivShouldQuit(void); // areaevents.c typedef struct clickCounter clickCounter; diff --git a/darwin/main.m b/darwin/main.m index f3392ce6..6d149fc3 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -91,7 +91,7 @@ static BOOL stepsIsRunning; { // for debugging NSLog(@"in applicationShouldTerminate:"); - if (shouldQuit()) { + if (uiprivShouldQuit()) { canQuit = YES; // this will call terminate:, which is the same as uiQuit() return NSTerminateNow; diff --git a/darwin/menu.m b/darwin/menu.m index ca6cce4b..79adbae6 100644 --- a/darwin/menu.m +++ b/darwin/menu.m @@ -71,7 +71,7 @@ static void mapItemReleaser(void *key, void *value) - (IBAction)onQuitClicked:(id)sender { - if (shouldQuit()) + if (uiprivShouldQuit()) uiQuit(); } diff --git a/unix/menu.c b/unix/menu.c index 3d02e939..17189c8e 100644 --- a/unix/menu.c +++ b/unix/menu.c @@ -81,7 +81,7 @@ static void defaultOnClicked(uiMenuItem *item, uiWindow *w, void *data) static void onQuitClicked(uiMenuItem *item, uiWindow *w, void *data) { - if (shouldQuit()) + if (uiprivShouldQuit()) uiQuit(); } diff --git a/windows/menu.cpp b/windows/menu.cpp index 09dfcf30..65791bfb 100644 --- a/windows/menu.cpp +++ b/windows/menu.cpp @@ -68,7 +68,7 @@ static void defaultOnClicked(uiMenuItem *item, uiWindow *w, void *data) static void onQuitClicked(uiMenuItem *item, uiWindow *w, void *data) { - if (shouldQuit()) + if (uiprivShouldQuit()) uiQuit(); } diff --git a/windows/utilwin.cpp b/windows/utilwin.cpp index 28950674..34b72ba8 100644 --- a/windows/utilwin.cpp +++ b/windows/utilwin.cpp @@ -23,8 +23,8 @@ static LRESULT CALLBACK utilWindowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, L return lResult; switch (uMsg) { case WM_QUERYENDSESSION: - // TODO block handler - if (shouldQuit()) { + // TODO block handler (TODO figure out if this meant the Vista-style block handler or not) + if (uiprivShouldQuit()) { uiQuit(); return TRUE; } From 0dddf4a490c4fad781850002807294a444756e6a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 Apr 2018 22:26:51 -0400 Subject: [PATCH 0975/1329] clickCounter -> uiprivClickCounter. --- common/OLD_uipriv.h | 17 ----------------- common/areaevents.c | 4 ++-- common/uipriv.h | 8 ++++---- unix/area.c | 10 +++++----- windows/area.cpp | 4 ++-- windows/area.hpp | 2 +- windows/areaevents.cpp | 12 ++++++------ 7 files changed, 20 insertions(+), 37 deletions(-) diff --git a/common/OLD_uipriv.h b/common/OLD_uipriv.h index b6e0581d..29583b0c 100644 --- a/common/OLD_uipriv.h +++ b/common/OLD_uipriv.h @@ -1,21 +1,4 @@ -// areaevents.c -typedef struct clickCounter clickCounter; -// you should call Reset() to zero-initialize a new instance -// it doesn't matter that all the non-count fields are zero: the first click will fail the curButton test straightaway, so it'll return 1 and set the rest of the structure accordingly -struct clickCounter { - int curButton; - int rectX0; - int rectY0; - int rectX1; - int rectY1; - uintptr_t prevTime; - int count; -}; -int clickCounterClick(clickCounter *c, int button, int x, int y, uintptr_t time, uintptr_t maxTime, int32_t xdist, int32_t ydist); -extern void clickCounterReset(clickCounter *); -extern int fromScancode(uintptr_t, uiAreaKeyEvent *); - // matrix.c extern void fallbackSkew(uiDrawMatrix *, double, double, double, double); extern void scaleCenter(double, double, double *, double *); diff --git a/common/areaevents.c b/common/areaevents.c index 189673a2..d913b763 100644 --- a/common/areaevents.c +++ b/common/areaevents.c @@ -16,7 +16,7 @@ TODO note the bits about asymmetry and g_rcClick initial value not mattering in // x, y, xdist, ydist, and c.rect must have the same units // so must time, maxTime, and c.prevTime -int clickCounterClick(clickCounter *c, int button, int x, int y, uintptr_t time, uintptr_t maxTime, int32_t xdist, int32_t ydist) +int uiprivClickCounterClick(uiprivClickCounter *c, int button, int x, int y, uintptr_t time, uintptr_t maxTime, int32_t xdist, int32_t ydist) { // different button than before? if so, don't count if (button != c->curButton) @@ -50,7 +50,7 @@ int clickCounterClick(clickCounter *c, int button, int x, int y, uintptr_t time, return c->count; } -void clickCounterReset(clickCounter *c) +void uiprivClickCounterReset(uiprivClickCounter *c) { c->curButton = 0; c->rectX0 = 0; diff --git a/common/uipriv.h b/common/uipriv.h index 7d3ff297..363450a8 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -37,10 +37,10 @@ extern void uiprivDoUserBug(const char *file, const char *line, const char *func extern int uiprivShouldQuit(void); // areaevents.c -typedef struct clickCounter clickCounter; +typedef struct uiprivClickCounter uiprivClickCounter; // you should call Reset() to zero-initialize a new instance // it doesn't matter that all the non-count fields are zero: the first click will fail the curButton test straightaway, so it'll return 1 and set the rest of the structure accordingly -struct clickCounter { +struct uiprivClickCounter { int curButton; int rectX0; int rectY0; @@ -49,8 +49,8 @@ struct clickCounter { uintptr_t prevTime; int count; }; -int clickCounterClick(clickCounter *c, int button, int x, int y, uintptr_t time, uintptr_t maxTime, int32_t xdist, int32_t ydist); -extern void clickCounterReset(clickCounter *); +extern int uiprivClickCounterClick(uiprivClickCounter *c, int button, int x, int y, uintptr_t time, uintptr_t maxTime, int32_t xdist, int32_t ydist); +extern void uiprivClickCounterReset(uiprivClickCounter *); extern int fromScancode(uintptr_t, uiAreaKeyEvent *); // matrix.c diff --git a/unix/area.c b/unix/area.c index abf868ca..ca245bb0 100644 --- a/unix/area.c +++ b/unix/area.c @@ -19,7 +19,7 @@ struct areaWidget { // construct-only parameters aare not set until after the init() function has returned // we need this particular object available during init(), so put it here instead of in uiArea // keep a pointer in uiArea for convenience, though - clickCounter cc; + uiprivClickCounter cc; }; struct areaWidgetClass { @@ -45,7 +45,7 @@ struct uiArea { int scrollHeight; // note that this is a pointer; see above - clickCounter *cc; + uiprivClickCounter *cc; // for user window drags GdkEventButton *dragevent; @@ -68,7 +68,7 @@ static void areaWidget_init(areaWidget *aw) gtk_widget_set_can_focus(GTK_WIDGET(aw), TRUE); - clickCounterReset(&(aw->cc)); + uiprivClickCounterReset(&(aw->cc)); } static void areaWidget_dispose(GObject *obj) @@ -261,7 +261,7 @@ static gboolean areaWidget_button_press_event(GtkWidget *w, GdkEventButton *e) // e->time is guint32 // e->x and e->y are floating-point; just make them 32-bit integers // maxTime and maxDistance... are gint, which *should* fit, hopefully... - me.Count = clickCounterClick(a->cc, me.Down, + me.Count = uiprivClickCounterClick(a->cc, me.Down, e->x, e->y, e->time, maxTime, maxDistance, maxDistance); @@ -309,7 +309,7 @@ static gboolean onCrossing(areaWidget *aw, int left) uiArea *a = aw->a; (*(a->ah->MouseCrossed))(a->ah, a, left); - clickCounterReset(a->cc); + uiprivClickCounterReset(a->cc); return GDK_EVENT_PROPAGATE; } diff --git a/windows/area.cpp b/windows/area.cpp index ab69ff15..0042fccc 100644 --- a/windows/area.cpp +++ b/windows/area.cpp @@ -168,7 +168,7 @@ uiArea *uiNewArea(uiAreaHandler *ah) a->ah = ah; a->scrolling = FALSE; - clickCounterReset(&(a->cc)); + uiprivClickCounterReset(&(a->cc)); // a->hwnd is assigned in areaWndProc() uiWindowsEnsureCreateControlHWND(0, @@ -190,7 +190,7 @@ uiArea *uiNewScrollingArea(uiAreaHandler *ah, int width, int height) a->scrolling = TRUE; a->scrollWidth = width; a->scrollHeight = height; - clickCounterReset(&(a->cc)); + uiprivClickCounterReset(&(a->cc)); // a->hwnd is assigned in areaWndProc() uiWindowsEnsureCreateControlHWND(0, diff --git a/windows/area.hpp b/windows/area.hpp index 86a62de6..dfc2bc58 100644 --- a/windows/area.hpp +++ b/windows/area.hpp @@ -18,7 +18,7 @@ struct uiArea { int hwheelCarry; int vwheelCarry; - clickCounter cc; + uiprivClickCounter cc; BOOL capturing; BOOL inside; diff --git a/windows/areaevents.cpp b/windows/areaevents.cpp index 615c06ea..4bf55665 100644 --- a/windows/areaevents.cpp +++ b/windows/areaevents.cpp @@ -92,11 +92,11 @@ static void areaMouseEvent(uiArea *a, int down, int up, WPARAM wParam, LPARAM l if (inClient && !a->inside) { a->inside = TRUE; (*(a->ah->MouseCrossed))(a->ah, a, 0); - clickCounterReset(&(a->cc)); + uiprivClickCounterReset(&(a->cc)); } else if (!inClient && a->inside) { a->inside = FALSE; (*(a->ah->MouseCrossed))(a->ah, a, 1); - clickCounterReset(&(a->cc)); + uiprivClickCounterReset(&(a->cc)); } } @@ -120,7 +120,7 @@ static void areaMouseEvent(uiArea *a, int down, int up, WPARAM wParam, LPARAM l // GetMessageTime() returns LONG and GetDoubleClckTime() returns UINT, which are int32 and uint32, respectively, but we don't need to worry about the signedness because for the same bit widths and two's complement arithmetic, s1-s2 == u1-u2 if bits(s1)==bits(s2) and bits(u1)==bits(u2) (and Windows requires two's complement: http://blogs.msdn.com/b/oldnewthing/archive/2005/05/27/422551.aspx) // signedness isn't much of an issue for these calls anyway because http://stackoverflow.com/questions/24022225/what-are-the-sign-extension-rules-for-calling-windows-api-functions-stdcall-t and that we're only using unsigned values (think back to how you (didn't) handle signedness in assembly language) AND because of the above AND because the statistics below (time interval and width/height) really don't make sense if negative // GetSystemMetrics() returns int, which is int32 - me.Count = clickCounterClick(&(a->cc), me.Down, + me.Count = uiprivClickCounterClick(&(a->cc), me.Down, me.X, me.Y, GetMessageTime(), GetDoubleClickTime(), GetSystemMetrics(SM_CXDOUBLECLK) / 2, @@ -164,7 +164,7 @@ static void onMouseEntered(uiArea *a) track(a, TRUE); (*(a->ah->MouseCrossed))(a->ah, a, 0); // TODO figure out why we did this to begin with; either we do it on both GTK+ and Windows or not at all - clickCounterReset(&(a->cc)); + uiprivClickCounterReset(&(a->cc)); } // TODO genericize it so that it can be called above @@ -174,7 +174,7 @@ static void onMouseLeft(uiArea *a) a->inside = FALSE; (*(a->ah->MouseCrossed))(a->ah, a, 1); // TODO figure out why we did this to begin with; either we do it on both GTK+ and Windows or not at all - clickCounterReset(&(a->cc)); + uiprivClickCounterReset(&(a->cc)); } // we use VK_SNAPSHOT as a sentinel because libui will never support the print screen key; that key belongs to the user @@ -325,7 +325,7 @@ BOOL areaDoEvents(uiArea *a, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *l switch (uMsg) { case WM_ACTIVATE: // don't keep the double-click timer running if the user switched programs in between clicks - clickCounterReset(&(a->cc)); + uiprivClickCounterReset(&(a->cc)); *lResult = 0; return TRUE; case WM_MOUSEMOVE: From 24a4b0997c65ea052760657058b338c79046c5e2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 Apr 2018 22:31:17 -0400 Subject: [PATCH 0976/1329] fromScancode() -> uiprivFromScancode(). --- common/areaevents.c | 2 +- common/uipriv.h | 2 +- unix/area.c | 2 +- windows/areaevents.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/common/areaevents.c b/common/areaevents.c index d913b763..491a7283 100644 --- a/common/areaevents.c +++ b/common/areaevents.c @@ -151,7 +151,7 @@ static const struct { { 0xFFFF, 0 }, }; -int fromScancode(uintptr_t scancode, uiAreaKeyEvent *ke) +int uiprivFromScancode(uintptr_t scancode, uiAreaKeyEvent *ke) { int i; diff --git a/common/uipriv.h b/common/uipriv.h index 363450a8..8fc3d42e 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -51,7 +51,7 @@ struct uiprivClickCounter { }; extern int uiprivClickCounterClick(uiprivClickCounter *c, int button, int x, int y, uintptr_t time, uintptr_t maxTime, int32_t xdist, int32_t ydist); extern void uiprivClickCounterReset(uiprivClickCounter *); -extern int fromScancode(uintptr_t, uiAreaKeyEvent *); +extern int uiprivFromScancode(uintptr_t, uiAreaKeyEvent *); // matrix.c extern void fallbackSkew(uiDrawMatrix *, double, double, double, double); diff --git a/unix/area.c b/unix/area.c index ca245bb0..cba1f5e7 100644 --- a/unix/area.c +++ b/unix/area.c @@ -411,7 +411,7 @@ static int areaKeyEvent(uiArea *a, int up, GdkEventKey *e) goto keyFound; } - if (fromScancode(e->hardware_keycode - 8, &ke)) + if (uiprivFromScancode(e->hardware_keycode - 8, &ke)) goto keyFound; // no supported key found; treat as unhandled diff --git a/windows/areaevents.cpp b/windows/areaevents.cpp index 4bf55665..c7014ecb 100644 --- a/windows/areaevents.cpp +++ b/windows/areaevents.cpp @@ -300,7 +300,7 @@ static int areaKeyEvent(uiArea *a, int up, WPARAM wParam, LPARAM lParam) } // and finally everything else - if (fromScancode((lParam >> 16) & 0xFF, &ke)) + if (uiprivFromScancode((lParam >> 16) & 0xFF, &ke)) goto keyFound; // not a supported key, assume unhandled From 01d6422664d56bbf3c9078ec3a7b2fbda39bd2ef Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 Apr 2018 22:39:34 -0400 Subject: [PATCH 0977/1329] Added uipriv prefixes to the matrix functions. --- common/OLD_uipriv.h | 5 ----- common/matrix.c | 6 +++--- common/uipriv.h | 6 +++--- darwin/draw.m | 4 ++-- unix/drawmatrix.c | 4 ++-- windows/drawmatrix.cpp | 2 +- 6 files changed, 11 insertions(+), 16 deletions(-) diff --git a/common/OLD_uipriv.h b/common/OLD_uipriv.h index 29583b0c..95ab829c 100644 --- a/common/OLD_uipriv.h +++ b/common/OLD_uipriv.h @@ -1,8 +1,3 @@ -// matrix.c -extern void fallbackSkew(uiDrawMatrix *, double, double, double, double); -extern void scaleCenter(double, double, double *, double *); -extern void fallbackTransformSize(uiDrawMatrix *, double *, double *); - // OS-specific text.* files extern int uiprivStricmp(const char *a, const char *b); diff --git a/common/matrix.c b/common/matrix.c index 676885d1..93d4d357 100644 --- a/common/matrix.c +++ b/common/matrix.c @@ -18,7 +18,7 @@ void uiDrawMatrixSetIdentity(uiDrawMatrix *m) // see https://msdn.microsoft.com/en-us/library/windows/desktop/ff684171%28v=vs.85%29.aspx#skew_transform // TODO see if there's a way we can avoid the multiplication -void fallbackSkew(uiDrawMatrix *m, double x, double y, double xamount, double yamount) +void uiprivFallbackSkew(uiDrawMatrix *m, double x, double y, double xamount, double yamount) { uiDrawMatrix n; @@ -31,7 +31,7 @@ void fallbackSkew(uiDrawMatrix *m, double x, double y, double xamount, double ya uiDrawMatrixMultiply(m, &n); } -void scaleCenter(double xCenter, double yCenter, double *x, double *y) +void uiprivScaleCenter(double xCenter, double yCenter, double *x, double *y) { *x = xCenter - (*x * xCenter); *y = yCenter - (*y * yCenter); @@ -39,7 +39,7 @@ void scaleCenter(double xCenter, double yCenter, double *x, double *y) // the basic algorithm is from cairo // but it's the same algorithm as the transform point, just without M31 and M32 taken into account, so let's just do that instead -void fallbackTransformSize(uiDrawMatrix *m, double *x, double *y) +void uiprivFallbackTransformSize(uiDrawMatrix *m, double *x, double *y) { uiDrawMatrix m2; diff --git a/common/uipriv.h b/common/uipriv.h index 8fc3d42e..f9ad4c41 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -54,9 +54,9 @@ extern void uiprivClickCounterReset(uiprivClickCounter *); extern int uiprivFromScancode(uintptr_t, uiAreaKeyEvent *); // matrix.c -extern void fallbackSkew(uiDrawMatrix *, double, double, double, double); -extern void scaleCenter(double, double, double *, double *); -extern void fallbackTransformSize(uiDrawMatrix *, double *, double *); +extern void uiprivFallbackSkew(uiDrawMatrix *, double, double, double, double); +extern void uiprivScaleCenter(double, double, double *, double *); +extern void uiprivFallbackTransformSize(uiDrawMatrix *, double *, double *); // OS-specific text.* files extern int uiprivStricmp(const char *a, const char *b); diff --git a/darwin/draw.m b/darwin/draw.m index 7ca854d2..cf7d8f13 100644 --- a/darwin/draw.m +++ b/darwin/draw.m @@ -334,7 +334,7 @@ void uiDrawMatrixScale(uiDrawMatrix *m, double xCenter, double yCenter, double x m2c(m, &c); xt = x; yt = y; - scaleCenter(xCenter, yCenter, &xt, &yt); + uiprivScaleCenter(xCenter, yCenter, &xt, &yt); c = CGAffineTransformTranslate(c, xt, yt); c = CGAffineTransformScale(c, x, y); c = CGAffineTransformTranslate(c, -xt, -yt); @@ -354,7 +354,7 @@ void uiDrawMatrixRotate(uiDrawMatrix *m, double x, double y, double amount) void uiDrawMatrixSkew(uiDrawMatrix *m, double x, double y, double xamount, double yamount) { - fallbackSkew(m, x, y, xamount, yamount); + uiprivFallbackSkew(m, x, y, xamount, yamount); } void uiDrawMatrixMultiply(uiDrawMatrix *dest, uiDrawMatrix *src) diff --git a/unix/drawmatrix.c b/unix/drawmatrix.c index ac7ac579..7d15d920 100644 --- a/unix/drawmatrix.c +++ b/unix/drawmatrix.c @@ -39,7 +39,7 @@ void uiDrawMatrixScale(uiDrawMatrix *m, double xCenter, double yCenter, double x m2c(m, &c); xt = x; yt = y; - scaleCenter(xCenter, yCenter, &xt, &yt); + uiprivScaleCenter(xCenter, yCenter, &xt, &yt); cairo_matrix_translate(&c, xt, yt); cairo_matrix_scale(&c, x, y); cairo_matrix_translate(&c, -xt, -yt); @@ -59,7 +59,7 @@ void uiDrawMatrixRotate(uiDrawMatrix *m, double x, double y, double amount) void uiDrawMatrixSkew(uiDrawMatrix *m, double x, double y, double xamount, double yamount) { - fallbackSkew(m, x, y, xamount, yamount); + uiprivFallbackSkew(m, x, y, xamount, yamount); } void uiDrawMatrixMultiply(uiDrawMatrix *dest, uiDrawMatrix *src) diff --git a/windows/drawmatrix.cpp b/windows/drawmatrix.cpp index 090972a5..4ddc5e9a 100644 --- a/windows/drawmatrix.cpp +++ b/windows/drawmatrix.cpp @@ -113,5 +113,5 @@ void uiDrawMatrixTransformPoint(uiDrawMatrix *m, double *x, double *y) void uiDrawMatrixTransformSize(uiDrawMatrix *m, double *x, double *y) { - fallbackTransformSize(m, x, y); + uiprivFallbackTransformSize(m, x, y); } From 8e2004cf6773cc8d880f269d4e0451af229e0729 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 Apr 2018 22:43:14 -0400 Subject: [PATCH 0978/1329] struct graphemes -> uiprivGraphemes. Also deleted OLD_uipriv.h now that that one is settled. --- common/OLD_uipriv.h | 3 --- common/attrstr.c | 2 +- common/attrstr.h | 5 +++-- darwin/graphemes.m | 6 +++--- unix/graphemes.c | 6 +++--- windows/graphemes.cpp | 6 +++--- 6 files changed, 13 insertions(+), 15 deletions(-) delete mode 100644 common/OLD_uipriv.h diff --git a/common/OLD_uipriv.h b/common/OLD_uipriv.h deleted file mode 100644 index 95ab829c..00000000 --- a/common/OLD_uipriv.h +++ /dev/null @@ -1,3 +0,0 @@ - -// OS-specific text.* files -extern int uiprivStricmp(const char *a, const char *b); diff --git a/common/attrstr.c b/common/attrstr.c index ee2b1616..2445c330 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -18,7 +18,7 @@ struct uiAttributedString { size_t *u16tou8; // this is lazily created to keep things from getting *too* slow - struct graphemes *graphemes; + uiprivGraphemes *graphemes; }; static void resize(uiAttributedString *s, size_t u8, size_t u16) diff --git a/common/attrstr.h b/common/attrstr.h index 54e43feb..475589f3 100644 --- a/common/attrstr.h +++ b/common/attrstr.h @@ -28,10 +28,11 @@ extern size_t *uiprivAttributedStringCopyUTF8ToUTF16Table(const uiAttributedStri extern size_t *uiprivAttributedStringCopyUTF16ToUTF8Table(const uiAttributedString *s, size_t *n); // per-OS graphemes.c/graphemes.cpp/graphemes.m/etc. -struct graphemes { +typedef struct uiprivGraphemes uiprivGraphemes; +struct uiprivGraphemes { size_t len; size_t *pointsToGraphemes; size_t *graphemesToPoints; }; extern int uiprivGraphemesTakesUTF16(void); -extern struct graphemes *uiprivNewGraphemes(void *s, size_t len); +extern uiprivGraphemes *uiprivNewGraphemes(void *s, size_t len); diff --git a/darwin/graphemes.m b/darwin/graphemes.m index 3fdbc17e..a92534f3 100644 --- a/darwin/graphemes.m +++ b/darwin/graphemes.m @@ -10,16 +10,16 @@ int uiprivGraphemesTakesUTF16(void) return 1; } -struct graphemes *uiprivNewGraphemes(void *s, size_t len) +uiprivGraphemes *uiprivNewGraphemes(void *s, size_t len) { - struct graphemes *g; + uiprivGraphemes *g; UniChar *str = (UniChar *) s; CFStringRef cfstr; size_t ppos, gpos; CFRange range; size_t i; - g = uiprivNew(struct graphemes); + g = uiprivNew(uiprivGraphemes); cfstr = CFStringCreateWithCharactersNoCopy(NULL, str, len, kCFAllocatorNull); if (cfstr == NULL) { diff --git a/unix/graphemes.c b/unix/graphemes.c index b5edb95a..952f1ef8 100644 --- a/unix/graphemes.c +++ b/unix/graphemes.c @@ -7,16 +7,16 @@ int uiprivGraphemesTakesUTF16(void) return 0; } -struct graphemes *uiprivNewGraphemes(void *s, size_t len) +uiprivGraphemes *uiprivNewGraphemes(void *s, size_t len) { - struct graphemes *g; + uiprivGraphemes *g; char *text = (char *) s; size_t lenchars; PangoLogAttr *logattrs; size_t i; size_t *op; - g = uiprivNew(struct graphemes); + g = uiprivNew(uiprivGraphemes); // TODO see if we can use the utf routines lenchars = g_utf8_strlen(text, -1); diff --git a/windows/graphemes.cpp b/windows/graphemes.cpp index 266cf97f..c11dd203 100644 --- a/windows/graphemes.cpp +++ b/windows/graphemes.cpp @@ -11,13 +11,13 @@ int uiprivGraphemesTakesUTF16(void) return 1; } -struct graphemes *uiprivNewGraphemes(void *s, size_t len) +uiprivGraphemes *uiprivNewGraphemes(void *s, size_t len) { - struct graphemes *g; + uiprivGraphemes *g; WCHAR *str; size_t *pPTG, *pGTP; - g = uiprivNew(struct graphemes); + g = uiprivNew(uiprivGraphemes); g->len = 0; str = (WCHAR *) s; From c3992cc6473c640c2d260d4e7e18bd95464614f1 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 Apr 2018 23:08:57 -0400 Subject: [PATCH 0979/1329] uipriv-ized utf.c. --- common/attrstr.c | 12 ++++++------ common/controlsigs.h | 2 ++ common/utf.c | 41 +++++++++++++++++++++-------------------- common/utf.h | 36 ++++++++++++++++++++---------------- windows/utf16.cpp | 12 ++++++------ 5 files changed, 55 insertions(+), 48 deletions(-) diff --git a/common/attrstr.c b/common/attrstr.c index 2445c330..f2cdb66b 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -93,11 +93,11 @@ static void u8u16len(const char *str, size_t *n8, size_t *n16) *n8 = 0; *n16 = 0; while (*str) { - str = utf8DecodeRune(str, 0, &rune); + str = uiprivUTF8DecodeRune(str, 0, &rune); // TODO document the use of the function vs a pointer subtract here // TODO also we need to consider namespace collision with utf.h... - *n8 += utf8EncodeRune(rune, buf); - *n16 += utf16EncodeRune(rune, buf16); + *n8 += uiprivUTF8EncodeRune(rune, buf); + *n16 += uiprivUTF16EncodeRune(rune, buf16); } } @@ -179,9 +179,9 @@ void uiAttributedStringInsertAtUnattributed(uiAttributedString *s, const char *s while (*str) { size_t n; - str = utf8DecodeRune(str, 0, &rune); - n = utf8EncodeRune(rune, buf); - n16 = utf16EncodeRune(rune, buf16); + str = uiprivUTF8DecodeRune(str, 0, &rune); + n = uiprivUTF8EncodeRune(rune, buf); + n16 = uiprivUTF16EncodeRune(rune, buf16); s->s[old] = buf[0]; s->u8tou16[old] = old16; if (n > 1) { diff --git a/common/controlsigs.h b/common/controlsigs.h index 1cbf18d5..06397187 100644 --- a/common/controlsigs.h +++ b/common/controlsigs.h @@ -1,5 +1,7 @@ // 24 april 2016 +// TODO if I don't decide to remove these outright, should they be renamed uiprivTypeNameSignature? these aren't real symbols, so... + #define uiAreaSignature 0x41726561 #define uiBoxSignature 0x426F784C #define uiButtonSignature 0x42746F6E diff --git a/common/utf.c b/common/utf.c index 9efb9493..5577529b 100644 --- a/common/utf.c +++ b/common/utf.c @@ -1,5 +1,6 @@ // utf by pietro gagliardi (andlabs) — https://github.com/andlabs/utf/ // 10 november 2016 +// function names have been altered to avoid namespace collisions in libui static builds (see utf.h) #include "utf.h" // this code imitates Go's unicode/utf8 and unicode/utf16 @@ -9,7 +10,7 @@ // encoded must be at most 4 bytes // TODO clean this code up somehow -size_t utf8EncodeRune(uint32_t rune, char *encoded) +size_t uiprivUTF8EncodeRune(uint32_t rune, char *encoded) { uint8_t b, c, d, e; size_t n; @@ -72,7 +73,7 @@ done: return n; } -const char *utf8DecodeRune(const char *s, size_t nElem, uint32_t *rune) +const char *uiprivUTF8DecodeRune(const char *s, size_t nElem, uint32_t *rune) { uint8_t b, c; uint8_t lowestAllowed, highestAllowed; @@ -172,7 +173,7 @@ const char *utf8DecodeRune(const char *s, size_t nElem, uint32_t *rune) } // encoded must have at most 2 elements -size_t utf16EncodeRune(uint32_t rune, uint16_t *encoded) +size_t uiprivUTF16EncodeRune(uint32_t rune, uint16_t *encoded) { uint16_t low, high; @@ -198,7 +199,7 @@ size_t utf16EncodeRune(uint32_t rune, uint16_t *encoded) } // TODO see if this can be cleaned up somehow -const uint16_t *utf16DecodeRune(const uint16_t *s, size_t nElem, uint32_t *rune) +const uint16_t *uiprivUTF16DecodeRune(const uint16_t *s, size_t nElem, uint32_t *rune) { uint16_t high, low; @@ -240,7 +241,7 @@ const uint16_t *utf16DecodeRune(const uint16_t *s, size_t nElem, uint32_t *rune) // TODO find a way to reduce the code in all of these somehow // TODO find a way to remove u as well -size_t utf8RuneCount(const char *s, size_t nElem) +size_t uiprivUTF8RuneCount(const char *s, size_t nElem) { size_t len; uint32_t rune; @@ -251,7 +252,7 @@ size_t utf8RuneCount(const char *s, size_t nElem) len = 0; t = s; while (nElem != 0) { - u = utf8DecodeRune(t, nElem, &rune); + u = uiprivUTF8DecodeRune(t, nElem, &rune); len++; nElem -= u - t; t = u; @@ -260,13 +261,13 @@ size_t utf8RuneCount(const char *s, size_t nElem) } len = 0; while (*s) { - s = utf8DecodeRune(s, nElem, &rune); + s = uiprivUTF8DecodeRune(s, nElem, &rune); len++; } return len; } -size_t utf8UTF16Count(const char *s, size_t nElem) +size_t uiprivUTF8UTF16Count(const char *s, size_t nElem) { size_t len; uint32_t rune; @@ -278,8 +279,8 @@ size_t utf8UTF16Count(const char *s, size_t nElem) len = 0; t = s; while (nElem != 0) { - u = utf8DecodeRune(t, nElem, &rune); - len += utf16EncodeRune(rune, encoded); + u = uiprivUTF8DecodeRune(t, nElem, &rune); + len += uiprivUTF16EncodeRune(rune, encoded); nElem -= u - t; t = u; } @@ -287,13 +288,13 @@ size_t utf8UTF16Count(const char *s, size_t nElem) } len = 0; while (*s) { - s = utf8DecodeRune(s, nElem, &rune); - len += utf16EncodeRune(rune, encoded); + s = uiprivUTF8DecodeRune(s, nElem, &rune); + len += uiprivUTF16EncodeRune(rune, encoded); } return len; } -size_t utf16RuneCount(const uint16_t *s, size_t nElem) +size_t uiprivUTF16RuneCount(const uint16_t *s, size_t nElem) { size_t len; uint32_t rune; @@ -304,7 +305,7 @@ size_t utf16RuneCount(const uint16_t *s, size_t nElem) len = 0; t = s; while (nElem != 0) { - u = utf16DecodeRune(t, nElem, &rune); + u = uiprivUTF16DecodeRune(t, nElem, &rune); len++; nElem -= u - t; t = u; @@ -313,13 +314,13 @@ size_t utf16RuneCount(const uint16_t *s, size_t nElem) } len = 0; while (*s) { - s = utf16DecodeRune(s, nElem, &rune); + s = uiprivUTF16DecodeRune(s, nElem, &rune); len++; } return len; } -size_t utf16UTF8Count(const uint16_t *s, size_t nElem) +size_t uiprivUTF16UTF8Count(const uint16_t *s, size_t nElem) { size_t len; uint32_t rune; @@ -331,8 +332,8 @@ size_t utf16UTF8Count(const uint16_t *s, size_t nElem) len = 0; t = s; while (nElem != 0) { - u = utf16DecodeRune(t, nElem, &rune); - len += utf8EncodeRune(rune, encoded); + u = uiprivUTF16DecodeRune(t, nElem, &rune); + len += uiprivUTF8EncodeRune(rune, encoded); nElem -= u - t; t = u; } @@ -340,8 +341,8 @@ size_t utf16UTF8Count(const uint16_t *s, size_t nElem) } len = 0; while (*s) { - s = utf16DecodeRune(s, nElem, &rune); - len += utf8EncodeRune(rune, encoded); + s = uiprivUTF16DecodeRune(s, nElem, &rune); + len += uiprivUTF8EncodeRune(rune, encoded); } return len; } diff --git a/common/utf.h b/common/utf.h index b810a49d..41e556f8 100644 --- a/common/utf.h +++ b/common/utf.h @@ -1,25 +1,29 @@ // utf by pietro gagliardi (andlabs) — https://github.com/andlabs/utf/ // 10 november 2016 +// note the overridden names with uipriv at the beginning; this avoids potential symbol clashes when building libui as a static library +// LONGTERM find a way to encode the name overrides directly into the utf library + #ifdef __cplusplus extern "C" { #endif +// TODO (for utf itself as well) should this go outside the extern "C" block or not #include #include // if nElem == 0, assume the buffer has no upper limit and is '\0' terminated // otherwise, assume buffer is NOT '\0' terminated but is bounded by nElem *elements* -extern size_t utf8EncodeRune(uint32_t rune, char *encoded); -extern const char *utf8DecodeRune(const char *s, size_t nElem, uint32_t *rune); -extern size_t utf16EncodeRune(uint32_t rune, uint16_t *encoded); -extern const uint16_t *utf16DecodeRune(const uint16_t *s, size_t nElem, uint32_t *rune); +extern size_t uiprivUTF8EncodeRune(uint32_t rune, char *encoded); +extern const char *uiprivUTF8DecodeRune(const char *s, size_t nElem, uint32_t *rune); +extern size_t uiprivUTF16EncodeRune(uint32_t rune, uint16_t *encoded); +extern const uint16_t *uiprivUTF16DecodeRune(const uint16_t *s, size_t nElem, uint32_t *rune); -extern size_t utf8RuneCount(const char *s, size_t nElem); -extern size_t utf8UTF16Count(const char *s, size_t nElem); -extern size_t utf16RuneCount(const uint16_t *s, size_t nElem); -extern size_t utf16UTF8Count(const uint16_t *s, size_t nElem); +extern size_t uiprivUTF8RuneCount(const char *s, size_t nElem); +extern size_t uiprivUTF8UTF16Count(const char *s, size_t nElem); +extern size_t uiprivUTF16RuneCount(const uint16_t *s, size_t nElem); +extern size_t uiprivUTF16UTF8Count(const uint16_t *s, size_t nElem); #ifdef __cplusplus } @@ -33,27 +37,27 @@ extern size_t utf16UTF8Count(const uint16_t *s, size_t nElem); // TODO same for UniChar/unichar on Mac? if both are unsigned then we have nothing to worry about #if defined(_MSC_VER) -inline size_t utf16EncodeRune(uint32_t rune, __wchar_t *encoded) +inline size_t uiprivUTF16EncodeRune(uint32_t rune, __wchar_t *encoded) { - return utf16EncodeRune(rune, reinterpret_cast(encoded)); + return uiprivUTF16EncodeRune(rune, reinterpret_cast(encoded)); } -inline const __wchar_t *utf16DecodeRune(const __wchar_t *s, size_t nElem, uint32_t *rune) +inline const __wchar_t *uiprivUTF16DecodeRune(const __wchar_t *s, size_t nElem, uint32_t *rune) { const uint16_t *ret; - ret = utf16DecodeRune(reinterpret_cast(s), nElem, rune); + ret = uiprivUTF16DecodeRune(reinterpret_cast(s), nElem, rune); return reinterpret_cast(ret); } -inline size_t utf16RuneCount(const __wchar_t *s, size_t nElem) +inline size_t uiprivUTF16RuneCount(const __wchar_t *s, size_t nElem) { - return utf16RuneCount(reinterpret_cast(s), nElem); + return uiprivUTF16RuneCount(reinterpret_cast(s), nElem); } -inline size_t utf16UTF8Count(const __wchar_t *s, size_t nElem) +inline size_t uiprivUTF16UTF8Count(const __wchar_t *s, size_t nElem) { - return utf16UTF8Count(reinterpret_cast(s), nElem); + return uiprivUTF16UTF8Count(reinterpret_cast(s), nElem); } #endif diff --git a/windows/utf16.cpp b/windows/utf16.cpp index 6afd0b0e..b9e57599 100644 --- a/windows/utf16.cpp +++ b/windows/utf16.cpp @@ -12,12 +12,12 @@ WCHAR *toUTF16(const char *str) if (*str == '\0') // empty string return emptyUTF16(); - n = utf8UTF16Count(str, 0); + n = uiprivUTF8UTF16Count(str, 0); wstr = (WCHAR *) uiprivAlloc((n + 1) * sizeof (WCHAR), "WCHAR[]"); wp = wstr; while (*str) { - str = utf8DecodeRune(str, 0, &rune); - n = utf16EncodeRune(rune, wp); + str = uiprivUTF8DecodeRune(str, 0, &rune); + n = uiprivUTF16EncodeRune(rune, wp); wp += n; } return wstr; @@ -32,12 +32,12 @@ char *toUTF8(const WCHAR *wstr) if (*wstr == L'\0') // empty string return emptyUTF8(); - n = utf16RuneCount(wstr, 0); + n = uiprivUTF16RuneCount(wstr, 0); str = (char *) uiprivAlloc((n + 1) * sizeof (char), "char[]"); sp = str; while (*wstr) { - wstr = utf16DecodeRune(wstr, 0, &rune); - n = utf8EncodeRune(rune, sp); + wstr = uiprivUTF16DecodeRune(wstr, 0, &rune); + n = uiprivUTF8EncodeRune(rune, sp); sp += n; } return str; From fcc26ab9a97ce14dc1bc3334e8901683b1aacc85 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 Apr 2018 23:10:11 -0400 Subject: [PATCH 0980/1329] =?UTF-8?q?Fixed=20a=20serious=20bug=20in=20wind?= =?UTF-8?q?ows/utf16.cpp=20that=20went=20unnoticed=20for=20this=20long:=20?= =?UTF-8?q?we=20wanted=20utf16UTF8Count(),=20not=20utf16RuneCount(),=20in?= =?UTF-8?q?=20toUTF8();=20any=20non-ASCII=20text=20had=20the=20wrong=20num?= =?UTF-8?q?ber=20of=20bytes,=20and=20thus=20random=20heap=20corruption.=20?= =?UTF-8?q?The=20string=20"=E9=8E=BF=E5=B6=84=E7=B6=94=E9=8E=B4=E6=84=AC?= =?UTF-8?q?=E5=A7=9B=E7=80=B9=E5=B1=BE=E5=9E=9A=E9=8A=86"=20(taken=20from?= =?UTF-8?q?=20the=20completely=20unrelated=20#337)=20and=20the=20Set=20But?= =?UTF-8?q?ton=20Text=20button=20was=20enough=20to=20trigger=20this.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- windows/utf16.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/utf16.cpp b/windows/utf16.cpp index b9e57599..131759e9 100644 --- a/windows/utf16.cpp +++ b/windows/utf16.cpp @@ -32,7 +32,7 @@ char *toUTF8(const WCHAR *wstr) if (*wstr == L'\0') // empty string return emptyUTF8(); - n = uiprivUTF16RuneCount(wstr, 0); + n = uiprivUTF16UTF8Count(wstr, 0); str = (char *) uiprivAlloc((n + 1) * sizeof (char), "char[]"); sp = str; while (*wstr) { From b3df05eb8eefd2270f6fcbd62b709f9a237cfda8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 15 Apr 2018 23:16:04 -0400 Subject: [PATCH 0981/1329] More TODOs. (This was originally added in a prior commit but I forgot to mention it; I wanted it to be LONGTERM but forgot about that the first time.) --- common/controlsigs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/controlsigs.h b/common/controlsigs.h index 06397187..944afa9b 100644 --- a/common/controlsigs.h +++ b/common/controlsigs.h @@ -1,6 +1,6 @@ // 24 april 2016 -// TODO if I don't decide to remove these outright, should they be renamed uiprivTypeNameSignature? these aren't real symbols, so... +// LONGTERM if I don't decide to remove these outright, should they be renamed uiprivTypeNameSignature? these aren't real symbols, so... #define uiAreaSignature 0x41726561 #define uiBoxSignature 0x426F784C From 7cd88ddd03cd912aeca3f6d6f8acf2d15b0bcc5a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 16 Apr 2018 01:33:21 -0400 Subject: [PATCH 0982/1329] More notes. --- _notes/misc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/_notes/misc b/_notes/misc index 5d40d908..2fd78a92 100644 --- a/_notes/misc +++ b/_notes/misc @@ -184,3 +184,9 @@ https://msdn.microsoft.com/en-us/library/windows/desktop/aa969513(v=vs.85).aspx https://msdn.microsoft.com/en-us/library/windows/desktop/aa969527(v=vs.85).aspx https://msdn.microsoft.com/en-us/library/windows/desktop/dd388198(v=vs.85).aspx I hope the MFC ribbon can have customizable colors too, otherwise I'll have to do some serious image processing... + +windows debugging +https://msdn.microsoft.com/en-us/library/windows/desktop/hh780351(v=vs.85).aspx + https://msdn.microsoft.com/en-us/library/windows/desktop/ff476881(v=vs.85).aspx#Debug + https://msdn.microsoft.com/en-us/library/windows/desktop/hh780343(v=vs.85).aspx + https://msdn.microsoft.com/en-us/library/windows/desktop/dn457937(v=vs.85).aspx From 750f4214b72c42924c9fd5dfbd4d40d3c9352193 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 16 Apr 2018 02:31:24 -0400 Subject: [PATCH 0983/1329] Cleaned up private symbols (and in one case, explicit initialization) of symbols in all *.c files in common/. Now to decide what to do about whether uipriv.h should include ui.h and if attrstr.h should even stay, and then I can merge this back. --- common/control.c | 4 ++-- common/shouldquit.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/common/control.c b/common/control.c index 3b5b8286..98cb94aa 100644 --- a/common/control.c +++ b/common/control.c @@ -57,14 +57,14 @@ void uiControlDisable(uiControl *c) (*(c->Disable))(c); } -#define uiControlSignature 0x7569436F +#define uiprivControlSignature 0x7569436F uiControl *uiAllocControl(size_t size, uint32_t OSsig, uint32_t typesig, const char *typenamestr) { uiControl *c; c = (uiControl *) uiprivAlloc(size, typenamestr); - c->Signature = uiControlSignature; + c->Signature = uiprivControlSignature; c->OSSignature = OSsig; c->TypeSignature = typesig; return c; diff --git a/common/shouldquit.c b/common/shouldquit.c index dddd879c..df57b6c5 100644 --- a/common/shouldquit.c +++ b/common/shouldquit.c @@ -8,7 +8,7 @@ static int defaultOnShouldQuit(void *data) } static int (*onShouldQuit)(void *) = defaultOnShouldQuit; -static void *onShouldQuitData; +static void *onShouldQuitData = NULL; void uiOnShouldQuit(int (*f)(void *), void *data) { From 27b8ce1db37b3460b489dc0ad8446d19264db2a8 Mon Sep 17 00:00:00 2001 From: cody271 Date: Sat, 19 Aug 2017 15:32:52 -0700 Subject: [PATCH 0984/1329] Add uiTimer() API and example --- darwin/main.m | 4 +++ examples/CMakeLists.txt | 8 +++++- examples/timer/main.c | 64 +++++++++++++++++++++++++++++++++++++++++ ui.h | 2 ++ unix/main.c | 4 +++ windows/main.cpp | 4 +++ 6 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 examples/timer/main.c diff --git a/darwin/main.m b/darwin/main.m index 865fb942..0bdb2965 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -243,3 +243,7 @@ void uiQueueMain(void (*f)(void *data), void *data) // the signature of f matches dispatch_function_t dispatch_async_f(dispatch_get_main_queue(), data, f); } + +void uiTimer(int milliseconds, int (*f)(void *data), void *data) +{ +} diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 61ed7b82..8d83566e 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -42,9 +42,15 @@ _add_example(drawtext ${_EXAMPLE_RESOURCES_RC} ) +_add_example(timer + timer/main.c + ${_EXAMPLE_RESOURCES_RC} +) + add_custom_target(examples DEPENDS controlgallery histogram cpp-multithread - drawtext) + drawtext + timer) diff --git a/examples/timer/main.c b/examples/timer/main.c new file mode 100644 index 00000000..d1b80b97 --- /dev/null +++ b/examples/timer/main.c @@ -0,0 +1,64 @@ +#include +#include +#include +#include "../../ui.h" + +uiMultilineEntry *e; + +int sayTime(void *data) +{ + time_t t; + char *s; + + t = time(NULL); + s = ctime(&t); + + uiMultilineEntryAppend(e, s); + return 1; +} + +int onClosing(uiWindow *w, void *data) +{ + uiQuit(); + return 1; +} + +void saySomething(uiButton *b, void *data) +{ + uiMultilineEntryAppend(e, "Saying something\n"); +} + +int main(void) +{ + uiInitOptions o; + uiWindow *w; + uiBox *b; + uiButton *btn; + + memset(&o, 0, sizeof (uiInitOptions)); + if (uiInit(&o) != NULL) + abort(); + + w = uiNewWindow("Hello", 320, 240, 0); + uiWindowSetMargined(w, 1); + + b = uiNewVerticalBox(); + uiBoxSetPadded(b, 1); + uiWindowSetChild(w, uiControl(b)); + + e = uiNewMultilineEntry(); + uiMultilineEntrySetReadOnly(e, 1); + + btn = uiNewButton("Say Something"); + uiButtonOnClicked(btn, saySomething, NULL); + uiBoxAppend(b, uiControl(btn), 0); + + uiBoxAppend(b, uiControl(e), 1); + + uiTimer(1000, sayTime, NULL); + + uiWindowOnClosing(w, onClosing, NULL); + uiControlShow(uiControl(w)); + uiMain(); + return 0; +} diff --git a/ui.h b/ui.h index e3f0c908..3eed0e39 100644 --- a/ui.h +++ b/ui.h @@ -62,6 +62,8 @@ _UI_EXTERN void uiQuit(void); _UI_EXTERN void uiQueueMain(void (*f)(void *data), void *data); +_UI_EXTERN void uiTimer(int milliseconds, int (*f)(void *data), void *data); + _UI_EXTERN void uiOnShouldQuit(int (*f)(void *data), void *data); _UI_EXTERN void uiFreeText(char *text); diff --git a/unix/main.c b/unix/main.c index 2998bf31..1047f5c5 100644 --- a/unix/main.c +++ b/unix/main.c @@ -106,3 +106,7 @@ void uiQueueMain(void (*f)(void *data), void *data) q->data = data; gdk_threads_add_idle(doqueued, q); } + +void uiTimer(int milliseconds, int (*f)(void *data), void *data) +{ +} diff --git a/windows/main.cpp b/windows/main.cpp index eb6d8492..00ae26c2 100644 --- a/windows/main.cpp +++ b/windows/main.cpp @@ -128,3 +128,7 @@ void uiQueueMain(void (*f)(void *data), void *data) // LONGTERM this is likely not safe to call across threads (allocates memory) logLastError(L"error queueing function to run on main thread"); } + +void uiTimer(int milliseconds, int (*f)(void *data), void *data) +{ +} From 5dbe20593db4fbaacaac6e4d9bb70493356a3e39 Mon Sep 17 00:00:00 2001 From: cody271 Date: Sat, 19 Aug 2017 15:41:04 -0700 Subject: [PATCH 0985/1329] Implement uiTimer() for GTK+ and OS X --- darwin/main.m | 36 ++++++++++++++++++++++++++++++++++++ unix/main.c | 23 +++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/darwin/main.m b/darwin/main.m index 0bdb2965..2aef0af0 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -244,6 +244,42 @@ void uiQueueMain(void (*f)(void *data), void *data) dispatch_async_f(dispatch_get_main_queue(), data, f); } +@interface TimerDelegate : NSObject { + int (*f)(void *data); + void *data; +} +- (id)initWithCallback:(int (*)(void *))callback data:(void*)callbackData; +- (void)doTimer:(NSTimer *)timer; +@end + +@implementation TimerDelegate + +- (id)initWithCallback:(int (*)(void *))callback data:(void*)callbackData +{ + self = [super init]; + if (self) { + f = callback; + data = callbackData; + } + return self; +} + +- (void)doTimer:(NSTimer *)timer +{ + if (!f(data)) + [timer invalidate]; +} + +@end + void uiTimer(int milliseconds, int (*f)(void *data), void *data) { + TimerDelegate *delegate; + + delegate = [[TimerDelegate alloc] initWithCallback:f data:data]; + [NSTimer scheduledTimerWithTimeInterval:milliseconds / 1000.0 + target:delegate + selector:@selector(doTimer:) + userInfo:nil + repeats:YES]; } diff --git a/unix/main.c b/unix/main.c index 1047f5c5..ec3182db 100644 --- a/unix/main.c +++ b/unix/main.c @@ -107,6 +107,29 @@ void uiQueueMain(void (*f)(void *data), void *data) gdk_threads_add_idle(doqueued, q); } +struct timer { + int (*f)(void *); + void *data; +}; + +static gboolean dotimer(gpointer data) +{ + struct timer *t = (struct timer *) data; + + if((*(t->f))(t->data)) + return TRUE; + else { + g_free(t); + return FALSE; + } +} + void uiTimer(int milliseconds, int (*f)(void *data), void *data) { + struct timer *t; + + t = g_new0(struct timer, 1); + t->f = f; + t->data = data; + g_timeout_add(milliseconds, dotimer, t); } From 68e1223e4d47211e5a716f19a003d6f1307d95aa Mon Sep 17 00:00:00 2001 From: cody271 Date: Sun, 20 Aug 2017 15:05:41 -0700 Subject: [PATCH 0986/1329] Fix TimerDelegate memory leak --- darwin/main.m | 1 + 1 file changed, 1 insertion(+) diff --git a/darwin/main.m b/darwin/main.m index 2aef0af0..4ff4e079 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -282,4 +282,5 @@ void uiTimer(int milliseconds, int (*f)(void *data), void *data) selector:@selector(doTimer:) userInfo:nil repeats:YES]; + [delegate release]; } From b1210165a0e8cac69ad62f82aa9515fe1174f925 Mon Sep 17 00:00:00 2001 From: cody271 Date: Sun, 20 Aug 2017 18:31:41 -0700 Subject: [PATCH 0987/1329] Implement uiTimer() for Windows --- windows/main.cpp | 5 +++++ windows/uipriv_windows.hpp | 11 +++++++++++ windows/utilwin.cpp | 10 ++++++++++ 3 files changed, 26 insertions(+) diff --git a/windows/main.cpp b/windows/main.cpp index 00ae26c2..26a04e9e 100644 --- a/windows/main.cpp +++ b/windows/main.cpp @@ -1,6 +1,7 @@ // 6 april 2015 #include "uipriv_windows.hpp" +std::map timerHandlers; static HHOOK filter; static LRESULT CALLBACK filterProc(int code, WPARAM wParam, LPARAM lParam) @@ -131,4 +132,8 @@ void uiQueueMain(void (*f)(void *data), void *data) void uiTimer(int milliseconds, int (*f)(void *data), void *data) { + UINT_PTR id = timerHandlers.size() + 1; + if (SetTimer(utilWindow, id, milliseconds, NULL) == 0) + logLastError(L"SetTimer()"); + timerHandlers[id] = TimerHandler(f, data); } diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index 3244b7b4..c96d7a1a 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -77,6 +77,17 @@ extern void setWindowInsertAfter(HWND hwnd, HWND insertAfter); extern HWND getDlgItem(HWND hwnd, int id); extern void invalidateRect(HWND hwnd, RECT *r, BOOL erase); +struct TimerHandler { + int(*f)(void *data); + void *data; + TimerHandler() {} + TimerHandler(int(*f)(void *data), void *data) { + this->f = f; + this->data = data; + } +}; +extern std::map timerHandlers; + // text.cpp extern WCHAR *windowTextAndLen(HWND hwnd, LRESULT *len); extern WCHAR *windowText(HWND hwnd); diff --git a/windows/utilwin.cpp b/windows/utilwin.cpp index 28950674..7a27df4a 100644 --- a/windows/utilwin.cpp +++ b/windows/utilwin.cpp @@ -18,6 +18,7 @@ static LRESULT CALLBACK utilWindowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, L { void (*qf)(void *); LRESULT lResult; + UINT_PTR id; if (handleParentMessages(hwnd, uMsg, wParam, lParam, &lResult) != FALSE) return lResult; @@ -36,6 +37,15 @@ static LRESULT CALLBACK utilWindowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, L qf = (void (*)(void *)) wParam; (*qf)((void *) lParam); return 0; + case WM_TIMER: + id = (UINT_PTR)wParam; + TimerHandler timer = timerHandlers[id]; + if (!timer.f(timer.data)) { + if (!KillTimer(utilWindow, id)) + logLastError(L"KillTimer()"); + timerHandlers.erase(id); + } + return 0; } return DefWindowProcW(hwnd, uMsg, wParam, lParam); } From 4e6adca08ca769988a812d5bdcf9432dd0af8879 Mon Sep 17 00:00:00 2001 From: cody271 Date: Mon, 21 Aug 2017 13:04:13 -0700 Subject: [PATCH 0988/1329] uiTimer() Ensure Windows timer IDs are unique --- windows/main.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/windows/main.cpp b/windows/main.cpp index 26a04e9e..2443714e 100644 --- a/windows/main.cpp +++ b/windows/main.cpp @@ -133,6 +133,8 @@ void uiQueueMain(void (*f)(void *data), void *data) void uiTimer(int milliseconds, int (*f)(void *data), void *data) { UINT_PTR id = timerHandlers.size() + 1; + while (timerHandlers.find(id) != timerHandlers.end()) + id++; if (SetTimer(utilWindow, id, milliseconds, NULL) == 0) logLastError(L"SetTimer()"); timerHandlers[id] = TimerHandler(f, data); From c9d11a85cc5fe69811fd4dc902fb41d0ccd96387 Mon Sep 17 00:00:00 2001 From: cody271 Date: Tue, 22 Aug 2017 21:52:02 -0700 Subject: [PATCH 0989/1329] uiTimer() Refactor TimerHandler for Windows timer IDs --- windows/uipriv_windows.hpp | 28 +++++++++++++++++----------- windows/utilwin.cpp | 3 +-- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index c96d7a1a..660b57c3 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -77,17 +77,6 @@ extern void setWindowInsertAfter(HWND hwnd, HWND insertAfter); extern HWND getDlgItem(HWND hwnd, int id); extern void invalidateRect(HWND hwnd, RECT *r, BOOL erase); -struct TimerHandler { - int(*f)(void *data); - void *data; - TimerHandler() {} - TimerHandler(int(*f)(void *data), void *data) { - this->f = f; - this->data = data; - } -}; -extern std::map timerHandlers; - // text.cpp extern WCHAR *windowTextAndLen(HWND hwnd, LRESULT *len); extern WCHAR *windowText(HWND hwnd); @@ -106,8 +95,25 @@ extern const char *initUtilWindow(HICON hDefaultIcon, HCURSOR hDefaultCursor); extern void uninitUtilWindow(void); // main.cpp +struct TimerHandler { +public: + TimerHandler() : TimerHandler(NULL, NULL) {} + TimerHandler(int(*f)(void *data), void *data) + { + this->f = f; + this->data = data; + } + int operator()() + { + return this->f(this->data); + } +private: + int(*f)(void *data); + void *data; +}; extern int registerMessageFilter(void); extern void unregisterMessageFilter(void); +extern std::map timerHandlers; // parent.cpp extern void paintContainerBackground(HWND hwnd, HDC dc, RECT *paintRect); diff --git a/windows/utilwin.cpp b/windows/utilwin.cpp index 7a27df4a..89f2789a 100644 --- a/windows/utilwin.cpp +++ b/windows/utilwin.cpp @@ -39,8 +39,7 @@ static LRESULT CALLBACK utilWindowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, L return 0; case WM_TIMER: id = (UINT_PTR)wParam; - TimerHandler timer = timerHandlers[id]; - if (!timer.f(timer.data)) { + if (!timerHandlers[id]()) { if (!KillTimer(utilWindow, id)) logLastError(L"KillTimer()"); timerHandlers.erase(id); From d99549ec18181d50e3ad8dbc5f95337eafba97f5 Mon Sep 17 00:00:00 2001 From: cody271 Date: Sat, 26 Aug 2017 15:05:53 -0700 Subject: [PATCH 0990/1329] uiTimer() Use TimerHandler pointers directly as Windows timer IDs --- windows/main.cpp | 10 ++++------ windows/uipriv_windows.hpp | 1 - windows/utilwin.cpp | 10 +++++----- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/windows/main.cpp b/windows/main.cpp index 2443714e..65e49dbb 100644 --- a/windows/main.cpp +++ b/windows/main.cpp @@ -1,7 +1,6 @@ // 6 april 2015 #include "uipriv_windows.hpp" -std::map timerHandlers; static HHOOK filter; static LRESULT CALLBACK filterProc(int code, WPARAM wParam, LPARAM lParam) @@ -132,10 +131,9 @@ void uiQueueMain(void (*f)(void *data), void *data) void uiTimer(int milliseconds, int (*f)(void *data), void *data) { - UINT_PTR id = timerHandlers.size() + 1; - while (timerHandlers.find(id) != timerHandlers.end()) - id++; - if (SetTimer(utilWindow, id, milliseconds, NULL) == 0) + UINT_PTR timer; + + timer = (UINT_PTR) new TimerHandler(f, data); + if (SetTimer(utilWindow, timer, milliseconds, NULL) == 0) logLastError(L"SetTimer()"); - timerHandlers[id] = TimerHandler(f, data); } diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index 660b57c3..dfe40ca2 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -113,7 +113,6 @@ private: }; extern int registerMessageFilter(void); extern void unregisterMessageFilter(void); -extern std::map timerHandlers; // parent.cpp extern void paintContainerBackground(HWND hwnd, HDC dc, RECT *paintRect); diff --git a/windows/utilwin.cpp b/windows/utilwin.cpp index 89f2789a..0941b7cd 100644 --- a/windows/utilwin.cpp +++ b/windows/utilwin.cpp @@ -18,7 +18,7 @@ static LRESULT CALLBACK utilWindowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, L { void (*qf)(void *); LRESULT lResult; - UINT_PTR id; + TimerHandler *timer; if (handleParentMessages(hwnd, uMsg, wParam, lParam, &lResult) != FALSE) return lResult; @@ -38,11 +38,11 @@ static LRESULT CALLBACK utilWindowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, L (*qf)((void *) lParam); return 0; case WM_TIMER: - id = (UINT_PTR)wParam; - if (!timerHandlers[id]()) { - if (!KillTimer(utilWindow, id)) + timer = (TimerHandler *) wParam; + if (!(*timer)()) { + if (!KillTimer(utilWindow, (UINT_PTR) timer)) logLastError(L"KillTimer()"); - timerHandlers.erase(id); + delete timer; } return 0; } From ee986363c86ceaaa73eaf083735c050a3d8458b7 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 16 Apr 2018 20:35:47 -0400 Subject: [PATCH 0991/1329] More notes. --- _notes/misc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/_notes/misc b/_notes/misc index 2fd78a92..649d6cb4 100644 --- a/_notes/misc +++ b/_notes/misc @@ -190,3 +190,8 @@ https://msdn.microsoft.com/en-us/library/windows/desktop/hh780351(v=vs.85).aspx https://msdn.microsoft.com/en-us/library/windows/desktop/ff476881(v=vs.85).aspx#Debug https://msdn.microsoft.com/en-us/library/windows/desktop/hh780343(v=vs.85).aspx https://msdn.microsoft.com/en-us/library/windows/desktop/dn457937(v=vs.85).aspx + +more OS2 stuff +https://www.google.com/search?q=%22ibm+graphics+development+toolkit%22&ie=utf-8&oe=utf-8&client=firefox-b-1 +https://www.google.com/search?q=%22os%2F2+graphics+development+toolkit%22&ie=utf-8&oe=utf-8&client=firefox-b-1 +http://www.edm2.com/index.php/IBM_OS/2_Developer%27s_Packages From fa7466e7b4757d620dece6e917d00e4717ab5a3f Mon Sep 17 00:00:00 2001 From: cody271 Date: Mon, 16 Apr 2018 17:38:59 -0700 Subject: [PATCH 0992/1329] uiTimer() Qualify Objective-C instance variables with 'self' --- darwin/main.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/darwin/main.m b/darwin/main.m index 4ff4e079..35dc88f0 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -258,15 +258,15 @@ void uiQueueMain(void (*f)(void *data), void *data) { self = [super init]; if (self) { - f = callback; - data = callbackData; + self->f = callback; + self->data = callbackData; } return self; } - (void)doTimer:(NSTimer *)timer { - if (!f(data)) + if (!self->f(self->data)) [timer invalidate]; } From f921dde9f63b27af432feb8fc3fffc3ec497a6ec Mon Sep 17 00:00:00 2001 From: cody271 Date: Mon, 16 Apr 2018 17:48:33 -0700 Subject: [PATCH 0993/1329] uiTimer() Use 'uipriv' prefix convention --- darwin/main.m | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/darwin/main.m b/darwin/main.m index 35dc88f0..5b71021d 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -244,7 +244,7 @@ void uiQueueMain(void (*f)(void *data), void *data) dispatch_async_f(dispatch_get_main_queue(), data, f); } -@interface TimerDelegate : NSObject { +@interface uiprivTimerDelegate : NSObject { int (*f)(void *data); void *data; } @@ -252,7 +252,7 @@ void uiQueueMain(void (*f)(void *data), void *data) - (void)doTimer:(NSTimer *)timer; @end -@implementation TimerDelegate +@implementation uiprivTimerDelegate - (id)initWithCallback:(int (*)(void *))callback data:(void*)callbackData { @@ -274,9 +274,9 @@ void uiQueueMain(void (*f)(void *data), void *data) void uiTimer(int milliseconds, int (*f)(void *data), void *data) { - TimerDelegate *delegate; + uiprivTimerDelegate *delegate; - delegate = [[TimerDelegate alloc] initWithCallback:f data:data]; + delegate = [[uiprivTimerDelegate alloc] initWithCallback:f data:data]; [NSTimer scheduledTimerWithTimeInterval:milliseconds / 1000.0 target:delegate selector:@selector(doTimer:) From 5622b13c23c79150d39133d38f27b707a06c171b Mon Sep 17 00:00:00 2001 From: cody271 Date: Mon, 16 Apr 2018 17:49:08 -0700 Subject: [PATCH 0994/1329] uiTimer() Use uiNew() and uiFree() --- unix/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unix/main.c b/unix/main.c index ec3182db..5ec95b56 100644 --- a/unix/main.c +++ b/unix/main.c @@ -119,7 +119,7 @@ static gboolean dotimer(gpointer data) if((*(t->f))(t->data)) return TRUE; else { - g_free(t); + uiFree(t); return FALSE; } } @@ -128,7 +128,7 @@ void uiTimer(int milliseconds, int (*f)(void *data), void *data) { struct timer *t; - t = g_new0(struct timer, 1); + t = uiNew(struct timer); t->f = f; t->data = data; g_timeout_add(milliseconds, dotimer, t); From 4df47a6ee8a05f1109b60f6bd45f65c957ac3deb Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 17 Apr 2018 21:04:04 -0400 Subject: [PATCH 0995/1329] Decided what to do about uipriv.h including ui.h (uipriv_OS.h* wrecks that plan, so make a note of us not doing it). Now to just decide what to do about attrstr.h and then we can merge back. --- common/uipriv.h | 1 + 1 file changed, 1 insertion(+) diff --git a/common/uipriv.h b/common/uipriv.h index f9ad4c41..6441ada5 100644 --- a/common/uipriv.h +++ b/common/uipriv.h @@ -1,4 +1,5 @@ // 6 april 2015 +// note: this file should not include ui.h, as the OS-specific ui_*.h files are included between that one and this one in the OS-specific uipriv_*.h* files #include #include #include "controlsigs.h" From 52dc39a5537432c34cd1cdd17faf10fff77bb86f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 18 Apr 2018 00:54:24 -0400 Subject: [PATCH 0996/1329] Decided to keep attrstr.h for now; removed extern "C" hack from attrstr.hpp on Windows and moved it into attrstr.h. Let's merge back. --- common/attrstr.h | 8 ++++++++ windows/attrstr.hpp | 2 -- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/common/attrstr.h b/common/attrstr.h index 475589f3..69ada5c1 100644 --- a/common/attrstr.h +++ b/common/attrstr.h @@ -1,5 +1,9 @@ // 19 february 2018 +#ifdef __cplusplus +extern "C" { +#endif + // attribute.c extern uiAttribute *uiprivAttributeRetain(uiAttribute *a); extern void uiprivAttributeRelease(uiAttribute *a); @@ -36,3 +40,7 @@ struct uiprivGraphemes { }; extern int uiprivGraphemesTakesUTF16(void); extern uiprivGraphemes *uiprivNewGraphemes(void *s, size_t len); + +#ifdef __cplusplus +} +#endif diff --git a/windows/attrstr.hpp b/windows/attrstr.hpp index 449000ed..bd522ca1 100644 --- a/windows/attrstr.hpp +++ b/windows/attrstr.hpp @@ -1,7 +1,5 @@ // 11 march 2018 -extern "C" { #include "../common/attrstr.h" -} // dwrite.cpp extern IDWriteFactory *dwfactory; From 9cf6c3faf560d0ab69a46a540780ae405323dde8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 18 Apr 2018 00:57:53 -0400 Subject: [PATCH 0997/1329] Updated the README with the previous merge. Update #308. Oops, forgot to do this with the merge... --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index cfcd02d6..355bb009 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,9 @@ This README is being written.
*Note that today's entry (Eastern Time) may be updated later today.* +* **18 April 2018** + * Migrated all code in the `common/` directory to use `uipriv` prefixes for everything that isn't `static`. This is the first step toward fixing static library oddities within libui, allowing libui to truly be safely used as either a static library or a shared library. + * **17 June 2016** * `uiMainSteps()` no longer takes any arguments and no longer needs to invoke a function to do the work. You still need to call it, but once you do, it will return immediately and you can then get right to your main loop. * **CMake 3.1.0 is now required.** This is due to CMake's rapid development pace in the past few years adding things libui needs to build on as many systems as possible. If your OS is supported by libui but its repositories ship with an older version of CMake, you will need to find an updated one somewhere. From d4414551123f6ef1430ac9431bb4f9f9bccd1973 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 18 Apr 2018 10:06:43 -0400 Subject: [PATCH 0998/1329] Updated common uipriv names on OS X. --- darwin/table.m | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/darwin/table.m b/darwin/table.m index 5e29de26..43822dfc 100644 --- a/darwin/table.m +++ b/darwin/table.m @@ -186,7 +186,7 @@ done: break; } if (row == -1) - implbug("table model action triggered on view with no associated table"); + uiprivImplBug("table model action triggered on view with no associated table"); if ([view isKindOfClass:[NSTextField class]]) data = [[((NSTextField *) view) stringValue] UTF8String]; @@ -200,7 +200,7 @@ done: if(1); else data = NULL; } else - implbug("table model editing action triggered on non-editable view"); + uiprivImplBug("table model editing action triggered on non-editable view"); // note the use of [view tag] — we need the model column, which we store in the view tag for relevant views below (*(m->mh->SetCellValue))(m->mh, m, @@ -240,7 +240,7 @@ if(1); else case partText: data = (*(m->mh->CellValue))(m->mh, m, row, self.textColumn); str = toNSString((char *) data); - uiFree(data); + uiprivFree(data); tf = newLabel(str); // TODO set wrap and ellipsize modes? if (self.textColorColumn != -1) { @@ -364,7 +364,7 @@ void *uiTableModelStrdup(const char *str) // TODO don't we have this already? char *dup; - dup = (char *) uiAlloc((strlen(str) + 1) * sizeof (char), "char[]"); + dup = (char *) uiprivAlloc((strlen(str) + 1) * sizeof (char), "char[]"); strcpy(dup, str); return dup; } @@ -373,7 +373,7 @@ uiTableModel *uiNewTableModel(uiTableModelHandler *mh) { uiTableModel *m; - m = uiNew(uiTableModel); + m = uiprivNew(uiTableModel); m->mh = mh; m->m = [[tableModel alloc] initWithModel:m]; m->tables = [NSMutableArray new]; @@ -388,10 +388,10 @@ void *uiTableModelGiveColor(double r, double g, double b, double a) void uiFreeTableModel(uiTableModel *m) { if ([m->tables count] != 0) - userbug("You cannot free a uiTableModel while uiTables are using it."); + uiprivUserBug("You cannot free a uiTableModel while uiTables are using it."); [m->tables release]; [m->m release]; - uiFree(m); + uiprivFree(m); } void uiTableModelRowInserted(uiTableModel *m, int newIndex) @@ -519,7 +519,7 @@ uiTableColumn *uiTableAppendColumn(uiTable *t, const char *name) { uiTableColumn *c; - c = uiNew(uiTableColumn); + c = uiprivNew(uiTableColumn); c->c = [[tableColumn alloc] initWithIdentifier:@""]; c->c.libui_col = c; // via Interface Builder From 6507a0d3a1baa38925abb941cb0d630fff4e5ddf Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 18 Apr 2018 10:17:41 -0400 Subject: [PATCH 0999/1329] Updated common uipriv names on Unix. --- unix/table.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/unix/table.c b/unix/table.c index a11844c2..ac8b03d3 100644 --- a/unix/table.c +++ b/unix/table.c @@ -459,7 +459,7 @@ void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand) struct tablePart *part; GtkCellRenderer *r; - part = uiNew(struct tablePart); + part = uiprivNew(struct tablePart); part->type = partText; part->textColumn = modelColumn; part->tv = c->tv; @@ -476,7 +476,7 @@ void uiTableColumnAppendImagePart(uiTableColumn *c, int modelColumn, int expand) { struct tablePart *part; - part = uiNew(struct tablePart); + part = uiprivNew(struct tablePart); part->type = partImage; part->imageColumn = modelColumn; part->tv = c->tv; @@ -498,7 +498,7 @@ void uiTableColumnAppendButtonPart(uiTableColumn *c, int modelColumn, int expand struct tablePart *part; GtkCellRenderer *r; - part = uiNew(struct tablePart); + part = uiprivNew(struct tablePart); part->type = partButton; part->textColumn = modelColumn; part->tv = c->tv; @@ -534,7 +534,7 @@ void uiTableColumnAppendCheckboxPart(uiTableColumn *c, int modelColumn, int expa struct tablePart *part; GtkCellRenderer *r; - part = uiNew(struct tablePart); + part = uiprivNew(struct tablePart); part->type = partCheckbox; part->valueColumn = modelColumn; part->tv = c->tv; @@ -550,7 +550,7 @@ void uiTableColumnAppendProgressBarPart(uiTableColumn *c, int modelColumn, int e { struct tablePart *part; - part = uiNew(struct tablePart); + part = uiprivNew(struct tablePart); part->type = partProgressBar; part->valueColumn = modelColumn; part->tv = c->tv; @@ -600,7 +600,7 @@ uiTableColumn *uiTableAppendColumn(uiTable *t, const char *name) { uiTableColumn *c; - c = uiNew(uiTableColumn); + c = uiprivNew(uiTableColumn); c->c = gtk_tree_view_column_new(); gtk_tree_view_column_set_resizable(c->c, TRUE); gtk_tree_view_column_set_title(c->c, name); From bd2f436d91670c2e505174034f2b2cade8c95c51 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 18 Apr 2018 10:20:55 -0400 Subject: [PATCH 1000/1329] Fixed uiImage redeclaration warnings on GTK+. --- unix/uipriv_unix.h | 1 - 1 file changed, 1 deletion(-) diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index 43ed144b..ba592a97 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -47,7 +47,6 @@ extern uiDrawContext *newContext(cairo_t *cr, GtkStyleContext *style); extern void freeContext(uiDrawContext *); // image.c -/*TODO remove this*/typedef struct uiImage uiImage; extern cairo_surface_t *imageAppropriateSurface(uiImage *i, GtkWidget *w); // cellrendererbutton.c From ab336e0e8b4e261d04981e76de265c2c3af0a015 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 18 Apr 2018 10:29:16 -0400 Subject: [PATCH 1001/1329] Fixed uiImage redeclaration warnings on OS X. --- darwin/uipriv_darwin.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 8b95e315..c221464f 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -19,8 +19,6 @@ #define NSAppKitVersionNumber10_9 1265 #endif -/*TODO remove this*/typedef struct uiImage uiImage; - // menu.m @interface menuManager : NSObject { struct mapTable *items; From cac4cd9e81c13017d14343f0920b102451855110 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 18 Apr 2018 21:04:12 -0400 Subject: [PATCH 1002/1329] Cleaned up stylistic nits and common branch naming issues on the new uiTimer() code. Also switched the Windows code to use a simple struct instead of the class (and with a uipriv name). --- darwin/main.m | 8 ++++---- unix/main.c | 13 ++++++------- windows/main.cpp | 12 +++++++----- windows/uipriv_windows.hpp | 17 +++-------------- windows/utilwin.cpp | 12 ++++++------ 5 files changed, 26 insertions(+), 36 deletions(-) diff --git a/darwin/main.m b/darwin/main.m index 52537882..184a90c8 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -247,13 +247,13 @@ void uiQueueMain(void (*f)(void *data), void *data) int (*f)(void *data); void *data; } -- (id)initWithCallback:(int (*)(void *))callback data:(void*)callbackData; +- (id)initWithCallback:(int (*)(void *))callback data:(void *)callbackData; - (void)doTimer:(NSTimer *)timer; @end @implementation uiprivTimerDelegate -- (id)initWithCallback:(int (*)(void *))callback data:(void*)callbackData +- (id)initWithCallback:(int (*)(void *))callback data:(void *)callbackData { self = [super init]; if (self) { @@ -265,7 +265,7 @@ void uiQueueMain(void (*f)(void *data), void *data) - (void)doTimer:(NSTimer *)timer { - if (!self->f(self->data)) + if (!(*(self->f))(self->data)) [timer invalidate]; } @@ -276,7 +276,7 @@ void uiTimer(int milliseconds, int (*f)(void *data), void *data) uiprivTimerDelegate *delegate; delegate = [[uiprivTimerDelegate alloc] initWithCallback:f data:data]; - [NSTimer scheduledTimerWithTimeInterval:milliseconds / 1000.0 + [NSTimer scheduledTimerWithTimeInterval:(milliseconds / 1000.0) target:delegate selector:@selector(doTimer:) userInfo:nil diff --git a/unix/main.c b/unix/main.c index 338cf716..650fe06f 100644 --- a/unix/main.c +++ b/unix/main.c @@ -112,24 +112,23 @@ struct timer { void *data; }; -static gboolean dotimer(gpointer data) +static gboolean doTimer(gpointer data) { struct timer *t = (struct timer *) data; - if((*(t->f))(t->data)) - return TRUE; - else { - uiFree(t); + if (!(*(t->f))(t->data)) { + uiprivFree(t); return FALSE; } + return TRUE; } void uiTimer(int milliseconds, int (*f)(void *data), void *data) { struct timer *t; - t = uiNew(struct timer); + t = uiprivNew(struct timer); t->f = f; t->data = data; - g_timeout_add(milliseconds, dotimer, t); + g_timeout_add(milliseconds, doTimer, t); } diff --git a/windows/main.cpp b/windows/main.cpp index 65e49dbb..100c1873 100644 --- a/windows/main.cpp +++ b/windows/main.cpp @@ -131,9 +131,11 @@ void uiQueueMain(void (*f)(void *data), void *data) void uiTimer(int milliseconds, int (*f)(void *data), void *data) { - UINT_PTR timer; - - timer = (UINT_PTR) new TimerHandler(f, data); - if (SetTimer(utilWindow, timer, milliseconds, NULL) == 0) - logLastError(L"SetTimer()"); + uiprivTimer *timer; + + timer = uiprivNew(uiprivTimer); + timer->f = f; + timer->data = data; + if (SetTimer(utilWindow, (UINT_PTR) timer, milliseconds, NULL) == 0) + logLastError(L"error calling SetTimer() in uiTimer()"); } diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index e77cf439..64a7e8b7 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -95,20 +95,9 @@ extern const char *initUtilWindow(HICON hDefaultIcon, HCURSOR hDefaultCursor); extern void uninitUtilWindow(void); // main.cpp -struct TimerHandler { -public: - TimerHandler() : TimerHandler(NULL, NULL) {} - TimerHandler(int(*f)(void *data), void *data) - { - this->f = f; - this->data = data; - } - int operator()() - { - return this->f(this->data); - } -private: - int(*f)(void *data); +typedef struct uiprivTimer; +struct uiprivTimer { + void (*f)(void *); void *data; }; extern int registerMessageFilter(void); diff --git a/windows/utilwin.cpp b/windows/utilwin.cpp index fce3af09..fc2d15d4 100644 --- a/windows/utilwin.cpp +++ b/windows/utilwin.cpp @@ -18,7 +18,7 @@ static LRESULT CALLBACK utilWindowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, L { void (*qf)(void *); LRESULT lResult; - TimerHandler *timer; + uiprivTimer *timer; if (handleParentMessages(hwnd, uMsg, wParam, lParam, &lResult) != FALSE) return lResult; @@ -38,11 +38,11 @@ static LRESULT CALLBACK utilWindowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, L (*qf)((void *) lParam); return 0; case WM_TIMER: - timer = (TimerHandler *) wParam; - if (!(*timer)()) { - if (!KillTimer(utilWindow, (UINT_PTR) timer)) - logLastError(L"KillTimer()"); - delete timer; + timer = (uiprivTimer *) wParam; + if (!(*(timer->f))(timer->data)) { + if (KillTimer(utilWindow, (UINT_PTR) timer) == 0) + logLastError(L"error calling KillTimer() to end uiTimer() procedure"); + uiprivFree(timer); } return 0; } From 83b04cda47cf67f91816f19b3d28f015df3c8a67 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 18 Apr 2018 21:09:24 -0400 Subject: [PATCH 1003/1329] And added documentation nits and TODOs to the uiTimer() code. --- ui.h | 5 +++++ windows/main.cpp | 1 + 2 files changed, 6 insertions(+) diff --git a/ui.h b/ui.h index 3eed0e39..10159331 100644 --- a/ui.h +++ b/ui.h @@ -62,6 +62,11 @@ _UI_EXTERN void uiQuit(void); _UI_EXTERN void uiQueueMain(void (*f)(void *data), void *data); +// TODO standardize the looping behavior return type, either with some enum or something, and the test expressions throughout the code +// TODO figure out what to do about looping and the exact point that the timer is rescheduled so we can document it; see https://github.com/andlabs/libui/pull/277 +// TODO (also in the above link) document that this cannot be called from any thread, unlike uiQueueMain() +// TODO document that the minimum exact timing, either accuracy (timer burst, etc.) or granularity (15ms on Windows, etc.), is OS-defined +// TODO also figure out how long until the initial tick is registered on all platforms to document _UI_EXTERN void uiTimer(int milliseconds, int (*f)(void *data), void *data); _UI_EXTERN void uiOnShouldQuit(int (*f)(void *data), void *data); diff --git a/windows/main.cpp b/windows/main.cpp index 100c1873..d171e339 100644 --- a/windows/main.cpp +++ b/windows/main.cpp @@ -136,6 +136,7 @@ void uiTimer(int milliseconds, int (*f)(void *data), void *data) timer = uiprivNew(uiprivTimer); timer->f = f; timer->data = data; + // note that timer IDs are pointer sized precisely so we can use them as timer IDs; see https://blogs.msdn.microsoft.com/oldnewthing/20150924-00/?p=91521 if (SetTimer(utilWindow, (UINT_PTR) timer, milliseconds, NULL) == 0) logLastError(L"error calling SetTimer() in uiTimer()"); } From 591b9c87721b2e38fe6b2b1c455187375c764fa6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 18 Apr 2018 21:11:23 -0400 Subject: [PATCH 1004/1329] And added to the README. Woo! (Also more TODOs.) --- README.md | 3 +++ ui.h | 1 + 2 files changed, 4 insertions(+) diff --git a/README.md b/README.md index 355bb009..d8e07745 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,9 @@ This README is being written.
## Announcements +* **18 April 2018** + * Introduced a new `uiTimer()` function for running code on a timer on the main thread. (Thanks to @cody271.) + * **18 March 2018** * 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](https://github.com/andlabs/libui/blob/8944a3fc5528445b9027b1294b6c86bae03eeb89/ui_attrstr.h). There is also a new examples for it: `drawtext`, which shows the whole API at a glance. It doesn't yet support measuring or manipulating text, nor does it currently support functions that would be necessary for things like text editors; all of this will be added back later. * libui also now uses my [utf library](https://github.com/andlabs/utf) for UTF-8 and UTF-16 processing, to allow consistent behavior across platforms. This usage is not completely propagated throughout libui, but the Windows port uses it in most places now, and eventually this will become what libui will use throughout. diff --git a/ui.h b/ui.h index 10159331..ce3e4104 100644 --- a/ui.h +++ b/ui.h @@ -67,6 +67,7 @@ _UI_EXTERN void uiQueueMain(void (*f)(void *data), void *data); // TODO (also in the above link) document that this cannot be called from any thread, unlike uiQueueMain() // TODO document that the minimum exact timing, either accuracy (timer burst, etc.) or granularity (15ms on Windows, etc.), is OS-defined // TODO also figure out how long until the initial tick is registered on all platforms to document +// TODO also add a comment about how useful this could be in bindings, depending on the language being bound to _UI_EXTERN void uiTimer(int milliseconds, int (*f)(void *data), void *data); _UI_EXTERN void uiOnShouldQuit(int (*f)(void *data), void *data); From e61f7fb0cbb15294fb4f18a0bf643af2b50147f8 Mon Sep 17 00:00:00 2001 From: Andrea Parodi Date: Thu, 19 Apr 2018 21:03:49 +0200 Subject: [PATCH 1005/1329] Fix uiprivTimer::f: it should return an int --- windows/uipriv_windows.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index 64a7e8b7..51d451b9 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -97,7 +97,7 @@ extern void uninitUtilWindow(void); // main.cpp typedef struct uiprivTimer; struct uiprivTimer { - void (*f)(void *); + int (*f)(void *); void *data; }; extern int registerMessageFilter(void); From 5cc85c26095971420a3ab3018ba594c64848cda8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 19 Apr 2018 20:33:20 -0400 Subject: [PATCH 1006/1329] More ifxes. --- windows/uipriv_windows.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index 51d451b9..77982322 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -95,7 +95,8 @@ extern const char *initUtilWindow(HICON hDefaultIcon, HCURSOR hDefaultCursor); extern void uninitUtilWindow(void); // main.cpp -typedef struct uiprivTimer; +// TODO how the hell did MSVC accept this without the second uiprivTimer??????? +typedef struct uiprivTimer uiprivTimer; struct uiprivTimer { int (*f)(void *); void *data; From f88c23602eecb3c3efc9fbbc9781b9bf5f389862 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 22 Apr 2018 23:47:22 -0400 Subject: [PATCH 1007/1329] More notes. --- _notes/misc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/_notes/misc b/_notes/misc index 649d6cb4..115045bd 100644 --- a/_notes/misc +++ b/_notes/misc @@ -195,3 +195,10 @@ more OS2 stuff https://www.google.com/search?q=%22ibm+graphics+development+toolkit%22&ie=utf-8&oe=utf-8&client=firefox-b-1 https://www.google.com/search?q=%22os%2F2+graphics+development+toolkit%22&ie=utf-8&oe=utf-8&client=firefox-b-1 http://www.edm2.com/index.php/IBM_OS/2_Developer%27s_Packages + +[17:48:52] andlabs: I got splitView and NSWindow size restoration working btw +[17:49:09] andlabs: see https://github.com/eintw1ck/mail/blob/master/Sources/mail/MainViewController.swift for splitView + +old stuff +font1.gif (GIF Image, 424 × 475 pixels) http://www.functionx.com/win32/controls/dlgboxes/font1.gif +Inskcape’s Hidden Little Feature: Mesh Gradients | OCS-Mag http://www.ocsmag.com/2016/02/27/inskcapes-hidden-little-feature-mesh-gradients/ (near "When you’re done colouring in all the nodes, you may want to drag") From 55f2eba45b1666d037582f544cbda4678c11096d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 30 Apr 2018 07:46:38 -0400 Subject: [PATCH 1008/1329] Add wchar_t overloads to utf.h for C++ on Windows with non-MSVC compilers; I did not realize that was also a thing I needed to worry about! Update #287 --- common/utf.h | 50 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/common/utf.h b/common/utf.h index 41e556f8..eafcea0d 100644 --- a/common/utf.h +++ b/common/utf.h @@ -28,12 +28,52 @@ extern size_t uiprivUTF16UTF8Count(const uint16_t *s, size_t nElem); #ifdef __cplusplus } -// Provide overloads on Windows for using these functions with wchar_t and WCHAR when wchar_t is a keyword in C++ mode (the default). -// Otherwise, you'd need to cast to pass a wchar_t pointer, WCHAR pointer, or equivalent to these functions. -// We use __wchar_t to be independent of the setting; see https://blogs.msdn.microsoft.com/oldnewthing/20161201-00/?p=94836 (ironically posted one day after I initially wrote this code!). -// TODO check this on MinGW-w64 +// TODO sync this back to upstream (need copyright clearance first) + +// On Windows, wchar_t is equivalent to uint16_t, but C++ requires +// wchar_t to be a completely distinct type. These overloads allow +// passing wchar_t pointers directly into these functions from C++ +// on Windows. Otherwise, you'd need to cast to pass a wchar_t +// pointer, WCHAR pointer, or equivalent to these functions. +// +// This does not apply to MSVC because the situation there is +// slightly more complicated; see below. +#if defined(_WIN32) && !defined(_MSC_VER) + +inline size_t uiprivUTF16EncodeRune(uint32_t rune, wchar_t *encoded) +{ + return uiprivUTF16EncodeRune(rune, reinterpret_cast(encoded)); +} + +inline const wchar_t *uiprivUTF16DecodeRune(const wchar_t *s, size_t nElem, uint32_t *rune) +{ + const uint16_t *ret; + + ret = uiprivUTF16DecodeRune(reinterpret_cast(s), nElem, rune); + return reinterpret_cast(ret); +} + +inline size_t uiprivUTF16RuneCount(const wchar_t *s, size_t nElem) +{ + return uiprivUTF16RuneCount(reinterpret_cast(s), nElem); +} + +inline size_t uiprivUTF16UTF8Count(const wchar_t *s, size_t nElem) +{ + return uiprivUTF16UTF8Count(reinterpret_cast(s), nElem); +} + +#endif + +// This is the same as the above, except that with MSVC, whether +// wchar_t is a keyword or not is controlled by a compiler option! +// (At least with gcc, this is not the case; thanks redi in +// irc.freenode.net/#gcc.) We use __wchar_t to be independent of +// the option; see https://blogs.msdn.microsoft.com/oldnewthing/20161201-00/?p=94836 +// (ironically posted one day after I initially wrote this code!). +// TODO should defined(_WIN32) be used too? // TODO check this under /Wall -// TODO C-style casts enough? or will that fail in /Wall? +// TODO are C-style casts enough? or will that fail in /Wall? // TODO same for UniChar/unichar on Mac? if both are unsigned then we have nothing to worry about #if defined(_MSC_VER) From 2be8fd3eb3d412355ccca51ac840cb869d5d5f8c Mon Sep 17 00:00:00 2001 From: Ben Campbell Date: Tue, 1 May 2018 12:59:26 +1200 Subject: [PATCH 1009/1329] Fixes to compile on windows with msys2 msys2 seems to be unsure of it's IDWriteTypography definition and disables it by default. This overrides the disabling (The IDWriteTypography definition looks OK to me, and seems to work fine). This commit also adds in a canary to detect compilers that have a non-16bit wchar_t on windows (eg a gcc focused on porting unix code rather than compiling windows code). --- windows/utf16.cpp | 7 +++++++ windows/winapi.hpp | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/windows/utf16.cpp b/windows/utf16.cpp index 131759e9..143bad24 100644 --- a/windows/utf16.cpp +++ b/windows/utf16.cpp @@ -1,6 +1,13 @@ // 21 april 2016 #include "uipriv_windows.hpp" +// sanity check - make sure wchar_t is 16 bits (the assumption on windows) +// (MinGW-w64 gcc does seem to define a 16bit wchar_t, but you never know. Other windows gcc ports might not) +#if WCHAR_MAX > 0xFFFF + #error wchar_t larger than 16bit +#endif + + // see http://stackoverflow.com/a/29556509/3408572 WCHAR *toUTF16(const char *str) diff --git a/windows/winapi.hpp b/windows/winapi.hpp index 1b2ab000..909aa56b 100644 --- a/windows/winapi.hpp +++ b/windows/winapi.hpp @@ -23,6 +23,13 @@ #define _WIN32_IE 0x0700 #define NTDDI_VERSION 0x06000000 +// the msys2 header has an unverified IDWriteTypography definition. +// Would be good to get it verified and fixed in msys2, but in the meantime, +// this define lets us compile.... +#if !defined(_MSC_VER) +#define __MINGW_USE_BROKEN_INTERFACE +#endif + #include // Microsoft's resource compiler will segfault if we feed it headers it was not designed to handle From 17ec9562eaa17ee1255e0420fad241607893352e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 30 Apr 2018 21:55:54 -0400 Subject: [PATCH 1010/1329] Cleanup of previous merge, including TODOs and massive infodumps. --- windows/compilerver.hpp | 82 +++++++++++++++++++++++++++++++++++++++++ windows/utf16.cpp | 7 ---- windows/winapi.hpp | 9 +++-- 3 files changed, 87 insertions(+), 11 deletions(-) diff --git a/windows/compilerver.hpp b/windows/compilerver.hpp index 6c9e6b81..c902ec20 100644 --- a/windows/compilerver.hpp +++ b/windows/compilerver.hpp @@ -11,3 +11,85 @@ // LONGTERM MinGW // other compilers can be added here as necessary + +/* TODO this should not be necessary, but I don't know + +here's @bcampbell's original comment: +// sanity check - make sure wchar_t is 16 bits (the assumption on windows) +// (MinGW-w64 gcc does seem to define a 16bit wchar_t, but you never know. Other windows gcc ports might not) + +here's what I got when I tried to investigate on irc.oftc.net/#mingw-w64: +{ +[08:45:20] andlabs, the c++ standard requires `wchar_t` to be a distinct type. On Windows you can simply `static_assert(sizeof(wchar_t) == sizeof(unsigned short) && alignof(wchar_t) == alignof(unsigned short), "");` then reinterpret_cast those pointers. +[09:22:16] lh_mouse: yes; that was the point of my question =P but whether that static_assert is always true is another question I have, because again, windows embeds the idea of wchar_t being UTF-16 throughout the API, but when I went to look, I found that the clang developers had a very heated debate about it :S +[09:22:28] and I couldn't find any concrete information other than the msvc docs +[09:23:04] Since Windows 2000 the NT kernel uses UTF-16. +[09:23:50] If you don't care about Windows 9x you can just pretend non-UTF-16 APIs didn't exist. +[09:24:04] that's not what I meant +[09:24:06] Actually long long long ago Windows used UCS2. +[09:24:15] I meant whether sizeof(wchar_t) must necessarily equal sizeof(uint16_t) +[09:24:18] and likewise for alignof +[09:24:27] for all windows compilers +[09:24:29] anyway afk +[09:24:31] Yes. That is what the ABI says. +[09:24:40] is there a source for that I can point at other people +[09:24:45] the ABI != on Windows +[09:26:00] okay I really need to afk now but I was about to ask what you meant +[09:26:06] and by source I meant URL +[09:49:18] andlabs: Sent 17 minutes ago: Here is what Microsoft people describe `wchar_t`: https://docs.microsoft.com/en-us/cpp/cpp/char-wchar-t-char16-t-char32-t +[09:49:19] andlabs: Sent 17 minutes ago: It is quite guaranteed: 'In the Microsoft compiler, it represents a 16-bit wide character used to store Unicode encoded as UTF-16LE, the native character type on Windows operating systems.' +[09:50:08] andlabs, If you build for cygwin then `wchar_t` is probably `int`, just like what it is on Linux. +[09:51:00] yes but that's still a compiler-specific reference; I still don't know hwere Microsoft keeps its ABI documentation, and I'm still wondering what you mean by "the ABI != on Windows" with regards to establishing that guarantee +[09:52:13] This is already the ABI documentation: https://docs.microsoft.com/en-us/cpp/cpp/char-wchar-t-char16-t-char32-t +[09:52:15] Title: char, wchar_t, char16_t, char32_t | Microsoft Docs (at docs.microsoft.com) +[09:52:19] It describes C++ types, +[09:53:09] oh, ok +[09:54:47] I assume by the != statement you mean code that doesn't make any windows API calls can theoretically be compiled any which way, right +[09:55:05] yes. think about MSYS and Cygwin. +[09:55:21] They have 8-byte `long` and 4-byte `wchar_t`. +[09:57:37] right, except the code I'm trying to compile does use the Windows API, so that wouldn't apply to me +[09:57:43] I assume +[09:57:53] it wouldn't. +[09:59:12] On Windows it is sometimes necessary to assume specific ABI definition. For example, when a callback function returning a `DWORD` is to be declared in a header, in order to prevent `#include`'ing windows.h, you can just write `unsigned long` there. +[09:59:32] This is guaranteed to work on Windows. Linux will say otherwise. +[10:00:41] We all know `#include ` in a public header lets the genie out of the bottle, doesn't it? +[10:04:24] the zombie of win32_lean_and_mean lives forever +[10:04:53] of course now we have stdint.h and cstdint (which took longer because lolC++03) which helps stuff +[10:06:19] no `uint32_t` is `unsigned int` while `DWORD` is `unsigned long` hence they are incompatible. :( +[10:06:39] in what sense +[10:06:55] a `unsigned int *` cannot be converted to `unsigned long *` implicitly. +[10:07:41] the C standard says they are distinct pointer types and are not compatible, although `unsigned int` and `unsigned long` might have the same bit representation and alignment requirement. +[10:08:04] oh +[10:08:22] casting would indeed make code compile, but I tend to keep away from them unless necessary. +[10:08:24] wel yeah, but we haven't left the world of windows-specific code yet +[10:08:38] my point was more we don't need to use names like DWORD anymore +[10:08:51] of course it's easier to do so +[10:09:04] just use `uint32_t`. +[10:09:44] I just tested GCC 8 today and noticed they had added a warning for casting between incompatible function pointer types. +[10:10:10] So casting from `unsigned (*)(void)` to `unsigned long (*)(void)` now results in a warning. +[10:10:43] With `-Werror` it is a hard error. This can be worked around by casting the operand to an intermediate result of `intptr_t`. +[10:10:59] ... not so serious. +[10:11:42] oh good I wonder what else will break :D +[10:12:19] though the docs for dlsym() tell you what you should do instead for systems that use libdl (cast the address of your destination variable to void**) +[10:13:23] POSIX requires casting from `void *` to function pointers to work explicitly (see the docs for `dlsym()`). I am not sure what GCC people think about it. +[10:13:45] yes that's what I just said =P it avoids the problem entirely +[10:13:49] C++ says this is 'conditionally supported' and it is not a warning or error there. +[10:13:50] dlsym already returns void* +[10:14:13] something like dlsym would require an ABI guarantee on the matter anyway +[10:14:16] by definition +[10:14:32] Casting is evil. Double casting is double evil. So I keep myself away from them. +[10:15:03] sadly this is C (and C++) =P +[10:15:25] for `*-w64-mingw32` targets it is safe to cast between `unsigned short`, `wchar_t` and `char16_t`. +[10:15:33] as well as pointers to them. +[10:16:30] you just need to `static_assert` it, so something naughty will not compile. +[12:36:14] actually I didn't notice that last message until just now +[12:36:23] I was asking because I was sitting here thinking such a static_assert was unnecessary +} +clang debate: http://clang-developers.42468.n3.nabble.com/Is-that-getting-wchar-t-to-be-32bit-on-win32-a-good-idea-for-compatible-with-Unix-world-by-implement-td4045412.html + +so I'm not sure what is correct, but I do need to find out +*/ +#include +#if WCHAR_MAX > 0xFFFF +#error unexpected: wchar_t larger than 16-bit on a Windows ABI build; contact andlabs with your build setup information +#endif diff --git a/windows/utf16.cpp b/windows/utf16.cpp index 143bad24..131759e9 100644 --- a/windows/utf16.cpp +++ b/windows/utf16.cpp @@ -1,13 +1,6 @@ // 21 april 2016 #include "uipriv_windows.hpp" -// sanity check - make sure wchar_t is 16 bits (the assumption on windows) -// (MinGW-w64 gcc does seem to define a 16bit wchar_t, but you never know. Other windows gcc ports might not) -#if WCHAR_MAX > 0xFFFF - #error wchar_t larger than 16bit -#endif - - // see http://stackoverflow.com/a/29556509/3408572 WCHAR *toUTF16(const char *str) diff --git a/windows/winapi.hpp b/windows/winapi.hpp index 909aa56b..4f24f607 100644 --- a/windows/winapi.hpp +++ b/windows/winapi.hpp @@ -23,10 +23,11 @@ #define _WIN32_IE 0x0700 #define NTDDI_VERSION 0x06000000 -// the msys2 header has an unverified IDWriteTypography definition. -// Would be good to get it verified and fixed in msys2, but in the meantime, -// this define lets us compile.... -#if !defined(_MSC_VER) +// The MinGW-w64 header has an unverified IDWriteTypography definition. +// TODO I can confirm this myself, but I don't know how long it will take for them to note my adjustments... Either way, I have to confirm this myself. +// TODO change the check from _MSC_VER to a MinGW-w64-specific check +// TODO keep track of what else is guarded by this +#ifndef _MSC_VER #define __MINGW_USE_BROKEN_INTERFACE #endif From 10de22f9acb8c616bb12fe9b24b6bb582ea06abd Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 2 May 2018 09:56:06 -0400 Subject: [PATCH 1011/1329] Added a program to dump resources to C structs. --- windows/_rc2bin/build.bat | 10 ++++ windows/_rc2bin/libui.manifest | 31 +++++++++++ windows/_rc2bin/main.cpp | 67 ++++++++++++++++++++++++ windows/_rc2bin/resources.hpp | 37 +++++++++++++ windows/_rc2bin/resources.rc | 96 ++++++++++++++++++++++++++++++++++ windows/_rc2bin/winapi.hpp | 60 +++++++++++++++++++++ 6 files changed, 301 insertions(+) create mode 100644 windows/_rc2bin/build.bat create mode 100644 windows/_rc2bin/libui.manifest create mode 100644 windows/_rc2bin/main.cpp create mode 100644 windows/_rc2bin/resources.hpp create mode 100644 windows/_rc2bin/resources.rc create mode 100644 windows/_rc2bin/winapi.hpp diff --git a/windows/_rc2bin/build.bat b/windows/_rc2bin/build.bat new file mode 100644 index 00000000..5aaccf4c --- /dev/null +++ b/windows/_rc2bin/build.bat @@ -0,0 +1,10 @@ +@rem 2 may 2018 +@echo off + +cl /nologo /TP /GR /EHsc /MDd /Ob0 /Od /RTC1 /W4 /wd4100 /bigobj /RTC1 /RTCs /RTCu /FS -c main.cpp +if errorlevel 1 goto out +rc -foresources.res resources.rc +if errorlevel 1 goto out +link /nologo main.obj resources.res /out:main.exe /LARGEADDRESSAWARE /NOLOGO /INCREMENTAL:NO /MANIFEST:NO /debug user32.lib kernel32.lib gdi32.lib comctl32.lib uxtheme.lib msimg32.lib comdlg32.lib d2d1.lib dwrite.lib ole32.lib oleaut32.lib oleacc.lib uuid.lib + +:out diff --git a/windows/_rc2bin/libui.manifest b/windows/_rc2bin/libui.manifest new file mode 100644 index 00000000..8beb6cfc --- /dev/null +++ b/windows/_rc2bin/libui.manifest @@ -0,0 +1,31 @@ + + + +Your application description here. + + + + + + + + + + + + + + + diff --git a/windows/_rc2bin/main.cpp b/windows/_rc2bin/main.cpp new file mode 100644 index 00000000..3f929719 --- /dev/null +++ b/windows/_rc2bin/main.cpp @@ -0,0 +1,67 @@ +// 2 may 2018 +#include "winapi.h" +#include +#include +#include "resources.h" + +void die(const char *f, const char *constname) +{ + DWORD le; + + le = GetLastError(); + fprintf(stderr, "error calling %s for %s: %I32d\n", f, constname, le); + exit(1); +} + +void dumpResource(const char *constname, const WCHAR *name, const WCHAR *type) +{ + HRSRC hrsrc; + HGLOBAL res; + uint8_t *b, *bp; + DWORD i, n; + DWORD j; + + hrsrc = FindResourceW(NULL, name, type); + if (hrsrc == NULL) + die("FindResourceW()", constname); + n = SizeofResource(NULL, hrsrc); + if (n == 0) + die("SizeofResource()", constname); + res = LoadResource(NULL, hrsrc); + if (res == NULL) + die("LoadResource()", constname); + b = (uint8_t *) LockResource(res); + if (b == NULL) + die("LockResource()", constname); + + printf("static const uint8_t %s[] = {\n", constname); + bp = b; + j = 0; + for (i = 0; i < n; i++) { + if (j == 0) + printf("\t"); + printf("0x%02I32X,", (uint32_t) (*bp)); + bp++; + if (j == 15) { + printf("\n"); + j = 0; + } else { + printf(" "); + j++; + } + } + if (j != 0) + printf("\n"); + printf("};\n"); + printf("static_assert(ARRAYSIZE(%s) == %I32d, \"wrong size for resource %s\")\n", constname, n, constname); + printf("\n"); +} + +int main(void) +{ +#define d(c, t) dumpResource(#c, MAKEINTRESOURCEW(c), t) + d(rcTabPageDialog, RT_DIALOG); + d(rcFontDialog, RT_DIALOG); + d(rcColorDialog, RT_DIALOG); + return 0; +} diff --git a/windows/_rc2bin/resources.hpp b/windows/_rc2bin/resources.hpp new file mode 100644 index 00000000..4ae54725 --- /dev/null +++ b/windows/_rc2bin/resources.hpp @@ -0,0 +1,37 @@ +// 30 may 2015 + +#define rcTabPageDialog 29000 +#define rcFontDialog 29001 +#define rcColorDialog 29002 + +// TODO normalize these + +#define rcFontFamilyCombobox 1000 +#define rcFontStyleCombobox 1001 +#define rcFontSizeCombobox 1002 +#define rcFontSamplePlacement 1003 + +#define rcColorSVChooser 1100 +#define rcColorHSlider 1101 +#define rcPreview 1102 +#define rcOpacitySlider 1103 +#define rcH 1104 +#define rcS 1105 +#define rcV 1106 +#define rcRDouble 1107 +#define rcRInt 1108 +#define rcGDouble 1109 +#define rcGInt 1110 +#define rcBDouble 1111 +#define rcBInt 1112 +#define rcADouble 1113 +#define rcAInt 1114 +#define rcHex 1115 +#define rcHLabel 1116 +#define rcSLabel 1117 +#define rcVLabel 1118 +#define rcRLabel 1119 +#define rcGLabel 1120 +#define rcBLabel 1121 +#define rcALabel 1122 +#define rcHexLabel 1123 diff --git a/windows/_rc2bin/resources.rc b/windows/_rc2bin/resources.rc new file mode 100644 index 00000000..989dfc91 --- /dev/null +++ b/windows/_rc2bin/resources.rc @@ -0,0 +1,96 @@ +// 30 may 2015 +#include "winapi.hpp" +#include "resources.hpp" + +// this is a UTF-8 file +#pragma code_page(65001) + +// this is the Common Controls 6 manifest +// we only define it in a shared build; static builds have to include the appropriate parts of the manifest in the output executable +// LONGTERM set up the string values here +#ifndef _UI_STATIC +ISOLATIONAWARE_MANIFEST_RESOURCE_ID RT_MANIFEST "libui.manifest" +#endif + +// this is the dialog template used by tab pages; see windows/tabpage.c for details +rcTabPageDialog DIALOGEX 0, 0, 100, 100 +STYLE DS_CONTROL | WS_CHILD | WS_VISIBLE +EXSTYLE WS_EX_CONTROLPARENT +BEGIN + // nothing +END + +// this is for our custom DirectWrite-based font dialog (see fontdialog.cpp) +// this is based on the "New Font Dialog with Syslink" in Microsoft's font.dlg +// LONGTERM look at localization +// LONGTERM make it look tighter and nicer like the real one, including the actual heights of the font family and style comboboxes +rcFontDialog DIALOGEX 13, 54, 243, 200 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU | DS_3DLOOK +CAPTION "Font" +FONT 9, "Segoe UI" +BEGIN + LTEXT "&Font:", -1, 7, 7, 98, 9 + COMBOBOX rcFontFamilyCombobox, 7, 16, 98, 76, + CBS_SIMPLE | CBS_AUTOHSCROLL | CBS_DISABLENOSCROLL | + CBS_SORT | WS_VSCROLL | WS_TABSTOP | CBS_HASSTRINGS + + LTEXT "Font st&yle:", -1, 114, 7, 74, 9 + COMBOBOX rcFontStyleCombobox, 114, 16, 74, 76, + CBS_SIMPLE | CBS_AUTOHSCROLL | CBS_DISABLENOSCROLL | + WS_VSCROLL | WS_TABSTOP | CBS_HASSTRINGS + + LTEXT "&Size:", -1, 198, 7, 36, 9 + COMBOBOX rcFontSizeCombobox, 198, 16, 36, 76, + CBS_SIMPLE | CBS_AUTOHSCROLL | CBS_DISABLENOSCROLL | + CBS_SORT | WS_VSCROLL | WS_TABSTOP | CBS_HASSTRINGS + + GROUPBOX "Sample", -1, 7, 97, 227, 70, WS_GROUP + CTEXT "AaBbYyZz", rcFontSamplePlacement, 9, 106, 224, 60, SS_NOPREFIX | NOT WS_VISIBLE + + DEFPUSHBUTTON "OK", IDOK, 141, 181, 45, 14, WS_GROUP + PUSHBUTTON "Cancel", IDCANCEL, 190, 181, 45, 14, WS_GROUP +END + +rcColorDialog DIALOGEX 13, 54, 344, 209 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU | DS_3DLOOK +CAPTION "Color" +FONT 9, "Segoe UI" +BEGIN + // this size should be big enough to get at least 256x256 on font sizes >= 8 pt + CTEXT "AaBbYyZz", rcColorSVChooser, 7, 7, 195, 195, SS_NOPREFIX | SS_BLACKRECT + + // width is the suggested slider height since this is vertical + CTEXT "AaBbYyZz", rcColorHSlider, 206, 7, 15, 195, SS_NOPREFIX | SS_BLACKRECT + + LTEXT "Preview:", -1, 230, 7, 107, 9, SS_NOPREFIX + CTEXT "AaBbYyZz", rcPreview, 230, 16, 107, 20, SS_NOPREFIX | SS_BLACKRECT + + LTEXT "Opacity:", -1, 230, 45, 107, 9, SS_NOPREFIX + CTEXT "AaBbYyZz", rcOpacitySlider, 230, 54, 107, 15, SS_NOPREFIX | SS_BLACKRECT + + LTEXT "&H:", rcHLabel, 230, 81, 8, 8 + EDITTEXT rcH, 238, 78, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + LTEXT "&S:", rcSLabel, 230, 95, 8, 8 + EDITTEXT rcS, 238, 92, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + LTEXT "&V:", rcVLabel, 230, 109, 8, 8 + EDITTEXT rcV, 238, 106, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + + LTEXT "&R:", rcRLabel, 277, 81, 8, 8 + EDITTEXT rcRDouble, 285, 78, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + EDITTEXT rcRInt, 315, 78, 20, 14, ES_LEFT | ES_AUTOHSCROLL | ES_NUMBER | WS_TABSTOP, WS_EX_CLIENTEDGE + LTEXT "&G:", rcGLabel, 277, 95, 8, 8 + EDITTEXT rcGDouble, 285, 92, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + EDITTEXT rcGInt, 315, 92, 20, 14, ES_LEFT | ES_AUTOHSCROLL | ES_NUMBER | WS_TABSTOP, WS_EX_CLIENTEDGE + LTEXT "&B:", rcBLabel, 277, 109, 8, 8 + EDITTEXT rcBDouble, 285, 106, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + EDITTEXT rcBInt, 315, 106, 20, 14, ES_LEFT | ES_AUTOHSCROLL | ES_NUMBER | WS_TABSTOP, WS_EX_CLIENTEDGE + LTEXT "&A:", rcALabel, 277, 123, 8, 8 + EDITTEXT rcADouble, 285, 120, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + EDITTEXT rcAInt, 315, 120, 20, 14, ES_LEFT | ES_AUTOHSCROLL | ES_NUMBER | WS_TABSTOP, WS_EX_CLIENTEDGE + + LTEXT "He&x:", rcHexLabel, 269, 146, 16, 8 + EDITTEXT rcHex, 285, 143, 50, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + + DEFPUSHBUTTON "OK", IDOK, 243, 188, 45, 14, WS_GROUP + PUSHBUTTON "Cancel", IDCANCEL, 292, 188, 45, 14, WS_GROUP +END diff --git a/windows/_rc2bin/winapi.hpp b/windows/_rc2bin/winapi.hpp new file mode 100644 index 00000000..4f24f607 --- /dev/null +++ b/windows/_rc2bin/winapi.hpp @@ -0,0 +1,60 @@ +// 31 may 2015 +#define UNICODE +#define _UNICODE +#define STRICT +#define STRICT_TYPED_ITEMIDS + +// see https://github.com/golang/go/issues/9916#issuecomment-74812211 +// TODO get rid of this +#define INITGUID + +// for the manifest +#ifndef _UI_STATIC +#define ISOLATION_AWARE_ENABLED 1 +#endif + +// get Windows version right; right now Windows Vista +// unless otherwise stated, all values from Microsoft's sdkddkver.h +// TODO is all of this necessary? how is NTDDI_VERSION used? +// TODO plaform update sp2 +#define WINVER 0x0600 /* from Microsoft's winnls.h */ +#define _WIN32_WINNT 0x0600 +#define _WIN32_WINDOWS 0x0600 /* from Microsoft's pdh.h */ +#define _WIN32_IE 0x0700 +#define NTDDI_VERSION 0x06000000 + +// The MinGW-w64 header has an unverified IDWriteTypography definition. +// TODO I can confirm this myself, but I don't know how long it will take for them to note my adjustments... Either way, I have to confirm this myself. +// TODO change the check from _MSC_VER to a MinGW-w64-specific check +// TODO keep track of what else is guarded by this +#ifndef _MSC_VER +#define __MINGW_USE_BROKEN_INTERFACE +#endif + +#include + +// Microsoft's resource compiler will segfault if we feed it headers it was not designed to handle +#ifndef RC_INVOKED +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#endif From 88882592acc9381a30621a26564c0e8e5a44ac2a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 2 May 2018 09:59:29 -0400 Subject: [PATCH 1012/1329] Fixed compiler errors and generated the output. --- windows/_rc2bin/main.cpp | 4 +- windows/_rc2bin/out32 | 116 +++++++++++++++++++++++++++++++++++++++ windows/_rc2bin/out64 | 116 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 234 insertions(+), 2 deletions(-) create mode 100644 windows/_rc2bin/out32 create mode 100644 windows/_rc2bin/out64 diff --git a/windows/_rc2bin/main.cpp b/windows/_rc2bin/main.cpp index 3f929719..84068a93 100644 --- a/windows/_rc2bin/main.cpp +++ b/windows/_rc2bin/main.cpp @@ -1,8 +1,8 @@ // 2 may 2018 -#include "winapi.h" +#include "winapi.hpp" #include #include -#include "resources.h" +#include "resources.hpp" void die(const char *f, const char *constname) { diff --git a/windows/_rc2bin/out32 b/windows/_rc2bin/out32 new file mode 100644 index 00000000..8cdcdbe3 --- /dev/null +++ b/windows/_rc2bin/out32 @@ -0,0 +1,116 @@ +static const uint8_t rcTabPageDialog[] = { + 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x50, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; +static_assert(ARRAYSIZE(rcTabPageDialog) == 32, "wrong size for resource rcTabPageDialog") + +static const uint8_t rcFontDialog[] = { + 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC4, 0x00, 0xC8, 0x80, + 0x0A, 0x00, 0x0D, 0x00, 0x36, 0x00, 0xF3, 0x00, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, + 0x6F, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x01, 0x53, 0x00, + 0x65, 0x00, 0x67, 0x00, 0x6F, 0x00, 0x65, 0x00, 0x20, 0x00, 0x55, 0x00, 0x49, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0x07, 0x00, 0x07, 0x00, + 0x62, 0x00, 0x09, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, 0x26, 0x00, 0x46, 0x00, + 0x6F, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x0B, 0x21, 0x50, 0x07, 0x00, 0x10, 0x00, 0x62, 0x00, 0x4C, 0x00, + 0xE8, 0x03, 0x00, 0x00, 0xFF, 0xFF, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0x72, 0x00, 0x07, 0x00, 0x4A, 0x00, 0x09, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, 0x46, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x74, 0x00, + 0x20, 0x00, 0x73, 0x00, 0x74, 0x00, 0x26, 0x00, 0x79, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x3A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x0A, 0x21, 0x50, + 0x72, 0x00, 0x10, 0x00, 0x4A, 0x00, 0x4C, 0x00, 0xE9, 0x03, 0x00, 0x00, 0xFF, 0xFF, 0x85, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, + 0xC6, 0x00, 0x07, 0x00, 0x24, 0x00, 0x09, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, + 0x26, 0x00, 0x53, 0x00, 0x69, 0x00, 0x7A, 0x00, 0x65, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x0B, 0x21, 0x50, 0xC6, 0x00, 0x10, 0x00, + 0x24, 0x00, 0x4C, 0x00, 0xEA, 0x03, 0x00, 0x00, 0xFF, 0xFF, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x02, 0x50, 0x07, 0x00, 0x61, 0x00, + 0xE3, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x53, 0x00, 0x61, 0x00, + 0x6D, 0x00, 0x70, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x02, 0x40, 0x09, 0x00, 0x6A, 0x00, 0xE0, 0x00, 0x3C, 0x00, + 0xEB, 0x03, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, + 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x50, 0x8D, 0x00, 0xB5, 0x00, 0x2D, 0x00, 0x0E, 0x00, + 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x00, 0x4F, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x50, 0xBE, 0x00, 0xB5, 0x00, + 0x2D, 0x00, 0x0E, 0x00, 0x02, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x00, 0x43, 0x00, 0x61, 0x00, + 0x6E, 0x00, 0x63, 0x00, 0x65, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, +}; +static_assert(ARRAYSIZE(rcFontDialog) == 476, "wrong size for resource rcFontDialog") + +static const uint8_t rcColorDialog[] = { + 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC4, 0x00, 0xC8, 0x80, + 0x1C, 0x00, 0x0D, 0x00, 0x36, 0x00, 0x58, 0x01, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x00, + 0x6F, 0x00, 0x6C, 0x00, 0x6F, 0x00, 0x72, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x53, 0x00, 0x65, 0x00, 0x67, 0x00, 0x6F, 0x00, 0x65, 0x00, 0x20, 0x00, 0x55, 0x00, 0x49, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x02, 0x50, + 0x07, 0x00, 0x07, 0x00, 0xC3, 0x00, 0xC3, 0x00, 0x4C, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x02, 0x50, + 0xCE, 0x00, 0x07, 0x00, 0x0F, 0x00, 0xC3, 0x00, 0x4D, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x02, 0x50, + 0xE6, 0x00, 0x07, 0x00, 0x6B, 0x00, 0x09, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, + 0x50, 0x00, 0x72, 0x00, 0x65, 0x00, 0x76, 0x00, 0x69, 0x00, 0x65, 0x00, 0x77, 0x00, 0x3A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x02, 0x50, + 0xE6, 0x00, 0x10, 0x00, 0x6B, 0x00, 0x14, 0x00, 0x4E, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x02, 0x50, + 0xE6, 0x00, 0x2D, 0x00, 0x6B, 0x00, 0x09, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, + 0x4F, 0x00, 0x70, 0x00, 0x61, 0x00, 0x63, 0x00, 0x69, 0x00, 0x74, 0x00, 0x79, 0x00, 0x3A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x02, 0x50, + 0xE6, 0x00, 0x36, 0x00, 0x6B, 0x00, 0x0F, 0x00, 0x4F, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, + 0xE6, 0x00, 0x51, 0x00, 0x08, 0x00, 0x08, 0x00, 0x5C, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x26, 0x00, 0x48, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, 0xEE, 0x00, 0x4E, 0x00, 0x1E, 0x00, 0x0E, 0x00, + 0x50, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0xE6, 0x00, 0x5F, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x5D, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, 0x26, 0x00, 0x53, 0x00, 0x3A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, + 0xEE, 0x00, 0x5C, 0x00, 0x1E, 0x00, 0x0E, 0x00, 0x51, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, + 0xE6, 0x00, 0x6D, 0x00, 0x08, 0x00, 0x08, 0x00, 0x5E, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x26, 0x00, 0x56, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, 0xEE, 0x00, 0x6A, 0x00, 0x1E, 0x00, 0x0E, 0x00, + 0x52, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0x15, 0x01, 0x51, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x5F, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, 0x26, 0x00, 0x52, 0x00, 0x3A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, + 0x1D, 0x01, 0x4E, 0x00, 0x1E, 0x00, 0x0E, 0x00, 0x53, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x20, 0x81, 0x50, + 0x3B, 0x01, 0x4E, 0x00, 0x14, 0x00, 0x0E, 0x00, 0x54, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, + 0x15, 0x01, 0x5F, 0x00, 0x08, 0x00, 0x08, 0x00, 0x60, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x26, 0x00, 0x47, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, 0x1D, 0x01, 0x5C, 0x00, 0x1E, 0x00, 0x0E, 0x00, + 0x55, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x20, 0x81, 0x50, 0x3B, 0x01, 0x5C, 0x00, 0x14, 0x00, 0x0E, 0x00, + 0x56, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0x15, 0x01, 0x6D, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x61, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, 0x26, 0x00, 0x42, 0x00, 0x3A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, + 0x1D, 0x01, 0x6A, 0x00, 0x1E, 0x00, 0x0E, 0x00, 0x57, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x20, 0x81, 0x50, + 0x3B, 0x01, 0x6A, 0x00, 0x14, 0x00, 0x0E, 0x00, 0x58, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, + 0x15, 0x01, 0x7B, 0x00, 0x08, 0x00, 0x08, 0x00, 0x62, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x26, 0x00, 0x41, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, 0x1D, 0x01, 0x78, 0x00, 0x1E, 0x00, 0x0E, 0x00, + 0x59, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x20, 0x81, 0x50, 0x3B, 0x01, 0x78, 0x00, 0x14, 0x00, 0x0E, 0x00, + 0x5A, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0x0D, 0x01, 0x92, 0x00, 0x10, 0x00, 0x08, 0x00, + 0x63, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, 0x48, 0x00, 0x65, 0x00, 0x26, 0x00, 0x78, 0x00, + 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x80, 0x00, 0x81, 0x50, 0x1D, 0x01, 0x8F, 0x00, 0x32, 0x00, 0x0E, 0x00, 0x5B, 0x04, 0x00, 0x00, + 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x03, 0x50, 0xF3, 0x00, 0xBC, 0x00, 0x2D, 0x00, 0x0E, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x80, 0x00, 0x4F, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x50, 0x24, 0x01, 0xBC, 0x00, 0x2D, 0x00, 0x0E, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x00, 0x43, 0x00, 0x61, 0x00, 0x6E, 0x00, 0x63, 0x00, + 0x65, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, +}; +static_assert(ARRAYSIZE(rcColorDialog) == 1144, "wrong size for resource rcColorDialog") + diff --git a/windows/_rc2bin/out64 b/windows/_rc2bin/out64 new file mode 100644 index 00000000..8cdcdbe3 --- /dev/null +++ b/windows/_rc2bin/out64 @@ -0,0 +1,116 @@ +static const uint8_t rcTabPageDialog[] = { + 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x50, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; +static_assert(ARRAYSIZE(rcTabPageDialog) == 32, "wrong size for resource rcTabPageDialog") + +static const uint8_t rcFontDialog[] = { + 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC4, 0x00, 0xC8, 0x80, + 0x0A, 0x00, 0x0D, 0x00, 0x36, 0x00, 0xF3, 0x00, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, + 0x6F, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x01, 0x53, 0x00, + 0x65, 0x00, 0x67, 0x00, 0x6F, 0x00, 0x65, 0x00, 0x20, 0x00, 0x55, 0x00, 0x49, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0x07, 0x00, 0x07, 0x00, + 0x62, 0x00, 0x09, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, 0x26, 0x00, 0x46, 0x00, + 0x6F, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x0B, 0x21, 0x50, 0x07, 0x00, 0x10, 0x00, 0x62, 0x00, 0x4C, 0x00, + 0xE8, 0x03, 0x00, 0x00, 0xFF, 0xFF, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0x72, 0x00, 0x07, 0x00, 0x4A, 0x00, 0x09, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, 0x46, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x74, 0x00, + 0x20, 0x00, 0x73, 0x00, 0x74, 0x00, 0x26, 0x00, 0x79, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x3A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x0A, 0x21, 0x50, + 0x72, 0x00, 0x10, 0x00, 0x4A, 0x00, 0x4C, 0x00, 0xE9, 0x03, 0x00, 0x00, 0xFF, 0xFF, 0x85, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, + 0xC6, 0x00, 0x07, 0x00, 0x24, 0x00, 0x09, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, + 0x26, 0x00, 0x53, 0x00, 0x69, 0x00, 0x7A, 0x00, 0x65, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x0B, 0x21, 0x50, 0xC6, 0x00, 0x10, 0x00, + 0x24, 0x00, 0x4C, 0x00, 0xEA, 0x03, 0x00, 0x00, 0xFF, 0xFF, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x02, 0x50, 0x07, 0x00, 0x61, 0x00, + 0xE3, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x53, 0x00, 0x61, 0x00, + 0x6D, 0x00, 0x70, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x02, 0x40, 0x09, 0x00, 0x6A, 0x00, 0xE0, 0x00, 0x3C, 0x00, + 0xEB, 0x03, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, + 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x50, 0x8D, 0x00, 0xB5, 0x00, 0x2D, 0x00, 0x0E, 0x00, + 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x00, 0x4F, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x50, 0xBE, 0x00, 0xB5, 0x00, + 0x2D, 0x00, 0x0E, 0x00, 0x02, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x00, 0x43, 0x00, 0x61, 0x00, + 0x6E, 0x00, 0x63, 0x00, 0x65, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, +}; +static_assert(ARRAYSIZE(rcFontDialog) == 476, "wrong size for resource rcFontDialog") + +static const uint8_t rcColorDialog[] = { + 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC4, 0x00, 0xC8, 0x80, + 0x1C, 0x00, 0x0D, 0x00, 0x36, 0x00, 0x58, 0x01, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x00, + 0x6F, 0x00, 0x6C, 0x00, 0x6F, 0x00, 0x72, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x53, 0x00, 0x65, 0x00, 0x67, 0x00, 0x6F, 0x00, 0x65, 0x00, 0x20, 0x00, 0x55, 0x00, 0x49, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x02, 0x50, + 0x07, 0x00, 0x07, 0x00, 0xC3, 0x00, 0xC3, 0x00, 0x4C, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x02, 0x50, + 0xCE, 0x00, 0x07, 0x00, 0x0F, 0x00, 0xC3, 0x00, 0x4D, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x02, 0x50, + 0xE6, 0x00, 0x07, 0x00, 0x6B, 0x00, 0x09, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, + 0x50, 0x00, 0x72, 0x00, 0x65, 0x00, 0x76, 0x00, 0x69, 0x00, 0x65, 0x00, 0x77, 0x00, 0x3A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x02, 0x50, + 0xE6, 0x00, 0x10, 0x00, 0x6B, 0x00, 0x14, 0x00, 0x4E, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x02, 0x50, + 0xE6, 0x00, 0x2D, 0x00, 0x6B, 0x00, 0x09, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, + 0x4F, 0x00, 0x70, 0x00, 0x61, 0x00, 0x63, 0x00, 0x69, 0x00, 0x74, 0x00, 0x79, 0x00, 0x3A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x02, 0x50, + 0xE6, 0x00, 0x36, 0x00, 0x6B, 0x00, 0x0F, 0x00, 0x4F, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, + 0xE6, 0x00, 0x51, 0x00, 0x08, 0x00, 0x08, 0x00, 0x5C, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x26, 0x00, 0x48, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, 0xEE, 0x00, 0x4E, 0x00, 0x1E, 0x00, 0x0E, 0x00, + 0x50, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0xE6, 0x00, 0x5F, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x5D, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, 0x26, 0x00, 0x53, 0x00, 0x3A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, + 0xEE, 0x00, 0x5C, 0x00, 0x1E, 0x00, 0x0E, 0x00, 0x51, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, + 0xE6, 0x00, 0x6D, 0x00, 0x08, 0x00, 0x08, 0x00, 0x5E, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x26, 0x00, 0x56, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, 0xEE, 0x00, 0x6A, 0x00, 0x1E, 0x00, 0x0E, 0x00, + 0x52, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0x15, 0x01, 0x51, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x5F, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, 0x26, 0x00, 0x52, 0x00, 0x3A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, + 0x1D, 0x01, 0x4E, 0x00, 0x1E, 0x00, 0x0E, 0x00, 0x53, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x20, 0x81, 0x50, + 0x3B, 0x01, 0x4E, 0x00, 0x14, 0x00, 0x0E, 0x00, 0x54, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, + 0x15, 0x01, 0x5F, 0x00, 0x08, 0x00, 0x08, 0x00, 0x60, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x26, 0x00, 0x47, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, 0x1D, 0x01, 0x5C, 0x00, 0x1E, 0x00, 0x0E, 0x00, + 0x55, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x20, 0x81, 0x50, 0x3B, 0x01, 0x5C, 0x00, 0x14, 0x00, 0x0E, 0x00, + 0x56, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0x15, 0x01, 0x6D, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x61, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, 0x26, 0x00, 0x42, 0x00, 0x3A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, + 0x1D, 0x01, 0x6A, 0x00, 0x1E, 0x00, 0x0E, 0x00, 0x57, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x20, 0x81, 0x50, + 0x3B, 0x01, 0x6A, 0x00, 0x14, 0x00, 0x0E, 0x00, 0x58, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, + 0x15, 0x01, 0x7B, 0x00, 0x08, 0x00, 0x08, 0x00, 0x62, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x26, 0x00, 0x41, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, 0x1D, 0x01, 0x78, 0x00, 0x1E, 0x00, 0x0E, 0x00, + 0x59, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x20, 0x81, 0x50, 0x3B, 0x01, 0x78, 0x00, 0x14, 0x00, 0x0E, 0x00, + 0x5A, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0x0D, 0x01, 0x92, 0x00, 0x10, 0x00, 0x08, 0x00, + 0x63, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, 0x48, 0x00, 0x65, 0x00, 0x26, 0x00, 0x78, 0x00, + 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x80, 0x00, 0x81, 0x50, 0x1D, 0x01, 0x8F, 0x00, 0x32, 0x00, 0x0E, 0x00, 0x5B, 0x04, 0x00, 0x00, + 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x03, 0x50, 0xF3, 0x00, 0xBC, 0x00, 0x2D, 0x00, 0x0E, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x80, 0x00, 0x4F, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x50, 0x24, 0x01, 0xBC, 0x00, 0x2D, 0x00, 0x0E, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x00, 0x43, 0x00, 0x61, 0x00, 0x6E, 0x00, 0x63, 0x00, + 0x65, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, +}; +static_assert(ARRAYSIZE(rcColorDialog) == 1144, "wrong size for resource rcColorDialog") + From e11196304f67621ef706b1fb277e90b3d24f205e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 2 May 2018 10:00:44 -0400 Subject: [PATCH 1013/1329] Since out32 and out64 are identical, deduplicate them. Now we can move its contents elsewhere. --- windows/_rc2bin/{out32 => out} | 0 windows/_rc2bin/out64 | 116 --------------------------------- 2 files changed, 116 deletions(-) rename windows/_rc2bin/{out32 => out} (100%) delete mode 100644 windows/_rc2bin/out64 diff --git a/windows/_rc2bin/out32 b/windows/_rc2bin/out similarity index 100% rename from windows/_rc2bin/out32 rename to windows/_rc2bin/out diff --git a/windows/_rc2bin/out64 b/windows/_rc2bin/out64 deleted file mode 100644 index 8cdcdbe3..00000000 --- a/windows/_rc2bin/out64 +++ /dev/null @@ -1,116 +0,0 @@ -static const uint8_t rcTabPageDialog[] = { - 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x50, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; -static_assert(ARRAYSIZE(rcTabPageDialog) == 32, "wrong size for resource rcTabPageDialog") - -static const uint8_t rcFontDialog[] = { - 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC4, 0x00, 0xC8, 0x80, - 0x0A, 0x00, 0x0D, 0x00, 0x36, 0x00, 0xF3, 0x00, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, - 0x6F, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x01, 0x53, 0x00, - 0x65, 0x00, 0x67, 0x00, 0x6F, 0x00, 0x65, 0x00, 0x20, 0x00, 0x55, 0x00, 0x49, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0x07, 0x00, 0x07, 0x00, - 0x62, 0x00, 0x09, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, 0x26, 0x00, 0x46, 0x00, - 0x6F, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x0B, 0x21, 0x50, 0x07, 0x00, 0x10, 0x00, 0x62, 0x00, 0x4C, 0x00, - 0xE8, 0x03, 0x00, 0x00, 0xFF, 0xFF, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0x72, 0x00, 0x07, 0x00, 0x4A, 0x00, 0x09, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, 0x46, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x74, 0x00, - 0x20, 0x00, 0x73, 0x00, 0x74, 0x00, 0x26, 0x00, 0x79, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x3A, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x0A, 0x21, 0x50, - 0x72, 0x00, 0x10, 0x00, 0x4A, 0x00, 0x4C, 0x00, 0xE9, 0x03, 0x00, 0x00, 0xFF, 0xFF, 0x85, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, - 0xC6, 0x00, 0x07, 0x00, 0x24, 0x00, 0x09, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, - 0x26, 0x00, 0x53, 0x00, 0x69, 0x00, 0x7A, 0x00, 0x65, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x0B, 0x21, 0x50, 0xC6, 0x00, 0x10, 0x00, - 0x24, 0x00, 0x4C, 0x00, 0xEA, 0x03, 0x00, 0x00, 0xFF, 0xFF, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x02, 0x50, 0x07, 0x00, 0x61, 0x00, - 0xE3, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x53, 0x00, 0x61, 0x00, - 0x6D, 0x00, 0x70, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x02, 0x40, 0x09, 0x00, 0x6A, 0x00, 0xE0, 0x00, 0x3C, 0x00, - 0xEB, 0x03, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, - 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x50, 0x8D, 0x00, 0xB5, 0x00, 0x2D, 0x00, 0x0E, 0x00, - 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x00, 0x4F, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x50, 0xBE, 0x00, 0xB5, 0x00, - 0x2D, 0x00, 0x0E, 0x00, 0x02, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x00, 0x43, 0x00, 0x61, 0x00, - 0x6E, 0x00, 0x63, 0x00, 0x65, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, -}; -static_assert(ARRAYSIZE(rcFontDialog) == 476, "wrong size for resource rcFontDialog") - -static const uint8_t rcColorDialog[] = { - 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC4, 0x00, 0xC8, 0x80, - 0x1C, 0x00, 0x0D, 0x00, 0x36, 0x00, 0x58, 0x01, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x00, - 0x6F, 0x00, 0x6C, 0x00, 0x6F, 0x00, 0x72, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x53, 0x00, 0x65, 0x00, 0x67, 0x00, 0x6F, 0x00, 0x65, 0x00, 0x20, 0x00, 0x55, 0x00, 0x49, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x02, 0x50, - 0x07, 0x00, 0x07, 0x00, 0xC3, 0x00, 0xC3, 0x00, 0x4C, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, - 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x02, 0x50, - 0xCE, 0x00, 0x07, 0x00, 0x0F, 0x00, 0xC3, 0x00, 0x4D, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, - 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x02, 0x50, - 0xE6, 0x00, 0x07, 0x00, 0x6B, 0x00, 0x09, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, - 0x50, 0x00, 0x72, 0x00, 0x65, 0x00, 0x76, 0x00, 0x69, 0x00, 0x65, 0x00, 0x77, 0x00, 0x3A, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x02, 0x50, - 0xE6, 0x00, 0x10, 0x00, 0x6B, 0x00, 0x14, 0x00, 0x4E, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, - 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x02, 0x50, - 0xE6, 0x00, 0x2D, 0x00, 0x6B, 0x00, 0x09, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, - 0x4F, 0x00, 0x70, 0x00, 0x61, 0x00, 0x63, 0x00, 0x69, 0x00, 0x74, 0x00, 0x79, 0x00, 0x3A, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x02, 0x50, - 0xE6, 0x00, 0x36, 0x00, 0x6B, 0x00, 0x0F, 0x00, 0x4F, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, - 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, - 0xE6, 0x00, 0x51, 0x00, 0x08, 0x00, 0x08, 0x00, 0x5C, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, - 0x26, 0x00, 0x48, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, 0xEE, 0x00, 0x4E, 0x00, 0x1E, 0x00, 0x0E, 0x00, - 0x50, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0xE6, 0x00, 0x5F, 0x00, 0x08, 0x00, 0x08, 0x00, - 0x5D, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, 0x26, 0x00, 0x53, 0x00, 0x3A, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, - 0xEE, 0x00, 0x5C, 0x00, 0x1E, 0x00, 0x0E, 0x00, 0x51, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, - 0xE6, 0x00, 0x6D, 0x00, 0x08, 0x00, 0x08, 0x00, 0x5E, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, - 0x26, 0x00, 0x56, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, 0xEE, 0x00, 0x6A, 0x00, 0x1E, 0x00, 0x0E, 0x00, - 0x52, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0x15, 0x01, 0x51, 0x00, 0x08, 0x00, 0x08, 0x00, - 0x5F, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, 0x26, 0x00, 0x52, 0x00, 0x3A, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, - 0x1D, 0x01, 0x4E, 0x00, 0x1E, 0x00, 0x0E, 0x00, 0x53, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x20, 0x81, 0x50, - 0x3B, 0x01, 0x4E, 0x00, 0x14, 0x00, 0x0E, 0x00, 0x54, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, - 0x15, 0x01, 0x5F, 0x00, 0x08, 0x00, 0x08, 0x00, 0x60, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, - 0x26, 0x00, 0x47, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, 0x1D, 0x01, 0x5C, 0x00, 0x1E, 0x00, 0x0E, 0x00, - 0x55, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x80, 0x20, 0x81, 0x50, 0x3B, 0x01, 0x5C, 0x00, 0x14, 0x00, 0x0E, 0x00, - 0x56, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0x15, 0x01, 0x6D, 0x00, 0x08, 0x00, 0x08, 0x00, - 0x61, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, 0x26, 0x00, 0x42, 0x00, 0x3A, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, - 0x1D, 0x01, 0x6A, 0x00, 0x1E, 0x00, 0x0E, 0x00, 0x57, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x20, 0x81, 0x50, - 0x3B, 0x01, 0x6A, 0x00, 0x14, 0x00, 0x0E, 0x00, 0x58, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, - 0x15, 0x01, 0x7B, 0x00, 0x08, 0x00, 0x08, 0x00, 0x62, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, - 0x26, 0x00, 0x41, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, 0x1D, 0x01, 0x78, 0x00, 0x1E, 0x00, 0x0E, 0x00, - 0x59, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x80, 0x20, 0x81, 0x50, 0x3B, 0x01, 0x78, 0x00, 0x14, 0x00, 0x0E, 0x00, - 0x5A, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0x0D, 0x01, 0x92, 0x00, 0x10, 0x00, 0x08, 0x00, - 0x63, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, 0x48, 0x00, 0x65, 0x00, 0x26, 0x00, 0x78, 0x00, - 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, - 0x80, 0x00, 0x81, 0x50, 0x1D, 0x01, 0x8F, 0x00, 0x32, 0x00, 0x0E, 0x00, 0x5B, 0x04, 0x00, 0x00, - 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x03, 0x50, 0xF3, 0x00, 0xBC, 0x00, 0x2D, 0x00, 0x0E, 0x00, 0x01, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0x80, 0x00, 0x4F, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x50, 0x24, 0x01, 0xBC, 0x00, 0x2D, 0x00, 0x0E, 0x00, - 0x02, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x00, 0x43, 0x00, 0x61, 0x00, 0x6E, 0x00, 0x63, 0x00, - 0x65, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, -}; -static_assert(ARRAYSIZE(rcColorDialog) == 1144, "wrong size for resource rcColorDialog") - From c6979fa73870da72fa33c53124b2b6abf0ce67ba Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 2 May 2018 20:17:08 -0400 Subject: [PATCH 1014/1329] Removed rcTabPageDIalog from the resources. Also removed CRs from the out file. Need to fix that generator... --- windows/_rc2bin/out | 228 +++++++++++++++++++++---------------------- windows/resources.rc | 8 -- windows/tabpage.cpp | 20 +++- 3 files changed, 131 insertions(+), 125 deletions(-) diff --git a/windows/_rc2bin/out b/windows/_rc2bin/out index 8cdcdbe3..4dcfbaeb 100644 --- a/windows/_rc2bin/out +++ b/windows/_rc2bin/out @@ -1,116 +1,112 @@ -static const uint8_t rcTabPageDialog[] = { - 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x50, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; -static_assert(ARRAYSIZE(rcTabPageDialog) == 32, "wrong size for resource rcTabPageDialog") - -static const uint8_t rcFontDialog[] = { - 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC4, 0x00, 0xC8, 0x80, - 0x0A, 0x00, 0x0D, 0x00, 0x36, 0x00, 0xF3, 0x00, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, - 0x6F, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x01, 0x53, 0x00, - 0x65, 0x00, 0x67, 0x00, 0x6F, 0x00, 0x65, 0x00, 0x20, 0x00, 0x55, 0x00, 0x49, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0x07, 0x00, 0x07, 0x00, - 0x62, 0x00, 0x09, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, 0x26, 0x00, 0x46, 0x00, - 0x6F, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x0B, 0x21, 0x50, 0x07, 0x00, 0x10, 0x00, 0x62, 0x00, 0x4C, 0x00, - 0xE8, 0x03, 0x00, 0x00, 0xFF, 0xFF, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0x72, 0x00, 0x07, 0x00, 0x4A, 0x00, 0x09, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, 0x46, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x74, 0x00, - 0x20, 0x00, 0x73, 0x00, 0x74, 0x00, 0x26, 0x00, 0x79, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x3A, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x0A, 0x21, 0x50, - 0x72, 0x00, 0x10, 0x00, 0x4A, 0x00, 0x4C, 0x00, 0xE9, 0x03, 0x00, 0x00, 0xFF, 0xFF, 0x85, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, - 0xC6, 0x00, 0x07, 0x00, 0x24, 0x00, 0x09, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, - 0x26, 0x00, 0x53, 0x00, 0x69, 0x00, 0x7A, 0x00, 0x65, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x0B, 0x21, 0x50, 0xC6, 0x00, 0x10, 0x00, - 0x24, 0x00, 0x4C, 0x00, 0xEA, 0x03, 0x00, 0x00, 0xFF, 0xFF, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x02, 0x50, 0x07, 0x00, 0x61, 0x00, - 0xE3, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x53, 0x00, 0x61, 0x00, - 0x6D, 0x00, 0x70, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x02, 0x40, 0x09, 0x00, 0x6A, 0x00, 0xE0, 0x00, 0x3C, 0x00, - 0xEB, 0x03, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, - 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x50, 0x8D, 0x00, 0xB5, 0x00, 0x2D, 0x00, 0x0E, 0x00, - 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x00, 0x4F, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x50, 0xBE, 0x00, 0xB5, 0x00, - 0x2D, 0x00, 0x0E, 0x00, 0x02, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x00, 0x43, 0x00, 0x61, 0x00, - 0x6E, 0x00, 0x63, 0x00, 0x65, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, -}; -static_assert(ARRAYSIZE(rcFontDialog) == 476, "wrong size for resource rcFontDialog") - -static const uint8_t rcColorDialog[] = { - 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC4, 0x00, 0xC8, 0x80, - 0x1C, 0x00, 0x0D, 0x00, 0x36, 0x00, 0x58, 0x01, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x00, - 0x6F, 0x00, 0x6C, 0x00, 0x6F, 0x00, 0x72, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x53, 0x00, 0x65, 0x00, 0x67, 0x00, 0x6F, 0x00, 0x65, 0x00, 0x20, 0x00, 0x55, 0x00, 0x49, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x02, 0x50, - 0x07, 0x00, 0x07, 0x00, 0xC3, 0x00, 0xC3, 0x00, 0x4C, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, - 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x02, 0x50, - 0xCE, 0x00, 0x07, 0x00, 0x0F, 0x00, 0xC3, 0x00, 0x4D, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, - 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x02, 0x50, - 0xE6, 0x00, 0x07, 0x00, 0x6B, 0x00, 0x09, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, - 0x50, 0x00, 0x72, 0x00, 0x65, 0x00, 0x76, 0x00, 0x69, 0x00, 0x65, 0x00, 0x77, 0x00, 0x3A, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x02, 0x50, - 0xE6, 0x00, 0x10, 0x00, 0x6B, 0x00, 0x14, 0x00, 0x4E, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, - 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x02, 0x50, - 0xE6, 0x00, 0x2D, 0x00, 0x6B, 0x00, 0x09, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, - 0x4F, 0x00, 0x70, 0x00, 0x61, 0x00, 0x63, 0x00, 0x69, 0x00, 0x74, 0x00, 0x79, 0x00, 0x3A, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x02, 0x50, - 0xE6, 0x00, 0x36, 0x00, 0x6B, 0x00, 0x0F, 0x00, 0x4F, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, - 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, - 0xE6, 0x00, 0x51, 0x00, 0x08, 0x00, 0x08, 0x00, 0x5C, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, - 0x26, 0x00, 0x48, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, 0xEE, 0x00, 0x4E, 0x00, 0x1E, 0x00, 0x0E, 0x00, - 0x50, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0xE6, 0x00, 0x5F, 0x00, 0x08, 0x00, 0x08, 0x00, - 0x5D, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, 0x26, 0x00, 0x53, 0x00, 0x3A, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, - 0xEE, 0x00, 0x5C, 0x00, 0x1E, 0x00, 0x0E, 0x00, 0x51, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, - 0xE6, 0x00, 0x6D, 0x00, 0x08, 0x00, 0x08, 0x00, 0x5E, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, - 0x26, 0x00, 0x56, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, 0xEE, 0x00, 0x6A, 0x00, 0x1E, 0x00, 0x0E, 0x00, - 0x52, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0x15, 0x01, 0x51, 0x00, 0x08, 0x00, 0x08, 0x00, - 0x5F, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, 0x26, 0x00, 0x52, 0x00, 0x3A, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, - 0x1D, 0x01, 0x4E, 0x00, 0x1E, 0x00, 0x0E, 0x00, 0x53, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x20, 0x81, 0x50, - 0x3B, 0x01, 0x4E, 0x00, 0x14, 0x00, 0x0E, 0x00, 0x54, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, - 0x15, 0x01, 0x5F, 0x00, 0x08, 0x00, 0x08, 0x00, 0x60, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, - 0x26, 0x00, 0x47, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, 0x1D, 0x01, 0x5C, 0x00, 0x1E, 0x00, 0x0E, 0x00, - 0x55, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x80, 0x20, 0x81, 0x50, 0x3B, 0x01, 0x5C, 0x00, 0x14, 0x00, 0x0E, 0x00, - 0x56, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0x15, 0x01, 0x6D, 0x00, 0x08, 0x00, 0x08, 0x00, - 0x61, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, 0x26, 0x00, 0x42, 0x00, 0x3A, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, - 0x1D, 0x01, 0x6A, 0x00, 0x1E, 0x00, 0x0E, 0x00, 0x57, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x20, 0x81, 0x50, - 0x3B, 0x01, 0x6A, 0x00, 0x14, 0x00, 0x0E, 0x00, 0x58, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, - 0x15, 0x01, 0x7B, 0x00, 0x08, 0x00, 0x08, 0x00, 0x62, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, - 0x26, 0x00, 0x41, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, 0x1D, 0x01, 0x78, 0x00, 0x1E, 0x00, 0x0E, 0x00, - 0x59, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x80, 0x20, 0x81, 0x50, 0x3B, 0x01, 0x78, 0x00, 0x14, 0x00, 0x0E, 0x00, - 0x5A, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0x0D, 0x01, 0x92, 0x00, 0x10, 0x00, 0x08, 0x00, - 0x63, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, 0x48, 0x00, 0x65, 0x00, 0x26, 0x00, 0x78, 0x00, - 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, - 0x80, 0x00, 0x81, 0x50, 0x1D, 0x01, 0x8F, 0x00, 0x32, 0x00, 0x0E, 0x00, 0x5B, 0x04, 0x00, 0x00, - 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x03, 0x50, 0xF3, 0x00, 0xBC, 0x00, 0x2D, 0x00, 0x0E, 0x00, 0x01, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0x80, 0x00, 0x4F, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x50, 0x24, 0x01, 0xBC, 0x00, 0x2D, 0x00, 0x0E, 0x00, - 0x02, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x00, 0x43, 0x00, 0x61, 0x00, 0x6E, 0x00, 0x63, 0x00, - 0x65, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, -}; -static_assert(ARRAYSIZE(rcColorDialog) == 1144, "wrong size for resource rcColorDialog") - +// because Windows doesn't really support resources in static libraries, we have to embed this directly; oh well + +static const uint8_t rcFontDialog[] = { + 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC4, 0x00, 0xC8, 0x80, + 0x0A, 0x00, 0x0D, 0x00, 0x36, 0x00, 0xF3, 0x00, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, + 0x6F, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x01, 0x53, 0x00, + 0x65, 0x00, 0x67, 0x00, 0x6F, 0x00, 0x65, 0x00, 0x20, 0x00, 0x55, 0x00, 0x49, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0x07, 0x00, 0x07, 0x00, + 0x62, 0x00, 0x09, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, 0x26, 0x00, 0x46, 0x00, + 0x6F, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x0B, 0x21, 0x50, 0x07, 0x00, 0x10, 0x00, 0x62, 0x00, 0x4C, 0x00, + 0xE8, 0x03, 0x00, 0x00, 0xFF, 0xFF, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0x72, 0x00, 0x07, 0x00, 0x4A, 0x00, 0x09, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, 0x46, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x74, 0x00, + 0x20, 0x00, 0x73, 0x00, 0x74, 0x00, 0x26, 0x00, 0x79, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x3A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x0A, 0x21, 0x50, + 0x72, 0x00, 0x10, 0x00, 0x4A, 0x00, 0x4C, 0x00, 0xE9, 0x03, 0x00, 0x00, 0xFF, 0xFF, 0x85, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, + 0xC6, 0x00, 0x07, 0x00, 0x24, 0x00, 0x09, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, + 0x26, 0x00, 0x53, 0x00, 0x69, 0x00, 0x7A, 0x00, 0x65, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x0B, 0x21, 0x50, 0xC6, 0x00, 0x10, 0x00, + 0x24, 0x00, 0x4C, 0x00, 0xEA, 0x03, 0x00, 0x00, 0xFF, 0xFF, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x02, 0x50, 0x07, 0x00, 0x61, 0x00, + 0xE3, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x53, 0x00, 0x61, 0x00, + 0x6D, 0x00, 0x70, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x02, 0x40, 0x09, 0x00, 0x6A, 0x00, 0xE0, 0x00, 0x3C, 0x00, + 0xEB, 0x03, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, + 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x50, 0x8D, 0x00, 0xB5, 0x00, 0x2D, 0x00, 0x0E, 0x00, + 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x00, 0x4F, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x50, 0xBE, 0x00, 0xB5, 0x00, + 0x2D, 0x00, 0x0E, 0x00, 0x02, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x00, 0x43, 0x00, 0x61, 0x00, + 0x6E, 0x00, 0x63, 0x00, 0x65, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, +}; +static_assert(ARRAYSIZE(rcFontDialog) == 476, "wrong size for resource rcFontDialog") + +static const uint8_t rcColorDialog[] = { + 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC4, 0x00, 0xC8, 0x80, + 0x1C, 0x00, 0x0D, 0x00, 0x36, 0x00, 0x58, 0x01, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x00, + 0x6F, 0x00, 0x6C, 0x00, 0x6F, 0x00, 0x72, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x53, 0x00, 0x65, 0x00, 0x67, 0x00, 0x6F, 0x00, 0x65, 0x00, 0x20, 0x00, 0x55, 0x00, 0x49, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x02, 0x50, + 0x07, 0x00, 0x07, 0x00, 0xC3, 0x00, 0xC3, 0x00, 0x4C, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x02, 0x50, + 0xCE, 0x00, 0x07, 0x00, 0x0F, 0x00, 0xC3, 0x00, 0x4D, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x02, 0x50, + 0xE6, 0x00, 0x07, 0x00, 0x6B, 0x00, 0x09, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, + 0x50, 0x00, 0x72, 0x00, 0x65, 0x00, 0x76, 0x00, 0x69, 0x00, 0x65, 0x00, 0x77, 0x00, 0x3A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x02, 0x50, + 0xE6, 0x00, 0x10, 0x00, 0x6B, 0x00, 0x14, 0x00, 0x4E, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x02, 0x50, + 0xE6, 0x00, 0x2D, 0x00, 0x6B, 0x00, 0x09, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, + 0x4F, 0x00, 0x70, 0x00, 0x61, 0x00, 0x63, 0x00, 0x69, 0x00, 0x74, 0x00, 0x79, 0x00, 0x3A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x02, 0x50, + 0xE6, 0x00, 0x36, 0x00, 0x6B, 0x00, 0x0F, 0x00, 0x4F, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, + 0xE6, 0x00, 0x51, 0x00, 0x08, 0x00, 0x08, 0x00, 0x5C, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x26, 0x00, 0x48, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, 0xEE, 0x00, 0x4E, 0x00, 0x1E, 0x00, 0x0E, 0x00, + 0x50, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0xE6, 0x00, 0x5F, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x5D, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, 0x26, 0x00, 0x53, 0x00, 0x3A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, + 0xEE, 0x00, 0x5C, 0x00, 0x1E, 0x00, 0x0E, 0x00, 0x51, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, + 0xE6, 0x00, 0x6D, 0x00, 0x08, 0x00, 0x08, 0x00, 0x5E, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x26, 0x00, 0x56, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, 0xEE, 0x00, 0x6A, 0x00, 0x1E, 0x00, 0x0E, 0x00, + 0x52, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0x15, 0x01, 0x51, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x5F, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, 0x26, 0x00, 0x52, 0x00, 0x3A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, + 0x1D, 0x01, 0x4E, 0x00, 0x1E, 0x00, 0x0E, 0x00, 0x53, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x20, 0x81, 0x50, + 0x3B, 0x01, 0x4E, 0x00, 0x14, 0x00, 0x0E, 0x00, 0x54, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, + 0x15, 0x01, 0x5F, 0x00, 0x08, 0x00, 0x08, 0x00, 0x60, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x26, 0x00, 0x47, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, 0x1D, 0x01, 0x5C, 0x00, 0x1E, 0x00, 0x0E, 0x00, + 0x55, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x20, 0x81, 0x50, 0x3B, 0x01, 0x5C, 0x00, 0x14, 0x00, 0x0E, 0x00, + 0x56, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0x15, 0x01, 0x6D, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x61, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, 0x26, 0x00, 0x42, 0x00, 0x3A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, + 0x1D, 0x01, 0x6A, 0x00, 0x1E, 0x00, 0x0E, 0x00, 0x57, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x20, 0x81, 0x50, + 0x3B, 0x01, 0x6A, 0x00, 0x14, 0x00, 0x0E, 0x00, 0x58, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, + 0x15, 0x01, 0x7B, 0x00, 0x08, 0x00, 0x08, 0x00, 0x62, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x26, 0x00, 0x41, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, 0x1D, 0x01, 0x78, 0x00, 0x1E, 0x00, 0x0E, 0x00, + 0x59, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x20, 0x81, 0x50, 0x3B, 0x01, 0x78, 0x00, 0x14, 0x00, 0x0E, 0x00, + 0x5A, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0x0D, 0x01, 0x92, 0x00, 0x10, 0x00, 0x08, 0x00, + 0x63, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, 0x48, 0x00, 0x65, 0x00, 0x26, 0x00, 0x78, 0x00, + 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x80, 0x00, 0x81, 0x50, 0x1D, 0x01, 0x8F, 0x00, 0x32, 0x00, 0x0E, 0x00, 0x5B, 0x04, 0x00, 0x00, + 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x03, 0x50, 0xF3, 0x00, 0xBC, 0x00, 0x2D, 0x00, 0x0E, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x80, 0x00, 0x4F, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x50, 0x24, 0x01, 0xBC, 0x00, 0x2D, 0x00, 0x0E, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x00, 0x43, 0x00, 0x61, 0x00, 0x6E, 0x00, 0x63, 0x00, + 0x65, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, +}; +static_assert(ARRAYSIZE(rcColorDialog) == 1144, "wrong size for resource rcColorDialog") + diff --git a/windows/resources.rc b/windows/resources.rc index 989dfc91..4ec9c492 100644 --- a/windows/resources.rc +++ b/windows/resources.rc @@ -12,14 +12,6 @@ ISOLATIONAWARE_MANIFEST_RESOURCE_ID RT_MANIFEST "libui.manifest" #endif -// this is the dialog template used by tab pages; see windows/tabpage.c for details -rcTabPageDialog DIALOGEX 0, 0, 100, 100 -STYLE DS_CONTROL | WS_CHILD | WS_VISIBLE -EXSTYLE WS_EX_CONTROLPARENT -BEGIN - // nothing -END - // this is for our custom DirectWrite-based font dialog (see fontdialog.cpp) // this is based on the "New Font Dialog with Syslink" in Microsoft's font.dlg // LONGTERM look at localization diff --git a/windows/tabpage.cpp b/windows/tabpage.cpp index dc98fb88..c2584019 100644 --- a/windows/tabpage.cpp +++ b/windows/tabpage.cpp @@ -78,6 +78,24 @@ static INT_PTR CALLBACK dlgproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lPar return FALSE; } +// because Windows doesn't really support resources in static libraries, we have to embed this directly; oh well +/* +// this is the dialog template used by tab pages; see windows/tabpage.c for details +rcTabPageDialog DIALOGEX 0, 0, 100, 100 +STYLE DS_CONTROL | WS_CHILD | WS_VISIBLE +EXSTYLE WS_EX_CONTROLPARENT +BEGIN + // nothing +END +*/ +static const uint8_t data_rcTabPageDialog[] = { + 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x00, 0x50, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, + 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; +static_assert(ARRAYSIZE(data_rcTabPageDialog) == 32, "wrong size for resource rcTabPageDialog"); + struct tabPage *newTabPage(uiControl *child) { struct tabPage *tp; @@ -86,7 +104,7 @@ struct tabPage *newTabPage(uiControl *child) tp = uiprivNew(struct tabPage); // unfortunately this needs to be a proper dialog for EnableThemeDialogTexture() to work; CreateWindowExW() won't suffice - if (CreateDialogParamW(hInstance, MAKEINTRESOURCE(rcTabPageDialog), + if (CreateDialogIndirectParamW(hInstance, (const DLGTEMPLATE *) data_rcTabPageDialog, utilWindow, dlgproc, (LPARAM) tp) == NULL) logLastError(L"error creating tab page"); From b975cfb83e6d4d6ea0023e2fed928b726b623b06 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 2 May 2018 20:20:06 -0400 Subject: [PATCH 1015/1329] Fixes and TODOs in rc2bin. Going to manually adjust out instead of regenerating it, though. --- windows/_rc2bin/main.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/windows/_rc2bin/main.cpp b/windows/_rc2bin/main.cpp index 84068a93..0b90361e 100644 --- a/windows/_rc2bin/main.cpp +++ b/windows/_rc2bin/main.cpp @@ -4,6 +4,8 @@ #include #include "resources.hpp" +// TODO make sure there are no CRs in the output + void die(const char *f, const char *constname) { DWORD le; @@ -42,7 +44,7 @@ void dumpResource(const char *constname, const WCHAR *name, const WCHAR *type) printf("\t"); printf("0x%02I32X,", (uint32_t) (*bp)); bp++; - if (j == 15) { + if (j == 7) { printf("\n"); j = 0; } else { From a4cb17820c2527b9d412905be649569f9e36faad Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 2 May 2018 20:21:51 -0400 Subject: [PATCH 1016/1329] Forgot a fix (thanks to two people on Telegram) --- windows/_rc2bin/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/_rc2bin/main.cpp b/windows/_rc2bin/main.cpp index 0b90361e..2143fc80 100644 --- a/windows/_rc2bin/main.cpp +++ b/windows/_rc2bin/main.cpp @@ -55,7 +55,7 @@ void dumpResource(const char *constname, const WCHAR *name, const WCHAR *type) if (j != 0) printf("\n"); printf("};\n"); - printf("static_assert(ARRAYSIZE(%s) == %I32d, \"wrong size for resource %s\")\n", constname, n, constname); + printf("static_assert(ARRAYSIZE(%s) == %I32d, \"wrong size for resource %s\");\n", constname, n, constname); printf("\n"); } From 3522ec02b39feec6a720a96edd8fcff284bc9975 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 2 May 2018 20:24:30 -0400 Subject: [PATCH 1017/1329] Fixed out. --- windows/_rc2bin/out | 307 +++++++++++++++++++++++++++++--------------- 1 file changed, 204 insertions(+), 103 deletions(-) diff --git a/windows/_rc2bin/out b/windows/_rc2bin/out index 4dcfbaeb..0868bb66 100644 --- a/windows/_rc2bin/out +++ b/windows/_rc2bin/out @@ -1,112 +1,213 @@ // because Windows doesn't really support resources in static libraries, we have to embed this directly; oh well static const uint8_t rcFontDialog[] = { - 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC4, 0x00, 0xC8, 0x80, - 0x0A, 0x00, 0x0D, 0x00, 0x36, 0x00, 0xF3, 0x00, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, - 0x6F, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x01, 0x53, 0x00, - 0x65, 0x00, 0x67, 0x00, 0x6F, 0x00, 0x65, 0x00, 0x20, 0x00, 0x55, 0x00, 0x49, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0x07, 0x00, 0x07, 0x00, - 0x62, 0x00, 0x09, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, 0x26, 0x00, 0x46, 0x00, - 0x6F, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x0B, 0x21, 0x50, 0x07, 0x00, 0x10, 0x00, 0x62, 0x00, 0x4C, 0x00, - 0xE8, 0x03, 0x00, 0x00, 0xFF, 0xFF, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0x72, 0x00, 0x07, 0x00, 0x4A, 0x00, 0x09, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, 0x46, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x74, 0x00, - 0x20, 0x00, 0x73, 0x00, 0x74, 0x00, 0x26, 0x00, 0x79, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x3A, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x0A, 0x21, 0x50, - 0x72, 0x00, 0x10, 0x00, 0x4A, 0x00, 0x4C, 0x00, 0xE9, 0x03, 0x00, 0x00, 0xFF, 0xFF, 0x85, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, - 0xC6, 0x00, 0x07, 0x00, 0x24, 0x00, 0x09, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, - 0x26, 0x00, 0x53, 0x00, 0x69, 0x00, 0x7A, 0x00, 0x65, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x0B, 0x21, 0x50, 0xC6, 0x00, 0x10, 0x00, - 0x24, 0x00, 0x4C, 0x00, 0xEA, 0x03, 0x00, 0x00, 0xFF, 0xFF, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x02, 0x50, 0x07, 0x00, 0x61, 0x00, - 0xE3, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x53, 0x00, 0x61, 0x00, - 0x6D, 0x00, 0x70, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x02, 0x40, 0x09, 0x00, 0x6A, 0x00, 0xE0, 0x00, 0x3C, 0x00, - 0xEB, 0x03, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, - 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x50, 0x8D, 0x00, 0xB5, 0x00, 0x2D, 0x00, 0x0E, 0x00, - 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x00, 0x4F, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x50, 0xBE, 0x00, 0xB5, 0x00, - 0x2D, 0x00, 0x0E, 0x00, 0x02, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x00, 0x43, 0x00, 0x61, 0x00, - 0x6E, 0x00, 0x63, 0x00, 0x65, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xC4, 0x00, 0xC8, 0x80, + 0x0A, 0x00, 0x0D, 0x00, 0x36, 0x00, 0xF3, 0x00, + 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, + 0x6F, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x00, 0x01, 0x53, 0x00, + 0x65, 0x00, 0x67, 0x00, 0x6F, 0x00, 0x65, 0x00, + 0x20, 0x00, 0x55, 0x00, 0x49, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x50, 0x07, 0x00, 0x07, 0x00, + 0x62, 0x00, 0x09, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x82, 0x00, 0x26, 0x00, 0x46, 0x00, + 0x6F, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x3A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x0B, 0x21, 0x50, + 0x07, 0x00, 0x10, 0x00, 0x62, 0x00, 0x4C, 0x00, + 0xE8, 0x03, 0x00, 0x00, 0xFF, 0xFF, 0x85, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, + 0x72, 0x00, 0x07, 0x00, 0x4A, 0x00, 0x09, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, + 0x46, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x74, 0x00, + 0x20, 0x00, 0x73, 0x00, 0x74, 0x00, 0x26, 0x00, + 0x79, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x3A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x0A, 0x21, 0x50, + 0x72, 0x00, 0x10, 0x00, 0x4A, 0x00, 0x4C, 0x00, + 0xE9, 0x03, 0x00, 0x00, 0xFF, 0xFF, 0x85, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, + 0xC6, 0x00, 0x07, 0x00, 0x24, 0x00, 0x09, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, + 0x26, 0x00, 0x53, 0x00, 0x69, 0x00, 0x7A, 0x00, + 0x65, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x0B, 0x21, 0x50, 0xC6, 0x00, 0x10, 0x00, + 0x24, 0x00, 0x4C, 0x00, 0xEA, 0x03, 0x00, 0x00, + 0xFF, 0xFF, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x02, 0x50, 0x07, 0x00, 0x61, 0x00, + 0xE3, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x80, 0x00, 0x53, 0x00, 0x61, 0x00, + 0x6D, 0x00, 0x70, 0x00, 0x6C, 0x00, 0x65, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x02, 0x40, + 0x09, 0x00, 0x6A, 0x00, 0xE0, 0x00, 0x3C, 0x00, + 0xEB, 0x03, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, + 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x50, + 0x8D, 0x00, 0xB5, 0x00, 0x2D, 0x00, 0x0E, 0x00, + 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x00, + 0x4F, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x50, 0xBE, 0x00, 0xB5, 0x00, + 0x2D, 0x00, 0x0E, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x80, 0x00, 0x43, 0x00, 0x61, 0x00, + 0x6E, 0x00, 0x63, 0x00, 0x65, 0x00, 0x6C, 0x00, + 0x00, 0x00, 0x00, 0x00, }; -static_assert(ARRAYSIZE(rcFontDialog) == 476, "wrong size for resource rcFontDialog") +static_assert(ARRAYSIZE(rcFontDialog) == 476, "wrong size for resource rcFontDialog"); static const uint8_t rcColorDialog[] = { - 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC4, 0x00, 0xC8, 0x80, - 0x1C, 0x00, 0x0D, 0x00, 0x36, 0x00, 0x58, 0x01, 0xD1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x00, - 0x6F, 0x00, 0x6C, 0x00, 0x6F, 0x00, 0x72, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x53, 0x00, 0x65, 0x00, 0x67, 0x00, 0x6F, 0x00, 0x65, 0x00, 0x20, 0x00, 0x55, 0x00, 0x49, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x02, 0x50, - 0x07, 0x00, 0x07, 0x00, 0xC3, 0x00, 0xC3, 0x00, 0x4C, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, - 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x02, 0x50, - 0xCE, 0x00, 0x07, 0x00, 0x0F, 0x00, 0xC3, 0x00, 0x4D, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, - 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x02, 0x50, - 0xE6, 0x00, 0x07, 0x00, 0x6B, 0x00, 0x09, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, - 0x50, 0x00, 0x72, 0x00, 0x65, 0x00, 0x76, 0x00, 0x69, 0x00, 0x65, 0x00, 0x77, 0x00, 0x3A, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x02, 0x50, - 0xE6, 0x00, 0x10, 0x00, 0x6B, 0x00, 0x14, 0x00, 0x4E, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, - 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x02, 0x50, - 0xE6, 0x00, 0x2D, 0x00, 0x6B, 0x00, 0x09, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, - 0x4F, 0x00, 0x70, 0x00, 0x61, 0x00, 0x63, 0x00, 0x69, 0x00, 0x74, 0x00, 0x79, 0x00, 0x3A, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x02, 0x50, - 0xE6, 0x00, 0x36, 0x00, 0x6B, 0x00, 0x0F, 0x00, 0x4F, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, - 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, - 0xE6, 0x00, 0x51, 0x00, 0x08, 0x00, 0x08, 0x00, 0x5C, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, - 0x26, 0x00, 0x48, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, 0xEE, 0x00, 0x4E, 0x00, 0x1E, 0x00, 0x0E, 0x00, - 0x50, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0xE6, 0x00, 0x5F, 0x00, 0x08, 0x00, 0x08, 0x00, - 0x5D, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, 0x26, 0x00, 0x53, 0x00, 0x3A, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, - 0xEE, 0x00, 0x5C, 0x00, 0x1E, 0x00, 0x0E, 0x00, 0x51, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, - 0xE6, 0x00, 0x6D, 0x00, 0x08, 0x00, 0x08, 0x00, 0x5E, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, - 0x26, 0x00, 0x56, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, 0xEE, 0x00, 0x6A, 0x00, 0x1E, 0x00, 0x0E, 0x00, - 0x52, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0x15, 0x01, 0x51, 0x00, 0x08, 0x00, 0x08, 0x00, - 0x5F, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, 0x26, 0x00, 0x52, 0x00, 0x3A, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, - 0x1D, 0x01, 0x4E, 0x00, 0x1E, 0x00, 0x0E, 0x00, 0x53, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x20, 0x81, 0x50, - 0x3B, 0x01, 0x4E, 0x00, 0x14, 0x00, 0x0E, 0x00, 0x54, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, - 0x15, 0x01, 0x5F, 0x00, 0x08, 0x00, 0x08, 0x00, 0x60, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, - 0x26, 0x00, 0x47, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, 0x1D, 0x01, 0x5C, 0x00, 0x1E, 0x00, 0x0E, 0x00, - 0x55, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x80, 0x20, 0x81, 0x50, 0x3B, 0x01, 0x5C, 0x00, 0x14, 0x00, 0x0E, 0x00, - 0x56, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0x15, 0x01, 0x6D, 0x00, 0x08, 0x00, 0x08, 0x00, - 0x61, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, 0x26, 0x00, 0x42, 0x00, 0x3A, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, - 0x1D, 0x01, 0x6A, 0x00, 0x1E, 0x00, 0x0E, 0x00, 0x57, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x80, 0x20, 0x81, 0x50, - 0x3B, 0x01, 0x6A, 0x00, 0x14, 0x00, 0x0E, 0x00, 0x58, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, - 0x15, 0x01, 0x7B, 0x00, 0x08, 0x00, 0x08, 0x00, 0x62, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, - 0x26, 0x00, 0x41, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, 0x1D, 0x01, 0x78, 0x00, 0x1E, 0x00, 0x0E, 0x00, - 0x59, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x80, 0x20, 0x81, 0x50, 0x3B, 0x01, 0x78, 0x00, 0x14, 0x00, 0x0E, 0x00, - 0x5A, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, 0x0D, 0x01, 0x92, 0x00, 0x10, 0x00, 0x08, 0x00, - 0x63, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, 0x48, 0x00, 0x65, 0x00, 0x26, 0x00, 0x78, 0x00, - 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, - 0x80, 0x00, 0x81, 0x50, 0x1D, 0x01, 0x8F, 0x00, 0x32, 0x00, 0x0E, 0x00, 0x5B, 0x04, 0x00, 0x00, - 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x03, 0x50, 0xF3, 0x00, 0xBC, 0x00, 0x2D, 0x00, 0x0E, 0x00, 0x01, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0x80, 0x00, 0x4F, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x50, 0x24, 0x01, 0xBC, 0x00, 0x2D, 0x00, 0x0E, 0x00, - 0x02, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x00, 0x43, 0x00, 0x61, 0x00, 0x6E, 0x00, 0x63, 0x00, + 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xC4, 0x00, 0xC8, 0x80, + 0x1C, 0x00, 0x0D, 0x00, 0x36, 0x00, 0x58, 0x01, + 0xD1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x00, + 0x6F, 0x00, 0x6C, 0x00, 0x6F, 0x00, 0x72, 0x00, + 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x53, 0x00, 0x65, 0x00, 0x67, 0x00, 0x6F, 0x00, + 0x65, 0x00, 0x20, 0x00, 0x55, 0x00, 0x49, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x02, 0x50, + 0x07, 0x00, 0x07, 0x00, 0xC3, 0x00, 0xC3, 0x00, + 0x4C, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, + 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x02, 0x50, + 0xCE, 0x00, 0x07, 0x00, 0x0F, 0x00, 0xC3, 0x00, + 0x4D, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, + 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x02, 0x50, + 0xE6, 0x00, 0x07, 0x00, 0x6B, 0x00, 0x09, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, + 0x50, 0x00, 0x72, 0x00, 0x65, 0x00, 0x76, 0x00, + 0x69, 0x00, 0x65, 0x00, 0x77, 0x00, 0x3A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x02, 0x50, + 0xE6, 0x00, 0x10, 0x00, 0x6B, 0x00, 0x14, 0x00, + 0x4E, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, + 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x02, 0x50, + 0xE6, 0x00, 0x2D, 0x00, 0x6B, 0x00, 0x09, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, + 0x4F, 0x00, 0x70, 0x00, 0x61, 0x00, 0x63, 0x00, + 0x69, 0x00, 0x74, 0x00, 0x79, 0x00, 0x3A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x02, 0x50, + 0xE6, 0x00, 0x36, 0x00, 0x6B, 0x00, 0x0F, 0x00, + 0x4F, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, + 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, + 0xE6, 0x00, 0x51, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x5C, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x26, 0x00, 0x48, 0x00, 0x3A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, + 0xEE, 0x00, 0x4E, 0x00, 0x1E, 0x00, 0x0E, 0x00, + 0x50, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, + 0xE6, 0x00, 0x5F, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x5D, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x26, 0x00, 0x53, 0x00, 0x3A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, + 0xEE, 0x00, 0x5C, 0x00, 0x1E, 0x00, 0x0E, 0x00, + 0x51, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, + 0xE6, 0x00, 0x6D, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x5E, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x26, 0x00, 0x56, 0x00, 0x3A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, + 0xEE, 0x00, 0x6A, 0x00, 0x1E, 0x00, 0x0E, 0x00, + 0x52, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, + 0x15, 0x01, 0x51, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x5F, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x26, 0x00, 0x52, 0x00, 0x3A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, + 0x1D, 0x01, 0x4E, 0x00, 0x1E, 0x00, 0x0E, 0x00, + 0x53, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x20, 0x81, 0x50, + 0x3B, 0x01, 0x4E, 0x00, 0x14, 0x00, 0x0E, 0x00, + 0x54, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, + 0x15, 0x01, 0x5F, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x60, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x26, 0x00, 0x47, 0x00, 0x3A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, + 0x1D, 0x01, 0x5C, 0x00, 0x1E, 0x00, 0x0E, 0x00, + 0x55, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x20, 0x81, 0x50, + 0x3B, 0x01, 0x5C, 0x00, 0x14, 0x00, 0x0E, 0x00, + 0x56, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, + 0x15, 0x01, 0x6D, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x61, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x26, 0x00, 0x42, 0x00, 0x3A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, + 0x1D, 0x01, 0x6A, 0x00, 0x1E, 0x00, 0x0E, 0x00, + 0x57, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x20, 0x81, 0x50, + 0x3B, 0x01, 0x6A, 0x00, 0x14, 0x00, 0x0E, 0x00, + 0x58, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, + 0x15, 0x01, 0x7B, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x62, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x26, 0x00, 0x41, 0x00, 0x3A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, + 0x1D, 0x01, 0x78, 0x00, 0x1E, 0x00, 0x0E, 0x00, + 0x59, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x20, 0x81, 0x50, + 0x3B, 0x01, 0x78, 0x00, 0x14, 0x00, 0x0E, 0x00, + 0x5A, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, + 0x0D, 0x01, 0x92, 0x00, 0x10, 0x00, 0x08, 0x00, + 0x63, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x48, 0x00, 0x65, 0x00, 0x26, 0x00, 0x78, 0x00, + 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x80, 0x00, 0x81, 0x50, 0x1D, 0x01, 0x8F, 0x00, + 0x32, 0x00, 0x0E, 0x00, 0x5B, 0x04, 0x00, 0x00, + 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x03, 0x50, 0xF3, 0x00, 0xBC, 0x00, + 0x2D, 0x00, 0x0E, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x80, 0x00, 0x4F, 0x00, 0x4B, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x50, + 0x24, 0x01, 0xBC, 0x00, 0x2D, 0x00, 0x0E, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x00, + 0x43, 0x00, 0x61, 0x00, 0x6E, 0x00, 0x63, 0x00, 0x65, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, }; -static_assert(ARRAYSIZE(rcColorDialog) == 1144, "wrong size for resource rcColorDialog") +static_assert(ARRAYSIZE(rcColorDialog) == 1144, "wrong size for resource rcColorDialog"); From e3e88c7a6a8ce0477259a14d166c94d0765eea3b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 2 May 2018 20:57:50 -0400 Subject: [PATCH 1018/1329] And migrated the font dialog. --- windows/_rc2bin/out | 64 --------------------------- windows/fontdialog.cpp | 99 +++++++++++++++++++++++++++++++++++++++++- windows/resources.rc | 31 ------------- 3 files changed, 98 insertions(+), 96 deletions(-) diff --git a/windows/_rc2bin/out b/windows/_rc2bin/out index 0868bb66..824ca2fd 100644 --- a/windows/_rc2bin/out +++ b/windows/_rc2bin/out @@ -1,69 +1,5 @@ // because Windows doesn't really support resources in static libraries, we have to embed this directly; oh well -static const uint8_t rcFontDialog[] = { - 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xC4, 0x00, 0xC8, 0x80, - 0x0A, 0x00, 0x0D, 0x00, 0x36, 0x00, 0xF3, 0x00, - 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, - 0x6F, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x00, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x00, 0x01, 0x53, 0x00, - 0x65, 0x00, 0x67, 0x00, 0x6F, 0x00, 0x65, 0x00, - 0x20, 0x00, 0x55, 0x00, 0x49, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x02, 0x50, 0x07, 0x00, 0x07, 0x00, - 0x62, 0x00, 0x09, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0x82, 0x00, 0x26, 0x00, 0x46, 0x00, - 0x6F, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x3A, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x0B, 0x21, 0x50, - 0x07, 0x00, 0x10, 0x00, 0x62, 0x00, 0x4C, 0x00, - 0xE8, 0x03, 0x00, 0x00, 0xFF, 0xFF, 0x85, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, - 0x72, 0x00, 0x07, 0x00, 0x4A, 0x00, 0x09, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, - 0x46, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x74, 0x00, - 0x20, 0x00, 0x73, 0x00, 0x74, 0x00, 0x26, 0x00, - 0x79, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x3A, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x0A, 0x21, 0x50, - 0x72, 0x00, 0x10, 0x00, 0x4A, 0x00, 0x4C, 0x00, - 0xE9, 0x03, 0x00, 0x00, 0xFF, 0xFF, 0x85, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, - 0xC6, 0x00, 0x07, 0x00, 0x24, 0x00, 0x09, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, - 0x26, 0x00, 0x53, 0x00, 0x69, 0x00, 0x7A, 0x00, - 0x65, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x0B, 0x21, 0x50, 0xC6, 0x00, 0x10, 0x00, - 0x24, 0x00, 0x4C, 0x00, 0xEA, 0x03, 0x00, 0x00, - 0xFF, 0xFF, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x02, 0x50, 0x07, 0x00, 0x61, 0x00, - 0xE3, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0x80, 0x00, 0x53, 0x00, 0x61, 0x00, - 0x6D, 0x00, 0x70, 0x00, 0x6C, 0x00, 0x65, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x02, 0x40, - 0x09, 0x00, 0x6A, 0x00, 0xE0, 0x00, 0x3C, 0x00, - 0xEB, 0x03, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, - 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, - 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x50, - 0x8D, 0x00, 0xB5, 0x00, 0x2D, 0x00, 0x0E, 0x00, - 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x00, - 0x4F, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x03, 0x50, 0xBE, 0x00, 0xB5, 0x00, - 0x2D, 0x00, 0x0E, 0x00, 0x02, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0x80, 0x00, 0x43, 0x00, 0x61, 0x00, - 0x6E, 0x00, 0x63, 0x00, 0x65, 0x00, 0x6C, 0x00, - 0x00, 0x00, 0x00, 0x00, -}; -static_assert(ARRAYSIZE(rcFontDialog) == 476, "wrong size for resource rcFontDialog"); - static const uint8_t rcColorDialog[] = { 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC4, 0x00, 0xC8, 0x80, diff --git a/windows/fontdialog.cpp b/windows/fontdialog.cpp index ea02a416..fa9c1d0d 100644 --- a/windows/fontdialog.cpp +++ b/windows/fontdialog.cpp @@ -591,9 +591,106 @@ static INT_PTR CALLBACK fontDialogDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, L return FALSE; } +// because Windows doesn't really support resources in static libraries, we have to embed this directly; oh well +/* +// this is for our custom DirectWrite-based font dialog (see fontdialog.cpp) +// this is based on the "New Font Dialog with Syslink" in Microsoft's font.dlg +// LONGTERM look at localization +// LONGTERM make it look tighter and nicer like the real one, including the actual heights of the font family and style comboboxes +rcFontDialog DIALOGEX 13, 54, 243, 200 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU | DS_3DLOOK +CAPTION "Font" +FONT 9, "Segoe UI" +BEGIN + LTEXT "&Font:", -1, 7, 7, 98, 9 + COMBOBOX rcFontFamilyCombobox, 7, 16, 98, 76, + CBS_SIMPLE | CBS_AUTOHSCROLL | CBS_DISABLENOSCROLL | + CBS_SORT | WS_VSCROLL | WS_TABSTOP | CBS_HASSTRINGS + + LTEXT "Font st&yle:", -1, 114, 7, 74, 9 + COMBOBOX rcFontStyleCombobox, 114, 16, 74, 76, + CBS_SIMPLE | CBS_AUTOHSCROLL | CBS_DISABLENOSCROLL | + WS_VSCROLL | WS_TABSTOP | CBS_HASSTRINGS + + LTEXT "&Size:", -1, 198, 7, 36, 9 + COMBOBOX rcFontSizeCombobox, 198, 16, 36, 76, + CBS_SIMPLE | CBS_AUTOHSCROLL | CBS_DISABLENOSCROLL | + CBS_SORT | WS_VSCROLL | WS_TABSTOP | CBS_HASSTRINGS + + GROUPBOX "Sample", -1, 7, 97, 227, 70, WS_GROUP + CTEXT "AaBbYyZz", rcFontSamplePlacement, 9, 106, 224, 60, SS_NOPREFIX | NOT WS_VISIBLE + + DEFPUSHBUTTON "OK", IDOK, 141, 181, 45, 14, WS_GROUP + PUSHBUTTON "Cancel", IDCANCEL, 190, 181, 45, 14, WS_GROUP +END +*/ +static const uint8_t data_rcFontDialog[] = { + 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xC4, 0x00, 0xC8, 0x80, + 0x0A, 0x00, 0x0D, 0x00, 0x36, 0x00, 0xF3, 0x00, + 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, + 0x6F, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x00, 0x01, 0x53, 0x00, + 0x65, 0x00, 0x67, 0x00, 0x6F, 0x00, 0x65, 0x00, + 0x20, 0x00, 0x55, 0x00, 0x49, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x50, 0x07, 0x00, 0x07, 0x00, + 0x62, 0x00, 0x09, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x82, 0x00, 0x26, 0x00, 0x46, 0x00, + 0x6F, 0x00, 0x6E, 0x00, 0x74, 0x00, 0x3A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x0B, 0x21, 0x50, + 0x07, 0x00, 0x10, 0x00, 0x62, 0x00, 0x4C, 0x00, + 0xE8, 0x03, 0x00, 0x00, 0xFF, 0xFF, 0x85, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, + 0x72, 0x00, 0x07, 0x00, 0x4A, 0x00, 0x09, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, + 0x46, 0x00, 0x6F, 0x00, 0x6E, 0x00, 0x74, 0x00, + 0x20, 0x00, 0x73, 0x00, 0x74, 0x00, 0x26, 0x00, + 0x79, 0x00, 0x6C, 0x00, 0x65, 0x00, 0x3A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x0A, 0x21, 0x50, + 0x72, 0x00, 0x10, 0x00, 0x4A, 0x00, 0x4C, 0x00, + 0xE9, 0x03, 0x00, 0x00, 0xFF, 0xFF, 0x85, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, + 0xC6, 0x00, 0x07, 0x00, 0x24, 0x00, 0x09, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, + 0x26, 0x00, 0x53, 0x00, 0x69, 0x00, 0x7A, 0x00, + 0x65, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x0B, 0x21, 0x50, 0xC6, 0x00, 0x10, 0x00, + 0x24, 0x00, 0x4C, 0x00, 0xEA, 0x03, 0x00, 0x00, + 0xFF, 0xFF, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x02, 0x50, 0x07, 0x00, 0x61, 0x00, + 0xE3, 0x00, 0x46, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x80, 0x00, 0x53, 0x00, 0x61, 0x00, + 0x6D, 0x00, 0x70, 0x00, 0x6C, 0x00, 0x65, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x02, 0x40, + 0x09, 0x00, 0x6A, 0x00, 0xE0, 0x00, 0x3C, 0x00, + 0xEB, 0x03, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, + 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x50, + 0x8D, 0x00, 0xB5, 0x00, 0x2D, 0x00, 0x0E, 0x00, + 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x00, + 0x4F, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x50, 0xBE, 0x00, 0xB5, 0x00, + 0x2D, 0x00, 0x0E, 0x00, 0x02, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x80, 0x00, 0x43, 0x00, 0x61, 0x00, + 0x6E, 0x00, 0x63, 0x00, 0x65, 0x00, 0x6C, 0x00, + 0x00, 0x00, 0x00, 0x00, +}; +static_assert(ARRAYSIZE(data_rcFontDialog) == 476, "wrong size for resource rcFontDialog"); + BOOL uiprivShowFontDialog(HWND parent, struct fontDialogParams *params) { - switch (DialogBoxParamW(hInstance, MAKEINTRESOURCE(rcFontDialog), parent, fontDialogDlgProc, (LPARAM) params)) { + switch (DialogBoxIndirectParamW(hInstance, (const DLGTEMPLATE *) data_rcFontDialog, parent, fontDialogDlgProc, (LPARAM) params)) { case 1: // cancel return FALSE; case 2: // ok diff --git a/windows/resources.rc b/windows/resources.rc index 4ec9c492..1dfff92e 100644 --- a/windows/resources.rc +++ b/windows/resources.rc @@ -12,37 +12,6 @@ ISOLATIONAWARE_MANIFEST_RESOURCE_ID RT_MANIFEST "libui.manifest" #endif -// this is for our custom DirectWrite-based font dialog (see fontdialog.cpp) -// this is based on the "New Font Dialog with Syslink" in Microsoft's font.dlg -// LONGTERM look at localization -// LONGTERM make it look tighter and nicer like the real one, including the actual heights of the font family and style comboboxes -rcFontDialog DIALOGEX 13, 54, 243, 200 -STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU | DS_3DLOOK -CAPTION "Font" -FONT 9, "Segoe UI" -BEGIN - LTEXT "&Font:", -1, 7, 7, 98, 9 - COMBOBOX rcFontFamilyCombobox, 7, 16, 98, 76, - CBS_SIMPLE | CBS_AUTOHSCROLL | CBS_DISABLENOSCROLL | - CBS_SORT | WS_VSCROLL | WS_TABSTOP | CBS_HASSTRINGS - - LTEXT "Font st&yle:", -1, 114, 7, 74, 9 - COMBOBOX rcFontStyleCombobox, 114, 16, 74, 76, - CBS_SIMPLE | CBS_AUTOHSCROLL | CBS_DISABLENOSCROLL | - WS_VSCROLL | WS_TABSTOP | CBS_HASSTRINGS - - LTEXT "&Size:", -1, 198, 7, 36, 9 - COMBOBOX rcFontSizeCombobox, 198, 16, 36, 76, - CBS_SIMPLE | CBS_AUTOHSCROLL | CBS_DISABLENOSCROLL | - CBS_SORT | WS_VSCROLL | WS_TABSTOP | CBS_HASSTRINGS - - GROUPBOX "Sample", -1, 7, 97, 227, 70, WS_GROUP - CTEXT "AaBbYyZz", rcFontSamplePlacement, 9, 106, 224, 60, SS_NOPREFIX | NOT WS_VISIBLE - - DEFPUSHBUTTON "OK", IDOK, 141, 181, 45, 14, WS_GROUP - PUSHBUTTON "Cancel", IDCANCEL, 190, 181, 45, 14, WS_GROUP -END - rcColorDialog DIALOGEX 13, 54, 344, 209 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU | DS_3DLOOK CAPTION "Color" From 4432e39a44f50959607021ab5fb650b35b9cd93d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 2 May 2018 21:13:35 -0400 Subject: [PATCH 1019/1329] And the color dialog. All done! Now we just need to clean up and remove the kludge. --- windows/_rc2bin/out | 48 +++++++++- windows/colordialog.cpp | 195 +++++++++++++++++++++++++++++++++++++++- windows/resources.rc | 44 --------- 3 files changed, 240 insertions(+), 47 deletions(-) diff --git a/windows/_rc2bin/out b/windows/_rc2bin/out index 824ca2fd..192e3815 100644 --- a/windows/_rc2bin/out +++ b/windows/_rc2bin/out @@ -1,6 +1,50 @@ // because Windows doesn't really support resources in static libraries, we have to embed this directly; oh well +/* +rcColorDialog DIALOGEX 13, 54, 344, 209 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU | DS_3DLOOK +CAPTION "Color" +FONT 9, "Segoe UI" +BEGIN + // this size should be big enough to get at least 256x256 on font sizes >= 8 pt + CTEXT "AaBbYyZz", rcColorSVChooser, 7, 7, 195, 195, SS_NOPREFIX | SS_BLACKRECT -static const uint8_t rcColorDialog[] = { + // width is the suggested slider height since this is vertical + CTEXT "AaBbYyZz", rcColorHSlider, 206, 7, 15, 195, SS_NOPREFIX | SS_BLACKRECT + + LTEXT "Preview:", -1, 230, 7, 107, 9, SS_NOPREFIX + CTEXT "AaBbYyZz", rcPreview, 230, 16, 107, 20, SS_NOPREFIX | SS_BLACKRECT + + LTEXT "Opacity:", -1, 230, 45, 107, 9, SS_NOPREFIX + CTEXT "AaBbYyZz", rcOpacitySlider, 230, 54, 107, 15, SS_NOPREFIX | SS_BLACKRECT + + LTEXT "&H:", rcHLabel, 230, 81, 8, 8 + EDITTEXT rcH, 238, 78, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + LTEXT "&S:", rcSLabel, 230, 95, 8, 8 + EDITTEXT rcS, 238, 92, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + LTEXT "&V:", rcVLabel, 230, 109, 8, 8 + EDITTEXT rcV, 238, 106, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + + LTEXT "&R:", rcRLabel, 277, 81, 8, 8 + EDITTEXT rcRDouble, 285, 78, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + EDITTEXT rcRInt, 315, 78, 20, 14, ES_LEFT | ES_AUTOHSCROLL | ES_NUMBER | WS_TABSTOP, WS_EX_CLIENTEDGE + LTEXT "&G:", rcGLabel, 277, 95, 8, 8 + EDITTEXT rcGDouble, 285, 92, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + EDITTEXT rcGInt, 315, 92, 20, 14, ES_LEFT | ES_AUTOHSCROLL | ES_NUMBER | WS_TABSTOP, WS_EX_CLIENTEDGE + LTEXT "&B:", rcBLabel, 277, 109, 8, 8 + EDITTEXT rcBDouble, 285, 106, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + EDITTEXT rcBInt, 315, 106, 20, 14, ES_LEFT | ES_AUTOHSCROLL | ES_NUMBER | WS_TABSTOP, WS_EX_CLIENTEDGE + LTEXT "&A:", rcALabel, 277, 123, 8, 8 + EDITTEXT rcADouble, 285, 120, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + EDITTEXT rcAInt, 315, 120, 20, 14, ES_LEFT | ES_AUTOHSCROLL | ES_NUMBER | WS_TABSTOP, WS_EX_CLIENTEDGE + + LTEXT "He&x:", rcHexLabel, 269, 146, 16, 8 + EDITTEXT rcHex, 285, 143, 50, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + + DEFPUSHBUTTON "OK", IDOK, 243, 188, 45, 14, WS_GROUP + PUSHBUTTON "Cancel", IDCANCEL, 292, 188, 45, 14, WS_GROUP +END +*/ +static const uint8_t data_rcColorDialog[] = { 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC4, 0x00, 0xC8, 0x80, 0x1C, 0x00, 0x0D, 0x00, 0x36, 0x00, 0x58, 0x01, @@ -145,5 +189,5 @@ static const uint8_t rcColorDialog[] = { 0x43, 0x00, 0x61, 0x00, 0x6E, 0x00, 0x63, 0x00, 0x65, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, }; -static_assert(ARRAYSIZE(rcColorDialog) == 1144, "wrong size for resource rcColorDialog"); +static_assert(ARRAYSIZE(data_rcColorDialog) == 1144, "wrong size for resource rcColorDialog"); diff --git a/windows/colordialog.cpp b/windows/colordialog.cpp index d3d9bde9..d7030a4f 100644 --- a/windows/colordialog.cpp +++ b/windows/colordialog.cpp @@ -1249,9 +1249,202 @@ static INT_PTR CALLBACK colorDialogDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, return FALSE; } +// because Windows doesn't really support resources in static libraries, we have to embed this directly; oh well +/* +rcColorDialog DIALOGEX 13, 54, 344, 209 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU | DS_3DLOOK +CAPTION "Color" +FONT 9, "Segoe UI" +BEGIN + // this size should be big enough to get at least 256x256 on font sizes >= 8 pt + CTEXT "AaBbYyZz", rcColorSVChooser, 7, 7, 195, 195, SS_NOPREFIX | SS_BLACKRECT + + // width is the suggested slider height since this is vertical + CTEXT "AaBbYyZz", rcColorHSlider, 206, 7, 15, 195, SS_NOPREFIX | SS_BLACKRECT + + LTEXT "Preview:", -1, 230, 7, 107, 9, SS_NOPREFIX + CTEXT "AaBbYyZz", rcPreview, 230, 16, 107, 20, SS_NOPREFIX | SS_BLACKRECT + + LTEXT "Opacity:", -1, 230, 45, 107, 9, SS_NOPREFIX + CTEXT "AaBbYyZz", rcOpacitySlider, 230, 54, 107, 15, SS_NOPREFIX | SS_BLACKRECT + + LTEXT "&H:", rcHLabel, 230, 81, 8, 8 + EDITTEXT rcH, 238, 78, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + LTEXT "&S:", rcSLabel, 230, 95, 8, 8 + EDITTEXT rcS, 238, 92, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + LTEXT "&V:", rcVLabel, 230, 109, 8, 8 + EDITTEXT rcV, 238, 106, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + + LTEXT "&R:", rcRLabel, 277, 81, 8, 8 + EDITTEXT rcRDouble, 285, 78, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + EDITTEXT rcRInt, 315, 78, 20, 14, ES_LEFT | ES_AUTOHSCROLL | ES_NUMBER | WS_TABSTOP, WS_EX_CLIENTEDGE + LTEXT "&G:", rcGLabel, 277, 95, 8, 8 + EDITTEXT rcGDouble, 285, 92, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + EDITTEXT rcGInt, 315, 92, 20, 14, ES_LEFT | ES_AUTOHSCROLL | ES_NUMBER | WS_TABSTOP, WS_EX_CLIENTEDGE + LTEXT "&B:", rcBLabel, 277, 109, 8, 8 + EDITTEXT rcBDouble, 285, 106, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + EDITTEXT rcBInt, 315, 106, 20, 14, ES_LEFT | ES_AUTOHSCROLL | ES_NUMBER | WS_TABSTOP, WS_EX_CLIENTEDGE + LTEXT "&A:", rcALabel, 277, 123, 8, 8 + EDITTEXT rcADouble, 285, 120, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + EDITTEXT rcAInt, 315, 120, 20, 14, ES_LEFT | ES_AUTOHSCROLL | ES_NUMBER | WS_TABSTOP, WS_EX_CLIENTEDGE + + LTEXT "He&x:", rcHexLabel, 269, 146, 16, 8 + EDITTEXT rcHex, 285, 143, 50, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE + + DEFPUSHBUTTON "OK", IDOK, 243, 188, 45, 14, WS_GROUP + PUSHBUTTON "Cancel", IDCANCEL, 292, 188, 45, 14, WS_GROUP +END +*/ +static const uint8_t data_rcColorDialog[] = { + 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xC4, 0x00, 0xC8, 0x80, + 0x1C, 0x00, 0x0D, 0x00, 0x36, 0x00, 0x58, 0x01, + 0xD1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x00, + 0x6F, 0x00, 0x6C, 0x00, 0x6F, 0x00, 0x72, 0x00, + 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x53, 0x00, 0x65, 0x00, 0x67, 0x00, 0x6F, 0x00, + 0x65, 0x00, 0x20, 0x00, 0x55, 0x00, 0x49, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x02, 0x50, + 0x07, 0x00, 0x07, 0x00, 0xC3, 0x00, 0xC3, 0x00, + 0x4C, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, + 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x02, 0x50, + 0xCE, 0x00, 0x07, 0x00, 0x0F, 0x00, 0xC3, 0x00, + 0x4D, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, + 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x02, 0x50, + 0xE6, 0x00, 0x07, 0x00, 0x6B, 0x00, 0x09, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, + 0x50, 0x00, 0x72, 0x00, 0x65, 0x00, 0x76, 0x00, + 0x69, 0x00, 0x65, 0x00, 0x77, 0x00, 0x3A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x02, 0x50, + 0xE6, 0x00, 0x10, 0x00, 0x6B, 0x00, 0x14, 0x00, + 0x4E, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, + 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x02, 0x50, + 0xE6, 0x00, 0x2D, 0x00, 0x6B, 0x00, 0x09, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, + 0x4F, 0x00, 0x70, 0x00, 0x61, 0x00, 0x63, 0x00, + 0x69, 0x00, 0x74, 0x00, 0x79, 0x00, 0x3A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x02, 0x50, + 0xE6, 0x00, 0x36, 0x00, 0x6B, 0x00, 0x0F, 0x00, + 0x4F, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, + 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, + 0xE6, 0x00, 0x51, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x5C, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x26, 0x00, 0x48, 0x00, 0x3A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, + 0xEE, 0x00, 0x4E, 0x00, 0x1E, 0x00, 0x0E, 0x00, + 0x50, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, + 0xE6, 0x00, 0x5F, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x5D, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x26, 0x00, 0x53, 0x00, 0x3A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, + 0xEE, 0x00, 0x5C, 0x00, 0x1E, 0x00, 0x0E, 0x00, + 0x51, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, + 0xE6, 0x00, 0x6D, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x5E, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x26, 0x00, 0x56, 0x00, 0x3A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, + 0xEE, 0x00, 0x6A, 0x00, 0x1E, 0x00, 0x0E, 0x00, + 0x52, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, + 0x15, 0x01, 0x51, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x5F, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x26, 0x00, 0x52, 0x00, 0x3A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, + 0x1D, 0x01, 0x4E, 0x00, 0x1E, 0x00, 0x0E, 0x00, + 0x53, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x20, 0x81, 0x50, + 0x3B, 0x01, 0x4E, 0x00, 0x14, 0x00, 0x0E, 0x00, + 0x54, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, + 0x15, 0x01, 0x5F, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x60, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x26, 0x00, 0x47, 0x00, 0x3A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, + 0x1D, 0x01, 0x5C, 0x00, 0x1E, 0x00, 0x0E, 0x00, + 0x55, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x20, 0x81, 0x50, + 0x3B, 0x01, 0x5C, 0x00, 0x14, 0x00, 0x0E, 0x00, + 0x56, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, + 0x15, 0x01, 0x6D, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x61, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x26, 0x00, 0x42, 0x00, 0x3A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, + 0x1D, 0x01, 0x6A, 0x00, 0x1E, 0x00, 0x0E, 0x00, + 0x57, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x20, 0x81, 0x50, + 0x3B, 0x01, 0x6A, 0x00, 0x14, 0x00, 0x0E, 0x00, + 0x58, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, + 0x15, 0x01, 0x7B, 0x00, 0x08, 0x00, 0x08, 0x00, + 0x62, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x26, 0x00, 0x41, 0x00, 0x3A, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, + 0x1D, 0x01, 0x78, 0x00, 0x1E, 0x00, 0x0E, 0x00, + 0x59, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x80, 0x20, 0x81, 0x50, + 0x3B, 0x01, 0x78, 0x00, 0x14, 0x00, 0x0E, 0x00, + 0x5A, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, + 0x0D, 0x01, 0x92, 0x00, 0x10, 0x00, 0x08, 0x00, + 0x63, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, + 0x48, 0x00, 0x65, 0x00, 0x26, 0x00, 0x78, 0x00, + 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x80, 0x00, 0x81, 0x50, 0x1D, 0x01, 0x8F, 0x00, + 0x32, 0x00, 0x0E, 0x00, 0x5B, 0x04, 0x00, 0x00, + 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x03, 0x50, 0xF3, 0x00, 0xBC, 0x00, + 0x2D, 0x00, 0x0E, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0x80, 0x00, 0x4F, 0x00, 0x4B, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x50, + 0x24, 0x01, 0xBC, 0x00, 0x2D, 0x00, 0x0E, 0x00, + 0x02, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x00, + 0x43, 0x00, 0x61, 0x00, 0x6E, 0x00, 0x63, 0x00, + 0x65, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, +}; +static_assert(ARRAYSIZE(data_rcColorDialog) == 1144, "wrong size for resource rcColorDialog"); + BOOL showColorDialog(HWND parent, struct colorDialogRGBA *c) { - switch (DialogBoxParamW(hInstance, MAKEINTRESOURCE(rcColorDialog), parent, colorDialogDlgProc, (LPARAM) c)) { + switch (DialogBoxIndirectParamW(hInstance, (const DLGTEMPLATE *) data_rcColorDialog, parent, colorDialogDlgProc, (LPARAM) c)) { case 1: // cancel return FALSE; case 2: // ok diff --git a/windows/resources.rc b/windows/resources.rc index 1dfff92e..2ce7ee53 100644 --- a/windows/resources.rc +++ b/windows/resources.rc @@ -11,47 +11,3 @@ #ifndef _UI_STATIC ISOLATIONAWARE_MANIFEST_RESOURCE_ID RT_MANIFEST "libui.manifest" #endif - -rcColorDialog DIALOGEX 13, 54, 344, 209 -STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU | DS_3DLOOK -CAPTION "Color" -FONT 9, "Segoe UI" -BEGIN - // this size should be big enough to get at least 256x256 on font sizes >= 8 pt - CTEXT "AaBbYyZz", rcColorSVChooser, 7, 7, 195, 195, SS_NOPREFIX | SS_BLACKRECT - - // width is the suggested slider height since this is vertical - CTEXT "AaBbYyZz", rcColorHSlider, 206, 7, 15, 195, SS_NOPREFIX | SS_BLACKRECT - - LTEXT "Preview:", -1, 230, 7, 107, 9, SS_NOPREFIX - CTEXT "AaBbYyZz", rcPreview, 230, 16, 107, 20, SS_NOPREFIX | SS_BLACKRECT - - LTEXT "Opacity:", -1, 230, 45, 107, 9, SS_NOPREFIX - CTEXT "AaBbYyZz", rcOpacitySlider, 230, 54, 107, 15, SS_NOPREFIX | SS_BLACKRECT - - LTEXT "&H:", rcHLabel, 230, 81, 8, 8 - EDITTEXT rcH, 238, 78, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE - LTEXT "&S:", rcSLabel, 230, 95, 8, 8 - EDITTEXT rcS, 238, 92, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE - LTEXT "&V:", rcVLabel, 230, 109, 8, 8 - EDITTEXT rcV, 238, 106, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE - - LTEXT "&R:", rcRLabel, 277, 81, 8, 8 - EDITTEXT rcRDouble, 285, 78, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE - EDITTEXT rcRInt, 315, 78, 20, 14, ES_LEFT | ES_AUTOHSCROLL | ES_NUMBER | WS_TABSTOP, WS_EX_CLIENTEDGE - LTEXT "&G:", rcGLabel, 277, 95, 8, 8 - EDITTEXT rcGDouble, 285, 92, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE - EDITTEXT rcGInt, 315, 92, 20, 14, ES_LEFT | ES_AUTOHSCROLL | ES_NUMBER | WS_TABSTOP, WS_EX_CLIENTEDGE - LTEXT "&B:", rcBLabel, 277, 109, 8, 8 - EDITTEXT rcBDouble, 285, 106, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE - EDITTEXT rcBInt, 315, 106, 20, 14, ES_LEFT | ES_AUTOHSCROLL | ES_NUMBER | WS_TABSTOP, WS_EX_CLIENTEDGE - LTEXT "&A:", rcALabel, 277, 123, 8, 8 - EDITTEXT rcADouble, 285, 120, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE - EDITTEXT rcAInt, 315, 120, 20, 14, ES_LEFT | ES_AUTOHSCROLL | ES_NUMBER | WS_TABSTOP, WS_EX_CLIENTEDGE - - LTEXT "He&x:", rcHexLabel, 269, 146, 16, 8 - EDITTEXT rcHex, 285, 143, 50, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE - - DEFPUSHBUTTON "OK", IDOK, 243, 188, 45, 14, WS_GROUP - PUSHBUTTON "Cancel", IDCANCEL, 292, 188, 45, 14, WS_GROUP -END From 7ee7de1b92e9965f27494caabcc0c537b39896c9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 2 May 2018 23:04:43 -0400 Subject: [PATCH 1020/1329] And removed the rc file copying stuff from CMakeLists.txt. I wonder if this fixes the msbuild generators in cmake, so let's find out... --- CMakeLists.txt | 2 +- windows/CMakeLists.txt | 22 ++--- windows/_rc2bin/out | 193 ----------------------------------------- 3 files changed, 12 insertions(+), 205 deletions(-) delete mode 100644 windows/_rc2bin/out diff --git a/CMakeLists.txt b/CMakeLists.txt index fbcb3358..fe5f71df 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -210,7 +210,7 @@ macro(_add_exec _name) add_executable(${_name} EXCLUDE_FROM_ALL ${ARGN}) - target_link_libraries(${_name} libui ${_LIBUI_STATIC_RES}) + target_link_libraries(${_name} libui) _target_link_options_private(${_name} _COMMON_LDFLAGS) # make shared-linked executables PIC too diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index eab9ca90..4b208385 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -56,8 +56,17 @@ list(APPEND _LIBUI_SOURCES windows/window.cpp windows/winpublic.cpp windows/winutil.cpp - windows/resources.rc ) +# resources.rc only contains the libui manifest. +# For a DLL, we have to include this directly, so we do so. +# Windows won't link resources in static libraries, so including this would have no effect. +# In those cases, we just need them to include the manifest with the executable (or link it directly into the output executable themselves); they can also customize the manifest as they see fit (assuming nothing breaks in the process). +# TODO make sure this gets added to both binary-only archives and install rules in this case +if(BUILD_SHARED_LIBS) + list(APPEND _LIBUI_SOURCES + windows/resources.rc + ) +endif() set(_LIBUI_SOURCES ${_LIBUI_SOURCES} PARENT_SCOPE) list(APPEND _LIBUI_INCLUDEDIRS @@ -65,18 +74,9 @@ list(APPEND _LIBUI_INCLUDEDIRS ) set(_LIBUI_INCLUDEDIRS _LIBUI_INCLUDEDIRS PARENT_SCOPE) -# Windows won't link resources in static libraries; we need to provide the libui.res file in this case. set(_LIBUINAME libui PARENT_SCOPE) -if(NOT BUILD_SHARED_LIBS) - set(_LIBUI_STATIC_RES ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/libui.res PARENT_SCOPE) -endif() macro(_handle_static) - # TODO this full path feels hacky - add_custom_command( - TARGET libui POST_BUILD - COMMAND - ${CMAKE_COMMAND} -E copy $/CMakeFiles/libui.dir/windows/resources.rc.* ${_LIBUI_STATIC_RES} - COMMENT "Copying libui.res") + # do nothing endmacro() # TODO prune this list diff --git a/windows/_rc2bin/out b/windows/_rc2bin/out deleted file mode 100644 index 192e3815..00000000 --- a/windows/_rc2bin/out +++ /dev/null @@ -1,193 +0,0 @@ -// because Windows doesn't really support resources in static libraries, we have to embed this directly; oh well -/* -rcColorDialog DIALOGEX 13, 54, 344, 209 -STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU | DS_3DLOOK -CAPTION "Color" -FONT 9, "Segoe UI" -BEGIN - // this size should be big enough to get at least 256x256 on font sizes >= 8 pt - CTEXT "AaBbYyZz", rcColorSVChooser, 7, 7, 195, 195, SS_NOPREFIX | SS_BLACKRECT - - // width is the suggested slider height since this is vertical - CTEXT "AaBbYyZz", rcColorHSlider, 206, 7, 15, 195, SS_NOPREFIX | SS_BLACKRECT - - LTEXT "Preview:", -1, 230, 7, 107, 9, SS_NOPREFIX - CTEXT "AaBbYyZz", rcPreview, 230, 16, 107, 20, SS_NOPREFIX | SS_BLACKRECT - - LTEXT "Opacity:", -1, 230, 45, 107, 9, SS_NOPREFIX - CTEXT "AaBbYyZz", rcOpacitySlider, 230, 54, 107, 15, SS_NOPREFIX | SS_BLACKRECT - - LTEXT "&H:", rcHLabel, 230, 81, 8, 8 - EDITTEXT rcH, 238, 78, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE - LTEXT "&S:", rcSLabel, 230, 95, 8, 8 - EDITTEXT rcS, 238, 92, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE - LTEXT "&V:", rcVLabel, 230, 109, 8, 8 - EDITTEXT rcV, 238, 106, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE - - LTEXT "&R:", rcRLabel, 277, 81, 8, 8 - EDITTEXT rcRDouble, 285, 78, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE - EDITTEXT rcRInt, 315, 78, 20, 14, ES_LEFT | ES_AUTOHSCROLL | ES_NUMBER | WS_TABSTOP, WS_EX_CLIENTEDGE - LTEXT "&G:", rcGLabel, 277, 95, 8, 8 - EDITTEXT rcGDouble, 285, 92, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE - EDITTEXT rcGInt, 315, 92, 20, 14, ES_LEFT | ES_AUTOHSCROLL | ES_NUMBER | WS_TABSTOP, WS_EX_CLIENTEDGE - LTEXT "&B:", rcBLabel, 277, 109, 8, 8 - EDITTEXT rcBDouble, 285, 106, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE - EDITTEXT rcBInt, 315, 106, 20, 14, ES_LEFT | ES_AUTOHSCROLL | ES_NUMBER | WS_TABSTOP, WS_EX_CLIENTEDGE - LTEXT "&A:", rcALabel, 277, 123, 8, 8 - EDITTEXT rcADouble, 285, 120, 30, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE - EDITTEXT rcAInt, 315, 120, 20, 14, ES_LEFT | ES_AUTOHSCROLL | ES_NUMBER | WS_TABSTOP, WS_EX_CLIENTEDGE - - LTEXT "He&x:", rcHexLabel, 269, 146, 16, 8 - EDITTEXT rcHex, 285, 143, 50, 14, ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP, WS_EX_CLIENTEDGE - - DEFPUSHBUTTON "OK", IDOK, 243, 188, 45, 14, WS_GROUP - PUSHBUTTON "Cancel", IDCANCEL, 292, 188, 45, 14, WS_GROUP -END -*/ -static const uint8_t data_rcColorDialog[] = { - 0x01, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xC4, 0x00, 0xC8, 0x80, - 0x1C, 0x00, 0x0D, 0x00, 0x36, 0x00, 0x58, 0x01, - 0xD1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x00, - 0x6F, 0x00, 0x6C, 0x00, 0x6F, 0x00, 0x72, 0x00, - 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x53, 0x00, 0x65, 0x00, 0x67, 0x00, 0x6F, 0x00, - 0x65, 0x00, 0x20, 0x00, 0x55, 0x00, 0x49, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x02, 0x50, - 0x07, 0x00, 0x07, 0x00, 0xC3, 0x00, 0xC3, 0x00, - 0x4C, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, - 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, - 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x02, 0x50, - 0xCE, 0x00, 0x07, 0x00, 0x0F, 0x00, 0xC3, 0x00, - 0x4D, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, - 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, - 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x02, 0x50, - 0xE6, 0x00, 0x07, 0x00, 0x6B, 0x00, 0x09, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, - 0x50, 0x00, 0x72, 0x00, 0x65, 0x00, 0x76, 0x00, - 0x69, 0x00, 0x65, 0x00, 0x77, 0x00, 0x3A, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x02, 0x50, - 0xE6, 0x00, 0x10, 0x00, 0x6B, 0x00, 0x14, 0x00, - 0x4E, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, - 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, - 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x02, 0x50, - 0xE6, 0x00, 0x2D, 0x00, 0x6B, 0x00, 0x09, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x82, 0x00, - 0x4F, 0x00, 0x70, 0x00, 0x61, 0x00, 0x63, 0x00, - 0x69, 0x00, 0x74, 0x00, 0x79, 0x00, 0x3A, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x02, 0x50, - 0xE6, 0x00, 0x36, 0x00, 0x6B, 0x00, 0x0F, 0x00, - 0x4F, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, - 0x41, 0x00, 0x61, 0x00, 0x42, 0x00, 0x62, 0x00, - 0x59, 0x00, 0x79, 0x00, 0x5A, 0x00, 0x7A, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, - 0xE6, 0x00, 0x51, 0x00, 0x08, 0x00, 0x08, 0x00, - 0x5C, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, - 0x26, 0x00, 0x48, 0x00, 0x3A, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, - 0xEE, 0x00, 0x4E, 0x00, 0x1E, 0x00, 0x0E, 0x00, - 0x50, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, - 0xE6, 0x00, 0x5F, 0x00, 0x08, 0x00, 0x08, 0x00, - 0x5D, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, - 0x26, 0x00, 0x53, 0x00, 0x3A, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, - 0xEE, 0x00, 0x5C, 0x00, 0x1E, 0x00, 0x0E, 0x00, - 0x51, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, - 0xE6, 0x00, 0x6D, 0x00, 0x08, 0x00, 0x08, 0x00, - 0x5E, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, - 0x26, 0x00, 0x56, 0x00, 0x3A, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, - 0xEE, 0x00, 0x6A, 0x00, 0x1E, 0x00, 0x0E, 0x00, - 0x52, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, - 0x15, 0x01, 0x51, 0x00, 0x08, 0x00, 0x08, 0x00, - 0x5F, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, - 0x26, 0x00, 0x52, 0x00, 0x3A, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, - 0x1D, 0x01, 0x4E, 0x00, 0x1E, 0x00, 0x0E, 0x00, - 0x53, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x80, 0x20, 0x81, 0x50, - 0x3B, 0x01, 0x4E, 0x00, 0x14, 0x00, 0x0E, 0x00, - 0x54, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, - 0x15, 0x01, 0x5F, 0x00, 0x08, 0x00, 0x08, 0x00, - 0x60, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, - 0x26, 0x00, 0x47, 0x00, 0x3A, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, - 0x1D, 0x01, 0x5C, 0x00, 0x1E, 0x00, 0x0E, 0x00, - 0x55, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x80, 0x20, 0x81, 0x50, - 0x3B, 0x01, 0x5C, 0x00, 0x14, 0x00, 0x0E, 0x00, - 0x56, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, - 0x15, 0x01, 0x6D, 0x00, 0x08, 0x00, 0x08, 0x00, - 0x61, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, - 0x26, 0x00, 0x42, 0x00, 0x3A, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, - 0x1D, 0x01, 0x6A, 0x00, 0x1E, 0x00, 0x0E, 0x00, - 0x57, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x80, 0x20, 0x81, 0x50, - 0x3B, 0x01, 0x6A, 0x00, 0x14, 0x00, 0x0E, 0x00, - 0x58, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, - 0x15, 0x01, 0x7B, 0x00, 0x08, 0x00, 0x08, 0x00, - 0x62, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, - 0x26, 0x00, 0x41, 0x00, 0x3A, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x80, 0x00, 0x81, 0x50, - 0x1D, 0x01, 0x78, 0x00, 0x1E, 0x00, 0x0E, 0x00, - 0x59, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x80, 0x20, 0x81, 0x50, - 0x3B, 0x01, 0x78, 0x00, 0x14, 0x00, 0x0E, 0x00, - 0x5A, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x81, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x50, - 0x0D, 0x01, 0x92, 0x00, 0x10, 0x00, 0x08, 0x00, - 0x63, 0x04, 0x00, 0x00, 0xFF, 0xFF, 0x82, 0x00, - 0x48, 0x00, 0x65, 0x00, 0x26, 0x00, 0x78, 0x00, - 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, - 0x80, 0x00, 0x81, 0x50, 0x1D, 0x01, 0x8F, 0x00, - 0x32, 0x00, 0x0E, 0x00, 0x5B, 0x04, 0x00, 0x00, - 0xFF, 0xFF, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x03, 0x50, 0xF3, 0x00, 0xBC, 0x00, - 0x2D, 0x00, 0x0E, 0x00, 0x01, 0x00, 0x00, 0x00, - 0xFF, 0xFF, 0x80, 0x00, 0x4F, 0x00, 0x4B, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x50, - 0x24, 0x01, 0xBC, 0x00, 0x2D, 0x00, 0x0E, 0x00, - 0x02, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x00, - 0x43, 0x00, 0x61, 0x00, 0x6E, 0x00, 0x63, 0x00, - 0x65, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, -}; -static_assert(ARRAYSIZE(data_rcColorDialog) == 1144, "wrong size for resource rcColorDialog"); - From 134acf6ec0878ea587becd7ad2e544ba2f5d1183 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 2 May 2018 23:38:08 -0400 Subject: [PATCH 1021/1329] And now that I tested everything to see that it worked, including msbuild and MinGW-w64 builds, updated the README.md. Time to merge back with master! --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index d8e07745..e7b90918 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,9 @@ This README is being written.
## Announcements +* **2 May 2018** + * On Windows, you no longer need to carry around a `libui.res` file with static builds. You do need to link in the appropriate manifest file, such as the one in the `windows/` folder (I still need to figure out exactly what is needed apart from the Common Controls v6 dependency, or at least to create a complete-ish template), or at least include it alongside your executables. This also means you should no longer see random cmake errors when building the static libraries. + * **18 April 2018** * Introduced a new `uiTimer()` function for running code on a timer on the main thread. (Thanks to @cody271.) From 6a4a3e1b4df8bde7ceafdfa17c00af2274d46aed Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 3 May 2018 01:55:35 -0400 Subject: [PATCH 1022/1329] More TODOs. --- darwin/winmoveresize.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/darwin/winmoveresize.m b/darwin/winmoveresize.m index 2753b93b..d43f3209 100644 --- a/darwin/winmoveresize.m +++ b/darwin/winmoveresize.m @@ -1,6 +1,8 @@ // 1 november 2016 #import "uipriv_darwin.h" +// TODO option while resizing resizes both opposing sides at once (thanks swillits in irc.freenode.net/#macdev for showing this to me); figure out how far back that behavior goes when we do implement it + // because we are changing the window frame each time the mouse moves, the successive -[NSEvent locationInWindow]s cannot be meaningfully used together // make sure they are all following some sort of standard to avoid this problem; the screen is the most obvious possibility since it requires only one conversion (the only one that a NSWindow provides) static NSPoint makeIndependent(NSPoint p, NSWindow *w) From 4fe74fbd569c42b0454f831080e39fc7a0c84920 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 3 May 2018 22:28:02 -0400 Subject: [PATCH 1023/1329] Started OS X name migration by moving uipriv_darwin.h out of the way. --- darwin/OLD_uipriv_darwin.h | 143 ++++++++++++++++++++++++++++++++++++ darwin/uipriv_darwin.h | 145 +------------------------------------ 2 files changed, 145 insertions(+), 143 deletions(-) create mode 100644 darwin/OLD_uipriv_darwin.h diff --git a/darwin/OLD_uipriv_darwin.h b/darwin/OLD_uipriv_darwin.h new file mode 100644 index 00000000..c45e450f --- /dev/null +++ b/darwin/OLD_uipriv_darwin.h @@ -0,0 +1,143 @@ + +#define toNSString(str) [NSString stringWithUTF8String:(str)] +#define fromNSString(str) [(str) UTF8String] + +#ifndef NSAppKitVersionNumber10_9 +#define NSAppKitVersionNumber10_9 1265 +#endif + +/*TODO remove this*/typedef struct uiImage uiImage; + +// menu.m +@interface menuManager : NSObject { + struct mapTable *items; + BOOL hasQuit; + BOOL hasPreferences; + BOOL hasAbout; +} +@property (strong) NSMenuItem *quitItem; +@property (strong) NSMenuItem *preferencesItem; +@property (strong) NSMenuItem *aboutItem; +// NSMenuValidation is only informal +- (BOOL)validateMenuItem:(NSMenuItem *)item; +- (NSMenu *)makeMenubar; +@end +extern void finalizeMenus(void); +extern void uninitMenus(void); + +// main.m +@interface applicationClass : NSApplication +@end +// this is needed because NSApp is of type id, confusing clang +#define realNSApp() ((applicationClass *) NSApp) +@interface appDelegate : NSObject +@property (strong) menuManager *menuManager; +@end +#define appDelegate() ((appDelegate *) [realNSApp() delegate]) +struct nextEventArgs { + NSEventMask mask; + NSDate *duration; + // LONGTERM no NSRunLoopMode? + NSString *mode; + BOOL dequeue; +}; +extern int mainStep(struct nextEventArgs *nea, BOOL (^interceptEvent)(NSEvent *)); + +// util.m +extern void disableAutocorrect(NSTextView *); + +// entry.m +extern void finishNewTextField(NSTextField *, BOOL); +extern NSTextField *newEditableTextField(void); + +// window.m +@interface libuiNSWindow : NSWindow +- (void)libui_doMove:(NSEvent *)initialEvent; +- (void)libui_doResize:(NSEvent *)initialEvent on:(uiWindowResizeEdge)edge; +@end +extern uiWindow *windowFromNSWindow(NSWindow *); + +// alloc.m +extern NSMutableArray *delegates; +extern void initAlloc(void); +extern void uninitAlloc(void); + +// autolayout.m +extern NSLayoutConstraint *mkConstraint(id view1, NSLayoutAttribute attr1, NSLayoutRelation relation, id view2, NSLayoutAttribute attr2, CGFloat multiplier, CGFloat c, NSString *desc); +extern void jiggleViewLayout(NSView *view); +struct singleChildConstraints { + NSLayoutConstraint *leadingConstraint; + NSLayoutConstraint *topConstraint; + NSLayoutConstraint *trailingConstraintGreater; + NSLayoutConstraint *trailingConstraintEqual; + NSLayoutConstraint *bottomConstraintGreater; + NSLayoutConstraint *bottomConstraintEqual; +}; +extern void singleChildConstraintsEstablish(struct singleChildConstraints *c, NSView *contentView, NSView *childView, BOOL hugsTrailing, BOOL hugsBottom, int margined, NSString *desc); +extern void singleChildConstraintsRemove(struct singleChildConstraints *c, NSView *cv); +extern void singleChildConstraintsSetMargined(struct singleChildConstraints *c, int margined); + +// map.m +extern struct mapTable *newMap(void); +extern void mapDestroy(struct mapTable *m); +extern void *mapGet(struct mapTable *m, void *key); +extern void mapSet(struct mapTable *m, void *key, void *value); +extern void mapDelete(struct mapTable *m, void *key); +extern void mapWalk(struct mapTable *m, void (*f)(void *key, void *value)); +extern void mapReset(struct mapTable *m); + +// area.m +extern int sendAreaEvents(NSEvent *); + +// areaevents.m +extern BOOL fromKeycode(unsigned short keycode, uiAreaKeyEvent *ke); +extern BOOL keycodeModifier(unsigned short keycode, uiModifiers *mod); + +// draw.m +extern uiDrawContext *newContext(CGContextRef, CGFloat); +extern void freeContext(uiDrawContext *); + +// fontbutton.m +extern BOOL uiprivFontButtonInhibitSendAction(SEL sel, id from, id to); +extern BOOL uiprivFontButtonOverrideTargetForAction(SEL sel, id from, id to, id *override); +extern void uiprivSetupFontPanel(void); + +// colorbutton.m +extern BOOL colorButtonInhibitSendAction(SEL sel, id from, id to); + +// scrollview.m +struct scrollViewCreateParams { + NSView *DocumentView; + NSColor *BackgroundColor; + BOOL DrawsBackground; + BOOL Bordered; + BOOL HScroll; + BOOL VScroll; +}; +struct scrollViewData; +extern NSScrollView *mkScrollView(struct scrollViewCreateParams *p, struct scrollViewData **dout); +extern void scrollViewSetScrolling(NSScrollView *sv, struct scrollViewData *d, BOOL hscroll, BOOL vscroll); +extern void scrollViewFreeData(NSScrollView *sv, struct scrollViewData *d); + +// label.m +extern NSTextField *newLabel(NSString *str); + +// image.m +extern NSImage *imageImage(uiImage *); + +// winmoveresize.m +extern void doManualMove(NSWindow *w, NSEvent *initialEvent); +extern void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdge edge); + +// future.m +extern CFStringRef *FUTURE_kCTFontOpenTypeFeatureTag; +extern CFStringRef *FUTURE_kCTFontOpenTypeFeatureValue; +extern CFStringRef *FUTURE_kCTBackgroundColorAttributeName; +extern void loadFutures(void); +extern void FUTURE_NSLayoutConstraint_setIdentifier(NSLayoutConstraint *constraint, NSString *identifier); +extern BOOL FUTURE_NSWindow_performWindowDragWithEvent(NSWindow *w, NSEvent *initialEvent); + +// undocumented.m +extern CFStringRef UNDOC_kCTFontPreferredSubFamilyNameKey; +extern CFStringRef UNDOC_kCTFontPreferredFamilyNameKey; +extern void loadUndocumented(void); diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 8b95e315..6b8c1179 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -3,7 +3,7 @@ #define MAC_OS_X_VERSION_MIN_REQUIRED MAC_OS_X_VERSION_10_8 #define MAC_OS_X_VERSION_MAX_ALLOWED MAC_OS_X_VERSION_10_8 #import -#include // see future.m +#import // see future.m #import "../ui.h" #import "../ui_darwin.h" #import "../common/uipriv.h" @@ -12,145 +12,4 @@ #error Sorry, libui cannot be compiled with ARC. #endif -#define toNSString(str) [NSString stringWithUTF8String:(str)] -#define fromNSString(str) [(str) UTF8String] - -#ifndef NSAppKitVersionNumber10_9 -#define NSAppKitVersionNumber10_9 1265 -#endif - -/*TODO remove this*/typedef struct uiImage uiImage; - -// menu.m -@interface menuManager : NSObject { - struct mapTable *items; - BOOL hasQuit; - BOOL hasPreferences; - BOOL hasAbout; -} -@property (strong) NSMenuItem *quitItem; -@property (strong) NSMenuItem *preferencesItem; -@property (strong) NSMenuItem *aboutItem; -// NSMenuValidation is only informal -- (BOOL)validateMenuItem:(NSMenuItem *)item; -- (NSMenu *)makeMenubar; -@end -extern void finalizeMenus(void); -extern void uninitMenus(void); - -// main.m -@interface applicationClass : NSApplication -@end -// this is needed because NSApp is of type id, confusing clang -#define realNSApp() ((applicationClass *) NSApp) -@interface appDelegate : NSObject -@property (strong) menuManager *menuManager; -@end -#define appDelegate() ((appDelegate *) [realNSApp() delegate]) -struct nextEventArgs { - NSEventMask mask; - NSDate *duration; - // LONGTERM no NSRunLoopMode? - NSString *mode; - BOOL dequeue; -}; -extern int mainStep(struct nextEventArgs *nea, BOOL (^interceptEvent)(NSEvent *)); - -// util.m -extern void disableAutocorrect(NSTextView *); - -// entry.m -extern void finishNewTextField(NSTextField *, BOOL); -extern NSTextField *newEditableTextField(void); - -// window.m -@interface libuiNSWindow : NSWindow -- (void)libui_doMove:(NSEvent *)initialEvent; -- (void)libui_doResize:(NSEvent *)initialEvent on:(uiWindowResizeEdge)edge; -@end -extern uiWindow *windowFromNSWindow(NSWindow *); - -// alloc.m -extern NSMutableArray *delegates; -extern void initAlloc(void); -extern void uninitAlloc(void); - -// autolayout.m -extern NSLayoutConstraint *mkConstraint(id view1, NSLayoutAttribute attr1, NSLayoutRelation relation, id view2, NSLayoutAttribute attr2, CGFloat multiplier, CGFloat c, NSString *desc); -extern void jiggleViewLayout(NSView *view); -struct singleChildConstraints { - NSLayoutConstraint *leadingConstraint; - NSLayoutConstraint *topConstraint; - NSLayoutConstraint *trailingConstraintGreater; - NSLayoutConstraint *trailingConstraintEqual; - NSLayoutConstraint *bottomConstraintGreater; - NSLayoutConstraint *bottomConstraintEqual; -}; -extern void singleChildConstraintsEstablish(struct singleChildConstraints *c, NSView *contentView, NSView *childView, BOOL hugsTrailing, BOOL hugsBottom, int margined, NSString *desc); -extern void singleChildConstraintsRemove(struct singleChildConstraints *c, NSView *cv); -extern void singleChildConstraintsSetMargined(struct singleChildConstraints *c, int margined); - -// map.m -extern struct mapTable *newMap(void); -extern void mapDestroy(struct mapTable *m); -extern void *mapGet(struct mapTable *m, void *key); -extern void mapSet(struct mapTable *m, void *key, void *value); -extern void mapDelete(struct mapTable *m, void *key); -extern void mapWalk(struct mapTable *m, void (*f)(void *key, void *value)); -extern void mapReset(struct mapTable *m); - -// area.m -extern int sendAreaEvents(NSEvent *); - -// areaevents.m -extern BOOL fromKeycode(unsigned short keycode, uiAreaKeyEvent *ke); -extern BOOL keycodeModifier(unsigned short keycode, uiModifiers *mod); - -// draw.m -extern uiDrawContext *newContext(CGContextRef, CGFloat); -extern void freeContext(uiDrawContext *); - -// fontbutton.m -extern BOOL uiprivFontButtonInhibitSendAction(SEL sel, id from, id to); -extern BOOL uiprivFontButtonOverrideTargetForAction(SEL sel, id from, id to, id *override); -extern void uiprivSetupFontPanel(void); - -// colorbutton.m -extern BOOL colorButtonInhibitSendAction(SEL sel, id from, id to); - -// scrollview.m -struct scrollViewCreateParams { - NSView *DocumentView; - NSColor *BackgroundColor; - BOOL DrawsBackground; - BOOL Bordered; - BOOL HScroll; - BOOL VScroll; -}; -struct scrollViewData; -extern NSScrollView *mkScrollView(struct scrollViewCreateParams *p, struct scrollViewData **dout); -extern void scrollViewSetScrolling(NSScrollView *sv, struct scrollViewData *d, BOOL hscroll, BOOL vscroll); -extern void scrollViewFreeData(NSScrollView *sv, struct scrollViewData *d); - -// label.m -extern NSTextField *newLabel(NSString *str); - -// image.m -extern NSImage *imageImage(uiImage *); - -// winmoveresize.m -extern void doManualMove(NSWindow *w, NSEvent *initialEvent); -extern void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdge edge); - -// future.m -extern CFStringRef *FUTURE_kCTFontOpenTypeFeatureTag; -extern CFStringRef *FUTURE_kCTFontOpenTypeFeatureValue; -extern CFStringRef *FUTURE_kCTBackgroundColorAttributeName; -extern void loadFutures(void); -extern void FUTURE_NSLayoutConstraint_setIdentifier(NSLayoutConstraint *constraint, NSString *identifier); -extern BOOL FUTURE_NSWindow_performWindowDragWithEvent(NSWindow *w, NSEvent *initialEvent); - -// undocumented.m -extern CFStringRef UNDOC_kCTFontPreferredSubFamilyNameKey; -extern CFStringRef UNDOC_kCTFontPreferredFamilyNameKey; -extern void loadUndocumented(void); +#import "OLD_uipriv_darwin.h" From 5a113e1e0b04e7e2d3caffe0cc60827c307c7347 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 3 May 2018 22:38:21 -0400 Subject: [PATCH 1024/1329] Renamed toNSString() and fromNSString() to uiprivToNSString() and uiprivFromNSString(), respectively. --- darwin/OLD_uipriv_darwin.h | 9 --------- darwin/button.m | 4 ++-- darwin/checkbox.m | 4 ++-- darwin/combobox.m | 2 +- darwin/editablecombo.m | 4 ++-- darwin/entry.m | 2 +- darwin/form.m | 2 +- darwin/group.m | 4 ++-- darwin/label.m | 4 ++-- darwin/menu.m | 6 +++--- darwin/multilineentry.m | 4 ++-- darwin/radiobuttons.m | 2 +- darwin/stddialogs.m | 4 ++-- darwin/tab.m | 2 +- darwin/uipriv_darwin.h | 10 ++++++++++ darwin/window.m | 4 ++-- 16 files changed, 34 insertions(+), 33 deletions(-) diff --git a/darwin/OLD_uipriv_darwin.h b/darwin/OLD_uipriv_darwin.h index c45e450f..3cc41922 100644 --- a/darwin/OLD_uipriv_darwin.h +++ b/darwin/OLD_uipriv_darwin.h @@ -1,13 +1,4 @@ -#define toNSString(str) [NSString stringWithUTF8String:(str)] -#define fromNSString(str) [(str) UTF8String] - -#ifndef NSAppKitVersionNumber10_9 -#define NSAppKitVersionNumber10_9 1265 -#endif - -/*TODO remove this*/typedef struct uiImage uiImage; - // menu.m @interface menuManager : NSObject { struct mapTable *items; diff --git a/darwin/button.m b/darwin/button.m index baccabbb..8e3fa51b 100644 --- a/darwin/button.m +++ b/darwin/button.m @@ -75,7 +75,7 @@ char *uiButtonText(uiButton *b) void uiButtonSetText(uiButton *b, const char *text) { - [b->button setTitle:toNSString(text)]; + [b->button setTitle:uiprivToNSString(text)]; } void uiButtonOnClicked(uiButton *b, void (*f)(uiButton *, void *), void *data) @@ -96,7 +96,7 @@ uiButton *uiNewButton(const char *text) uiDarwinNewControl(uiButton, b); b->button = [[NSButton alloc] initWithFrame:NSZeroRect]; - [b->button setTitle:toNSString(text)]; + [b->button setTitle:uiprivToNSString(text)]; [b->button setButtonType:NSMomentaryPushInButton]; [b->button setBordered:YES]; [b->button setBezelStyle:NSRoundedBezelStyle]; diff --git a/darwin/checkbox.m b/darwin/checkbox.m index dd1ce093..124258cd 100644 --- a/darwin/checkbox.m +++ b/darwin/checkbox.m @@ -75,7 +75,7 @@ char *uiCheckboxText(uiCheckbox *c) void uiCheckboxSetText(uiCheckbox *c, const char *text) { - [c->button setTitle:toNSString(text)]; + [c->button setTitle:uiprivToNSString(text)]; } void uiCheckboxOnToggled(uiCheckbox *c, void (*f)(uiCheckbox *, void *), void *data) @@ -111,7 +111,7 @@ uiCheckbox *uiNewCheckbox(const char *text) uiDarwinNewControl(uiCheckbox, c); c->button = [[NSButton alloc] initWithFrame:NSZeroRect]; - [c->button setTitle:toNSString(text)]; + [c->button setTitle:uiprivToNSString(text)]; [c->button setButtonType:NSSwitchButton]; // doesn't seem to have an associated bezel style [c->button setBordered:NO]; diff --git a/darwin/combobox.m b/darwin/combobox.m index 89a2e28c..c282a2fc 100644 --- a/darwin/combobox.m +++ b/darwin/combobox.m @@ -78,7 +78,7 @@ static void uiComboboxDestroy(uiControl *cc) void uiComboboxAppend(uiCombobox *c, const char *text) { - [c->pbac addObject:toNSString(text)]; + [c->pbac addObject:uiprivToNSString(text)]; } int uiComboboxSelected(uiCombobox *c) diff --git a/darwin/editablecombo.m b/darwin/editablecombo.m index 434add70..d7f6c7e1 100644 --- a/darwin/editablecombo.m +++ b/darwin/editablecombo.m @@ -106,7 +106,7 @@ static void uiEditableComboboxDestroy(uiControl *cc) void uiEditableComboboxAppend(uiEditableCombobox *c, const char *text) { - [c->cb addItemWithObjectValue:toNSString(text)]; + [c->cb addItemWithObjectValue:uiprivToNSString(text)]; } char *uiEditableComboboxText(uiEditableCombobox *c) @@ -118,7 +118,7 @@ void uiEditableComboboxSetText(uiEditableCombobox *c, const char *text) { NSString *t; - t = toNSString(text); + t = uiprivToNSString(text); [c->cb setStringValue:t]; // yes, let's imitate the behavior that caused uiEditableCombobox to be separate in the first place! // just to avoid confusion when users see an option in the list in the text field but not selected in the list diff --git a/darwin/entry.m b/darwin/entry.m index 219d0805..821515b3 100644 --- a/darwin/entry.m +++ b/darwin/entry.m @@ -145,7 +145,7 @@ char *uiEntryText(uiEntry *e) void uiEntrySetText(uiEntry *e, const char *text) { - [e->textfield setStringValue:toNSString(text)]; + [e->textfield setStringValue:uiprivToNSString(text)]; // don't queue the control for resize; entry sizes are independent of their contents } diff --git a/darwin/form.m b/darwin/form.m index 613818a9..f9b9b2a8 100644 --- a/darwin/form.m +++ b/darwin/form.m @@ -531,7 +531,7 @@ void uiFormAppend(uiForm *f, const char *label, uiControl *c, int stretchy) // or at leat allow this and implicitly turn it into a spacer if (c == NULL) uiprivUserBug("You cannot add NULL to a uiForm."); - [f->view append:toNSString(label) c:c stretchy:stretchy]; + [f->view append:uiprivToNSString(label) c:c stretchy:stretchy]; } void uiFormDelete(uiForm *f, int n) diff --git a/darwin/group.m b/darwin/group.m index 0050bbdd..9c5824a7 100644 --- a/darwin/group.m +++ b/darwin/group.m @@ -131,7 +131,7 @@ char *uiGroupTitle(uiGroup *g) void uiGroupSetTitle(uiGroup *g, const char *title) { - [g->box setTitle:toNSString(title)]; + [g->box setTitle:uiprivToNSString(title)]; } void uiGroupSetChild(uiGroup *g, uiControl *child) @@ -178,7 +178,7 @@ uiGroup *uiNewGroup(const char *title) uiDarwinNewControl(uiGroup, g); g->box = [[NSBox alloc] initWithFrame:NSZeroRect]; - [g->box setTitle:toNSString(title)]; + [g->box setTitle:uiprivToNSString(title)]; [g->box setBoxType:NSBoxPrimary]; [g->box setBorderType:NSLineBorder]; [g->box setTransparent:NO]; diff --git a/darwin/label.m b/darwin/label.m index 897bc3ff..c06cb24a 100644 --- a/darwin/label.m +++ b/darwin/label.m @@ -15,7 +15,7 @@ char *uiLabelText(uiLabel *l) void uiLabelSetText(uiLabel *l, const char *text) { - [l->textfield setStringValue:toNSString(text)]; + [l->textfield setStringValue:uiprivToNSString(text)]; } NSTextField *newLabel(NSString *str) @@ -37,7 +37,7 @@ uiLabel *uiNewLabel(const char *text) uiDarwinNewControl(uiLabel, l); - l->textfield = newLabel(toNSString(text)); + l->textfield = newLabel(uiprivToNSString(text)); return l; } diff --git a/darwin/menu.m b/darwin/menu.m index 79adbae6..aba63d2e 100644 --- a/darwin/menu.m +++ b/darwin/menu.m @@ -259,7 +259,7 @@ static uiMenuItem *newItem(uiMenu *m, int type, const char *name) [m->menu addItem:item->item]; break; default: - item->item = [[NSMenuItem alloc] initWithTitle:toNSString(name) action:@selector(onClicked:) keyEquivalent:@""]; + item->item = [[NSMenuItem alloc] initWithTitle:uiprivToNSString(name) action:@selector(onClicked:) keyEquivalent:@""]; [item->item setTarget:appDelegate().menuManager]; [m->menu addItem:item->item]; break; @@ -321,10 +321,10 @@ uiMenu *uiNewMenu(const char *name) m = uiprivNew(uiMenu); - m->menu = [[NSMenu alloc] initWithTitle:toNSString(name)]; + m->menu = [[NSMenu alloc] initWithTitle:uiprivToNSString(name)]; // use automatic menu item enabling for all menus for consistency's sake - m->item = [[NSMenuItem alloc] initWithTitle:toNSString(name) action:NULL keyEquivalent:@""]; + m->item = [[NSMenuItem alloc] initWithTitle:uiprivToNSString(name) action:NULL keyEquivalent:@""]; [m->item setSubmenu:m->menu]; m->items = [NSMutableArray new]; diff --git a/darwin/multilineentry.m b/darwin/multilineentry.m index 605e9004..fdd7cf11 100644 --- a/darwin/multilineentry.m +++ b/darwin/multilineentry.m @@ -78,7 +78,7 @@ char *uiMultilineEntryText(uiMultilineEntry *e) void uiMultilineEntrySetText(uiMultilineEntry *e, const char *text) { [[e->tv textStorage] replaceCharactersInRange:NSMakeRange(0, [[e->tv string] length]) - withString:toNSString(text)]; + withString:uiprivToNSString(text)]; // must be called explicitly according to the documentation of shouldChangeTextInRange:replacementString: e->changing = YES; [e->tv didChangeText]; @@ -89,7 +89,7 @@ void uiMultilineEntrySetText(uiMultilineEntry *e, const char *text) void uiMultilineEntryAppend(uiMultilineEntry *e, const char *text) { [[e->tv textStorage] replaceCharactersInRange:NSMakeRange([[e->tv string] length], 0) - withString:toNSString(text)]; + withString:uiprivToNSString(text)]; e->changing = YES; [e->tv didChangeText]; e->changing = NO; diff --git a/darwin/radiobuttons.m b/darwin/radiobuttons.m index 25d773c9..986c477d 100644 --- a/darwin/radiobuttons.m +++ b/darwin/radiobuttons.m @@ -87,7 +87,7 @@ void uiRadioButtonsAppend(uiRadioButtons *r, const char *text) NSLayoutConstraint *constraint; b = [[NSButton alloc] initWithFrame:NSZeroRect]; - [b setTitle:toNSString(text)]; + [b setTitle:uiprivToNSString(text)]; [b setButtonType:NSRadioButton]; // doesn't seem to have an associated bezel style [b setBordered:NO]; diff --git a/darwin/stddialogs.m b/darwin/stddialogs.m index 57ce9596..be7e2dd6 100644 --- a/darwin/stddialogs.m +++ b/darwin/stddialogs.m @@ -103,8 +103,8 @@ static void msgbox(NSWindow *parent, const char *title, const char *description, [a setAlertStyle:style]; [a setShowsHelp:NO]; [a setShowsSuppressionButton:NO]; - [a setMessageText:toNSString(title)]; - [a setInformativeText:toNSString(description)]; + [a setMessageText:uiprivToNSString(title)]; + [a setInformativeText:uiprivToNSString(description)]; [a addButtonWithTitle:@"OK"]; cm = [[libuiCodeModalAlertPanel alloc] initWithPanel:a parent:parent]; [cm run]; diff --git a/darwin/tab.m b/darwin/tab.m index 3d2ca9f0..4ca2bec3 100644 --- a/darwin/tab.m +++ b/darwin/tab.m @@ -220,7 +220,7 @@ void uiTabInsertAt(uiTab *t, const char *name, int n, uiControl *child) [t->pages insertObject:page atIndex:n]; i = [[[NSTabViewItem alloc] initWithIdentifier:pageID] autorelease]; - [i setLabel:toNSString(name)]; + [i setLabel:uiprivToNSString(name)]; [i setView:view]; [t->tabview insertTabViewItem:i atIndex:n]; diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 6b8c1179..33e7e486 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -12,4 +12,14 @@ #error Sorry, libui cannot be compiled with ARC. #endif +#define uiprivToNSString(str) [NSString stringWithUTF8String:(str)] +#define uiprivFromNSString(str) [(str) UTF8String] + +// TODO find a better place for this +#ifndef NSAppKitVersionNumber10_9 +#define NSAppKitVersionNumber10_9 1265 +#endif + +/*TODO remove this*/typedef struct uiImage uiImage; + #import "OLD_uipriv_darwin.h" diff --git a/darwin/window.m b/darwin/window.m index 97c22e62..cf14a1f5 100644 --- a/darwin/window.m +++ b/darwin/window.m @@ -252,7 +252,7 @@ char *uiWindowTitle(uiWindow *w) void uiWindowSetTitle(uiWindow *w, const char *title) { - [w->window setTitle:toNSString(title)]; + [w->window setTitle:uiprivToNSString(title)]; } void uiWindowContentSize(uiWindow *w, int *width, int *height) @@ -379,7 +379,7 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) styleMask:defaultStyleMask backing:NSBackingStoreBuffered defer:YES]; - [w->window setTitle:toNSString(title)]; + [w->window setTitle:uiprivToNSString(title)]; // do NOT release when closed // we manually do this in uiWindowDestroy() above From b8316c61ddcb35848eb9cee29d40548d7d9038a3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 3 May 2018 23:00:50 -0400 Subject: [PATCH 1025/1329] Converted struct mapTable to uiprivMap first, since that typedef will be a dependency of later stuff. ALso I didn't realize whoever wrote that new menu code also completely rewrote map.m... Cleaned up style inconsistencies I found in both. Anyway I plan on getting rid of that menu code anyway, and I could just have something else for target-action instead of this depending on whatever happens with ARC... --- darwin/OLD_uipriv_darwin.h | 11 +---------- darwin/button.m | 12 ++++++------ darwin/checkbox.m | 12 ++++++------ darwin/combobox.m | 12 ++++++------ darwin/editablecombo.m | 13 +++++++------ darwin/entry.m | 12 ++++++------ darwin/map.m | 32 +++++++++++++++++--------------- darwin/menu.m | 16 ++++++++-------- darwin/slider.m | 12 ++++++------ darwin/uipriv_darwin.h | 10 ++++++++++ darwin/window.m | 12 ++++++------ 11 files changed, 79 insertions(+), 75 deletions(-) diff --git a/darwin/OLD_uipriv_darwin.h b/darwin/OLD_uipriv_darwin.h index 3cc41922..9326315f 100644 --- a/darwin/OLD_uipriv_darwin.h +++ b/darwin/OLD_uipriv_darwin.h @@ -1,7 +1,7 @@ // menu.m @interface menuManager : NSObject { - struct mapTable *items; + uiprivMap *items; BOOL hasQuit; BOOL hasPreferences; BOOL hasAbout; @@ -68,15 +68,6 @@ extern void singleChildConstraintsEstablish(struct singleChildConstraints *c, NS extern void singleChildConstraintsRemove(struct singleChildConstraints *c, NSView *cv); extern void singleChildConstraintsSetMargined(struct singleChildConstraints *c, int margined); -// map.m -extern struct mapTable *newMap(void); -extern void mapDestroy(struct mapTable *m); -extern void *mapGet(struct mapTable *m, void *key); -extern void mapSet(struct mapTable *m, void *key, void *value); -extern void mapDelete(struct mapTable *m, void *key); -extern void mapWalk(struct mapTable *m, void (*f)(void *key, void *value)); -extern void mapReset(struct mapTable *m); - // area.m extern int sendAreaEvents(NSEvent *); diff --git a/darwin/button.m b/darwin/button.m index 8e3fa51b..7256007b 100644 --- a/darwin/button.m +++ b/darwin/button.m @@ -9,7 +9,7 @@ struct uiButton { }; @interface buttonDelegateClass : NSObject { - struct mapTable *buttons; + uiprivMap *buttons; } - (IBAction)onClicked:(id)sender; - (void)registerButton:(uiButton *)b; @@ -22,13 +22,13 @@ struct uiButton { { self = [super init]; if (self) - self->buttons = newMap(); + self->buttons = uiprivNewMap(); return self; } - (void)dealloc { - mapDestroy(self->buttons); + uiprivMapDestroy(self->buttons); [super dealloc]; } @@ -36,13 +36,13 @@ struct uiButton { { uiButton *b; - b = (uiButton *) mapGet(self->buttons, sender); + b = (uiButton *) uiprivMapGet(self->buttons, sender); (*(b->onClicked))(b, b->onClickedData); } - (void)registerButton:(uiButton *)b { - mapSet(self->buttons, b->button, b); + uiprivMapSet(self->buttons, b->button, b); [b->button setTarget:self]; [b->button setAction:@selector(onClicked:)]; } @@ -50,7 +50,7 @@ struct uiButton { - (void)unregisterButton:(uiButton *)b { [b->button setTarget:nil]; - mapDelete(self->buttons, b->button); + uiprivMapDelete(self->buttons, b->button); } @end diff --git a/darwin/checkbox.m b/darwin/checkbox.m index 124258cd..c844718e 100644 --- a/darwin/checkbox.m +++ b/darwin/checkbox.m @@ -9,7 +9,7 @@ struct uiCheckbox { }; @interface checkboxDelegateClass : NSObject { - struct mapTable *buttons; + uiprivMap *buttons; } - (IBAction)onToggled:(id)sender; - (void)registerCheckbox:(uiCheckbox *)c; @@ -22,13 +22,13 @@ struct uiCheckbox { { self = [super init]; if (self) - self->buttons = newMap(); + self->buttons = uiprivNewMap(); return self; } - (void)dealloc { - mapDestroy(self->buttons); + uiprivMapDestroy(self->buttons); [super dealloc]; } @@ -36,13 +36,13 @@ struct uiCheckbox { { uiCheckbox *c; - c = (uiCheckbox *) mapGet(self->buttons, sender); + c = (uiCheckbox *) uiprivMapGet(self->buttons, sender); (*(c->onToggled))(c, c->onToggledData); } - (void)registerCheckbox:(uiCheckbox *)c { - mapSet(self->buttons, c->button, c); + uiprivMapSet(self->buttons, c->button, c); [c->button setTarget:self]; [c->button setAction:@selector(onToggled:)]; } @@ -50,7 +50,7 @@ struct uiCheckbox { - (void)unregisterCheckbox:(uiCheckbox *)c { [c->button setTarget:nil]; - mapDelete(self->buttons, c->button); + uiprivMapDelete(self->buttons, c->button); } @end diff --git a/darwin/combobox.m b/darwin/combobox.m index c282a2fc..7af7b4a6 100644 --- a/darwin/combobox.m +++ b/darwin/combobox.m @@ -14,7 +14,7 @@ struct uiCombobox { }; @interface comboboxDelegateClass : NSObject { - struct mapTable *comboboxes; + uiprivMap *comboboxes; } - (IBAction)onSelected:(id)sender; - (void)registerCombobox:(uiCombobox *)c; @@ -27,13 +27,13 @@ struct uiCombobox { { self = [super init]; if (self) - self->comboboxes = newMap(); + self->comboboxes = uiprivNewMap(); return self; } - (void)dealloc { - mapDestroy(self->comboboxes); + uiprivMapDestroy(self->comboboxes); [super dealloc]; } @@ -41,13 +41,13 @@ struct uiCombobox { { uiCombobox *c; - c = uiCombobox(mapGet(self->comboboxes, sender)); + c = uiCombobox(uiprivMapGet(self->comboboxes, sender)); (*(c->onSelected))(c, c->onSelectedData); } - (void)registerCombobox:(uiCombobox *)c { - mapSet(self->comboboxes, c->pb, c); + uiprivMapSet(self->comboboxes, c->pb, c); [c->pb setTarget:self]; [c->pb setAction:@selector(onSelected:)]; } @@ -55,7 +55,7 @@ struct uiCombobox { - (void)unregisterCombobox:(uiCombobox *)c { [c->pb setTarget:nil]; - mapDelete(self->comboboxes, c->pb); + uiprivMapDelete(self->comboboxes, c->pb); } @end diff --git a/darwin/editablecombo.m b/darwin/editablecombo.m index d7f6c7e1..305f503f 100644 --- a/darwin/editablecombo.m +++ b/darwin/editablecombo.m @@ -34,7 +34,7 @@ struct uiEditableCombobox { }; @interface editableComboboxDelegateClass : NSObject { - struct mapTable *comboboxes; + uiprivMap *comboboxes; } - (void)controlTextDidChange:(NSNotification *)note; - (void)comboBoxSelectionDidChange:(NSNotification *)note; @@ -48,13 +48,13 @@ struct uiEditableCombobox { { self = [super init]; if (self) - self->comboboxes = newMap(); + self->comboboxes = uiprivNewMap(); return self; } - (void)dealloc { - mapDestroy(self->comboboxes); + uiprivMapDestroy(self->comboboxes); [super dealloc]; } @@ -62,7 +62,8 @@ struct uiEditableCombobox { { uiEditableCombobox *c; - c = uiEditableCombobox(mapGet(self->comboboxes, [note object])); + // TODO normalize the cast styles in these calls + c = uiEditableCombobox(uiprivMapGet(self->comboboxes, [note object])); (*(c->onChanged))(c, c->onChangedData); } @@ -79,14 +80,14 @@ struct uiEditableCombobox { - (void)registerCombobox:(uiEditableCombobox *)c { - mapSet(self->comboboxes, c->cb, c); + uiprivMapSet(self->comboboxes, c->cb, c); [c->cb setDelegate:self]; } - (void)unregisterCombobox:(uiEditableCombobox *)c { [c->cb setDelegate:nil]; - mapDelete(self->comboboxes, c->cb); + uiprivMapDelete(self->comboboxes, c->cb); } @end diff --git a/darwin/entry.m b/darwin/entry.m index 821515b3..892860bc 100644 --- a/darwin/entry.m +++ b/darwin/entry.m @@ -67,7 +67,7 @@ static BOOL isSearchField(NSTextField *tf) } @interface entryDelegateClass : NSObject { - struct mapTable *entries; + uiprivMap *entries; } - (void)controlTextDidChange:(NSNotification *)note; - (IBAction)onSearch:(id)sender; @@ -81,13 +81,13 @@ static BOOL isSearchField(NSTextField *tf) { self = [super init]; if (self) - self->entries = newMap(); + self->entries = uiprivNewMap(); return self; } - (void)dealloc { - mapDestroy(self->entries); + uiprivMapDestroy(self->entries); [super dealloc]; } @@ -100,13 +100,13 @@ static BOOL isSearchField(NSTextField *tf) { uiEntry *e; - e = (uiEntry *) mapGet(self->entries, sender); + e = (uiEntry *) uiprivMapGet(self->entries, sender); (*(e->onChanged))(e, e->onChangedData); } - (void)registerEntry:(uiEntry *)e { - mapSet(self->entries, e->textfield, e); + uiprivMapSet(self->entries, e->textfield, e); if (isSearchField(e->textfield)) { [e->textfield setTarget:self]; [e->textfield setAction:@selector(onSearch:)]; @@ -120,7 +120,7 @@ static BOOL isSearchField(NSTextField *tf) [e->textfield setTarget:nil]; else [e->textfield setDelegate:nil]; - mapDelete(self->entries, e->textfield); + uiprivMapDelete(self->entries, e->textfield); } @end diff --git a/darwin/map.m b/darwin/map.m index 190218a1..a9774170 100644 --- a/darwin/map.m +++ b/darwin/map.m @@ -4,22 +4,22 @@ // unfortunately NSMutableDictionary copies its keys, meaning we can't use it for pointers // hence, this file // we could expose a NSMapTable directly, but let's treat all pointers as opaque and hide the implementation, just to be safe and prevent even more rewrites later -struct mapTable { +struct uiprivMap { NSMapTable *m; }; -struct mapTable *newMap(void) +uiprivMap *uiprivNewMap(void) { - struct mapTable *m; + uiprivMap *m; - m = uiprivNew(struct mapTable); + m = uiprivNew(uiprivMap); m->m = [[NSMapTable alloc] initWithKeyOptions:(NSPointerFunctionsOpaqueMemory | NSPointerFunctionsOpaquePersonality) valueOptions:(NSPointerFunctionsOpaqueMemory | NSPointerFunctionsOpaquePersonality) capacity:0]; return m; } -void mapDestroy(struct mapTable *m) +void uiprivMapDestroy(uiprivMap *m) { if ([m->m count] != 0) uiprivImplBug("attempt to destroy map with items inside"); @@ -27,33 +27,35 @@ void mapDestroy(struct mapTable *m) uiprivFree(m); } -void *mapGet(struct mapTable *m, void *key) +void *uiprivMapGet(uiprivMap *m, void *key) { return NSMapGet(m->m, key); } -void mapSet(struct mapTable *m, void *key, void *value) +void uiprivMapSet(uiprivMap *m, void *key, void *value) { NSMapInsert(m->m, key, value); } -void mapDelete(struct mapTable *m, void *key) +void uiprivMapDelete(uiprivMap *m, void *key) { NSMapRemove(m->m, key); } -void mapWalk(struct mapTable *m, void (*f)(void *key, void *value)) +void uiprivMapWalk(uiprivMap *m, void (*f)(void *key, void *value)) { - NSMapEnumerator e = NSEnumerateMapTable(m->m); - void *k = NULL; - void *v = NULL; - while (NSNextMapEnumeratorPair(&e, &k, &v)) { + NSMapEnumerator e; + void *k, *v; + + e = NSEnumerateMapTable(m->m); + k = NULL; + v = NULL; + while (NSNextMapEnumeratorPair(&e, &k, &v)) f(k, v); - } NSEndMapTableEnumeration(&e); } -void mapReset(struct mapTable *m) +void uiprivMapReset(uiprivMap *m) { NSResetMapTable(m->m); } diff --git a/darwin/menu.m b/darwin/menu.m index aba63d2e..46f650e9 100644 --- a/darwin/menu.m +++ b/darwin/menu.m @@ -31,7 +31,7 @@ static void mapItemReleaser(void *key, void *value) { uiMenuItem *item; - item = (uiMenuItem *)value; + item = (uiMenuItem *) value; [item->item release]; } @@ -41,7 +41,7 @@ static void mapItemReleaser(void *key, void *value) { self = [super init]; if (self) { - self->items = newMap(); + self->items = uiprivNewMap(); self->hasQuit = NO; self->hasPreferences = NO; self->hasAbout = NO; @@ -51,9 +51,9 @@ static void mapItemReleaser(void *key, void *value) - (void)dealloc { - mapWalk(self->items, mapItemReleaser); - mapReset(self->items); - mapDestroy(self->items); + uiprivMapWalk(self->items, mapItemReleaser); + uiprivMapReset(self->items); + uiprivMapDestroy(self->items); uninitMenus(); [super dealloc]; } @@ -62,7 +62,7 @@ static void mapItemReleaser(void *key, void *value) { uiMenuItem *item; - item = (uiMenuItem *) mapGet(self->items, sender); + item = (uiMenuItem *) uiprivMapGet(self->items, sender); if (item->type == typeCheckbox) uiMenuItemSetChecked(item, !uiMenuItemChecked(item)); // use the key window as the source of the menu event; it's the active window @@ -94,7 +94,7 @@ static void mapItemReleaser(void *key, void *value) self->hasAbout = YES; break; } - mapSet(self->items, item, smi); + uiprivMapSet(self->items, item, smi); } // on OS X there are two ways to handle menu items being enabled or disabled: automatically and manually @@ -112,7 +112,7 @@ static void mapItemReleaser(void *key, void *value) if (item == self.aboutItem && !self->hasAbout) return NO; // then poll the item's enabled/disabled state - smi = (uiMenuItem *) mapGet(self->items, item); + smi = (uiMenuItem *) uiprivMapGet(self->items, item); return !smi->disabled; } diff --git a/darwin/slider.m b/darwin/slider.m index f00da50f..a959264b 100644 --- a/darwin/slider.m +++ b/darwin/slider.m @@ -29,7 +29,7 @@ struct uiSlider { }; @interface sliderDelegateClass : NSObject { - struct mapTable *sliders; + uiprivMap *sliders; } - (IBAction)onChanged:(id)sender; - (void)registerSlider:(uiSlider *)b; @@ -42,13 +42,13 @@ struct uiSlider { { self = [super init]; if (self) - self->sliders = newMap(); + self->sliders = uiprivNewMap(); return self; } - (void)dealloc { - mapDestroy(self->sliders); + uiprivMapDestroy(self->sliders); [super dealloc]; } @@ -56,13 +56,13 @@ struct uiSlider { { uiSlider *s; - s = (uiSlider *) mapGet(self->sliders, sender); + s = (uiSlider *) uiprivMapGet(self->sliders, sender); (*(s->onChanged))(s, s->onChangedData); } - (void)registerSlider:(uiSlider *)s { - mapSet(self->sliders, s->slider, s); + uiprivMapSet(self->sliders, s->slider, s); [s->slider setTarget:self]; [s->slider setAction:@selector(onChanged:)]; } @@ -70,7 +70,7 @@ struct uiSlider { - (void)unregisterSlider:(uiSlider *)s { [s->slider setTarget:nil]; - mapDelete(self->sliders, s->slider); + uiprivMapDelete(self->sliders, s->slider); } @end diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 33e7e486..7a727a15 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -22,4 +22,14 @@ /*TODO remove this*/typedef struct uiImage uiImage; +// map.m +typedef struct uiprivMap uiprivMap; +extern uiprivMap *uiprivNewMap(void); +extern void uiprivMapDestroy(uiprivMap *m); +extern void *uiprivMapGet(uiprivMap *m, void *key); +extern void uiprivMapSet(uiprivMap *m, void *key, void *value); +extern void uiprivMapDelete(uiprivMap *m, void *key); +extern void uiprivMapWalk(uiprivMap *m, void (*f)(void *key, void *value)); +extern void uiprivMapReset(uiprivMap *m); + #import "OLD_uipriv_darwin.h" diff --git a/darwin/window.m b/darwin/window.m index cf14a1f5..6bf9ef13 100644 --- a/darwin/window.m +++ b/darwin/window.m @@ -33,7 +33,7 @@ struct uiWindow { @end @interface windowDelegateClass : NSObject { - struct mapTable *windows; + uiprivMap *windows; } - (BOOL)windowShouldClose:(id)sender; - (void)windowDidResize:(NSNotification *)note; @@ -50,13 +50,13 @@ struct uiWindow { { self = [super init]; if (self) - self->windows = newMap(); + self->windows = uiprivNewMap(); return self; } - (void)dealloc { - mapDestroy(self->windows); + uiprivMapDestroy(self->windows); [super dealloc]; } @@ -100,21 +100,21 @@ struct uiWindow { - (void)registerWindow:(uiWindow *)w { - mapSet(self->windows, w->window, w); + uiprivMapSet(self->windows, w->window, w); [w->window setDelegate:self]; } - (void)unregisterWindow:(uiWindow *)w { [w->window setDelegate:nil]; - mapDelete(self->windows, w->window); + uiprivMapDelete(self->windows, w->window); } - (uiWindow *)lookupWindow:(NSWindow *)w { uiWindow *v; - v = uiWindow(mapGet(self->windows, w)); + v = uiWindow(uiprivMapGet(self->windows, w)); // this CAN (and IS ALLOWED TO) return NULL, just in case we're called with some OS X-provided window as the key window return v; } From 60e71c717414cd3365c4b80cd86cd1894e744eb9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 3 May 2018 23:19:42 -0400 Subject: [PATCH 1026/1329] Migrated menu.m for now. Need to figure out what to do about ivars and methods and properties. Also still not happy with the property and autoreleasepool mess of main.m... --- darwin/OLD_uipriv_darwin.h | 18 +----------------- darwin/main.m | 2 +- darwin/menu.m | 8 ++++---- darwin/uipriv_darwin.h | 17 +++++++++++++++++ darwin/window.m | 2 +- 5 files changed, 24 insertions(+), 23 deletions(-) diff --git a/darwin/OLD_uipriv_darwin.h b/darwin/OLD_uipriv_darwin.h index 9326315f..09cfd900 100644 --- a/darwin/OLD_uipriv_darwin.h +++ b/darwin/OLD_uipriv_darwin.h @@ -1,20 +1,4 @@ -// menu.m -@interface menuManager : NSObject { - uiprivMap *items; - BOOL hasQuit; - BOOL hasPreferences; - BOOL hasAbout; -} -@property (strong) NSMenuItem *quitItem; -@property (strong) NSMenuItem *preferencesItem; -@property (strong) NSMenuItem *aboutItem; -// NSMenuValidation is only informal -- (BOOL)validateMenuItem:(NSMenuItem *)item; -- (NSMenu *)makeMenubar; -@end -extern void finalizeMenus(void); -extern void uninitMenus(void); // main.m @interface applicationClass : NSApplication @@ -22,7 +6,7 @@ extern void uninitMenus(void); // this is needed because NSApp is of type id, confusing clang #define realNSApp() ((applicationClass *) NSApp) @interface appDelegate : NSObject -@property (strong) menuManager *menuManager; +@property (strong) uiprivMenuManager *menuManager; @end #define appDelegate() ((appDelegate *) [realNSApp() delegate]) struct nextEventArgs { diff --git a/darwin/main.m b/darwin/main.m index 184a90c8..96c2b32e 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -124,7 +124,7 @@ const char *uiInit(uiInitOptions *o) loadUndocumented(); // always do this so we always have an application menu - appDelegate().menuManager = [[menuManager new] autorelease]; + appDelegate().menuManager = [[uiprivMenuManager new] autorelease]; [realNSApp() setMainMenu:[appDelegate().menuManager makeMenubar]]; uiprivSetupFontPanel(); diff --git a/darwin/menu.m b/darwin/menu.m index 46f650e9..5b83e3b1 100644 --- a/darwin/menu.m +++ b/darwin/menu.m @@ -35,7 +35,7 @@ static void mapItemReleaser(void *key, void *value) [item->item release]; } -@implementation menuManager +@implementation uiprivMenuManager - (id)init { @@ -54,7 +54,7 @@ static void mapItemReleaser(void *key, void *value) uiprivMapWalk(self->items, mapItemReleaser); uiprivMapReset(self->items); uiprivMapDestroy(self->items); - uninitMenus(); + uiprivUninitMenus(); [super dealloc]; } @@ -338,12 +338,12 @@ uiMenu *uiNewMenu(const char *name) } // @autoreleasepool } -void finalizeMenus(void) +void uiprivFinalizeMenus(void) { menusFinalized = YES; } -void uninitMenus(void) +void uiprivUninitMenus(void) { if (menus == NULL) return; diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 7a727a15..a507259f 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -32,4 +32,21 @@ extern void uiprivMapDelete(uiprivMap *m, void *key); extern void uiprivMapWalk(uiprivMap *m, void (*f)(void *key, void *value)); extern void uiprivMapReset(uiprivMap *m); +// menu.m +@interface uiprivMenuManager : NSObject { + uiprivMap *items; + BOOL hasQuit; + BOOL hasPreferences; + BOOL hasAbout; +} +@property (strong) NSMenuItem *quitItem; +@property (strong) NSMenuItem *preferencesItem; +@property (strong) NSMenuItem *aboutItem; +// NSMenuValidation is only informal +- (BOOL)validateMenuItem:(NSMenuItem *)item; +- (NSMenu *)makeMenubar; +@end +extern void uiprivFinalizeMenus(void); +extern void uiprivUninitMenus(void); + #import "OLD_uipriv_darwin.h" diff --git a/darwin/window.m b/darwin/window.m index 6bf9ef13..340caab9 100644 --- a/darwin/window.m +++ b/darwin/window.m @@ -371,7 +371,7 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) { uiWindow *w; - finalizeMenus(); + uiprivFinalizeMenus(); uiDarwinNewControl(uiWindow, w); From 1381edfa6e23abd8b9fc9d9bacb9f3bb7e245770 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 4 May 2018 19:50:02 -0400 Subject: [PATCH 1027/1329] Migrated main.m functions. Before we merge this back in I absoltuely must do something about main.m and menu.m, even if ethereal. --- darwin/OLD_uipriv_darwin.h | 20 ---------------- darwin/main.m | 48 +++++++++++++++++++------------------- darwin/menu.m | 16 ++++++------- darwin/stddialogs.m | 8 +++---- darwin/uipriv_darwin.h | 19 +++++++++++++++ darwin/winmoveresize.m | 8 +++---- 6 files changed, 59 insertions(+), 60 deletions(-) diff --git a/darwin/OLD_uipriv_darwin.h b/darwin/OLD_uipriv_darwin.h index 09cfd900..e1e99dc0 100644 --- a/darwin/OLD_uipriv_darwin.h +++ b/darwin/OLD_uipriv_darwin.h @@ -1,23 +1,3 @@ - - -// main.m -@interface applicationClass : NSApplication -@end -// this is needed because NSApp is of type id, confusing clang -#define realNSApp() ((applicationClass *) NSApp) -@interface appDelegate : NSObject -@property (strong) uiprivMenuManager *menuManager; -@end -#define appDelegate() ((appDelegate *) [realNSApp() delegate]) -struct nextEventArgs { - NSEventMask mask; - NSDate *duration; - // LONGTERM no NSRunLoopMode? - NSString *mode; - BOOL dequeue; -}; -extern int mainStep(struct nextEventArgs *nea, BOOL (^interceptEvent)(NSEvent *)); - // util.m extern void disableAutocorrect(NSTextView *); diff --git a/darwin/main.m b/darwin/main.m index 96c2b32e..cb225255 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -4,13 +4,13 @@ static BOOL canQuit = NO; static NSAutoreleasePool *globalPool; -static applicationClass *app; -static appDelegate *delegate; +static uiprivApplicationClass *app; +static uiprivAppDelegate *delegate; static BOOL (^isRunning)(void); static BOOL stepsIsRunning; -@implementation applicationClass +@implementation uiprivApplicationClass - (void)sendEvent:(NSEvent *)e { @@ -59,7 +59,7 @@ static BOOL stepsIsRunning; if (!canQuit) uiprivImplBug("call to [NSApp terminate:] when not ready to terminate; definitely contact andlabs"); - [realNSApp() stop:realNSApp()]; + [uiprivNSApp() stop:uiprivNSApp()]; // stop: won't register until another event has passed; let's synthesize one e = [NSEvent otherEventWithType:NSApplicationDefined location:NSZeroPoint @@ -70,7 +70,7 @@ static BOOL stepsIsRunning; subtype:0 data1:0 data2:0]; - [realNSApp() postEvent:e atStart:NO]; // let pending events take priority (this is what PostQuitMessage() on Windows does so we have to do it here too for parity; thanks to mikeash in irc.freenode.net/#macdev for confirming that this parameter should indeed be NO) + [uiprivNSApp() postEvent:e atStart:NO]; // let pending events take priority (this is what PostQuitMessage() on Windows does so we have to do it here too for parity; thanks to mikeash in irc.freenode.net/#macdev for confirming that this parameter should indeed be NO) // and in case uiMainSteps() was called stepsIsRunning = NO; @@ -78,7 +78,7 @@ static BOOL stepsIsRunning; @end -@implementation appDelegate +@implementation uiprivAppDelegate - (void)dealloc { @@ -112,20 +112,20 @@ const char *uiInit(uiInitOptions *o) { @autoreleasepool { uiprivOptions = *o; - app = [[applicationClass sharedApplication] retain]; + app = [[uiprivApplicationClass sharedApplication] retain]; // don't check for a NO return; something (launch services?) causes running from application bundles to always return NO when asking to change activation policy, even if the change is to the same activation policy! // see https://github.com/andlabs/ui/issues/6 - [realNSApp() setActivationPolicy:NSApplicationActivationPolicyRegular]; - delegate = [appDelegate new]; - [realNSApp() setDelegate:delegate]; + [uiprivNSApp() setActivationPolicy:NSApplicationActivationPolicyRegular]; + delegate = [uiprivAppDelegate new]; + [uiprivNSApp() setDelegate:delegate]; initAlloc(); loadFutures(); loadUndocumented(); // always do this so we always have an application menu - appDelegate().menuManager = [[uiprivMenuManager new] autorelease]; - [realNSApp() setMainMenu:[appDelegate().menuManager makeMenubar]]; + uiprivAppDelegate().menuManager = [[uiprivMenuManager new] autorelease]; + [uiprivNSApp() setMainMenu:[uiprivAppDelegate().menuManager makeMenubar]]; uiprivSetupFontPanel(); @@ -146,7 +146,7 @@ void uiUninit(void) @autoreleasepool { uiprivUninitUnderlineColors(); [delegate release]; - [realNSApp() setDelegate:nil]; + [uiprivNSApp() setDelegate:nil]; [app release]; uninitAlloc(); } @@ -159,15 +159,15 @@ void uiFreeInitError(const char *err) void uiMain(void) { isRunning = ^{ - return [realNSApp() isRunning]; + return [uiprivNSApp() isRunning]; }; - [realNSApp() run]; + [uiprivNSApp() run]; } void uiMainSteps(void) { // SDL does this and it seems to be necessary for the menubar to work (see #182) - [realNSApp() finishLaunching]; + [uiprivNSApp() finishLaunching]; isRunning = ^{ return stepsIsRunning; }; @@ -176,7 +176,7 @@ void uiMainSteps(void) int uiMainStep(int wait) { - struct nextEventArgs nea; + uiprivNextEventArgs nea; nea.mask = NSAnyEventMask; @@ -189,7 +189,7 @@ int uiMainStep(int wait) nea.mode = NSDefaultRunLoopMode; nea.dequeue = YES; - return mainStep(&nea, ^(NSEvent *e) { + return uiprivMainStep(&nea, ^(NSEvent *e) { return NO; }); } @@ -197,7 +197,7 @@ int uiMainStep(int wait) // see also: // - http://www.cocoawithlove.com/2009/01/demystifying-nsapplication-by.html // - https://github.com/gnustep/gui/blob/master/Source/NSApplication.m -int mainStep(struct nextEventArgs *nea, BOOL (^interceptEvent)(NSEvent *e)) +int uiprivMainStep(uiprivNextEventArgs *nea, BOOL (^interceptEvent)(NSEvent *e)) { NSDate *expire; NSEvent *e; @@ -207,7 +207,7 @@ int mainStep(struct nextEventArgs *nea, BOOL (^interceptEvent)(NSEvent *e)) if (!isRunning()) return 0; - e = [realNSApp() nextEventMatchingMask:nea->mask + e = [uiprivNSApp() nextEventMatchingMask:nea->mask untilDate:nea->duration inMode:nea->mode dequeue:nea->dequeue]; @@ -216,13 +216,13 @@ int mainStep(struct nextEventArgs *nea, BOOL (^interceptEvent)(NSEvent *e)) type = [e type]; if (!interceptEvent(e)) - [realNSApp() sendEvent:e]; - [realNSApp() updateWindows]; + [uiprivNSApp() sendEvent:e]; + [uiprivNSApp() updateWindows]; // GNUstep does this // it also updates the Services menu but there doesn't seem to be a public API for that so if (type != NSPeriodic && type != NSMouseMoved) - [[realNSApp() mainMenu] update]; + [[uiprivNSApp() mainMenu] update]; return 1; } @@ -231,7 +231,7 @@ int mainStep(struct nextEventArgs *nea, BOOL (^interceptEvent)(NSEvent *e)) void uiQuit(void) { canQuit = YES; - [realNSApp() terminate:realNSApp()]; + [uiprivNSApp() terminate:uiprivNSApp()]; } // thanks to mikeash in irc.freenode.net/#macdev for suggesting the use of Grand Central Dispatch for this diff --git a/darwin/menu.m b/darwin/menu.m index 5b83e3b1..41d322d9 100644 --- a/darwin/menu.m +++ b/darwin/menu.m @@ -66,7 +66,7 @@ static void mapItemReleaser(void *key, void *value) if (item->type == typeCheckbox) uiMenuItemSetChecked(item, !uiMenuItemChecked(item)); // use the key window as the source of the menu event; it's the active window - (*(item->onClicked))(item, windowFromNSWindow([realNSApp() keyWindow]), item->onClickedData); + (*(item->onClicked))(item, windowFromNSWindow([uiprivNSApp() keyWindow]), item->onClickedData); } - (IBAction)onQuitClicked:(id)sender @@ -154,7 +154,7 @@ static void mapItemReleaser(void *key, void *value) item = [[[NSMenuItem alloc] initWithTitle:@"Services" action:NULL keyEquivalent:@""] autorelease]; servicesMenu = [[[NSMenu alloc] initWithTitle:@"Services"] autorelease]; [item setSubmenu:servicesMenu]; - [realNSApp() setServicesMenu:servicesMenu]; + [uiprivNSApp() setServicesMenu:servicesMenu]; [appMenu addItem:item]; [appMenu addItem:[NSMenuItem separatorItem]]; @@ -246,13 +246,13 @@ static uiMenuItem *newItem(uiMenu *m, int type, const char *name) item->type = type; switch (item->type) { case typeQuit: - item->item = [appDelegate().menuManager.quitItem retain]; + item->item = [uiprivAppDelegate().menuManager.quitItem retain]; break; case typePreferences: - item->item = [appDelegate().menuManager.preferencesItem retain]; + item->item = [uiprivAppDelegate().menuManager.preferencesItem retain]; break; case typeAbout: - item->item = [appDelegate().menuManager.aboutItem retain]; + item->item = [uiprivAppDelegate().menuManager.aboutItem retain]; break; case typeSeparator: item->item = [[NSMenuItem separatorItem] retain]; @@ -260,12 +260,12 @@ static uiMenuItem *newItem(uiMenu *m, int type, const char *name) break; default: item->item = [[NSMenuItem alloc] initWithTitle:uiprivToNSString(name) action:@selector(onClicked:) keyEquivalent:@""]; - [item->item setTarget:appDelegate().menuManager]; + [item->item setTarget:uiprivAppDelegate().menuManager]; [m->menu addItem:item->item]; break; } - [appDelegate().menuManager register:item->item to:item]; + [uiprivAppDelegate().menuManager register:item->item to:item]; item->onClicked = defaultOnClicked; [m->items addObject:[NSValue valueWithPointer:item]]; @@ -329,7 +329,7 @@ uiMenu *uiNewMenu(const char *name) m->items = [NSMutableArray new]; - [[realNSApp() mainMenu] addItem:m->item]; + [[uiprivNSApp() mainMenu] addItem:m->item]; [menus addObject:[NSValue valueWithPointer:m]]; diff --git a/darwin/stddialogs.m b/darwin/stddialogs.m index be7e2dd6..814456ab 100644 --- a/darwin/stddialogs.m +++ b/darwin/stddialogs.m @@ -24,9 +24,9 @@ static char *runSavePanel(NSWindow *parent, NSSavePanel *s) char *filename; [s beginSheetModalForWindow:parent completionHandler:^(NSInteger result) { - [realNSApp() stopModalWithCode:result]; + [uiprivNSApp() stopModalWithCode:result]; }]; - if ([realNSApp() runModalForWindow:s] != NSFileHandlingPanelOKButton) + if ([uiprivNSApp() runModalForWindow:s] != NSFileHandlingPanelOKButton) return NULL; filename = uiDarwinNSStringToText([[s URL] path]); return filename; @@ -84,12 +84,12 @@ char *uiSaveFile(uiWindow *parent) modalDelegate:self didEndSelector:@selector(panelEnded:result:data:) contextInfo:NULL]; - return [realNSApp() runModalForWindow:[self->panel window]]; + return [uiprivNSApp() runModalForWindow:[self->panel window]]; } - (void)panelEnded:(NSAlert *)panel result:(NSInteger)result data:(void *)data { - [realNSApp() stopModalWithCode:result]; + [uiprivNSApp() stopModalWithCode:result]; } @end diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index a507259f..b2032deb 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -49,4 +49,23 @@ extern void uiprivMapReset(uiprivMap *m); extern void uiprivFinalizeMenus(void); extern void uiprivUninitMenus(void); +// main.m +@interface uiprivApplicationClass : NSApplication +@end +// this is needed because NSApp is of type id, confusing clang +#define uiprivNSApp() ((uiprivApplicationClass *) NSApp) +@interface uiprivAppDelegate : NSObject +@property (strong) uiprivMenuManager *menuManager; +@end +#define uiprivAppDelegate() ((uiprivAppDelegate *) [uiprivNSApp() delegate]) +typedef struct uiprivNextEventArgs uiprivNextEventArgs; +struct uiprivNextEventArgs { + NSEventMask mask; + NSDate *duration; + // LONGTERM no NSRunLoopMode? + NSString *mode; + BOOL dequeue; +}; +extern int uiprivMainStep(uiprivNextEventArgs *nea, BOOL (^interceptEvent)(NSEvent *)); + #import "OLD_uipriv_darwin.h" diff --git a/darwin/winmoveresize.m b/darwin/winmoveresize.m index d43f3209..44d19a2c 100644 --- a/darwin/winmoveresize.m +++ b/darwin/winmoveresize.m @@ -46,7 +46,7 @@ void onMoveDrag(struct onMoveDragParams *p, NSEvent *e) void doManualMove(NSWindow *w, NSEvent *initialEvent) { __block struct onMoveDragParams mdp; - struct nextEventArgs nea; + uiprivNextEventArgs nea; BOOL (^handleEvent)(NSEvent *e); __block BOOL done; @@ -72,7 +72,7 @@ void doManualMove(NSWindow *w, NSEvent *initialEvent) return YES; // do not send }; done = NO; - while (mainStep(&nea, handleEvent)) + while (uiprivMainStep(&nea, handleEvent)) if (done) break; } @@ -223,7 +223,7 @@ static void onResizeDrag(struct onResizeDragParams *p, NSEvent *e) void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdge edge) { __block struct onResizeDragParams rdp; - struct nextEventArgs nea; + uiprivNextEventArgs nea; BOOL (^handleEvent)(NSEvent *e); __block BOOL done; @@ -247,7 +247,7 @@ void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdge edge) return YES; // do not send }; done = NO; - while (mainStep(&nea, handleEvent)) + while (uiprivMainStep(&nea, handleEvent)) if (done) break; } From 2f92f644b501345b2a7464170e0197017cc7b93b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 4 May 2018 20:26:13 -0400 Subject: [PATCH 1028/1329] Migrated util.m and the new NSTextField functions. --- darwin/OLD_uipriv_darwin.h | 7 ------- darwin/entry.m | 6 +++--- darwin/label.m | 2 +- darwin/multilineentry.m | 2 +- darwin/spinbox.m | 2 +- darwin/uipriv_darwin.h | 7 +++++++ darwin/util.m | 3 ++- 7 files changed, 15 insertions(+), 14 deletions(-) diff --git a/darwin/OLD_uipriv_darwin.h b/darwin/OLD_uipriv_darwin.h index e1e99dc0..d7dd96ac 100644 --- a/darwin/OLD_uipriv_darwin.h +++ b/darwin/OLD_uipriv_darwin.h @@ -1,10 +1,3 @@ -// util.m -extern void disableAutocorrect(NSTextView *); - -// entry.m -extern void finishNewTextField(NSTextField *, BOOL); -extern NSTextField *newEditableTextField(void); - // window.m @interface libuiNSWindow : NSWindow - (void)libui_doMove:(NSEvent *)initialEvent; diff --git a/darwin/entry.m b/darwin/entry.m index 892860bc..dc311ec6 100644 --- a/darwin/entry.m +++ b/darwin/entry.m @@ -176,7 +176,7 @@ static void defaultOnChanged(uiEntry *e, void *data) } // these are based on interface builder defaults; my comments in the old code weren't very good so I don't really know what talked about what, sorry :/ -void finishNewTextField(NSTextField *t, BOOL isEntry) +void uiprivFinishNewTextField(NSTextField *t, BOOL isEntry) { uiDarwinSetControlFont(t, NSRegularControlSize); @@ -197,11 +197,11 @@ static NSTextField *realNewEditableTextField(Class class) tf = [[class alloc] initWithFrame:NSZeroRect]; [tf setSelectable:YES]; // otherwise the setting is masked by the editable default of YES - finishNewTextField(tf, YES); + uiprivFinishNewTextField(tf, YES); return tf; } -NSTextField *newEditableTextField(void) +NSTextField *uiprivNewEditableTextField(void) { return realNewEditableTextField([libui_intrinsicWidthNSTextField class]); } diff --git a/darwin/label.m b/darwin/label.m index c06cb24a..c5dde802 100644 --- a/darwin/label.m +++ b/darwin/label.m @@ -27,7 +27,7 @@ NSTextField *newLabel(NSString *str) [tf setEditable:NO]; [tf setSelectable:NO]; [tf setDrawsBackground:NO]; - finishNewTextField(tf, NO); + uiprivFinishNewTextField(tf, NO); return tf; } diff --git a/darwin/multilineentry.m b/darwin/multilineentry.m index fdd7cf11..901835c3 100644 --- a/darwin/multilineentry.m +++ b/darwin/multilineentry.m @@ -182,7 +182,7 @@ static uiMultilineEntry *finishMultilineEntry(BOOL hscroll) [[e->tv layoutManager] setAllowsNonContiguousLayout:YES]; // now just to be safe; this will do some of the above but whatever - disableAutocorrect(e->tv); + uiprivDisableAutocorrect(e->tv); // see https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/TextUILayer/Tasks/TextInScrollView.html // notice we don't use the Auto Layout code; see scrollview.m for more details diff --git a/darwin/spinbox.m b/darwin/spinbox.m index 73474d04..28ac4dd0 100644 --- a/darwin/spinbox.m +++ b/darwin/spinbox.m @@ -46,7 +46,7 @@ static CGFloat stepperYDelta(void) { self = [super initWithFrame:r]; if (self) { - self->tf = newEditableTextField(); + self->tf = uiprivNewEditableTextField(); [self->tf setTranslatesAutoresizingMaskIntoConstraints:NO]; self->formatter = [NSNumberFormatter new]; diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index b2032deb..a7438e42 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -68,4 +68,11 @@ struct uiprivNextEventArgs { }; extern int uiprivMainStep(uiprivNextEventArgs *nea, BOOL (^interceptEvent)(NSEvent *)); +// util.m +extern void uiprivDisableAutocorrect(NSTextView *); + +// entry.m +extern void uiprivFinishNewTextField(NSTextField *, BOOL); +extern NSTextField *uiprivNewEditableTextField(void); + #import "OLD_uipriv_darwin.h" diff --git a/darwin/util.m b/darwin/util.m index ab873906..418d958e 100644 --- a/darwin/util.m +++ b/darwin/util.m @@ -2,7 +2,8 @@ #import "uipriv_darwin.h" // LONGTERM do we really want to do this? make it an option? -void disableAutocorrect(NSTextView *tv) +// TODO figure out why we removed this from window.m +void uiprivDisableAutocorrect(NSTextView *tv) { [tv setEnabledTextCheckingTypes:0]; [tv setAutomaticDashSubstitutionEnabled:NO]; From 090c783147a6e5f236d1b9ac2faa9bd1c47cf0c7 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 5 May 2018 19:38:16 -0400 Subject: [PATCH 1029/1329] Migrated functions from window.m. --- darwin/OLD_uipriv_darwin.h | 7 ------- darwin/area.m | 12 ++++++------ darwin/menu.m | 2 +- darwin/uipriv_darwin.h | 7 +++++++ darwin/window.m | 10 +++++----- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/darwin/OLD_uipriv_darwin.h b/darwin/OLD_uipriv_darwin.h index d7dd96ac..5c277669 100644 --- a/darwin/OLD_uipriv_darwin.h +++ b/darwin/OLD_uipriv_darwin.h @@ -1,10 +1,3 @@ -// window.m -@interface libuiNSWindow : NSWindow -- (void)libui_doMove:(NSEvent *)initialEvent; -- (void)libui_doResize:(NSEvent *)initialEvent on:(uiWindowResizeEdge)edge; -@end -extern uiWindow *windowFromNSWindow(NSWindow *); - // alloc.m extern NSMutableArray *delegates; extern void initAlloc(void); diff --git a/darwin/area.m b/darwin/area.m index 1442479a..1db9233e 100644 --- a/darwin/area.m +++ b/darwin/area.m @@ -409,26 +409,26 @@ void uiAreaScrollTo(uiArea *a, double x, double y, double width, double height) void uiAreaBeginUserWindowMove(uiArea *a) { - libuiNSWindow *w; + uiprivNSWindow *w; - w = (libuiNSWindow *) [a->area window]; + w = (uiprivNSWindow *) [a->area window]; if (w == nil) return; // TODO if (a->dragevent == nil) return; // TODO - [w libui_doMove:a->dragevent]; + [w uiprivDoMove:a->dragevent]; } void uiAreaBeginUserWindowResize(uiArea *a, uiWindowResizeEdge edge) { - libuiNSWindow *w; + uiprivNSWindow *w; - w = (libuiNSWindow *) [a->area window]; + w = (uiprivNSWindow *) [a->area window]; if (w == nil) return; // TODO if (a->dragevent == nil) return; // TODO - [w libui_doResize:a->dragevent on:edge]; + [w uiprivDoResize:a->dragevent on:edge]; } uiArea *uiNewArea(uiAreaHandler *ah) diff --git a/darwin/menu.m b/darwin/menu.m index 41d322d9..153255cd 100644 --- a/darwin/menu.m +++ b/darwin/menu.m @@ -66,7 +66,7 @@ static void mapItemReleaser(void *key, void *value) if (item->type == typeCheckbox) uiMenuItemSetChecked(item, !uiMenuItemChecked(item)); // use the key window as the source of the menu event; it's the active window - (*(item->onClicked))(item, windowFromNSWindow([uiprivNSApp() keyWindow]), item->onClickedData); + (*(item->onClicked))(item, uiprivWindowFromNSWindow([uiprivNSApp() keyWindow]), item->onClickedData); } - (IBAction)onQuitClicked:(id)sender diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index a7438e42..17723511 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -75,4 +75,11 @@ extern void uiprivDisableAutocorrect(NSTextView *); extern void uiprivFinishNewTextField(NSTextField *, BOOL); extern NSTextField *uiprivNewEditableTextField(void); +// window.m +@interface uiprivNSWindow : NSWindow +- (void)uiprivDoMove:(NSEvent *)initialEvent; +- (void)uiprivDoResize:(NSEvent *)initialEvent on:(uiWindowResizeEdge)edge; +@end +extern uiWindow *uiprivWindowFromNSWindow(NSWindow *); + #import "OLD_uipriv_darwin.h" diff --git a/darwin/window.m b/darwin/window.m index 340caab9..0ade558d 100644 --- a/darwin/window.m +++ b/darwin/window.m @@ -18,14 +18,14 @@ struct uiWindow { int borderless; }; -@implementation libuiNSWindow +@implementation uiprivNSWindow -- (void)libui_doMove:(NSEvent *)initialEvent +- (void)uiprivDoMove:(NSEvent *)initialEvent { doManualMove(self, initialEvent); } -- (void)libui_doResize:(NSEvent *)initialEvent on:(uiWindowResizeEdge)edge +- (void)uiprivDoResize:(NSEvent *)initialEvent on:(uiWindowResizeEdge)edge { doManualResize(self, initialEvent, edge); } @@ -375,7 +375,7 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) uiDarwinNewControl(uiWindow, w); - w->window = [[libuiNSWindow alloc] initWithContentRect:NSMakeRect(0, 0, (CGFloat) width, (CGFloat) height) + w->window = [[uiprivNSWindow alloc] initWithContentRect:NSMakeRect(0, 0, (CGFloat) width, (CGFloat) height) styleMask:defaultStyleMask backing:NSBackingStoreBuffered defer:YES]; @@ -397,7 +397,7 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) } // utility function for menus -uiWindow *windowFromNSWindow(NSWindow *w) +uiWindow *uiprivWindowFromNSWindow(NSWindow *w) { if (w == nil) return NULL; From eb28beff1b3f191438da105569965b47fad622fc Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 5 May 2018 19:46:57 -0400 Subject: [PATCH 1030/1329] Migrated alloc.m functions. --- darwin/OLD_uipriv_darwin.h | 4 ---- darwin/alloc.m | 10 +++++----- darwin/button.m | 2 +- darwin/checkbox.m | 2 +- darwin/combobox.m | 2 +- darwin/editablecombo.m | 2 +- darwin/entry.m | 2 +- darwin/main.m | 4 ++-- darwin/slider.m | 2 +- darwin/uipriv_darwin.h | 5 +++++ darwin/window.m | 2 +- 11 files changed, 19 insertions(+), 18 deletions(-) diff --git a/darwin/OLD_uipriv_darwin.h b/darwin/OLD_uipriv_darwin.h index 5c277669..53898c6c 100644 --- a/darwin/OLD_uipriv_darwin.h +++ b/darwin/OLD_uipriv_darwin.h @@ -1,7 +1,3 @@ -// alloc.m -extern NSMutableArray *delegates; -extern void initAlloc(void); -extern void uninitAlloc(void); // autolayout.m extern NSLayoutConstraint *mkConstraint(id view1, NSLayoutAttribute attr1, NSLayoutRelation relation, id view2, NSLayoutAttribute attr2, CGFloat multiplier, CGFloat c, NSString *desc); diff --git a/darwin/alloc.m b/darwin/alloc.m index fbafc153..e20f67f3 100644 --- a/darwin/alloc.m +++ b/darwin/alloc.m @@ -3,12 +3,12 @@ #import "uipriv_darwin.h" static NSMutableArray *allocations; -NSMutableArray *delegates; +NSMutableArray *uiprivDelegates; -void initAlloc(void) +void uiprivInitAlloc(void) { allocations = [NSMutableArray new]; - delegates = [NSMutableArray new]; + uiprivDelegates = [NSMutableArray new]; } #define UINT8(p) ((uint8_t *) (p)) @@ -20,12 +20,12 @@ void initAlloc(void) #define CCHAR(p) ((const char **) (p)) #define TYPE(p) CCHAR(UINT8(p) + sizeof (size_t)) -void uninitAlloc(void) +void uiprivUninitAlloc(void) { NSMutableString *str; NSValue *v; - [delegates release]; + [uiprivDelegates release]; if ([allocations count] == 0) { [allocations release]; return; diff --git a/darwin/button.m b/darwin/button.m index 7256007b..c3a6e075 100644 --- a/darwin/button.m +++ b/darwin/button.m @@ -104,7 +104,7 @@ uiButton *uiNewButton(const char *text) if (buttonDelegate == nil) { buttonDelegate = [[buttonDelegateClass new] autorelease]; - [delegates addObject:buttonDelegate]; + [uiprivDelegates addObject:buttonDelegate]; } [buttonDelegate registerButton:b]; uiButtonOnClicked(b, defaultOnClicked, NULL); diff --git a/darwin/checkbox.m b/darwin/checkbox.m index c844718e..d5a3d6e0 100644 --- a/darwin/checkbox.m +++ b/darwin/checkbox.m @@ -120,7 +120,7 @@ uiCheckbox *uiNewCheckbox(const char *text) if (checkboxDelegate == nil) { checkboxDelegate = [[checkboxDelegateClass new] autorelease]; - [delegates addObject:checkboxDelegate]; + [uiprivDelegates addObject:checkboxDelegate]; } [checkboxDelegate registerCheckbox:c]; uiCheckboxOnToggled(c, defaultOnToggled, NULL); diff --git a/darwin/combobox.m b/darwin/combobox.m index 7af7b4a6..cc2f330a 100644 --- a/darwin/combobox.m +++ b/darwin/combobox.m @@ -136,7 +136,7 @@ uiCombobox *uiNewCombobox(void) if (comboboxDelegate == nil) { comboboxDelegate = [[comboboxDelegateClass new] autorelease]; - [delegates addObject:comboboxDelegate]; + [uiprivDelegates addObject:comboboxDelegate]; } [comboboxDelegate registerCombobox:c]; uiComboboxOnSelected(c, defaultOnSelected, NULL); diff --git a/darwin/editablecombo.m b/darwin/editablecombo.m index 305f503f..7b1a1134 100644 --- a/darwin/editablecombo.m +++ b/darwin/editablecombo.m @@ -177,7 +177,7 @@ uiEditableCombobox *uiNewEditableCombobox(void) if (comboboxDelegate == nil) { comboboxDelegate = [[editableComboboxDelegateClass new] autorelease]; - [delegates addObject:comboboxDelegate]; + [uiprivDelegates addObject:comboboxDelegate]; } [comboboxDelegate registerCombobox:c]; uiEditableComboboxOnChanged(c, defaultOnChanged, NULL); diff --git a/darwin/entry.m b/darwin/entry.m index dc311ec6..99630264 100644 --- a/darwin/entry.m +++ b/darwin/entry.m @@ -216,7 +216,7 @@ static uiEntry *finishNewEntry(Class class) if (entryDelegate == nil) { entryDelegate = [[entryDelegateClass new] autorelease]; - [delegates addObject:entryDelegate]; + [uiprivDelegates addObject:entryDelegate]; } [entryDelegate registerEntry:e]; uiEntryOnChanged(e, defaultOnChanged, NULL); diff --git a/darwin/main.m b/darwin/main.m index cb225255..b11beb68 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -119,7 +119,7 @@ const char *uiInit(uiInitOptions *o) delegate = [uiprivAppDelegate new]; [uiprivNSApp() setDelegate:delegate]; - initAlloc(); + uiprivInitAlloc(); loadFutures(); loadUndocumented(); @@ -148,7 +148,7 @@ void uiUninit(void) [delegate release]; [uiprivNSApp() setDelegate:nil]; [app release]; - uninitAlloc(); + uiprivUninitAlloc(); } } diff --git a/darwin/slider.m b/darwin/slider.m index a959264b..6f5c5a76 100644 --- a/darwin/slider.m +++ b/darwin/slider.m @@ -138,7 +138,7 @@ uiSlider *uiNewSlider(int min, int max) if (sliderDelegate == nil) { sliderDelegate = [[sliderDelegateClass new] autorelease]; - [delegates addObject:sliderDelegate]; + [uiprivDelegates addObject:sliderDelegate]; } [sliderDelegate registerSlider:s]; uiSliderOnChanged(s, defaultOnChanged, NULL); diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 17723511..ce79ac42 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -82,4 +82,9 @@ extern NSTextField *uiprivNewEditableTextField(void); @end extern uiWindow *uiprivWindowFromNSWindow(NSWindow *); +// alloc.m +extern NSMutableArray *uiprivDelegates; +extern void uiprivInitAlloc(void); +extern void uiprivUninitAlloc(void); + #import "OLD_uipriv_darwin.h" diff --git a/darwin/window.m b/darwin/window.m index 0ade558d..f40df704 100644 --- a/darwin/window.m +++ b/darwin/window.m @@ -387,7 +387,7 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) if (windowDelegate == nil) { windowDelegate = [[windowDelegateClass new] autorelease]; - [delegates addObject:windowDelegate]; + [uiprivDelegates addObject:windowDelegate]; } [windowDelegate registerWindow:w]; uiWindowOnClosing(w, defaultOnClosing, NULL); From b8fc9fa817c4c5c00a8f8e74ef2273f4e16e7b5b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 5 May 2018 20:15:32 -0400 Subject: [PATCH 1031/1329] Migrated shared functions and types of autolayout.m. --- darwin/OLD_uipriv_darwin.h | 15 --------------- darwin/autolayout.m | 22 +++++++++++----------- darwin/box.m | 14 +++++++------- darwin/form.m | 32 ++++++++++++++++---------------- darwin/grid.m | 28 ++++++++++++++-------------- darwin/group.m | 10 +++++----- darwin/radiobuttons.m | 10 +++++----- darwin/spinbox.m | 14 +++++++------- darwin/tab.m | 10 +++++----- darwin/uipriv_darwin.h | 16 ++++++++++++++++ darwin/window.m | 8 ++++---- darwin/winmoveresize.m | 8 ++++---- 12 files changed, 94 insertions(+), 93 deletions(-) diff --git a/darwin/OLD_uipriv_darwin.h b/darwin/OLD_uipriv_darwin.h index 53898c6c..c2cfb957 100644 --- a/darwin/OLD_uipriv_darwin.h +++ b/darwin/OLD_uipriv_darwin.h @@ -1,19 +1,4 @@ -// autolayout.m -extern NSLayoutConstraint *mkConstraint(id view1, NSLayoutAttribute attr1, NSLayoutRelation relation, id view2, NSLayoutAttribute attr2, CGFloat multiplier, CGFloat c, NSString *desc); -extern void jiggleViewLayout(NSView *view); -struct singleChildConstraints { - NSLayoutConstraint *leadingConstraint; - NSLayoutConstraint *topConstraint; - NSLayoutConstraint *trailingConstraintGreater; - NSLayoutConstraint *trailingConstraintEqual; - NSLayoutConstraint *bottomConstraintGreater; - NSLayoutConstraint *bottomConstraintEqual; -}; -extern void singleChildConstraintsEstablish(struct singleChildConstraints *c, NSView *contentView, NSView *childView, BOOL hugsTrailing, BOOL hugsBottom, int margined, NSString *desc); -extern void singleChildConstraintsRemove(struct singleChildConstraints *c, NSView *cv); -extern void singleChildConstraintsSetMargined(struct singleChildConstraints *c, int margined); - // area.m extern int sendAreaEvents(NSEvent *); diff --git a/darwin/autolayout.m b/darwin/autolayout.m index 3bf4acb7..a4e4395e 100644 --- a/darwin/autolayout.m +++ b/darwin/autolayout.m @@ -1,7 +1,7 @@ // 15 august 2015 #import "uipriv_darwin.h" -NSLayoutConstraint *mkConstraint(id view1, NSLayoutAttribute attr1, NSLayoutRelation relation, id view2, NSLayoutAttribute attr2, CGFloat multiplier, CGFloat c, NSString *desc) +NSLayoutConstraint *uiprivMkConstraint(id view1, NSLayoutAttribute attr1, NSLayoutRelation relation, id view2, NSLayoutAttribute attr2, CGFloat multiplier, CGFloat c, NSString *desc) { NSLayoutConstraint *constraint; @@ -29,7 +29,7 @@ CGFloat uiDarwinPaddingAmount(void *reserved) // this is needed for NSSplitView to work properly; see http://stackoverflow.com/questions/34574478/how-can-i-set-the-position-of-a-nssplitview-nowadays-setpositionofdivideratind (stal in irc.freenode.net/#macdev came up with the exact combination) // turns out it also works on NSTabView and NSBox too, possibly others! // and for bonus points, it even seems to fix unsatisfiable-constraint-autoresizing-mask issues with NSTabView and NSBox too!!! this is nuts -void jiggleViewLayout(NSView *view) +void uiprivJiggleViewLayout(NSView *view) { [view setNeedsLayout:YES]; [view layoutSubtreeIfNeeded]; @@ -42,13 +42,13 @@ static CGFloat margins(int margined) return uiDarwinMarginAmount(NULL); } -void singleChildConstraintsEstablish(struct singleChildConstraints *c, NSView *contentView, NSView *childView, BOOL hugsTrailing, BOOL hugsBottom, int margined, NSString *desc) +void uiprivSingleChildConstraintsEstablish(uiprivSingleChildConstraints *c, NSView *contentView, NSView *childView, BOOL hugsTrailing, BOOL hugsBottom, int margined, NSString *desc) { CGFloat margin; margin = margins(margined); - c->leadingConstraint = mkConstraint(contentView, NSLayoutAttributeLeading, + c->leadingConstraint = uiprivMkConstraint(contentView, NSLayoutAttributeLeading, NSLayoutRelationEqual, childView, NSLayoutAttributeLeading, 1, -margin, @@ -56,7 +56,7 @@ void singleChildConstraintsEstablish(struct singleChildConstraints *c, NSView *c [contentView addConstraint:c->leadingConstraint]; [c->leadingConstraint retain]; - c->topConstraint = mkConstraint(contentView, NSLayoutAttributeTop, + c->topConstraint = uiprivMkConstraint(contentView, NSLayoutAttributeTop, NSLayoutRelationEqual, childView, NSLayoutAttributeTop, 1, -margin, @@ -64,7 +64,7 @@ void singleChildConstraintsEstablish(struct singleChildConstraints *c, NSView *c [contentView addConstraint:c->topConstraint]; [c->topConstraint retain]; - c->trailingConstraintGreater = mkConstraint(contentView, NSLayoutAttributeTrailing, + c->trailingConstraintGreater = uiprivMkConstraint(contentView, NSLayoutAttributeTrailing, NSLayoutRelationGreaterThanOrEqual, childView, NSLayoutAttributeTrailing, 1, margin, @@ -74,7 +74,7 @@ void singleChildConstraintsEstablish(struct singleChildConstraints *c, NSView *c [contentView addConstraint:c->trailingConstraintGreater]; [c->trailingConstraintGreater retain]; - c->trailingConstraintEqual = mkConstraint(contentView, NSLayoutAttributeTrailing, + c->trailingConstraintEqual = uiprivMkConstraint(contentView, NSLayoutAttributeTrailing, NSLayoutRelationEqual, childView, NSLayoutAttributeTrailing, 1, margin, @@ -84,7 +84,7 @@ void singleChildConstraintsEstablish(struct singleChildConstraints *c, NSView *c [contentView addConstraint:c->trailingConstraintEqual]; [c->trailingConstraintEqual retain]; - c->bottomConstraintGreater = mkConstraint(contentView, NSLayoutAttributeBottom, + c->bottomConstraintGreater = uiprivMkConstraint(contentView, NSLayoutAttributeBottom, NSLayoutRelationGreaterThanOrEqual, childView, NSLayoutAttributeBottom, 1, margin, @@ -94,7 +94,7 @@ void singleChildConstraintsEstablish(struct singleChildConstraints *c, NSView *c [contentView addConstraint:c->bottomConstraintGreater]; [c->bottomConstraintGreater retain]; - c->bottomConstraintEqual = mkConstraint(contentView, NSLayoutAttributeBottom, + c->bottomConstraintEqual = uiprivMkConstraint(contentView, NSLayoutAttributeBottom, NSLayoutRelationEqual, childView, NSLayoutAttributeBottom, 1, margin, @@ -105,7 +105,7 @@ void singleChildConstraintsEstablish(struct singleChildConstraints *c, NSView *c [c->bottomConstraintEqual retain]; } -void singleChildConstraintsRemove(struct singleChildConstraints *c, NSView *cv) +void uiprivSingleChildConstraintsRemove(uiprivSingleChildConstraints *c, NSView *cv) { if (c->leadingConstraint != nil) { [cv removeConstraint:c->leadingConstraint]; @@ -139,7 +139,7 @@ void singleChildConstraintsRemove(struct singleChildConstraints *c, NSView *cv) } } -void singleChildConstraintsSetMargined(struct singleChildConstraints *c, int margined) +void uiprivSingleChildConstraintsSetMargined(uiprivSingleChildConstraints *c, int margined) { CGFloat margin; diff --git a/darwin/box.m b/darwin/box.m index 6a1941ea..72b5a71d 100644 --- a/darwin/box.m +++ b/darwin/box.m @@ -167,7 +167,7 @@ struct uiBox { if (!uiControlVisible(bc.c)) continue; if (prev == nil) { // first view - self->first = mkConstraint(self, self->primaryStart, + self->first = uiprivMkConstraint(self, self->primaryStart, NSLayoutRelationEqual, [bc view], self->primaryStart, 1, 0, @@ -178,7 +178,7 @@ struct uiBox { continue; } // not the first; link it - c = mkConstraint(prev, self->primaryEnd, + c = uiprivMkConstraint(prev, self->primaryEnd, NSLayoutRelationEqual, [bc view], self->primaryStart, 1, -padding, @@ -189,7 +189,7 @@ struct uiBox { } if (prev == nil) // no control visible; act as if no controls return; - self->last = mkConstraint(prev, self->primaryEnd, + self->last = uiprivMkConstraint(prev, self->primaryEnd, NSLayoutRelationEqual, self, self->primaryEnd, 1, 0, @@ -204,14 +204,14 @@ struct uiBox { for (bc in self->children) { if (!uiControlVisible(bc.c)) continue; - c = mkConstraint(self, self->secondaryStart, + c = uiprivMkConstraint(self, self->secondaryStart, NSLayoutRelationEqual, [bc view], self->secondaryStart, 1, 0, @"uiBox secondary start constraint"); [self addConstraint:c]; [self->otherConstraints addObject:c]; - c = mkConstraint([bc view], self->secondaryEnd, + c = uiprivMkConstraint([bc view], self->secondaryEnd, NSLayoutRelationLessThanOrEqual, self, self->secondaryEnd, 1, 0, @@ -220,7 +220,7 @@ struct uiBox { [c setPriority:NSLayoutPriorityDefaultLow]; [self addConstraint:c]; [self->otherConstraints addObject:c]; - c = mkConstraint([bc view], self->secondaryEnd, + c = uiprivMkConstraint([bc view], self->secondaryEnd, NSLayoutRelationEqual, self, self->secondaryEnd, 1, 0, @@ -244,7 +244,7 @@ struct uiBox { prev = [bc view]; continue; } - c = mkConstraint(prev, self->primarySize, + c = uiprivMkConstraint(prev, self->primarySize, NSLayoutRelationEqual, [bc view], self->primarySize, 1, 0, diff --git a/darwin/form.m b/darwin/form.m index f9b9b2a8..148dd17e 100644 --- a/darwin/form.m +++ b/darwin/form.m @@ -66,25 +66,25 @@ struct uiForm { [self.label setContentCompressionResistancePriority:NSLayoutPriorityRequired forOrientation:NSLayoutConstraintOrientationVertical]; [self addSubview:self.label]; - self.leading = mkConstraint(self.label, NSLayoutAttributeLeading, + self.leading = uiprivMkConstraint(self.label, NSLayoutAttributeLeading, NSLayoutRelationGreaterThanOrEqual, self, NSLayoutAttributeLeading, 1, 0, @"uiForm label leading"); [self addConstraint:self.leading]; - self.top = mkConstraint(self.label, NSLayoutAttributeTop, + self.top = uiprivMkConstraint(self.label, NSLayoutAttributeTop, NSLayoutRelationEqual, self, NSLayoutAttributeTop, 1, 0, @"uiForm label top"); [self addConstraint:self.top]; - self.trailing = mkConstraint(self.label, NSLayoutAttributeTrailing, + self.trailing = uiprivMkConstraint(self.label, NSLayoutAttributeTrailing, NSLayoutRelationEqual, self, NSLayoutAttributeTrailing, 1, 0, @"uiForm label trailing"); [self addConstraint:self.trailing]; - self.bottom = mkConstraint(self.label, NSLayoutAttributeBottom, + self.bottom = uiprivMkConstraint(self.label, NSLayoutAttributeBottom, NSLayoutRelationEqual, self, NSLayoutAttributeBottom, 1, 0, @@ -224,7 +224,7 @@ struct uiForm { if (!uiControlVisible(fc.c)) continue; if (prev == nil) { // first view - self->first = mkConstraint(self, NSLayoutAttributeTop, + self->first = uiprivMkConstraint(self, NSLayoutAttributeTop, NSLayoutRelationEqual, [fc view], NSLayoutAttributeTop, 1, 0, @@ -236,7 +236,7 @@ struct uiForm { continue; } // not the first; link it - c = mkConstraint(prev, NSLayoutAttributeBottom, + c = uiprivMkConstraint(prev, NSLayoutAttributeBottom, NSLayoutRelationEqual, [fc view], NSLayoutAttributeTop, 1, -padding, @@ -244,14 +244,14 @@ struct uiForm { [self addConstraint:c]; [self->inBetweens addObject:c]; // and make the same width - c = mkConstraint(prev, NSLayoutAttributeWidth, + c = uiprivMkConstraint(prev, NSLayoutAttributeWidth, NSLayoutRelationEqual, [fc view], NSLayoutAttributeWidth, 1, 0, @"uiForm control width constraint"); [self addConstraint:c]; [self->widths addObject:c]; - c = mkConstraint(prevlabel, NSLayoutAttributeWidth, + c = uiprivMkConstraint(prevlabel, NSLayoutAttributeWidth, NSLayoutRelationEqual, fc, NSLayoutAttributeWidth, 1, 0, @@ -263,7 +263,7 @@ struct uiForm { } if (prev == nil) // all hidden; act as if nothing there return; - self->last = mkConstraint(prev, NSLayoutAttributeBottom, + self->last = uiprivMkConstraint(prev, NSLayoutAttributeBottom, NSLayoutRelationEqual, self, NSLayoutAttributeBottom, 1, 0, @@ -275,7 +275,7 @@ struct uiForm { for (fc in self->children) { if (!uiControlVisible(fc.c)) continue; - c = mkConstraint(self, NSLayoutAttributeLeading, + c = uiprivMkConstraint(self, NSLayoutAttributeLeading, NSLayoutRelationEqual, fc, NSLayoutAttributeLeading, 1, 0, @@ -284,7 +284,7 @@ struct uiForm { [self->leadings addObject:c]; // coerce the control to be as wide as possible // see http://stackoverflow.com/questions/37710892/in-auto-layout-i-set-up-labels-that-shouldnt-grow-horizontally-and-controls-th - c = mkConstraint(self, NSLayoutAttributeLeading, + c = uiprivMkConstraint(self, NSLayoutAttributeLeading, NSLayoutRelationEqual, [fc view], NSLayoutAttributeLeading, 1, 0, @@ -292,14 +292,14 @@ struct uiForm { [c setPriority:NSLayoutPriorityDefaultHigh]; [self addConstraint:c]; [self->leadings addObject:c]; - c = mkConstraint(fc, NSLayoutAttributeTrailing, + c = uiprivMkConstraint(fc, NSLayoutAttributeTrailing, NSLayoutRelationEqual, [fc view], NSLayoutAttributeLeading, 1, -padding, @"uiForm middle constraint"); [self addConstraint:c]; [self->middles addObject:c]; - c = mkConstraint([fc view], NSLayoutAttributeTrailing, + c = uiprivMkConstraint([fc view], NSLayoutAttributeTrailing, NSLayoutRelationEqual, self, NSLayoutAttributeTrailing, 1, 0, @@ -307,7 +307,7 @@ struct uiForm { [self addConstraint:c]; [self->trailings addObject:c]; // TODO - c = mkConstraint(fc, NSLayoutAttributeBottom, + c = uiprivMkConstraint(fc, NSLayoutAttributeBottom, NSLayoutRelationLessThanOrEqual, self, NSLayoutAttributeBottom, 1, 0, @@ -327,7 +327,7 @@ struct uiForm { prev = [fc view]; continue; } - c = mkConstraint([fc view], NSLayoutAttributeHeight, + c = uiprivMkConstraint([fc view], NSLayoutAttributeHeight, NSLayoutRelationEqual, prev, NSLayoutAttributeHeight, 1, 0, @@ -376,7 +376,7 @@ struct uiForm { attribute = NSLayoutAttributeBaseline; if ([[fc view] isKindOfClass:[NSScrollView class]]) attribute = NSLayoutAttributeTop; - fc.baseline = mkConstraint(fc.label, attribute, + fc.baseline = uiprivMkConstraint(fc.label, attribute, NSLayoutRelationEqual, [fc view], attribute, 1, 0, diff --git a/darwin/grid.m b/darwin/grid.m index 218a5f63..4cbf34c2 100644 --- a/darwin/grid.m +++ b/darwin/grid.m @@ -72,7 +72,7 @@ struct uiGrid { uiDarwinControlSyncEnableState(uiDarwinControl(self.c), uiControlEnabledToUser(uiControl(g))); if (self.halign == uiAlignStart || self.halign == uiAlignFill) { - self.leadingc = mkConstraint(self, NSLayoutAttributeLeading, + self.leadingc = uiprivMkConstraint(self, NSLayoutAttributeLeading, NSLayoutRelationEqual, [self view], NSLayoutAttributeLeading, 1, 0, @@ -80,7 +80,7 @@ struct uiGrid { [self addConstraint:self.leadingc]; } if (self.halign == uiAlignCenter) { - self.xcenterc = mkConstraint(self, NSLayoutAttributeCenterX, + self.xcenterc = uiprivMkConstraint(self, NSLayoutAttributeCenterX, NSLayoutRelationEqual, [self view], NSLayoutAttributeCenterX, 1, 0, @@ -88,7 +88,7 @@ struct uiGrid { [self addConstraint:self.xcenterc]; } if (self.halign == uiAlignEnd || self.halign == uiAlignFill) { - self.trailingc = mkConstraint(self, NSLayoutAttributeTrailing, + self.trailingc = uiprivMkConstraint(self, NSLayoutAttributeTrailing, NSLayoutRelationEqual, [self view], NSLayoutAttributeTrailing, 1, 0, @@ -97,7 +97,7 @@ struct uiGrid { } if (self.valign == uiAlignStart || self.valign == uiAlignFill) { - self.topc = mkConstraint(self, NSLayoutAttributeTop, + self.topc = uiprivMkConstraint(self, NSLayoutAttributeTop, NSLayoutRelationEqual, [self view], NSLayoutAttributeTop, 1, 0, @@ -105,7 +105,7 @@ struct uiGrid { [self addConstraint:self.topc]; } if (self.valign == uiAlignCenter) { - self.ycenterc = mkConstraint(self, NSLayoutAttributeCenterY, + self.ycenterc = uiprivMkConstraint(self, NSLayoutAttributeCenterY, NSLayoutRelationEqual, [self view], NSLayoutAttributeCenterY, 1, 0, @@ -113,7 +113,7 @@ struct uiGrid { [self addConstraint:self.ycenterc]; } if (self.valign == uiAlignEnd || self.valign == uiAlignFill) { - self.bottomc = mkConstraint(self, NSLayoutAttributeBottom, + self.bottomc = uiprivMkConstraint(self, NSLayoutAttributeBottom, NSLayoutRelationEqual, [self view], NSLayoutAttributeBottom, 1, 0, @@ -403,14 +403,14 @@ struct uiGrid { // now establish all the edge constraints // leading and trailing edges for (y = 0; y < ycount; y++) { - c = mkConstraint(self, NSLayoutAttributeLeading, + c = uiprivMkConstraint(self, NSLayoutAttributeLeading, NSLayoutRelationEqual, gv[y][0], NSLayoutAttributeLeading, 1, 0, @"uiGrid leading edge constraint"); [self addConstraint:c]; [self->edges addObject:c]; - c = mkConstraint(self, NSLayoutAttributeTrailing, + c = uiprivMkConstraint(self, NSLayoutAttributeTrailing, NSLayoutRelationEqual, gv[y][xcount - 1], NSLayoutAttributeTrailing, 1, 0, @@ -420,14 +420,14 @@ struct uiGrid { } // top and bottom edges for (x = 0; x < xcount; x++) { - c = mkConstraint(self, NSLayoutAttributeTop, + c = uiprivMkConstraint(self, NSLayoutAttributeTop, NSLayoutRelationEqual, gv[0][x], NSLayoutAttributeTop, 1, 0, @"uiGrid top edge constraint"); [self addConstraint:c]; [self->edges addObject:c]; - c = mkConstraint(self, NSLayoutAttributeBottom, + c = uiprivMkConstraint(self, NSLayoutAttributeBottom, NSLayoutRelationEqual, gv[ycount - 1][x], NSLayoutAttributeBottom, 1, 0, @@ -446,7 +446,7 @@ struct uiGrid { for (y++; y < ycount; y++) { if (gspan[y][x]) continue; - c = mkConstraint(gv[firsty][x], NSLayoutAttributeLeading, + c = uiprivMkConstraint(gv[firsty][x], NSLayoutAttributeLeading, NSLayoutRelationEqual, gv[y][x], NSLayoutAttributeLeading, 1, 0, @@ -463,7 +463,7 @@ struct uiGrid { for (x++; x < xcount; x++) { if (gspan[y][x]) continue; - c = mkConstraint(gv[y][firstx], NSLayoutAttributeTop, + c = uiprivMkConstraint(gv[y][firstx], NSLayoutAttributeTop, NSLayoutRelationEqual, gv[y][x], NSLayoutAttributeTop, 1, 0, @@ -477,7 +477,7 @@ struct uiGrid { for (y = 0; y < ycount; y++) for (x = 1; x < xcount; x++) if (gv[y][x - 1] != gv[y][x]) { - c = mkConstraint(gv[y][x - 1], NSLayoutAttributeTrailing, + c = uiprivMkConstraint(gv[y][x - 1], NSLayoutAttributeTrailing, NSLayoutRelationEqual, gv[y][x], NSLayoutAttributeLeading, 1, -padding, @@ -488,7 +488,7 @@ struct uiGrid { for (x = 0; x < xcount; x++) for (y = 1; y < ycount; y++) if (gv[y - 1][x] != gv[y][x]) { - c = mkConstraint(gv[y - 1][x], NSLayoutAttributeBottom, + c = uiprivMkConstraint(gv[y - 1][x], NSLayoutAttributeBottom, NSLayoutRelationEqual, gv[y][x], NSLayoutAttributeTop, 1, -padding, diff --git a/darwin/group.m b/darwin/group.m index 9c5824a7..2cfcdf47 100644 --- a/darwin/group.m +++ b/darwin/group.m @@ -8,7 +8,7 @@ struct uiGroup { NSLayoutPriority oldHorzHuggingPri; NSLayoutPriority oldVertHuggingPri; int margined; - struct singleChildConstraints constraints; + uiprivSingleChildConstraints constraints; NSLayoutPriority horzHuggingPri; NSLayoutPriority vertHuggingPri; }; @@ -16,7 +16,7 @@ struct uiGroup { static void removeConstraints(uiGroup *g) { // set to contentView instead of to the box itself, otherwise we get clipping underneath the label - singleChildConstraintsRemove(&(g->constraints), [g->box contentView]); + uiprivSingleChildConstraintsRemove(&(g->constraints), [g->box contentView]); } static void uiGroupDestroy(uiControl *c) @@ -64,14 +64,14 @@ static void groupRelayout(uiGroup *g) if (g->child == NULL) return; childView = (NSView *) uiControlHandle(g->child); - singleChildConstraintsEstablish(&(g->constraints), + uiprivSingleChildConstraintsEstablish(&(g->constraints), [g->box contentView], childView, uiDarwinControlHugsTrailingEdge(uiDarwinControl(g->child)), uiDarwinControlHugsBottom(uiDarwinControl(g->child)), g->margined, @"uiGroup"); // needed for some very rare drawing errors... - jiggleViewLayout(g->box); + uiprivJiggleViewLayout(g->box); } // TODO rename these since I'm starting to get confused by what they mean by hugging @@ -168,7 +168,7 @@ int uiGroupMargined(uiGroup *g) void uiGroupSetMargined(uiGroup *g, int margined) { g->margined = margined; - singleChildConstraintsSetMargined(&(g->constraints), g->margined); + uiprivSingleChildConstraintsSetMargined(&(g->constraints), g->margined); } uiGroup *uiNewGroup(const char *title) diff --git a/darwin/radiobuttons.m b/darwin/radiobuttons.m index 986c477d..c7b03717 100644 --- a/darwin/radiobuttons.m +++ b/darwin/radiobuttons.m @@ -102,14 +102,14 @@ void uiRadioButtonsAppend(uiRadioButtons *r, const char *text) [r->view addSubview:b]; // pin horizontally to the edges of the superview - constraint = mkConstraint(b, NSLayoutAttributeLeading, + constraint = uiprivMkConstraint(b, NSLayoutAttributeLeading, NSLayoutRelationEqual, r->view, NSLayoutAttributeLeading, 1, 0, @"uiRadioButtons button leading constraint"); [r->view addConstraint:constraint]; [r->constraints addObject:constraint]; - constraint = mkConstraint(b, NSLayoutAttributeTrailing, + constraint = uiprivMkConstraint(b, NSLayoutAttributeTrailing, NSLayoutRelationEqual, r->view, NSLayoutAttributeTrailing, 1, 0, @@ -120,14 +120,14 @@ void uiRadioButtonsAppend(uiRadioButtons *r, const char *text) // if this is the first view, pin it to the top // otherwise pin to the bottom of the last if ([r->buttons count] == 1) - constraint = mkConstraint(b, NSLayoutAttributeTop, + constraint = uiprivMkConstraint(b, NSLayoutAttributeTop, NSLayoutRelationEqual, r->view, NSLayoutAttributeTop, 1, 0, @"uiRadioButtons first button top constraint"); else { b2 = buttonAt(r, [r->buttons count] - 2); - constraint = mkConstraint(b, NSLayoutAttributeTop, + constraint = uiprivMkConstraint(b, NSLayoutAttributeTop, NSLayoutRelationEqual, b2, NSLayoutAttributeBottom, 1, 0, @@ -144,7 +144,7 @@ void uiRadioButtonsAppend(uiRadioButtons *r, const char *text) } // and make the new bottom constraint - r->lastv = mkConstraint(b, NSLayoutAttributeBottom, + r->lastv = uiprivMkConstraint(b, NSLayoutAttributeBottom, NSLayoutRelationEqual, r->view, NSLayoutAttributeBottom, 1, 0, diff --git a/darwin/spinbox.m b/darwin/spinbox.m index 28ac4dd0..a22ecf13 100644 --- a/darwin/spinbox.m +++ b/darwin/spinbox.m @@ -70,37 +70,37 @@ static CGFloat stepperYDelta(void) [self addSubview:self->tf]; [self addSubview:self->stepper]; - [self addConstraint:mkConstraint(self->tf, NSLayoutAttributeLeading, + [self addConstraint:uiprivMkConstraint(self->tf, NSLayoutAttributeLeading, NSLayoutRelationEqual, self, NSLayoutAttributeLeading, 1, 0, @"uiSpinbox left edge")]; - [self addConstraint:mkConstraint(self->stepper, NSLayoutAttributeTrailing, + [self addConstraint:uiprivMkConstraint(self->stepper, NSLayoutAttributeTrailing, NSLayoutRelationEqual, self, NSLayoutAttributeTrailing, 1, 0, @"uiSpinbox right edge")]; - [self addConstraint:mkConstraint(self->tf, NSLayoutAttributeTop, + [self addConstraint:uiprivMkConstraint(self->tf, NSLayoutAttributeTop, NSLayoutRelationEqual, self, NSLayoutAttributeTop, 1, 0, @"uiSpinbox top edge text field")]; - [self addConstraint:mkConstraint(self->tf, NSLayoutAttributeBottom, + [self addConstraint:uiprivMkConstraint(self->tf, NSLayoutAttributeBottom, NSLayoutRelationEqual, self, NSLayoutAttributeBottom, 1, 0, @"uiSpinbox bottom edge text field")]; - [self addConstraint:mkConstraint(self->stepper, NSLayoutAttributeTop, + [self addConstraint:uiprivMkConstraint(self->stepper, NSLayoutAttributeTop, NSLayoutRelationEqual, self, NSLayoutAttributeTop, 1, stepperYDelta(), @"uiSpinbox top edge stepper")]; - [self addConstraint:mkConstraint(self->stepper, NSLayoutAttributeBottom, + [self addConstraint:uiprivMkConstraint(self->stepper, NSLayoutAttributeBottom, NSLayoutRelationEqual, self, NSLayoutAttributeBottom, 1, stepperYDelta(), @"uiSpinbox bottom edge stepper")]; - [self addConstraint:mkConstraint(self->tf, NSLayoutAttributeTrailing, + [self addConstraint:uiprivMkConstraint(self->tf, NSLayoutAttributeTrailing, NSLayoutRelationEqual, self->stepper, NSLayoutAttributeLeading, 1, -3, // arbitrary amount; good enough visually (and it seems to match NSDatePicker too, at least on 10.11, which is even better) diff --git a/darwin/tab.m b/darwin/tab.m index 4ca2bec3..28c38318 100644 --- a/darwin/tab.m +++ b/darwin/tab.m @@ -4,7 +4,7 @@ // TODO need to jiggle on tab change too (second page disabled tab label initially ambiguous) @interface tabPage : NSObject { - struct singleChildConstraints constraints; + uiprivSingleChildConstraints constraints; int margined; NSView *view; // the NSTabViewItem view itself NSObject *pageID; @@ -58,7 +58,7 @@ struct uiTab { [self removeChildConstraints]; if (self.c == NULL) return; - singleChildConstraintsEstablish(&(self->constraints), + uiprivSingleChildConstraintsEstablish(&(self->constraints), self->view, [self childView], uiDarwinControlHugsTrailingEdge(uiDarwinControl(self.c)), uiDarwinControlHugsBottom(uiDarwinControl(self.c)), @@ -68,7 +68,7 @@ struct uiTab { - (void)removeChildConstraints { - singleChildConstraintsRemove(&(self->constraints), self->view); + uiprivSingleChildConstraintsRemove(&(self->constraints), self->view); } - (int)isMargined @@ -79,7 +79,7 @@ struct uiTab { - (void)setMargined:(int)m { self->margined = m; - singleChildConstraintsSetMargined(&(self->constraints), self->margined); + uiprivSingleChildConstraintsSetMargined(&(self->constraints), self->margined); } @end @@ -136,7 +136,7 @@ static void tabRelayout(uiTab *t) for (page in t->pages) [page establishChildConstraints]; // and this gets rid of some weird issues with regards to box alignment - jiggleViewLayout(t->tabview); + uiprivJiggleViewLayout(t->tabview); } BOOL uiTabHugsTrailingEdge(uiDarwinControl *c) diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index ce79ac42..b3cd3e23 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -87,4 +87,20 @@ extern NSMutableArray *uiprivDelegates; extern void uiprivInitAlloc(void); extern void uiprivUninitAlloc(void); +// autolayout.m +extern NSLayoutConstraint *uiprivMkConstraint(id view1, NSLayoutAttribute attr1, NSLayoutRelation relation, id view2, NSLayoutAttribute attr2, CGFloat multiplier, CGFloat c, NSString *desc); +extern void uiprivJiggleViewLayout(NSView *view); +typedef struct uiprivSingleChildConstraints uiprivSingleChildConstraints; +struct uiprivSingleChildConstraints { + NSLayoutConstraint *leadingConstraint; + NSLayoutConstraint *topConstraint; + NSLayoutConstraint *trailingConstraintGreater; + NSLayoutConstraint *trailingConstraintEqual; + NSLayoutConstraint *bottomConstraintGreater; + NSLayoutConstraint *bottomConstraintEqual; +}; +extern void uiprivSingleChildConstraintsEstablish(uiprivSingleChildConstraints *c, NSView *contentView, NSView *childView, BOOL hugsTrailing, BOOL hugsBottom, int margined, NSString *desc); +extern void uiprivSingleChildConstraintsRemove(uiprivSingleChildConstraints *c, NSView *cv); +extern void uiprivSingleChildConstraintsSetMargined(uiprivSingleChildConstraints *c, int margined); + #import "OLD_uipriv_darwin.h" diff --git a/darwin/window.m b/darwin/window.m index f40df704..9ca8dd9f 100644 --- a/darwin/window.m +++ b/darwin/window.m @@ -10,7 +10,7 @@ struct uiWindow { int margined; int (*onClosing)(uiWindow *, void *); void *onClosingData; - struct singleChildConstraints constraints; + uiprivSingleChildConstraints constraints; void (*onContentSizeChanged)(uiWindow *, void *); void *onContentSizeChangedData; BOOL suppressSizeChanged; @@ -128,7 +128,7 @@ static void removeConstraints(uiWindow *w) NSView *cv; cv = [w->window contentView]; - singleChildConstraintsRemove(&(w->constraints), cv); + uiprivSingleChildConstraintsRemove(&(w->constraints), cv); } static void uiWindowDestroy(uiControl *c) @@ -215,7 +215,7 @@ static void windowRelayout(uiWindow *w) return; childView = (NSView *) uiControlHandle(w->child); contentView = [w->window contentView]; - singleChildConstraintsEstablish(&(w->constraints), + uiprivSingleChildConstraintsEstablish(&(w->constraints), contentView, childView, uiDarwinControlHugsTrailingEdge(uiDarwinControl(w->child)), uiDarwinControlHugsBottom(uiDarwinControl(w->child)), @@ -354,7 +354,7 @@ int uiWindowMargined(uiWindow *w) void uiWindowSetMargined(uiWindow *w, int margined) { w->margined = margined; - singleChildConstraintsSetMargined(&(w->constraints), w->margined); + uiprivSingleChildConstraintsSetMargined(&(w->constraints), w->margined); } static int defaultOnClosing(uiWindow *w, void *data) diff --git a/darwin/winmoveresize.m b/darwin/winmoveresize.m index 44d19a2c..671ad203 100644 --- a/darwin/winmoveresize.m +++ b/darwin/winmoveresize.m @@ -90,14 +90,14 @@ static void minMaxAutoLayoutSizes(NSWindow *w, NSSize *min, NSSize *max) // minimum: encourage the window to be as small as possible contentView = [w contentView]; - cw = mkConstraint(contentView, NSLayoutAttributeWidth, + cw = uiprivMkConstraint(contentView, NSLayoutAttributeWidth, NSLayoutRelationEqual, nil, NSLayoutAttributeNotAnAttribute, 0, 0, @"window minimum width finding constraint"); [cw setPriority:NSLayoutPriorityDragThatCanResizeWindow]; [contentView addConstraint:cw]; - ch = mkConstraint(contentView, NSLayoutAttributeHeight, + ch = uiprivMkConstraint(contentView, NSLayoutAttributeHeight, NSLayoutRelationEqual, nil, NSLayoutAttributeNotAnAttribute, 0, 0, @@ -110,14 +110,14 @@ static void minMaxAutoLayoutSizes(NSWindow *w, NSSize *min, NSSize *max) // maximum: encourage the window to be as large as possible contentView = [w contentView]; - cw = mkConstraint(contentView, NSLayoutAttributeWidth, + cw = uiprivMkConstraint(contentView, NSLayoutAttributeWidth, NSLayoutRelationEqual, nil, NSLayoutAttributeNotAnAttribute, 0, CGFLOAT_MAX, @"window maximum width finding constraint"); [cw setPriority:NSLayoutPriorityDragThatCanResizeWindow]; [contentView addConstraint:cw]; - ch = mkConstraint(contentView, NSLayoutAttributeHeight, + ch = uiprivMkConstraint(contentView, NSLayoutAttributeHeight, NSLayoutRelationEqual, nil, NSLayoutAttributeNotAnAttribute, 0, CGFLOAT_MAX, From 323a8945eb1d7065f4d44a3ae7feefe6f96afa45 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 5 May 2018 20:20:57 -0400 Subject: [PATCH 1032/1329] Migrated sendAreaEvents() and the areaevents.m keycode functions. --- darwin/OLD_uipriv_darwin.h | 8 -------- darwin/area.m | 6 +++--- darwin/areaevents.m | 4 ++-- darwin/main.m | 2 +- darwin/uipriv_darwin.h | 7 +++++++ 5 files changed, 13 insertions(+), 14 deletions(-) diff --git a/darwin/OLD_uipriv_darwin.h b/darwin/OLD_uipriv_darwin.h index c2cfb957..998ab36c 100644 --- a/darwin/OLD_uipriv_darwin.h +++ b/darwin/OLD_uipriv_darwin.h @@ -1,11 +1,3 @@ - -// area.m -extern int sendAreaEvents(NSEvent *); - -// areaevents.m -extern BOOL fromKeycode(unsigned short keycode, uiAreaKeyEvent *ke); -extern BOOL keycodeModifier(unsigned short keycode, uiModifiers *mod); - // draw.m extern uiDrawContext *newContext(CGContextRef, CGFloat); extern void freeContext(uiDrawContext *); diff --git a/darwin/area.m b/darwin/area.m index 1db9233e..5705c896 100644 --- a/darwin/area.m +++ b/darwin/area.m @@ -264,7 +264,7 @@ mouseEvent(otherMouseUp) ke.Up = up; - if (!fromKeycode([e keyCode], &ke)) + if (!uiprivFromKeycode([e keyCode], &ke)) return 0; return [self sendKeyEvent:&ke]; } @@ -289,7 +289,7 @@ mouseEvent(otherMouseUp) // Mac OS X sends this event on both key up and key down. // Fortunately -[e keyCode] IS valid here, so we can simply map from key code to Modifiers, get the value of [e modifierFlags], and check if the respective bit is set or not — that will give us the up/down state - if (!keycodeModifier([e keyCode], &whichmod)) + if (!uiprivKeycodeModifier([e keyCode], &whichmod)) return 0; ke.Modifier = whichmod; ke.Modifiers = [self parseModifiers:e]; @@ -361,7 +361,7 @@ static void uiAreaDestroy(uiControl *c) // by default, NSApplication eats some key events // this prevents that from happening with uiArea // see http://stackoverflow.com/questions/24099063/how-do-i-detect-keyup-in-my-nsview-with-the-command-key-held and http://lists.apple.com/archives/cocoa-dev/2003/Oct/msg00442.html -int sendAreaEvents(NSEvent *e) +int uiprivSendAreaEvents(NSEvent *e) { NSEventType type; id focused; diff --git a/darwin/areaevents.m b/darwin/areaevents.m index d7ceaaad..27b5dd64 100644 --- a/darwin/areaevents.m +++ b/darwin/areaevents.m @@ -129,7 +129,7 @@ static const struct { { 0xFFFF, 0 }, }; -BOOL fromKeycode(unsigned short keycode, uiAreaKeyEvent *ke) +BOOL uiprivFromKeycode(unsigned short keycode, uiAreaKeyEvent *ke) { int i; @@ -146,7 +146,7 @@ BOOL fromKeycode(unsigned short keycode, uiAreaKeyEvent *ke) return NO; } -BOOL keycodeModifier(unsigned short keycode, uiModifiers *mod) +BOOL uiprivKeycodeModifier(unsigned short keycode, uiModifiers *mod) { int i; diff --git a/darwin/main.m b/darwin/main.m index b11beb68..4c930dae 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -14,7 +14,7 @@ static BOOL stepsIsRunning; - (void)sendEvent:(NSEvent *)e { - if (sendAreaEvents(e) != 0) + if (uiprivSendAreaEvents(e) != 0) return; [super sendEvent:e]; } diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index b3cd3e23..1cd88dbd 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -103,4 +103,11 @@ extern void uiprivSingleChildConstraintsEstablish(uiprivSingleChildConstraints * extern void uiprivSingleChildConstraintsRemove(uiprivSingleChildConstraints *c, NSView *cv); extern void uiprivSingleChildConstraintsSetMargined(uiprivSingleChildConstraints *c, int margined); +// area.m +extern int uiprivSendAreaEvents(NSEvent *); + +// areaevents.m +extern BOOL uiprivFromKeycode(unsigned short keycode, uiAreaKeyEvent *ke); +extern BOOL uiprivKeycodeModifier(unsigned short keycode, uiModifiers *mod); + #import "OLD_uipriv_darwin.h" From cc271ccc37a83d77cbe6a59caffacd7abbfd7655 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 5 May 2018 20:28:00 -0400 Subject: [PATCH 1033/1329] Migrated newContext(), freeContext(), and colorButtonInhibitSendAction(). --- darwin/OLD_uipriv_darwin.h | 12 ------------ darwin/area.m | 4 ++-- darwin/colorbutton.m | 2 +- darwin/draw.m | 4 ++-- darwin/main.m | 2 +- darwin/uipriv_darwin.h | 12 ++++++++++++ 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/darwin/OLD_uipriv_darwin.h b/darwin/OLD_uipriv_darwin.h index 998ab36c..299db15b 100644 --- a/darwin/OLD_uipriv_darwin.h +++ b/darwin/OLD_uipriv_darwin.h @@ -1,15 +1,3 @@ -// draw.m -extern uiDrawContext *newContext(CGContextRef, CGFloat); -extern void freeContext(uiDrawContext *); - -// fontbutton.m -extern BOOL uiprivFontButtonInhibitSendAction(SEL sel, id from, id to); -extern BOOL uiprivFontButtonOverrideTargetForAction(SEL sel, id from, id to, id *override); -extern void uiprivSetupFontPanel(void); - -// colorbutton.m -extern BOOL colorButtonInhibitSendAction(SEL sel, id from, id to); - // scrollview.m struct scrollViewCreateParams { NSView *DocumentView; diff --git a/darwin/area.m b/darwin/area.m index 5705c896..f4c2b897 100644 --- a/darwin/area.m +++ b/darwin/area.m @@ -57,7 +57,7 @@ struct uiArea { c = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort]; // see draw.m under text for why we need the height - dp.Context = newContext(c, [self bounds].size.height); + dp.Context = uiprivDrawNewContext(c, [self bounds].size.height); dp.AreaWidth = 0; dp.AreaHeight = 0; @@ -74,7 +74,7 @@ struct uiArea { // no need to save or restore the graphics state to reset transformations; Cocoa creates a brand-new context each time (*(a->ah->Draw))(a->ah, a, &dp); - freeContext(dp.Context); + uiprivDrawFreeContext(dp.Context); } - (BOOL)isFlipped diff --git a/darwin/colorbutton.m b/darwin/colorbutton.m index 83b61571..f2bee775 100644 --- a/darwin/colorbutton.m +++ b/darwin/colorbutton.m @@ -117,7 +117,7 @@ uiDarwinControlAllDefaults(uiColorButton, button) // we do not want color change events to be sent to any controls other than the color buttons // see main.m for more details -BOOL colorButtonInhibitSendAction(SEL sel, id from, id to) +BOOL uiprivColorButtonInhibitSendAction(SEL sel, id from, id to) { if (sel != @selector(changeColor:)) return NO; diff --git a/darwin/draw.m b/darwin/draw.m index cf7d8f13..e54ecdd4 100644 --- a/darwin/draw.m +++ b/darwin/draw.m @@ -104,7 +104,7 @@ void uiDrawPathEnd(uiDrawPath *p) p->ended = TRUE; } -uiDrawContext *newContext(CGContextRef ctxt, CGFloat height) +uiDrawContext *uiprivDrawNewContext(CGContextRef ctxt, CGFloat height) { uiDrawContext *c; @@ -114,7 +114,7 @@ uiDrawContext *newContext(CGContextRef ctxt, CGFloat height) return c; } -void freeContext(uiDrawContext *c) +void uiprivDrawFreeContext(uiDrawContext *c) { uiprivFree(c); } diff --git a/darwin/main.m b/darwin/main.m index 4c930dae..bf40e61e 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -25,7 +25,7 @@ static BOOL stepsIsRunning; // it turns out NSFontManager also sends changeFont: through this; let's inhibit that here too (see fontbutton.m) - (BOOL)sendAction:(SEL)sel to:(id)to from:(id)from { - if (colorButtonInhibitSendAction(sel, from, to)) + if (uiprivColorButtonInhibitSendAction(sel, from, to)) return NO; if (uiprivFontButtonInhibitSendAction(sel, from, to)) return NO; diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 1cd88dbd..a41c0135 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -110,4 +110,16 @@ extern int uiprivSendAreaEvents(NSEvent *); extern BOOL uiprivFromKeycode(unsigned short keycode, uiAreaKeyEvent *ke); extern BOOL uiprivKeycodeModifier(unsigned short keycode, uiModifiers *mod); +// draw.m +extern uiDrawContext *uiprivDrawNewContext(CGContextRef, CGFloat); +extern void uiprivDrawFreeContext(uiDrawContext *); + +// fontbutton.m +extern BOOL uiprivFontButtonInhibitSendAction(SEL sel, id from, id to); +extern BOOL uiprivFontButtonOverrideTargetForAction(SEL sel, id from, id to, id *override); +extern void uiprivSetupFontPanel(void); + +// colorbutton.m +extern BOOL uiprivColorButtonInhibitSendAction(SEL sel, id from, id to); + #import "OLD_uipriv_darwin.h" From 69922a0fb378adeaf5dcacec3819bc623547a46d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 5 May 2018 21:21:44 -0400 Subject: [PATCH 1034/1329] Migrated shared scrollview.m types and functions. --- darwin/OLD_uipriv_darwin.h | 13 ------------- darwin/area.m | 10 +++++----- darwin/multilineentry.m | 10 +++++----- darwin/scrollview.m | 14 +++++++------- darwin/uipriv_darwin.h | 16 ++++++++++++++++ 5 files changed, 33 insertions(+), 30 deletions(-) diff --git a/darwin/OLD_uipriv_darwin.h b/darwin/OLD_uipriv_darwin.h index 299db15b..2cb8d640 100644 --- a/darwin/OLD_uipriv_darwin.h +++ b/darwin/OLD_uipriv_darwin.h @@ -1,16 +1,3 @@ -// scrollview.m -struct scrollViewCreateParams { - NSView *DocumentView; - NSColor *BackgroundColor; - BOOL DrawsBackground; - BOOL Bordered; - BOOL HScroll; - BOOL VScroll; -}; -struct scrollViewData; -extern NSScrollView *mkScrollView(struct scrollViewCreateParams *p, struct scrollViewData **dout); -extern void scrollViewSetScrolling(NSScrollView *sv, struct scrollViewData *d, BOOL hscroll, BOOL vscroll); -extern void scrollViewFreeData(NSScrollView *sv, struct scrollViewData *d); // label.m extern NSTextField *newLabel(NSString *str); diff --git a/darwin/area.m b/darwin/area.m index f4c2b897..d8786f4f 100644 --- a/darwin/area.m +++ b/darwin/area.m @@ -29,7 +29,7 @@ struct uiArea { NSView *view; // either sv or area depending on whether it is scrolling NSScrollView *sv; areaView *area; - struct scrollViewData *d; + uiprivScrollViewData *d; uiAreaHandler *ah; BOOL scrolling; NSEvent *dragevent; @@ -350,7 +350,7 @@ static void uiAreaDestroy(uiControl *c) uiArea *a = uiArea(c); if (a->scrolling) - scrollViewFreeData(a->sv, a->d); + uiprivScrollViewFreeData(a->sv, a->d); [a->area release]; if (a->scrolling) [a->sv release]; @@ -450,7 +450,7 @@ uiArea *uiNewArea(uiAreaHandler *ah) uiArea *uiNewScrollingArea(uiAreaHandler *ah, int width, int height) { uiArea *a; - struct scrollViewCreateParams p; + uiprivScrollViewCreateParams p; uiDarwinNewControl(uiArea, a); @@ -460,14 +460,14 @@ uiArea *uiNewScrollingArea(uiAreaHandler *ah, int width, int height) a->area = [[areaView alloc] initWithFrame:NSMakeRect(0, 0, width, height) area:a]; - memset(&p, 0, sizeof (struct scrollViewCreateParams)); + memset(&p, 0, sizeof (uiprivScrollViewCreateParams)); p.DocumentView = a->area; p.BackgroundColor = [NSColor controlColor]; p.DrawsBackground = 1; p.Bordered = NO; p.HScroll = YES; p.VScroll = YES; - a->sv = mkScrollView(&p, &(a->d)); + a->sv = uiprivMkScrollView(&p, &(a->d)); a->view = a->sv; diff --git a/darwin/multilineentry.m b/darwin/multilineentry.m index 901835c3..d57284a0 100644 --- a/darwin/multilineentry.m +++ b/darwin/multilineentry.m @@ -14,7 +14,7 @@ struct uiMultilineEntry { uiDarwinControl c; NSScrollView *sv; intrinsicSizeTextView *tv; - struct scrollViewData *d; + uiprivScrollViewData *d; void (*onChanged)(uiMultilineEntry *, void *); void *onChangedData; BOOL changing; @@ -59,7 +59,7 @@ static void uiMultilineEntryDestroy(uiControl *c) { uiMultilineEntry *e = uiMultilineEntry(c); - scrollViewFreeData(e->sv, e->d); + uiprivScrollViewFreeData(e->sv, e->d); [e->tv release]; [e->sv release]; uiFreeControl(uiControl(e)); @@ -120,7 +120,7 @@ static uiMultilineEntry *finishMultilineEntry(BOOL hscroll) { uiMultilineEntry *e; NSFont *font; - struct scrollViewCreateParams p; + uiprivScrollViewCreateParams p; uiDarwinNewControl(uiMultilineEntry, e); @@ -207,7 +207,7 @@ static uiMultilineEntry *finishMultilineEntry(BOOL hscroll) // let's just set it to the standard control font anyway, just to be safe [e->tv setFont:font]; - memset(&p, 0, sizeof (struct scrollViewCreateParams)); + memset(&p, 0, sizeof (uiprivScrollViewCreateParams)); p.DocumentView = e->tv; // this is what Interface Builder sets it to p.BackgroundColor = [NSColor colorWithCalibratedWhite:1.0 alpha:1.0]; @@ -215,7 +215,7 @@ static uiMultilineEntry *finishMultilineEntry(BOOL hscroll) p.Bordered = YES; p.HScroll = hscroll; p.VScroll = YES; - e->sv = mkScrollView(&p, &(e->d)); + e->sv = uiprivMkScrollView(&p, &(e->d)); uiMultilineEntryOnChanged(e, defaultOnChanged, NULL); diff --git a/darwin/scrollview.m b/darwin/scrollview.m index b583a00f..1b5cc8d9 100644 --- a/darwin/scrollview.m +++ b/darwin/scrollview.m @@ -4,16 +4,16 @@ // see http://stackoverflow.com/questions/37979445/how-do-i-properly-set-up-a-scrolling-nstableview-using-auto-layout-what-ive-tr for why we don't use auto layout // TODO do the same with uiGroup and uiTab? -struct scrollViewData { +struct uiprivScrollViewData { BOOL hscroll; BOOL vscroll; }; -NSScrollView *mkScrollView(struct scrollViewCreateParams *p, struct scrollViewData **dout) +NSScrollView *uiprivMkScrollView(uiprivScrollViewCreateParams *p, uiprivScrollViewData **dout) { NSScrollView *sv; NSBorderType border; - struct scrollViewData *d; + uiprivScrollViewData *d; sv = [[NSScrollView alloc] initWithFrame:NSZeroRect]; if (p->BackgroundColor != nil) @@ -39,15 +39,15 @@ NSScrollView *mkScrollView(struct scrollViewCreateParams *p, struct scrollViewDa [sv setAllowsMagnification:NO]; [sv setDocumentView:p->DocumentView]; - d = uiprivNew(struct scrollViewData); - scrollViewSetScrolling(sv, d, p->HScroll, p->VScroll); + d = uiprivNew(uiprivScrollViewData); + uiprivScrollViewSetScrolling(sv, d, p->HScroll, p->VScroll); *dout = d; return sv; } // based on http://blog.bjhomer.com/2014/08/nsscrollview-and-autolayout.html because (as pointed out there) Apple's official guide is really only for iOS -void scrollViewSetScrolling(NSScrollView *sv, struct scrollViewData *d, BOOL hscroll, BOOL vscroll) +void uiprivScrollViewSetScrolling(NSScrollView *sv, uiprivScrollViewData *d, BOOL hscroll, BOOL vscroll) { d->hscroll = hscroll; [sv setHasHorizontalScroller:d->hscroll]; @@ -55,7 +55,7 @@ void scrollViewSetScrolling(NSScrollView *sv, struct scrollViewData *d, BOOL hsc [sv setHasVerticalScroller:d->vscroll]; } -void scrollViewFreeData(NSScrollView *sv, struct scrollViewData *d) +void uiprivScrollViewFreeData(NSScrollView *sv, uiprivScrollViewData *d) { uiprivFree(d); } diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index a41c0135..32dfd42a 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -122,4 +122,20 @@ extern void uiprivSetupFontPanel(void); // colorbutton.m extern BOOL uiprivColorButtonInhibitSendAction(SEL sel, id from, id to); +// scrollview.m +typedef struct uiprivScrollViewCreateParams uiprivScrollViewCreateParams; +struct uiprivScrollViewCreateParams { + // TODO MAYBE fix these identifiers + NSView *DocumentView; + NSColor *BackgroundColor; + BOOL DrawsBackground; + BOOL Bordered; + BOOL HScroll; + BOOL VScroll; +}; +typedef struct uiprivScrollViewData uiprivScrollViewData; +extern NSScrollView *uiprivMkScrollView(uiprivScrollViewCreateParams *p, uiprivScrollViewData **dout); +extern void uiprivScrollViewSetScrolling(NSScrollView *sv, uiprivScrollViewData *d, BOOL hscroll, BOOL vscroll); +extern void uiprivScrollViewFreeData(NSScrollView *sv, uiprivScrollViewData *d); + #import "OLD_uipriv_darwin.h" From 3914451c34f3f09ff737eb20609b01c1b90dee26 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 5 May 2018 21:28:13 -0400 Subject: [PATCH 1035/1329] Migrated newLabel(), imageImage(), doManualMove(), and doManualResize(). --- darwin/OLD_uipriv_darwin.h | 10 ---------- darwin/form.m | 2 +- darwin/image.m | 2 +- darwin/label.m | 2 +- darwin/uipriv_darwin.h | 13 +++++++++++++ darwin/window.m | 4 ++-- darwin/winmoveresize.m | 4 ++-- 7 files changed, 20 insertions(+), 17 deletions(-) diff --git a/darwin/OLD_uipriv_darwin.h b/darwin/OLD_uipriv_darwin.h index 2cb8d640..5a9ef4ce 100644 --- a/darwin/OLD_uipriv_darwin.h +++ b/darwin/OLD_uipriv_darwin.h @@ -1,14 +1,4 @@ -// label.m -extern NSTextField *newLabel(NSString *str); - -// image.m -extern NSImage *imageImage(uiImage *); - -// winmoveresize.m -extern void doManualMove(NSWindow *w, NSEvent *initialEvent); -extern void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdge edge); - // future.m extern CFStringRef *FUTURE_kCTFontOpenTypeFeatureTag; extern CFStringRef *FUTURE_kCTFontOpenTypeFeatureValue; diff --git a/darwin/form.m b/darwin/form.m index 148dd17e..af50e363 100644 --- a/darwin/form.m +++ b/darwin/form.m @@ -347,7 +347,7 @@ struct uiForm { NSLayoutAttribute attribute; int oldnStretchy; - fc = [[formChild alloc] initWithLabel:newLabel(label)]; + fc = [[formChild alloc] initWithLabel:uiprivNewLabel(label)]; fc.c = c; fc.stretchy = stretchy; fc.oldHorzHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(fc.c), NSLayoutConstraintOrientationHorizontal); diff --git a/darwin/image.m b/darwin/image.m index aa1b945b..ae5be6d9 100644 --- a/darwin/image.m +++ b/darwin/image.m @@ -76,7 +76,7 @@ void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, in [i->swizzled addObject:[NSValue valueWithPointer:swizzled]]; } -NSImage *imageImage(uiImage *i) +NSImage *uiprivImageNSImage(uiImage *i) { return i->i; } diff --git a/darwin/label.m b/darwin/label.m index c5dde802..2e2772dc 100644 --- a/darwin/label.m +++ b/darwin/label.m @@ -18,7 +18,7 @@ void uiLabelSetText(uiLabel *l, const char *text) [l->textfield setStringValue:uiprivToNSString(text)]; } -NSTextField *newLabel(NSString *str) +NSTextField *uiprivNewLabel(NSString *str) { NSTextField *tf; diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 32dfd42a..363f22c7 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -8,6 +8,9 @@ #import "../ui_darwin.h" #import "../common/uipriv.h" +// TODO should we rename the uiprivMk things or not +// TODO what about renaming class wrapper functions that return the underlying class (like uiprivNewLabel()) + #if __has_feature(objc_arc) #error Sorry, libui cannot be compiled with ARC. #endif @@ -138,4 +141,14 @@ extern NSScrollView *uiprivMkScrollView(uiprivScrollViewCreateParams *p, uiprivS extern void uiprivScrollViewSetScrolling(NSScrollView *sv, uiprivScrollViewData *d, BOOL hscroll, BOOL vscroll); extern void uiprivScrollViewFreeData(NSScrollView *sv, uiprivScrollViewData *d); +// label.m +extern NSTextField *uiprivNewLabel(NSString *str); + +// image.m +extern NSImage *uiprivImageNSImage(uiImage *); + +// winmoveresize.m +extern void uiprivDoManualMove(NSWindow *w, NSEvent *initialEvent); +extern void uiprivDoManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdge edge); + #import "OLD_uipriv_darwin.h" diff --git a/darwin/window.m b/darwin/window.m index 9ca8dd9f..1a048207 100644 --- a/darwin/window.m +++ b/darwin/window.m @@ -22,12 +22,12 @@ struct uiWindow { - (void)uiprivDoMove:(NSEvent *)initialEvent { - doManualMove(self, initialEvent); + uiprivDoManualMove(self, initialEvent); } - (void)uiprivDoResize:(NSEvent *)initialEvent on:(uiWindowResizeEdge)edge { - doManualResize(self, initialEvent, edge); + uiprivDoManualResize(self, initialEvent, edge); } @end diff --git a/darwin/winmoveresize.m b/darwin/winmoveresize.m index 671ad203..4ea98dca 100644 --- a/darwin/winmoveresize.m +++ b/darwin/winmoveresize.m @@ -43,7 +43,7 @@ void onMoveDrag(struct onMoveDragParams *p, NSEvent *e) [p->w setFrameOrigin:frame.origin]; } -void doManualMove(NSWindow *w, NSEvent *initialEvent) +void uiprivDoManualMove(NSWindow *w, NSEvent *initialEvent) { __block struct onMoveDragParams mdp; uiprivNextEventArgs nea; @@ -220,7 +220,7 @@ static void onResizeDrag(struct onResizeDragParams *p, NSEvent *e) } // TODO do our events get fired with this? *should* they? -void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdge edge) +void uiprivDoManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdge edge) { __block struct onResizeDragParams rdp; uiprivNextEventArgs nea; From fdff9b2dbbb6ee8edb22d33366d31ad73552ba6b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 5 May 2018 22:02:25 -0400 Subject: [PATCH 1036/1329] Renamed future and undocumented names. Now for draw.h (and whatever other headers are left behind), and then we figure out what to do about other names (including specifically pinning a naming convention). --- darwin/OLD_uipriv_darwin.h | 13 ------------- darwin/attrstr.m | 4 ++-- darwin/autolayout.m | 2 +- darwin/fontmatch.m | 4 ++-- darwin/future.m | 18 +++++++++--------- darwin/label.m | 2 +- darwin/main.m | 4 ++-- darwin/opentype.m | 6 +++--- darwin/uipriv_darwin.h | 13 ++++++++++++- darwin/undocumented.m | 10 +++++----- darwin/winmoveresize.m | 2 +- 11 files changed, 38 insertions(+), 40 deletions(-) delete mode 100644 darwin/OLD_uipriv_darwin.h diff --git a/darwin/OLD_uipriv_darwin.h b/darwin/OLD_uipriv_darwin.h deleted file mode 100644 index 5a9ef4ce..00000000 --- a/darwin/OLD_uipriv_darwin.h +++ /dev/null @@ -1,13 +0,0 @@ - -// future.m -extern CFStringRef *FUTURE_kCTFontOpenTypeFeatureTag; -extern CFStringRef *FUTURE_kCTFontOpenTypeFeatureValue; -extern CFStringRef *FUTURE_kCTBackgroundColorAttributeName; -extern void loadFutures(void); -extern void FUTURE_NSLayoutConstraint_setIdentifier(NSLayoutConstraint *constraint, NSString *identifier); -extern BOOL FUTURE_NSWindow_performWindowDragWithEvent(NSWindow *w, NSEvent *initialEvent); - -// undocumented.m -extern CFStringRef UNDOC_kCTFontPreferredSubFamilyNameKey; -extern CFStringRef UNDOC_kCTFontPreferredFamilyNameKey; -extern void loadUndocumented(void); diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 7d62bcfe..36a180be 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -296,14 +296,14 @@ static void addBackgroundAttribute(struct foreachParams *p, size_t start, size_t uiprivDrawTextBackgroundParams *dtb; // TODO make sure this works properly with line paragraph spacings (after figuring out what that means, of course) - if (FUTURE_kCTBackgroundColorAttributeName != NULL) { + if (uiprivFUTURE_kCTBackgroundColorAttributeName != NULL) { CGColorRef color; CFRange range; color = mkcolor(r, g, b, a); range.location = start; range.length = end - start; - CFAttributedStringSetAttribute(p->mas, range, *FUTURE_kCTBackgroundColorAttributeName, color); + CFAttributedStringSetAttribute(p->mas, range, *uiprivFUTURE_kCTBackgroundColorAttributeName, color); CFRelease(color); return; } diff --git a/darwin/autolayout.m b/darwin/autolayout.m index a4e4395e..6bc5ce84 100644 --- a/darwin/autolayout.m +++ b/darwin/autolayout.m @@ -12,7 +12,7 @@ NSLayoutConstraint *uiprivMkConstraint(id view1, NSLayoutAttribute attr1, NSLayo attribute:attr2 multiplier:multiplier constant:c]; - FUTURE_NSLayoutConstraint_setIdentifier(constraint, desc); + uiprivFUTURE_NSLayoutConstraint_setIdentifier(constraint, desc); return constraint; } diff --git a/darwin/fontmatch.m b/darwin/fontmatch.m index 2c240b95..6daa1e8d 100644 --- a/darwin/fontmatch.m +++ b/darwin/fontmatch.m @@ -246,7 +246,7 @@ FONTNAME(preferredSubFamilyName, self->didPreferredSubFamilyName, self->preferredSubFamilyName, - UNDOC_kCTFontPreferredSubFamilyNameKey) + uiprivUNDOC_kCTFontPreferredSubFamilyNameKey) FONTNAME(subFamilyName, self->didSubFamilyName, self->subFamilyName, @@ -258,7 +258,7 @@ FONTNAME(fullName, FONTNAME(preferredFamilyName, self->didPreferredFamilyName, self->preferredFamilyName, - UNDOC_kCTFontPreferredFamilyNameKey) + uiprivUNDOC_kCTFontPreferredFamilyNameKey) FONTNAME(familyName, self->didFamilyName, self->familyName, diff --git a/darwin/future.m b/darwin/future.m index a262d009..e6d05ef4 100644 --- a/darwin/future.m +++ b/darwin/future.m @@ -5,14 +5,14 @@ // note: for constants, dlsym() returns the address of the constant itself, as if we had done &constantName // added in OS X 10.10; we need 10.8 -CFStringRef *FUTURE_kCTFontOpenTypeFeatureTag = NULL; -CFStringRef *FUTURE_kCTFontOpenTypeFeatureValue = NULL; +CFStringRef *uiprivFUTURE_kCTFontOpenTypeFeatureTag = NULL; +CFStringRef *uiprivFUTURE_kCTFontOpenTypeFeatureValue = NULL; // added in OS X 10.12; we need 10.8 -CFStringRef *FUTURE_kCTBackgroundColorAttributeName = NULL; +CFStringRef *uiprivFUTURE_kCTBackgroundColorAttributeName = NULL; // note that we treat any error as "the symbols aren't there" (and don't care if dlclose() failed) -void loadFutures(void) +void uiprivLoadFutures(void) { void *handle; @@ -21,9 +21,9 @@ void loadFutures(void) if (handle == NULL) return; #define GET(var, fn) *((void **) (&var)) = dlsym(handle, #fn) - GET(FUTURE_kCTFontOpenTypeFeatureTag, kCTFontOpenTypeFeatureTag); - GET(FUTURE_kCTFontOpenTypeFeatureValue, kCTFontOpenTypeFeatureValue); - GET(FUTURE_kCTBackgroundColorAttributeName, kCTBackgroundColorAttributeName); + GET(uiprivFUTURE_kCTFontOpenTypeFeatureTag, kCTFontOpenTypeFeatureTag); + GET(uiprivFUTURE_kCTFontOpenTypeFeatureValue, kCTFontOpenTypeFeatureValue); + GET(uiprivFUTURE_kCTBackgroundColorAttributeName, kCTBackgroundColorAttributeName); dlclose(handle); } @@ -31,7 +31,7 @@ void loadFutures(void) // keep them in one place for convenience // apparently only added in 10.9; we need 10.8 -void FUTURE_NSLayoutConstraint_setIdentifier(NSLayoutConstraint *constraint, NSString *identifier) +void uiprivFUTURE_NSLayoutConstraint_setIdentifier(NSLayoutConstraint *constraint, NSString *identifier) { id cid = (id) constraint; @@ -41,7 +41,7 @@ void FUTURE_NSLayoutConstraint_setIdentifier(NSLayoutConstraint *constraint, NSS // added in 10.11; we need 10.8 // return whether this was done because we recreate its effects if not (see winmoveresize.m) -BOOL FUTURE_NSWindow_performWindowDragWithEvent(NSWindow *w, NSEvent *initialEvent) +BOOL uiprivFUTURE_NSWindow_performWindowDragWithEvent(NSWindow *w, NSEvent *initialEvent) { id cw = (id) w; diff --git a/darwin/label.m b/darwin/label.m index 2e2772dc..942b153c 100644 --- a/darwin/label.m +++ b/darwin/label.m @@ -37,7 +37,7 @@ uiLabel *uiNewLabel(const char *text) uiDarwinNewControl(uiLabel, l); - l->textfield = newLabel(uiprivToNSString(text)); + l->textfield = uiprivNewLabel(uiprivToNSString(text)); return l; } diff --git a/darwin/main.m b/darwin/main.m index bf40e61e..f7790b01 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -120,8 +120,8 @@ const char *uiInit(uiInitOptions *o) [uiprivNSApp() setDelegate:delegate]; uiprivInitAlloc(); - loadFutures(); - loadUndocumented(); + uiprivLoadFutures(); + uiprivLoadUndocumented(); // always do this so we always have an application menu uiprivAppDelegate().menuManager = [[uiprivMenuManager new] autorelease]; diff --git a/darwin/opentype.m b/darwin/opentype.m index 84dbde36..be12917f 100644 --- a/darwin/opentype.m +++ b/darwin/opentype.m @@ -74,7 +74,7 @@ static uiForEach otfArrayForEachOT(const uiOpenTypeFeatures *otf, char a, char b p.array = (CFMutableArrayRef) data; - p.tagKey = *FUTURE_kCTFontOpenTypeFeatureTag; + p.tagKey = *uiprivFUTURE_kCTFontOpenTypeFeatureTag; p.tagIsNumber = NO; tagcstr[0] = a; tagcstr[1] = b; @@ -87,7 +87,7 @@ static uiForEach otfArrayForEachOT(const uiOpenTypeFeatures *otf, char a, char b } p.tagValue = tagstr; - p.valueKey = *FUTURE_kCTFontOpenTypeFeatureValue; + p.valueKey = *uiprivFUTURE_kCTFontOpenTypeFeatureValue; p.valueType = kCFNumberSInt32Type; p.valueValue = (const SInt32 *) (&value); addCTFeatureEntry(&p); @@ -106,7 +106,7 @@ CFArrayRef uiprivOpenTypeFeaturesToCTFeatures(const uiOpenTypeFeatures *otf) // TODO } f = otfArrayForEachAAT; - if (FUTURE_kCTFontOpenTypeFeatureTag != NULL && FUTURE_kCTFontOpenTypeFeatureValue != NULL) + if (uiprivFUTURE_kCTFontOpenTypeFeatureTag != NULL && uiprivFUTURE_kCTFontOpenTypeFeatureValue != NULL) f = otfArrayForEachOT; uiOpenTypeFeaturesForEach(otf, f, array); return array; diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index 363f22c7..bc8b1837 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -151,4 +151,15 @@ extern NSImage *uiprivImageNSImage(uiImage *); extern void uiprivDoManualMove(NSWindow *w, NSEvent *initialEvent); extern void uiprivDoManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdge edge); -#import "OLD_uipriv_darwin.h" +// future.m +extern CFStringRef *uiprivFUTURE_kCTFontOpenTypeFeatureTag; +extern CFStringRef *uiprivFUTURE_kCTFontOpenTypeFeatureValue; +extern CFStringRef *uiprivFUTURE_kCTBackgroundColorAttributeName; +extern void uiprivLoadFutures(void); +extern void uiprivFUTURE_NSLayoutConstraint_setIdentifier(NSLayoutConstraint *constraint, NSString *identifier); +extern BOOL uiprivFUTURE_NSWindow_performWindowDragWithEvent(NSWindow *w, NSEvent *initialEvent); + +// undocumented.m +extern CFStringRef uiprivUNDOC_kCTFontPreferredSubFamilyNameKey; +extern CFStringRef uiprivUNDOC_kCTFontPreferredFamilyNameKey; +extern void uiprivLoadUndocumented(void); diff --git a/darwin/undocumented.m b/darwin/undocumented.m index 0e016dd6..a3984aa8 100644 --- a/darwin/undocumented.m +++ b/darwin/undocumented.m @@ -6,11 +6,11 @@ // we also provide default values just in case // these values come from 10.12.6 -CFStringRef UNDOC_kCTFontPreferredSubFamilyNameKey = CFSTR("CTFontPreferredSubFamilyName"); -CFStringRef UNDOC_kCTFontPreferredFamilyNameKey = CFSTR("CTFontPreferredFamilyName"); +CFStringRef uiprivUNDOC_kCTFontPreferredSubFamilyNameKey = CFSTR("CTFontPreferredSubFamilyName"); +CFStringRef uiprivUNDOC_kCTFontPreferredFamilyNameKey = CFSTR("CTFontPreferredFamilyName"); // note that we treat any error as "the symbols aren't there" (and don't care if dlclose() failed) -void loadUndocumented(void) +void uiprivLoadUndocumented(void) { void *handle; CFStringRef *str; @@ -23,9 +23,9 @@ void loadUndocumented(void) GET(str, kCTFontPreferredSubFamilyNameKey); NSLog(@"get %p", str); if (str != NULL) - UNDOC_kCTFontPreferredSubFamilyNameKey = *str; + uiprivUNDOC_kCTFontPreferredSubFamilyNameKey = *str; GET(str, kCTFontPreferredFamilyNameKey); if (str != NULL) - UNDOC_kCTFontPreferredFamilyNameKey = *str; + uiprivUNDOC_kCTFontPreferredFamilyNameKey = *str; dlclose(handle); } diff --git a/darwin/winmoveresize.m b/darwin/winmoveresize.m index 4ea98dca..efb61eae 100644 --- a/darwin/winmoveresize.m +++ b/darwin/winmoveresize.m @@ -52,7 +52,7 @@ void uiprivDoManualMove(NSWindow *w, NSEvent *initialEvent) // 10.11 gives us a method to handle this for us // use it if available; this lets us use the real OS dragging code, which means we can take advantage of OS features like Spaces - if (FUTURE_NSWindow_performWindowDragWithEvent(w, initialEvent)) + if (uiprivFUTURE_NSWindow_performWindowDragWithEvent(w, initialEvent)) return; mdp.w = w; From 98fe8736b590ceea1683a92b50f0e8f8c4fe8cf0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 5 May 2018 22:14:29 -0400 Subject: [PATCH 1037/1329] ...draw.h has nothing, so more TODOs. Now for investigating each file in turn. --- darwin/draw.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/darwin/draw.h b/darwin/draw.h index 41629809..382b7e77 100644 --- a/darwin/draw.h +++ b/darwin/draw.h @@ -1,5 +1,7 @@ // 6 january 2017 +// TODO why do we still have this file; should we just split draw.m or not + struct uiDrawContext { CGContextRef c; CGFloat height; // needed for text; see below From fb60d5860e68d89954f1aa5747892da73417752f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 6 May 2018 11:27:43 -0400 Subject: [PATCH 1038/1329] Started pinning down reserved names. This is awkward... --- doc/names.md | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 doc/names.md diff --git a/doc/names.md b/doc/names.md new file mode 100644 index 00000000..f096534e --- /dev/null +++ b/doc/names.md @@ -0,0 +1,45 @@ +TODO find out specifics of -fvisibility=hidden in static libs, which is the point of this + +In general, all names that begin with "ui" and are followed by a capital letter and all names htat begin with "uipriv" and are followed by a capita lletter are reserved by libui. This applies even in C++, where name mangling may affect the actual names in the object file. + +Within libui itself, the following rules apply: + +C + +C++ + +# Objective-C + +Objective-C rules amend the C rules with the following. + +libui should not expose any Objective-C classes or protocols of its own in the public API. This may be changed in the future; in the meantime, this section implies the `uipriv` prefix. + +All class and protocol names must be prefixed, since there is only one namespace for these. This includes file-scope classes. + +The following set of name prefixes are also reserved in addition to the regular `uipriv` prefix, each for different reasons; the reasons (and by extension, the correct usage) are described below. + +- `initWithUipriv` +- `initWithFrame:uipriv` +- `isUipriv` +- `setUipriv` +- `_uipriv` + +Each of these prefixes is also followed by an uppercase letter. + +If an `init` method requires a custom method name (that is, it can't just use one of the superclass's), it should prefix its first name part with `initWithUipriv` instead of just `initWIth`. For instance, instead of + +In the case of NSView subclasses, libui usually passes NSZeroRect as the initial frame, so if you need a custom init function, you could get away with embedding that directly in the `[super initWithFrame:]` call. (TODO should this rule be removed instead?) However, the `initWithFrame:uipriv` prefix is reserved it his is not sutable. (TODO maybe this isn't a good idea either...) + +Examples: + +```objective-c +// instead of... +- (id)initWithThing:(Thing *)thing measure:(int)value; +- (id)initWithFrame:(NSRect)r thing:(Thing *)thing; + +// ...use +- (id)initWithUiprivThing:(Thing *)thing measure:(int)value; +- (id)initWithFrame:(NSRect)r uiprivThing:(Thing *)thing; +``` + +GObject From 1e5f1b8254adbfc0f2915ed0d184e29d5183a1fa Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 6 May 2018 14:56:38 -0400 Subject: [PATCH 1039/1329] More name stuff. Argh. --- doc/names.md | 46 ++++++++++++++++++++-------------------------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/doc/names.md b/doc/names.md index f096534e..449e34e2 100644 --- a/doc/names.md +++ b/doc/names.md @@ -2,44 +2,38 @@ TODO find out specifics of -fvisibility=hidden in static libs, which is the poin In general, all names that begin with "ui" and are followed by a capital letter and all names htat begin with "uipriv" and are followed by a capita lletter are reserved by libui. This applies even in C++, where name mangling may affect the actual names in the object file. -Within libui itself, the following rules apply: +# Reserved names; for users -C +All reserved names in libui are defined by a prefix followed by any uppercase letter in ASCII. The bullet lists before list those prefixes. -C++ +Global-scope identifiers of any form (variables, constant names, functions, structure names, union names, C++ class names, enum type names, enum value names, C++ namespace names, GObject class and interface struct names, and Objective-C class and protocol name identifiers) and macro names: -# Objective-C +- `ui` +- `uipriv` -Objective-C rules amend the C rules with the following. +GObject and Objective-C class, interface, and protocol name strings, in the form they take in their respective runtime memory (e.g. when passed to `g_type_from_name()` and `NSClassFromString()`, respectively): -libui should not expose any Objective-C classes or protocols of its own in the public API. This may be changed in the future; in the meantime, this section implies the `uipriv` prefix. +- `uipriv` -All class and protocol names must be prefixed, since there is only one namespace for these. This includes file-scope classes. - -The following set of name prefixes are also reserved in addition to the regular `uipriv` prefix, each for different reasons; the reasons (and by extension, the correct usage) are described below. +Objective-C method names: - `initWithUipriv` -- `initWithFrame:uipriv` -- `isUipriv` -- `setUipriv` -- `_uipriv` +- `initWithFrame:uipriv` (TODO probably worth removing) +- `uipriv` +- `isUipriv` (for compatibility with KVO and `@property` statements) +- `setUipriv` (for compatibility with KVO and `@property` statements) -Each of these prefixes is also followed by an uppercase letter. +Objective-C ivar names: -If an `init` method requires a custom method name (that is, it can't just use one of the superclass's), it should prefix its first name part with `initWithUipriv` instead of just `initWIth`. For instance, instead of +- `uipriv` +- `_uipriv` (for compatibility with KVO and `@property` statements) -In the case of NSView subclasses, libui usually passes NSZeroRect as the initial frame, so if you need a custom init function, you could get away with embedding that directly in the `[super initWithFrame:]` call. (TODO should this rule be removed instead?) However, the `initWithFrame:uipriv` prefix is reserved it his is not sutable. (TODO maybe this isn't a good idea either...) +Objective-C property names: -Examples: +- `uipriv` -```objective-c -// instead of... -- (id)initWithThing:(Thing *)thing measure:(int)value; -- (id)initWithFrame:(NSRect)r thing:(Thing *)thing; +TODO GObject macros (in libui's source code), properties, and signals -// ...use -- (id)initWithUiprivThing:(Thing *)thing measure:(int)value; -- (id)initWithFrame:(NSRect)r uiprivThing:(Thing *)thing; -``` +# Developer notes -GObject +TODO From 7be597f6748f07aa63cccf3ce2e32deba70cc9f0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 6 May 2018 19:32:56 -0400 Subject: [PATCH 1040/1329] TODO-ize names.md. I'm just going to merge this as-is, since I have not yet solidified the Obj-C rules yet. --- doc/names.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/names.md b/doc/names.md index 449e34e2..02036def 100644 --- a/doc/names.md +++ b/doc/names.md @@ -1,4 +1,6 @@ -TODO find out specifics of -fvisibility=hidden in static libs, which is the point of this +TODO clean this up + +TODO note that you -fvisibility=hidden means nothing in static libraries, hence this (confirmed on OS X) In general, all names that begin with "ui" and are followed by a capital letter and all names htat begin with "uipriv" and are followed by a capita lletter are reserved by libui. This applies even in C++, where name mangling may affect the actual names in the object file. From f1c4976fe08fdb20cf18ee04f9a24cdf6aafa261 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 6 May 2018 19:34:58 -0400 Subject: [PATCH 1041/1329] Oh right, I forgot to remove the static library fuckery from the darwin CMakeLists.txt. NOW MERGING. --- darwin/CMakeLists.txt | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/darwin/CMakeLists.txt b/darwin/CMakeLists.txt index d03d0f0d..6bf91a85 100644 --- a/darwin/CMakeLists.txt +++ b/darwin/CMakeLists.txt @@ -58,31 +58,8 @@ list(APPEND _LIBUI_INCLUDEDIRS set(_LIBUI_INCLUDEDIRS _LIBUI_INCLUDEDIRS PARENT_SCOPE) set(_LIBUINAME libui PARENT_SCOPE) -if(NOT BUILD_SHARED_LIBS) - set(_LIBUINAME libui-temporary PARENT_SCOPE) -endif() -# thanks to Mr-Hide in irc.freenode.net/#cmake -# TODO remove all these temporary files after linking the final archive file macro(_handle_static) - set_target_properties(${_LIBUINAME} PROPERTIES - ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}") - set(_aname $) - set(_lname libui-combined.list) - set(_oname libui-combined.o) - add_custom_command( - OUTPUT ${_oname} - COMMAND - nm -m ${_aname} | sed -E -n "'s/^[0-9a-f]* \\([A-Z_]+,[a-z_]+\\) external //p'" > ${_lname} - COMMAND - ld -exported_symbols_list ${_lname} -r -all_load ${_aname} -o ${_oname} - COMMENT "Removing hidden symbols") - add_library(libui STATIC ${_oname}) - # otherwise cmake won't know which linker to use - set_target_properties(libui PROPERTIES - LINKER_LANGUAGE C) - set(_aname) - set(_lname) - set(_oname) + # do nothing endmacro() set(_LIBUI_LIBS From dc98bc2c255efdf99d4c7c4e12f17457d4958445 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 6 May 2018 20:01:54 -0400 Subject: [PATCH 1042/1329] Avoid merge conflict in uipriv_darwin.h. --- darwin/uipriv_darwin.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index c221464f..afd97691 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -19,6 +19,15 @@ #define NSAppKitVersionNumber10_9 1265 #endif +// map.m +extern struct mapTable *newMap(void); +extern void mapDestroy(struct mapTable *m); +extern void *mapGet(struct mapTable *m, void *key); +extern void mapSet(struct mapTable *m, void *key, void *value); +extern void mapDelete(struct mapTable *m, void *key); +extern void mapWalk(struct mapTable *m, void (*f)(void *key, void *value)); +extern void mapReset(struct mapTable *m); + // menu.m @interface menuManager : NSObject { struct mapTable *items; @@ -88,15 +97,6 @@ extern void singleChildConstraintsEstablish(struct singleChildConstraints *c, NS extern void singleChildConstraintsRemove(struct singleChildConstraints *c, NSView *cv); extern void singleChildConstraintsSetMargined(struct singleChildConstraints *c, int margined); -// map.m -extern struct mapTable *newMap(void); -extern void mapDestroy(struct mapTable *m); -extern void *mapGet(struct mapTable *m, void *key); -extern void mapSet(struct mapTable *m, void *key, void *value); -extern void mapDelete(struct mapTable *m, void *key); -extern void mapWalk(struct mapTable *m, void (*f)(void *key, void *value)); -extern void mapReset(struct mapTable *m); - // area.m extern int sendAreaEvents(NSEvent *); From 221e8731c0fa370ed7c986817a3f78953afb5f47 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 6 May 2018 21:26:51 -0400 Subject: [PATCH 1043/1329] Revert "Avoid merge conflict in uipriv_darwin.h." Nope, this confused git further This reverts commit dc98bc2c255efdf99d4c7c4e12f17457d4958445. --- darwin/uipriv_darwin.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index afd97691..c221464f 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -19,15 +19,6 @@ #define NSAppKitVersionNumber10_9 1265 #endif -// map.m -extern struct mapTable *newMap(void); -extern void mapDestroy(struct mapTable *m); -extern void *mapGet(struct mapTable *m, void *key); -extern void mapSet(struct mapTable *m, void *key, void *value); -extern void mapDelete(struct mapTable *m, void *key); -extern void mapWalk(struct mapTable *m, void (*f)(void *key, void *value)); -extern void mapReset(struct mapTable *m); - // menu.m @interface menuManager : NSObject { struct mapTable *items; @@ -97,6 +88,15 @@ extern void singleChildConstraintsEstablish(struct singleChildConstraints *c, NS extern void singleChildConstraintsRemove(struct singleChildConstraints *c, NSView *cv); extern void singleChildConstraintsSetMargined(struct singleChildConstraints *c, int margined); +// map.m +extern struct mapTable *newMap(void); +extern void mapDestroy(struct mapTable *m); +extern void *mapGet(struct mapTable *m, void *key); +extern void mapSet(struct mapTable *m, void *key, void *value); +extern void mapDelete(struct mapTable *m, void *key); +extern void mapWalk(struct mapTable *m, void (*f)(void *key, void *value)); +extern void mapReset(struct mapTable *m); + // area.m extern int sendAreaEvents(NSEvent *); From 5ac579df35dda533bda110b3f4a3190f08aa310b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 6 May 2018 22:13:03 -0400 Subject: [PATCH 1044/1329] Fixed table.m to line up with the changes on master that have since been merged in. --- darwin/table.m | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/darwin/table.m b/darwin/table.m index 43822dfc..66666863 100644 --- a/darwin/table.m +++ b/darwin/table.m @@ -63,7 +63,7 @@ struct uiTable { uiDarwinControl c; NSScrollView *sv; tableView *tv; - struct scrollViewData *d; + uiprivScrollViewData *d; int backgroundColumn; }; @@ -111,7 +111,7 @@ struct uiTable { [v addSubview:view]; // TODO set [v imageView] and [v textField] as appropriate? if (prev == nil) { // first view - [v addConstraint:mkConstraint(v, NSLayoutAttributeLeading, + [v addConstraint:uiprivMkConstraint(v, NSLayoutAttributeLeading, NSLayoutRelationEqual, view, NSLayoutAttributeLeading, 1, -xleft, @@ -119,14 +119,14 @@ struct uiTable { prev = view; continue; } - [v addConstraint:mkConstraint(prev, NSLayoutAttributeTrailing, + [v addConstraint:uiprivMkConstraint(prev, NSLayoutAttributeTrailing, NSLayoutRelationEqual, view, NSLayoutAttributeLeading, 1, -xmiddle, @"uiTableColumn middle horizontal constraint")]; prev = view; } - [v addConstraint:mkConstraint(prev, NSLayoutAttributeTrailing, + [v addConstraint:uiprivMkConstraint(prev, NSLayoutAttributeTrailing, NSLayoutRelationEqual, v, NSLayoutAttributeTrailing, 1, -xright, @@ -134,14 +134,14 @@ struct uiTable { // and vertically for (view in views) { - [v addConstraint:mkConstraint(view, NSLayoutAttributeCenterY, + [v addConstraint:uiprivMkConstraint(view, NSLayoutAttributeCenterY, NSLayoutRelationEqual, v, NSLayoutAttributeCenterY, 1, 0, @"uiTableColumn part vertical constraint")]; // TODO avoid the need for this hack if ([view isKindOfClass:[NSImageView class]]) - [v addConstraint:mkConstraint(view, NSLayoutAttributeTop, + [v addConstraint:uiprivMkConstraint(view, NSLayoutAttributeTop, NSLayoutRelationEqual, v, NSLayoutAttributeTop, 1, 0, @@ -239,9 +239,9 @@ if(1); else switch (self.type) { case partText: data = (*(m->mh->CellValue))(m->mh, m, row, self.textColumn); - str = toNSString((char *) data); + str = uiprivToNSString((char *) data); uiprivFree(data); - tf = newLabel(str); + tf = uiprivNewLabel(str); // TODO set wrap and ellipsize modes? if (self.textColorColumn != -1) { NSColor *color; @@ -262,13 +262,13 @@ if(1); else case partImage: data = (*(m->mh->CellValue))(m->mh, m, row, self.imageColumn); iv = [[NSImageView alloc] initWithFrame:NSZeroRect]; - [iv setImage:imageImage((uiImage *) data)]; + [iv setImage:uiprivImageNSImage((uiImage *) data)]; [iv setImageFrameStyle:NSImageFrameNone]; [iv setImageAlignment:NSImageAlignCenter]; [iv setImageScaling:NSImageScaleProportionallyDown]; [iv setAnimates:NO]; [iv setEditable:NO]; - [iv addConstraint:mkConstraint(iv, NSLayoutAttributeWidth, + [iv addConstraint:uiprivMkConstraint(iv, NSLayoutAttributeWidth, NSLayoutRelationEqual, iv, NSLayoutAttributeHeight, 1, 0, @@ -279,7 +279,7 @@ if(1); else case partButton: // TODO buttons get clipped data = (*(m->mh->CellValue))(m->mh, m, row, self.textColumn); - str = toNSString((char *) data); + str = uiprivToNSString((char *) data); b = [[NSButton alloc] initWithFrame:NSZeroRect]; [b setTitle:str]; [b setButtonType:NSMomentaryPushInButton]; @@ -525,7 +525,7 @@ uiTableColumn *uiTableAppendColumn(uiTable *t, const char *name) // via Interface Builder [c->c setResizingMask:(NSTableColumnAutoresizingMask | NSTableColumnUserResizingMask)]; // 10.10 adds -[NSTableColumn setTitle:]; before then we have to do this - [[c->c headerCell] setStringValue:toNSString(name)]; + [[c->c headerCell] setStringValue:uiprivToNSString(name)]; // TODO is this sufficient? [[c->c headerCell] setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]]; c->parts = [NSMutableArray new]; @@ -541,7 +541,7 @@ void uiTableSetRowBackgroundColorModelColumn(uiTable *t, int modelColumn) uiTable *uiNewTable(uiTableModel *model) { uiTable *t; - struct scrollViewCreateParams p; + uiprivScrollViewCreateParams p; uiDarwinNewControl(uiTable, t); @@ -565,7 +565,7 @@ uiTable *uiNewTable(uiTableModel *model) [t->tv setAllowsTypeSelect:YES]; // TODO floatsGroupRows — do we even allow group rows? - memset(&p, 0, sizeof (struct scrollViewCreateParams)); + memset(&p, 0, sizeof (uiprivScrollViewCreateParams)); p.DocumentView = t->tv; // this is what Interface Builder sets it to // TODO verify @@ -574,7 +574,7 @@ uiTable *uiNewTable(uiTableModel *model) p.Bordered = YES; p.HScroll = YES; p.VScroll = YES; - t->sv = mkScrollView(&p, &(t->d)); + t->sv = uiprivMkScrollView(&p, &(t->d)); t->backgroundColumn = -1; From 1c83d674a520010a9c59cafa7cb51dd103cd92c9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 12 May 2018 11:20:11 -0400 Subject: [PATCH 1045/1329] Dummy commit to split uipriv_unix.h and move the old stuff out of the way first, to create this branch. --- unix/OLD_uipriv_unix.h | 45 +++++++++++++++++++++++++++++++++++++++++ unix/uipriv_unix.h | 46 +----------------------------------------- 2 files changed, 46 insertions(+), 45 deletions(-) create mode 100644 unix/OLD_uipriv_unix.h diff --git a/unix/OLD_uipriv_unix.h b/unix/OLD_uipriv_unix.h new file mode 100644 index 00000000..94f97fae --- /dev/null +++ b/unix/OLD_uipriv_unix.h @@ -0,0 +1,45 @@ +#define gtkXMargin 12 +#define gtkYMargin 12 +#define gtkXPadding 12 +#define gtkYPadding 6 + +// menu.c +extern GtkWidget *makeMenubar(uiWindow *); +extern void freeMenubar(GtkWidget *); +extern void uninitMenus(void); + +// alloc.c +extern void initAlloc(void); +extern void uninitAlloc(void); + +// util.c +extern void setMargined(GtkContainer *, int); + +// child.c +extern struct child *newChild(uiControl *child, uiControl *parent, GtkContainer *parentContainer); +extern struct child *newChildWithBox(uiControl *child, uiControl *parent, GtkContainer *parentContainer, int margined); +extern void childRemove(struct child *c); +extern void childDestroy(struct child *c); +extern GtkWidget *childWidget(struct child *c); +extern int childFlag(struct child *c); +extern void childSetFlag(struct child *c, int flag); +extern GtkWidget *childBox(struct child *c); +extern void childSetMargined(struct child *c, int margined); + +// draw.c +extern uiDrawContext *newContext(cairo_t *cr, GtkStyleContext *style); +extern void freeContext(uiDrawContext *); + +// image.c +/*TODO remove this*/typedef struct uiImage uiImage; +extern cairo_surface_t *imageAppropriateSurface(uiImage *i, GtkWidget *w); + +// cellrendererbutton.c +extern GtkCellRenderer *newCellRendererButton(void); + +// future.c +extern void loadFutures(void); +extern PangoAttribute *FUTURE_pango_attr_font_features_new(const gchar *features); +extern PangoAttribute *FUTURE_pango_attr_foreground_alpha_new(guint16 alpha); +extern PangoAttribute *FUTURE_pango_attr_background_alpha_new(guint16 alpha); +extern gboolean FUTURE_gtk_widget_path_iter_set_object_name(GtkWidgetPath *path, gint pos, const char *name); diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index 43ed144b..7d2a3e0e 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -14,48 +14,4 @@ #include "../ui_unix.h" #include "../common/uipriv.h" -#define gtkXMargin 12 -#define gtkYMargin 12 -#define gtkXPadding 12 -#define gtkYPadding 6 - -// menu.c -extern GtkWidget *makeMenubar(uiWindow *); -extern void freeMenubar(GtkWidget *); -extern void uninitMenus(void); - -// alloc.c -extern void initAlloc(void); -extern void uninitAlloc(void); - -// util.c -extern void setMargined(GtkContainer *, int); - -// child.c -extern struct child *newChild(uiControl *child, uiControl *parent, GtkContainer *parentContainer); -extern struct child *newChildWithBox(uiControl *child, uiControl *parent, GtkContainer *parentContainer, int margined); -extern void childRemove(struct child *c); -extern void childDestroy(struct child *c); -extern GtkWidget *childWidget(struct child *c); -extern int childFlag(struct child *c); -extern void childSetFlag(struct child *c, int flag); -extern GtkWidget *childBox(struct child *c); -extern void childSetMargined(struct child *c, int margined); - -// draw.c -extern uiDrawContext *newContext(cairo_t *cr, GtkStyleContext *style); -extern void freeContext(uiDrawContext *); - -// image.c -/*TODO remove this*/typedef struct uiImage uiImage; -extern cairo_surface_t *imageAppropriateSurface(uiImage *i, GtkWidget *w); - -// cellrendererbutton.c -extern GtkCellRenderer *newCellRendererButton(void); - -// future.c -extern void loadFutures(void); -extern PangoAttribute *FUTURE_pango_attr_font_features_new(const gchar *features); -extern PangoAttribute *FUTURE_pango_attr_foreground_alpha_new(guint16 alpha); -extern PangoAttribute *FUTURE_pango_attr_background_alpha_new(guint16 alpha); -extern gboolean FUTURE_gtk_widget_path_iter_set_object_name(GtkWidgetPath *path, gint pos, const char *name); +#include "OLD_uipriv_unix.h" From afaec644cc5e14bb6bedca884d041cb506a8f647 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 12 May 2018 12:47:21 -0400 Subject: [PATCH 1046/1329] Migrated the margin and padding constants and functions in menu.c, alloc.c, and util.c. --- unix/OLD_uipriv_unix.h | 14 -------------- unix/alloc.c | 4 ++-- unix/box.c | 4 ++-- unix/child.c | 2 +- unix/form.c | 4 ++-- unix/grid.c | 4 ++-- unix/main.c | 6 +++--- unix/menu.c | 6 +++--- unix/uipriv_unix.h | 17 +++++++++++++++++ unix/util.c | 4 ++-- unix/window.c | 6 +++--- 11 files changed, 37 insertions(+), 34 deletions(-) diff --git a/unix/OLD_uipriv_unix.h b/unix/OLD_uipriv_unix.h index 94f97fae..53305ae6 100644 --- a/unix/OLD_uipriv_unix.h +++ b/unix/OLD_uipriv_unix.h @@ -1,19 +1,5 @@ -#define gtkXMargin 12 -#define gtkYMargin 12 -#define gtkXPadding 12 -#define gtkYPadding 6 -// menu.c -extern GtkWidget *makeMenubar(uiWindow *); -extern void freeMenubar(GtkWidget *); -extern void uninitMenus(void); -// alloc.c -extern void initAlloc(void); -extern void uninitAlloc(void); - -// util.c -extern void setMargined(GtkContainer *, int); // child.c extern struct child *newChild(uiControl *child, uiControl *parent, GtkContainer *parentContainer); diff --git a/unix/alloc.c b/unix/alloc.c index 2fdd2052..3fa0fd41 100644 --- a/unix/alloc.c +++ b/unix/alloc.c @@ -13,7 +13,7 @@ static GPtrArray *allocations; #define CCHAR(p) ((const char **) (p)) #define TYPE(p) CCHAR(UINT8(p) + sizeof (size_t)) -void initAlloc(void) +void uiprivInitAlloc(void) { allocations = g_ptr_array_new(); } @@ -30,7 +30,7 @@ static void uninitComplain(gpointer ptr, gpointer data) *str = str2; } -void uninitAlloc(void) +void uiprivUninitAlloc(void) { char *str = NULL; diff --git a/unix/box.c b/unix/box.c index 23fb7f7c..82cf2dd1 100644 --- a/unix/box.c +++ b/unix/box.c @@ -119,9 +119,9 @@ void uiBoxSetPadded(uiBox *b, int padded) b->padded = padded; if (b->padded) if (b->vertical) - gtk_box_set_spacing(b->box, gtkYPadding); + gtk_box_set_spacing(b->box, uiprivGTKYPadding); else - gtk_box_set_spacing(b->box, gtkXPadding); + gtk_box_set_spacing(b->box, uiprivGTKXPadding); else gtk_box_set_spacing(b->box, 0); } diff --git a/unix/child.c b/unix/child.c index b6e48807..a0644c89 100644 --- a/unix/child.c +++ b/unix/child.c @@ -116,5 +116,5 @@ GtkWidget *childBox(struct child *c) void childSetMargined(struct child *c, int margined) { - setMargined(GTK_CONTAINER(c->box), margined); + uiprivSetMargined(GTK_CONTAINER(c->box), margined); } diff --git a/unix/form.c b/unix/form.c index 54422b3d..36ad77d5 100644 --- a/unix/form.c +++ b/unix/form.c @@ -133,8 +133,8 @@ void uiFormSetPadded(uiForm *f, int padded) { f->padded = padded; if (f->padded) { - gtk_grid_set_row_spacing(f->grid, gtkYPadding); - gtk_grid_set_column_spacing(f->grid, gtkXPadding); + gtk_grid_set_row_spacing(f->grid, uiprivGTKYPadding); + gtk_grid_set_column_spacing(f->grid, uiprivGTKXPadding); } else { gtk_grid_set_row_spacing(f->grid, 0); gtk_grid_set_column_spacing(f->grid, 0); diff --git a/unix/grid.c b/unix/grid.c index 6d9813b3..7759cecc 100644 --- a/unix/grid.c +++ b/unix/grid.c @@ -117,8 +117,8 @@ void uiGridSetPadded(uiGrid *g, int padded) { g->padded = padded; if (g->padded) { - gtk_grid_set_row_spacing(g->grid, gtkYPadding); - gtk_grid_set_column_spacing(g->grid, gtkXPadding); + gtk_grid_set_row_spacing(g->grid, uiprivGTKYPadding); + gtk_grid_set_column_spacing(g->grid, uiprivGTKXPadding); } else { gtk_grid_set_row_spacing(g->grid, 0); gtk_grid_set_column_spacing(g->grid, 0); diff --git a/unix/main.c b/unix/main.c index 650fe06f..c065449a 100644 --- a/unix/main.c +++ b/unix/main.c @@ -14,15 +14,15 @@ const char *uiInit(uiInitOptions *o) g_error_free(err); return msg; } - initAlloc(); + uiprivInitAlloc(); loadFutures(); return NULL; } void uiUninit(void) { - uninitMenus(); - uninitAlloc(); + uiprivUninitMenus(); + uiprivUninitAlloc(); } void uiFreeInitError(const char *err) diff --git a/unix/menu.c b/unix/menu.c index 17189c8e..a3142ee0 100644 --- a/unix/menu.c +++ b/unix/menu.c @@ -266,7 +266,7 @@ static void appendMenuItem(GtkMenuShell *submenu, uiMenuItem *item, uiWindow *w) g_hash_table_insert(item->windows, menuitem, ww); } -GtkWidget *makeMenubar(uiWindow *w) +GtkWidget *uiprivMakeMenubar(uiWindow *w) { GtkWidget *menubar; guint i, j; @@ -330,7 +330,7 @@ static void freeMenu(GtkWidget *widget, gpointer data) (*i)++; } -void freeMenubar(GtkWidget *mb) +void uiprivFreeMenubar(GtkWidget *mb) { guint i; @@ -339,7 +339,7 @@ void freeMenubar(GtkWidget *mb) // no need to worry about destroying any widgets; destruction of the window they're in will do it for us } -void uninitMenus(void) +void uiprivUninitMenus(void) { uiMenu *m; uiMenuItem *item; diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index 7d2a3e0e..0f45c123 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -14,4 +14,21 @@ #include "../ui_unix.h" #include "../common/uipriv.h" +#define uiprivGTKXMargin 12 +#define uiprivGTKYMargin 12 +#define uiprivGTKXPadding 12 +#define uiprivGTKYPadding 6 + +// menu.c +extern GtkWidget *uiprivMakeMenubar(uiWindow *); +extern void uiprivFreeMenubar(GtkWidget *); +extern void uiprivUninitMenus(void); + +// alloc.c +extern void uiprivInitAlloc(void); +extern void uiprivUninitAlloc(void); + +// util.c +extern void uiprivSetMargined(GtkContainer *, int); + #include "OLD_uipriv_unix.h" diff --git a/unix/util.c b/unix/util.c index 7f4f43fb..f3929ccb 100644 --- a/unix/util.c +++ b/unix/util.c @@ -1,10 +1,10 @@ // 18 april 2015 #include "uipriv_unix.h" -void setMargined(GtkContainer *c, int margined) +void uiprivSetMargined(GtkContainer *c, int margined) { if (margined) - gtk_container_set_border_width(c, gtkXMargin); + gtk_container_set_border_width(c, uiprivGTKXMargin); else gtk_container_set_border_width(c, 0); } diff --git a/unix/window.c b/unix/window.c index ea9ba370..c5ba2038 100644 --- a/unix/window.c +++ b/unix/window.c @@ -70,7 +70,7 @@ static void uiWindowDestroy(uiControl *c) } // now destroy the menus, if any if (w->menubar != NULL) - freeMenubar(w->menubar); + uiprivFreeMenubar(w->menubar); gtk_widget_destroy(w->childHolderWidget); gtk_widget_destroy(w->vboxWidget); // and finally free ourselves @@ -226,7 +226,7 @@ int uiWindowMargined(uiWindow *w) void uiWindowSetMargined(uiWindow *w, int margined) { w->margined = margined; - setMargined(w->childHolderContainer, w->margined); + uiprivSetMargined(w->childHolderContainer, w->margined); } uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) @@ -250,7 +250,7 @@ uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar) gtk_container_add(w->container, w->vboxWidget); if (hasMenubar) { - w->menubar = makeMenubar(uiWindow(w)); + w->menubar = uiprivMakeMenubar(uiWindow(w)); gtk_container_add(w->vboxContainer, w->menubar); } From 70fd8cbf8e4e7ac2bc2a67ac400c3b5c117ad755 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 12 May 2018 13:03:55 -0400 Subject: [PATCH 1047/1329] Migrated the types and functions of child.c. --- unix/OLD_uipriv_unix.h | 14 -------------- unix/child.c | 32 ++++++++++++++++---------------- unix/group.c | 12 ++++++------ unix/tab.c | 38 +++++++++++++++++++------------------- unix/uipriv_unix.h | 12 ++++++++++++ 5 files changed, 53 insertions(+), 55 deletions(-) diff --git a/unix/OLD_uipriv_unix.h b/unix/OLD_uipriv_unix.h index 53305ae6..a60e0834 100644 --- a/unix/OLD_uipriv_unix.h +++ b/unix/OLD_uipriv_unix.h @@ -1,17 +1,3 @@ - - - -// child.c -extern struct child *newChild(uiControl *child, uiControl *parent, GtkContainer *parentContainer); -extern struct child *newChildWithBox(uiControl *child, uiControl *parent, GtkContainer *parentContainer, int margined); -extern void childRemove(struct child *c); -extern void childDestroy(struct child *c); -extern GtkWidget *childWidget(struct child *c); -extern int childFlag(struct child *c); -extern void childSetFlag(struct child *c, int flag); -extern GtkWidget *childBox(struct child *c); -extern void childSetMargined(struct child *c, int margined); - // draw.c extern uiDrawContext *newContext(cairo_t *cr, GtkStyleContext *style); extern void freeContext(uiDrawContext *); diff --git a/unix/child.c b/unix/child.c index a0644c89..240cc82a 100644 --- a/unix/child.c +++ b/unix/child.c @@ -3,7 +3,7 @@ // This file contains helpers for managing child controls. -struct child { +struct uiprivChild { uiControl *c; GtkWidget *widget; @@ -26,14 +26,14 @@ struct child { int flag; }; -struct child *newChild(uiControl *child, uiControl *parent, GtkContainer *parentContainer) +uiprivChild *uiprivNewChild(uiControl *child, uiControl *parent, GtkContainer *parentContainer) { - struct child *c; + uiprivChild *c; if (child == NULL) return NULL; - c = uiprivNew(struct child); + c = uiprivNew(uiprivChild); c->c = child; c->widget = GTK_WIDGET(uiControlHandle(c->c)); @@ -49,27 +49,27 @@ struct child *newChild(uiControl *child, uiControl *parent, GtkContainer *parent return c; } -struct child *newChildWithBox(uiControl *child, uiControl *parent, GtkContainer *parentContainer, int margined) +uiprivChild *uiprivNewChildWithBox(uiControl *child, uiControl *parent, GtkContainer *parentContainer, int margined) { - struct child *c; + uiprivChild *c; GtkWidget *box; if (child == NULL) return NULL; box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); gtk_widget_show(box); - c = newChild(child, parent, GTK_CONTAINER(box)); + c = uiprivNewChild(child, parent, GTK_CONTAINER(box)); gtk_widget_set_hexpand(c->widget, TRUE); gtk_widget_set_halign(c->widget, GTK_ALIGN_FILL); gtk_widget_set_vexpand(c->widget, TRUE); gtk_widget_set_valign(c->widget, GTK_ALIGN_FILL); c->box = box; gtk_container_add(parentContainer, c->box); - childSetMargined(c, margined); + uiprivChildSetMargined(c, margined); return c; } -void childRemove(struct child *c) +void uiprivChildRemove(uiprivChild *c) { uiControlSetParent(c->c, NULL); uiUnixControlSetContainer(uiUnixControl(c->c), c->parent, TRUE); @@ -85,36 +85,36 @@ void childRemove(struct child *c) uiprivFree(c); } -void childDestroy(struct child *c) +void uiprivChildDestroy(uiprivChild *c) { uiControl *child; child = c->c; - childRemove(c); + uiprivChildRemove(c); uiControlDestroy(child); } -GtkWidget *childWidget(struct child *c) +GtkWidget *uiprivChildWidget(uiprivChild *c) { return c->widget; } -int childFlag(struct child *c) +int uiprivChildFlag(uiprivChild *c) { return c->flag; } -void childSetFlag(struct child *c, int flag) +void uiprivChildSetFlag(uiprivChild *c, int flag) { c->flag = flag; } -GtkWidget *childBox(struct child *c) +GtkWidget *uiprivChildBox(uiprivChild *c) { return c->box; } -void childSetMargined(struct child *c, int margined) +void uiprivChildSetMargined(uiprivChild *c, int margined) { uiprivSetMargined(GTK_CONTAINER(c->box), margined); } diff --git a/unix/group.c b/unix/group.c index 6238a1b6..545c7121 100644 --- a/unix/group.c +++ b/unix/group.c @@ -8,8 +8,8 @@ struct uiGroup { GtkBin *bin; GtkFrame *frame; - // unfortunately, even though a GtkFrame is a GtkBin, calling gtk_container_set_border_width() on it /includes/ the GtkFrame's label; we don't want tht - struct child *child; + // unfortunately, even though a GtkFrame is a GtkBin, calling gtk_container_set_border_width() on it /includes/ the GtkFrame's label; we don't want that + uiprivChild *child; int margined; }; @@ -21,7 +21,7 @@ static void uiGroupDestroy(uiControl *c) uiGroup *g = uiGroup(c); if (g->child != NULL) - childDestroy(g->child); + uiprivChildDestroy(g->child); g_object_unref(g->widget); uiFreeControl(uiControl(g)); } @@ -39,8 +39,8 @@ void uiGroupSetTitle(uiGroup *g, const char *text) void uiGroupSetChild(uiGroup *g, uiControl *child) { if (g->child != NULL) - childRemove(g->child); - g->child = newChildWithBox(child, uiControl(g), g->container, g->margined); + uiprivChildRemove(g->child); + g->child = uiprivNewChildWithBox(child, uiControl(g), g->container, g->margined); } int uiGroupMargined(uiGroup *g) @@ -52,7 +52,7 @@ void uiGroupSetMargined(uiGroup *g, int margined) { g->margined = margined; if (g->child != NULL) - childSetMargined(g->child, g->margined); + uiprivChildSetMargined(g->child, g->margined); } uiGroup *uiNewGroup(const char *text) diff --git a/unix/tab.c b/unix/tab.c index 552e0e31..b49c98b9 100644 --- a/unix/tab.c +++ b/unix/tab.c @@ -8,7 +8,7 @@ struct uiTab { GtkContainer *container; GtkNotebook *notebook; - GArray *pages; // []*struct child + GArray *pages; // []*uiprivChild }; uiUnixControlAllDefaultsExceptDestroy(uiTab) @@ -17,11 +17,11 @@ static void uiTabDestroy(uiControl *c) { uiTab *t = uiTab(c); guint i; - struct child *page; + uiprivChild *page; for (i = 0; i < t->pages->len; i++) { - page = g_array_index(t->pages, struct child *, i); - childDestroy(page); + page = g_array_index(t->pages, uiprivChild *, i); + uiprivChildDestroy(page); } g_array_free(t->pages, TRUE); // and free ourselves @@ -36,24 +36,24 @@ void uiTabAppend(uiTab *t, const char *name, uiControl *child) void uiTabInsertAt(uiTab *t, const char *name, int n, uiControl *child) { - struct child *page; + uiprivChild *page; // this will create a tab, because of gtk_container_add() - page = newChildWithBox(child, uiControl(t), t->container, 0); + page = uiprivNewChildWithBox(child, uiControl(t), t->container, 0); - gtk_notebook_set_tab_label_text(t->notebook, childBox(page), name); - gtk_notebook_reorder_child(t->notebook, childBox(page), n); + gtk_notebook_set_tab_label_text(t->notebook, uiprivChildBox(page), name); + gtk_notebook_reorder_child(t->notebook, uiprivChildBox(page), n); g_array_insert_val(t->pages, n, page); } void uiTabDelete(uiTab *t, int n) { - struct child *page; + uiprivChild *page; - page = g_array_index(t->pages, struct child *, n); + page = g_array_index(t->pages, uiprivChild *, n); // this will remove the tab, because gtk_widget_destroy() calls gtk_container_remove() - childRemove(page); + uiprivChildRemove(page); g_array_remove_index(t->pages, n); } @@ -64,19 +64,19 @@ int uiTabNumPages(uiTab *t) int uiTabMargined(uiTab *t, int n) { - struct child *page; + uiprivChild *page; - page = g_array_index(t->pages, struct child *, n); - return childFlag(page); + page = g_array_index(t->pages, uiprivChild *, n); + return uiprivChildFlag(page); } void uiTabSetMargined(uiTab *t, int n, int margined) { - struct child *page; + uiprivChild *page; - page = g_array_index(t->pages, struct child *, n); - childSetFlag(page, margined); - childSetMargined(page, childFlag(page)); + page = g_array_index(t->pages, uiprivChild *, n); + uiprivChildSetFlag(page, margined); + uiprivChildSetMargined(page, uiprivChildFlag(page)); } uiTab *uiNewTab(void) @@ -91,7 +91,7 @@ uiTab *uiNewTab(void) gtk_notebook_set_scrollable(t->notebook, TRUE); - t->pages = g_array_new(FALSE, TRUE, sizeof (struct child *)); + t->pages = g_array_new(FALSE, TRUE, sizeof (uiprivChild *)); return t; } diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index 0f45c123..9fd354da 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -31,4 +31,16 @@ extern void uiprivUninitAlloc(void); // util.c extern void uiprivSetMargined(GtkContainer *, int); +// child.c +typedef struct uiprivChild uiprivChild; +extern uiprivChild *uiprivNewChild(uiControl *child, uiControl *parent, GtkContainer *parentContainer); +extern uiprivChild *uiprivNewChildWithBox(uiControl *child, uiControl *parent, GtkContainer *parentContainer, int margined); +extern void uiprivChildRemove(uiprivChild *c); +extern void uiprivChildDestroy(uiprivChild *c); +extern GtkWidget *uiprivChildWidget(uiprivChild *c); +extern int uiprivChildFlag(uiprivChild *c); +extern void uiprivChildSetFlag(uiprivChild *c, int flag); +extern GtkWidget *uiprivChildBox(uiprivChild *c); +extern void uiprivChildSetMargined(uiprivChild *c, int margined); + #include "OLD_uipriv_unix.h" From e0a2fc5841597da38e6236a90443ea10f656080a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 12 May 2018 13:14:39 -0400 Subject: [PATCH 1048/1329] Renamed shared functions in draw.c, image.c, and cellrendererbutton.c. --- unix/OLD_uipriv_unix.h | 10 ---------- unix/area.c | 4 ++-- unix/cellrendererbutton.c | 2 +- unix/draw.c | 4 ++-- unix/image.c | 2 +- unix/uipriv_unix.h | 11 +++++++++++ 6 files changed, 17 insertions(+), 16 deletions(-) diff --git a/unix/OLD_uipriv_unix.h b/unix/OLD_uipriv_unix.h index a60e0834..5f263a28 100644 --- a/unix/OLD_uipriv_unix.h +++ b/unix/OLD_uipriv_unix.h @@ -1,13 +1,3 @@ -// draw.c -extern uiDrawContext *newContext(cairo_t *cr, GtkStyleContext *style); -extern void freeContext(uiDrawContext *); - -// image.c -/*TODO remove this*/typedef struct uiImage uiImage; -extern cairo_surface_t *imageAppropriateSurface(uiImage *i, GtkWidget *w); - -// cellrendererbutton.c -extern GtkCellRenderer *newCellRendererButton(void); // future.c extern void loadFutures(void); diff --git a/unix/area.c b/unix/area.c index cba1f5e7..0a2e6d69 100644 --- a/unix/area.c +++ b/unix/area.c @@ -122,7 +122,7 @@ static gboolean areaWidget_draw(GtkWidget *w, cairo_t *cr) uiAreaDrawParams dp; double clipX0, clipY0, clipX1, clipY1; - dp.Context = newContext(cr, + dp.Context = uiprivNewContext(cr, gtk_widget_get_style_context(a->widget)); loadAreaSize(a, &(dp.AreaWidth), &(dp.AreaHeight)); @@ -136,7 +136,7 @@ static gboolean areaWidget_draw(GtkWidget *w, cairo_t *cr) // no need to save or restore the graphics state to reset transformations; GTK+ does that for us (*(a->ah->Draw))(a->ah, a, &dp); - freeContext(dp.Context); + uiprivFreeContext(dp.Context); return FALSE; } diff --git a/unix/cellrendererbutton.c b/unix/cellrendererbutton.c index e3bbf48b..e686b454 100644 --- a/unix/cellrendererbutton.c +++ b/unix/cellrendererbutton.c @@ -293,7 +293,7 @@ static void cellRendererButton_class_init(cellRendererButtonClass *class) 1, G_TYPE_STRING); } -GtkCellRenderer *newCellRendererButton(void) +GtkCellRenderer *uiprivNewCellRendererButton(void) { return GTK_CELL_RENDERER(g_object_new(cellRendererButtonType, NULL)); } diff --git a/unix/draw.c b/unix/draw.c index 15abb611..3e28fb37 100644 --- a/unix/draw.c +++ b/unix/draw.c @@ -2,7 +2,7 @@ #include "uipriv_unix.h" #include "draw.h" -uiDrawContext *newContext(cairo_t *cr, GtkStyleContext *style) +uiDrawContext *uiprivNewContext(cairo_t *cr, GtkStyleContext *style) { uiDrawContext *c; @@ -12,7 +12,7 @@ uiDrawContext *newContext(cairo_t *cr, GtkStyleContext *style) return c; } -void freeContext(uiDrawContext *c) +void uiprivFreeContext(uiDrawContext *c) { // free neither cr nor style; we own neither uiprivFree(c); diff --git a/unix/image.c b/unix/image.c index 1bdf0d64..3b5db020 100644 --- a/unix/image.c +++ b/unix/image.c @@ -105,7 +105,7 @@ writeMatch: m->distY = abs(m->targetY - y); } -cairo_surface_t *imageAppropriateSurface(uiImage *i, GtkWidget *w) +cairo_surface_t *uiprivImageAppropriateSurface(uiImage *i, GtkWidget *w) { struct matcher m; diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index 9fd354da..d706db19 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -43,4 +43,15 @@ extern void uiprivChildSetFlag(uiprivChild *c, int flag); extern GtkWidget *uiprivChildBox(uiprivChild *c); extern void uiprivChildSetMargined(uiprivChild *c, int margined); +// draw.c +extern uiDrawContext *uiprivNewContext(cairo_t *cr, GtkStyleContext *style); +extern void uiprivFreeContext(uiDrawContext *); + +// image.c +/*TODO remove this*/typedef struct uiImage uiImage; +extern cairo_surface_t *uiprivImageAppropriateSurface(uiImage *i, GtkWidget *w); + +// cellrendererbutton.c +extern GtkCellRenderer *uiprivNewCellRendererButton(void); + #include "OLD_uipriv_unix.h" From add92694bf69b062e0c593a665728a2f58449d4f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 12 May 2018 13:19:35 -0400 Subject: [PATCH 1049/1329] And renamed the things in future.c, to round out uipriv_unix.h. --- unix/OLD_uipriv_unix.h | 7 ------- unix/attrstr.c | 6 +++--- unix/cellrendererbutton.c | 2 +- unix/future.c | 10 +++++----- unix/main.c | 2 +- unix/uipriv_unix.h | 7 ++++++- 6 files changed, 16 insertions(+), 18 deletions(-) delete mode 100644 unix/OLD_uipriv_unix.h diff --git a/unix/OLD_uipriv_unix.h b/unix/OLD_uipriv_unix.h deleted file mode 100644 index 5f263a28..00000000 --- a/unix/OLD_uipriv_unix.h +++ /dev/null @@ -1,7 +0,0 @@ - -// future.c -extern void loadFutures(void); -extern PangoAttribute *FUTURE_pango_attr_font_features_new(const gchar *features); -extern PangoAttribute *FUTURE_pango_attr_foreground_alpha_new(guint16 alpha); -extern PangoAttribute *FUTURE_pango_attr_background_alpha_new(guint16 alpha); -extern gboolean FUTURE_gtk_widget_path_iter_set_object_name(GtkWidgetPath *path, gint pos, const char *name); diff --git a/unix/attrstr.c b/unix/attrstr.c index a378e452..f543943b 100644 --- a/unix/attrstr.c +++ b/unix/attrstr.c @@ -57,7 +57,7 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute (guint16) (g * 65535.0), (guint16) (b * 65535.0))); addattr(p, start, end, - FUTURE_pango_attr_foreground_alpha_new( + uiprivFUTURE_pango_attr_foreground_alpha_new( (guint16) (a * 65535.0))); break; case uiAttributeTypeBackground: @@ -69,7 +69,7 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute (guint16) (g * 65535.0), (guint16) (b * 65535.0))); addattr(p, start, end, - FUTURE_pango_attr_background_alpha_new( + uiprivFUTURE_pango_attr_background_alpha_new( (guint16) (a * 65535.0))); break; case uiAttributeTypeUnderline: @@ -125,7 +125,7 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute break; featurestr = uiprivOpenTypeFeaturesToPangoCSSFeaturesString(features); addattr(p, start, end, - FUTURE_pango_attr_font_features_new(featurestr->str)); + uiprivFUTURE_pango_attr_font_features_new(featurestr->str)); g_string_free(featurestr, TRUE); break; default: diff --git a/unix/cellrendererbutton.c b/unix/cellrendererbutton.c index e686b454..c3ff4f82 100644 --- a/unix/cellrendererbutton.c +++ b/unix/cellrendererbutton.c @@ -68,7 +68,7 @@ static GtkStyleContext *setButtonStyle(GtkWidget *widget) path = gtk_widget_path_copy(gtk_style_context_get_path(base)); gtk_widget_path_append_type(path, G_TYPE_NONE); - if (!FUTURE_gtk_widget_path_iter_set_object_name(path, -1, "button")) + if (!uiprivFUTURE_gtk_widget_path_iter_set_object_name(path, -1, "button")) // not on 3.20; try the type gtk_widget_path_iter_set_object_type(path, -1, GTK_TYPE_BUTTON); diff --git a/unix/future.c b/unix/future.c index 68730ead..475dbc19 100644 --- a/unix/future.c +++ b/unix/future.c @@ -14,7 +14,7 @@ static PangoAttribute *(*newBGAlphaAttr)(guint16 alpha) = NULL; static void (*gwpIterSetObjectName)(GtkWidgetPath *path, gint pos, const char *name) = NULL; // note that we treat any error as "the symbols aren't there" (and don't care if dlclose() failed) -void loadFutures(void) +void uiprivLoadFutures(void) { void *handle; @@ -30,28 +30,28 @@ void loadFutures(void) dlclose(handle); } -PangoAttribute *FUTURE_pango_attr_font_features_new(const gchar *features) +PangoAttribute *uiprivFUTURE_pango_attr_font_features_new(const gchar *features) { if (newFeaturesAttr == NULL) return NULL; return (*newFeaturesAttr)(features); } -PangoAttribute *FUTURE_pango_attr_foreground_alpha_new(guint16 alpha) +PangoAttribute *uiprivFUTURE_pango_attr_foreground_alpha_new(guint16 alpha) { if (newFGAlphaAttr == NULL) return NULL; return (*newFGAlphaAttr)(alpha); } -PangoAttribute *FUTURE_pango_attr_background_alpha_new(guint16 alpha) +PangoAttribute *uiprivFUTURE_pango_attr_background_alpha_new(guint16 alpha) { if (newBGAlphaAttr == NULL) return NULL; return (*newBGAlphaAttr)(alpha); } -gboolean FUTURE_gtk_widget_path_iter_set_object_name(GtkWidgetPath *path, gint pos, const char *name) +gboolean uiprivFUTURE_gtk_widget_path_iter_set_object_name(GtkWidgetPath *path, gint pos, const char *name) { if (gwpIterSetObjectName == NULL) return FALSE; diff --git a/unix/main.c b/unix/main.c index c065449a..8ebdf5f5 100644 --- a/unix/main.c +++ b/unix/main.c @@ -15,7 +15,7 @@ const char *uiInit(uiInitOptions *o) return msg; } uiprivInitAlloc(); - loadFutures(); + uiprivLoadFutures(); return NULL; } diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index d706db19..27fd673b 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -54,4 +54,9 @@ extern cairo_surface_t *uiprivImageAppropriateSurface(uiImage *i, GtkWidget *w); // cellrendererbutton.c extern GtkCellRenderer *uiprivNewCellRendererButton(void); -#include "OLD_uipriv_unix.h" +// future.c +extern void uiprivLoadFutures(void); +extern PangoAttribute *uiprivFUTURE_pango_attr_font_features_new(const gchar *features); +extern PangoAttribute *uiprivFUTURE_pango_attr_foreground_alpha_new(guint16 alpha); +extern PangoAttribute *uiprivFUTURE_pango_attr_background_alpha_new(guint16 alpha); +extern gboolean uiprivFUTURE_gtk_widget_path_iter_set_object_name(GtkWidgetPath *path, gint pos, const char *name); From 241d8b59f07d5b50c8c4b131dddfb180749819ec Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 12 May 2018 13:25:40 -0400 Subject: [PATCH 1050/1329] And handled the functions in draw.h. Now to test this final build, then get rid of the shared library stuff from CMakeLists.txt... --- unix/draw.c | 12 ++++++------ unix/draw.h | 6 +++--- unix/drawmatrix.c | 8 +++++++- unix/drawpath.c | 4 ++-- 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/unix/draw.c b/unix/draw.c index 3e28fb37..a8f26d7f 100644 --- a/unix/draw.c +++ b/unix/draw.c @@ -59,7 +59,7 @@ void uiDrawStroke(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b, uiDrawStro { cairo_pattern_t *pat; - runPath(path, c->cr); + uiprivRunPath(path, c->cr); pat = mkbrush(b); cairo_set_source(c->cr, pat); switch (p->Cap) { @@ -95,10 +95,10 @@ void uiDrawFill(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b) { cairo_pattern_t *pat; - runPath(path, c->cr); + uiprivRunPath(path, c->cr); pat = mkbrush(b); cairo_set_source(c->cr, pat); - switch (pathFillMode(path)) { + switch (uiprivPathFillMode(path)) { case uiDrawFillModeWinding: cairo_set_fill_rule(c->cr, CAIRO_FILL_RULE_WINDING); break; @@ -114,14 +114,14 @@ void uiDrawTransform(uiDrawContext *c, uiDrawMatrix *m) { cairo_matrix_t cm; - m2c(m, &cm); + uiprivM2C(m, &cm); cairo_transform(c->cr, &cm); } void uiDrawClip(uiDrawContext *c, uiDrawPath *path) { - runPath(path, c->cr); - switch (pathFillMode(path)) { + uiprivRunPath(path, c->cr); + switch (uiprivPathFillMode(path)) { case uiDrawFillModeWinding: cairo_set_fill_rule(c->cr, CAIRO_FILL_RULE_WINDING); break; diff --git a/unix/draw.h b/unix/draw.h index 9eec2846..d46d074f 100644 --- a/unix/draw.h +++ b/unix/draw.h @@ -7,8 +7,8 @@ struct uiDrawContext { }; // drawpath.c -extern void runPath(uiDrawPath *p, cairo_t *cr); -extern uiDrawFillMode pathFillMode(uiDrawPath *path); +extern void uiprivRunPath(uiDrawPath *p, cairo_t *cr); +extern uiDrawFillMode uiprivPathFillMode(uiDrawPath *path); // drawmatrix.c -extern void m2c(uiDrawMatrix *m, cairo_matrix_t *c); +extern void uiprivM2C(uiDrawMatrix *m, cairo_matrix_t *c); diff --git a/unix/drawmatrix.c b/unix/drawmatrix.c index 7d15d920..ffb4db34 100644 --- a/unix/drawmatrix.c +++ b/unix/drawmatrix.c @@ -2,7 +2,7 @@ #include "uipriv_unix.h" #include "draw.h" -void m2c(uiDrawMatrix *m, cairo_matrix_t *c) +static void m2c(uiDrawMatrix *m, cairo_matrix_t *c) { c->xx = m->M11; c->yx = m->M12; @@ -12,6 +12,12 @@ void m2c(uiDrawMatrix *m, cairo_matrix_t *c) c->y0 = m->M32; } +// needed by uiDrawTransform() +void uiprivM2C(uiDrawMatrix *m, cairo_matrix_t *c) +{ + m2c(m, c); +} + static void c2m(cairo_matrix_t *c, uiDrawMatrix *m) { m->M11 = c->xx; diff --git a/unix/drawpath.c b/unix/drawpath.c index 28eeb981..045660f5 100644 --- a/unix/drawpath.c +++ b/unix/drawpath.c @@ -138,7 +138,7 @@ void uiDrawPathEnd(uiDrawPath *p) p->ended = TRUE; } -void runPath(uiDrawPath *p, cairo_t *cr) +void uiprivRunPath(uiDrawPath *p, cairo_t *cr) { guint i; struct piece *piece; @@ -193,7 +193,7 @@ void runPath(uiDrawPath *p, cairo_t *cr) } } -uiDrawFillMode pathFillMode(uiDrawPath *path) +uiDrawFillMode uiprivPathFillMode(uiDrawPath *path) { return path->fillMode; } From af192eedab29ba0eef66f7d9f06e298895103550 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 12 May 2018 13:42:48 -0400 Subject: [PATCH 1051/1329] And removed static library hacks from GTK+. Let's hope this works! --- unix/CMakeLists.txt | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/unix/CMakeLists.txt b/unix/CMakeLists.txt index eba09ad9..da4cdd0b 100644 --- a/unix/CMakeLists.txt +++ b/unix/CMakeLists.txt @@ -55,28 +55,8 @@ list(APPEND _LIBUI_INCLUDEDIRS set(_LIBUI_INCLUDEDIRS _LIBUI_INCLUDEDIRS PARENT_SCOPE) set(_LIBUINAME libui PARENT_SCOPE) -if(NOT BUILD_SHARED_LIBS) - set(_LIBUINAME libui-temporary PARENT_SCOPE) -endif() -# TODO remove all these temporary files after linking the final archive file macro(_handle_static) - set_target_properties(${_LIBUINAME} PROPERTIES - ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}") - set(_aname $) - set(_oname libui-combined.o) - add_custom_command( - OUTPUT ${_oname} - COMMAND - ld -r --whole-archive ${_aname} -o ${_oname} - COMMAND - objcopy --localize-hidden ${_oname} - COMMENT "Removing hidden symbols") - add_library(libui STATIC ${_oname}) - # otherwise cmake won't know which linker to use - set_target_properties(libui PROPERTIES - LINKER_LANGUAGE C) - set(_aname) - set(_oname) + # do nothing endmacro() # TODO the other variables don't work? From f647dda85044962a658ddd2df5d511b207e3cc41 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 12 May 2018 13:52:07 -0400 Subject: [PATCH 1052/1329] Made the past few branches into an update in README.md. --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index e7b90918..c1c04797 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,9 @@ This README is being written.
*Note that today's entry (Eastern Time) may be updated later today.* +* **12 May 2018** + * GTK+ and OS X now have a cleaner build process for static libraries which no longer has intermediate files and differing configurations. As a result, certain issues should no longer be present. New naming rules for internal symbols of libui have also started being drafted; runtime symbols and edge cases still need to be handled (and the rules applied to Windows) before this can become a regular thing. + * **18 April 2018** * Migrated all code in the `common/` directory to use `uipriv` prefixes for everything that isn't `static`. This is the first step toward fixing static library oddities within libui, allowing libui to truly be safely used as either a static library or a shared library. From 0b8e86e4f8fa0f4d6319ca6d5d2ff984ab1a471b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 12 May 2018 13:59:22 -0400 Subject: [PATCH 1053/1329] Started applying new uipriv names to table.c. Let's let the compiler tell us what we missed. --- unix/table.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unix/table.c b/unix/table.c index ac8b03d3..d8cbceaf 100644 --- a/unix/table.c +++ b/unix/table.c @@ -392,7 +392,7 @@ static void dataFunc(GtkTreeViewColumn *c, GtkCellRenderer *r, GtkTreeModel *mm, gtk_tree_model_get_value(mm, iter, part->imageColumn, &value); img = (uiImage *) g_value_get_pointer(&value); g_object_set(r, "surface", - imageAppropriateSurface(img, part->tv->treeWidget), + uiprivImageAppropriateSurface(img, part->tv->treeWidget), NULL); break; case partButton: @@ -503,7 +503,7 @@ void uiTableColumnAppendButtonPart(uiTableColumn *c, int modelColumn, int expand part->textColumn = modelColumn; part->tv = c->tv; - r = newCellRendererButton(); + r = uiprivNewCellRendererButton(); g_object_set(r, "sensitive", TRUE, NULL); // editable by default g_signal_connect(r, "clicked", G_CALLBACK(buttonClicked), part); From 2768fef3ce836eea12ab8711070b07fa935fe84f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 12 May 2018 23:59:43 -0400 Subject: [PATCH 1054/1329] Cleaned up old static-library stuff from CMakeLists.txt. --- CMakeLists.txt | 19 +++++++++---------- darwin/CMakeLists.txt | 5 ----- unix/CMakeLists.txt | 5 ----- windows/CMakeLists.txt | 5 ----- 4 files changed, 9 insertions(+), 25 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fe5f71df..92937780 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -148,34 +148,33 @@ endmacro() add_subdirectory("common") add_subdirectory("${_OSNAME}") -add_library(${_LIBUINAME} ${_LIBUI_SOURCES}) -target_include_directories(${_LIBUINAME} +add_library(libui ${_LIBUI_SOURCES}) +target_include_directories(libui PUBLIC . PRIVATE ${_LIBUI_INCLUEDIRS}) -target_compile_definitions(${_LIBUINAME} +target_compile_definitions(libui PRIVATE ${_LIBUI_DEFS}) # cmake produces this for us by default but only for shared libraries -target_compile_definitions(${_LIBUINAME} +target_compile_definitions(libui PRIVATE libui_EXPORTS) -target_compile_options(${_LIBUINAME} +target_compile_options(libui PUBLIC ${_COMMON_CFLAGS} PRIVATE ${_LIBUI_CFLAGS}) # TODO link directories? if(BUILD_SHARED_LIBS) - target_link_libraries(${_LIBUINAME} + target_link_libraries(libui PRIVATE ${_LIBUI_LIBS}) endif() # TODO INTERFACE libs don't inherit to grandhcildren? # on Windows the linker for static libraries is different; don't give it the flags if(BUILD_SHARED_LIBS) - _target_link_options_private(${_LIBUINAME} + _target_link_options_private(libui _COMMON_LDFLAGS _LIBUI_LDFLAGS) endif() if(NOT BUILD_SHARED_LIBS) - _handle_static() # TODO figure out a way to tell libui that it's static - target_compile_definitions(${_LIBUINAME} + target_compile_definitions(libui PUBLIC _UI_STATIC) endif() if(NOT MSVC) @@ -200,7 +199,7 @@ if(NOT MSVC) endif() if(BUILD_SHARED_LIBS) if(_HASVERSION) - set_target_properties(${_LIBUINAME} PROPERTIES + set_target_properties(libui PROPERTIES SOVERSION "${_VERSION}") endif() endif() diff --git a/darwin/CMakeLists.txt b/darwin/CMakeLists.txt index 6bf91a85..bbab3c9a 100644 --- a/darwin/CMakeLists.txt +++ b/darwin/CMakeLists.txt @@ -57,11 +57,6 @@ list(APPEND _LIBUI_INCLUDEDIRS ) set(_LIBUI_INCLUDEDIRS _LIBUI_INCLUDEDIRS PARENT_SCOPE) -set(_LIBUINAME libui PARENT_SCOPE) -macro(_handle_static) - # do nothing -endmacro() - set(_LIBUI_LIBS objc "-framework Foundation" "-framework AppKit" PARENT_SCOPE) diff --git a/unix/CMakeLists.txt b/unix/CMakeLists.txt index da4cdd0b..78873146 100644 --- a/unix/CMakeLists.txt +++ b/unix/CMakeLists.txt @@ -54,11 +54,6 @@ list(APPEND _LIBUI_INCLUDEDIRS ) set(_LIBUI_INCLUDEDIRS _LIBUI_INCLUDEDIRS PARENT_SCOPE) -set(_LIBUINAME libui PARENT_SCOPE) -macro(_handle_static) - # do nothing -endmacro() - # TODO the other variables don't work? set(_LIBUI_CFLAGS ${GTK_CFLAGS} diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 4b208385..16beefa7 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -74,11 +74,6 @@ list(APPEND _LIBUI_INCLUDEDIRS ) set(_LIBUI_INCLUDEDIRS _LIBUI_INCLUDEDIRS PARENT_SCOPE) -set(_LIBUINAME libui PARENT_SCOPE) -macro(_handle_static) - # do nothing -endmacro() - # TODO prune this list set(_LIBUI_LIBS user32 kernel32 gdi32 comctl32 uxtheme msimg32 comdlg32 d2d1 dwrite ole32 oleaut32 oleacc uuid From bc140429358e06e4e6f20fce6c0abe5b4d69872c Mon Sep 17 00:00:00 2001 From: cody271 Date: Mon, 19 Feb 2018 22:39:44 -0800 Subject: [PATCH 1055/1329] Add uiDateTimePickerTime() APIs --- darwin/datetimepicker.m | 12 ++++ examples/CMakeLists.txt | 6 ++ examples/datetime/main.c | 132 +++++++++++++++++++++++++++++++++++++ ui.h | 4 ++ unix/datetimepicker.c | 12 ++++ windows/datetimepicker.cpp | 12 ++++ 6 files changed, 178 insertions(+) create mode 100644 examples/datetime/main.c diff --git a/darwin/datetimepicker.m b/darwin/datetimepicker.m index 44364d9d..7a8bdd5b 100644 --- a/darwin/datetimepicker.m +++ b/darwin/datetimepicker.m @@ -40,3 +40,15 @@ uiDateTimePicker *uiNewTimePicker(void) { return finishNewDateTimePicker(NSHourMinuteSecondDatePickerElementFlag); } + +void uiDateTimePickerTime(uiDateTimePicker *d, struct tm *time) +{ +} + +void uiDateTimePickerSetTime(uiDateTimePicker *d, const struct tm *time) +{ +} + +void uiDateTimePickerOnChanged(uiDateTimePicker *d, void (*f)(uiDateTimePicker *, void *), void *data) +{ +} diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 8d83566e..4c049b9a 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -23,6 +23,11 @@ _add_example(histogram ${_EXAMPLE_RESOURCES_RC} ) +_add_example(datetime + datetime/main.c + ${_EXAMPLE_RESOURCES_RC} +) + _add_example(cpp-multithread cpp-multithread/main.cpp ${_EXAMPLE_RESOURCES_RC} @@ -53,4 +58,5 @@ add_custom_target(examples histogram cpp-multithread drawtext + datetime timer) diff --git a/examples/datetime/main.c b/examples/datetime/main.c new file mode 100644 index 00000000..26c835b9 --- /dev/null +++ b/examples/datetime/main.c @@ -0,0 +1,132 @@ +#include +#include +#include +#include "../../ui.h" + +uiDateTimePicker *dtboth, *dtdate, *dttime; + +const char * timeFormat(uiDateTimePicker *d) +{ + const char *fmt; + + if (d == dtboth) + fmt = "%c"; + else if (d == dtdate) + fmt = "%x"; + else if (d == dttime) + fmt = "%X"; + else + fmt = ""; + + return fmt; +} + +void onChanged(uiDateTimePicker *d, void *data) +{ + struct tm time; + char buf[64]; + + uiDateTimePickerTime(d, &time); + strftime(buf, sizeof(buf), timeFormat(d), &time); + uiLabelSetText(uiLabel(data), buf); +} + +void onClicked(uiButton *b, void *data) +{ + intptr_t now; + time_t t; + struct tm tmbuf; + + now = (intptr_t) data; + t = 0; + if (now) + t = time(NULL); + tmbuf = *localtime(&t); + + if (now) { + uiDateTimePickerSetTime(dtdate, &tmbuf); + uiDateTimePickerSetTime(dttime, &tmbuf); + } + else + uiDateTimePickerSetTime(dtboth, &tmbuf); +} + +int onClosing(uiWindow *w, void *data) +{ + uiQuit(); + return 1; +} + +int main(void) +{ + uiInitOptions o; + uiWindow *w; + uiGrid *g; + uiLabel *l; + uiButton *b; + + memset(&o, 0, sizeof (uiInitOptions)); + if (uiInit(&o) != NULL) + abort(); + + w = uiNewWindow("Date / Time", 320, 240, 0); + uiWindowSetMargined(w, 1); + + g = uiNewGrid(); + uiGridSetPadded(g, 1); + uiWindowSetChild(w, uiControl(g)); + + dtboth = uiNewDateTimePicker(); + dtdate = uiNewDatePicker(); + dttime = uiNewTimePicker(); + + uiGridAppend(g, uiControl(dtboth), + 0, 0, 2, 1, + 1, uiAlignFill, 0, uiAlignFill); + uiGridAppend(g, uiControl(dtdate), + 0, 1, 1, 1, + 1, uiAlignFill, 0, uiAlignFill); + uiGridAppend(g, uiControl(dttime), + 1, 1, 1, 1, + 1, uiAlignFill, 0, uiAlignFill); + + uiGridAppend(g, uiControl(uiNewVerticalSeparator()), + 0, 2, 2, 1, + 1, uiAlignFill, 0, uiAlignFill); + + l = uiNewLabel(""); + uiGridAppend(g, uiControl(l), + 0, 3, 2, 1, + 1, uiAlignCenter, 0, uiAlignFill); + uiDateTimePickerOnChanged(dtboth, onChanged, l); + l = uiNewLabel(""); + uiGridAppend(g, uiControl(l), + 0, 4, 1, 1, + 1, uiAlignCenter, 0, uiAlignFill); + uiDateTimePickerOnChanged(dtdate, onChanged, l); + l = uiNewLabel(""); + uiGridAppend(g, uiControl(l), + 1, 4, 1, 1, + 1, uiAlignCenter, 0, uiAlignFill); + uiDateTimePickerOnChanged(dttime, onChanged, l); + + uiGridAppend(g, uiControl(uiNewVerticalSeparator()), + 0, 5, 2, 1, + 1, uiAlignFill, 0, uiAlignFill); + + b = uiNewButton("Now"); + uiButtonOnClicked(b, onClicked, (void *) 1); + uiGridAppend(g, uiControl(b), + 0, 6, 1, 1, + 1, uiAlignFill, 0, uiAlignFill); + b = uiNewButton("Unix epoch"); + uiButtonOnClicked(b, onClicked, (void *) 0); + uiGridAppend(g, uiControl(b), + 1, 6, 1, 1, + 1, uiAlignFill, 0, uiAlignFill); + + uiWindowOnClosing(w, onClosing, NULL); + uiControlShow(uiControl(w)); + uiMain(); + return 0; +} diff --git a/ui.h b/ui.h index ce3e4104..765b7733 100644 --- a/ui.h +++ b/ui.h @@ -248,8 +248,12 @@ _UI_EXTERN void uiRadioButtonsSetSelected(uiRadioButtons *r, int n); _UI_EXTERN void uiRadioButtonsOnSelected(uiRadioButtons *r, void (*f)(uiRadioButtons *, void *), void *data); _UI_EXTERN uiRadioButtons *uiNewRadioButtons(void); +struct tm; typedef struct uiDateTimePicker uiDateTimePicker; #define uiDateTimePicker(this) ((uiDateTimePicker *) (this)) +_UI_EXTERN void uiDateTimePickerTime(uiDateTimePicker *d, struct tm *time); +_UI_EXTERN void uiDateTimePickerSetTime(uiDateTimePicker *d, const struct tm *time); +_UI_EXTERN void uiDateTimePickerOnChanged(uiDateTimePicker *d, void (*f)(uiDateTimePicker *, void *), void *data); _UI_EXTERN uiDateTimePicker *uiNewDateTimePicker(void); _UI_EXTERN uiDateTimePicker *uiNewDatePicker(void); _UI_EXTERN uiDateTimePicker *uiNewTimePicker(void); diff --git a/unix/datetimepicker.c b/unix/datetimepicker.c index 19689a22..c15a610d 100644 --- a/unix/datetimepicker.c +++ b/unix/datetimepicker.c @@ -597,3 +597,15 @@ uiDateTimePicker *uiNewTimePicker(void) { return finishNewDateTimePicker(newTP); } + +void uiDateTimePickerTime(uiDateTimePicker *d, struct tm *time) +{ +} + +void uiDateTimePickerSetTime(uiDateTimePicker *d, const struct tm *time) +{ +} + +void uiDateTimePickerOnChanged(uiDateTimePicker *d, void (*f)(uiDateTimePicker *, void *), void *data) +{ +} diff --git a/windows/datetimepicker.cpp b/windows/datetimepicker.cpp index 6784fec2..40dfe7e9 100644 --- a/windows/datetimepicker.cpp +++ b/windows/datetimepicker.cpp @@ -189,3 +189,15 @@ uiDateTimePicker *uiNewTimePicker(void) { return finishNewDateTimePicker(DTS_TIMEFORMAT); } + +void uiDateTimePickerTime(uiDateTimePicker *d, struct tm *time) +{ +} + +void uiDateTimePickerSetTime(uiDateTimePicker *d, const struct tm *time) +{ +} + +void uiDateTimePickerOnChanged(uiDateTimePicker *d, void (*f)(uiDateTimePicker *, void *), void *data) +{ +} From 3e9bdd26f113c0cc2a8ed6a8816c6f077f0d472f Mon Sep 17 00:00:00 2001 From: cody271 Date: Wed, 21 Feb 2018 21:26:43 -0800 Subject: [PATCH 1056/1329] Fix layout in datetime/main.c --- examples/datetime/main.c | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/examples/datetime/main.c b/examples/datetime/main.c index 26c835b9..a01dc889 100644 --- a/examples/datetime/main.c +++ b/examples/datetime/main.c @@ -90,40 +90,32 @@ int main(void) 1, 1, 1, 1, 1, uiAlignFill, 0, uiAlignFill); - uiGridAppend(g, uiControl(uiNewVerticalSeparator()), - 0, 2, 2, 1, - 1, uiAlignFill, 0, uiAlignFill); - l = uiNewLabel(""); uiGridAppend(g, uiControl(l), - 0, 3, 2, 1, + 0, 2, 2, 1, 1, uiAlignCenter, 0, uiAlignFill); uiDateTimePickerOnChanged(dtboth, onChanged, l); l = uiNewLabel(""); uiGridAppend(g, uiControl(l), - 0, 4, 1, 1, + 0, 3, 1, 1, 1, uiAlignCenter, 0, uiAlignFill); uiDateTimePickerOnChanged(dtdate, onChanged, l); l = uiNewLabel(""); uiGridAppend(g, uiControl(l), - 1, 4, 1, 1, + 1, 3, 1, 1, 1, uiAlignCenter, 0, uiAlignFill); uiDateTimePickerOnChanged(dttime, onChanged, l); - uiGridAppend(g, uiControl(uiNewVerticalSeparator()), - 0, 5, 2, 1, - 1, uiAlignFill, 0, uiAlignFill); - b = uiNewButton("Now"); uiButtonOnClicked(b, onClicked, (void *) 1); uiGridAppend(g, uiControl(b), - 0, 6, 1, 1, - 1, uiAlignFill, 0, uiAlignFill); + 0, 4, 1, 1, + 1, uiAlignFill, 1, uiAlignEnd); b = uiNewButton("Unix epoch"); uiButtonOnClicked(b, onClicked, (void *) 0); uiGridAppend(g, uiControl(b), - 1, 6, 1, 1, - 1, uiAlignFill, 0, uiAlignFill); + 1, 4, 1, 1, + 1, uiAlignFill, 1, uiAlignEnd); uiWindowOnClosing(w, onClosing, NULL); uiControlShow(uiControl(w)); From 04ce39a9413a183278a9092d7768ab3c606b4fef Mon Sep 17 00:00:00 2001 From: cody271 Date: Wed, 21 Feb 2018 21:47:45 -0800 Subject: [PATCH 1057/1329] Implement uiDateTimePickerTime() for OS X --- darwin/datetimepicker.m | 101 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/darwin/datetimepicker.m b/darwin/datetimepicker.m index 7a8bdd5b..c7067af8 100644 --- a/darwin/datetimepicker.m +++ b/darwin/datetimepicker.m @@ -4,10 +4,82 @@ struct uiDateTimePicker { uiDarwinControl c; NSDatePicker *dp; + void (*onChanged)(uiDateTimePicker *, void *); + void *onChangedData; }; +@interface datePickerDelegateClass : NSObject { + struct mapTable *pickers; +} +- (void)datePickerCell:(NSDatePickerCell *)aDatePickerCell + validateProposedDateValue:(NSDate **)proposedDateValue + timeInterval:(NSTimeInterval *)proposedTimeInterval; +- (void)doTimer:(NSTimer *)timer; +- (void)registerPicker:(uiDateTimePicker *)b; +- (void)unregisterPicker:(uiDateTimePicker *)b; +@end + +@implementation datePickerDelegateClass + +- (id)init +{ + self = [super init]; + if (self) + self->pickers = newMap(); + return self; +} + +- (void)dealloc +{ + mapDestroy(self->pickers); + [super dealloc]; +} + +- (void)datePickerCell:(NSDatePickerCell *)aDatePickerCell + validateProposedDateValue:(NSDate **)proposedDateValue + timeInterval:(NSTimeInterval *)proposedTimeInterval +{ + uiDateTimePicker *d; + + d = (uiDateTimePicker *) mapGet(self->pickers, aDatePickerCell); + [NSTimer scheduledTimerWithTimeInterval:0 + target:self + selector:@selector(doTimer:) + userInfo:[NSValue valueWithPointer:d] + repeats:NO]; +} + +- (void)doTimer:(NSTimer *)timer +{ + uiDateTimePicker *d; + + d = (uiDateTimePicker*) [[timer userInfo] pointerValue]; + (*(d->onChanged))(d, d->onChangedData); +} + +- (void)registerPicker:(uiDateTimePicker *)d +{ + mapSet(self->pickers, d->dp.cell, d); + [d->dp setDelegate:self]; +} + +- (void)unregisterPicker:(uiDateTimePicker *)d +{ + [d->dp setDelegate:nil]; + mapDelete(self->pickers, d->dp.cell); +} + +@end + +static datePickerDelegateClass *datePickerDelegate = nil; + uiDarwinControlAllDefaults(uiDateTimePicker, dp) +static void defaultOnChanged(uiDateTimePicker *d, void *data) +{ + // do nothing +} + static uiDateTimePicker *finishNewDateTimePicker(NSDatePickerElementFlags elements) { uiDateTimePicker *d; @@ -15,6 +87,7 @@ static uiDateTimePicker *finishNewDateTimePicker(NSDatePickerElementFlags elemen uiDarwinNewControl(uiDateTimePicker, d); d->dp = [[NSDatePicker alloc] initWithFrame:NSZeroRect]; + [d->dp setDateValue:[NSDate date]]; [d->dp setBordered:NO]; [d->dp setBezeled:YES]; [d->dp setDrawsBackground:YES]; @@ -23,6 +96,13 @@ static uiDateTimePicker *finishNewDateTimePicker(NSDatePickerElementFlags elemen [d->dp setDatePickerMode:NSSingleDateMode]; uiDarwinSetControlFont(d->dp, NSRegularControlSize); + if (datePickerDelegate == nil) { + datePickerDelegate = [[datePickerDelegateClass new] autorelease]; + [delegates addObject:datePickerDelegate]; + } + [datePickerDelegate registerPicker:d]; + uiDateTimePickerOnChanged(d, defaultOnChanged, NULL); + return d; } @@ -43,12 +123,33 @@ uiDateTimePicker *uiNewTimePicker(void) void uiDateTimePickerTime(uiDateTimePicker *d, struct tm *time) { + time_t t; + struct tm tmbuf; + NSDate *date; + NSTimeInterval interval; + + date = [d->dp dateValue]; + interval = [[NSTimeZone systemTimeZone] secondsFromGMTForDate:date]; + date = [date dateByAddingTimeInterval:interval]; + t = (time_t) [date timeIntervalSince1970]; + + tmbuf = *gmtime(&t); + memcpy(time, &tmbuf, sizeof(struct tm)); } void uiDateTimePickerSetTime(uiDateTimePicker *d, const struct tm *time) { + time_t t; + struct tm tmbuf; + + memcpy(&tmbuf, time, sizeof(struct tm)); + t = mktime(&tmbuf); + + [d->dp setDateValue:[NSDate dateWithTimeIntervalSince1970:t]]; } void uiDateTimePickerOnChanged(uiDateTimePicker *d, void (*f)(uiDateTimePicker *, void *), void *data) { + d->onChanged = f; + d->onChangedData = data; } From 57b225a629b828156c2777513abac7c66461e1e3 Mon Sep 17 00:00:00 2001 From: cody271 Date: Wed, 21 Feb 2018 22:06:21 -0800 Subject: [PATCH 1058/1329] Implement uiDateTimePickerTime() for GTK+ --- unix/datetimepicker.c | 98 ++++++++++++++++++++++++++++++------------- 1 file changed, 69 insertions(+), 29 deletions(-) diff --git a/unix/datetimepicker.c b/unix/datetimepicker.c index c15a610d..3e7b47c8 100644 --- a/unix/datetimepicker.c +++ b/unix/datetimepicker.c @@ -38,6 +38,7 @@ struct dateTimePickerWidget { GdkDevice *keyboard; GdkDevice *mouse; + uiDateTimePicker *control; }; struct dateTimePickerWidgetClass { @@ -46,6 +47,16 @@ struct dateTimePickerWidgetClass { G_DEFINE_TYPE(dateTimePickerWidget, dateTimePickerWidget, GTK_TYPE_TOGGLE_BUTTON) +struct uiDateTimePicker { + uiUnixControl c; + GtkWidget *widget; + dateTimePickerWidget *d; + void (*onChanged)(uiDateTimePicker *, void *); + void *onChangedData; +}; + +uiUnixControlAllDefaults(uiDateTimePicker) + static int realSpinValue(GtkSpinButton *spinButton) { GtkAdjustment *adj; @@ -111,8 +122,11 @@ static void setLabel(dateTimePickerWidget *d) static void dateTimeChanged(dateTimePickerWidget *d) { + uiDateTimePicker *c; + + c = d->control; + (*(c->onChanged))(c, c->onChangedData); setLabel(d); - // TODO fire event here } // we don't want ::toggled to be sent again @@ -422,13 +436,37 @@ static void setTimeOnly(dateTimePickerWidget *d) gtk_container_remove(GTK_CONTAINER(d->box), d->calendar); } -static void dateTimePickerWidget_init(dateTimePickerWidget *d) +static void dateTimePickerWidget_setTime(dateTimePickerWidget *d, GDateTime *dt) { - GDateTime *dt; gint year, month, day; gint hour; gulong calendarBlock; + // notice how we block signals from firing + if (d->hasDate) { + calendarBlock = g_signal_connect(d->calendar, "day-selected", G_CALLBACK(dateChanged), d); + g_date_time_get_ymd(dt, &year, &month, &day); + month--; // GDateTime/GtkCalendar differences + g_signal_handler_block(d->calendar, calendarBlock); + gtk_calendar_select_month(GTK_CALENDAR(d->calendar), month, year); + gtk_calendar_select_day(GTK_CALENDAR(d->calendar), day); + g_signal_handler_unblock(d->calendar, calendarBlock); + } + if (d->hasTime) { + hour = g_date_time_get_hour(dt); + if (hour >= 12) { + hour -= 12; + setRealSpinValue(GTK_SPIN_BUTTON(d->ampm), 1, d->ampmBlock); + } + setRealSpinValue(GTK_SPIN_BUTTON(d->hours), hour, d->hoursBlock); + setRealSpinValue(GTK_SPIN_BUTTON(d->minutes), g_date_time_get_minute(dt), d->minutesBlock); + setRealSpinValue(GTK_SPIN_BUTTON(d->seconds), g_date_time_get_seconds(dt), d->secondsBlock); + } + g_date_time_unref(dt); +} + +static void dateTimePickerWidget_init(dateTimePickerWidget *d) +{ d->window = gtk_window_new(GTK_WINDOW_POPUP); gtk_window_set_resizable(GTK_WINDOW(d->window), FALSE); gtk_window_set_attached_to(GTK_WINDOW(d->window), GTK_WIDGET(d)); @@ -446,7 +484,6 @@ static void dateTimePickerWidget_init(dateTimePickerWidget *d) gtk_container_add(GTK_CONTAINER(d->window), d->box); d->calendar = gtk_calendar_new(); - calendarBlock = g_signal_connect(d->calendar, "day-selected", G_CALLBACK(dateChanged), d); gtk_container_add(GTK_CONTAINER(d->box), d->calendar); d->timebox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); @@ -493,23 +530,7 @@ static void dateTimePickerWidget_init(dateTimePickerWidget *d) d->hasDate = TRUE; // set the current date/time - // notice how we block signals from firing - dt = g_date_time_new_now_local(); - g_date_time_get_ymd(dt, &year, &month, &day); - month--; // GDateTime/GtkCalendar differences - g_signal_handler_block(d->calendar, calendarBlock); - gtk_calendar_select_month(GTK_CALENDAR(d->calendar), month, year); - gtk_calendar_select_day(GTK_CALENDAR(d->calendar), day); - g_signal_handler_unblock(d->calendar, calendarBlock); - hour = g_date_time_get_hour(dt); - if (hour >= 12) { - hour -= 12; - setRealSpinValue(GTK_SPIN_BUTTON(d->ampm), 1, d->ampmBlock); - } - setRealSpinValue(GTK_SPIN_BUTTON(d->hours), hour, d->hoursBlock); - setRealSpinValue(GTK_SPIN_BUTTON(d->minutes), g_date_time_get_minute(dt), d->minutesBlock); - setRealSpinValue(GTK_SPIN_BUTTON(d->seconds), g_date_time_get_seconds(dt), d->secondsBlock); - g_date_time_unref(dt); + dateTimePickerWidget_setTime(d, g_date_time_new_now_local()); } static void dateTimePickerWidget_dispose(GObject *obj) @@ -534,6 +555,11 @@ static void dateTimePickerWidget_class_init(dateTimePickerWidgetClass *class) G_OBJECT_CLASS(class)->finalize = dateTimePickerWidget_finalize; } +static void defaultOnChanged(uiDateTimePicker *d, void *data) +{ + // do nothing +} + static GtkWidget *newDTP(void) { GtkWidget *w; @@ -563,14 +589,6 @@ static GtkWidget *newTP(void) return w; } -struct uiDateTimePicker { - uiUnixControl c; - GtkWidget *widget; - dateTimePickerWidget *d; -}; - -uiUnixControlAllDefaults(uiDateTimePicker) - uiDateTimePicker *finishNewDateTimePicker(GtkWidget *(*fn)(void)) { uiDateTimePicker *d; @@ -579,6 +597,8 @@ uiDateTimePicker *finishNewDateTimePicker(GtkWidget *(*fn)(void)) d->widget = (*fn)(); d->d = dateTimePickerWidget(d->widget); + d->d->control = d; + uiDateTimePickerOnChanged(d, defaultOnChanged, NULL); return d; } @@ -600,12 +620,32 @@ uiDateTimePicker *uiNewTimePicker(void) void uiDateTimePickerTime(uiDateTimePicker *d, struct tm *time) { + time_t t; + struct tm tmbuf; + GDateTime *dt; + + dt = selected(d->d); + t = g_date_time_to_unix(dt); + g_date_time_unref(dt); + + tmbuf = *localtime(&t); + memcpy(time, &tmbuf, sizeof(struct tm)); } void uiDateTimePickerSetTime(uiDateTimePicker *d, const struct tm *time) { + time_t t; + struct tm tmbuf; + + memcpy(&tmbuf, time, sizeof(struct tm)); + t = mktime(&tmbuf); + + dateTimePickerWidget_setTime(d->d, g_date_time_new_from_unix_local(t)); + dateTimeChanged(d->d); } void uiDateTimePickerOnChanged(uiDateTimePicker *d, void (*f)(uiDateTimePicker *, void *), void *data) { + d->onChanged = f; + d->onChangedData = data; } From cf671c1da5ecf6f2dfabbaf5bfb4c4a72dec2e7a Mon Sep 17 00:00:00 2001 From: cody271 Date: Thu, 22 Feb 2018 18:36:37 -0800 Subject: [PATCH 1059/1329] uiDateTimePicker Fix NSDatePicker timezone handling --- darwin/datetimepicker.m | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/darwin/datetimepicker.m b/darwin/datetimepicker.m index c7067af8..54c53b4b 100644 --- a/darwin/datetimepicker.m +++ b/darwin/datetimepicker.m @@ -126,14 +126,11 @@ void uiDateTimePickerTime(uiDateTimePicker *d, struct tm *time) time_t t; struct tm tmbuf; NSDate *date; - NSTimeInterval interval; date = [d->dp dateValue]; - interval = [[NSTimeZone systemTimeZone] secondsFromGMTForDate:date]; - date = [date dateByAddingTimeInterval:interval]; t = (time_t) [date timeIntervalSince1970]; - tmbuf = *gmtime(&t); + tmbuf = *localtime(&t); memcpy(time, &tmbuf, sizeof(struct tm)); } From 0baf2d2eb6d54057da8d5cb629fe18cd30fa2f0b Mon Sep 17 00:00:00 2001 From: cody271 Date: Thu, 22 Feb 2018 19:20:16 -0800 Subject: [PATCH 1060/1329] uiDateTimePicker Keep libui constructors at the end of the file --- darwin/datetimepicker.m | 60 +++++++++++++++++------------------ unix/datetimepicker.c | 64 +++++++++++++++++++------------------- windows/datetimepicker.cpp | 24 +++++++------- 3 files changed, 74 insertions(+), 74 deletions(-) diff --git a/darwin/datetimepicker.m b/darwin/datetimepicker.m index 54c53b4b..87ab0c3c 100644 --- a/darwin/datetimepicker.m +++ b/darwin/datetimepicker.m @@ -80,6 +80,36 @@ static void defaultOnChanged(uiDateTimePicker *d, void *data) // do nothing } +void uiDateTimePickerTime(uiDateTimePicker *d, struct tm *time) +{ + time_t t; + struct tm tmbuf; + NSDate *date; + + date = [d->dp dateValue]; + t = (time_t) [date timeIntervalSince1970]; + + tmbuf = *localtime(&t); + memcpy(time, &tmbuf, sizeof(struct tm)); +} + +void uiDateTimePickerSetTime(uiDateTimePicker *d, const struct tm *time) +{ + time_t t; + struct tm tmbuf; + + memcpy(&tmbuf, time, sizeof(struct tm)); + t = mktime(&tmbuf); + + [d->dp setDateValue:[NSDate dateWithTimeIntervalSince1970:t]]; +} + +void uiDateTimePickerOnChanged(uiDateTimePicker *d, void (*f)(uiDateTimePicker *, void *), void *data) +{ + d->onChanged = f; + d->onChangedData = data; +} + static uiDateTimePicker *finishNewDateTimePicker(NSDatePickerElementFlags elements) { uiDateTimePicker *d; @@ -120,33 +150,3 @@ uiDateTimePicker *uiNewTimePicker(void) { return finishNewDateTimePicker(NSHourMinuteSecondDatePickerElementFlag); } - -void uiDateTimePickerTime(uiDateTimePicker *d, struct tm *time) -{ - time_t t; - struct tm tmbuf; - NSDate *date; - - date = [d->dp dateValue]; - t = (time_t) [date timeIntervalSince1970]; - - tmbuf = *localtime(&t); - memcpy(time, &tmbuf, sizeof(struct tm)); -} - -void uiDateTimePickerSetTime(uiDateTimePicker *d, const struct tm *time) -{ - time_t t; - struct tm tmbuf; - - memcpy(&tmbuf, time, sizeof(struct tm)); - t = mktime(&tmbuf); - - [d->dp setDateValue:[NSDate dateWithTimeIntervalSince1970:t]]; -} - -void uiDateTimePickerOnChanged(uiDateTimePicker *d, void (*f)(uiDateTimePicker *, void *), void *data) -{ - d->onChanged = f; - d->onChangedData = data; -} diff --git a/unix/datetimepicker.c b/unix/datetimepicker.c index 3e7b47c8..2fa9f956 100644 --- a/unix/datetimepicker.c +++ b/unix/datetimepicker.c @@ -560,6 +560,38 @@ static void defaultOnChanged(uiDateTimePicker *d, void *data) // do nothing } +void uiDateTimePickerTime(uiDateTimePicker *d, struct tm *time) +{ + time_t t; + struct tm tmbuf; + GDateTime *dt; + + dt = selected(d->d); + t = g_date_time_to_unix(dt); + g_date_time_unref(dt); + + tmbuf = *localtime(&t); + memcpy(time, &tmbuf, sizeof(struct tm)); +} + +void uiDateTimePickerSetTime(uiDateTimePicker *d, const struct tm *time) +{ + time_t t; + struct tm tmbuf; + + memcpy(&tmbuf, time, sizeof(struct tm)); + t = mktime(&tmbuf); + + dateTimePickerWidget_setTime(d->d, g_date_time_new_from_unix_local(t)); + dateTimeChanged(d->d); +} + +void uiDateTimePickerOnChanged(uiDateTimePicker *d, void (*f)(uiDateTimePicker *, void *), void *data) +{ + d->onChanged = f; + d->onChangedData = data; +} + static GtkWidget *newDTP(void) { GtkWidget *w; @@ -617,35 +649,3 @@ uiDateTimePicker *uiNewTimePicker(void) { return finishNewDateTimePicker(newTP); } - -void uiDateTimePickerTime(uiDateTimePicker *d, struct tm *time) -{ - time_t t; - struct tm tmbuf; - GDateTime *dt; - - dt = selected(d->d); - t = g_date_time_to_unix(dt); - g_date_time_unref(dt); - - tmbuf = *localtime(&t); - memcpy(time, &tmbuf, sizeof(struct tm)); -} - -void uiDateTimePickerSetTime(uiDateTimePicker *d, const struct tm *time) -{ - time_t t; - struct tm tmbuf; - - memcpy(&tmbuf, time, sizeof(struct tm)); - t = mktime(&tmbuf); - - dateTimePickerWidget_setTime(d->d, g_date_time_new_from_unix_local(t)); - dateTimeChanged(d->d); -} - -void uiDateTimePickerOnChanged(uiDateTimePicker *d, void (*f)(uiDateTimePicker *, void *), void *data) -{ - d->onChanged = f; - d->onChangedData = data; -} diff --git a/windows/datetimepicker.cpp b/windows/datetimepicker.cpp index 40dfe7e9..7d8bea39 100644 --- a/windows/datetimepicker.cpp +++ b/windows/datetimepicker.cpp @@ -168,6 +168,18 @@ static LRESULT CALLBACK datetimepickerSubProc(HWND hwnd, UINT uMsg, WPARAM wPara return DefSubclassProc(hwnd, uMsg, wParam, lParam); } +void uiDateTimePickerTime(uiDateTimePicker *d, struct tm *time) +{ +} + +void uiDateTimePickerSetTime(uiDateTimePicker *d, const struct tm *time) +{ +} + +void uiDateTimePickerOnChanged(uiDateTimePicker *d, void (*f)(uiDateTimePicker *, void *), void *data) +{ +} + uiDateTimePicker *uiNewDateTimePicker(void) { uiDateTimePicker *d; @@ -189,15 +201,3 @@ uiDateTimePicker *uiNewTimePicker(void) { return finishNewDateTimePicker(DTS_TIMEFORMAT); } - -void uiDateTimePickerTime(uiDateTimePicker *d, struct tm *time) -{ -} - -void uiDateTimePickerSetTime(uiDateTimePicker *d, const struct tm *time) -{ -} - -void uiDateTimePickerOnChanged(uiDateTimePicker *d, void (*f)(uiDateTimePicker *, void *), void *data) -{ -} From 5ec5ebdfae872a7b0b12c0e1ffbd5fc9dd46dc95 Mon Sep 17 00:00:00 2001 From: cody271 Date: Thu, 22 Feb 2018 19:56:57 -0800 Subject: [PATCH 1061/1329] uiDateTimePicker Emit proper GTK+ signal in dateTimePickerWidget --- unix/datetimepicker.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/unix/datetimepicker.c b/unix/datetimepicker.c index 2fa9f956..0c0dba8a 100644 --- a/unix/datetimepicker.c +++ b/unix/datetimepicker.c @@ -38,7 +38,6 @@ struct dateTimePickerWidget { GdkDevice *keyboard; GdkDevice *mouse; - uiDateTimePicker *control; }; struct dateTimePickerWidgetClass { @@ -120,12 +119,11 @@ static void setLabel(dateTimePickerWidget *d) g_date_time_unref(dt); } +static int changedSignal; + static void dateTimeChanged(dateTimePickerWidget *d) { - uiDateTimePicker *c; - - c = d->control; - (*(c->onChanged))(c, c->onChangedData); + g_signal_emit(d, changedSignal, 0); setLabel(d); } @@ -553,6 +551,13 @@ static void dateTimePickerWidget_class_init(dateTimePickerWidgetClass *class) { G_OBJECT_CLASS(class)->dispose = dateTimePickerWidget_dispose; G_OBJECT_CLASS(class)->finalize = dateTimePickerWidget_finalize; + + changedSignal = g_signal_new("changed", + G_TYPE_FROM_CLASS(class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 0); } static void defaultOnChanged(uiDateTimePicker *d, void *data) @@ -592,6 +597,14 @@ void uiDateTimePickerOnChanged(uiDateTimePicker *d, void (*f)(uiDateTimePicker * d->onChangedData = data; } +static void onChanged(dateTimePickerWidget *d, gpointer data) +{ + uiDateTimePicker *c; + + c = uiDateTimePicker(data); + (*(c->onChanged))(c, c->onChangedData); +} + static GtkWidget *newDTP(void) { GtkWidget *w; @@ -629,7 +642,7 @@ uiDateTimePicker *finishNewDateTimePicker(GtkWidget *(*fn)(void)) d->widget = (*fn)(); d->d = dateTimePickerWidget(d->widget); - d->d->control = d; + g_signal_connect(d->widget, "changed", G_CALLBACK(onChanged), d); uiDateTimePickerOnChanged(d, defaultOnChanged, NULL); return d; From 594e3a1ccc4c100bb4c6742e3266d29d365f7dda Mon Sep 17 00:00:00 2001 From: cody271 Date: Thu, 22 Feb 2018 20:13:55 -0800 Subject: [PATCH 1062/1329] uiDateTimePicker Move struct definition back --- unix/datetimepicker.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/unix/datetimepicker.c b/unix/datetimepicker.c index 0c0dba8a..94fa6b9a 100644 --- a/unix/datetimepicker.c +++ b/unix/datetimepicker.c @@ -46,16 +46,6 @@ struct dateTimePickerWidgetClass { G_DEFINE_TYPE(dateTimePickerWidget, dateTimePickerWidget, GTK_TYPE_TOGGLE_BUTTON) -struct uiDateTimePicker { - uiUnixControl c; - GtkWidget *widget; - dateTimePickerWidget *d; - void (*onChanged)(uiDateTimePicker *, void *); - void *onChangedData; -}; - -uiUnixControlAllDefaults(uiDateTimePicker) - static int realSpinValue(GtkSpinButton *spinButton) { GtkAdjustment *adj; @@ -560,6 +550,16 @@ static void dateTimePickerWidget_class_init(dateTimePickerWidgetClass *class) G_TYPE_NONE, 0); } +struct uiDateTimePicker { + uiUnixControl c; + GtkWidget *widget; + dateTimePickerWidget *d; + void (*onChanged)(uiDateTimePicker *, void *); + void *onChangedData; +}; + +uiUnixControlAllDefaults(uiDateTimePicker) + static void defaultOnChanged(uiDateTimePicker *d, void *data) { // do nothing From 0ac4ffdc87ab3d5e40467ecfd1246171c87cbc01 Mon Sep 17 00:00:00 2001 From: cody271 Date: Fri, 23 Feb 2018 16:02:39 -0800 Subject: [PATCH 1063/1329] Implement uiDateTimePickerTime() for Windows --- windows/datetimepicker.cpp | 83 ++++++++++++++++++++++++++++++++------ 1 file changed, 71 insertions(+), 12 deletions(-) diff --git a/windows/datetimepicker.cpp b/windows/datetimepicker.cpp index 7d8bea39..2ea366c2 100644 --- a/windows/datetimepicker.cpp +++ b/windows/datetimepicker.cpp @@ -4,6 +4,8 @@ struct uiDateTimePicker { uiWindowsControl c; HWND hwnd; + void(*onChanged)(uiDateTimePicker *, void *); + void *onChangedData; }; // utility functions @@ -103,6 +105,7 @@ static void uiDateTimePickerDestroy(uiControl *c) uiDateTimePicker *d = uiDateTimePicker(c); uiWindowsUnregisterReceiveWM_WININICHANGE(d->hwnd); + uiWindowsUnregisterWM_NOTIFYHandler(d->hwnd); uiWindowsEnsureDestroyWindow(d->hwnd); uiFreeControl(uiControl(d)); } @@ -131,6 +134,72 @@ static void uiDateTimePickerMinimumSize(uiWindowsControl *c, int *width, int *he *height = y; } +static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult) +{ + uiDateTimePicker *d = uiDateTimePicker(c); + + if (nmhdr->code != DTN_DATETIMECHANGE) + return FALSE; + (*(d->onChanged))(d, d->onChangedData); + *lResult = 0; + return TRUE; +} + +static void fromSystemTime(LPSYSTEMTIME systime, struct tm *time) +{ + memset(time, 0, sizeof(struct tm)); + time->tm_sec = systime->wSecond; + time->tm_min = systime->wMinute; + time->tm_hour = systime->wHour; + time->tm_mday = systime->wDay; + time->tm_mon = systime->wMonth - 1; + time->tm_year = systime->wYear - 1900; + time->tm_wday = systime->wDayOfWeek; + time->tm_isdst = -1; +} + +static void toSystemTime(const struct tm *time, LPSYSTEMTIME systime) +{ + memset(systime, 0, sizeof(SYSTEMTIME)); + systime->wYear = time->tm_year + 1900; + systime->wMonth = time->tm_mon + 1; + systime->wDayOfWeek = time->tm_wday; + systime->wDay = time->tm_mday; + systime->wHour = time->tm_hour; + systime->wMinute = time->tm_min; + systime->wSecond = time->tm_sec; +} + +static void defaultOnChanged(uiDateTimePicker *d, void *data) +{ + // do nothing +} + +void uiDateTimePickerTime(uiDateTimePicker *d, struct tm *time) +{ + SYSTEMTIME systime; + + if (DateTime_GetSystemtime(d->hwnd, &systime) != GDT_VALID) + implbug("DTM_GETSYSTEMTIME message failed"); + fromSystemTime(&systime, time); +} + +void uiDateTimePickerSetTime(uiDateTimePicker *d, const struct tm *time) +{ + SYSTEMTIME systime; + + toSystemTime(time, &systime); + if (!DateTime_SetSystemtime(d->hwnd, GDT_VALID, &systime)) + implbug("DTM_SETSYSTEMTIME message failed"); + (*(d->onChanged))(d, d->onChangedData); +} + +void uiDateTimePickerOnChanged(uiDateTimePicker *d, void(*f)(uiDateTimePicker *, void *), void *data) +{ + d->onChanged = f; + d->onChangedData = data; +} + static uiDateTimePicker *finishNewDateTimePicker(DWORD style) { uiDateTimePicker *d; @@ -147,6 +216,8 @@ static uiDateTimePicker *finishNewDateTimePicker(DWORD style) // for the standard styles, this is in the date-time picker itself // for our date/time mode, we do it in a subclass assigned in uiNewDateTimePicker() uiWindowsRegisterReceiveWM_WININICHANGE(d->hwnd); + uiWindowsRegisterWM_NOTIFYHandler(d->hwnd, onWM_NOTIFY, uiControl(d)); + uiDateTimePickerOnChanged(d, defaultOnChanged, NULL); return d; } @@ -168,18 +239,6 @@ static LRESULT CALLBACK datetimepickerSubProc(HWND hwnd, UINT uMsg, WPARAM wPara return DefSubclassProc(hwnd, uMsg, wParam, lParam); } -void uiDateTimePickerTime(uiDateTimePicker *d, struct tm *time) -{ -} - -void uiDateTimePickerSetTime(uiDateTimePicker *d, const struct tm *time) -{ -} - -void uiDateTimePickerOnChanged(uiDateTimePicker *d, void (*f)(uiDateTimePicker *, void *), void *data) -{ -} - uiDateTimePicker *uiNewDateTimePicker(void) { uiDateTimePicker *d; From 0b436a8c7432d3c71feb46d35799654a0fbe69d0 Mon Sep 17 00:00:00 2001 From: cody271 Date: Sat, 24 Feb 2018 15:27:02 -0800 Subject: [PATCH 1064/1329] uiDateTimePicker Use proper Windows backend conventions --- windows/datetimepicker.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/windows/datetimepicker.cpp b/windows/datetimepicker.cpp index 2ea366c2..fc94c3e9 100644 --- a/windows/datetimepicker.cpp +++ b/windows/datetimepicker.cpp @@ -147,7 +147,7 @@ static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult) static void fromSystemTime(LPSYSTEMTIME systime, struct tm *time) { - memset(time, 0, sizeof(struct tm)); + ZeroMemory(time, sizeof(struct tm)); time->tm_sec = systime->wSecond; time->tm_min = systime->wMinute; time->tm_hour = systime->wHour; @@ -160,7 +160,7 @@ static void fromSystemTime(LPSYSTEMTIME systime, struct tm *time) static void toSystemTime(const struct tm *time, LPSYSTEMTIME systime) { - memset(systime, 0, sizeof(SYSTEMTIME)); + ZeroMemory(systime, sizeof(SYSTEMTIME)); systime->wYear = time->tm_year + 1900; systime->wMonth = time->tm_mon + 1; systime->wDayOfWeek = time->tm_wday; @@ -179,8 +179,8 @@ void uiDateTimePickerTime(uiDateTimePicker *d, struct tm *time) { SYSTEMTIME systime; - if (DateTime_GetSystemtime(d->hwnd, &systime) != GDT_VALID) - implbug("DTM_GETSYSTEMTIME message failed"); + if (SendMessageW(d->hwnd, DTM_GETSYSTEMTIME, 0, (LPARAM) &systime) != GDT_VALID) + logLastError(L"error getting date and time"); fromSystemTime(&systime, time); } @@ -189,8 +189,8 @@ void uiDateTimePickerSetTime(uiDateTimePicker *d, const struct tm *time) SYSTEMTIME systime; toSystemTime(time, &systime); - if (!DateTime_SetSystemtime(d->hwnd, GDT_VALID, &systime)) - implbug("DTM_SETSYSTEMTIME message failed"); + if (!SendMessageW(d->hwnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM) &systime)) + logLastError(L"error setting date and time"); (*(d->onChanged))(d, d->onChangedData); } From edd4127f8e14ffd4075d319ee77f571ddadd70f7 Mon Sep 17 00:00:00 2001 From: cody271 Date: Sat, 5 May 2018 21:21:25 -0700 Subject: [PATCH 1065/1329] uiDateTimePicker Fix style consistency --- darwin/datetimepicker.m | 6 ++---- examples/datetime/main.c | 7 +++---- unix/datetimepicker.c | 3 ++- windows/datetimepicker.cpp | 12 ++++++------ 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/darwin/datetimepicker.m b/darwin/datetimepicker.m index 87ab0c3c..7e9d6c6b 100644 --- a/darwin/datetimepicker.m +++ b/darwin/datetimepicker.m @@ -11,9 +11,7 @@ struct uiDateTimePicker { @interface datePickerDelegateClass : NSObject { struct mapTable *pickers; } -- (void)datePickerCell:(NSDatePickerCell *)aDatePickerCell - validateProposedDateValue:(NSDate **)proposedDateValue - timeInterval:(NSTimeInterval *)proposedTimeInterval; +- (void)datePickerCell:(NSDatePickerCell *)aDatePickerCell validateProposedDateValue:(NSDate **)proposedDateValue timeInterval:(NSTimeInterval *)proposedTimeInterval; - (void)doTimer:(NSTimer *)timer; - (void)registerPicker:(uiDateTimePicker *)b; - (void)unregisterPicker:(uiDateTimePicker *)b; @@ -53,7 +51,7 @@ struct uiDateTimePicker { { uiDateTimePicker *d; - d = (uiDateTimePicker*) [[timer userInfo] pointerValue]; + d = (uiDateTimePicker *) [((NSValue *)[timer userInfo]) pointerValue]; (*(d->onChanged))(d, d->onChangedData); } diff --git a/examples/datetime/main.c b/examples/datetime/main.c index a01dc889..e71b54a6 100644 --- a/examples/datetime/main.c +++ b/examples/datetime/main.c @@ -5,7 +5,7 @@ uiDateTimePicker *dtboth, *dtdate, *dttime; -const char * timeFormat(uiDateTimePicker *d) +const char *timeFormat(uiDateTimePicker *d) { const char *fmt; @@ -27,7 +27,7 @@ void onChanged(uiDateTimePicker *d, void *data) char buf[64]; uiDateTimePickerTime(d, &time); - strftime(buf, sizeof(buf), timeFormat(d), &time); + strftime(buf, sizeof (buf), timeFormat(d), &time); uiLabelSetText(uiLabel(data), buf); } @@ -46,8 +46,7 @@ void onClicked(uiButton *b, void *data) if (now) { uiDateTimePickerSetTime(dtdate, &tmbuf); uiDateTimePickerSetTime(dttime, &tmbuf); - } - else + } else uiDateTimePickerSetTime(dtboth, &tmbuf); } diff --git a/unix/datetimepicker.c b/unix/datetimepicker.c index 94fa6b9a..c4c9decb 100644 --- a/unix/datetimepicker.c +++ b/unix/datetimepicker.c @@ -547,7 +547,8 @@ static void dateTimePickerWidget_class_init(dateTimePickerWidgetClass *class) G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, - G_TYPE_NONE, 0); + G_TYPE_NONE, + 0); } struct uiDateTimePicker { diff --git a/windows/datetimepicker.cpp b/windows/datetimepicker.cpp index fc94c3e9..26841dae 100644 --- a/windows/datetimepicker.cpp +++ b/windows/datetimepicker.cpp @@ -145,9 +145,9 @@ static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult) return TRUE; } -static void fromSystemTime(LPSYSTEMTIME systime, struct tm *time) +static void fromSystemTime(SYSTEMTIME *systime, struct tm *time) { - ZeroMemory(time, sizeof(struct tm)); + ZeroMemory(time, sizeof (struct tm)); time->tm_sec = systime->wSecond; time->tm_min = systime->wMinute; time->tm_hour = systime->wHour; @@ -158,9 +158,9 @@ static void fromSystemTime(LPSYSTEMTIME systime, struct tm *time) time->tm_isdst = -1; } -static void toSystemTime(const struct tm *time, LPSYSTEMTIME systime) +static void toSystemTime(const struct tm *time, SYSTEMTIME *systime) { - ZeroMemory(systime, sizeof(SYSTEMTIME)); + ZeroMemory(systime, sizeof (SYSTEMTIME)); systime->wYear = time->tm_year + 1900; systime->wMonth = time->tm_mon + 1; systime->wDayOfWeek = time->tm_wday; @@ -179,7 +179,7 @@ void uiDateTimePickerTime(uiDateTimePicker *d, struct tm *time) { SYSTEMTIME systime; - if (SendMessageW(d->hwnd, DTM_GETSYSTEMTIME, 0, (LPARAM) &systime) != GDT_VALID) + if (SendMessageW(d->hwnd, DTM_GETSYSTEMTIME, 0, (LPARAM) (&systime)) != GDT_VALID) logLastError(L"error getting date and time"); fromSystemTime(&systime, time); } @@ -189,7 +189,7 @@ void uiDateTimePickerSetTime(uiDateTimePicker *d, const struct tm *time) SYSTEMTIME systime; toSystemTime(time, &systime); - if (!SendMessageW(d->hwnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM) &systime)) + if (SendMessageW(d->hwnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM) (&systime)) == 0) logLastError(L"error setting date and time"); (*(d->onChanged))(d, d->onChangedData); } From f3d0fee21a37eac29e880fc32dbec6cdf4deab83 Mon Sep 17 00:00:00 2001 From: cody271 Date: Sat, 5 May 2018 21:28:19 -0700 Subject: [PATCH 1066/1329] uiDateTimePicker Use 'uipriv' convention --- darwin/datetimepicker.m | 8 ++-- unix/datetimepicker.c | 102 ++++++++++++++++++++-------------------- 2 files changed, 55 insertions(+), 55 deletions(-) diff --git a/darwin/datetimepicker.m b/darwin/datetimepicker.m index 7e9d6c6b..fadf94ca 100644 --- a/darwin/datetimepicker.m +++ b/darwin/datetimepicker.m @@ -8,7 +8,7 @@ struct uiDateTimePicker { void *onChangedData; }; -@interface datePickerDelegateClass : NSObject { +@interface uiprivDatePickerDelegateClass : NSObject { struct mapTable *pickers; } - (void)datePickerCell:(NSDatePickerCell *)aDatePickerCell validateProposedDateValue:(NSDate **)proposedDateValue timeInterval:(NSTimeInterval *)proposedTimeInterval; @@ -17,7 +17,7 @@ struct uiDateTimePicker { - (void)unregisterPicker:(uiDateTimePicker *)b; @end -@implementation datePickerDelegateClass +@implementation uiprivDatePickerDelegateClass - (id)init { @@ -69,7 +69,7 @@ struct uiDateTimePicker { @end -static datePickerDelegateClass *datePickerDelegate = nil; +static uiprivDatePickerDelegateClass *datePickerDelegate = nil; uiDarwinControlAllDefaults(uiDateTimePicker, dp) @@ -125,7 +125,7 @@ static uiDateTimePicker *finishNewDateTimePicker(NSDatePickerElementFlags elemen uiDarwinSetControlFont(d->dp, NSRegularControlSize); if (datePickerDelegate == nil) { - datePickerDelegate = [[datePickerDelegateClass new] autorelease]; + datePickerDelegate = [[uiprivDatePickerDelegateClass new] autorelease]; [delegates addObject:datePickerDelegate]; } [datePickerDelegate registerPicker:d]; diff --git a/unix/datetimepicker.c b/unix/datetimepicker.c index c4c9decb..46ea19e8 100644 --- a/unix/datetimepicker.c +++ b/unix/datetimepicker.c @@ -4,17 +4,17 @@ // LONGTERM imitate gnome-calendar's day/month/year entries above the calendar // LONGTERM allow entering a 24-hour hour in the hour spinbutton and adjust accordingly -#define dateTimePickerWidgetType (dateTimePickerWidget_get_type()) -#define dateTimePickerWidget(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), dateTimePickerWidgetType, dateTimePickerWidget)) -#define isDateTimePickerWidget(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), dateTimePickerWidgetType)) -#define dateTimePickerWidgetClass(class) (G_TYPE_CHECK_CLASS_CAST((class), dateTimePickerWidgetType, dateTimePickerWidgetClass)) -#define isDateTimePickerWidgetClass(class) (G_TYPE_CHECK_CLASS_TYPE((class), dateTimePickerWidget)) -#define getDateTimePickerWidgetClass(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), dateTimePickerWidgetType, dateTimePickerWidgetClass)) +#define uiprivDateTimePickerWidgetType (uiprivDateTimePickerWidget_get_type()) +#define uiprivDateTimePickerWidget(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), uiprivDateTimePickerWidgetType, uiprivDateTimePickerWidget)) +#define isDateTimePickerWidget(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), uiprivDateTimePickerWidgetType)) +#define uiprivDateTimePickerWidgetClass(class) (G_TYPE_CHECK_CLASS_CAST((class), uiprivDateTimePickerWidgetType, uiprivDateTimePickerWidgetClass)) +#define isDateTimePickerWidgetClass(class) (G_TYPE_CHECK_CLASS_TYPE((class), uiprivDateTimePickerWidget)) +#define getDateTimePickerWidgetClass(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), uiprivDateTimePickerWidgetType, uiprivDateTimePickerWidgetClass)) -typedef struct dateTimePickerWidget dateTimePickerWidget; -typedef struct dateTimePickerWidgetClass dateTimePickerWidgetClass; +typedef struct uiprivDateTimePickerWidget uiprivDateTimePickerWidget; +typedef struct uiprivDateTimePickerWidgetClass uiprivDateTimePickerWidgetClass; -struct dateTimePickerWidget { +struct uiprivDateTimePickerWidget { GtkToggleButton parent_instance; gulong toggledSignal; @@ -40,11 +40,11 @@ struct dateTimePickerWidget { GdkDevice *mouse; }; -struct dateTimePickerWidgetClass { +struct uiprivDateTimePickerWidgetClass { GtkToggleButtonClass parent_class; }; -G_DEFINE_TYPE(dateTimePickerWidget, dateTimePickerWidget, GTK_TYPE_TOGGLE_BUTTON) +G_DEFINE_TYPE(uiprivDateTimePickerWidget, uiprivDateTimePickerWidget, GTK_TYPE_TOGGLE_BUTTON) static int realSpinValue(GtkSpinButton *spinButton) { @@ -64,7 +64,7 @@ static void setRealSpinValue(GtkSpinButton *spinButton, int value, gulong block) g_signal_handler_unblock(spinButton, block); } -static GDateTime *selected(dateTimePickerWidget *d) +static GDateTime *selected(uiprivDateTimePickerWidget *d) { // choose a day for which all times are likely to be valid for the default date in case we're only dealing with time guint year = 1970, month = 1, day = 1; @@ -84,7 +84,7 @@ static GDateTime *selected(dateTimePickerWidget *d) return g_date_time_new_local(year, month, day, hour, minute, second); } -static void setLabel(dateTimePickerWidget *d) +static void setLabel(uiprivDateTimePickerWidget *d) { GDateTime *dt; char *fmt; @@ -111,14 +111,14 @@ static void setLabel(dateTimePickerWidget *d) static int changedSignal; -static void dateTimeChanged(dateTimePickerWidget *d) +static void dateTimeChanged(uiprivDateTimePickerWidget *d) { g_signal_emit(d, changedSignal, 0); setLabel(d); } // we don't want ::toggled to be sent again -static void setActive(dateTimePickerWidget *d, gboolean active) +static void setActive(uiprivDateTimePickerWidget *d, gboolean active) { g_signal_handler_block(d, d->toggledSignal); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d), active); @@ -126,7 +126,7 @@ static void setActive(dateTimePickerWidget *d, gboolean active) } // like startGrab() below, a lot of this is in the order that GtkComboBox does it -static void endGrab(dateTimePickerWidget *d) +static void endGrab(uiprivDateTimePickerWidget *d) { if (d->keyboard != NULL) gdk_device_ungrab(d->keyboard, GDK_CURRENT_TIME); @@ -136,7 +136,7 @@ static void endGrab(dateTimePickerWidget *d) d->mouse = NULL; } -static void hidePopup(dateTimePickerWidget *d) +static void hidePopup(uiprivDateTimePickerWidget *d) { endGrab(d); gtk_widget_hide(d->window); @@ -144,7 +144,7 @@ static void hidePopup(dateTimePickerWidget *d) } // this consolidates a good chunk of what GtkComboBox does -static gboolean startGrab(dateTimePickerWidget *d) +static gboolean startGrab(uiprivDateTimePickerWidget *d) { GdkDevice *dev; guint32 time; @@ -199,7 +199,7 @@ static gboolean startGrab(dateTimePickerWidget *d) } // based on gtk_combo_box_list_position() in the GTK+ source code -static void allocationToScreen(dateTimePickerWidget *d, gint *x, gint *y) +static void allocationToScreen(uiprivDateTimePickerWidget *d, gint *x, gint *y) { GdkWindow *window; GtkAllocation a; @@ -239,7 +239,7 @@ static void allocationToScreen(dateTimePickerWidget *d, gint *x, gint *y) *y = otherY; } -static void showPopup(dateTimePickerWidget *d) +static void showPopup(uiprivDateTimePickerWidget *d) { GtkWidget *toplevel; gint x, y; @@ -261,7 +261,7 @@ static void showPopup(dateTimePickerWidget *d) static void onToggled(GtkToggleButton *b, gpointer data) { - dateTimePickerWidget *d = dateTimePickerWidget(b); + uiprivDateTimePickerWidget *d = uiprivDateTimePickerWidget(b); if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(d))) showPopup(d); @@ -271,7 +271,7 @@ static void onToggled(GtkToggleButton *b, gpointer data) static gboolean grabBroken(GtkWidget *w, GdkEventGrabBroken *e, gpointer data) { - dateTimePickerWidget *d = dateTimePickerWidget(data); + uiprivDateTimePickerWidget *d = uiprivDateTimePickerWidget(data); hidePopup(d); return TRUE; // this is what GtkComboBox does @@ -279,7 +279,7 @@ static gboolean grabBroken(GtkWidget *w, GdkEventGrabBroken *e, gpointer data) static gboolean buttonReleased(GtkWidget *w, GdkEventButton *e, gpointer data) { - dateTimePickerWidget *d = dateTimePickerWidget(data); + uiprivDateTimePickerWidget *d = uiprivDateTimePickerWidget(data); int winx, winy; GtkAllocation wina; gboolean in; @@ -384,12 +384,12 @@ static gboolean ampmSpinboxOutput(GtkSpinButton *sb, gpointer data) static void spinboxChanged(GtkSpinButton *sb, gpointer data) { - dateTimePickerWidget *d = dateTimePickerWidget(data); + uiprivDateTimePickerWidget *d = uiprivDateTimePickerWidget(data); dateTimeChanged(d); } -static GtkWidget *newSpinbox(dateTimePickerWidget *d, int min, int max, gint (*input)(GtkSpinButton *, gpointer, gpointer), gboolean (*output)(GtkSpinButton *, gpointer), gulong *block) +static GtkWidget *newSpinbox(uiprivDateTimePickerWidget *d, int min, int max, gint (*input)(GtkSpinButton *, gpointer, gpointer), gboolean (*output)(GtkSpinButton *, gpointer), gulong *block) { GtkWidget *sb; @@ -407,24 +407,24 @@ static GtkWidget *newSpinbox(dateTimePickerWidget *d, int min, int max, gint (*i static void dateChanged(GtkCalendar *c, gpointer data) { - dateTimePickerWidget *d = dateTimePickerWidget(data); + uiprivDateTimePickerWidget *d = uiprivDateTimePickerWidget(data); dateTimeChanged(d); } -static void setDateOnly(dateTimePickerWidget *d) +static void setDateOnly(uiprivDateTimePickerWidget *d) { d->hasTime = FALSE; gtk_container_remove(GTK_CONTAINER(d->box), d->timebox); } -static void setTimeOnly(dateTimePickerWidget *d) +static void setTimeOnly(uiprivDateTimePickerWidget *d) { d->hasDate = FALSE; gtk_container_remove(GTK_CONTAINER(d->box), d->calendar); } -static void dateTimePickerWidget_setTime(dateTimePickerWidget *d, GDateTime *dt) +static void uiprivDateTimePickerWidget_setTime(uiprivDateTimePickerWidget *d, GDateTime *dt) { gint year, month, day; gint hour; @@ -453,7 +453,7 @@ static void dateTimePickerWidget_setTime(dateTimePickerWidget *d, GDateTime *dt) g_date_time_unref(dt); } -static void dateTimePickerWidget_init(dateTimePickerWidget *d) +static void uiprivDateTimePickerWidget_init(uiprivDateTimePickerWidget *d) { d->window = gtk_window_new(GTK_WINDOW_POPUP); gtk_window_set_resizable(GTK_WINDOW(d->window), FALSE); @@ -518,29 +518,29 @@ static void dateTimePickerWidget_init(dateTimePickerWidget *d) d->hasDate = TRUE; // set the current date/time - dateTimePickerWidget_setTime(d, g_date_time_new_now_local()); + uiprivDateTimePickerWidget_setTime(d, g_date_time_new_now_local()); } -static void dateTimePickerWidget_dispose(GObject *obj) +static void uiprivDateTimePickerWidget_dispose(GObject *obj) { - dateTimePickerWidget *d = dateTimePickerWidget(obj); + uiprivDateTimePickerWidget *d = uiprivDateTimePickerWidget(obj); if (d->window != NULL) { gtk_widget_destroy(d->window); d->window = NULL; } - G_OBJECT_CLASS(dateTimePickerWidget_parent_class)->dispose(obj); + G_OBJECT_CLASS(uiprivDateTimePickerWidget_parent_class)->dispose(obj); } -static void dateTimePickerWidget_finalize(GObject *obj) +static void uiprivDateTimePickerWidget_finalize(GObject *obj) { - G_OBJECT_CLASS(dateTimePickerWidget_parent_class)->finalize(obj); + G_OBJECT_CLASS(uiprivDateTimePickerWidget_parent_class)->finalize(obj); } -static void dateTimePickerWidget_class_init(dateTimePickerWidgetClass *class) +static void uiprivDateTimePickerWidget_class_init(uiprivDateTimePickerWidgetClass *class) { - G_OBJECT_CLASS(class)->dispose = dateTimePickerWidget_dispose; - G_OBJECT_CLASS(class)->finalize = dateTimePickerWidget_finalize; + G_OBJECT_CLASS(class)->dispose = uiprivDateTimePickerWidget_dispose; + G_OBJECT_CLASS(class)->finalize = uiprivDateTimePickerWidget_finalize; changedSignal = g_signal_new("changed", G_TYPE_FROM_CLASS(class), @@ -554,7 +554,7 @@ static void dateTimePickerWidget_class_init(dateTimePickerWidgetClass *class) struct uiDateTimePicker { uiUnixControl c; GtkWidget *widget; - dateTimePickerWidget *d; + uiprivDateTimePickerWidget *d; void (*onChanged)(uiDateTimePicker *, void *); void *onChangedData; }; @@ -588,7 +588,7 @@ void uiDateTimePickerSetTime(uiDateTimePicker *d, const struct tm *time) memcpy(&tmbuf, time, sizeof(struct tm)); t = mktime(&tmbuf); - dateTimePickerWidget_setTime(d->d, g_date_time_new_from_unix_local(t)); + uiprivDateTimePickerWidget_setTime(d->d, g_date_time_new_from_unix_local(t)); dateTimeChanged(d->d); } @@ -598,7 +598,7 @@ void uiDateTimePickerOnChanged(uiDateTimePicker *d, void (*f)(uiDateTimePicker * d->onChangedData = data; } -static void onChanged(dateTimePickerWidget *d, gpointer data) +static void onChanged(uiprivDateTimePickerWidget *d, gpointer data) { uiDateTimePicker *c; @@ -610,8 +610,8 @@ static GtkWidget *newDTP(void) { GtkWidget *w; - w = GTK_WIDGET(g_object_new(dateTimePickerWidgetType, "label", "", NULL)); - setLabel(dateTimePickerWidget(w)); + w = GTK_WIDGET(g_object_new(uiprivDateTimePickerWidgetType, "label", "", NULL)); + setLabel(uiprivDateTimePickerWidget(w)); return w; } @@ -619,9 +619,9 @@ static GtkWidget *newDP(void) { GtkWidget *w; - w = GTK_WIDGET(g_object_new(dateTimePickerWidgetType, "label", "", NULL)); - setDateOnly(dateTimePickerWidget(w)); - setLabel(dateTimePickerWidget(w)); + w = GTK_WIDGET(g_object_new(uiprivDateTimePickerWidgetType, "label", "", NULL)); + setDateOnly(uiprivDateTimePickerWidget(w)); + setLabel(uiprivDateTimePickerWidget(w)); return w; } @@ -629,9 +629,9 @@ static GtkWidget *newTP(void) { GtkWidget *w; - w = GTK_WIDGET(g_object_new(dateTimePickerWidgetType, "label", "", NULL)); - setTimeOnly(dateTimePickerWidget(w)); - setLabel(dateTimePickerWidget(w)); + w = GTK_WIDGET(g_object_new(uiprivDateTimePickerWidgetType, "label", "", NULL)); + setTimeOnly(uiprivDateTimePickerWidget(w)); + setLabel(uiprivDateTimePickerWidget(w)); return w; } @@ -642,7 +642,7 @@ uiDateTimePicker *finishNewDateTimePicker(GtkWidget *(*fn)(void)) uiUnixNewControl(uiDateTimePicker, d); d->widget = (*fn)(); - d->d = dateTimePickerWidget(d->widget); + d->d = uiprivDateTimePickerWidget(d->widget); g_signal_connect(d->widget, "changed", G_CALLBACK(onChanged), d); uiDateTimePickerOnChanged(d, defaultOnChanged, NULL); From 266c6339725b84fb56bf142224a9224c94eb7f10 Mon Sep 17 00:00:00 2001 From: cody271 Date: Sat, 5 May 2018 22:04:54 -0700 Subject: [PATCH 1067/1329] uiprivDateTimePickerWidget Fix calendarBlock GTK+ signal --- unix/datetimepicker.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/unix/datetimepicker.c b/unix/datetimepicker.c index 46ea19e8..dc2a0328 100644 --- a/unix/datetimepicker.c +++ b/unix/datetimepicker.c @@ -31,6 +31,7 @@ struct uiprivDateTimePickerWidget { GtkWidget *seconds; GtkWidget *ampm; + gulong calendarBlock; gulong hoursBlock; gulong minutesBlock; gulong secondsBlock; @@ -428,17 +429,15 @@ static void uiprivDateTimePickerWidget_setTime(uiprivDateTimePickerWidget *d, GD { gint year, month, day; gint hour; - gulong calendarBlock; // notice how we block signals from firing if (d->hasDate) { - calendarBlock = g_signal_connect(d->calendar, "day-selected", G_CALLBACK(dateChanged), d); g_date_time_get_ymd(dt, &year, &month, &day); month--; // GDateTime/GtkCalendar differences - g_signal_handler_block(d->calendar, calendarBlock); + g_signal_handler_block(d->calendar, d->calendarBlock); gtk_calendar_select_month(GTK_CALENDAR(d->calendar), month, year); gtk_calendar_select_day(GTK_CALENDAR(d->calendar), day); - g_signal_handler_unblock(d->calendar, calendarBlock); + g_signal_handler_unblock(d->calendar, d->calendarBlock); } if (d->hasTime) { hour = g_date_time_get_hour(dt); @@ -472,6 +471,7 @@ static void uiprivDateTimePickerWidget_init(uiprivDateTimePickerWidget *d) gtk_container_add(GTK_CONTAINER(d->window), d->box); d->calendar = gtk_calendar_new(); + d->calendarBlock = g_signal_connect(d->calendar, "day-selected", G_CALLBACK(dateChanged), d); gtk_container_add(GTK_CONTAINER(d->box), d->calendar); d->timebox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); From 9aba97714d1e9c0413267c894284587376a350c5 Mon Sep 17 00:00:00 2001 From: cody271 Date: Sat, 12 May 2018 20:03:20 -0700 Subject: [PATCH 1068/1329] uiDateTimePicker Add comments --- darwin/datetimepicker.m | 3 +++ ui.h | 1 + unix/datetimepicker.c | 3 +++ 3 files changed, 7 insertions(+) diff --git a/darwin/datetimepicker.m b/darwin/datetimepicker.m index fadf94ca..3c09e72a 100644 --- a/darwin/datetimepicker.m +++ b/darwin/datetimepicker.m @@ -87,6 +87,8 @@ void uiDateTimePickerTime(uiDateTimePicker *d, struct tm *time) date = [d->dp dateValue]; t = (time_t) [date timeIntervalSince1970]; + // Copy time to minimize a race condition + // time.h functions use global non-thread-safe data tmbuf = *localtime(&t); memcpy(time, &tmbuf, sizeof(struct tm)); } @@ -96,6 +98,7 @@ void uiDateTimePickerSetTime(uiDateTimePicker *d, const struct tm *time) time_t t; struct tm tmbuf; + // Copy time because mktime() modifies its argument memcpy(&tmbuf, time, sizeof(struct tm)); t = mktime(&tmbuf); diff --git a/ui.h b/ui.h index 765b7733..b5fb9a27 100644 --- a/ui.h +++ b/ui.h @@ -251,6 +251,7 @@ _UI_EXTERN uiRadioButtons *uiNewRadioButtons(void); struct tm; typedef struct uiDateTimePicker uiDateTimePicker; #define uiDateTimePicker(this) ((uiDateTimePicker *) (this)) +// TODO document that tm_wday and tm_yday are undefined, and tm_isdst should be -1 _UI_EXTERN void uiDateTimePickerTime(uiDateTimePicker *d, struct tm *time); _UI_EXTERN void uiDateTimePickerSetTime(uiDateTimePicker *d, const struct tm *time); _UI_EXTERN void uiDateTimePickerOnChanged(uiDateTimePicker *d, void (*f)(uiDateTimePicker *, void *), void *data); diff --git a/unix/datetimepicker.c b/unix/datetimepicker.c index dc2a0328..5f052dca 100644 --- a/unix/datetimepicker.c +++ b/unix/datetimepicker.c @@ -576,6 +576,8 @@ void uiDateTimePickerTime(uiDateTimePicker *d, struct tm *time) t = g_date_time_to_unix(dt); g_date_time_unref(dt); + // Copy time to minimize a race condition + // time.h functions use global non-thread-safe data tmbuf = *localtime(&t); memcpy(time, &tmbuf, sizeof(struct tm)); } @@ -585,6 +587,7 @@ void uiDateTimePickerSetTime(uiDateTimePicker *d, const struct tm *time) time_t t; struct tm tmbuf; + // Copy time because mktime() modifies its argument memcpy(&tmbuf, time, sizeof(struct tm)); t = mktime(&tmbuf); From fd3b693ffe9ca7d1bcca1c5a386c8b28d836ae6e Mon Sep 17 00:00:00 2001 From: cody271 Date: Sat, 12 May 2018 21:40:47 -0700 Subject: [PATCH 1069/1329] uiDateTimePicker Use more 'uipriv' on OS X --- darwin/datetimepicker.m | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/darwin/datetimepicker.m b/darwin/datetimepicker.m index 3c09e72a..54c6ba38 100644 --- a/darwin/datetimepicker.m +++ b/darwin/datetimepicker.m @@ -9,7 +9,7 @@ struct uiDateTimePicker { }; @interface uiprivDatePickerDelegateClass : NSObject { - struct mapTable *pickers; + struct uiprivMap *pickers; } - (void)datePickerCell:(NSDatePickerCell *)aDatePickerCell validateProposedDateValue:(NSDate **)proposedDateValue timeInterval:(NSTimeInterval *)proposedTimeInterval; - (void)doTimer:(NSTimer *)timer; @@ -23,13 +23,13 @@ struct uiDateTimePicker { { self = [super init]; if (self) - self->pickers = newMap(); + self->pickers = uiprivNewMap(); return self; } - (void)dealloc { - mapDestroy(self->pickers); + uiprivMapDestroy(self->pickers); [super dealloc]; } @@ -39,7 +39,7 @@ struct uiDateTimePicker { { uiDateTimePicker *d; - d = (uiDateTimePicker *) mapGet(self->pickers, aDatePickerCell); + d = (uiDateTimePicker *) uiprivMapGet(self->pickers, aDatePickerCell); [NSTimer scheduledTimerWithTimeInterval:0 target:self selector:@selector(doTimer:) @@ -57,14 +57,14 @@ struct uiDateTimePicker { - (void)registerPicker:(uiDateTimePicker *)d { - mapSet(self->pickers, d->dp.cell, d); + uiprivMapSet(self->pickers, d->dp.cell, d); [d->dp setDelegate:self]; } - (void)unregisterPicker:(uiDateTimePicker *)d { [d->dp setDelegate:nil]; - mapDelete(self->pickers, d->dp.cell); + uiprivMapDelete(self->pickers, d->dp.cell); } @end @@ -129,7 +129,7 @@ static uiDateTimePicker *finishNewDateTimePicker(NSDatePickerElementFlags elemen if (datePickerDelegate == nil) { datePickerDelegate = [[uiprivDatePickerDelegateClass new] autorelease]; - [delegates addObject:datePickerDelegate]; + [uiprivDelegates addObject:datePickerDelegate]; } [datePickerDelegate registerPicker:d]; uiDateTimePickerOnChanged(d, defaultOnChanged, NULL); From 8874532302bb1f5a104134da4deedec01ba07f38 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 13 May 2018 11:18:43 -0400 Subject: [PATCH 1070/1329] Updated the README with the previous commit and updated CMakeLists.txt to address issues raised by the other PR. Update #261 --- CMakeLists.txt | 2 ++ README.md | 3 +++ 2 files changed, 5 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 239d6b84..17c87683 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,7 @@ set(MSVC_INCREMENTAL_DEFAULT ON) # default to debug builds # do this before project() just to be safe +# either DEBUG or Debug will work; we use DEBUG as that's what cmake does internally (https://cmake.org/pipermail/cmake/2013-June/055177.html) if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE DEBUG CACHE STRING "" FORCE) endif() @@ -75,6 +76,7 @@ if(MSVC) # TODO /sdl turns C4996 into an ERROR # don't use /analyze; that requires us to write annotations everywhere # TODO undecided flags from qo? + # the RTC flags are only supplied in debug builds because they are only supposed to be used by debug builds (see "This is because run-time error checks are not valid in a release (optimized) build." in https://docs.microsoft.com/cpp/build/reference/rtc-run-time-error-checks) # /RTCc is not supplied because it's discouraged as of VS2015; see https://www.reddit.com/r/cpp/comments/46mhne/rtcc_rejects_conformant_code_with_visual_c_2015/d06auq5 # /EHsc is to shut the compiler up in some cases # TODO make /EHsc C++-only diff --git a/README.md b/README.md index c1c04797..8495d007 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,9 @@ This README is being written.
*Note that today's entry (Eastern Time) may be updated later today.* +* **13 May 2018** + * Release builds on Windows with MSVC should be fixed now; thanks @l0calh05t, @slahn, @mischnic, and @zentner-kyle. + * **12 May 2018** * GTK+ and OS X now have a cleaner build process for static libraries which no longer has intermediate files and differing configurations. As a result, certain issues should no longer be present. New naming rules for internal symbols of libui have also started being drafted; runtime symbols and edge cases still need to be handled (and the rules applied to Windows) before this can become a regular thing. From ca1079f34478420b272340619b6341675cc872af Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 13 May 2018 12:05:09 -0400 Subject: [PATCH 1071/1329] Cleaned up from the previous merge and added it to the README. --- README.md | 3 +++ darwin/datetimepicker.m | 21 ++++++++++++--------- examples/CMakeLists.txt | 14 +++++++------- examples/datetime/main.c | 10 +++++++--- unix/datetimepicker.c | 5 +++-- windows/datetimepicker.cpp | 5 ++--- 6 files changed, 34 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 8495d007..de83d890 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,9 @@ This README is being written.
## Announcements +* **13 May 2018** + * Added new functions to work with uiDateTimePickers: `uiDateTimePickerTime()`, `uiDateTimePickerSetTime()`, and `uiDateTimePickerOnChanged()`. These operate on standard `` `struct tm`s. Thanks @cody271! + * **2 May 2018** * On Windows, you no longer need to carry around a `libui.res` file with static builds. You do need to link in the appropriate manifest file, such as the one in the `windows/` folder (I still need to figure out exactly what is needed apart from the Common Controls v6 dependency, or at least to create a complete-ish template), or at least include it alongside your executables. This also means you should no longer see random cmake errors when building the static libraries. diff --git a/darwin/datetimepicker.m b/darwin/datetimepicker.m index 54c6ba38..d4424678 100644 --- a/darwin/datetimepicker.m +++ b/darwin/datetimepicker.m @@ -8,8 +8,10 @@ struct uiDateTimePicker { void *onChangedData; }; -@interface uiprivDatePickerDelegateClass : NSObject { - struct uiprivMap *pickers; +// TODO see if target-action works here or not; I forgot what cody271@ originally said +// the primary advantage of the delegate is the ability to reject changes, but libui doesn't support that yet — we should consider that API option as well +@interface uiprivDatePickerDelegateClass : NSObject { + uiprivMap *pickers; } - (void)datePickerCell:(NSDatePickerCell *)aDatePickerCell validateProposedDateValue:(NSDate **)proposedDateValue timeInterval:(NSTimeInterval *)proposedTimeInterval; - (void)doTimer:(NSTimer *)timer; @@ -33,13 +35,11 @@ struct uiDateTimePicker { [super dealloc]; } -- (void)datePickerCell:(NSDatePickerCell *)aDatePickerCell - validateProposedDateValue:(NSDate **)proposedDateValue - timeInterval:(NSTimeInterval *)proposedTimeInterval +- (void)datePickerCell:(NSDatePickerCell *)cell validateProposedDateValue:(NSDate **)proposedDateValue timeInterval:(NSTimeInterval *)proposedTimeInterval { uiDateTimePicker *d; - d = (uiDateTimePicker *) uiprivMapGet(self->pickers, aDatePickerCell); + d = (uiDateTimePicker *) uiprivMapGet(self->pickers, cell); [NSTimer scheduledTimerWithTimeInterval:0 target:self selector:@selector(doTimer:) @@ -49,9 +49,11 @@ struct uiDateTimePicker { - (void)doTimer:(NSTimer *)timer { + NSValue *v; uiDateTimePicker *d; - d = (uiDateTimePicker *) [((NSValue *)[timer userInfo]) pointerValue]; + v = (NSValue *) [timer userInfo]; + d = (uiDateTimePicker *) [v pointerValue]; (*(d->onChanged))(d, d->onChangedData); } @@ -78,6 +80,7 @@ static void defaultOnChanged(uiDateTimePicker *d, void *data) // do nothing } +// TODO consider using NSDateComponents iff we ever need the extra accuracy of not using NSTimeInterval void uiDateTimePickerTime(uiDateTimePicker *d, struct tm *time) { time_t t; @@ -90,7 +93,7 @@ void uiDateTimePickerTime(uiDateTimePicker *d, struct tm *time) // Copy time to minimize a race condition // time.h functions use global non-thread-safe data tmbuf = *localtime(&t); - memcpy(time, &tmbuf, sizeof(struct tm)); + memcpy(time, &tmbuf, sizeof (struct tm)); } void uiDateTimePickerSetTime(uiDateTimePicker *d, const struct tm *time) @@ -99,7 +102,7 @@ void uiDateTimePickerSetTime(uiDateTimePicker *d, const struct tm *time) struct tm tmbuf; // Copy time because mktime() modifies its argument - memcpy(&tmbuf, time, sizeof(struct tm)); + memcpy(&tmbuf, time, sizeof (struct tm)); t = mktime(&tmbuf); [d->dp setDateValue:[NSDate dateWithTimeIntervalSince1970:t]]; diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 4c049b9a..b68f9706 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -23,11 +23,6 @@ _add_example(histogram ${_EXAMPLE_RESOURCES_RC} ) -_add_example(datetime - datetime/main.c - ${_EXAMPLE_RESOURCES_RC} -) - _add_example(cpp-multithread cpp-multithread/main.cpp ${_EXAMPLE_RESOURCES_RC} @@ -52,11 +47,16 @@ _add_example(timer ${_EXAMPLE_RESOURCES_RC} ) +_add_example(datetime + datetime/main.c + ${_EXAMPLE_RESOURCES_RC} +) + add_custom_target(examples DEPENDS controlgallery histogram cpp-multithread drawtext - datetime - timer) + timer + datetime) diff --git a/examples/datetime/main.c b/examples/datetime/main.c index e71b54a6..faf2c2f4 100644 --- a/examples/datetime/main.c +++ b/examples/datetime/main.c @@ -17,7 +17,6 @@ const char *timeFormat(uiDateTimePicker *d) fmt = "%X"; else fmt = ""; - return fmt; } @@ -59,14 +58,19 @@ int onClosing(uiWindow *w, void *data) int main(void) { uiInitOptions o; + const char *err; uiWindow *w; uiGrid *g; uiLabel *l; uiButton *b; memset(&o, 0, sizeof (uiInitOptions)); - if (uiInit(&o) != NULL) - abort(); + err = uiInit(&o); + if (err != NULL) { + fprintf(stderr, "error initializing ui: %s\n", err); + uiFreeInitError(err); + return 1; + } w = uiNewWindow("Date / Time", 320, 240, 0); uiWindowSetMargined(w, 1); diff --git a/unix/datetimepicker.c b/unix/datetimepicker.c index 5f052dca..0f57b721 100644 --- a/unix/datetimepicker.c +++ b/unix/datetimepicker.c @@ -116,6 +116,7 @@ static void dateTimeChanged(uiprivDateTimePickerWidget *d) { g_signal_emit(d, changedSignal, 0); setLabel(d); + // TODO fire event here instead? } // we don't want ::toggled to be sent again @@ -579,7 +580,7 @@ void uiDateTimePickerTime(uiDateTimePicker *d, struct tm *time) // Copy time to minimize a race condition // time.h functions use global non-thread-safe data tmbuf = *localtime(&t); - memcpy(time, &tmbuf, sizeof(struct tm)); + memcpy(time, &tmbuf, sizeof (struct tm)); } void uiDateTimePickerSetTime(uiDateTimePicker *d, const struct tm *time) @@ -588,7 +589,7 @@ void uiDateTimePickerSetTime(uiDateTimePicker *d, const struct tm *time) struct tm tmbuf; // Copy time because mktime() modifies its argument - memcpy(&tmbuf, time, sizeof(struct tm)); + memcpy(&tmbuf, time, sizeof (struct tm)); t = mktime(&tmbuf); uiprivDateTimePickerWidget_setTime(d->d, g_date_time_new_from_unix_local(t)); diff --git a/windows/datetimepicker.cpp b/windows/datetimepicker.cpp index 26841dae..32546cf8 100644 --- a/windows/datetimepicker.cpp +++ b/windows/datetimepicker.cpp @@ -4,7 +4,7 @@ struct uiDateTimePicker { uiWindowsControl c; HWND hwnd; - void(*onChanged)(uiDateTimePicker *, void *); + void (*onChanged)(uiDateTimePicker *, void *); void *onChangedData; }; @@ -191,10 +191,9 @@ void uiDateTimePickerSetTime(uiDateTimePicker *d, const struct tm *time) toSystemTime(time, &systime); if (SendMessageW(d->hwnd, DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM) (&systime)) == 0) logLastError(L"error setting date and time"); - (*(d->onChanged))(d, d->onChangedData); } -void uiDateTimePickerOnChanged(uiDateTimePicker *d, void(*f)(uiDateTimePicker *, void *), void *data) +void uiDateTimePickerOnChanged(uiDateTimePicker *d, void (*f)(uiDateTimePicker *, void *), void *data) { d->onChanged = f; d->onChangedData = data; From 6533c0be3222c22696191141face2f067d85f549 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 13 May 2018 13:00:42 -0400 Subject: [PATCH 1072/1329] Fixed the datetime example --- examples/datetime/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/datetime/main.c b/examples/datetime/main.c index faf2c2f4..5f10eb0f 100644 --- a/examples/datetime/main.c +++ b/examples/datetime/main.c @@ -1,5 +1,6 @@ -#include +#include #include +#include #include #include "../../ui.h" From e00a91ded49fd279617501f2ee67f7f9f757ddef Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 13 May 2018 18:00:25 -0400 Subject: [PATCH 1073/1329] Fixed event propagation in GTK+ uiDateTimePicker. --- unix/datetimepicker.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/unix/datetimepicker.c b/unix/datetimepicker.c index 0f57b721..8ca3a274 100644 --- a/unix/datetimepicker.c +++ b/unix/datetimepicker.c @@ -558,6 +558,7 @@ struct uiDateTimePicker { uiprivDateTimePickerWidget *d; void (*onChanged)(uiDateTimePicker *, void *); void *onChangedData; + gulong setBlock; }; uiUnixControlAllDefaults(uiDateTimePicker) @@ -588,12 +589,17 @@ void uiDateTimePickerSetTime(uiDateTimePicker *d, const struct tm *time) time_t t; struct tm tmbuf; + // TODO find a better way to avoid this; possibly by removing the signal entirely, or the call to dateTimeChanged() (most likely both) + g_signal_handler_block(d->d, d->setBlock); + // Copy time because mktime() modifies its argument memcpy(&tmbuf, time, sizeof (struct tm)); t = mktime(&tmbuf); uiprivDateTimePickerWidget_setTime(d->d, g_date_time_new_from_unix_local(t)); dateTimeChanged(d->d); + + g_signal_handler_unblock(d->d, d->setBlock); } void uiDateTimePickerOnChanged(uiDateTimePicker *d, void (*f)(uiDateTimePicker *, void *), void *data) @@ -647,7 +653,7 @@ uiDateTimePicker *finishNewDateTimePicker(GtkWidget *(*fn)(void)) d->widget = (*fn)(); d->d = uiprivDateTimePickerWidget(d->widget); - g_signal_connect(d->widget, "changed", G_CALLBACK(onChanged), d); + d->setBlock = g_signal_connect(d->widget, "changed", G_CALLBACK(onChanged), d); uiDateTimePickerOnChanged(d, defaultOnChanged, NULL); return d; From 02fc3e267ebdd8702b443a021c74ee3b032417ec Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 13 May 2018 18:04:57 -0400 Subject: [PATCH 1074/1329] And fixed on OS X too. IIRC I handled it on Windows in a prior commit. --- darwin/datetimepicker.m | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/darwin/datetimepicker.m b/darwin/datetimepicker.m index d4424678..ed164855 100644 --- a/darwin/datetimepicker.m +++ b/darwin/datetimepicker.m @@ -6,6 +6,7 @@ struct uiDateTimePicker { NSDatePicker *dp; void (*onChanged)(uiDateTimePicker *, void *); void *onChangedData; + BOOL blockSendOnce; }; // TODO see if target-action works here or not; I forgot what cody271@ originally said @@ -54,6 +55,10 @@ struct uiDateTimePicker { v = (NSValue *) [timer userInfo]; d = (uiDateTimePicker *) [v pointerValue]; + if (d->blockSendOnce) { + d->blockSendOnce = NO; + return; + } (*(d->onChanged))(d, d->onChangedData); } @@ -105,6 +110,8 @@ void uiDateTimePickerSetTime(uiDateTimePicker *d, const struct tm *time) memcpy(&tmbuf, time, sizeof (struct tm)); t = mktime(&tmbuf); + // TODO get rid of the need for this + d->blockSendOnce = YES; [d->dp setDateValue:[NSDate dateWithTimeIntervalSince1970:t]]; } From 8be919530283041b58f3e6f0169f4fcca6098c1f Mon Sep 17 00:00:00 2001 From: Mike Sinkovsky Date: Sat, 5 May 2018 16:29:23 +0500 Subject: [PATCH 1075/1329] Travis: matrix build --- .travis.yml | 98 ++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 78 insertions(+), 20 deletions(-) diff --git a/.travis.yml b/.travis.yml index d271bd8e..17ab46cb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,23 +1,81 @@ -os: - - linux - - osx - -# This makes us use Ubuntu 14 instead of 12 -dist: trusty - -# Notes: -# - Travis uses cmake 3.0.2 on OS X; we need 3.1 or newer (thanks tbodt) - language: c + +matrix: + include: + - env: + - BUILD_TYPE: shared-linux-amd64 + os: linux + dist: trusty + addons: + apt: + packages: &linux_x64_packages + - libgtk-3-dev + + - env: + - BUILD_TYPE: static-linux-amd64 + - CMAKE_FLAGS: -DBUILD_SHARED_LIBS=OFF + os: linux + dist: trusty + addons: + apt: + packages: *linux_x64_packages + + - env: + - BUILD_TYPE: shared-linux-386 + - CFLAGS: -m32 + - CXXFLAGS: -m32 + - PKG_CONFIG_PATH: /usr/lib/i386-linux-gnu/pkgconfig + os: linux + dist: trusty + addons: + apt: + packages: &linux_i386_packages + - gcc-multilib + - g++-multilib + - libgtk-3-dev:i386 + # the rest fixes broken dependencies of libgtk:i386 + - libgirepository-1.0-1:i386 + - libglib2.0-dev:i386 + - gir1.2-glib-2.0:i386 + - gir1.2-atk-1.0:i386 + - libatk1.0-dev:i386 + - libfreetype6-dev:i386 + - libfontconfig1-dev:i386 + - libcairo2-dev:i386 + - libgdk-pixbuf2.0-dev:i386 + - libpango1.0-dev:i386 + - libxft-dev:i386 + - libpng12-dev:i386 + + - env: + - BUILD_TYPE: static-linux-386 + - CMAKE_FLAGS: -DBUILD_SHARED_LIBS=OFF + - CFLAGS: -m32 + - CXXFLAGS: -m32 + - PKG_CONFIG_PATH: /usr/lib/i386-linux-gnu/pkgconfig + os: linux + dist: trusty + addons: + apt: + packages: *linux_i386_packages + + - env: + - BUILD_TYPE: shared-osx-amd64 + os: osx + osx_image: xcode8 + + - env: + - BUILD_TYPE: static-osx-amd64 + - CMAKE_FLAGS: -DBUILD_SHARED_LIBS=OFF + os: osx + osx_image: xcode8 + + script: - - if [ "$TRAVIS_OS_NAME" == "linux" ]; then sudo apt-get update; fi - - if [ "$TRAVIS_OS_NAME" == "linux" ]; then sudo apt-get install libgtk-3-dev -y || sudo apt-cache search libgtk3; fi - - if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew update; fi - - mkdir build - - cd build - cmake --version - - cmake .. -G "Unix Makefiles" - - make tester examples - - rm -rf * - - cmake .. -G "Unix Makefiles" -DBUILD_SHARED_LIBS=OFF - - make tester examples + - mkdir build + - pushd build && cmake ${CMAKE_FLAGS} .. && make tester examples && popd + +after_success: + - ls -lR build/out + - file build/out/test From fb19345e108db31f714bf0cd2ffa52ba2c7a1b7c Mon Sep 17 00:00:00 2001 From: Mike Sinkovsky Date: Fri, 4 May 2018 14:07:32 +0500 Subject: [PATCH 1076/1329] CI: use AppVeyor for testing Windows buildability --- .appveyor.yml | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 .appveyor.yml diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 00000000..da87137c --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,78 @@ +version: 'build #{build}' + +environment: + matrix: + - BUILD_TYPE: shared-windows-msvc2013-386 + CMAKE_FLAGS: -G "Visual Studio 12 2013" + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013 + + - BUILD_TYPE: static-windows-msvc2013-386 + CMAKE_FLAGS: -G "Visual Studio 12 2013" -DBUILD_SHARED_LIBS=OFF + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013 + + - BUILD_TYPE: shared-windows-msvc2013-amd64 + CMAKE_FLAGS: -G "Visual Studio 12 2013 Win64" + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013 + + - BUILD_TYPE: static-windows-msvc2013-amd64 + CMAKE_FLAGS: -G "Visual Studio 12 2013 Win64" -DBUILD_SHARED_LIBS=OFF + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013 + + - BUILD_TYPE: shared-windows-msvc2015-386 + CMAKE_FLAGS: -G "Visual Studio 14 2015" + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 + + - BUILD_TYPE: static-windows-msvc2015-386 + CMAKE_FLAGS: -G "Visual Studio 14 2015" -DBUILD_SHARED_LIBS=OFF + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 + + - BUILD_TYPE: shared-windows-msvc2015-amd64 + CMAKE_FLAGS: -G "Visual Studio 14 2015 Win64" + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 + + - BUILD_TYPE: static-windows-msvc2015-amd64 + CMAKE_FLAGS: -G "Visual Studio 14 2015 Win64" -DBUILD_SHARED_LIBS=OFF + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 + + - BUILD_TYPE: shared-windows-msvc2017-386 + CMAKE_FLAGS: -G "Visual Studio 15 2017" + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + + - BUILD_TYPE: static-windows-msvc2017-386 + CMAKE_FLAGS: -G "Visual Studio 15 2017" -DBUILD_SHARED_LIBS=OFF + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + + - BUILD_TYPE: shared-windows-msvc2017-amd64 + CMAKE_FLAGS: -G "Visual Studio 15 2017 Win64" + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + + - BUILD_TYPE: static-windows-msvc2017-amd64 + CMAKE_FLAGS: -G "Visual Studio 15 2017 Win64" -DBUILD_SHARED_LIBS=OFF + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 + + - BUILD_TYPE: static-windows-mingw-386 + MINGW_PATH: C:\msys64\mingw32\bin + CMAKE_FLAGS: -G "MinGW Makefiles" -DBUILD_SHARED_LIBS=OFF + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 + + - BUILD_TYPE: static-windows-mingw-amd64 + MINGW_PATH: C:\msys64\mingw64\bin + CMAKE_FLAGS: -G "MinGW Makefiles" -DBUILD_SHARED_LIBS=OFF + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 + + +before_build: + - if defined MINGW_PATH ( + set "PATH=%MINGW_PATH%;%PATH:C:\Program Files\Git\usr\bin;=%" + ) + +build_script: + - mkdir build + - if defined MINGW_PATH ( + pushd build && cmake %CMAKE_FLAGS% .. && mingw32-make tester examples && popd + ) else ( + pushd build && cmake %CMAKE_FLAGS% .. && msbuild libui.sln /t:Build /p:Configuration=Release && popd + ) + +after_build: + - dir /S build\out From c4b1e1b237b7c239a3be050f0e657ef928c97c09 Mon Sep 17 00:00:00 2001 From: Mike Sinkovsky Date: Tue, 15 May 2018 13:51:05 +0500 Subject: [PATCH 1077/1329] AppVeyor: refactor matrix --- .appveyor.yml | 94 +++++++++++++++++++-------------------------------- 1 file changed, 34 insertions(+), 60 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index da87137c..b7832741 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -2,77 +2,51 @@ version: 'build #{build}' environment: matrix: - - BUILD_TYPE: shared-windows-msvc2013-386 - CMAKE_FLAGS: -G "Visual Studio 12 2013" + - linking: shared + compiler: msvc2013 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013 - - - BUILD_TYPE: static-windows-msvc2013-386 - CMAKE_FLAGS: -G "Visual Studio 12 2013" -DBUILD_SHARED_LIBS=OFF + - linking: static + compiler: msvc2013 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013 - - - BUILD_TYPE: shared-windows-msvc2013-amd64 - CMAKE_FLAGS: -G "Visual Studio 12 2013 Win64" - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013 - - - BUILD_TYPE: static-windows-msvc2013-amd64 - CMAKE_FLAGS: -G "Visual Studio 12 2013 Win64" -DBUILD_SHARED_LIBS=OFF - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013 - - - BUILD_TYPE: shared-windows-msvc2015-386 - CMAKE_FLAGS: -G "Visual Studio 14 2015" + - linking: shared + compiler: msvc2015 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 - - - BUILD_TYPE: static-windows-msvc2015-386 - CMAKE_FLAGS: -G "Visual Studio 14 2015" -DBUILD_SHARED_LIBS=OFF + - linking: static + compiler: msvc2015 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 - - - BUILD_TYPE: shared-windows-msvc2015-amd64 - CMAKE_FLAGS: -G "Visual Studio 14 2015 Win64" - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 - - - BUILD_TYPE: static-windows-msvc2015-amd64 - CMAKE_FLAGS: -G "Visual Studio 14 2015 Win64" -DBUILD_SHARED_LIBS=OFF - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 - - - BUILD_TYPE: shared-windows-msvc2017-386 - CMAKE_FLAGS: -G "Visual Studio 15 2017" + - linking: shared + compiler: msvc2017 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - - - BUILD_TYPE: static-windows-msvc2017-386 - CMAKE_FLAGS: -G "Visual Studio 15 2017" -DBUILD_SHARED_LIBS=OFF + - linking: static + compiler: msvc2017 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - - - BUILD_TYPE: shared-windows-msvc2017-amd64 - CMAKE_FLAGS: -G "Visual Studio 15 2017 Win64" - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - - - BUILD_TYPE: static-windows-msvc2017-amd64 - CMAKE_FLAGS: -G "Visual Studio 15 2017 Win64" -DBUILD_SHARED_LIBS=OFF - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - - - BUILD_TYPE: static-windows-mingw-386 - MINGW_PATH: C:\msys64\mingw32\bin - CMAKE_FLAGS: -G "MinGW Makefiles" -DBUILD_SHARED_LIBS=OFF - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 - - - BUILD_TYPE: static-windows-mingw-amd64 - MINGW_PATH: C:\msys64\mingw64\bin - CMAKE_FLAGS: -G "MinGW Makefiles" -DBUILD_SHARED_LIBS=OFF + - linking: static + compiler: mingw APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 +platform: + - Win32 + - x64 before_build: - - if defined MINGW_PATH ( - set "PATH=%MINGW_PATH%;%PATH:C:\Program Files\Git\usr\bin;=%" - ) + - if %compiler%==msvc2013 ( set "CMAKE_GENERATOR=Visual Studio 12 2013" ) + else if %compiler%==msvc2015 ( set "CMAKE_GENERATOR=Visual Studio 14 2015" ) + else if %compiler%==msvc2017 ( set "CMAKE_GENERATOR=Visual Studio 15 2017" ) + else if %compiler%==mingw ( set "CMAKE_GENERATOR=MinGW Makefiles" ) + - if %compiler%-%platform%==mingw-Win32 ( set "PATH=C:\msys64\mingw32\bin;%PATH%" ) + else if %compiler%-%platform%==mingw-x64 ( set "PATH=C:\msys64\mingw64\bin;%PATH%" ) + else if %platform%==x64 ( set "CMAKE_GENERATOR=%CMAKE_GENERATOR% Win64" ) + - if %linking%==static ( set CMAKE_FLAGS=-DBUILD_SHARED_LIBS=OFF ) + - ren "C:\Program Files\Git\usr\bin\sh.exe" _sh.exe build_script: - - mkdir build - - if defined MINGW_PATH ( - pushd build && cmake %CMAKE_FLAGS% .. && mingw32-make tester examples && popd - ) else ( - pushd build && cmake %CMAKE_FLAGS% .. && msbuild libui.sln /t:Build /p:Configuration=Release && popd - ) + - md build && cd build + - cmake -G "%CMAKE_GENERATOR%" %CMAKE_FLAGS% .. + - if %compiler%==mingw ( mingw32-make tester examples ) + else ( msbuild libui.sln /t:Build /p:Configuration=Release /p:Platform=%platform% ) after_build: - - dir /S build\out + - if %platform%==x64 ( set "platform=amd64" ) else ( set "platform=386" ) + - echo set "target=%linking%-windows-%compiler%-%platform%" + - cd %APPVEYOR_BUILD_FOLDER% + - dir /S build\out From 858a49797883a108e1a753907135b3a3f2f50124 Mon Sep 17 00:00:00 2001 From: Mike Sinkovsky Date: Tue, 15 May 2018 17:10:20 +0500 Subject: [PATCH 1078/1329] AppVeyor: deploy artifacts --- .appveyor.yml | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index b7832741..e5e83dab 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -37,6 +37,7 @@ before_build: else if %compiler%-%platform%==mingw-x64 ( set "PATH=C:\msys64\mingw64\bin;%PATH%" ) else if %platform%==x64 ( set "CMAKE_GENERATOR=%CMAKE_GENERATOR% Win64" ) - if %linking%==static ( set CMAKE_FLAGS=-DBUILD_SHARED_LIBS=OFF ) + - if %compiler%==mingw ( set "outdir=build\out" ) else ( set "outdir=build\out\Release" ) - ren "C:\Program Files\Git\usr\bin\sh.exe" _sh.exe build_script: @@ -44,9 +45,28 @@ build_script: - cmake -G "%CMAKE_GENERATOR%" %CMAKE_FLAGS% .. - if %compiler%==mingw ( mingw32-make tester examples ) else ( msbuild libui.sln /t:Build /p:Configuration=Release /p:Platform=%platform% ) + - cd %APPVEYOR_BUILD_FOLDER% after_build: - if %platform%==x64 ( set "platform=amd64" ) else ( set "platform=386" ) - - echo set "target=%linking%-windows-%compiler%-%platform%" - - cd %APPVEYOR_BUILD_FOLDER% - - dir /S build\out + - if %APPVEYOR_REPO_TAG%==true ( set "version=%APPVEYOR_REPO_TAG_NAME%" ) + else ( set "version=%APPVEYOR_REPO_BRANCH%" ) + - set "artifact=%linking%-windows-%compiler%-%platform%-%version%" + - 7z a libui-%artifact%.zip .\%outdir%\libui.* ui.h ui_windows.h + - 7z l libui-%artifact%.zip + - 7z a examples-%artifact%.zip .\%outdir%\*.exe + - 7z l examples-%artifact%.zip + +artifacts: + - path: libui-*.zip + name: libui + - path: examples-*.zip + name: examples + +deploy: + provider: GitHub + artifact: libui, examples + auth_token: + secure: "2l/602m6FkqAB9TTIAkPX3Erfwg9jVTlf/Inmf2dWcbOrJJzK/WrCUIgY3B4ngly" + on: + appveyor_repo_tag: true # deploy on tag push only From 2b1c43c64243ca5b2e3b20767d2710554793d57c Mon Sep 17 00:00:00 2001 From: Mike Sinkovsky Date: Wed, 16 May 2018 15:25:40 +0500 Subject: [PATCH 1079/1329] Travis: refactor matrix --- .travis.yml | 125 ++++++++++++++++++++++++---------------------------- 1 file changed, 58 insertions(+), 67 deletions(-) diff --git a/.travis.yml b/.travis.yml index 17ab46cb..af29e414 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,75 +1,66 @@ language: c +include: &toolchain_linux_amd64 + os: linux + dist: trusty + addons: + apt: + packages: + - libgtk-3-dev + +include: &toolchain_linux_386 + os: linux + dist: trusty + addons: + apt: + packages: + - gcc-multilib + - g++-multilib + - libgtk-3-dev:i386 + # the rest fixes broken dependencies of libgtk:i386 + - libgirepository-1.0-1:i386 + - libglib2.0-dev:i386 + - gir1.2-glib-2.0:i386 + - gir1.2-atk-1.0:i386 + - libatk1.0-dev:i386 + - libfreetype6-dev:i386 + - libfontconfig1-dev:i386 + - libcairo2-dev:i386 + - libgdk-pixbuf2.0-dev:i386 + - libpango1.0-dev:i386 + - libxft-dev:i386 + - libpng12-dev:i386 + +include: &toolchain_osx_amd64 + os: osx + osx_image: xcode8 + +# Travis CI build matrix. +# Each entry below will trigger an extra, parallel build on Travis. matrix: include: - - env: - - BUILD_TYPE: shared-linux-amd64 - os: linux - dist: trusty - addons: - apt: - packages: &linux_x64_packages - - libgtk-3-dev - - - env: - - BUILD_TYPE: static-linux-amd64 - - CMAKE_FLAGS: -DBUILD_SHARED_LIBS=OFF - os: linux - dist: trusty - addons: - apt: - packages: *linux_x64_packages - - - env: - - BUILD_TYPE: shared-linux-386 - - CFLAGS: -m32 - - CXXFLAGS: -m32 - - PKG_CONFIG_PATH: /usr/lib/i386-linux-gnu/pkgconfig - os: linux - dist: trusty - addons: - apt: - packages: &linux_i386_packages - - gcc-multilib - - g++-multilib - - libgtk-3-dev:i386 - # the rest fixes broken dependencies of libgtk:i386 - - libgirepository-1.0-1:i386 - - libglib2.0-dev:i386 - - gir1.2-glib-2.0:i386 - - gir1.2-atk-1.0:i386 - - libatk1.0-dev:i386 - - libfreetype6-dev:i386 - - libfontconfig1-dev:i386 - - libcairo2-dev:i386 - - libgdk-pixbuf2.0-dev:i386 - - libpango1.0-dev:i386 - - libxft-dev:i386 - - libpng12-dev:i386 - - - env: - - BUILD_TYPE: static-linux-386 - - CMAKE_FLAGS: -DBUILD_SHARED_LIBS=OFF - - CFLAGS: -m32 - - CXXFLAGS: -m32 - - PKG_CONFIG_PATH: /usr/lib/i386-linux-gnu/pkgconfig - os: linux - dist: trusty - addons: - apt: - packages: *linux_i386_packages - - - env: - - BUILD_TYPE: shared-osx-amd64 - os: osx - osx_image: xcode8 - - - env: - - BUILD_TYPE: static-osx-amd64 - - CMAKE_FLAGS: -DBUILD_SHARED_LIBS=OFF - os: osx - osx_image: xcode8 + - env: linking=shared platform=amd64 + <<: *toolchain_linux_amd64 + - env: linking=static platform=amd64 + <<: *toolchain_linux_amd64 + - env: linking=shared platform=386 + <<: *toolchain_linux_386 + - env: linking=static platform=386 + <<: *toolchain_linux_386 + - env: linking=shared platform=amd64 + <<: *toolchain_osx_amd64 + - env: linking=static platform=amd64 + <<: *toolchain_osx_amd64 +install: + - if [[ "${platform}" == "386" ]]; then + export CFLAGS=-m32; + export CXXFLAGS=-m32; + export PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig; + fi + - if [[ "${linking}" == "static" ]]; then + export CMAKE_FLAGS=-DBUILD_SHARED_LIBS=OFF; + fi script: - cmake --version From 9aef70b9806015f7115fc7a94dc7fc9c8d580b26 Mon Sep 17 00:00:00 2001 From: Mike Sinkovsky Date: Wed, 16 May 2018 17:36:57 +0500 Subject: [PATCH 1080/1329] Travis: upload artifacts --- .travis.yml | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index af29e414..7c48f9a2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -65,8 +65,29 @@ install: script: - cmake --version - mkdir build - - pushd build && cmake ${CMAKE_FLAGS} .. && make tester examples && popd + - pushd build + - cmake -G "Unix Makefiles" ${CMAKE_FLAGS} .. + - make tester examples + - popd after_success: - ls -lR build/out - file build/out/test + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then cp ui.h ui_darwin.h build/out/; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then cp ui.h ui_unix.h build/out/; fi + - if [[ "x${TRAVIS_TAG}" != "x" ]]; then export version=${TRAVIS_TAG}; else export version=${TRAVIS_BRANCH}; fi + - export artifact=${linking}-${TRAVIS_OS_NAME}-${platform}-${version} + - pushd build/out + - tar -czvf libui-${artifact}.tgz libui.* *.h + - tar -czvf examples-${artifact}.tgz `find . -type f ! -name "*.*"` + - popd + +deploy: + provider: releases + api_key: + secure: MFpee9M8kzihNg7KmQQjtznIHmfTF88EVhkY5FcM3CZUg6XfNE8YDPRLhWnH9/Oyrn1WjL37UabrJSrMYoz44s7yqyKeGK0rXIpV2i9gGMdGDxe94iCrPdvcwK2BysDd7pfwDPy1EtM8nxCQHv7d01PPT+w0hDCjBAMbILOh0IHB4yDvwyZOy6ReWas3/co3djjfjHk5XCUm54cT2mxu3U5CHQY9gsJWVaom9eINLjtDkTgbStxGSuXXmeRGzpFReR5dF4KK/IVs2GbayNfWOS8Xqhgk5uKFalWaH4P+F5ACf4zH78mhK+FiDWCkhzB6/N+mJOwl6BV9BjUv9/4+pNDQVYZvc0P19Kv8wzQsL3jMJ/SRFf4tdIeoDgRFOrq0QL7JajWq16zazbhP6PiIEfcNKdbqlvVoG7LheUsSkbVbmV439He3oykA7Tbc+dBPn+8hRdpsyoG8CyY/8nDS+E8mMxU5JvRfmlc93ZL6uxAOeIW/w8V70/upb/Tdk4d2pEeihisdL24Ys5CYzbVJAyP6Vx4y5yyKrA1RSq0pBkjzc9DJI811c4XLiLpv8jglYomN4PHk5e7GEW7n7HYSTf3gXB4HgVt5fnDudhTHErRKtzxxiBKXMYLmaCUv5mOEjSu4hRZq4v0e/VbzbfGlFoMsB9krzd9JIk7Fl1FbkNo= + file: build/out/*.tgz + file_glob: true + skip_cleanup: true + on: + tags: true From 02b7ae90f1753e295c322d17db0ff3e85c4e7281 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 16 May 2018 21:55:04 -0400 Subject: [PATCH 1081/1329] Some fixups to the AppVeyor and Travis CIs: rearranged artifact name parts to match existing ones, changed "platform" to "arch" wherever possible, changed "osx" to "darwin" in artifacts. Also added the AppVeyor badge to the README. --- .appveyor.yml | 10 ++++++---- .travis.yml | 19 ++++++++++--------- README.md | 3 ++- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index e5e83dab..577f982b 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -48,10 +48,11 @@ build_script: - cd %APPVEYOR_BUILD_FOLDER% after_build: - - if %platform%==x64 ( set "platform=amd64" ) else ( set "platform=386" ) + - if %platform%==x64 ( set "arch=amd64" ) else ( set "arch=386" ) - if %APPVEYOR_REPO_TAG%==true ( set "version=%APPVEYOR_REPO_TAG_NAME%" ) else ( set "version=%APPVEYOR_REPO_BRANCH%" ) - - set "artifact=%linking%-windows-%compiler%-%platform%-%version%" + - if %linking%==shared ( set "artifact=shared" ) else ( set "artifact=%compiler%-static" ) + - set "artifact=%version%-windows-%arch%-%artifact%" - 7z a libui-%artifact%.zip .\%outdir%\libui.* ui.h ui_windows.h - 7z l libui-%artifact%.zip - 7z a examples-%artifact%.zip .\%outdir%\*.exe @@ -66,7 +67,8 @@ artifacts: deploy: provider: GitHub artifact: libui, examples - auth_token: - secure: "2l/602m6FkqAB9TTIAkPX3Erfwg9jVTlf/Inmf2dWcbOrJJzK/WrCUIgY3B4ngly" + # TODO https://www.appveyor.com/docs/deployment/github/ + #auth_token: + #secure: "2l/602m6FkqAB9TTIAkPX3Erfwg9jVTlf/Inmf2dWcbOrJJzK/WrCUIgY3B4ngly" on: appveyor_repo_tag: true # deploy on tag push only diff --git a/.travis.yml b/.travis.yml index 7c48f9a2..1358aca9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,21 +39,21 @@ include: &toolchain_osx_amd64 # Each entry below will trigger an extra, parallel build on Travis. matrix: include: - - env: linking=shared platform=amd64 + - env: linking=shared arch=amd64 <<: *toolchain_linux_amd64 - - env: linking=static platform=amd64 + - env: linking=static arch=amd64 <<: *toolchain_linux_amd64 - - env: linking=shared platform=386 + - env: linking=shared arch=386 <<: *toolchain_linux_386 - - env: linking=static platform=386 + - env: linking=static arch=386 <<: *toolchain_linux_386 - - env: linking=shared platform=amd64 + - env: linking=shared arch=amd64 <<: *toolchain_osx_amd64 - - env: linking=static platform=amd64 + - env: linking=static arch=amd64 <<: *toolchain_osx_amd64 install: - - if [[ "${platform}" == "386" ]]; then + - if [[ "${arch}" == "386" ]]; then export CFLAGS=-m32; export CXXFLAGS=-m32; export PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig; @@ -73,10 +73,11 @@ script: after_success: - ls -lR build/out - file build/out/test - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then cp ui.h ui_darwin.h build/out/; fi + - export platform="$TRAVIS_OS_NAME" + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then cp ui.h ui_darwin.h build/out/; export platform=darwin; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then cp ui.h ui_unix.h build/out/; fi - if [[ "x${TRAVIS_TAG}" != "x" ]]; then export version=${TRAVIS_TAG}; else export version=${TRAVIS_BRANCH}; fi - - export artifact=${linking}-${TRAVIS_OS_NAME}-${platform}-${version} + - export artifact=${version}-${platform}-${arch}-${linking} - pushd build/out - tar -czvf libui-${artifact}.tgz libui.* *.h - tar -czvf examples-${artifact}.tgz `find . -type f ! -name "*.*"` diff --git a/README.md b/README.md index de83d890..4b789be3 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ # libui: a portable GUI library for C This README is being written.
-[![Build Status](https://travis-ci.org/andlabs/libui.svg?branch=master)](https://travis-ci.org/andlabs/libui) +[![Build Status, Linux and macOS](https://travis-ci.org/andlabs/libui.svg?branch=master)](https://travis-ci.org/andlabs/libui)
+[![Build Status, Windows](https://ci.appveyor.com/api/projects/status/ouyk78c52mmisa31?svg=true)](https://ci.appveyor.com/project/andlabs/libui) ## Announcements From a0367201c98843ba8dabacbf132201eae18730f5 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 16 May 2018 22:04:46 -0400 Subject: [PATCH 1082/1329] Some more CI fine-tuning. --- .appveyor.yml | 1 + .travis.yml | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 577f982b..a9b296e0 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -53,6 +53,7 @@ after_build: else ( set "version=%APPVEYOR_REPO_BRANCH%" ) - if %linking%==shared ( set "artifact=shared" ) else ( set "artifact=%compiler%-static" ) - set "artifact=%version%-windows-%arch%-%artifact%" + - del .\%outdir%\libui.exp # remove unnecessary files - 7z a libui-%artifact%.zip .\%outdir%\libui.* ui.h ui_windows.h - 7z l libui-%artifact%.zip - 7z a examples-%artifact%.zip .\%outdir%\*.exe diff --git a/.travis.yml b/.travis.yml index 1358aca9..2c02134a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ include: &toolchain_linux_amd64 dist: trusty addons: apt: + update: true packages: - libgtk-3-dev @@ -78,15 +79,17 @@ after_success: - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then cp ui.h ui_unix.h build/out/; fi - if [[ "x${TRAVIS_TAG}" != "x" ]]; then export version=${TRAVIS_TAG}; else export version=${TRAVIS_BRANCH}; fi - export artifact=${version}-${platform}-${arch}-${linking} + - echo ${artifact} - pushd build/out + - # TODO do not include symlinks in the archive - tar -czvf libui-${artifact}.tgz libui.* *.h - tar -czvf examples-${artifact}.tgz `find . -type f ! -name "*.*"` - popd deploy: provider: releases - api_key: - secure: MFpee9M8kzihNg7KmQQjtznIHmfTF88EVhkY5FcM3CZUg6XfNE8YDPRLhWnH9/Oyrn1WjL37UabrJSrMYoz44s7yqyKeGK0rXIpV2i9gGMdGDxe94iCrPdvcwK2BysDd7pfwDPy1EtM8nxCQHv7d01PPT+w0hDCjBAMbILOh0IHB4yDvwyZOy6ReWas3/co3djjfjHk5XCUm54cT2mxu3U5CHQY9gsJWVaom9eINLjtDkTgbStxGSuXXmeRGzpFReR5dF4KK/IVs2GbayNfWOS8Xqhgk5uKFalWaH4P+F5ACf4zH78mhK+FiDWCkhzB6/N+mJOwl6BV9BjUv9/4+pNDQVYZvc0P19Kv8wzQsL3jMJ/SRFf4tdIeoDgRFOrq0QL7JajWq16zazbhP6PiIEfcNKdbqlvVoG7LheUsSkbVbmV439He3oykA7Tbc+dBPn+8hRdpsyoG8CyY/8nDS+E8mMxU5JvRfmlc93ZL6uxAOeIW/w8V70/upb/Tdk4d2pEeihisdL24Ys5CYzbVJAyP6Vx4y5yyKrA1RSq0pBkjzc9DJI811c4XLiLpv8jglYomN4PHk5e7GEW7n7HYSTf3gXB4HgVt5fnDudhTHErRKtzxxiBKXMYLmaCUv5mOEjSu4hRZq4v0e/VbzbfGlFoMsB9krzd9JIk7Fl1FbkNo= + #api_key: + #secure: TODO file: build/out/*.tgz file_glob: true skip_cleanup: true From 9d74f9f930117cbc7f8ee3566380dc290328dbba Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 16 May 2018 22:18:02 -0400 Subject: [PATCH 1083/1329] And added an announcement entry for this. --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 4b789be3..fc19dbfb 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,9 @@ This README is being written.
## Announcements +* **16 May 2018** + * Thanks to @parro-it and @msink, libui now has better CI, including AppVeyor for Windows CI, and automated creation of binary releases when I make a tagged release. + * **13 May 2018** * Added new functions to work with uiDateTimePickers: `uiDateTimePickerTime()`, `uiDateTimePickerSetTime()`, and `uiDateTimePickerOnChanged()`. These operate on standard `` `struct tm`s. Thanks @cody271! From 4e9f498dd3aab71898f4c2427ccd460c841a2d36 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 17 May 2018 09:49:02 -0400 Subject: [PATCH 1084/1329] Dump AppVeyor CPU count information. Update #364 --- .appveyor.yml | 1 + cpucount.cpp | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 cpucount.cpp diff --git a/.appveyor.yml b/.appveyor.yml index a9b296e0..93070f5f 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -41,6 +41,7 @@ before_build: - ren "C:\Program Files\Git\usr\bin\sh.exe" _sh.exe build_script: + - cl cpucount.cpp && .\cpucount.exe - md build && cd build - cmake -G "%CMAKE_GENERATOR%" %CMAKE_FLAGS% .. - if %compiler%==mingw ( mingw32-make tester examples ) diff --git a/cpucount.cpp b/cpucount.cpp new file mode 100644 index 00000000..1dad1dd7 --- /dev/null +++ b/cpucount.cpp @@ -0,0 +1,68 @@ +// 17 may 2018 +#define UNICODE +#define _UNICODE +#define STRICT +#define STRICT_TYPED_ITEMIDS +#define WINVER 0x0600 /* from Microsoft's winnls.h */ +#define _WIN32_WINNT 0x0600 +#define _WIN32_WINDOWS 0x0600 /* from Microsoft's pdh.h */ +#define _WIN32_IE 0x0700 +#define NTDDI_VERSION 0x06000000 +#include +#include +#include + +int affinityCount(DWORD_PTR a) +{ + int n = 0; + + while (a != 0) { + if ((a & 1) != 0) + n++; + a >>= 1; + } + return n; +} + +int main(void) +{ + HANDLE pseudoCurrent, current; + DWORD_PTR pa, sa; + SYSTEM_INFO si; + DWORD le; + + pseudoCurrent = GetCurrentProcess(); + if (GetProcessAffinityMask(pseudoCurrent, &pa, &sa) != 0) { + printf("GetProcessAffinityMask(GetCurrentProcess()):\n"); + printf("\tProcess - %d\n", affinityCount(pa)); + printf("\tSystem - %d\n", affinityCount(sa)); + } else { + le = GetLastError(); + fprintf(stderr, "error calling GetProcessAffinity(GetCurrentProcess()): %I32d\n", le); + } + + if (DuplicateHandle(pseudoCurrent, pseudoCurrent, + pseudoCurrent, ¤t, + 0, FALSE, DUPLICATE_SAME_ACCESS) != 0) + if (GetProcessAffinityMask(current, &pa, &sa) != 0) { + printf("GetProcessAffinityMask(real handle):\n"); + printf("\tProcess - %d\n", affinityCount(pa)); + printf("\tSystem - %d\n", affinityCount(sa)); + } else { + le = GetLastError(); + fprintf(stderr, "error calling GetProcessAffinity(real handle): %I32d\n", le); + } + else { + le = GetLastError(); + fprintf(stderr, "error calling DuplicateHandle(GetCurrentProcess()) (no real handle info available): %I32d\n", le); + } + + ZeroMemory(&si, sizeof (SYSTEM_INFO)); + GetSystemInfo(&si); + printf("GetSystemInfo() processor count: %I32d\n", si.dwNumberOfProcessors); + ZeroMemory(&si, sizeof (SYSTEM_INFO)); + GetNativeSystemInfo(&si); + printf("GetNativeSystemInfo() processor count: %I32d\n", si.dwNumberOfProcessors); + + return 0; +} From 18817c7633f2d73cc37711839c7d005e82057092 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 17 May 2018 09:54:47 -0400 Subject: [PATCH 1085/1329] Oops --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index 93070f5f..b77e2383 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -41,7 +41,7 @@ before_build: - ren "C:\Program Files\Git\usr\bin\sh.exe" _sh.exe build_script: - - cl cpucount.cpp && .\cpucount.exe + - "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\CL.exe" cpucount.cpp && .\cpucount.exe - md build && cd build - cmake -G "%CMAKE_GENERATOR%" %CMAKE_FLAGS% .. - if %compiler%==mingw ( mingw32-make tester examples ) From 3f1b6b3a49a4fa86a43694300d03b29eaebc7a33 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 17 May 2018 09:55:58 -0400 Subject: [PATCH 1086/1329] Fucking YAML; so much for simple git revert of all this. --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index b77e2383..3cf3a1b1 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -41,7 +41,7 @@ before_build: - ren "C:\Program Files\Git\usr\bin\sh.exe" _sh.exe build_script: - - "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\CL.exe" cpucount.cpp && .\cpucount.exe + - "C:\\Program Files (x86)\\Microsoft Visual Studio 12.0\\VC\\bin\\CL.exe" cpucount.cpp && .\cpucount.exe - md build && cd build - cmake -G "%CMAKE_GENERATOR%" %CMAKE_FLAGS% .. - if %compiler%==mingw ( mingw32-make tester examples ) From 26e584308374902078b860c3c698dbbbbd12c061 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 17 May 2018 09:59:25 -0400 Subject: [PATCH 1087/1329] ... *sigh* --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index 3cf3a1b1..005d7971 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -41,7 +41,7 @@ before_build: - ren "C:\Program Files\Git\usr\bin\sh.exe" _sh.exe build_script: - - "C:\\Program Files (x86)\\Microsoft Visual Studio 12.0\\VC\\bin\\CL.exe" cpucount.cpp && .\cpucount.exe + - 'C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\CL.exe' cpucount.cpp && .\cpucount.exe - md build && cd build - cmake -G "%CMAKE_GENERATOR%" %CMAKE_FLAGS% .. - if %compiler%==mingw ( mingw32-make tester examples ) From 4748828a2ec16fc51a6ff8451d3730786058cee5 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 17 May 2018 10:01:13 -0400 Subject: [PATCH 1088/1329] Good to experience that YAML is a complicated mess for myself now! --- .appveyor.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index 005d7971..ee0f19d5 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -41,7 +41,8 @@ before_build: - ren "C:\Program Files\Git\usr\bin\sh.exe" _sh.exe build_script: - - 'C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\CL.exe' cpucount.cpp && .\cpucount.exe + - | + C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\CL.exe cpucount.cpp && .\cpucount.exe - md build && cd build - cmake -G "%CMAKE_GENERATOR%" %CMAKE_FLAGS% .. - if %compiler%==mingw ( mingw32-make tester examples ) From 911ef405c77c5f20e9724f37efcbdf56d6fd028b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 17 May 2018 10:03:24 -0400 Subject: [PATCH 1089/1329] Okay, THAT worked, so let's fix the cmd.exe quoting --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index ee0f19d5..152b2a86 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -42,7 +42,7 @@ before_build: build_script: - | - C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\CL.exe cpucount.cpp && .\cpucount.exe + "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\CL.exe" cpucount.cpp && .\cpucount.exe - md build && cd build - cmake -G "%CMAKE_GENERATOR%" %CMAKE_FLAGS% .. - if %compiler%==mingw ( mingw32-make tester examples ) From d6ced62aab5b5232e422ceb19f685aa7eb1a1cd7 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 17 May 2018 10:08:15 -0400 Subject: [PATCH 1090/1329] Hooray Microsoft --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index 152b2a86..44fa93ed 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -42,7 +42,7 @@ before_build: build_script: - | - "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\CL.exe" cpucount.cpp && .\cpucount.exe + "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\vcvars32.bat" && cl cpucount.cpp && .\cpucount.exe - md build && cd build - cmake -G "%CMAKE_GENERATOR%" %CMAKE_FLAGS% .. - if %compiler%==mingw ( mingw32-make tester examples ) From d4c41e02700d01636fc713db1ab7973b4ad4b7a1 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 17 May 2018 10:14:13 -0400 Subject: [PATCH 1091/1329] And undid all those changes. --- .appveyor.yml | 2 -- cpucount.cpp | 68 --------------------------------------------------- 2 files changed, 70 deletions(-) delete mode 100644 cpucount.cpp diff --git a/.appveyor.yml b/.appveyor.yml index 44fa93ed..a9b296e0 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -41,8 +41,6 @@ before_build: - ren "C:\Program Files\Git\usr\bin\sh.exe" _sh.exe build_script: - - | - "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\vcvars32.bat" && cl cpucount.cpp && .\cpucount.exe - md build && cd build - cmake -G "%CMAKE_GENERATOR%" %CMAKE_FLAGS% .. - if %compiler%==mingw ( mingw32-make tester examples ) diff --git a/cpucount.cpp b/cpucount.cpp deleted file mode 100644 index 1dad1dd7..00000000 --- a/cpucount.cpp +++ /dev/null @@ -1,68 +0,0 @@ -// 17 may 2018 -#define UNICODE -#define _UNICODE -#define STRICT -#define STRICT_TYPED_ITEMIDS -#define WINVER 0x0600 /* from Microsoft's winnls.h */ -#define _WIN32_WINNT 0x0600 -#define _WIN32_WINDOWS 0x0600 /* from Microsoft's pdh.h */ -#define _WIN32_IE 0x0700 -#define NTDDI_VERSION 0x06000000 -#include -#include -#include - -int affinityCount(DWORD_PTR a) -{ - int n = 0; - - while (a != 0) { - if ((a & 1) != 0) - n++; - a >>= 1; - } - return n; -} - -int main(void) -{ - HANDLE pseudoCurrent, current; - DWORD_PTR pa, sa; - SYSTEM_INFO si; - DWORD le; - - pseudoCurrent = GetCurrentProcess(); - if (GetProcessAffinityMask(pseudoCurrent, &pa, &sa) != 0) { - printf("GetProcessAffinityMask(GetCurrentProcess()):\n"); - printf("\tProcess - %d\n", affinityCount(pa)); - printf("\tSystem - %d\n", affinityCount(sa)); - } else { - le = GetLastError(); - fprintf(stderr, "error calling GetProcessAffinity(GetCurrentProcess()): %I32d\n", le); - } - - if (DuplicateHandle(pseudoCurrent, pseudoCurrent, - pseudoCurrent, ¤t, - 0, FALSE, DUPLICATE_SAME_ACCESS) != 0) - if (GetProcessAffinityMask(current, &pa, &sa) != 0) { - printf("GetProcessAffinityMask(real handle):\n"); - printf("\tProcess - %d\n", affinityCount(pa)); - printf("\tSystem - %d\n", affinityCount(sa)); - } else { - le = GetLastError(); - fprintf(stderr, "error calling GetProcessAffinity(real handle): %I32d\n", le); - } - else { - le = GetLastError(); - fprintf(stderr, "error calling DuplicateHandle(GetCurrentProcess()) (no real handle info available): %I32d\n", le); - } - - ZeroMemory(&si, sizeof (SYSTEM_INFO)); - GetSystemInfo(&si); - printf("GetSystemInfo() processor count: %I32d\n", si.dwNumberOfProcessors); - ZeroMemory(&si, sizeof (SYSTEM_INFO)); - GetNativeSystemInfo(&si); - printf("GetNativeSystemInfo() processor count: %I32d\n", si.dwNumberOfProcessors); - - return 0; -} From dc62cbd27f18360d6a1d33488a169b673c418b23 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 18 May 2018 00:05:19 -0400 Subject: [PATCH 1092/1329] Let's try simultaneous builds in AppVeyor. --- .appveyor.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index a9b296e0..4fc35a8f 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -39,12 +39,16 @@ before_build: - if %linking%==static ( set CMAKE_FLAGS=-DBUILD_SHARED_LIBS=OFF ) - if %compiler%==mingw ( set "outdir=build\out" ) else ( set "outdir=build\out\Release" ) - ren "C:\Program Files\Git\usr\bin\sh.exe" _sh.exe + - set "simultaneous=2" build_script: - md build && cd build + - if %compiler%!=mingw ( set "CFLAGS=/MP%simultaneous% %CFLAGS%" ) + - if %compiler%!=mingw ( set "CPPFLAGS=/MP%simultaneous% %CPPFLAGS%" ) + - if %compiler%!=mingw ( set "CXXFLAGS=/MP%simultaneous% %CXXFLAGS%" ) - cmake -G "%CMAKE_GENERATOR%" %CMAKE_FLAGS% .. - - if %compiler%==mingw ( mingw32-make tester examples ) - else ( msbuild libui.sln /t:Build /p:Configuration=Release /p:Platform=%platform% ) + - if %compiler%==mingw ( mingw32-make -j%simultaneous% tester examples ) + else ( msbuild /verbosity:diagnostic libui.sln /t:Build /p:Configuration=Release /p:Platform=%platform% ) - cd %APPVEYOR_BUILD_FOLDER% after_build: From b4635902890c7f867a0dd85af3c89dfadc4fc94e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 18 May 2018 00:07:09 -0400 Subject: [PATCH 1093/1329] fucking --- .appveyor.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 4fc35a8f..e7b47b00 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -43,9 +43,9 @@ before_build: build_script: - md build && cd build - - if %compiler%!=mingw ( set "CFLAGS=/MP%simultaneous% %CFLAGS%" ) - - if %compiler%!=mingw ( set "CPPFLAGS=/MP%simultaneous% %CPPFLAGS%" ) - - if %compiler%!=mingw ( set "CXXFLAGS=/MP%simultaneous% %CXXFLAGS%" ) + - if %compiler%<>mingw ( set "CFLAGS=/MP%simultaneous% %CFLAGS%" ) + - if %compiler%<>mingw ( set "CPPFLAGS=/MP%simultaneous% %CPPFLAGS%" ) + - if %compiler%<>mingw ( set "CXXFLAGS=/MP%simultaneous% %CXXFLAGS%" ) - cmake -G "%CMAKE_GENERATOR%" %CMAKE_FLAGS% .. - if %compiler%==mingw ( mingw32-make -j%simultaneous% tester examples ) else ( msbuild /verbosity:diagnostic libui.sln /t:Build /p:Configuration=Release /p:Platform=%platform% ) From e4852c17b74deea2773c799c0fe81b3c7bea5963 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 18 May 2018 00:08:46 -0400 Subject: [PATCH 1094/1329] motherfucking --- .appveyor.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index e7b47b00..d4e80d7b 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -43,9 +43,9 @@ before_build: build_script: - md build && cd build - - if %compiler%<>mingw ( set "CFLAGS=/MP%simultaneous% %CFLAGS%" ) - - if %compiler%<>mingw ( set "CPPFLAGS=/MP%simultaneous% %CPPFLAGS%" ) - - if %compiler%<>mingw ( set "CXXFLAGS=/MP%simultaneous% %CXXFLAGS%" ) + - if not %compiler%==mingw ( set "CFLAGS=/MP%simultaneous% %CFLAGS%" ) + - if not %compiler%==mingw ( set "CPPFLAGS=/MP%simultaneous% %CPPFLAGS%" ) + - if not %compiler%==mingw ( set "CXXFLAGS=/MP%simultaneous% %CXXFLAGS%" ) - cmake -G "%CMAKE_GENERATOR%" %CMAKE_FLAGS% .. - if %compiler%==mingw ( mingw32-make -j%simultaneous% tester examples ) else ( msbuild /verbosity:diagnostic libui.sln /t:Build /p:Configuration=Release /p:Platform=%platform% ) From 4c8af90d649bbab71d62dd05059b4156ac361ab3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 18 May 2018 00:13:51 -0400 Subject: [PATCH 1095/1329] Okay, that seemed to apply the flag correctly. Let's turn off the debugging spew and see if it has the effect we want. --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index d4e80d7b..92a0dd6f 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -48,7 +48,7 @@ build_script: - if not %compiler%==mingw ( set "CXXFLAGS=/MP%simultaneous% %CXXFLAGS%" ) - cmake -G "%CMAKE_GENERATOR%" %CMAKE_FLAGS% .. - if %compiler%==mingw ( mingw32-make -j%simultaneous% tester examples ) - else ( msbuild /verbosity:diagnostic libui.sln /t:Build /p:Configuration=Release /p:Platform=%platform% ) + else ( msbuild libui.sln /t:Build /p:Configuration=Release /p:Platform=%platform% ) - cd %APPVEYOR_BUILD_FOLDER% after_build: From 96f2bf26034e7eb03405dfeb84d09144fe17df3f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 18 May 2018 01:28:32 -0400 Subject: [PATCH 1096/1329] And now with 3. --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index 92a0dd6f..2da0ed7f 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -39,7 +39,7 @@ before_build: - if %linking%==static ( set CMAKE_FLAGS=-DBUILD_SHARED_LIBS=OFF ) - if %compiler%==mingw ( set "outdir=build\out" ) else ( set "outdir=build\out\Release" ) - ren "C:\Program Files\Git\usr\bin\sh.exe" _sh.exe - - set "simultaneous=2" + - set "simultaneous=3" build_script: - md build && cd build From abc489095e688f94e0192e09285a1cb875b06d54 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 18 May 2018 08:56:42 -0400 Subject: [PATCH 1097/1329] Let's try jom with 1 to start. --- .appveyor.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 2da0ed7f..a8fc690d 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -37,18 +37,18 @@ before_build: else if %compiler%-%platform%==mingw-x64 ( set "PATH=C:\msys64\mingw64\bin;%PATH%" ) else if %platform%==x64 ( set "CMAKE_GENERATOR=%CMAKE_GENERATOR% Win64" ) - if %linking%==static ( set CMAKE_FLAGS=-DBUILD_SHARED_LIBS=OFF ) - - if %compiler%==mingw ( set "outdir=build\out" ) else ( set "outdir=build\out\Release" ) + - if %compiler%==mingw ( set "outdir=build\out" ) else ( set "outdir=build\out" ) + - if not %compiler%==mingw ( set "CMAKE_GENERATOR=NMake Makefiles JOM" ) + - if not %compiler%==mingw ( set "CMAKE_FLAGS=-DCMAKE_BUILD_TYPE=Release %CMAKE_FLAGS%" ) - ren "C:\Program Files\Git\usr\bin\sh.exe" _sh.exe - - set "simultaneous=3" + - set "simultaneous=1" build_script: - md build && cd build - - if not %compiler%==mingw ( set "CFLAGS=/MP%simultaneous% %CFLAGS%" ) - - if not %compiler%==mingw ( set "CPPFLAGS=/MP%simultaneous% %CPPFLAGS%" ) - - if not %compiler%==mingw ( set "CXXFLAGS=/MP%simultaneous% %CXXFLAGS%" ) + - if not %compiler%==mingw ( md jom && cd jom && curl -o jom.zip http://download.qt.io/official_releases/jom/jom.zip && 7z x jom.zip && cd .. ) - cmake -G "%CMAKE_GENERATOR%" %CMAKE_FLAGS% .. - if %compiler%==mingw ( mingw32-make -j%simultaneous% tester examples ) - else ( msbuild libui.sln /t:Build /p:Configuration=Release /p:Platform=%platform% ) + else ( jom\jom -j%simultaneous% ) - cd %APPVEYOR_BUILD_FOLDER% after_build: From 634ca8470df0c81ca703efe2f846a7e9a7e1e50a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 18 May 2018 08:59:48 -0400 Subject: [PATCH 1098/1329] Gotta love curl (so much for a clean revert of this...) --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index a8fc690d..62a28a2e 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -45,7 +45,7 @@ before_build: build_script: - md build && cd build - - if not %compiler%==mingw ( md jom && cd jom && curl -o jom.zip http://download.qt.io/official_releases/jom/jom.zip && 7z x jom.zip && cd .. ) + - if not %compiler%==mingw ( md jom && cd jom && curl -L -o jom.zip http://download.qt.io/official_releases/jom/jom.zip && 7z x jom.zip && cd .. ) - cmake -G "%CMAKE_GENERATOR%" %CMAKE_FLAGS% .. - if %compiler%==mingw ( mingw32-make -j%simultaneous% tester examples ) else ( jom\jom -j%simultaneous% ) From cfb51cb1b9788145d9b3f94bdab6c2031e03e1da Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 18 May 2018 09:08:14 -0400 Subject: [PATCH 1099/1329] And let's try again for MSVC. --- .appveyor.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 62a28a2e..86dedbe3 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -29,9 +29,10 @@ platform: - x64 before_build: - - if %compiler%==msvc2013 ( set "CMAKE_GENERATOR=Visual Studio 12 2013" ) - else if %compiler%==msvc2015 ( set "CMAKE_GENERATOR=Visual Studio 14 2015" ) - else if %compiler%==msvc2017 ( set "CMAKE_GENERATOR=Visual Studio 15 2017" ) + - | + if %compiler%==msvc2013 ( set "CMAKE_GENERATOR=Visual Studio 12 2013" && if %platform%==x64 ( call "C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd" /x64 && call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" x86_amd64 ) else ( call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" x86 ) ) + else if %compiler%==msvc2015 ( set "CMAKE_GENERATOR=Visual Studio 14 2015" && if %platform%==x64 ( call "C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd" /x64 && call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86_amd64 ) else ( call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86 ) ) + else if %compiler%==msvc2017 ( set "CMAKE_GENERATOR=Visual Studio 15 2017" && if %platform%==x64 ( call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat" ) else ( call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat" ) ) else if %compiler%==mingw ( set "CMAKE_GENERATOR=MinGW Makefiles" ) - if %compiler%-%platform%==mingw-Win32 ( set "PATH=C:\msys64\mingw32\bin;%PATH%" ) else if %compiler%-%platform%==mingw-x64 ( set "PATH=C:\msys64\mingw64\bin;%PATH%" ) From 422ec83162de7b9689b7fb066404119a020f1b0e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 18 May 2018 09:19:53 -0400 Subject: [PATCH 1100/1329] Okay, so any future changes will require super complex cmd.exe fuckery combined with yaml's bizarre, complex quoting rules, sooooo nope.avi (I'll try again after spliting all the appveyor stuff into batch files, but at this point I'm tempted to switch to VSTS, which I can pay for). --- .appveyor.yml | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 86dedbe3..2da0ed7f 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -29,27 +29,26 @@ platform: - x64 before_build: - - | - if %compiler%==msvc2013 ( set "CMAKE_GENERATOR=Visual Studio 12 2013" && if %platform%==x64 ( call "C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd" /x64 && call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" x86_amd64 ) else ( call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" x86 ) ) - else if %compiler%==msvc2015 ( set "CMAKE_GENERATOR=Visual Studio 14 2015" && if %platform%==x64 ( call "C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd" /x64 && call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86_amd64 ) else ( call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86 ) ) - else if %compiler%==msvc2017 ( set "CMAKE_GENERATOR=Visual Studio 15 2017" && if %platform%==x64 ( call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat" ) else ( call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat" ) ) + - if %compiler%==msvc2013 ( set "CMAKE_GENERATOR=Visual Studio 12 2013" ) + else if %compiler%==msvc2015 ( set "CMAKE_GENERATOR=Visual Studio 14 2015" ) + else if %compiler%==msvc2017 ( set "CMAKE_GENERATOR=Visual Studio 15 2017" ) else if %compiler%==mingw ( set "CMAKE_GENERATOR=MinGW Makefiles" ) - if %compiler%-%platform%==mingw-Win32 ( set "PATH=C:\msys64\mingw32\bin;%PATH%" ) else if %compiler%-%platform%==mingw-x64 ( set "PATH=C:\msys64\mingw64\bin;%PATH%" ) else if %platform%==x64 ( set "CMAKE_GENERATOR=%CMAKE_GENERATOR% Win64" ) - if %linking%==static ( set CMAKE_FLAGS=-DBUILD_SHARED_LIBS=OFF ) - - if %compiler%==mingw ( set "outdir=build\out" ) else ( set "outdir=build\out" ) - - if not %compiler%==mingw ( set "CMAKE_GENERATOR=NMake Makefiles JOM" ) - - if not %compiler%==mingw ( set "CMAKE_FLAGS=-DCMAKE_BUILD_TYPE=Release %CMAKE_FLAGS%" ) + - if %compiler%==mingw ( set "outdir=build\out" ) else ( set "outdir=build\out\Release" ) - ren "C:\Program Files\Git\usr\bin\sh.exe" _sh.exe - - set "simultaneous=1" + - set "simultaneous=3" build_script: - md build && cd build - - if not %compiler%==mingw ( md jom && cd jom && curl -L -o jom.zip http://download.qt.io/official_releases/jom/jom.zip && 7z x jom.zip && cd .. ) + - if not %compiler%==mingw ( set "CFLAGS=/MP%simultaneous% %CFLAGS%" ) + - if not %compiler%==mingw ( set "CPPFLAGS=/MP%simultaneous% %CPPFLAGS%" ) + - if not %compiler%==mingw ( set "CXXFLAGS=/MP%simultaneous% %CXXFLAGS%" ) - cmake -G "%CMAKE_GENERATOR%" %CMAKE_FLAGS% .. - if %compiler%==mingw ( mingw32-make -j%simultaneous% tester examples ) - else ( jom\jom -j%simultaneous% ) + else ( msbuild libui.sln /t:Build /p:Configuration=Release /p:Platform=%platform% ) - cd %APPVEYOR_BUILD_FOLDER% after_build: From 919ad1f16e39e77c19d99793e402975d59720a34 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 20 May 2018 10:20:19 -0400 Subject: [PATCH 1101/1329] More Rust bindings. This list is getting nuttily formatted; need to improve it... --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fc19dbfb..d441973c 100644 --- a/README.md +++ b/README.md @@ -193,7 +193,7 @@ Node.js | [libui-node](https://github.com/parro-it/libui-node) PHP | [ui](https://github.com/krakjoe/ui) Python | [pylibui](https://github.com/joaoventura/pylibui), [pylibui-cffi](https://github.com/Yardanico/pylibui-cffi) Ruby | [libui-ruby](https://github.com/jamescook/libui-ruby) -Rust | [libui-rs](https://github.com/pcwalton/libui-rs) +Rust | [libui-rs](https://github.com/pcwalton/libui-rs), [arcturu/libui-rs](https://github.com/arcturu/libui-rs), [LeoTindall/libui-rs](https://github.com/LeoTindall/libui-rs) Scala | [scalaui](https://github.com/lolgab/scalaui) Swift | [libui-swift](https://github.com/sclukey/libui-swift) From cc477d58f823088680eb905e15c447c7cf9382db Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 20 May 2018 12:55:50 -0400 Subject: [PATCH 1102/1329] More notes. --- _notes/misc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/_notes/misc b/_notes/misc index 115045bd..7eaf7c5c 100644 --- a/_notes/misc +++ b/_notes/misc @@ -202,3 +202,5 @@ http://www.edm2.com/index.php/IBM_OS/2_Developer%27s_Packages old stuff font1.gif (GIF Image, 424 × 475 pixels) http://www.functionx.com/win32/controls/dlgboxes/font1.gif Inskcape’s Hidden Little Feature: Mesh Gradients | OCS-Mag http://www.ocsmag.com/2016/02/27/inskcapes-hidden-little-feature-mesh-gradients/ (near "When you’re done colouring in all the nodes, you may want to drag") + +https://msdn.microsoft.com/en-us/library/windows/desktop/ms632615(v=vs.85).aspx we need to handle this alongisde WM_CAPTURECHANGED From bb3066e3f5ead9a2e34050d2f6c14614abb6d9a6 Mon Sep 17 00:00:00 2001 From: Mike Sinkovsky Date: Mon, 21 May 2018 16:10:12 +0500 Subject: [PATCH 1103/1329] Kotlin/Native bindings --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d441973c..2e692b62 100644 --- a/README.md +++ b/README.md @@ -187,6 +187,7 @@ Harbour | [HBUI](https://github.com/RJopek/HBUI) Haskell | [haskell-libui](https://github.com/beijaflor-io/haskell-libui) JavaScript | [libui.js (merged into libui-node?)](https://github.com/mavenave/libui.js), [proton-native](https://github.com/kusti8/proton-native) Julia | [Libui.jl](https://github.com/joa-quim/Libui.jl) +Kotlin | [kotlin-libui](https://github.com/msink/kotlin-libui) Lua | [libuilua](https://github.com/zevv/libuilua), [libui-lua](https://github.com/mdombroski/libui-lua), [lui](http://tset.de/lui/index.html) Nim | [ui](https://github.com/nim-lang/ui) Node.js | [libui-node](https://github.com/parro-it/libui-node) From 6c1a7b2b9b90c3386450b087228eb268d9a1a5de Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 21 May 2018 10:44:10 -0400 Subject: [PATCH 1104/1329] Cleared up exactly where libui stands. I didn't realize people were expecting more out of it than I let out, because I thought I was letting out exactly what I promised, when I didn't. Thanks to Bloodmeow (I think that was what their username was...). --- README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/README.md b/README.md index 2e692b62..570edc87 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,28 @@ This README is being written.
[![Build Status, Linux and macOS](https://travis-ci.org/andlabs/libui.svg?branch=master)](https://travis-ci.org/andlabs/libui)
[![Build Status, Windows](https://ci.appveyor.com/api/projects/status/ouyk78c52mmisa31?svg=true)](https://ci.appveyor.com/project/andlabs/libui) +## Status + +It has come to my attention that I have not been particularly clear about how usable or feature-complete libui is, and that this has fooled many people into expecting more from libui right this moment than I have explicitly promised to make available. I apologize for not doing this sooner. + +libui is currently **mid-alpha** software. Much of what is currently present runs stabily enough for the examples and perhaps some small programs to work, but the stability is still a work-in-progress, much of what is already there is not feature-complete, some of it will be buggy on certain platforms, and there's a lot of stuff missing. In short, here's a list of features that I would like to add to libui, but that aren't in yet: + +- tables and trees (the former is currently WIP and may land in preliminary form soon) +- clipboard support, including drag and drop +- more and better dialogs +- printing +- accessibility for uiArea and custom controls +- document-based programs +- tighter OS integration (especially for document-based programs), to allow programs to fully feel native, rather than merely look and act native +- better support for standard dialogs and features (search bars, etc.) +- OpenGL support (this was already being worked on by someone else, but I don't know what happened to them...) + +In addition, [here](https://github.com/andlabs/libui/issues?utf8=%E2%9C%93&q=master+in%3Atitle+is%3Aissue+is%3Aopen) is a list of issues generalizing existing problems. + +Furthermore, libui is not properly fully documented yet. This is mainly due to the fact that the API was initially unstable enough so as to result in rewriting documentation multiple times, in addition to me not being happy with really any existing C code documentation tool. That being said, I have started to pin down my ideal code documentation style in parts of `ui.h`, most notably in the uiAttributedString APIs. Over time, I plan on extending this to the rest of the headers. You can also use [the documentation for libui's Go bindings](https://godoc.org/github.com/andlabs/ui) as a reference, though it is somewhat stale and not optimally written. + +But libui is not dead; I am working on it whenever I can, and I hope to get it to a point of real quality soon! + ## Announcements * **16 May 2018** From 0da7b3fcebc9e6c5cf787d68d122d7e242450332 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 21 May 2018 20:10:46 -0400 Subject: [PATCH 1105/1329] More bindings. Thanks to anonymous-coward on reddit. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 570edc87..a18050c4 100644 --- a/README.md +++ b/README.md @@ -202,6 +202,7 @@ C++ | [libui-cpp](https://github.com/billyquith/libui-cpp), [cpp-libui-qtlike](h C# / .NET Framework | [LibUI.Binding](https://github.com/NattyNarwhal/LibUI.Binding) C# / .NET Core | [DevZH.UI](https://github.com/noliar/DevZH.UI), [SharpUI](https://github.com/benpye/sharpui/), [LibUISharp](https://github.com/tom-corwin/LibUISharp) CHICKEN Scheme | [wasamasa/libui](https://github.com/wasamasa/libui) +Common Lisp | [jinwoo/cl-ui](https://github.com/jinwoo/cl-ui) Crystal | [libui.cr](https://github.com/Fusion/libui.cr), [hedron](https://github.com/Qwerp-Derp/hedron) D | [DerelictLibui (flat API)](https://github.com/Extrawurst/DerelictLibui), [libuid (object-oriented)](https://github.com/mogud/libuid) Euphoria | [libui-euphoria](https://github.com/ghaberek/libui-euphoria) From fc2ea17bb8ccc5f3d1a9375d5584d273aea3b449 Mon Sep 17 00:00:00 2001 From: Ben Campbell Date: Sat, 5 May 2018 22:45:01 +1200 Subject: [PATCH 1106/1329] Add minimal uiTable implementation for windows This uses the win32 common controls listview to implement uiTable. There are limitations: - It supports only a single TextPart per column. - ImagePart, CheckboxPart and ProgessBarPart are not implemented. - There is no support for cell coloring. - Cell editing is not implemented. Some of these will be very hard to support using the standard common control listview, and probably require an entire custom listview. --- test/page16.c | 2 + windows/CMakeLists.txt | 2 + windows/image.cpp | 31 +++++ windows/table.cpp | 277 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 312 insertions(+) create mode 100644 windows/image.cpp create mode 100644 windows/table.cpp diff --git a/test/page16.c b/test/page16.c index 80ac0139..3a5ac639 100644 --- a/test/page16.c +++ b/test/page16.c @@ -132,6 +132,8 @@ uiBox *makePage16(void) uiTableSetRowBackgroundColorModelColumn(t, 3); + uiTableAppendTextColumn(t, "Numbers", 8); + tc = uiTableAppendColumn(t, "Buttons"); uiTableColumnAppendCheckboxPart(tc, 7, 0); uiTableColumnAppendButtonPart(tc, 6, 1); diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 16beefa7..b871b529 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -34,6 +34,7 @@ list(APPEND _LIBUI_SOURCES windows/graphemes.cpp windows/grid.cpp windows/group.cpp + windows/image.cpp windows/init.cpp windows/label.cpp windows/main.cpp @@ -49,6 +50,7 @@ list(APPEND _LIBUI_SOURCES windows/spinbox.cpp windows/stddialogs.cpp windows/tab.cpp + windows/table.cpp windows/tabpage.cpp windows/text.cpp windows/utf16.cpp diff --git a/windows/image.cpp b/windows/image.cpp new file mode 100644 index 00000000..bba013b1 --- /dev/null +++ b/windows/image.cpp @@ -0,0 +1,31 @@ +#include "uipriv_windows.hpp" +// stubbed out windows image list implementation. +// Required for uiTable control, but windows implemenation +// doesn't currently have image support. + +struct uiImage { + double width; + double height; + // HIMAGELIST images; +}; + +uiImage *uiNewImage(double width, double height) +{ + uiImage *i; + + i = uiprivNew(uiImage); + i->width = width; + i->height = height; + return i; +} + +void uiFreeImage(uiImage *i) +{ + uiprivFree(i); +} + +void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int pixelStride) +{ + // not implemented +} + diff --git a/windows/table.cpp b/windows/table.cpp new file mode 100644 index 00000000..5d2273af --- /dev/null +++ b/windows/table.cpp @@ -0,0 +1,277 @@ +#include "uipriv_windows.hpp" + +#include + +static void uiTableDestroy(uiControl *c); +static void uiTableMinimumSize(uiWindowsControl *c, int *width, int *height); +static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nm, LRESULT *lResult); + +struct uiTable; + +struct uiTableModel { + uiTableModelHandler *mh; + std::vector tables; +}; + +struct uiTableColumn { + uiTable *t; + WCHAR *name; + // don't really support parts (but this would part=>column mappings if we did) + int modelColumn; // -1 = none +}; + +struct uiTable { + uiWindowsControl c; + uiTableModel *model; + HWND hwnd; + std::vector columns; +}; + +void *uiTableModelStrdup(const char *str) +{ + return strdup(str); +} + +void *uiTableModelGiveColor(double r, double g, double b, double a) +{ + return 0; // not implemented +} + +uiTableModel *uiNewTableModel(uiTableModelHandler *mh) +{ + uiTableModel *m; + + m = new uiTableModel(); + m->mh = mh; + return m; +} + + +void uiFreeTableModel(uiTableModel *m) +{ + delete m; +} + +void uiTableModelRowInserted(uiTableModel *m, int newIndex) +{ + LVITEM item; + + item.mask = 0; + item.iItem = newIndex; + item.iSubItem = 0; //? + for (auto t : m->tables) { + ListView_InsertItem( t->hwnd, &item ); + } +} + +void uiTableModelRowChanged(uiTableModel *m, int index) +{ + for (auto t : m->tables) { + ListView_Update( t->hwnd, index ); + } +} + +void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) +{ + for (auto t : m->tables) { + ListView_DeleteItem( t->hwnd, oldIndex ); + } +} + +void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand) +{ + uiTable *t = c->t; + int lvIndex = 0; + + if (c->modelColumn >=0) { + return; // multiple parts not implemented + } + c->modelColumn = modelColumn; + + // work out appropriate listview index for the column + for (auto candidate : t->columns) { + if (candidate == c) { + break; + } + if (candidate->modelColumn >= 0) { + ++lvIndex; + } + } + + LV_COLUMN lvc; + lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT; /* | LVCF_SUBITEM; */ + lvc.fmt = LVCFMT_LEFT; + lvc.cx = 120; // TODO + lvc.pszText = c->name; + ListView_InsertColumn(c->t->hwnd, lvIndex, &lvc); +} + +void uiTableColumnAppendImagePart(uiTableColumn *c, int modelColumn, int expand) +{ + // not implemented +} + +void uiTableColumnAppendButtonPart(uiTableColumn *c, int modelColumn, int expand) +{ + // not implemented +} + +void uiTableColumnAppendCheckboxPart(uiTableColumn *c, int modelColumn, int expand) +{ + // not implemented +} + +void uiTableColumnAppendProgressBarPart(uiTableColumn *c, int modelColumn, int expand) +{ + // not implemented +} + +void uiTableColumnPartSetEditable(uiTableColumn *c, int part, int editable) +{ + // TODO +} + +void uiTableColumnPartSetTextColor(uiTableColumn *c, int part, int modelColumn) +{ + // not implemented +} + +// uiTable implementation + +uiWindowsControlAllDefaultsExceptDestroy(uiTable) + +uiTable *uiNewTable(uiTableModel *model) +{ + uiTable *t; + int winStyle = WS_CHILD | LVS_AUTOARRANGE | LVS_REPORT | LVS_OWNERDATA | LVS_SINGLESEL; + + uiWindowsNewControl(uiTable, t); + new(&t->columns) std::vector(); // (initialising in place) + t->model = model; + t->hwnd = uiWindowsEnsureCreateControlHWND(WS_EX_CLIENTEDGE, + WC_LISTVIEW, + L"", + winStyle, + hInstance, + NULL, + TRUE); + model->tables.push_back(t); + uiWindowsRegisterWM_NOTIFYHandler(t->hwnd, onWM_NOTIFY, uiControl(t)); + ListView_SetExtendedListViewStyle(t->hwnd, LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP); + // TODO: try LVS_EX_AUTOSIZECOLUMNS + int n = (*(model->mh->NumRows))(model->mh, model); + ListView_SetItemCountEx(t->hwnd, n, 0); + return t; +} + +uiTableColumn *uiTableAppendColumn(uiTable *t, const char *name) +{ + uiTableColumn *c = uiprivNew(uiTableColumn); + c->name = toUTF16(name); + c->t = t; + c->modelColumn = -1; // -1 = unassigned + // we defer the actual ListView_InsertColumn call until a part is added... + t->columns.push_back(c); + return c; +} + +void uiTableSetRowBackgroundColorModelColumn(uiTable *t, int modelColumn) +{ + // not implemented +} + +static void uiTableDestroy(uiControl *c) +{ + uiTable *t = uiTable(c); + uiTableModel *model = t->model; + std::vector::iterator it; + + uiWindowsUnregisterWM_NOTIFYHandler(t->hwnd); + uiWindowsEnsureDestroyWindow(t->hwnd); + // detach table from model + for (it = model->tables.begin(); it != model->tables.end(); ++it) { + if (*it == t) { + model->tables.erase(it); + break; + } + } + // free the columns + for (auto col: t->columns) { + uiprivFree(col->name); + uiprivFree(col); + } + t->columns.~vector(); // (created with placement new, so just call dtor directly) + uiFreeControl(uiControl(t)); +} + +static void uiTableMinimumSize(uiWindowsControl *c, int *width, int *height) +{ + uiTable *t = uiTable(c); + uiWindowsSizing sizing; + int x, y; + + // suggested listview sizing from http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing: + // "columns widths that avoid truncated data x an integral number of items" + // Don't think that'll cut it when some cells have overlong data (eg + // stupidly long URLs). So for now, just hardcode a minimum: + + x = 107; // in line with other controls + y = 14*3; // header + 2 lines (roughly) + uiWindowsGetSizing(t->hwnd, &sizing); + uiWindowsSizingDlgUnitsToPixels(&sizing, &x, &y); + *width = x; + *height = y; +} + +static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nm, LRESULT *lResult) +{ + uiTable *t = uiTable(c); + uiTableModelHandler *mh = t->model->mh; + BOOL ret = FALSE; + + switch (nm->code) { + case LVN_GETDISPINFO: + { + NMLVDISPINFO* di = (NMLVDISPINFO*)nm; + LVITEM* item = &di->item; + if (!(item->mask & LVIF_TEXT)) { + break; + } + int row = item->iItem; + int col = item->iSubItem; + if (col<0 || col>=(int)t->columns.size()) { + break; + } + + uiTableColumn *tc = (uiTableColumn*)t->columns[col]; + + int mcol = tc->modelColumn; + uiTableModelColumnType typ = (*mh->ColumnType)(mh,t->model,mcol); + if (typ == uiTableModelColumnString) { + void* data = (*(mh->CellValue))(mh, t->model, row, mcol); + int n = MultiByteToWideChar(CP_UTF8, 0, (const char*)data, -1, item->pszText, item->cchTextMax); + // make sure clipped strings are nul-terminated + if (n>=item->cchTextMax) { + item->pszText[item->cchTextMax-1] = L'\0'; + } + } else if (typ == uiTableModelColumnInt) { + char buf[32]; + intptr_t data = (intptr_t)(*(mh->CellValue))(mh, t->model, row, mcol); + sprintf(buf, "%d", (int)data); + int n = MultiByteToWideChar(CP_UTF8, 0, buf, -1, item->pszText, item->cchTextMax); + // make sure clipped strings are nul-terminated + if (n>=item->cchTextMax) { + item->pszText[item->cchTextMax-1] = L'\0'; + } + } else { + item->pszText[0] = L'\0'; + } + break; + } + default: + break; + } + *lResult = 0; + return ret; +} + From 7402dec26610b6317d40758986a54feaa34140e2 Mon Sep 17 00:00:00 2001 From: Ben Campbell Date: Sun, 6 May 2018 01:29:59 +1200 Subject: [PATCH 1107/1329] remove superfluous #include --- windows/table.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/windows/table.cpp b/windows/table.cpp index 5d2273af..4d2ebd12 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -1,7 +1,5 @@ #include "uipriv_windows.hpp" -#include - static void uiTableDestroy(uiControl *c); static void uiTableMinimumSize(uiWindowsControl *c, int *width, int *height); static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nm, LRESULT *lResult); From 4246ae5549c43cbd48c0bea726ec16b8d1f38282 Mon Sep 17 00:00:00 2001 From: Ben Campbell Date: Tue, 22 May 2018 22:53:25 +1200 Subject: [PATCH 1108/1329] assorted consistancy cleanup --- windows/table.cpp | 160 ++++++++++++++++++++++++++-------------------- 1 file changed, 90 insertions(+), 70 deletions(-) diff --git a/windows/table.cpp b/windows/table.cpp index 4d2ebd12..2f7737ee 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -1,14 +1,12 @@ #include "uipriv_windows.hpp" -static void uiTableDestroy(uiControl *c); -static void uiTableMinimumSize(uiWindowsControl *c, int *width, int *height); -static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nm, LRESULT *lResult); +//static void uiTableMinimumSize(uiWindowsControl *c, int *width, int *height); struct uiTable; struct uiTableModel { uiTableModelHandler *mh; - std::vector tables; + std::vector tables; }; struct uiTableColumn { @@ -22,7 +20,7 @@ struct uiTable { uiWindowsControl c; uiTableModel *model; HWND hwnd; - std::vector columns; + std::vector columns; }; void *uiTableModelStrdup(const char *str) @@ -52,27 +50,31 @@ void uiFreeTableModel(uiTableModel *m) void uiTableModelRowInserted(uiTableModel *m, int newIndex) { - LVITEM item; + LVITEMW item; + ZeroMemory(&item, sizeof (LVITEMW)); item.mask = 0; item.iItem = newIndex; item.iSubItem = 0; //? for (auto t : m->tables) { - ListView_InsertItem( t->hwnd, &item ); + if (SendMessageW(t->hwnd, LVM_INSERTITEM, 0, (LPARAM) (&item)) == (LRESULT) (-1)) + logLastError(L"error calling LVM_INSERTITEM in uiTableModelRowInserted()"); } } void uiTableModelRowChanged(uiTableModel *m, int index) { for (auto t : m->tables) { - ListView_Update( t->hwnd, index ); + if (SendMessageW(t->hwnd, LVM_UPDATE, (WPARAM) index, 0) == (LRESULT) (-1)) + logLastError(L"error calling LVM_UPDATE in uiTableModelRowChanged()"); } } void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) { for (auto t : m->tables) { - ListView_DeleteItem( t->hwnd, oldIndex ); + if (SendMessageW(t->hwnd, LVM_DELETEITEM, (WPARAM) oldIndex, 0) == (LRESULT) (-1)) + logLastError(L"error calling LVM_DELETEITEM in uiTableModelRowDeleted()"); } } @@ -80,6 +82,7 @@ void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand) { uiTable *t = c->t; int lvIndex = 0; + LVCOLUMNW lvc; if (c->modelColumn >=0) { return; // multiple parts not implemented @@ -92,16 +95,17 @@ void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand) break; } if (candidate->modelColumn >= 0) { - ++lvIndex; + lvIndex++; } } - - LV_COLUMN lvc; + + ZeroMemory(&lvc, sizeof (LVCOLUMNW)); lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT; /* | LVCF_SUBITEM; */ lvc.fmt = LVCFMT_LEFT; lvc.cx = 120; // TODO lvc.pszText = c->name; - ListView_InsertColumn(c->t->hwnd, lvIndex, &lvc); + if (SendMessageW(c->t->hwnd, LVM_INSERTCOLUMN, (WPARAM) lvIndex, (LPARAM) (&lvc)) == (LRESULT) (-1)) + logLastError(L"error calling LVM_INSERTCOLUMN in uiTableColumnPartSetTextPart()"); } void uiTableColumnAppendImagePart(uiTableColumn *c, int modelColumn, int expand) @@ -138,33 +142,11 @@ void uiTableColumnPartSetTextColor(uiTableColumn *c, int part, int modelColumn) uiWindowsControlAllDefaultsExceptDestroy(uiTable) -uiTable *uiNewTable(uiTableModel *model) -{ - uiTable *t; - int winStyle = WS_CHILD | LVS_AUTOARRANGE | LVS_REPORT | LVS_OWNERDATA | LVS_SINGLESEL; - - uiWindowsNewControl(uiTable, t); - new(&t->columns) std::vector(); // (initialising in place) - t->model = model; - t->hwnd = uiWindowsEnsureCreateControlHWND(WS_EX_CLIENTEDGE, - WC_LISTVIEW, - L"", - winStyle, - hInstance, - NULL, - TRUE); - model->tables.push_back(t); - uiWindowsRegisterWM_NOTIFYHandler(t->hwnd, onWM_NOTIFY, uiControl(t)); - ListView_SetExtendedListViewStyle(t->hwnd, LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP); - // TODO: try LVS_EX_AUTOSIZECOLUMNS - int n = (*(model->mh->NumRows))(model->mh, model); - ListView_SetItemCountEx(t->hwnd, n, 0); - return t; -} - uiTableColumn *uiTableAppendColumn(uiTable *t, const char *name) { - uiTableColumn *c = uiprivNew(uiTableColumn); + uiTableColumn *c; + + c = uiprivNew(uiTableColumn); c->name = toUTF16(name); c->t = t; c->modelColumn = -1; // -1 = unassigned @@ -182,81 +164,93 @@ static void uiTableDestroy(uiControl *c) { uiTable *t = uiTable(c); uiTableModel *model = t->model; - std::vector::iterator it; + std::vector::iterator it; uiWindowsUnregisterWM_NOTIFYHandler(t->hwnd); uiWindowsEnsureDestroyWindow(t->hwnd); // detach table from model - for (it = model->tables.begin(); it != model->tables.end(); ++it) { + for (it = model->tables.begin(); it != model->tables.end(); it++) { if (*it == t) { model->tables.erase(it); break; } } // free the columns - for (auto col: t->columns) { + for (auto col : t->columns) { uiprivFree(col->name); uiprivFree(col); } - t->columns.~vector(); // (created with placement new, so just call dtor directly) + t->columns.~vector(); // (created with placement new, so just call dtor directly) uiFreeControl(uiControl(t)); } +// suggested listview sizing from http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing: +// "columns widths that avoid truncated data x an integral number of items" +// Don't think that'll cut it when some cells have overlong data (eg +// stupidly long URLs). So for now, just hardcode a minimum: +#define tableMinWidth 107 /* in line with other controls */ +#define tableMinHeight (14*3) /* header + 2 lines (roughly) */ + static void uiTableMinimumSize(uiWindowsControl *c, int *width, int *height) { uiTable *t = uiTable(c); uiWindowsSizing sizing; int x, y; - // suggested listview sizing from http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing: - // "columns widths that avoid truncated data x an integral number of items" - // Don't think that'll cut it when some cells have overlong data (eg - // stupidly long URLs). So for now, just hardcode a minimum: - - x = 107; // in line with other controls - y = 14*3; // header + 2 lines (roughly) + x = tableMinWidth; + y = tableMinHeight; uiWindowsGetSizing(t->hwnd, &sizing); uiWindowsSizingDlgUnitsToPixels(&sizing, &x, &y); *width = x; *height = y; } -static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nm, LRESULT *lResult) + +static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult) { uiTable *t = uiTable(c); uiTableModelHandler *mh = t->model->mh; BOOL ret = FALSE; - switch (nm->code) { + switch (nmhdr->code) { case LVN_GETDISPINFO: { - NMLVDISPINFO* di = (NMLVDISPINFO*)nm; - LVITEM* item = &di->item; - if (!(item->mask & LVIF_TEXT)) { - break; - } - int row = item->iItem; - int col = item->iSubItem; - if (col<0 || col>=(int)t->columns.size()) { - break; - } + NMLVDISPINFOW *di; + LVITEMW *item; + int row, col; + uiTableColumn *tc; + int mcol; + uiTableModelColumnType typ; - uiTableColumn *tc = (uiTableColumn*)t->columns[col]; + di = (NMLVDISPINFOW *)nmhdr; + item = &(di->item); + if (!(item->mask & LVIF_TEXT)) + break; + row = item->iItem; + col = item->iSubItem; + if (col<0 || col>=(int)t->columns.size()) + break; + tc = (uiTableColumn *)t->columns[col]; + mcol = tc->modelColumn; + typ = (*mh->ColumnType)(mh, t->model, mcol); - int mcol = tc->modelColumn; - uiTableModelColumnType typ = (*mh->ColumnType)(mh,t->model,mcol); if (typ == uiTableModelColumnString) { - void* data = (*(mh->CellValue))(mh, t->model, row, mcol); - int n = MultiByteToWideChar(CP_UTF8, 0, (const char*)data, -1, item->pszText, item->cchTextMax); + void* data; + int n; + + data = (*(mh->CellValue))(mh, t->model, row, mcol); + n = MultiByteToWideChar(CP_UTF8, 0, (const char *)data, -1, item->pszText, item->cchTextMax); // make sure clipped strings are nul-terminated - if (n>=item->cchTextMax) { + if (n>=item->cchTextMax) item->pszText[item->cchTextMax-1] = L'\0'; - } } else if (typ == uiTableModelColumnInt) { char buf[32]; - intptr_t data = (intptr_t)(*(mh->CellValue))(mh, t->model, row, mcol); + intptr_t data; + int n; + + data = (intptr_t)(*(mh->CellValue))(mh, t->model, row, mcol); sprintf(buf, "%d", (int)data); - int n = MultiByteToWideChar(CP_UTF8, 0, buf, -1, item->pszText, item->cchTextMax); + n = MultiByteToWideChar(CP_UTF8, 0, buf, -1, item->pszText, item->cchTextMax); // make sure clipped strings are nul-terminated if (n>=item->cchTextMax) { item->pszText[item->cchTextMax-1] = L'\0'; @@ -273,3 +267,29 @@ static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nm, LRESULT *lResult) return ret; } +uiTable *uiNewTable(uiTableModel *model) +{ + uiTable *t; + int n; + + uiWindowsNewControl(uiTable, t); + new(&t->columns) std::vector(); // (initialising in place) + t->model = model; + t->hwnd = uiWindowsEnsureCreateControlHWND(WS_EX_CLIENTEDGE, + WC_LISTVIEW, L"", + LVS_REPORT | LVS_OWNERDATA | LVS_SINGLESEL | WS_TABSTOP | WS_HSCROLL | WS_VSCROLL, + hInstance, NULL, + TRUE); + model->tables.push_back(t); + uiWindowsRegisterWM_NOTIFYHandler(t->hwnd, onWM_NOTIFY, uiControl(t)); + + SendMessageW(t->hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, + (WPARAM) (LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP), + (LPARAM) (LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP)); + // TODO: try LVS_EX_AUTOSIZECOLUMNS + n = (*(model->mh->NumRows))(model->mh, model); + if (SendMessageW(t->hwnd, LVM_SETITEMCOUNT, (WPARAM) n, 0) == 0) + logLastError(L"error calling LVM_SETITEMCOUNT in uiNewTable()"); + return t; +} + From bd79a2fa8faec8146c47f52bc996140dcc598e34 Mon Sep 17 00:00:00 2001 From: George Zhao Date: Sun, 27 May 2018 14:52:21 +0800 Subject: [PATCH 1109/1329] Update README.md Add another libui bare bindings for lua --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a18050c4..016615d1 100644 --- a/README.md +++ b/README.md @@ -211,7 +211,7 @@ Haskell | [haskell-libui](https://github.com/beijaflor-io/haskell-libui) JavaScript | [libui.js (merged into libui-node?)](https://github.com/mavenave/libui.js), [proton-native](https://github.com/kusti8/proton-native) Julia | [Libui.jl](https://github.com/joa-quim/Libui.jl) Kotlin | [kotlin-libui](https://github.com/msink/kotlin-libui) -Lua | [libuilua](https://github.com/zevv/libuilua), [libui-lua](https://github.com/mdombroski/libui-lua), [lui](http://tset.de/lui/index.html) +Lua | [libuilua](https://github.com/zevv/libuilua), [libui-lua](https://github.com/mdombroski/libui-lua), [lui](http://tset.de/lui/index.html), [lui](https://github.com/zhaozg/lui) Nim | [ui](https://github.com/nim-lang/ui) Node.js | [libui-node](https://github.com/parro-it/libui-node) PHP | [ui](https://github.com/krakjoe/ui) From ba13227bed454ac8f76455feb4a6d409fd497b95 Mon Sep 17 00:00:00 2001 From: Ben Campbell Date: Mon, 28 May 2018 20:26:07 +1200 Subject: [PATCH 1110/1329] further style consistency tweaks --- windows/table.cpp | 43 +++++++++++++++---------------------------- 1 file changed, 15 insertions(+), 28 deletions(-) diff --git a/windows/table.cpp b/windows/table.cpp index 2f7737ee..ad05c62c 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -1,9 +1,5 @@ #include "uipriv_windows.hpp" -//static void uiTableMinimumSize(uiWindowsControl *c, int *width, int *height); - -struct uiTable; - struct uiTableModel { uiTableModelHandler *mh; std::vector tables; @@ -42,7 +38,6 @@ uiTableModel *uiNewTableModel(uiTableModelHandler *mh) return m; } - void uiFreeTableModel(uiTableModel *m) { delete m; @@ -55,27 +50,24 @@ void uiTableModelRowInserted(uiTableModel *m, int newIndex) ZeroMemory(&item, sizeof (LVITEMW)); item.mask = 0; item.iItem = newIndex; - item.iSubItem = 0; //? - for (auto t : m->tables) { + item.iSubItem = 0; + for (auto t : m->tables) if (SendMessageW(t->hwnd, LVM_INSERTITEM, 0, (LPARAM) (&item)) == (LRESULT) (-1)) logLastError(L"error calling LVM_INSERTITEM in uiTableModelRowInserted()"); - } } void uiTableModelRowChanged(uiTableModel *m, int index) { - for (auto t : m->tables) { + for (auto t : m->tables) if (SendMessageW(t->hwnd, LVM_UPDATE, (WPARAM) index, 0) == (LRESULT) (-1)) logLastError(L"error calling LVM_UPDATE in uiTableModelRowChanged()"); - } } void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) { - for (auto t : m->tables) { + for (auto t : m->tables) if (SendMessageW(t->hwnd, LVM_DELETEITEM, (WPARAM) oldIndex, 0) == (LRESULT) (-1)) logLastError(L"error calling LVM_DELETEITEM in uiTableModelRowDeleted()"); - } } void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand) @@ -84,19 +76,16 @@ void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand) int lvIndex = 0; LVCOLUMNW lvc; - if (c->modelColumn >=0) { + if (c->modelColumn >= 0) return; // multiple parts not implemented - } c->modelColumn = modelColumn; // work out appropriate listview index for the column for (auto candidate : t->columns) { - if (candidate == c) { + if (candidate == c) break; - } - if (candidate->modelColumn >= 0) { + if (candidate->modelColumn >= 0) lvIndex++; - } } ZeroMemory(&lvc, sizeof (LVCOLUMNW)); @@ -130,7 +119,7 @@ void uiTableColumnAppendProgressBarPart(uiTableColumn *c, int modelColumn, int e void uiTableColumnPartSetEditable(uiTableColumn *c, int part, int editable) { - // TODO + // not implemented } void uiTableColumnPartSetTextColor(uiTableColumn *c, int part, int modelColumn) @@ -187,7 +176,8 @@ static void uiTableDestroy(uiControl *c) // suggested listview sizing from http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing: // "columns widths that avoid truncated data x an integral number of items" // Don't think that'll cut it when some cells have overlong data (eg -// stupidly long URLs). So for now, just hardcode a minimum: +// stupidly long URLs). So for now, just hardcode a minimum. +// TODO: Investigate using LVM_GETHEADER/HDM_LAYOUT here... #define tableMinWidth 107 /* in line with other controls */ #define tableMinHeight (14*3) /* header + 2 lines (roughly) */ @@ -205,7 +195,6 @@ static void uiTableMinimumSize(uiWindowsControl *c, int *width, int *height) *height = y; } - static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult) { uiTable *t = uiTable(c); @@ -228,7 +217,7 @@ static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult) break; row = item->iItem; col = item->iSubItem; - if (col<0 || col>=(int)t->columns.size()) + if (col < 0 || col >= (int)t->columns.size()) break; tc = (uiTableColumn *)t->columns[col]; mcol = tc->modelColumn; @@ -241,7 +230,7 @@ static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult) data = (*(mh->CellValue))(mh, t->model, row, mcol); n = MultiByteToWideChar(CP_UTF8, 0, (const char *)data, -1, item->pszText, item->cchTextMax); // make sure clipped strings are nul-terminated - if (n>=item->cchTextMax) + if (n >= item->cchTextMax) item->pszText[item->cchTextMax-1] = L'\0'; } else if (typ == uiTableModelColumnInt) { char buf[32]; @@ -252,12 +241,10 @@ static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult) sprintf(buf, "%d", (int)data); n = MultiByteToWideChar(CP_UTF8, 0, buf, -1, item->pszText, item->cchTextMax); // make sure clipped strings are nul-terminated - if (n>=item->cchTextMax) { + if (n >= item->cchTextMax) item->pszText[item->cchTextMax-1] = L'\0'; - } - } else { + } else item->pszText[0] = L'\0'; - } break; } default: @@ -283,10 +270,10 @@ uiTable *uiNewTable(uiTableModel *model) model->tables.push_back(t); uiWindowsRegisterWM_NOTIFYHandler(t->hwnd, onWM_NOTIFY, uiControl(t)); + // TODO: try LVS_EX_AUTOSIZECOLUMNS SendMessageW(t->hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, (WPARAM) (LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP), (LPARAM) (LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP)); - // TODO: try LVS_EX_AUTOSIZECOLUMNS n = (*(model->mh->NumRows))(model->mh, model); if (SendMessageW(t->hwnd, LVM_SETITEMCOUNT, (WPARAM) n, 0) == 0) logLastError(L"error calling LVM_SETITEMCOUNT in uiNewTable()"); From c3be9f221c236c594c80713b12daa9dd818aeacb Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 28 May 2018 13:37:40 -0400 Subject: [PATCH 1111/1329] Fixed a typo that led to CRLF bugs in uiMultilineEntry on Windows. Thanks to @mimecorg for spotting it. Update #359 --- windows/multilineentry.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/multilineentry.cpp b/windows/multilineentry.cpp index 391f4855..1365e4e3 100644 --- a/windows/multilineentry.cpp +++ b/windows/multilineentry.cpp @@ -75,7 +75,7 @@ void uiMultilineEntrySetText(uiMultilineEntry *e, const char *text) // doing this raises an EN_CHANGED e->inhibitChanged = TRUE; crlf = LFtoCRLF(text); - uiWindowsSetWindowText(e->hwnd, text); + uiWindowsSetWindowText(e->hwnd, crlf); uiprivFree(crlf); e->inhibitChanged = FALSE; // don't queue the control for resize; entry sizes are independent of their contents From b3b21196a157f91dd2b1cd569f8586fbeadfa64b Mon Sep 17 00:00:00 2001 From: Ben Campbell Date: Tue, 29 May 2018 18:17:10 +1200 Subject: [PATCH 1112/1329] minor windows table tweaks --- windows/table.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/windows/table.cpp b/windows/table.cpp index ad05c62c..120bc830 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -119,7 +119,7 @@ void uiTableColumnAppendProgressBarPart(uiTableColumn *c, int modelColumn, int e void uiTableColumnPartSetEditable(uiTableColumn *c, int part, int editable) { - // not implemented + // TODO } void uiTableColumnPartSetTextColor(uiTableColumn *c, int part, int modelColumn) @@ -247,8 +247,6 @@ static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult) item->pszText[0] = L'\0'; break; } - default: - break; } *lResult = 0; return ret; From a7fe45b8a5753332cefe5e3708a0aa1dc93552ce Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 29 May 2018 20:26:48 -0400 Subject: [PATCH 1113/1329] Removed carriage returns. --- windows/image.cpp | 62 ++--- windows/table.cpp | 560 +++++++++++++++++++++++----------------------- 2 files changed, 311 insertions(+), 311 deletions(-) diff --git a/windows/image.cpp b/windows/image.cpp index bba013b1..e3b78475 100644 --- a/windows/image.cpp +++ b/windows/image.cpp @@ -1,31 +1,31 @@ -#include "uipriv_windows.hpp" -// stubbed out windows image list implementation. -// Required for uiTable control, but windows implemenation -// doesn't currently have image support. - -struct uiImage { - double width; - double height; - // HIMAGELIST images; -}; - -uiImage *uiNewImage(double width, double height) -{ - uiImage *i; - - i = uiprivNew(uiImage); - i->width = width; - i->height = height; - return i; -} - -void uiFreeImage(uiImage *i) -{ - uiprivFree(i); -} - -void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int pixelStride) -{ - // not implemented -} - +#include "uipriv_windows.hpp" +// stubbed out windows image list implementation. +// Required for uiTable control, but windows implemenation +// doesn't currently have image support. + +struct uiImage { + double width; + double height; + // HIMAGELIST images; +}; + +uiImage *uiNewImage(double width, double height) +{ + uiImage *i; + + i = uiprivNew(uiImage); + i->width = width; + i->height = height; + return i; +} + +void uiFreeImage(uiImage *i) +{ + uiprivFree(i); +} + +void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int pixelStride) +{ + // not implemented +} + diff --git a/windows/table.cpp b/windows/table.cpp index 120bc830..934acd46 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -1,280 +1,280 @@ -#include "uipriv_windows.hpp" - -struct uiTableModel { - uiTableModelHandler *mh; - std::vector tables; -}; - -struct uiTableColumn { - uiTable *t; - WCHAR *name; - // don't really support parts (but this would part=>column mappings if we did) - int modelColumn; // -1 = none -}; - -struct uiTable { - uiWindowsControl c; - uiTableModel *model; - HWND hwnd; - std::vector columns; -}; - -void *uiTableModelStrdup(const char *str) -{ - return strdup(str); -} - -void *uiTableModelGiveColor(double r, double g, double b, double a) -{ - return 0; // not implemented -} - -uiTableModel *uiNewTableModel(uiTableModelHandler *mh) -{ - uiTableModel *m; - - m = new uiTableModel(); - m->mh = mh; - return m; -} - -void uiFreeTableModel(uiTableModel *m) -{ - delete m; -} - -void uiTableModelRowInserted(uiTableModel *m, int newIndex) -{ - LVITEMW item; - - ZeroMemory(&item, sizeof (LVITEMW)); - item.mask = 0; - item.iItem = newIndex; - item.iSubItem = 0; - for (auto t : m->tables) - if (SendMessageW(t->hwnd, LVM_INSERTITEM, 0, (LPARAM) (&item)) == (LRESULT) (-1)) - logLastError(L"error calling LVM_INSERTITEM in uiTableModelRowInserted()"); -} - -void uiTableModelRowChanged(uiTableModel *m, int index) -{ - for (auto t : m->tables) - if (SendMessageW(t->hwnd, LVM_UPDATE, (WPARAM) index, 0) == (LRESULT) (-1)) - logLastError(L"error calling LVM_UPDATE in uiTableModelRowChanged()"); -} - -void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) -{ - for (auto t : m->tables) - if (SendMessageW(t->hwnd, LVM_DELETEITEM, (WPARAM) oldIndex, 0) == (LRESULT) (-1)) - logLastError(L"error calling LVM_DELETEITEM in uiTableModelRowDeleted()"); -} - -void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand) -{ - uiTable *t = c->t; - int lvIndex = 0; - LVCOLUMNW lvc; - - if (c->modelColumn >= 0) - return; // multiple parts not implemented - c->modelColumn = modelColumn; - - // work out appropriate listview index for the column - for (auto candidate : t->columns) { - if (candidate == c) - break; - if (candidate->modelColumn >= 0) - lvIndex++; - } - - ZeroMemory(&lvc, sizeof (LVCOLUMNW)); - lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT; /* | LVCF_SUBITEM; */ - lvc.fmt = LVCFMT_LEFT; - lvc.cx = 120; // TODO - lvc.pszText = c->name; - if (SendMessageW(c->t->hwnd, LVM_INSERTCOLUMN, (WPARAM) lvIndex, (LPARAM) (&lvc)) == (LRESULT) (-1)) - logLastError(L"error calling LVM_INSERTCOLUMN in uiTableColumnPartSetTextPart()"); -} - -void uiTableColumnAppendImagePart(uiTableColumn *c, int modelColumn, int expand) -{ - // not implemented -} - -void uiTableColumnAppendButtonPart(uiTableColumn *c, int modelColumn, int expand) -{ - // not implemented -} - -void uiTableColumnAppendCheckboxPart(uiTableColumn *c, int modelColumn, int expand) -{ - // not implemented -} - -void uiTableColumnAppendProgressBarPart(uiTableColumn *c, int modelColumn, int expand) -{ - // not implemented -} - -void uiTableColumnPartSetEditable(uiTableColumn *c, int part, int editable) -{ - // TODO -} - -void uiTableColumnPartSetTextColor(uiTableColumn *c, int part, int modelColumn) -{ - // not implemented -} - -// uiTable implementation - -uiWindowsControlAllDefaultsExceptDestroy(uiTable) - -uiTableColumn *uiTableAppendColumn(uiTable *t, const char *name) -{ - uiTableColumn *c; - - c = uiprivNew(uiTableColumn); - c->name = toUTF16(name); - c->t = t; - c->modelColumn = -1; // -1 = unassigned - // we defer the actual ListView_InsertColumn call until a part is added... - t->columns.push_back(c); - return c; -} - -void uiTableSetRowBackgroundColorModelColumn(uiTable *t, int modelColumn) -{ - // not implemented -} - -static void uiTableDestroy(uiControl *c) -{ - uiTable *t = uiTable(c); - uiTableModel *model = t->model; - std::vector::iterator it; - - uiWindowsUnregisterWM_NOTIFYHandler(t->hwnd); - uiWindowsEnsureDestroyWindow(t->hwnd); - // detach table from model - for (it = model->tables.begin(); it != model->tables.end(); it++) { - if (*it == t) { - model->tables.erase(it); - break; - } - } - // free the columns - for (auto col : t->columns) { - uiprivFree(col->name); - uiprivFree(col); - } - t->columns.~vector(); // (created with placement new, so just call dtor directly) - uiFreeControl(uiControl(t)); -} - -// suggested listview sizing from http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing: -// "columns widths that avoid truncated data x an integral number of items" -// Don't think that'll cut it when some cells have overlong data (eg -// stupidly long URLs). So for now, just hardcode a minimum. -// TODO: Investigate using LVM_GETHEADER/HDM_LAYOUT here... -#define tableMinWidth 107 /* in line with other controls */ -#define tableMinHeight (14*3) /* header + 2 lines (roughly) */ - -static void uiTableMinimumSize(uiWindowsControl *c, int *width, int *height) -{ - uiTable *t = uiTable(c); - uiWindowsSizing sizing; - int x, y; - - x = tableMinWidth; - y = tableMinHeight; - uiWindowsGetSizing(t->hwnd, &sizing); - uiWindowsSizingDlgUnitsToPixels(&sizing, &x, &y); - *width = x; - *height = y; -} - -static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult) -{ - uiTable *t = uiTable(c); - uiTableModelHandler *mh = t->model->mh; - BOOL ret = FALSE; - - switch (nmhdr->code) { - case LVN_GETDISPINFO: - { - NMLVDISPINFOW *di; - LVITEMW *item; - int row, col; - uiTableColumn *tc; - int mcol; - uiTableModelColumnType typ; - - di = (NMLVDISPINFOW *)nmhdr; - item = &(di->item); - if (!(item->mask & LVIF_TEXT)) - break; - row = item->iItem; - col = item->iSubItem; - if (col < 0 || col >= (int)t->columns.size()) - break; - tc = (uiTableColumn *)t->columns[col]; - mcol = tc->modelColumn; - typ = (*mh->ColumnType)(mh, t->model, mcol); - - if (typ == uiTableModelColumnString) { - void* data; - int n; - - data = (*(mh->CellValue))(mh, t->model, row, mcol); - n = MultiByteToWideChar(CP_UTF8, 0, (const char *)data, -1, item->pszText, item->cchTextMax); - // make sure clipped strings are nul-terminated - if (n >= item->cchTextMax) - item->pszText[item->cchTextMax-1] = L'\0'; - } else if (typ == uiTableModelColumnInt) { - char buf[32]; - intptr_t data; - int n; - - data = (intptr_t)(*(mh->CellValue))(mh, t->model, row, mcol); - sprintf(buf, "%d", (int)data); - n = MultiByteToWideChar(CP_UTF8, 0, buf, -1, item->pszText, item->cchTextMax); - // make sure clipped strings are nul-terminated - if (n >= item->cchTextMax) - item->pszText[item->cchTextMax-1] = L'\0'; - } else - item->pszText[0] = L'\0'; - break; - } - } - *lResult = 0; - return ret; -} - -uiTable *uiNewTable(uiTableModel *model) -{ - uiTable *t; - int n; - - uiWindowsNewControl(uiTable, t); - new(&t->columns) std::vector(); // (initialising in place) - t->model = model; - t->hwnd = uiWindowsEnsureCreateControlHWND(WS_EX_CLIENTEDGE, - WC_LISTVIEW, L"", - LVS_REPORT | LVS_OWNERDATA | LVS_SINGLESEL | WS_TABSTOP | WS_HSCROLL | WS_VSCROLL, - hInstance, NULL, - TRUE); - model->tables.push_back(t); - uiWindowsRegisterWM_NOTIFYHandler(t->hwnd, onWM_NOTIFY, uiControl(t)); - - // TODO: try LVS_EX_AUTOSIZECOLUMNS - SendMessageW(t->hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, - (WPARAM) (LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP), - (LPARAM) (LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP)); - n = (*(model->mh->NumRows))(model->mh, model); - if (SendMessageW(t->hwnd, LVM_SETITEMCOUNT, (WPARAM) n, 0) == 0) - logLastError(L"error calling LVM_SETITEMCOUNT in uiNewTable()"); - return t; -} - +#include "uipriv_windows.hpp" + +struct uiTableModel { + uiTableModelHandler *mh; + std::vector tables; +}; + +struct uiTableColumn { + uiTable *t; + WCHAR *name; + // don't really support parts (but this would part=>column mappings if we did) + int modelColumn; // -1 = none +}; + +struct uiTable { + uiWindowsControl c; + uiTableModel *model; + HWND hwnd; + std::vector columns; +}; + +void *uiTableModelStrdup(const char *str) +{ + return strdup(str); +} + +void *uiTableModelGiveColor(double r, double g, double b, double a) +{ + return 0; // not implemented +} + +uiTableModel *uiNewTableModel(uiTableModelHandler *mh) +{ + uiTableModel *m; + + m = new uiTableModel(); + m->mh = mh; + return m; +} + +void uiFreeTableModel(uiTableModel *m) +{ + delete m; +} + +void uiTableModelRowInserted(uiTableModel *m, int newIndex) +{ + LVITEMW item; + + ZeroMemory(&item, sizeof (LVITEMW)); + item.mask = 0; + item.iItem = newIndex; + item.iSubItem = 0; + for (auto t : m->tables) + if (SendMessageW(t->hwnd, LVM_INSERTITEM, 0, (LPARAM) (&item)) == (LRESULT) (-1)) + logLastError(L"error calling LVM_INSERTITEM in uiTableModelRowInserted()"); +} + +void uiTableModelRowChanged(uiTableModel *m, int index) +{ + for (auto t : m->tables) + if (SendMessageW(t->hwnd, LVM_UPDATE, (WPARAM) index, 0) == (LRESULT) (-1)) + logLastError(L"error calling LVM_UPDATE in uiTableModelRowChanged()"); +} + +void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) +{ + for (auto t : m->tables) + if (SendMessageW(t->hwnd, LVM_DELETEITEM, (WPARAM) oldIndex, 0) == (LRESULT) (-1)) + logLastError(L"error calling LVM_DELETEITEM in uiTableModelRowDeleted()"); +} + +void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand) +{ + uiTable *t = c->t; + int lvIndex = 0; + LVCOLUMNW lvc; + + if (c->modelColumn >= 0) + return; // multiple parts not implemented + c->modelColumn = modelColumn; + + // work out appropriate listview index for the column + for (auto candidate : t->columns) { + if (candidate == c) + break; + if (candidate->modelColumn >= 0) + lvIndex++; + } + + ZeroMemory(&lvc, sizeof (LVCOLUMNW)); + lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT; /* | LVCF_SUBITEM; */ + lvc.fmt = LVCFMT_LEFT; + lvc.cx = 120; // TODO + lvc.pszText = c->name; + if (SendMessageW(c->t->hwnd, LVM_INSERTCOLUMN, (WPARAM) lvIndex, (LPARAM) (&lvc)) == (LRESULT) (-1)) + logLastError(L"error calling LVM_INSERTCOLUMN in uiTableColumnPartSetTextPart()"); +} + +void uiTableColumnAppendImagePart(uiTableColumn *c, int modelColumn, int expand) +{ + // not implemented +} + +void uiTableColumnAppendButtonPart(uiTableColumn *c, int modelColumn, int expand) +{ + // not implemented +} + +void uiTableColumnAppendCheckboxPart(uiTableColumn *c, int modelColumn, int expand) +{ + // not implemented +} + +void uiTableColumnAppendProgressBarPart(uiTableColumn *c, int modelColumn, int expand) +{ + // not implemented +} + +void uiTableColumnPartSetEditable(uiTableColumn *c, int part, int editable) +{ + // TODO +} + +void uiTableColumnPartSetTextColor(uiTableColumn *c, int part, int modelColumn) +{ + // not implemented +} + +// uiTable implementation + +uiWindowsControlAllDefaultsExceptDestroy(uiTable) + +uiTableColumn *uiTableAppendColumn(uiTable *t, const char *name) +{ + uiTableColumn *c; + + c = uiprivNew(uiTableColumn); + c->name = toUTF16(name); + c->t = t; + c->modelColumn = -1; // -1 = unassigned + // we defer the actual ListView_InsertColumn call until a part is added... + t->columns.push_back(c); + return c; +} + +void uiTableSetRowBackgroundColorModelColumn(uiTable *t, int modelColumn) +{ + // not implemented +} + +static void uiTableDestroy(uiControl *c) +{ + uiTable *t = uiTable(c); + uiTableModel *model = t->model; + std::vector::iterator it; + + uiWindowsUnregisterWM_NOTIFYHandler(t->hwnd); + uiWindowsEnsureDestroyWindow(t->hwnd); + // detach table from model + for (it = model->tables.begin(); it != model->tables.end(); it++) { + if (*it == t) { + model->tables.erase(it); + break; + } + } + // free the columns + for (auto col : t->columns) { + uiprivFree(col->name); + uiprivFree(col); + } + t->columns.~vector(); // (created with placement new, so just call dtor directly) + uiFreeControl(uiControl(t)); +} + +// suggested listview sizing from http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing: +// "columns widths that avoid truncated data x an integral number of items" +// Don't think that'll cut it when some cells have overlong data (eg +// stupidly long URLs). So for now, just hardcode a minimum. +// TODO: Investigate using LVM_GETHEADER/HDM_LAYOUT here... +#define tableMinWidth 107 /* in line with other controls */ +#define tableMinHeight (14*3) /* header + 2 lines (roughly) */ + +static void uiTableMinimumSize(uiWindowsControl *c, int *width, int *height) +{ + uiTable *t = uiTable(c); + uiWindowsSizing sizing; + int x, y; + + x = tableMinWidth; + y = tableMinHeight; + uiWindowsGetSizing(t->hwnd, &sizing); + uiWindowsSizingDlgUnitsToPixels(&sizing, &x, &y); + *width = x; + *height = y; +} + +static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult) +{ + uiTable *t = uiTable(c); + uiTableModelHandler *mh = t->model->mh; + BOOL ret = FALSE; + + switch (nmhdr->code) { + case LVN_GETDISPINFO: + { + NMLVDISPINFOW *di; + LVITEMW *item; + int row, col; + uiTableColumn *tc; + int mcol; + uiTableModelColumnType typ; + + di = (NMLVDISPINFOW *)nmhdr; + item = &(di->item); + if (!(item->mask & LVIF_TEXT)) + break; + row = item->iItem; + col = item->iSubItem; + if (col < 0 || col >= (int)t->columns.size()) + break; + tc = (uiTableColumn *)t->columns[col]; + mcol = tc->modelColumn; + typ = (*mh->ColumnType)(mh, t->model, mcol); + + if (typ == uiTableModelColumnString) { + void* data; + int n; + + data = (*(mh->CellValue))(mh, t->model, row, mcol); + n = MultiByteToWideChar(CP_UTF8, 0, (const char *)data, -1, item->pszText, item->cchTextMax); + // make sure clipped strings are nul-terminated + if (n >= item->cchTextMax) + item->pszText[item->cchTextMax-1] = L'\0'; + } else if (typ == uiTableModelColumnInt) { + char buf[32]; + intptr_t data; + int n; + + data = (intptr_t)(*(mh->CellValue))(mh, t->model, row, mcol); + sprintf(buf, "%d", (int)data); + n = MultiByteToWideChar(CP_UTF8, 0, buf, -1, item->pszText, item->cchTextMax); + // make sure clipped strings are nul-terminated + if (n >= item->cchTextMax) + item->pszText[item->cchTextMax-1] = L'\0'; + } else + item->pszText[0] = L'\0'; + break; + } + } + *lResult = 0; + return ret; +} + +uiTable *uiNewTable(uiTableModel *model) +{ + uiTable *t; + int n; + + uiWindowsNewControl(uiTable, t); + new(&t->columns) std::vector(); // (initialising in place) + t->model = model; + t->hwnd = uiWindowsEnsureCreateControlHWND(WS_EX_CLIENTEDGE, + WC_LISTVIEW, L"", + LVS_REPORT | LVS_OWNERDATA | LVS_SINGLESEL | WS_TABSTOP | WS_HSCROLL | WS_VSCROLL, + hInstance, NULL, + TRUE); + model->tables.push_back(t); + uiWindowsRegisterWM_NOTIFYHandler(t->hwnd, onWM_NOTIFY, uiControl(t)); + + // TODO: try LVS_EX_AUTOSIZECOLUMNS + SendMessageW(t->hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, + (WPARAM) (LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP), + (LPARAM) (LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP)); + n = (*(model->mh->NumRows))(model->mh, model); + if (SendMessageW(t->hwnd, LVM_SETITEMCOUNT, (WPARAM) n, 0) == 0) + logLastError(L"error calling LVM_SETITEMCOUNT in uiNewTable()"); + return t; +} + From 374eed74329a706a53661c8636632e5c9cb88111 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 29 May 2018 20:27:31 -0400 Subject: [PATCH 1114/1329] Removed the facilities for printing an int as text; this was causing the tester to crash in a weird way on OS X (through NSApplication _crashOnException: without telling me what that exception was) and I didn't intend on this part type to be used in this way anyway... --- test/page16.c | 2 -- windows/table.cpp | 11 ----------- 2 files changed, 13 deletions(-) diff --git a/test/page16.c b/test/page16.c index 3a5ac639..80ac0139 100644 --- a/test/page16.c +++ b/test/page16.c @@ -132,8 +132,6 @@ uiBox *makePage16(void) uiTableSetRowBackgroundColorModelColumn(t, 3); - uiTableAppendTextColumn(t, "Numbers", 8); - tc = uiTableAppendColumn(t, "Buttons"); uiTableColumnAppendCheckboxPart(tc, 7, 0); uiTableColumnAppendButtonPart(tc, 6, 1); diff --git a/windows/table.cpp b/windows/table.cpp index 934acd46..c5dc9ef3 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -232,17 +232,6 @@ static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult) // make sure clipped strings are nul-terminated if (n >= item->cchTextMax) item->pszText[item->cchTextMax-1] = L'\0'; - } else if (typ == uiTableModelColumnInt) { - char buf[32]; - intptr_t data; - int n; - - data = (intptr_t)(*(mh->CellValue))(mh, t->model, row, mcol); - sprintf(buf, "%d", (int)data); - n = MultiByteToWideChar(CP_UTF8, 0, buf, -1, item->pszText, item->cchTextMax); - // make sure clipped strings are nul-terminated - if (n >= item->cchTextMax) - item->pszText[item->cchTextMax-1] = L'\0'; } else item->pszText[0] = L'\0'; break; From 0e7f8665316e12f078040e45a24b5fec7a6b7791 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 30 May 2018 08:05:16 -0400 Subject: [PATCH 1115/1329] More bindings. --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index a18050c4..4e69ba75 100644 --- a/README.md +++ b/README.md @@ -208,12 +208,11 @@ D | [DerelictLibui (flat API)](https://github.com/Extrawurst/DerelictLibui), [li Euphoria | [libui-euphoria](https://github.com/ghaberek/libui-euphoria) Harbour | [HBUI](https://github.com/RJopek/HBUI) Haskell | [haskell-libui](https://github.com/beijaflor-io/haskell-libui) -JavaScript | [libui.js (merged into libui-node?)](https://github.com/mavenave/libui.js), [proton-native](https://github.com/kusti8/proton-native) +JavaScript/Node.js | [libui-node](https://github.com/parro-it/libui-node), [libui.js (merged into libui-node?)](https://github.com/mavenave/libui.js), [proton-native](https://github.com/kusti8/proton-native), [vuido](https://github.com/mimecorg/vuido) Julia | [Libui.jl](https://github.com/joa-quim/Libui.jl) Kotlin | [kotlin-libui](https://github.com/msink/kotlin-libui) Lua | [libuilua](https://github.com/zevv/libuilua), [libui-lua](https://github.com/mdombroski/libui-lua), [lui](http://tset.de/lui/index.html) Nim | [ui](https://github.com/nim-lang/ui) -Node.js | [libui-node](https://github.com/parro-it/libui-node) PHP | [ui](https://github.com/krakjoe/ui) Python | [pylibui](https://github.com/joaoventura/pylibui), [pylibui-cffi](https://github.com/Yardanico/pylibui-cffi) Ruby | [libui-ruby](https://github.com/jamescook/libui-ruby) From 1530192673d9a11e3f3d6e3948fd5c1a44c5bb55 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 30 May 2018 08:49:24 -0400 Subject: [PATCH 1116/1329] Finally got around to merging ANNOUNCE and Changelog into NEWS. --- ANNOUNCE.md | 22 ---------- Changelog.md | 33 --------------- NEWS.md | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 95 ++++--------------------------------------- 4 files changed, 122 insertions(+), 141 deletions(-) delete mode 100644 ANNOUNCE.md delete mode 100644 Changelog.md create mode 100644 NEWS.md diff --git a/ANNOUNCE.md b/ANNOUNCE.md deleted file mode 100644 index 4db42eb4..00000000 --- a/ANNOUNCE.md +++ /dev/null @@ -1,22 +0,0 @@ -# Old Announcements - -* **29 May 2016** - * **Alpha 3 is here!** Get it [here](https://github.com/andlabs/libui/releases/tag/alpha3). - * The next packaged release will introduce: - * uiGrid, another way to lay out controls, a la GtkGrid - * uiOpenGLArea, a way to render OpenGL content in a libui uiArea - * uiTable, a data grid control that may or may not have tree facilities (if it does, it will be called uiTree instead) - * a complete, possibly rewritten, drawing and text rendering infrastructure - -* **24 May 2016** - * You can now help choose [a potential new build system for libui](https://github.com/andlabs/libui/issues/62). - * Tomorrow I will decide if OS X 10.7 will also be dropped alongside GTK+ 3.4-3.8 this Saturday. Stay tuned. - -* **22 May 2016** - * Two more open questions I'd like your feedback on are available [here](https://github.com/andlabs/libui/issues/48) and [here](https://github.com/andlabs/libui/issues/25). - * Sometime in the next 48 hours (before 23:59 EDT on 24 May 2016) I will split `uiCombobox` into two separate controls, `uiCombobox` and `uiEditableCombobox`, each with slightly different events and "selected item" mechanics. Prepare your existing code. - -* **21 May 2016** - * I will now post announcements and updates here. - * Now that Ubuntu 16.04 LTS is here, no earlier than next Saturday, 28 May 2016 at noon EDT, **I will bump the minimum GTK+ version from 3.4 to 3.10**. This will add a lot of new features that I can now add to libui, such as search-oriented uiEntries, lists of arbitrary control layouts, and more. If you are still running a Linux distribution that doesn't come with 3.10, you will either need to upgrade or use jhbuild to set up a newer version of GTK+ in a private environment. - * You can decide if I should also drop OS X 10.7 [here](https://github.com/andlabs/libui/issues/46). diff --git a/Changelog.md b/Changelog.md deleted file mode 100644 index 29fac274..00000000 --- a/Changelog.md +++ /dev/null @@ -1,33 +0,0 @@ -# Old Updates - -* **29 May 2016** - * Thanks to @pcwalton, we can now statically link libui! Simply do `make STATIC=1` instead of just `make`. - * On Windows you must link both `libui.lib` and `libui.res` AND provide a Common Controls 6 manifest for output static binaries to work properly. - -* **28 May 2016** - * As promised, **the minimum system requirements are now OS X 10.8 and GTK+ 3.10 for OS X and Unix, respectively**. - -* **26 May 2016** - * Two OS X-specific functions have been added: `uiDarwinMarginAmount()` and `uiDarwinPaddingAmount()`. These return the amount of margins and padding, respectively, to give to a control, and are intended for container implementations. These are suitable for the constant of a NSLayoutConstraint. They both take a pointer parameter that is reserved for future use and should be `NULL`. - -* **25 May 2016** - * uiDrawTextLayout attributes are now specified in units of *graphemes* on all platforms. This means characters as seen from a user's perspective, not Unicode codepoints or UTF-8 bytes. So a long string of combining marker codepoints after one codepoint would still count as one grapheme. - -* **24 May 2016** - * As promised, `uiCombobox` is now split into `uiCombobox` for non-editable comboboxes and `uiEditableCombobox` for editable comboboxes. Mind the function changes as well :) - * There is a new function `uiMainStep()`, which runs one iteration of the main loop. It takes a single boolean argument, indicating whether to wait for an event to occur or not. It returns true if an event was processed (or if no event is available if you don't want to wait) and false if the event loop was told to stop (for instance, `uiQuit()` was called). - -* **23 May 2016** - * Fixed surrogate pair drawing on OS X. - -* **22 May 2016** - * Removed `uiControlVerifyDestroy()`; that is now part of `uiFreeControl()` itself. - * Added `uiPi`, a constant for π. This is provided for C and C++ programmers, where there is no standard named constant for π; bindings authors shouldn't need to worry about this. - * Fixed uiMultilineEntry not properly having line breaks on Windows. - * Added `uiNewNonWrappingMultilineEntry()`, which creates a uiMultilineEntry that scrolls horizontally instead of wrapping lines. (This is not documented as being changeable after the fact on Windows, hence it's a creation-time choice.) - * uiAreas on Windows and some internal Direct2D areas now respond to `WM_PRINTCLIENT` properly, which should hopefully increase the quality of screenshots. - * uiDateTimePicker on GTK+ works properly on RTL layouts and no longer disappears off the bottom of the screen if not enough room is available. It will also no longer be marked for localization of the time format (what the separator should be and whether to use 24-hour time), as that information is not provided by the locale system. :( - * Added `uiUserBugCannotSetParentOnToplevel()`, which should be used by implementations of toplevel controls in their `SetParent()` implementations. This will also be the beginning of consolidating common user bug messages into a single place, though this will be one of the only few exported user bug functions. - * uiSpinbox and uiSlider now merely swap their min and max if min ≥ max. They will no longer panic and do nothing, respectively. - * Matrix scaling will no longer leave the matrix in an invalid state on OS X and GTK+. - * `uiMultilineEntrySetText()` and `uiMutlilineEntryAppend()` on GTK+ no longer fire `OnChanged()` events. diff --git a/NEWS.md b/NEWS.md new file mode 100644 index 00000000..d439ed40 --- /dev/null +++ b/NEWS.md @@ -0,0 +1,113 @@ +# Old News + +* **27 November 2016** + * Decided to split the table stuff into its own branch. It will be developed independently of everything else, along with a few other features. + +* **2 November 2016** + * Added two new functions to replace the deleted `uiWindowPosition()` and friends: `uiAreaBeginUserWindowMove()` and `uiAreaBeginUserWindowResize()`. When used in a `uiAreaHandler.Mouse()` event handler, these let you initiate a user-driven mouse move or mouse resize of the window at any point in a uiArea. + +* **31 October 2016** + * @krakjoe noticed that I accidentally used thread-unsafe code in uiQueueMain() on Unix. Fixed. + +* **24 October 2016** + * `uiWindowSetContentSize()` on Unix no longer needs to call up the GTK+ main loop. As a result, bugs related to strange behavior using that function (and the now-deleted `uiWindowSetPosition()` and `uiWindowCenter()`) should go away. I'll need to go through the bugs to verify as much, though. + +* **22 October 2016** + * Due to being unable to guarantee they will work (especially as we move toward capability-driven window systems like Wayland), or being unable to work without hacking that breaks other things, the following functions have been removed: `uiWindowPosition()`, `uiWindowSetPosition()`, `uiWindowCenter()`, and `uiWindowOnPositionChanged()`. Centering may come back at some point in the future, albeit in a possibly restricted form. A function to initiate a user move when a part of a uiArea is clicked will be provided soon. + +* **21 October 2016** + * `uiDrawTextWeightUltraBold` is now spelled correctly. Thanks to @krakjoe. + +* **18 June 2016** + * Help decide [the design of tables and trees in libui](https://github.com/andlabs/libui/issues/159); the implementation starts within the next few days, if not tomorrow! + +* **17 June 2016** + * **CMake 3.1.0 is now required.** This is due to CMake's rapid development pace in the past few years adding things libui needs to build on as many systems as possible. If your OS is supported by libui but its repositories ship with an older version of CMake, you will need to find an updated one somewhere. + * Please help [plan out a better menu API](https://github.com/andlabs/libui/issues/152). + * `uiMainSteps()` no longer takes any arguments and no longer needs to invoke a function to do the work. You still need to call it, but once you do, it will return immediately and you can then get right to your main loop. + * **CMake 3.1.0 is now required.** This is due to CMake's rapid development pace in the past few years adding things libui needs to build on as many systems as possible. If your OS is supported by libui but its repositories ship with an older version of CMake, you will need to find an updated one somewhere. + * Added `uiNewVerticalSeparator()` to complement `uiNewHorizontalSeparator()`. + +* **16 June 2016** + * Added `uiWindowContentSize()`, `uiWindowSetContentSize()`, and `uiWindowOnContentSizeChanged()` methods for manipulating uiWindow content sizes. Note the use of "content size"; the size you work with does NOT include window decorations (titlebars, menus, etc.). + * Added `uiWindowFullscreen()` and `uiWindowSetFullscreen()` to allow making fullscreen uiWindows, taking advantage of OS facilities for fullscreen and without changing the screen resolution (!). + * Added `uiWindowBorderless()` and `uiWindowSetBorderless()` for allowing borderless uiWindows. + * Added `uiMainSteps()`. You call this instead of `uiMain()` if you want to run the main loop yourself. You pass in a function that will be called; within that function, you call `uiMainStep()` repeatedly until it returns 0, doing whatever you need to do in the meantime. (This was needed because just having `uiMainStep()` by itself only worked on some systems.) + * Added `uiProgressBarValue()` and allowed passing -1 to `uiProgressBarSetValue()` to make an indeterminate progress bar. Thanks to @emersion. + +* **15 June 2016** + * Added `uiFormDelete()`; thanks to @emersion. + * Added `uiWindowPosition()`, `uiWindowSetPosition()`, `uiWindowCenter()`, and `uiWindowOnPositionChanged()`, methods for manipulating uiWindow position. + +* **14 June 2016** + * uiDarwinControl now has a `ChildVisibilityChanged()` method and a corresponding `NotifyVisibilityChanged()` function that is called by the default show/hide handlers. This is used to make visibility changes work on OS X; uiBox, uiForm, and uiGrid all respect these now. + * The same has been done on the Windows side as well. + * Hiding and showing controls and padding calculations are now correct on Windows at long last. + * Hiding a control in a uiForm now hides its label on all platforms. + +* **13 June 2016** + * `intmax_t` and `uintmax_t` are no longer used for libui API functions; now we use `int`. This should make things much easier for bindings. `int` should be at least 32 bits wide; this should be sufficient for all but the most extreme cases. + +* **12 June 2016** + * Added `uiGrid`, a new container control that arranges controls in rows and columns, with stretchy ("expanding") rows, stretchy ("expanding") columns, cells that span rows and columns, and cells whose content is aligned in either direction rather than just filling. It's quite powerful, is it? =P + +* **8 June 2016** + * Added `uiForm`, a new container control that arranges controls vertically, with properly aligned labels on each. Have fun! + +* **6 June 2016** + * Added `uiRadioButtonsSelected()`, `uiRadioButtonsSetSelected()`, and `uiRadioButtonsOnSelected()` to control selection of a radio button and catch an event when such a thing happens. + +* **5 June 2016** + * **Alpha 3.1 is here.** This was a much-needed update to Alpha 3 that changes a few things: + * **The build system is now cmake.** cmake 2.8.11 or higher is needed. + * Static linking is now fully possible. + * MinGW linking is back, but static only. + * Added `uiNewPasswordEntry()`, which creates a new `uiEntry` suitable for entering passwords. + * Added `uiNewSearchEntry()`, which creates a new `uiEntry` suitable for searching. On some systems, the `OnChanged()` event will be slightly delayed and/or combined, to produce a more natural feel when searching. + +* **29 May 2016** + * **Alpha 3 is here!** Get it [here](https://github.com/andlabs/libui/releases/tag/alpha3). + * The next packaged release will introduce: + * uiGrid, another way to lay out controls, a la GtkGrid + * uiOpenGLArea, a way to render OpenGL content in a libui uiArea + * uiTable, a data grid control that may or may not have tree facilities (if it does, it will be called uiTree instead) + * a complete, possibly rewritten, drawing and text rendering infrastructure + * Thanks to @pcwalton, we can now statically link libui! Simply do `make STATIC=1` instead of just `make`. + * On Windows you must link both `libui.lib` and `libui.res` AND provide a Common Controls 6 manifest for output static binaries to work properly. + +* **28 May 2016** + * As promised, **the minimum system requirements are now OS X 10.8 and GTK+ 3.10 for OS X and Unix, respectively**. + +* **26 May 2016** + * Two OS X-specific functions have been added: `uiDarwinMarginAmount()` and `uiDarwinPaddingAmount()`. These return the amount of margins and padding, respectively, to give to a control, and are intended for container implementations. These are suitable for the constant of a NSLayoutConstraint. They both take a pointer parameter that is reserved for future use and should be `NULL`. + +* **25 May 2016** + * uiDrawTextLayout attributes are now specified in units of *graphemes* on all platforms. This means characters as seen from a user's perspective, not Unicode codepoints or UTF-8 bytes. So a long string of combining marker codepoints after one codepoint would still count as one grapheme. + +* **24 May 2016** + * You can now help choose [a potential new build system for libui](https://github.com/andlabs/libui/issues/62). + * Tomorrow I will decide if OS X 10.7 will also be dropped alongside GTK+ 3.4-3.8 this Saturday. Stay tuned. + * As promised, `uiCombobox` is now split into `uiCombobox` for non-editable comboboxes and `uiEditableCombobox` for editable comboboxes. Mind the function changes as well :) + * There is a new function `uiMainStep()`, which runs one iteration of the main loop. It takes a single boolean argument, indicating whether to wait for an event to occur or not. It returns true if an event was processed (or if no event is available if you don't want to wait) and false if the event loop was told to stop (for instance, `uiQuit()` was called). + +* **23 May 2016** + * Fixed surrogate pair drawing on OS X. + +* **22 May 2016** + * Two more open questions I'd like your feedback on are available [here](https://github.com/andlabs/libui/issues/48) and [here](https://github.com/andlabs/libui/issues/25). + * Sometime in the next 48 hours (before 23:59 EDT on 24 May 2016) I will split `uiCombobox` into two separate controls, `uiCombobox` and `uiEditableCombobox`, each with slightly different events and "selected item" mechanics. Prepare your existing code. + * Removed `uiControlVerifyDestroy()`; that is now part of `uiFreeControl()` itself. + * Added `uiPi`, a constant for π. This is provided for C and C++ programmers, where there is no standard named constant for π; bindings authors shouldn't need to worry about this. + * Fixed uiMultilineEntry not properly having line breaks on Windows. + * Added `uiNewNonWrappingMultilineEntry()`, which creates a uiMultilineEntry that scrolls horizontally instead of wrapping lines. (This is not documented as being changeable after the fact on Windows, hence it's a creation-time choice.) + * uiAreas on Windows and some internal Direct2D areas now respond to `WM_PRINTCLIENT` properly, which should hopefully increase the quality of screenshots. + * uiDateTimePicker on GTK+ works properly on RTL layouts and no longer disappears off the bottom of the screen if not enough room is available. It will also no longer be marked for localization of the time format (what the separator should be and whether to use 24-hour time), as that information is not provided by the locale system. :( + * Added `uiUserBugCannotSetParentOnToplevel()`, which should be used by implementations of toplevel controls in their `SetParent()` implementations. This will also be the beginning of consolidating common user bug messages into a single place, though this will be one of the only few exported user bug functions. + * uiSpinbox and uiSlider now merely swap their min and max if min ≥ max. They will no longer panic and do nothing, respectively. + * Matrix scaling will no longer leave the matrix in an invalid state on OS X and GTK+. + * `uiMultilineEntrySetText()` and `uiMutlilineEntryAppend()` on GTK+ no longer fire `OnChanged()` events. + +* **21 May 2016** + * I will now post announcements and updates here. + * Now that Ubuntu 16.04 LTS is here, no earlier than next Saturday, 28 May 2016 at noon EDT, **I will bump the minimum GTK+ version from 3.4 to 3.10**. This will add a lot of new features that I can now add to libui, such as search-oriented uiEntries, lists of arbitrary control layouts, and more. If you are still running a Linux distribution that doesn't come with 3.10, you will either need to upgrade or use jhbuild to set up a newer version of GTK+ in a private environment. + * You can decide if I should also drop OS X 10.7 [here](https://github.com/andlabs/libui/issues/46). diff --git a/README.md b/README.md index 8c19f147..ae6a9691 100644 --- a/README.md +++ b/README.md @@ -26,19 +26,26 @@ Furthermore, libui is not properly fully documented yet. This is mainly due to t But libui is not dead; I am working on it whenever I can, and I hope to get it to a point of real quality soon! -## Announcements +## News + +*Note that today's entry (Eastern Time) may be updated later today.* * **16 May 2018** * Thanks to @parro-it and @msink, libui now has better CI, including AppVeyor for Windows CI, and automated creation of binary releases when I make a tagged release. * **13 May 2018** * Added new functions to work with uiDateTimePickers: `uiDateTimePickerTime()`, `uiDateTimePickerSetTime()`, and `uiDateTimePickerOnChanged()`. These operate on standard `` `struct tm`s. Thanks @cody271! + * Release builds on Windows with MSVC should be fixed now; thanks @l0calh05t, @slahn, @mischnic, and @zentner-kyle. + +* **12 May 2018** + * GTK+ and OS X now have a cleaner build process for static libraries which no longer has intermediate files and differing configurations. As a result, certain issues should no longer be present. New naming rules for internal symbols of libui have also started being drafted; runtime symbols and edge cases still need to be handled (and the rules applied to Windows) before this can become a regular thing. * **2 May 2018** * On Windows, you no longer need to carry around a `libui.res` file with static builds. You do need to link in the appropriate manifest file, such as the one in the `windows/` folder (I still need to figure out exactly what is needed apart from the Common Controls v6 dependency, or at least to create a complete-ish template), or at least include it alongside your executables. This also means you should no longer see random cmake errors when building the static libraries. * **18 April 2018** * Introduced a new `uiTimer()` function for running code on a timer on the main thread. (Thanks to @cody271.) + * Migrated all code in the `common/` directory to use `uipriv` prefixes for everything that isn't `static`. This is the first step toward fixing static library oddities within libui, allowing libui to truly be safely used as either a static library or a shared library. * **18 March 2018** * 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](https://github.com/andlabs/libui/blob/8944a3fc5528445b9027b1294b6c86bae03eeb89/ui_attrstr.h). There is also a new examples for it: `drawtext`, which shows the whole API at a glance. It doesn't yet support measuring or manipulating text, nor does it currently support functions that would be necessary for things like text editors; all of this will be added back later. @@ -50,91 +57,7 @@ But libui is not dead; I am working on it whenever I can, and I hope to get it t * **Alpha 3.5 is now here.** This is a quickie release primiarly intended to deploy the above fix to package ui itself. **It is a partial binary release; sorry!** More new things will come in the next release, which will also introduce semver (so it will be called v0.4.0 instead). * Alpha 3.5 also includes a new control gallery example. The screenshots below have not been updated yet. -* **27 November 2016** - * Decided to split the table stuff into its own branch. It will be developed independently of everything else, along with a few other features. - -* **2 November 2016** - * Added two new functions to replace the deleted `uiWindowPosition()` and friends: `uiAreaBeginUserWindowMove()` and `uiAreaBeginUserWindowResize()`. When used in a `uiAreaHandler.Mouse()` event handler, these let you initiate a user-driven mouse move or mouse resize of the window at any point in a uiArea. - -* **31 October 2016** - * @krakjoe noticed that I accidentally used thread-unsafe code in uiQueueMain() on Unix. Fixed. - -* **24 October 2016** - * `uiWindowSetContentSize()` on Unix no longer needs to call up the GTK+ main loop. As a result, bugs related to strange behavior using that function (and the now-deleted `uiWindowSetPosition()` and `uiWindowCenter()`) should go away. I'll need to go through the bugs to verify as much, though. - -* **22 October 2016** - * Due to being unable to guarantee they will work (especially as we move toward capability-driven window systems like Wayland), or being unable to work without hacking that breaks other things, the following functions have been removed: `uiWindowPosition()`, `uiWindowSetPosition()`, `uiWindowCenter()`, and `uiWindowOnPositionChanged()`. Centering may come back at some point in the future, albeit in a possibly restricted form. A function to initiate a user move when a part of a uiArea is clicked will be provided soon. - -* **21 October 2016** - * `uiDrawTextWeightUltraBold` is now spelled correctly. Thanks to @krakjoe. - -* **18 June 2016** - * Help decide [the design of tables and trees in libui](https://github.com/andlabs/libui/issues/159); the implementation starts within the next few days, if not tomorrow! - -* **17 June 2016** - * **CMake 3.1.0 is now required.** This is due to CMake's rapid development pace in the past few years adding things libui needs to build on as many systems as possible. If your OS is supported by libui but its repositories ship with an older version of CMake, you will need to find an updated one somewhere. - * Please help [plan out a better menu API](https://github.com/andlabs/libui/issues/152). - -* **5 June 2016** - * **Alpha 3.1 is here.** This was a much-needed update to Alpha 3 that changes a few things: - * **The build system is now cmake.** cmake 2.8.11 or higher is needed. - * Static linking is now fully possible. - * MinGW linking is back, but static only. - -*Old announcements can be found in the ANNOUNCE.md file.* - -## Updates - -*Note that today's entry (Eastern Time) may be updated later today.* - -* **13 May 2018** - * Release builds on Windows with MSVC should be fixed now; thanks @l0calh05t, @slahn, @mischnic, and @zentner-kyle. - -* **12 May 2018** - * GTK+ and OS X now have a cleaner build process for static libraries which no longer has intermediate files and differing configurations. As a result, certain issues should no longer be present. New naming rules for internal symbols of libui have also started being drafted; runtime symbols and edge cases still need to be handled (and the rules applied to Windows) before this can become a regular thing. - -* **18 April 2018** - * Migrated all code in the `common/` directory to use `uipriv` prefixes for everything that isn't `static`. This is the first step toward fixing static library oddities within libui, allowing libui to truly be safely used as either a static library or a shared library. - -* **17 June 2016** - * `uiMainSteps()` no longer takes any arguments and no longer needs to invoke a function to do the work. You still need to call it, but once you do, it will return immediately and you can then get right to your main loop. - * **CMake 3.1.0 is now required.** This is due to CMake's rapid development pace in the past few years adding things libui needs to build on as many systems as possible. If your OS is supported by libui but its repositories ship with an older version of CMake, you will need to find an updated one somewhere. - * Added `uiNewVerticalSeparator()` to complement `uiNewHorizontalSeparator()`. - -* **16 June 2016** - * Added `uiWindowContentSize()`, `uiWindowSetContentSize()`, and `uiWindowOnContentSizeChanged()` methods for manipulating uiWindow content sizes. Note the use of "content size"; the size you work with does NOT include window decorations (titlebars, menus, etc.). - * Added `uiWindowFullscreen()` and `uiWindowSetFullscreen()` to allow making fullscreen uiWindows, taking advantage of OS facilities for fullscreen and without changing the screen resolution (!). - * Added `uiWindowBorderless()` and `uiWindowSetBorderless()` for allowing borderless uiWindows. - * Added `uiMainSteps()`. You call this instead of `uiMain()` if you want to run the main loop yourself. You pass in a function that will be called; within that function, you call `uiMainStep()` repeatedly until it returns 0, doing whatever you need to do in the meantime. (This was needed because just having `uiMainStep()` by itself only worked on some systems.) - * Added `uiProgressBarValue()` and allowed passing -1 to `uiProgressBarSetValue()` to make an indeterminate progress bar. Thanks to @emersion. - -* **15 June 2016** - * Added `uiFormDelete()`; thanks to @emersion. - * Added `uiWindowPosition()`, `uiWindowSetPosition()`, `uiWindowCenter()`, and `uiWindowOnPositionChanged()`, methods for manipulating uiWindow position. - -* **14 June 2016** - * uiDarwinControl now has a `ChildVisibilityChanged()` method and a corresponding `NotifyVisibilityChanged()` function that is called by the default show/hide handlers. This is used to make visibility changes work on OS X; uiBox, uiForm, and uiGrid all respect these now. - * The same has been done on the Windows side as well. - * Hiding and showing controls and padding calculations are now correct on Windows at long last. - * Hiding a control in a uiForm now hides its label on all platforms. - -* **13 June 2016** - * `intmax_t` and `uintmax_t` are no longer used for libui API functions; now we use `int`. This should make things much easier for bindings. `int` should be at least 32 bits wide; this should be sufficient for all but the most extreme cases. - -* **12 June 2016** - * Added `uiGrid`, a new container control that arranges controls in rows and columns, with stretchy ("expanding") rows, stretchy ("expanding") columns, cells that span rows and columns, and cells whose content is aligned in either direction rather than just filling. It's quite powerful, is it? =P - -* **8 June 2016** - * Added `uiForm`, a new container control that arranges controls vertically, with properly aligned labels on each. Have fun! - -* **6 June 2016** - * Added `uiRadioButtonsSelected()`, `uiRadioButtonsSetSelected()`, and `uiRadioButtonsOnSelected()` to control selection of a radio button and catch an event when such a thing happens. - -* **5 June 2016** - * Added `uiNewPasswordEntry()`, which creates a new `uiEntry` suitable for entering passwords. - * Added `uiNewSearchEntry()`, which creates a new `uiEntry` suitable for searching. On some systems, the `OnChanged()` event will be slightly delayed and/or combined, to produce a more natural feel when searching. - -*Old updates can be found in the Changelog.md file.* +*Old announcements can be found in the NEWS.md file.* ## Runtime Requirements From 2f0e6e974b0935cb426f397bf271e365abf55ca5 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 30 May 2018 08:52:24 -0400 Subject: [PATCH 1117/1329] And updated the README. --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index ae6a9691..e9bbd4e5 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,9 @@ But libui is not dead; I am working on it whenever I can, and I hope to get it t *Note that today's entry (Eastern Time) may be updated later today.* +* **30 May 2018** + * Merged the previous Announcements and Updates section of this README into a single News section, and merged the respective archive files into a single NEWS.md file. + * **16 May 2018** * Thanks to @parro-it and @msink, libui now has better CI, including AppVeyor for Windows CI, and automated creation of binary releases when I make a tagged release. From 1cb0e9046f561ea42c67308284a63263d759f1f7 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 30 May 2018 22:48:46 -0400 Subject: [PATCH 1118/1329] Made the button cell renderer draw and size much more nicely than it did before, fixing a few bugs along the way. --- unix/cellrendererbutton.c | 151 +++++++++++++++++++++++--------------- 1 file changed, 92 insertions(+), 59 deletions(-) diff --git a/unix/cellrendererbutton.c b/unix/cellrendererbutton.c index c3ff4f82..608cd61d 100644 --- a/unix/cellrendererbutton.c +++ b/unix/cellrendererbutton.c @@ -3,10 +3,9 @@ // TODOs // - it's a rather tight fit -// - selected row text color is white -// - resizing a column with a button in it crashes the program +// - selected row text color is white (TODO not on 3.22) // - accessibility -// - right side too big? +// - right side too big? (TODO reverify) #define cellRendererButtonType (cellRendererButton_get_type()) #define cellRendererButton(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), cellRendererButtonType, cellRendererButton)) @@ -58,6 +57,7 @@ static GtkSizeRequestMode cellRendererButton_get_request_mode(GtkCellRenderer *r } // this is basically what GtkCellRendererToggle did in 3.10 and does in 3.20, as well as what the Foreign Drawing gtk3-demo demo does +// TODO how does this seem to work with highlight on 3.22, and does that work with 3.10 too static GtkStyleContext *setButtonStyle(GtkWidget *widget) { GtkStyleContext *base, *context; @@ -90,7 +90,56 @@ void unsetButtonStyle(GtkStyleContext *context) g_object_unref(context); } -// this is based on what GtkCellRendererText does +// this is based on what GtkCellRendererText in GTK+ 3.22.30 does +// TODO compare to 3.10.9 (https://gitlab.gnome.org/GNOME/gtk/blob/3.10.9/gtk/gtkcellrenderertext.c) +static PangoLayout *cellRendererButtonPangoLayout(cellRendererButton *c, GtkWidget *widget) +{ + PangoLayout *layout; + + layout = gtk_widget_create_pango_layout(widget, c->text); + pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_NONE); + pango_layout_set_width(layout, -1); + pango_layout_set_wrap(layout, PANGO_WRAP_CHAR); + pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER); + return layout; +} + +// this is based on what GtkCellRendererText in GTK+ 3.22.30 does +// TODO compare to 3.10.9 (https://gitlab.gnome.org/GNOME/gtk/blob/3.10.9/gtk/gtkcellrenderertext.c) +static void cellRendererButtonSize(cellRendererButton *c, GtkWidget *widget, PangoLayout *layout, const GdkRectangle *cell_area, gint *xoff, gint *yoff, gint *width, gint *height) +{ + PangoRectangle rect; + gint xpad, ypad; + gfloat xalign, yalign; + + gtk_cell_renderer_get_padding(GTK_CELL_RENDERER(c), &xpad, &ypad); + pango_layout_get_pixel_extents(layout, NULL, &rect); + if (rect.width > cell_area->width - (2 * xpad)) + rect.width = cell_area->width - (2 * xpad); + if (rect.height > cell_area->height - (2 * ypad)) + rect.height = cell_area->height - (2 * ypad); + + gtk_cell_renderer_get_alignment(GTK_CELL_RENDERER(c), &xalign, &yalign); + if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL) + xalign = 1.0 - xalign; + if (xoff != NULL) { + *xoff = cell_area->width - (rect.width + (2 * xpad)); + *xoff = (gint) ((gfloat) (*xoff) * xalign); + } + if (yoff != NULL) { + *yoff = cell_area->height - (rect.height + (2 * ypad)); + *yoff = (gint) ((gfloat) (*yoff) * yalign); + if (*yoff < 0) + *yoff = 0; + } + if (width != NULL) + *width = rect.width - (2 * xpad); + if (height != NULL) + *height = rect.height - (2 * ypad); +} + +// this is based on what GtkCellRendererText in GTK+ 3.22.30 does +// TODO compare to 3.10.9 (https://gitlab.gnome.org/GNOME/gtk/blob/3.10.9/gtk/gtkcellrenderertext.c) static void cellRendererButton_get_preferred_width(GtkCellRenderer *r, GtkWidget *widget, gint *minimum, gint *natural) { cellRendererButton *c = cellRendererButton(r); @@ -101,19 +150,21 @@ static void cellRendererButton_get_preferred_width(GtkCellRenderer *r, GtkWidget gtk_cell_renderer_get_padding(GTK_CELL_RENDERER(c), &xpad, NULL); - layout = gtk_widget_create_pango_layout(widget, c->text); - pango_layout_set_width(layout, -1); + layout = cellRendererButtonPangoLayout(c, widget); pango_layout_get_extents(layout, NULL, &rect); g_object_unref(layout); - out = 2 * xpad + PANGO_PIXELS_CEIL(rect.width); + out = PANGO_PIXELS_CEIL(rect.width) + (2 * xpad); + if (rect.x > 0) + out += rect.x; if (minimum != NULL) *minimum = out; if (natural != NULL) *natural = out; } -// this is based on what GtkCellRendererText does +// this is based on what GtkCellRendererText in GTK+ 3.22.30 does +// TODO compare to 3.10.9 (https://gitlab.gnome.org/GNOME/gtk/blob/3.10.9/gtk/gtkcellrenderertext.c) static void cellRendererButton_get_preferred_height_for_width(GtkCellRenderer *r, GtkWidget *widget, gint width, gint *minimum, gint *natural) { cellRendererButton *c = cellRendererButton(r); @@ -124,19 +175,20 @@ static void cellRendererButton_get_preferred_height_for_width(GtkCellRenderer *r gtk_cell_renderer_get_padding(GTK_CELL_RENDERER(c), &xpad, &ypad); - layout = gtk_widget_create_pango_layout(widget, c->text); - pango_layout_set_width(layout, ((2 * xpad + width) * PANGO_SCALE)); + layout = cellRendererButtonPangoLayout(c, widget); + pango_layout_set_width(layout, (width + (xpad * 2)) * PANGO_SCALE); pango_layout_get_pixel_size(layout, NULL, &height); g_object_unref(layout); - out = 2 * ypad + height; + out = height + (ypad * 2); if (minimum != NULL) *minimum = out; if (natural != NULL) *natural = out; } -// this is basically what GtkCellRendererText does +// this is based on what GtkCellRendererText in GTK+ 3.22.30 does +// TODO compare to 3.10.9 (https://gitlab.gnome.org/GNOME/gtk/blob/3.10.9/gtk/gtkcellrenderertext.c) static void cellRendererButton_get_preferred_height(GtkCellRenderer *r, GtkWidget *widget, gint *minimum, gint *natural) { gint width; @@ -145,84 +197,65 @@ static void cellRendererButton_get_preferred_height(GtkCellRenderer *r, GtkWidge gtk_cell_renderer_get_preferred_height_for_width(r, widget, width, minimum, natural); } -// this is based on what GtkCellRendererText does +// this is based on what GtkCellRendererText in GTK+ 3.22.30 does +// TODO compare to 3.10.9 (https://gitlab.gnome.org/GNOME/gtk/blob/3.10.9/gtk/gtkcellrenderertext.c) static void cellRendererButton_get_aligned_area(GtkCellRenderer *r, GtkWidget *widget, GtkCellRendererState flags, const GdkRectangle *cell_area, GdkRectangle *aligned_area) { cellRendererButton *c = cellRendererButton(r); - gint xpad, ypad; PangoLayout *layout; - PangoRectangle rect; - gfloat xalign, yalign; - gint xoffset, yoffset; + gint xoff, yoff; + gint width, height; - gtk_cell_renderer_get_padding(GTK_CELL_RENDERER(c), &xpad, &ypad); + layout = cellRendererButtonPangoLayout(c, widget); + cellRendererButtonSize(c, widget, layout, cell_area, + &xoff, &yoff, &width, &height); - layout = gtk_widget_create_pango_layout(widget, c->text); - pango_layout_set_width(layout, -1); - pango_layout_get_pixel_extents(layout, NULL, &rect); - - xoffset = 0; - yoffset = 0; - if (cell_area != NULL) { - gtk_cell_renderer_get_alignment(GTK_CELL_RENDERER(c), &xalign, &yalign); - xoffset = cell_area->width - (2 * xpad + rect.width); - // use explicit casts just to be safe - if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL) - xoffset = ((gdouble) xoffset) * (1.0 - xalign); - else - xoffset *= ((gdouble) xoffset) * xalign; - yoffset = yalign * (cell_area->height - (2 * ypad + rect.height)); - yoffset = MAX(yoffset, 0); - } - - aligned_area->x = cell_area->x + xoffset; - aligned_area->y = cell_area->y + yoffset; - aligned_area->width = 2 * xpad + rect.width; - aligned_area->height = 2 * ypad + rect.height; + aligned_area->x = cell_area->x + xoff; + aligned_area->y = cell_area->y + yoff; + aligned_area->width = width; + aligned_area->height = height; g_object_unref(layout); } -// this is based on both what GtkCellRendererText does and what GtkCellRendererToggle does +// this is based on both what GtkCellRendererText on 3.22.30 does and what GtkCellRendererToggle does (TODO verify the latter; both on 3.10.9) static void cellRendererButton_render(GtkCellRenderer *r, cairo_t *cr, GtkWidget *widget, const GdkRectangle *background_area, const GdkRectangle *cell_area, GtkCellRendererState flags) { cellRendererButton *c = cellRendererButton(r); gint xpad, ypad; GdkRectangle alignedArea; - gint xoffset, yoffset; + gint xoff, yoff; GtkStyleContext *context; PangoLayout *layout; PangoRectangle rect; gtk_cell_renderer_get_padding(GTK_CELL_RENDERER(c), &xpad, &ypad); - gtk_cell_renderer_get_aligned_area(GTK_CELL_RENDERER(c), widget, flags, cell_area, &alignedArea); - xoffset = alignedArea.x - cell_area->x; - yoffset = alignedArea.y - cell_area->y; + layout = cellRendererButtonPangoLayout(c, widget); + cellRendererButtonSize(c, widget, layout, cell_area, + &xoff, &yoff, NULL, NULL); context = setButtonStyle(widget); - layout = gtk_widget_create_pango_layout(widget, c->text); gtk_render_background(context, cr, - background_area->x + xoffset + xpad, - background_area->y + yoffset + ypad, - background_area->width - 2 * xpad, - background_area->height - 2 * ypad); + background_area->x + xpad, + background_area->y + ypad, + background_area->width - (xpad * 2), + background_area->height - (ypad * 2)); gtk_render_frame(context, cr, - background_area->x + xoffset + xpad, - background_area->y + yoffset + ypad, - background_area->width - 2 * xpad, - background_area->height - 2 * ypad); + background_area->x + xpad, + background_area->y + ypad, + background_area->width - (xpad * 2), + background_area->height - (ypad * 2)); - pango_layout_set_width(layout, -1); pango_layout_get_pixel_extents(layout, NULL, &rect); - xoffset -= rect.x; + xoff -= rect.x; gtk_render_layout(context, cr, - cell_area->x + xoffset + xpad, - cell_area->y + yoffset + ypad, + cell_area->x + xoff + xpad, + cell_area->y + yoff + ypad, layout); - g_object_unref(layout); unsetButtonStyle(context); + g_object_unref(layout); } static guint clickedSignal; From 1b4c898a0c84368bfb6709b38dc218f254b0ca0c Mon Sep 17 00:00:00 2001 From: Niklas Mischkulnig Date: Thu, 31 May 2018 13:01:29 +0200 Subject: [PATCH 1119/1329] Fix datetimepicker crash --- darwin/datetimepicker.m | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/darwin/datetimepicker.m b/darwin/datetimepicker.m index ed164855..b786dea9 100644 --- a/darwin/datetimepicker.m +++ b/darwin/datetimepicker.m @@ -16,8 +16,8 @@ struct uiDateTimePicker { } - (void)datePickerCell:(NSDatePickerCell *)aDatePickerCell validateProposedDateValue:(NSDate **)proposedDateValue timeInterval:(NSTimeInterval *)proposedTimeInterval; - (void)doTimer:(NSTimer *)timer; -- (void)registerPicker:(uiDateTimePicker *)b; -- (void)unregisterPicker:(uiDateTimePicker *)b; +- (void)registerPicker:(uiDateTimePicker *)d; +- (void)unregisterPicker:(uiDateTimePicker *)d; @end @implementation uiprivDatePickerDelegateClass @@ -78,7 +78,16 @@ struct uiDateTimePicker { static uiprivDatePickerDelegateClass *datePickerDelegate = nil; -uiDarwinControlAllDefaults(uiDateTimePicker, dp) +uiDarwinControlAllDefaultsExceptDestroy(uiDateTimePicker, dp) + +static void uiDateTimePickerDestroy(uiControl *c) +{ + uiDateTimePicker *d = uiDateTimePicker(c); + + [datePickerDelegate unregisterPicker:d]; + [d->dp release]; + uiFreeControl(uiControl(d)); +} static void defaultOnChanged(uiDateTimePicker *d, void *data) { From 773856fa90cb8bf111c8f308584c5e37b197bdb3 Mon Sep 17 00:00:00 2001 From: Niklas Mischkulnig Date: Thu, 31 May 2018 17:49:44 +0200 Subject: [PATCH 1120/1329] Fix uiAttributedStringDelete --- common/attrstr.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/common/attrstr.c b/common/attrstr.c index f2cdb66b..ca00c19d 100644 --- a/common/attrstr.c +++ b/common/attrstr.c @@ -263,20 +263,20 @@ void uiAttributedStringDelete(uiAttributedString *s, size_t start, size_t end) // update the conversion tables // note the use of <= to include the null terminator - for (i = 0; i <= count; i++) + for (i = 0; i <= (s->len - end); i++) s->u8tou16[start + i] -= count16; - for (i = 0; i <= count16; i++) + for (i = 0; i <= (s->u16len - end16); i++) s->u16tou8[start16 + i] -= count; // null-terminate the string - s->s[start + count] = 0; - s->u16[start16 + count16] = 0; + s->s[start + (s->len - end)] = 0; + s->u16[start16 + (s->u16len - end16)] = 0; // fix up attributes uiprivAttrListRemoveCharacters(s->attrs, start, end); // and finally resize - resize(s, start + count, start16 + count16); + resize(s, s->len - count, s->u16len - count16); } void uiAttributedStringSetAttribute(uiAttributedString *s, uiAttribute *a, size_t start, size_t end) From 3e5f4b3674a13d9490c54aa7509ec6a1678cc0fa Mon Sep 17 00:00:00 2001 From: Niklas Mischkulnig Date: Fri, 1 Jun 2018 15:45:55 +0200 Subject: [PATCH 1121/1329] Fix area setSize macOS --- darwin/area.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/darwin/area.m b/darwin/area.m index d8786f4f..32ecb045 100644 --- a/darwin/area.m +++ b/darwin/area.m @@ -309,11 +309,11 @@ mouseEvent(otherMouseUp) [self setNeedsDisplay:YES]; } -// TODO does this update the frame? - (void)setScrollingSize:(NSSize)s { self->libui_ss = s; [self invalidateIntrinsicContentSize]; + [self setFrameSize:s]; } - (NSSize)intrinsicContentSize From 2f83428ebee20b92ccf8fa1a21c9e4d3d3a42b0c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 3 Jun 2018 12:26:23 -0400 Subject: [PATCH 1122/1329] Trying out a new uiTable API. This will make implementations easier, and figures that very few people need dynamic control over table column layout or contents. --- OLD_uitable.h | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++ uitable.h | 52 ++++++++++++++++++++++++++++++----------- 2 files changed, 104 insertions(+), 13 deletions(-) create mode 100644 OLD_uitable.h diff --git a/OLD_uitable.h b/OLD_uitable.h new file mode 100644 index 00000000..a54398c5 --- /dev/null +++ b/OLD_uitable.h @@ -0,0 +1,65 @@ +// 20 june 2016 +// kept in a separate file for now + +typedef struct uiImage uiImage; + +// TODO use const void * for const correctness +_UI_EXTERN uiImage *uiNewImage(double width, double height); +_UI_EXTERN void uiFreeImage(uiImage *i); +_UI_EXTERN void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int pixelStride); + +typedef struct uiTableModel uiTableModel; +typedef struct uiTableModelHandler uiTableModelHandler; + +// TODO actually validate these +_UI_ENUM(uiTableModelColumnType) { + uiTableModelColumnString, + uiTableModelColumnImage, + uiTableModelColumnInt, + uiTableModelColumnColor, +}; + +// TODO validate ranges; validate types on each getter/setter call (? table columns only?) +struct uiTableModelHandler { + int (*NumColumns)(uiTableModelHandler *, uiTableModel *); + uiTableModelColumnType (*ColumnType)(uiTableModelHandler *, uiTableModel *, int); + int (*NumRows)(uiTableModelHandler *, uiTableModel *); + void *(*CellValue)(uiTableModelHandler *, uiTableModel *, int, int); + void (*SetCellValue)(uiTableModelHandler *, uiTableModel *, int, int, const void *); +}; + +_UI_EXTERN void *uiTableModelStrdup(const char *str); +// TODO rename the strdup one to this too +_UI_EXTERN void *uiTableModelGiveColor(double r, double g, double b, double a); +_UI_EXTERN void *uiTableModelGiveInt(int i); +// TODO TakeString +// TODO add const +_UI_EXTERN int uiTableModelTakeInt(void *v); + +_UI_EXTERN uiTableModel *uiNewTableModel(uiTableModelHandler *mh); +_UI_EXTERN void uiFreeTableModel(uiTableModel *m); +_UI_EXTERN void uiTableModelRowInserted(uiTableModel *m, int newIndex); +_UI_EXTERN void uiTableModelRowChanged(uiTableModel *m, int index); +_UI_EXTERN void uiTableModelRowDeleted(uiTableModel *m, int oldIndex); +// TODO reordering/moving + +typedef struct uiTableColumn uiTableColumn; + +_UI_EXTERN void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand); +// TODO images shouldn't expand... +_UI_EXTERN void uiTableColumnAppendImagePart(uiTableColumn *c, int modelColumn, int expand); +_UI_EXTERN void uiTableColumnAppendButtonPart(uiTableColumn *c, int modelColumn, int expand); +// TODO should these have labels? +_UI_EXTERN void uiTableColumnAppendCheckboxPart(uiTableColumn *c, int modelColumn, int expand); +_UI_EXTERN void uiTableColumnAppendProgressBarPart(uiTableColumn *c, int modelColumn, int expand); +// TODO Editable? +_UI_EXTERN void uiTableColumnPartSetEditable(uiTableColumn *c, int part, int editable); +_UI_EXTERN void uiTableColumnPartSetTextColor(uiTableColumn *c, int part, int modelColumn); + +typedef struct uiTable uiTable; +#define uiTable(this) ((uiTable *) (this)) +_UI_EXTERN uiTableColumn *uiTableAppendColumn(uiTable *t, const char *name); +_UI_EXTERN uiTableColumn *uiTableAppendTextColumn(uiTable *t, const char *name, int modelColumn); +// TODO getter? +_UI_EXTERN void uiTableSetRowBackgroundColorModelColumn(uiTable *t, int modelColumn); +_UI_EXTERN uiTable *uiNewTable(uiTableModel *model); diff --git a/uitable.h b/uitable.h index a54398c5..0f23c448 100644 --- a/uitable.h +++ b/uitable.h @@ -43,23 +43,49 @@ _UI_EXTERN void uiTableModelRowChanged(uiTableModel *m, int index); _UI_EXTERN void uiTableModelRowDeleted(uiTableModel *m, int oldIndex); // TODO reordering/moving -typedef struct uiTableColumn uiTableColumn; +#define uiTableModelColumnNeverEditable (-1) +#define uiTableModelColumnAlwaysEditable (-2) -_UI_EXTERN void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand); -// TODO images shouldn't expand... -_UI_EXTERN void uiTableColumnAppendImagePart(uiTableColumn *c, int modelColumn, int expand); -_UI_EXTERN void uiTableColumnAppendButtonPart(uiTableColumn *c, int modelColumn, int expand); -// TODO should these have labels? -_UI_EXTERN void uiTableColumnAppendCheckboxPart(uiTableColumn *c, int modelColumn, int expand); -_UI_EXTERN void uiTableColumnAppendProgressBarPart(uiTableColumn *c, int modelColumn, int expand); -// TODO Editable? -_UI_EXTERN void uiTableColumnPartSetEditable(uiTableColumn *c, int part, int editable); -_UI_EXTERN void uiTableColumnPartSetTextColor(uiTableColumn *c, int part, int modelColumn); +typedef struct uiTableTextColumnOptionalParams uiTableTextColumnOptionalParams; + +struct uiTableTextColumnOptionalParams { + int ColorModelColumn; +}; typedef struct uiTable uiTable; #define uiTable(this) ((uiTable *) (this)) -_UI_EXTERN uiTableColumn *uiTableAppendColumn(uiTable *t, const char *name); -_UI_EXTERN uiTableColumn *uiTableAppendTextColumn(uiTable *t, const char *name, int modelColumn); +_UI_EXTERN void uiTableAppendTextColumn(uiTable *t, + const char *name, + int textModelColumn, + int textEditableModelColumn, + uiTableTextColumnOptionalParams *params); +_UI_EXTERN void uiTableAppendImageColumn(uiTable *t, + const char *name, + int imageModelColumn); +_UI_EXTERN void uiTableAppendImageTextColumn(uiTable *t, + const char *name, + int imageModelColumn, + int textModelColumn, + int textEditableModelColumn, + uiTableTextColumnOptionalParams *textParams); +_UI_EXTERN void uiTableAppendCheckboxColumn(uiTable *t, + const char *name, + int checkboxModelColumn, + int checkboxEditableModelColumn); +_UI_EXTERN void uiTableAppendCheckboxTextColumn(uiTable *t, + const char *name, + int checkboxModelColumn, + int checkboxEditableModelColumn, + int textModelColumn, + int textEditableModelColumn, + uiTableTextColumnOptionalParams *textParams); +_UI_EXTERN void uiTableAppendProgressBarColumn(uiTable *t, + const char *name, + int progressModelColumn); +_UI_EXTERN void uiTableAppendButtonColumn(uiTable *t, + const char *name, + int buttonTextModelColumn, + int buttonClickableModelColumn); // TODO getter? _UI_EXTERN void uiTableSetRowBackgroundColorModelColumn(uiTable *t, int modelColumn); _UI_EXTERN uiTable *uiNewTable(uiTableModel *model); From 5d576667641921ee78f7ca63982e8b7960f0b17a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 3 Jun 2018 13:28:11 -0400 Subject: [PATCH 1123/1329] Started writing the new table column code on OS X. This new code will take full advantage of NSTableView features like the reuse cache. Right now we just have the boilerplate for text-only columns. --- darwin/{table.m => OLD_table.m} | 0 darwin/tablecolumn.m | 198 ++++++++++++++++++++++++++++++++ 2 files changed, 198 insertions(+) rename darwin/{table.m => OLD_table.m} (100%) create mode 100644 darwin/tablecolumn.m diff --git a/darwin/table.m b/darwin/OLD_table.m similarity index 100% rename from darwin/table.m rename to darwin/OLD_table.m diff --git a/darwin/tablecolumn.m b/darwin/tablecolumn.m new file mode 100644 index 00000000..da80455a --- /dev/null +++ b/darwin/tablecolumn.m @@ -0,0 +1,198 @@ +// 3 june 2018 +#import "uipriv_darwin.h" + +// values from interface builder +#define textColumnLeading 2 +#define textColumnTrailing 2 + +static void layoutCellSubview(NSView *superview, NSView *subview, NSView *leading, CGFloat leadingConstant, NSView *trailing, CGFloat trailingConstant, BOOL stretchy) +{ + [subview setTranslatesAutoresizingMaskIntoConstraints:NO]; + if (stretchy) + [subview setContentHuggingPriority:NSLayoutPriorityDefaultLow forOrientation:NSLayoutConstraintOrientationHorizontal]; + else + [subview setContentHuggingPriority:NSLayoutPriorityRequired forOrientation:NSLayoutConstraintOrientationHorizontal]; + if (leading != nil) + [superview addConstraint:uiprivMkConstraint(leading, NSLayoutAttributeLeading, + NSLayoutRelationEqual, + subview, NSLayoutAttributeLeading, + 1, -leadingConstant, + @"uiTable cell subview leading constraint")]; + [superview addConstraint:uiprivMkConstraint(superview, NSLayoutAttributeTop, + NSLayoutRelationEqual, + subview, NSLayoutAttributeTop, + 1, 0, + @"uiTable cell subview top constraint")]; + if (trailing != nil) + [superview addConstraint:uiprivMkConstraint(trailing, NSLayoutAttributeTrailing, + NSLayoutRelationEqual, + subview, NSLayoutAttributeLeading, + 1, trailingConstant, + @"uiTable cell subview trailing constraint")]; + [superview addConstraint:uiprivMkConstraint(superview, NSLayoutAttributeBottom, + NSLayoutRelationEqual, + subview, NSLayoutAttributeBottom, + 1, 0, + @"uiTable cell subview bottom constraint")]; +} + +@interface uiprivColumnCellView : NSTableCellView +- (void)uiprivUpdate:(NSInteger)row; +@end + +@implementation uiprivColumnCellView + +- (void)uiprivUpdate:(NSInteger)row +{ + [self doesNotRecognizeSelector:_cmd]; +} + +@end + +static uiTableTextColumnOptionalParams defaultTextColumnOptionalParams = { + .ColorModelColumn = -1, +}; + +static void updateCellTextField(NSTextField *tf, NSInteger row, uiTableModel *m, int modelColumn, int editableColumn, uiTableTextColumnOptionalParams *params) +{ + void *data; + NSString *str; + BOOL editable; + + data = (*(m->mh->CellValue))(m->mh, m, row, modelColumn); + str = uiprivToNSString((char *) data); + uiprivFree(data); + [tf setStringValue:str]; + + switch (editableColumn) { + case uiTableModelColumnNeverEditable: + editable = NO; + break; + case uiTableModelColumnAlwaysEditable: + editable = YES; + break; + default: + data = (*(m->mh->CellValue))(m->mh, m, row, editableColumn); + editable = uiTableModelTakeInt(data) != 0; + // TODO free data + } + [tf setEditable:editable]; + + color = nil; + if (params->ColorModelColumn != -1) + color = (NSColor *) ((*(m->mh->CellValue))(m->mh, m, row, params->ColorModelColumn)); + if (color == nil) + color = [NSColor controlTextColor]; + [tf setColor:color]; + // TODO release color +} + +@interface uiprivTextColumnCellView : uiprivColumnCellView { + uiTable *t; + uiTableModel *m; + NSTextField *tf; + int modelColumn; + int editableColumn; + uiTableTextColumnOptionalParams params; +} +- (id)initWithFrame:(NSRect)r table:(uiTable *)table model:(uiTableModel *)model modelColumn:(int)mc editableColumn:(int)ec params:(uiTableTextColumnOptionalParams *)p; +- (IBAction)uiprivOnAction:(id)sender; +@end + +@implementation uiprivTextColumnCellView + +- (id)initWithFrame:(NSRect)r ModelColumn:(int)mc editableColumn:(int)ec params:(uiTableTextColumnOptionalParams *)p +{ + self = [super initWithFrame:frame]; + if (self) { + self->t = table; + self->m = model; + self->modelColumn = mc; + self->editableColumn = ec; + if (p != NULL) + params = *p; + else + params = defaultTextColumnOptionalParams; + + self->tf = uiprivNewLabel(@""); + // TODO set wrap and ellipsize modes? + [self->tf setTarget:self]; + [self->tf setAction:@selector(uiprivOnAction:)]; + [self addSubview:self->tf]; + layoutCellSubview(self, self->tv, + self, textColumnLeading, + self, textColumnTrailing, + YES); + + // take advantage of NSTableCellView-provided accessibility features + [self setTextField:self->tf]; + } + return self; +} + +- (void)dealloc +{ + [self->tf release]; + self->tf = nil; + [super dealloc]; +} + +- (void)uiprivUpdate:(NSInteger)row +{ + updateCellTextField(self->tf, row, self->m, + self->modelColumn, self->editableColumn, &(self->params)); +} + +- (IBAction)onAction:(id)sender +{ + NSInteger row; + const void *data; + + row = [self->t->tv rowForView:self->tf]; + data = [[self->tf stringValue] UTF8String]; + (*(self->m->mh->SetCellValue))(self->m->mh, self->m, + row, self->modelColumn, data); + // always refresh the value in case the model rejected it + [self uiprivUpdate:row]; +} + +@end + +void uiTableAppendTextColumn(uiTable *t, + const char *name, + int textModelColumn, + int textEditableModelColumn, + uiTableTextColumnOptionalParams *params); + +void uiTableAppendImageColumn(uiTable *t, + const char *name, + int imageModelColumn); + +void uiTableAppendImageTextColumn(uiTable *t, + const char *name, + int imageModelColumn, + int textModelColumn, + int textEditableModelColumn, + uiTableTextColumnOptionalParams *textParams); + +void uiTableAppendCheckboxColumn(uiTable *t, + const char *name, + int checkboxModelColumn, + int checkboxEditableModelColumn); + +void uiTableAppendCheckboxTextColumn(uiTable *t, + const char *name, + int checkboxModelColumn, + int checkboxEditableModelColumn, + int textModelColumn, + int textEditableModelColumn, + uiTableTextColumnOptionalParams *textParams); + +void uiTableAppendProgressBarColumn(uiTable *t, + const char *name, + int progressModelColumn); + +void uiTableAppendButtonColumn(uiTable *t, + const char *name, + int buttonTextModelColumn, + int buttonClickableModelColumn); From d1b7d14a8277c71d9719fd072a495ac8690913eb Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 3 Jun 2018 14:13:32 -0400 Subject: [PATCH 1124/1329] Added image columns. --- darwin/tablecolumn.m | 120 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 1 deletion(-) diff --git a/darwin/tablecolumn.m b/darwin/tablecolumn.m index da80455a..834db985 100644 --- a/darwin/tablecolumn.m +++ b/darwin/tablecolumn.m @@ -4,6 +4,8 @@ // values from interface builder #define textColumnLeading 2 #define textColumnTrailing 2 +#define imageColumnLeading 3 +#define imageTextColumnLeading 7 static void layoutCellSubview(NSView *superview, NSView *subview, NSView *leading, CGFloat leadingConstant, NSView *trailing, CGFloat trailingConstant, BOOL stretchy) { @@ -119,7 +121,7 @@ static void updateCellTextField(NSTextField *tf, NSInteger row, uiTableModel *m, [self->tf setTarget:self]; [self->tf setAction:@selector(uiprivOnAction:)]; [self addSubview:self->tf]; - layoutCellSubview(self, self->tv, + layoutCellSubview(self, self->tf, self, textColumnLeading, self, textColumnTrailing, YES); @@ -158,6 +160,122 @@ static void updateCellTextField(NSTextField *tf, NSInteger row, uiTableModel *m, @end +xx TODO somehow merge this with the above +@interface uiprivImageTextColumnCellView : uiprivColumnCellView { + uiTable *t; + uiTableModel *m; + NSImageView *iv; + int modelColumn; + NSTextField *tf; + int textModelColumn; + int textEditableColumn; + uiTableTextColumnOptionalParams params; +} +- (id)initWithFrame:(NSRect)r table:(uiTable *)table model:(uiTableModel *)model modelColumn:(int)mc textModelColumn:(int)tmc editableColumn:(int)ec params:(uiTableTextColumnOptionalParams *)p; +- (IBAction)uiprivOnAction:(id)sender; +@end + +@implementation uiprivImageTextColumnCellView + +- (id)initWithFrame:(NSRect)r table:(uiTable *)table model:(uiTableModel *)model modelColumn:(int)mc textModelColumn:(int)tmc editableColumn:(int)ec params:(uiTableTextColumnOptionalParams *)p +{ + self = [super initWithFrame:frame]; + if (self) { + self->t = table; + self->m = model; + self->modelColumn = mc; + self->textModelColumn = tmc; + self->editableColumn = ec; + if (p != NULL) + params = *p; + else + params = defaultTextColumnOptionalParams; + + self->iv = [[NSImageView alloc] initWithFrame:NSZeroRect]; + [self->iv setImageFrameStyle:NSImageFrameNone]; + [self->iv setImageAlignment:NSImageAlignCenter]; + [self->iv setImageScaling:NSImageScaleProportionallyDown]; + [self->iv setAnimates:NO]; + [self->iv setEditable:NO]; + [self->iv addConstraint:uiprivMkConstraint(self->iv, NSLayoutAttributeWidth, + NSLayoutRelationEqual, + self->iv, NSLayoutAttributeHeight, + 1, 0, + @"uiTable image squareness constraint")]; + [self addSubview:self->iv]; + + self->tf = nil; + if (self->textModelColumn != -1) { + self->tf = uiprivNewLabel(@""); + // TODO set wrap and ellipsize modes? + [self->tf setTarget:self]; + [self->tf setAction:@selector(uiprivOnAction:)]; + [self addSubview:self->tf]; + layoutCellSubview(self, self->iv, + self, imageColumnLeading, + nil, 0, + NO); + layoutCellSubview(self, self->tf, + self, imageTextColumnLeading, + self, textColumnTrailing, + YES); + } else { + layoutCellSubview(self, self->iv, + nil, 0, + nil, 0, + NO); + [self addConstraint:uiprivMkConstraint(self, NSLayoutAttributeCenterX, + NSLayoutRelationEqual, + self->iv, NSLayoutAttributeCenterX, + 1, 0, + @"uiTable image centering constraint")]; + } + + // take advantage of NSTableCellView-provided accessibility features + [self setImageView:self->iv]; + if (self->tf != nil) + [self setTextField:self->tf]; + } + return self; +} + +- (void)dealloc +{ + if (self->tf != nil) { + [self->tf release]; + self->tf = nil; + } + [self->iv release]; + self->iv = nil; + [super dealloc]; +} + +- (void)uiprivUpdate:(NSInteger)row +{ + void *data; + + data = (*(self->m->mh->CellValue))(self->m->mh, self->m, row, self->modelColumn); + [self->iv setImage:uiprivImageNSImage((uiImage *) data)]; + if (self->tf != nil) + updateCellTextField(self->tf, row, self->m, + self->textModelColumn, self->editableColumn, &(self->params)); +} + +- (IBAction)onAction:(id)sender +{ + NSInteger row; + const void *data; + + row = [self->t->tv rowForView:self->tf]; + data = [[self->tf stringValue] UTF8String]; + (*(self->m->mh->SetCellValue))(self->m->mh, self->m, + row, self->textModelColumn, data); + // always refresh the value in case the model rejected it + [self uiprivUpdate:row]; +} + +@end + void uiTableAppendTextColumn(uiTable *t, const char *name, int textModelColumn, From 9c519f1bfa26455f1ba9c43b5d754a970c9805f9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 3 Jun 2018 16:50:00 -0400 Subject: [PATCH 1125/1329] Added checkboxes and consolidated all the columns with or without text. Progressbars and buttons will be separate object types. I do need to redo the data-passing method, and now that there's uiAttribute I can just model a better one off that. --- darwin/tablecolumn.m | 324 +++++++++++++++++++++++-------------------- 1 file changed, 171 insertions(+), 153 deletions(-) diff --git a/darwin/tablecolumn.m b/darwin/tablecolumn.m index 834db985..6c4f666d 100644 --- a/darwin/tablecolumn.m +++ b/darwin/tablecolumn.m @@ -6,6 +6,9 @@ #define textColumnTrailing 2 #define imageColumnLeading 3 #define imageTextColumnLeading 7 +#define checkboxTextColumnLeading 0 +// these aren't provided by IB; let's just choose one +#define checkboxColumnLeading imageColumnLeading static void layoutCellSubview(NSView *superview, NSView *subview, NSView *leading, CGFloat leadingConstant, NSView *trailing, CGFloat trailingConstant, BOOL stretchy) { @@ -51,217 +54,217 @@ static void layoutCellSubview(NSView *superview, NSView *subview, NSView *leadin @end +static BOOL isCellEditable(uiTableModel *m, NSInteger row, int modelColumn) +{ + void *data; + + switch (modelColumn) { + case uiTableModelColumnNeverEditable: + return NO; + case uiTableModelColumnAlwaysEditable: + return YES; + } + data = (*(m->mh->CellValue))(m->mh, m, row, modelColumn); + return uiTableModelTakeInt(data) != 0; + // TODO free data +} + static uiTableTextColumnOptionalParams defaultTextColumnOptionalParams = { .ColorModelColumn = -1, }; -static void updateCellTextField(NSTextField *tf, NSInteger row, uiTableModel *m, int modelColumn, int editableColumn, uiTableTextColumnOptionalParams *params) -{ - void *data; - NSString *str; - BOOL editable; - - data = (*(m->mh->CellValue))(m->mh, m, row, modelColumn); - str = uiprivToNSString((char *) data); - uiprivFree(data); - [tf setStringValue:str]; - - switch (editableColumn) { - case uiTableModelColumnNeverEditable: - editable = NO; - break; - case uiTableModelColumnAlwaysEditable: - editable = YES; - break; - default: - data = (*(m->mh->CellValue))(m->mh, m, row, editableColumn); - editable = uiTableModelTakeInt(data) != 0; - // TODO free data - } - [tf setEditable:editable]; - - color = nil; - if (params->ColorModelColumn != -1) - color = (NSColor *) ((*(m->mh->CellValue))(m->mh, m, row, params->ColorModelColumn)); - if (color == nil) - color = [NSColor controlTextColor]; - [tf setColor:color]; - // TODO release color -} - -@interface uiprivTextColumnCellView : uiprivColumnCellView { +struct textColumnCreateParams { uiTable *t; uiTableModel *m; + + BOOL makeTextField; + int textModelColumn; + int textEditableColumn; + uiTableTextColumnOptionalParams textParams; + + BOOL makeImage; + int imageModelColumn; + + BOOL makeCheckbox; + int checkboxModelColumn; + int checkboxEditableColumn; +}; + +@interface uiprivTextImageCheckboxColumnCellView : uiprivColumnCellView { + uiTable *t; + uiTableModel *m; + NSTextField *tf; - int modelColumn; - int editableColumn; - uiTableTextColumnOptionalParams params; + int textModelColumn; + int textEditableColumn; + uiTableTextColumnOptionalParams textParams; + + NSImageView *iv; + int imageModelColumn; + + NSButton *cb; + int checkboxModelColumn; + int checkboxEditableColumn; } -- (id)initWithFrame:(NSRect)r table:(uiTable *)table model:(uiTableModel *)model modelColumn:(int)mc editableColumn:(int)ec params:(uiTableTextColumnOptionalParams *)p; -- (IBAction)uiprivOnAction:(id)sender; +- (id)initWithFrame:(NSRect)r params:(struct textColumnCreateParams *)p; +- (IBAction)uiprivOnTextFieldAction:(id)sender; +- (IBAction)uiprivOnCheckboxAction:(id)sender; @end @implementation uiprivTextColumnCellView -- (id)initWithFrame:(NSRect)r ModelColumn:(int)mc editableColumn:(int)ec params:(uiTableTextColumnOptionalParams *)p +- (id)initWithFrame:(NSRect)r params:(struct textColumnCreateParams *)p { self = [super initWithFrame:frame]; if (self) { - self->t = table; - self->m = model; - self->modelColumn = mc; - self->editableColumn = ec; - if (p != NULL) - params = *p; - else - params = defaultTextColumnOptionalParams; + NSView *left; + CGFloat leftConstant; + CGFloat leftTextConstant; - self->tf = uiprivNewLabel(@""); - // TODO set wrap and ellipsize modes? - [self->tf setTarget:self]; - [self->tf setAction:@selector(uiprivOnAction:)]; - [self addSubview:self->tf]; - layoutCellSubview(self, self->tf, - self, textColumnLeading, - self, textColumnTrailing, - YES); - - // take advantage of NSTableCellView-provided accessibility features - [self setTextField:self->tf]; - } - return self; -} - -- (void)dealloc -{ - [self->tf release]; - self->tf = nil; - [super dealloc]; -} - -- (void)uiprivUpdate:(NSInteger)row -{ - updateCellTextField(self->tf, row, self->m, - self->modelColumn, self->editableColumn, &(self->params)); -} - -- (IBAction)onAction:(id)sender -{ - NSInteger row; - const void *data; - - row = [self->t->tv rowForView:self->tf]; - data = [[self->tf stringValue] UTF8String]; - (*(self->m->mh->SetCellValue))(self->m->mh, self->m, - row, self->modelColumn, data); - // always refresh the value in case the model rejected it - [self uiprivUpdate:row]; -} - -@end - -xx TODO somehow merge this with the above -@interface uiprivImageTextColumnCellView : uiprivColumnCellView { - uiTable *t; - uiTableModel *m; - NSImageView *iv; - int modelColumn; - NSTextField *tf; - int textModelColumn; - int textEditableColumn; - uiTableTextColumnOptionalParams params; -} -- (id)initWithFrame:(NSRect)r table:(uiTable *)table model:(uiTableModel *)model modelColumn:(int)mc textModelColumn:(int)tmc editableColumn:(int)ec params:(uiTableTextColumnOptionalParams *)p; -- (IBAction)uiprivOnAction:(id)sender; -@end - -@implementation uiprivImageTextColumnCellView - -- (id)initWithFrame:(NSRect)r table:(uiTable *)table model:(uiTableModel *)model modelColumn:(int)mc textModelColumn:(int)tmc editableColumn:(int)ec params:(uiTableTextColumnOptionalParams *)p -{ - self = [super initWithFrame:frame]; - if (self) { - self->t = table; - self->m = model; - self->modelColumn = mc; - self->textModelColumn = tmc; - self->editableColumn = ec; - if (p != NULL) - params = *p; - else - params = defaultTextColumnOptionalParams; - - self->iv = [[NSImageView alloc] initWithFrame:NSZeroRect]; - [self->iv setImageFrameStyle:NSImageFrameNone]; - [self->iv setImageAlignment:NSImageAlignCenter]; - [self->iv setImageScaling:NSImageScaleProportionallyDown]; - [self->iv setAnimates:NO]; - [self->iv setEditable:NO]; - [self->iv addConstraint:uiprivMkConstraint(self->iv, NSLayoutAttributeWidth, - NSLayoutRelationEqual, - self->iv, NSLayoutAttributeHeight, - 1, 0, - @"uiTable image squareness constraint")]; - [self addSubview:self->iv]; + self->t = p->t; + self->m = p->m; self->tf = nil; - if (self->textModelColumn != -1) { + if (p->makeTextField) { + self->textModelColumn = p->textModelColumn; + self->textEditableColumn = p->textEditableColumn; + self->textParams = p->textParams; + self->tf = uiprivNewLabel(@""); // TODO set wrap and ellipsize modes? [self->tf setTarget:self]; - [self->tf setAction:@selector(uiprivOnAction:)]; + [self->tf setAction:@selector(uiprivOnTextFieldAction:)]; [self addSubview:self->tf]; - layoutCellSubview(self, self->iv, - self, imageColumnLeading, + } + + left = nil; + + self->iv = nil; + if (p->makeImageView) { + self->iv = [[NSImageView alloc] initWithFrame:NSZeroRect]; + [self->iv setImageFrameStyle:NSImageFrameNone]; + [self->iv setImageAlignment:NSImageAlignCenter]; + [self->iv setImageScaling:NSImageScaleProportionallyDown]; + [self->iv setAnimates:NO]; + [self->iv setEditable:NO]; + [self->iv addConstraint:uiprivMkConstraint(self->iv, NSLayoutAttributeWidth, + NSLayoutRelationEqual, + self->iv, NSLayoutAttributeHeight, + 1, 0, + @"uiTable image squareness constraint")]; + [self addSubview:self->iv]; + left = self->iv; + leftConstant = imageColumnLeading; + leftTextConstant = imageTextColumnLeading; + } + + self->cb = nil; + if (p->makeCheckbox) { + self->cb = [[NSButton alloc] initWithFrame:NSZeroRect]; + [self->cb setTitle:@""]; + [self->cb setButtonType:NSSwitchButton]; + // doesn't seem to have an associated bezel style + [self->cb setBordered:NO]; + [self->cb setTransparent:NO]; + uiDarwinSetControlFont(self->cb, NSRegularControlSize); + [self addSubview:self->cb]; + left = self->cb; + leftConstant = checkboxColumnLeading; + leftTextConstant = checkboxTextColumnLeading; + } + + if (self->tf != nil && left == nil) + layoutCellSubview(self, self->tf, + self, textColumnLeading, + self, textColumnTrailing, + YES); + else if (self->tf != nil) { + layoutCellSubview(self, left, + self, leftConstant, nil, 0, NO); layoutCellSubview(self, self->tf, - self, imageTextColumnLeading, + left, leftTextConstant, self, textColumnTrailing, YES); } else { - layoutCellSubview(self, self->iv, + layoutCellSubview(self, left, nil, 0, nil, 0, NO); [self addConstraint:uiprivMkConstraint(self, NSLayoutAttributeCenterX, NSLayoutRelationEqual, - self->iv, NSLayoutAttributeCenterX, + left, NSLayoutAttributeCenterX, 1, 0, - @"uiTable image centering constraint")]; + @"uiTable image/checkbox centering constraint")]; } // take advantage of NSTableCellView-provided accessibility features - [self setImageView:self->iv]; if (self->tf != nil) [self setTextField:self->tf]; + if (self->iv != nil) + [self setImageView:self->iv]; } return self; } - (void)dealloc { + if (self->cb != nil) { + [self->cb release]; + self->cb = nil; + } + if (self->iv != nil) { + [self->iv release]; + self->iv = nil; + } if (self->tf != nil) { [self->tf release]; self->tf = nil; } - [self->iv release]; - self->iv = nil; [super dealloc]; } - (void)uiprivUpdate:(NSInteger)row { void *data; + BOOL editable; - data = (*(self->m->mh->CellValue))(self->m->mh, self->m, row, self->modelColumn); - [self->iv setImage:uiprivImageNSImage((uiImage *) data)]; - if (self->tf != nil) - updateCellTextField(self->tf, row, self->m, - self->textModelColumn, self->editableColumn, &(self->params)); + if (self->tv != nil) { + NSString *str; + BOOL editable; + + data = (*(self->m->mh->CellValue))(self->m->mh, self->m, row, self->textModelColumn); + str = uiprivToNSString((char *) data); + uiprivFree(data); + [self->tf setStringValue:str]; + + [self->tf setEditable:isCellEditable(self->m, row, self->textEditableColumn)]; + + color = nil; + if (self->textParams.ColorModelColumn != -1) + color = (NSColor *) ((*(self->m->mh->CellValue))(self->m->mh, self->m, row, self->textParams.ColorModelColumn)); + if (color == nil) + color = [NSColor controlTextColor]; + [self->tf setColor:color]; + // TODO release color + } + if (self->iv != nil) { + data = (*(self->m->mh->CellValue))(self->m->mh, self->m, row, self->imageModelColumn); + [self->iv setImage:uiprivImageNSImage((uiImage *) data)]; + } + if (self->cb != nil) { + data = (*(self->m->mh->CellValue))(self->m->mh, self->m, row, self->imageModelColumn); + if (TODO(data)) + [self->cb setState:NSOnState]; + else + [self->cb setState:NSOffState]; + + [self->cb setEditable:isCellEditable(self->m, row, self->checkboxEditableColumn)]; + } } -- (IBAction)onAction:(id)sender +- (IBAction)uiprivOnTextFieldAction:(id)sender { NSInteger row; const void *data; @@ -274,6 +277,21 @@ xx TODO somehow merge this with the above [self uiprivUpdate:row]; } +- (IBAction)uiprivOnCheckboxAction:(id)sender +{ + NSInteger row; + int val; + void *data; + + row = [self->t->tv rowForView:self->cb]; + val = [self->cb state] != NSOffState; + data = TODO(val); + (*(self->m->mh->SetCellValue))(self->m->mh, self->m, + row, self->checkboxModelColumn, data); + // always refresh the value in case the model rejected it + [self uiprivUpdate:row]; +} + @end void uiTableAppendTextColumn(uiTable *t, From 705bf2d9bfcd46860e5f120afcf3189d1fd14d2e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 3 Jun 2018 17:18:01 -0400 Subject: [PATCH 1126/1329] Planned a new system for transferring data between tables and models, based on uiAttribute. --- uitable.h | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/uitable.h b/uitable.h index 0f23c448..bd95cb53 100644 --- a/uitable.h +++ b/uitable.h @@ -8,24 +8,43 @@ _UI_EXTERN uiImage *uiNewImage(double width, double height); _UI_EXTERN void uiFreeImage(uiImage *i); _UI_EXTERN void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int pixelStride); -typedef struct uiTableModel uiTableModel; -typedef struct uiTableModelHandler uiTableModelHandler; +typedef struct uiTableData uiTableData; + +_UI_EXTERN void uiFreeTableData(uiTableData *d); // TODO actually validate these -_UI_ENUM(uiTableModelColumnType) { - uiTableModelColumnString, - uiTableModelColumnImage, - uiTableModelColumnInt, - uiTableModelColumnColor, +_UI_ENUM(uiTableDataType) { + uiTableDataTypeString, + uiTableDataTypeImage, + uiTableDataTypeInt, + uiTableDataTypeColor, }; +// TODO I don't like this name +_UI_EXTERN uiTableDataType uiTableDataGetType(const uiTableData *d); + +_UI_EXTERN uiTableData *uiNewTableDataString(const char *str); +_UI_EXTERN const char *uiTableDataString(const uiTableData *d); + +_UI_EXTERN uiTableData *uiNewTableDataImage(uiImage *img); +_UI_EXTERN uiImage *uiTableDataImage(const uiTableData *d); + +_UI_EXTERN uiTableData *uiNewTableDataInt(int i); +_UI_EXTERN int uiTableDataInt(const uiTableData *d); + +_UI_EXTERN uiTableData *uiNewTableDataColor(double r, double g, double b, double a); +_UI_EXTERN void uiTableDataColor(const uiTableData *d, double *r, double *g, double *b, double *a); + +typedef struct uiTableModel uiTableModel; +typedef struct uiTableModelHandler uiTableModelHandler; + // TODO validate ranges; validate types on each getter/setter call (? table columns only?) struct uiTableModelHandler { int (*NumColumns)(uiTableModelHandler *, uiTableModel *); - uiTableModelColumnType (*ColumnType)(uiTableModelHandler *, uiTableModel *, int); + uiTableDataType (*ColumnType)(uiTableModelHandler *, uiTableModel *, int); int (*NumRows)(uiTableModelHandler *, uiTableModel *); - void *(*CellValue)(uiTableModelHandler *, uiTableModel *, int, int); - void (*SetCellValue)(uiTableModelHandler *, uiTableModel *, int, int, const void *); + uiTableData *(*CellValue)(uiTableModelHandler *, uiTableModel *, int, int); + void (*SetCellValue)(uiTableModelHandler *, uiTableModel *, int, int, const uiTableData *); }; _UI_EXTERN void *uiTableModelStrdup(const char *str); From 43b1a466695a9aeec5f77eafb3c56b29cd40a0fd Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 3 Jun 2018 17:28:54 -0400 Subject: [PATCH 1127/1329] Wrote the common table data functions. --- common/CMakeLists.txt | 1 + common/tabledata.c | 105 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 common/tabledata.c diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 4ef742d6..23cf663b 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -12,6 +12,7 @@ list(APPEND _LIBUI_SOURCES common/opentype.c common/shouldquit.c common/table.c + common/tabledata.c common/userbugs.c common/utf.c ) diff --git a/common/tabledata.c b/common/tabledata.c new file mode 100644 index 00000000..25710bcf --- /dev/null +++ b/common/tabledata.c @@ -0,0 +1,105 @@ +// 3 june 2018 +#include "../ui.h" +#include "uipriv.h" + +struct uiTableData { + uiTableDataType type; + union { + char *str; + uiImage *img; + int i; + struct { + double r; + double g; + double b; + double a; + } color; + } u; +}; + +static uiTableData *newTableData(uiTableData type) +{ + uiTableData *d; + + d = uiprivNew(uiTableData); + d->type = type; + return d; +} + +void uiFreeAttribute(uiTableData *a) +{ + switch (d->type) { + case uiTableDataTypeString: + uiprivFree(d->u.str); + break; + } + uiprivFree(d); +} + +uiTableDataType uiTableDataGetType(const uiTableData *d) +{ + return d->type; +} + +uiTableData *uiNewTableDataString(const char *str) +{ + uiTableData *d; + + d = newTableData(uiTableDataTypeString); + d->u.str = (char *) uiprivAlloc((strlen(str) + 1) * sizeof (char), "char[] (uiTableData)"); + strcpy(d->u.str, str); + return d; +} + +const char *uiTableDataString(const uiTableData *d) +{ + return d->u.str; +} + +uiTableData *uiNewTableDataImage(uiImage *img) +{ + uiTableData *d; + + d = newTableData(uiTableDataTypeImage); + d->u.img = img; + return d; +} + +uiImage *uiTableDataImage(const uiTableData *d) +{ + return d->u.img; +} + +uiTableData *uiNewTableDataInt(int i) +{ + uiTableData *d; + + d = newTableData(uiTableDataTypeInt); + d->u.i = i; + return d; +} + +int uiTableDataInt(const uiTableData *d) +{ + return d->u.i; +} + +uiTableData *uiNewTableDataColor(double r, double g, double b, double a) +{ + uiTableData *d; + + d = newTableData(uiTableDataTypeColor); + d->u.color.r = r; + d->u.color.g = g; + d->u.color.b = b; + d->u.color.a = a; + return d; +} + +void uiTableDataColor(const uiTableData *d, double *r, double *g, double *b, double *a) +{ + *r = d->u.color.r; + *g = d->u.color.g; + *b = d->u.color.b; + *a = d->u.color.a; +} From c04f3d3fbe15c31d5a6b37317b89eb2f721b07f5 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 3 Jun 2018 17:39:02 -0400 Subject: [PATCH 1128/1329] Adjusted tablecolumn.m to use the new data functions. --- darwin/tablecolumn.m | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/darwin/tablecolumn.m b/darwin/tablecolumn.m index 6c4f666d..94a92733 100644 --- a/darwin/tablecolumn.m +++ b/darwin/tablecolumn.m @@ -56,7 +56,8 @@ static void layoutCellSubview(NSView *superview, NSView *subview, NSView *leadin static BOOL isCellEditable(uiTableModel *m, NSInteger row, int modelColumn) { - void *data; + uiTableData *data; + int value; switch (modelColumn) { case uiTableModelColumnNeverEditable: @@ -65,7 +66,9 @@ static BOOL isCellEditable(uiTableModel *m, NSInteger row, int modelColumn) return YES; } data = (*(m->mh->CellValue))(m->mh, m, row, modelColumn); - return uiTableModelTakeInt(data) != 0; + value = uiTableDataInt(data); + uiFreeTableData(data); + return value != 0; // TODO free data } @@ -227,7 +230,7 @@ struct textColumnCreateParams { - (void)uiprivUpdate:(NSInteger)row { - void *data; + uiTableData *data; BOOL editable; if (self->tv != nil) { @@ -235,30 +238,41 @@ struct textColumnCreateParams { BOOL editable; data = (*(self->m->mh->CellValue))(self->m->mh, self->m, row, self->textModelColumn); - str = uiprivToNSString((char *) data); - uiprivFree(data); + str = uiprivToNSString(uiTableDataString(data)); + uiFreeTableData(data); [self->tf setStringValue:str]; [self->tf setEditable:isCellEditable(self->m, row, self->textEditableColumn)]; color = nil; - if (self->textParams.ColorModelColumn != -1) - color = (NSColor *) ((*(self->m->mh->CellValue))(self->m->mh, self->m, row, self->textParams.ColorModelColumn)); + if (self->textParams.ColorModelColumn != -1) { + double r, g, b, a; + + data = (*(self->m->mh->CellValue))(self->m->mh, self->m, row, self->textParams.ColorModelColumn); + uiTableDataColor(data, &r, &g, &b, &a); + uiFreeTableData(data); + xx TODO + } if (color == nil) color = [NSColor controlTextColor]; [self->tf setColor:color]; // TODO release color } if (self->iv != nil) { + uiImage *img; + data = (*(self->m->mh->CellValue))(self->m->mh, self->m, row, self->imageModelColumn); - [self->iv setImage:uiprivImageNSImage((uiImage *) data)]; + img = uiTableDataImage(data); + uiFreeTableData(data); + [self->iv setImage:uiprivImageNSImage(img)]; } if (self->cb != nil) { data = (*(self->m->mh->CellValue))(self->m->mh, self->m, row, self->imageModelColumn); - if (TODO(data)) + if (uiTableDataInt(data) != 0) [self->cb setState:NSOnState]; else [self->cb setState:NSOffState]; + uiFreeTableData(data); [self->cb setEditable:isCellEditable(self->m, row, self->checkboxEditableColumn)]; } @@ -267,12 +281,13 @@ struct textColumnCreateParams { - (IBAction)uiprivOnTextFieldAction:(id)sender { NSInteger row; - const void *data; + uiTableData *data; row = [self->t->tv rowForView:self->tf]; - data = [[self->tf stringValue] UTF8String]; + data = uiNewTableDataString([[self->tf stringValue] UTF8String]); (*(self->m->mh->SetCellValue))(self->m->mh, self->m, row, self->textModelColumn, data); + uiFreeTableData(data); // always refresh the value in case the model rejected it [self uiprivUpdate:row]; } @@ -280,14 +295,13 @@ struct textColumnCreateParams { - (IBAction)uiprivOnCheckboxAction:(id)sender { NSInteger row; - int val; void *data; row = [self->t->tv rowForView:self->cb]; - val = [self->cb state] != NSOffState; - data = TODO(val); + data = uiNewTableDataInt([self->cb state] != NSOffState); (*(self->m->mh->SetCellValue))(self->m->mh, self->m, row, self->checkboxModelColumn, data); + uiFreeTableData(data); // always refresh the value in case the model rejected it [self uiprivUpdate:row]; } From 54ca41afb6e4eef9b44b713c8acee5cedf742d17 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 3 Jun 2018 17:57:17 -0400 Subject: [PATCH 1129/1329] Moved common/table.c out of the way for now; it's not relevant with the proposed colum changes and definite data changes. --- common/CMakeLists.txt | 2 +- common/{table.c => OLD_table.c} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename common/{table.c => OLD_table.c} (100%) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 23cf663b..99ceda09 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -11,7 +11,7 @@ list(APPEND _LIBUI_SOURCES common/matrix.c common/opentype.c common/shouldquit.c - common/table.c +# common/table.c common/tabledata.c common/userbugs.c common/utf.c diff --git a/common/table.c b/common/OLD_table.c similarity index 100% rename from common/table.c rename to common/OLD_table.c From b74b987fffbf21c97453b1024d363358e6cf01ec Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 3 Jun 2018 18:34:29 -0400 Subject: [PATCH 1130/1329] Added progressbar columns. --- darwin/tablecolumn.m | 66 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/darwin/tablecolumn.m b/darwin/tablecolumn.m index 94a92733..2bb9828b 100644 --- a/darwin/tablecolumn.m +++ b/darwin/tablecolumn.m @@ -9,6 +9,8 @@ #define checkboxTextColumnLeading 0 // these aren't provided by IB; let's just choose one #define checkboxColumnLeading imageColumnLeading +#define progressBarColumnLeading imageColumnLeading +#define progressBarColumnTrailing progressBarColumnLeading static void layoutCellSubview(NSView *superview, NSView *subview, NSView *leading, CGFloat leadingConstant, NSView *trailing, CGFloat trailingConstant, BOOL stretchy) { @@ -308,6 +310,70 @@ struct textColumnCreateParams { @end +@interface uiprivProgressBarColumnCellView : uiprivColumnCellView { + uiTable *t; + uiTableModel *m; + NSProgressIndicator *p; + int modelColumn; +} +- (id)initWithFrame:(NSRect)r table:(uiTable *)table model:(uiTableModel *)model modelColumn:(int)mc; +@end + +@implementation uiprivProgressBarColumnCellView + +- (id)initWithFrame:(NSRect)r table:(uiTable *)table model:(uiTableModel *)model modelColumn:(int)mc +{ + self = [super initWithFrame:r]; + if (self) { + self->t = table; + self->m = model; + self->modelColumn = mc; + + self->p = [[NSProgressIndicator alloc] initWithFrame:NSZeroRect]; + [self->p setControlSize:NSRegularControlSize]; + [self->p setBezeled:YES]; + [self->p setStyle:NSProgressIndicatorBarStyle]; + layoutCellSubview(self, self->p, + self, progressBarColumnLeading, + self, progressBarColumnTrailing, + YES); + } + return self; +} + +- (void)dealloc +{ + [self->p release]; + self->p = nil; + [super dealloc]; +} + +- (void)uiprivUpdate:(NSInteger)row +{ + uiTableData *data; + int value; + + data = (*(self->m->mh->CellValue))(self->m->mh, self->m, row, self->modelColumn); + value = uiTableDataInt(data); + uiFreeTableData(data); + if (value == -1) { + [self->p setIndeterminate:YES]; + [self->p startAnimation:self->p]; + } else if (value == 100) { + [self->p setIndeterminate:NO]; + [self->p setMaxValue:101]; + [self->p setDoubleValue:101]; + [self->p setDoubleValue:100]; + [self->p setMaxValue:100]; + } else { + [self->p setIndeterminate:NO]; + [self->p setDoubleValue:(value + 1)]; + [self->p setDoubleValue:value]; + } +} + +@end + void uiTableAppendTextColumn(uiTable *t, const char *name, int textModelColumn, From 8ee5c61fe822801521719b5c9c5dde39e1c592b5 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 3 Jun 2018 19:21:01 -0400 Subject: [PATCH 1131/1329] And added button columns. Now to start writing the rest of the uiTable glue. --- darwin/tablecolumn.m | 67 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/darwin/tablecolumn.m b/darwin/tablecolumn.m index 2bb9828b..47d0b8fb 100644 --- a/darwin/tablecolumn.m +++ b/darwin/tablecolumn.m @@ -253,7 +253,7 @@ struct textColumnCreateParams { data = (*(self->m->mh->CellValue))(self->m->mh, self->m, row, self->textParams.ColorModelColumn); uiTableDataColor(data, &r, &g, &b, &a); uiFreeTableData(data); - xx TODO + // TODO } if (color == nil) color = [NSColor controlTextColor]; @@ -374,6 +374,71 @@ struct textColumnCreateParams { @end +@interface uiprivButtonColumnCellView : uiprivColumnCellView { + uiTable *t; + uiTableModel *m; + NSButton *b; + int modelColumn; + int editableColumn; +} +- (id)initWithFrame:(NSRect)r table:(uiTable *)table model:(uiTableModel *)model modelColumn:(int)mc editableColumn:(int)ec; +- (IBAction)uiprivOnClicked:(id)sender; +@end + +@implementation uiprivProgressBarColumnCellView + +- (id)initWithFrame:(NSRect)r table:(uiTable *)table model:(uiTableModel *)model modelColumn:(int)mc editableColumn:(int)ec +{ + self = [super initWithFrame:r]; + if (self) { + self->t = table; + self->m = model; + self->modelColumn = mc; + self->editableColumn = ec; + + self->b = [[NSButton alloc] initWithFrame:NSZeroRect]; + [self->b setButtonType:NSMomentaryPushInButton]; + [self->b setBordered:YES]; + [self->b setBezelStyle:NSRoundRectBezelStyle]; + uiDarwinSetControlFont(self->b, NSRegularControlSize); + [self->b setTarget:self]; + [self->b setAction:@selector(uiprivOnClicked:)]; + layoutCellSubview(self, self->b, + self, progressBarColumnLeading, + self, progressBarColumnTrailing, + YES); + } + return self; +} + +- (void)dealloc +{ + [self->p release]; + self->p = nil; + [super dealloc]; +} + +- (void)uiprivUpdate:(NSInteger)row +{ + uiTableData *data; + NSString *str; + BOOL editable; + + data = (*(self->m->mh->CellValue))(self->m->mh, self->m, row, self->modelColumn); + str = uiprivToNSString(uiTableDataString(data)); + uiFreeTableData(data); + [self->b setTitle:str]; + + [self->b setEditable:isCellEditable(self->m, row, self->editableColumn)]; +} + +- (id)uiprivOnClicked:(id)sender +{ + // TODO +} + +@end + void uiTableAppendTextColumn(uiTable *t, const char *name, int textModelColumn, From da2a4c1e3600c08a750d73e9ab7d2a749337c314 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 3 Jun 2018 19:59:05 -0400 Subject: [PATCH 1132/1329] Started building back the uiTable implementation. --- darwin/table.m | 271 +++++++++++++++++++++++++++++++++++++++++++ darwin/tablecolumn.m | 24 ++++ 2 files changed, 295 insertions(+) create mode 100644 darwin/table.m diff --git a/darwin/table.m b/darwin/table.m new file mode 100644 index 00000000..62b465fc --- /dev/null +++ b/darwin/table.m @@ -0,0 +1,271 @@ +xx 3 june 2018 +#import "uipriv_darwin.h" + +@interface uiprivTableModel : NSObject { + uiTableModel *m; +} +- (id)initWithModel:(uiTableModel *)model; +@end + +struct uiTableModel { + uiTableModelHandler *mh; + uiprivTableModel *m; + NSMutableArray *tables; +}; + +struct uiTable { + uiDarwinControl c; + NSScrollView *sv; + NSTableView *tv; + uiprivScrollViewData *d; + int backgroundColumn; +}; + +@implementation tableModel + +- (id)initWithModel:(uiTableModel *)m +{ + self = [super init]; + if (self) + self->libui_m = m; + return self; +} + +- (NSInteger)numberOfRowsInTableView:(NSTableView *)tv +{ + return (*(self->m->mh->NumRows))(self->m->mh, self->m); +} + + - (NSView *)tableView:(NSTableView *)tv viewForTableColumn:(NSTableColumn *)cc row:(NSInteger)row +{ + uiprivTableColumn *c = (uiprivTableColumn *) cc; + xx TODO consider renaming this type to uiprivTableCellView + uiprivColumnCellView *cv; + + cv = (uiprivColumnCellView *) [tv makeViewWithIdentifier:[c identifier] owner:self]; + if (cv == nil) + cv = [c uiprivMakeCellView]; + [cv uiprivUpdate:row]; + return cv; +} + +- (void)tableView:(NSTableView *)nstv didAddRowView:(NSTableRowView *)rv forRow:(NSInteger)row +{ + xx TODO set background color +} + +@end + +=================== TODOTODO + +uiTableModel *uiNewTableModel(uiTableModelHandler *mh) +{ + uiTableModel *m; + + m = uiprivNew(uiTableModel); + m->mh = mh; + m->m = [[tableModel alloc] initWithModel:m]; + m->tables = [NSMutableArray new]; + return m; +} + +void *uiTableModelGiveColor(double r, double g, double b, double a) +{ + return [[NSColor colorWithSRGBRed:r green:g blue:b alpha:a] retain]; +} + +void uiFreeTableModel(uiTableModel *m) +{ + if ([m->tables count] != 0) + uiprivUserBug("You cannot free a uiTableModel while uiTables are using it."); + [m->tables release]; + [m->m release]; + uiprivFree(m); +} + +void uiTableModelRowInserted(uiTableModel *m, int newIndex) +{ + NSTableView *tv; + NSIndexSet *set; + + set = [NSIndexSet indexSetWithIndex:newIndex]; + for (tv in m->tables) + [tv insertRowsAtIndexes:set withAnimation:NSTableViewAnimationEffectNone]; + // set is autoreleased +} + +void uiTableModelRowChanged(uiTableModel *m, int index) +{ + NSTableView *tv; + NSIndexSet *set, *cols; + + set = [NSIndexSet indexSetWithIndex:index]; + for (tv in m->tables) { + cols = [[NSIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, [[tv tableColumns] count])]; + [tv reloadDataForRowIndexes:set columnIndexes:cols]; + // TODO this isn't enough + [cols release]; + } + // set is autoreleased +} + +void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) +{ + NSTableView *tv; + NSIndexSet *set; + + set = [NSIndexSet indexSetWithIndex:oldIndex]; + for (tv in m->tables) + [tv removeRowsAtIndexes:set withAnimation:NSTableViewAnimationEffectNone]; + // set is autoreleased +} + +void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand) +{ + tablePart *part; + + part = [tablePart new]; + part.type = partText; + part.textColumn = modelColumn; + part.expand = expand; + [c->parts addObject:part]; +} + +void uiTableColumnAppendImagePart(uiTableColumn *c, int modelColumn, int expand) +{ + tablePart *part; + + part = [tablePart new]; + part.type = partImage; + part.imageColumn = modelColumn; + part.expand = expand; + [c->parts addObject:part]; +} + +void uiTableColumnAppendButtonPart(uiTableColumn *c, int modelColumn, int expand) +{ + tablePart *part; + + part = [tablePart new]; + part.type = partButton; + part.textColumn = modelColumn; + part.expand = expand; + part.editable = 1; // editable by default + [c->parts addObject:part]; +} + +void uiTableColumnAppendCheckboxPart(uiTableColumn *c, int modelColumn, int expand) +{ + tablePart *part; + + part = [tablePart new]; + part.type = partCheckbox; + part.valueColumn = modelColumn; + part.expand = expand; + part.editable = 1; // editable by default + [c->parts addObject:part]; +} + +void uiTableColumnAppendProgressBarPart(uiTableColumn *c, int modelColumn, int expand) +{ + tablePart *part; + + part = [tablePart new]; + part.type = partProgressBar; + part.valueColumn = modelColumn; + part.expand = expand; + [c->parts addObject:part]; +} + +void uiTableColumnPartSetEditable(uiTableColumn *c, int part, int editable) +{ + tablePart *p; + + p = (tablePart *) [c->parts objectAtIndex:part]; + p.editable = editable; +} + +void uiTableColumnPartSetTextColor(uiTableColumn *c, int part, int modelColumn) +{ + tablePart *p; + + p = (tablePart *) [c->parts objectAtIndex:part]; + p.textColorColumn = modelColumn; +} + +uiDarwinControlAllDefaultsExceptDestroy(uiTable, sv) + +static void uiTableDestroy(uiControl *c) +{ + uiTable *t = uiTable(c); + + // TODO + [t->sv release]; + uiFreeControl(uiControl(t)); +} + +uiTableColumn *uiTableAppendColumn(uiTable *t, const char *name) +{ + uiTableColumn *c; + + c = uiprivNew(uiTableColumn); + c->c = [[tableColumn alloc] initWithIdentifier:@""]; + c->c.libui_col = c; + // via Interface Builder + [c->c setResizingMask:(NSTableColumnAutoresizingMask | NSTableColumnUserResizingMask)]; + // 10.10 adds -[NSTableColumn setTitle:]; before then we have to do this + [[c->c headerCell] setStringValue:uiprivToNSString(name)]; + // TODO is this sufficient? + [[c->c headerCell] setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]]; + c->parts = [NSMutableArray new]; + [t->tv addTableColumn:c->c]; + return c; +} + +void uiTableSetRowBackgroundColorModelColumn(uiTable *t, int modelColumn) +{ + t->backgroundColumn = modelColumn; +} + +uiTable *uiNewTable(uiTableModel *model) +{ + uiTable *t; + uiprivScrollViewCreateParams p; + + uiDarwinNewControl(uiTable, t); + + t->tv = [[tableView alloc] initWithFrame:NSZeroRect]; + t->tv.libui_t = t; + + [t->tv setDataSource:model->m]; + [t->tv setDelegate:model->m]; + [t->tv reloadData]; + [model->tables addObject:t->tv]; + + // TODO is this sufficient? + [t->tv setAllowsColumnReordering:NO]; + [t->tv setAllowsColumnResizing:YES]; + [t->tv setAllowsMultipleSelection:NO]; + [t->tv setAllowsEmptySelection:YES]; + [t->tv setAllowsColumnSelection:NO]; + [t->tv setUsesAlternatingRowBackgroundColors:YES]; + [t->tv setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleRegular]; + [t->tv setGridStyleMask:NSTableViewGridNone]; + [t->tv setAllowsTypeSelect:YES]; + // TODO floatsGroupRows — do we even allow group rows? + + memset(&p, 0, sizeof (uiprivScrollViewCreateParams)); + p.DocumentView = t->tv; + // this is what Interface Builder sets it to + // TODO verify + p.BackgroundColor = [NSColor colorWithCalibratedWhite:1.0 alpha:1.0]; + p.DrawsBackground = YES; + p.Bordered = YES; + p.HScroll = YES; + p.VScroll = YES; + t->sv = uiprivMkScrollView(&p, &(t->d)); + + t->backgroundColumn = -1; + + return t; +} diff --git a/darwin/tablecolumn.m b/darwin/tablecolumn.m index 47d0b8fb..cb7624b3 100644 --- a/darwin/tablecolumn.m +++ b/darwin/tablecolumn.m @@ -47,6 +47,13 @@ static void layoutCellSubview(NSView *superview, NSView *subview, NSView *leadin - (void)uiprivUpdate:(NSInteger)row; @end +@interface uiprivTableColumn : NSTableColumn { + uiprivColumnCellView *(^mkCell)(void); +} +- (id)initWithIdentifier:(NSUserInterfaceItemIdentifier)ident mkCellView:(uiprivColumnCellView *(^)(void))f; +- (uiprivColumnCellView *)uiprivMakeCellView; +@end + @implementation uiprivColumnCellView - (void)uiprivUpdate:(NSInteger)row @@ -56,6 +63,23 @@ static void layoutCellSubview(NSView *superview, NSView *subview, NSView *leadin @end +@implementation uiprivTableColumn + +- (id)initWithIdentifier:(NSUserInterfaceItemIdentifier)ident mkCellView:(uiprivColumnCellView *(^)(void))f +{ + self = [super initWithIdentifier:ident]; + if (self) + self->mkCell = f; + return self; +} + +- (uiprivColumnCellView *)uiprivMakeCellView +{ + return (self->mkCell)(); +} + +@end + static BOOL isCellEditable(uiTableModel *m, NSInteger row, int modelColumn) { uiTableData *data; From 247d63be60587c5ced582a817ba386b953a89990 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 3 Jun 2018 20:55:08 -0400 Subject: [PATCH 1133/1329] Added a table.h and simplified uiprivTableColumn into an abstract interface. --- darwin/table.h | 25 +++++++++++++++++++++++++ darwin/table.m | 19 +++---------------- darwin/tablecolumn.m | 38 ++++++++++---------------------------- 3 files changed, 38 insertions(+), 44 deletions(-) create mode 100644 darwin/table.h diff --git a/darwin/table.h b/darwin/table.h new file mode 100644 index 00000000..dbc26520 --- /dev/null +++ b/darwin/table.h @@ -0,0 +1,25 @@ +// 3 june 2018 + +// table.m +// TODO get rid of forward declaration +@class uiprivTableModel; +struct uiTableModel { + uiTableModelHandler *mh; + uiprivTableModel *m; + NSMutableArray *tables; +}; +struct uiTable { + uiDarwinControl c; + NSScrollView *sv; + NSTableView *tv; + uiprivScrollViewData *d; + int backgroundColumn; +}; + +// tablecolumn.m +@interface uiprivTableCellView : NSTableCellView +- (void)uiprivUpdate:(NSInteger)row; +@end +@interface uiprivTableColumn : NSTableColumn +- (uiprivColumnCellView *)uiprivMakeCellView; +@end diff --git a/darwin/table.m b/darwin/table.m index 62b465fc..ac2aef63 100644 --- a/darwin/table.m +++ b/darwin/table.m @@ -1,5 +1,6 @@ -xx 3 june 2018 +// 3 june 2018 #import "uipriv_darwin.h" +#import "table.h" @interface uiprivTableModel : NSObject { uiTableModel *m; @@ -7,21 +8,7 @@ xx 3 june 2018 - (id)initWithModel:(uiTableModel *)model; @end -struct uiTableModel { - uiTableModelHandler *mh; - uiprivTableModel *m; - NSMutableArray *tables; -}; - -struct uiTable { - uiDarwinControl c; - NSScrollView *sv; - NSTableView *tv; - uiprivScrollViewData *d; - int backgroundColumn; -}; - -@implementation tableModel +@implementation uiprivTableModel - (id)initWithModel:(uiTableModel *)m { diff --git a/darwin/tablecolumn.m b/darwin/tablecolumn.m index cb7624b3..e01fb546 100644 --- a/darwin/tablecolumn.m +++ b/darwin/tablecolumn.m @@ -1,5 +1,6 @@ // 3 june 2018 #import "uipriv_darwin.h" +#import "table.h" // values from interface builder #define textColumnLeading 2 @@ -43,18 +44,7 @@ static void layoutCellSubview(NSView *superview, NSView *subview, NSView *leadin @"uiTable cell subview bottom constraint")]; } -@interface uiprivColumnCellView : NSTableCellView -- (void)uiprivUpdate:(NSInteger)row; -@end - -@interface uiprivTableColumn : NSTableColumn { - uiprivColumnCellView *(^mkCell)(void); -} -- (id)initWithIdentifier:(NSUserInterfaceItemIdentifier)ident mkCellView:(uiprivColumnCellView *(^)(void))f; -- (uiprivColumnCellView *)uiprivMakeCellView; -@end - -@implementation uiprivColumnCellView +@implementation uiprivTableCellView - (void)uiprivUpdate:(NSInteger)row { @@ -65,17 +55,9 @@ static void layoutCellSubview(NSView *superview, NSView *subview, NSView *leadin @implementation uiprivTableColumn -- (id)initWithIdentifier:(NSUserInterfaceItemIdentifier)ident mkCellView:(uiprivColumnCellView *(^)(void))f +- (uiprivTableCellView *)uiprivMakeCellView { - self = [super initWithIdentifier:ident]; - if (self) - self->mkCell = f; - return self; -} - -- (uiprivColumnCellView *)uiprivMakeCellView -{ - return (self->mkCell)(); + [self doesNotRecognizeSelector:_cmd]; } @end @@ -119,7 +101,7 @@ struct textColumnCreateParams { int checkboxEditableColumn; }; -@interface uiprivTextImageCheckboxColumnCellView : uiprivColumnCellView { +@interface uiprivTextImageCheckboxTableCellView : uiprivTableCellView { uiTable *t; uiTableModel *m; @@ -140,7 +122,7 @@ struct textColumnCreateParams { - (IBAction)uiprivOnCheckboxAction:(id)sender; @end -@implementation uiprivTextColumnCellView +@implementation uiprivTextTableCellView - (id)initWithFrame:(NSRect)r params:(struct textColumnCreateParams *)p { @@ -334,7 +316,7 @@ struct textColumnCreateParams { @end -@interface uiprivProgressBarColumnCellView : uiprivColumnCellView { +@interface uiprivProgressBarTableCellView : uiprivTableCellView { uiTable *t; uiTableModel *m; NSProgressIndicator *p; @@ -343,7 +325,7 @@ struct textColumnCreateParams { - (id)initWithFrame:(NSRect)r table:(uiTable *)table model:(uiTableModel *)model modelColumn:(int)mc; @end -@implementation uiprivProgressBarColumnCellView +@implementation uiprivProgressBarTableCellView - (id)initWithFrame:(NSRect)r table:(uiTable *)table model:(uiTableModel *)model modelColumn:(int)mc { @@ -398,7 +380,7 @@ struct textColumnCreateParams { @end -@interface uiprivButtonColumnCellView : uiprivColumnCellView { +@interface uiprivButtonTableCellView : uiprivTableCellView { uiTable *t; uiTableModel *m; NSButton *b; @@ -409,7 +391,7 @@ struct textColumnCreateParams { - (IBAction)uiprivOnClicked:(id)sender; @end -@implementation uiprivProgressBarColumnCellView +@implementation uiprivProgressBarTableCellView - (id)initWithFrame:(NSRect)r table:(uiTable *)table model:(uiTableModel *)model modelColumn:(int)mc editableColumn:(int)ec { From 6457e1668fbeeff2af228137fa90bb0e37adc326 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 3 Jun 2018 21:39:49 -0400 Subject: [PATCH 1134/1329] Rewrote uiTableModelRowChanged() to properly update rows. We can do this now that we have the setup for row updates and reuse, but this is better than calling reloadData anyway (and reloadData doesn't update the row view, but we (almost) do, so...)... --- darwin/table.m | 33 +++++++++++++++++---------------- darwin/tablecolumn.m | 4 ++-- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/darwin/table.m b/darwin/table.m index ac2aef63..70b11c3e 100644 --- a/darwin/table.m +++ b/darwin/table.m @@ -26,7 +26,7 @@ - (NSView *)tableView:(NSTableView *)tv viewForTableColumn:(NSTableColumn *)cc row:(NSInteger)row { uiprivTableColumn *c = (uiprivTableColumn *) cc; - xx TODO consider renaming this type to uiprivTableCellView + // TODO consider renaming this type to uiprivTableCellView uiprivColumnCellView *cv; cv = (uiprivColumnCellView *) [tv makeViewWithIdentifier:[c identifier] owner:self]; @@ -43,24 +43,17 @@ @end -=================== TODOTODO - uiTableModel *uiNewTableModel(uiTableModelHandler *mh) { uiTableModel *m; m = uiprivNew(uiTableModel); m->mh = mh; - m->m = [[tableModel alloc] initWithModel:m]; + m->m = [[uiprivTableModel alloc] initWithModel:m]; m->tables = [NSMutableArray new]; return m; } -void *uiTableModelGiveColor(double r, double g, double b, double a) -{ - return [[NSColor colorWithSRGBRed:r green:g blue:b alpha:a] retain]; -} - void uiFreeTableModel(uiTableModel *m) { if ([m->tables count] != 0) @@ -84,16 +77,22 @@ void uiTableModelRowInserted(uiTableModel *m, int newIndex) void uiTableModelRowChanged(uiTableModel *m, int index) { NSTableView *tv; - NSIndexSet *set, *cols; + NSTableRowView *rv; + NSUInteger i, n; + uiprivTableColumnView *cv; - set = [NSIndexSet indexSetWithIndex:index]; for (tv in m->tables) { - cols = [[NSIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, [[tv tableColumns] count])]; - [tv reloadDataForRowIndexes:set columnIndexes:cols]; - // TODO this isn't enough - [cols release]; + rv = [tv rowViewForRow:index makeIfNecessary:NO]; + if (rv != nil) { + xx TODO update colors + } + n = [[tv tableColumns] count]; + for (i = 0; i < n; i++) { + cv = (uiprivTableCellView *) [tv viewForColumn:i row:index makeIfNecessary:NO]; + if (cv != nil) + [cv uiprivUpdate:index]; + } } - // set is autoreleased } void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) @@ -107,6 +106,8 @@ void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) // set is autoreleased } +=================== TODOTODO + void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand) { tablePart *part; diff --git a/darwin/tablecolumn.m b/darwin/tablecolumn.m index e01fb546..01ff5b27 100644 --- a/darwin/tablecolumn.m +++ b/darwin/tablecolumn.m @@ -259,12 +259,12 @@ struct textColumnCreateParams { data = (*(self->m->mh->CellValue))(self->m->mh, self->m, row, self->textParams.ColorModelColumn); uiTableDataColor(data, &r, &g, &b, &a); uiFreeTableData(data); - // TODO + color = [NSColor colorWithSRGBRed:r green:g blue:b alpha:a]; } if (color == nil) color = [NSColor controlTextColor]; [self->tf setColor:color]; - // TODO release color + // we don't own color in ether case; don't release } if (self->iv != nil) { uiImage *img; From 8fc4a9aaf561741d2ec23f756a07117c860cfb6a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 3 Jun 2018 22:37:36 -0400 Subject: [PATCH 1135/1329] Filled in the rest of tablecolumn.m: wrote the NSTableColumn subclasses and implemented the public functions. Let's build and see what breaks! --- darwin/table.h | 1 + darwin/table.m | 98 +---------------- darwin/tablecolumn.m | 246 +++++++++++++++++++++++++++++++++++++------ 3 files changed, 218 insertions(+), 127 deletions(-) diff --git a/darwin/table.h b/darwin/table.h index dbc26520..0871a740 100644 --- a/darwin/table.h +++ b/darwin/table.h @@ -14,6 +14,7 @@ struct uiTable { NSTableView *tv; uiprivScrollViewData *d; int backgroundColumn; + uiTableModel *m; }; // tablecolumn.m diff --git a/darwin/table.m b/darwin/table.m index 70b11c3e..b046c6db 100644 --- a/darwin/table.m +++ b/darwin/table.m @@ -106,81 +106,6 @@ void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) // set is autoreleased } -=================== TODOTODO - -void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand) -{ - tablePart *part; - - part = [tablePart new]; - part.type = partText; - part.textColumn = modelColumn; - part.expand = expand; - [c->parts addObject:part]; -} - -void uiTableColumnAppendImagePart(uiTableColumn *c, int modelColumn, int expand) -{ - tablePart *part; - - part = [tablePart new]; - part.type = partImage; - part.imageColumn = modelColumn; - part.expand = expand; - [c->parts addObject:part]; -} - -void uiTableColumnAppendButtonPart(uiTableColumn *c, int modelColumn, int expand) -{ - tablePart *part; - - part = [tablePart new]; - part.type = partButton; - part.textColumn = modelColumn; - part.expand = expand; - part.editable = 1; // editable by default - [c->parts addObject:part]; -} - -void uiTableColumnAppendCheckboxPart(uiTableColumn *c, int modelColumn, int expand) -{ - tablePart *part; - - part = [tablePart new]; - part.type = partCheckbox; - part.valueColumn = modelColumn; - part.expand = expand; - part.editable = 1; // editable by default - [c->parts addObject:part]; -} - -void uiTableColumnAppendProgressBarPart(uiTableColumn *c, int modelColumn, int expand) -{ - tablePart *part; - - part = [tablePart new]; - part.type = partProgressBar; - part.valueColumn = modelColumn; - part.expand = expand; - [c->parts addObject:part]; -} - -void uiTableColumnPartSetEditable(uiTableColumn *c, int part, int editable) -{ - tablePart *p; - - p = (tablePart *) [c->parts objectAtIndex:part]; - p.editable = editable; -} - -void uiTableColumnPartSetTextColor(uiTableColumn *c, int part, int modelColumn) -{ - tablePart *p; - - p = (tablePart *) [c->parts objectAtIndex:part]; - p.textColorColumn = modelColumn; -} - uiDarwinControlAllDefaultsExceptDestroy(uiTable, sv) static void uiTableDestroy(uiControl *c) @@ -192,27 +117,10 @@ static void uiTableDestroy(uiControl *c) uiFreeControl(uiControl(t)); } -uiTableColumn *uiTableAppendColumn(uiTable *t, const char *name) -{ - uiTableColumn *c; - - c = uiprivNew(uiTableColumn); - c->c = [[tableColumn alloc] initWithIdentifier:@""]; - c->c.libui_col = c; - // via Interface Builder - [c->c setResizingMask:(NSTableColumnAutoresizingMask | NSTableColumnUserResizingMask)]; - // 10.10 adds -[NSTableColumn setTitle:]; before then we have to do this - [[c->c headerCell] setStringValue:uiprivToNSString(name)]; - // TODO is this sufficient? - [[c->c headerCell] setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]]; - c->parts = [NSMutableArray new]; - [t->tv addTableColumn:c->c]; - return c; -} - void uiTableSetRowBackgroundColorModelColumn(uiTable *t, int modelColumn) { t->backgroundColumn = modelColumn; + // TODO update all rows } uiTable *uiNewTable(uiTableModel *model) @@ -221,9 +129,9 @@ uiTable *uiNewTable(uiTableModel *model) uiprivScrollViewCreateParams p; uiDarwinNewControl(uiTable, t); + t->m = model; - t->tv = [[tableView alloc] initWithFrame:NSZeroRect]; - t->tv.libui_t = t; + t->tv = [[NSTableView alloc] initWithFrame:NSZeroRect]; [t->tv setDataSource:model->m]; [t->tv setDelegate:model->m]; diff --git a/darwin/tablecolumn.m b/darwin/tablecolumn.m index 01ff5b27..339d4a6a 100644 --- a/darwin/tablecolumn.m +++ b/darwin/tablecolumn.m @@ -316,6 +316,33 @@ struct textColumnCreateParams { @end +@interface uiprivTextImageCheckboxTableColumn : uiprivTableColumn { + struct textColumnCreateParams params; +} +- (id)initWithIdentifier:(NSString *)ident params:(struct textColumnCreateParams *)p; +@end + +@implementation uiprivTextImageCheckboxTableColumn + +- (id)initWithIdentifier:(NSString *)ident params:(struct textColumnCreateParams *)p +{ + self = [super initWithIdentifier:ident]; + if (self) + self->params = *p; + return self; +} + +- (uiprivColumnCellView *)uiprivMakeCellView +{ + uiprivColumnCellView *cv; + + cv = [[uiprivTextImageCheckboxTableCellView alloc] initWithFrame:NSZeroRect params:&(self->params)]; + [cv setIdentifier:[self identifier]]; + return cv; +} + +@end + @interface uiprivProgressBarTableCellView : uiprivTableCellView { uiTable *t; uiTableModel *m; @@ -380,6 +407,33 @@ struct textColumnCreateParams { @end +@interface uiprivProgressBarTableColumn : uiprivTableColumn { + int modelColumn; +} +- (id)initWithIdentifier:(NSString *)ident modelColumn:(int)mc; +@end + +@implementation uiprivProgressBarTableColumn + +- (id)initWithIdentifier:(NSString *)ident modelColumn:(int)mc +{ + self = [super initWithIdentifier:ident]; + if (self) + self->modelColumn = mc; + return self; +} + +- (uiprivColumnCellView *)uiprivMakeCellView +{ + uiprivColumnCellView *cv; + + cv = [[uiprivProgressBarTableCellView alloc] initWithFrame:NSZeroRect modelColumn:self->modelColumn]; + [cv setIdentifier:[self identifier]]; + return cv; +} + +@end + @interface uiprivButtonTableCellView : uiprivTableCellView { uiTable *t; uiTableModel *m; @@ -445,41 +499,169 @@ struct textColumnCreateParams { @end -void uiTableAppendTextColumn(uiTable *t, - const char *name, - int textModelColumn, - int textEditableModelColumn, - uiTableTextColumnOptionalParams *params); +@interface uiprivButtonTableColumn : uiprivTableColumn { + int modelColumn; + int editableColumn; +} +- (id)initWithIdentifier:(NSString *)ident modelColumn:(int)mc editableColumn:(int)ec; +@end -void uiTableAppendImageColumn(uiTable *t, - const char *name, - int imageModelColumn); +@implementation uiprivButtonTableColumn -void uiTableAppendImageTextColumn(uiTable *t, - const char *name, - int imageModelColumn, - int textModelColumn, - int textEditableModelColumn, - uiTableTextColumnOptionalParams *textParams); +- (id)initWithIdentifier:(NSString *)ident modelColumn:(int)mc editableColumn:(int)ec +{ + self = [super initWithIdentifier:ident]; + if (self) { + self->modelColumn = mc; + self->editableColumn = ec; + } + return self; +} -void uiTableAppendCheckboxColumn(uiTable *t, - const char *name, - int checkboxModelColumn, - int checkboxEditableModelColumn); +- (uiprivColumnCellView *)uiprivMakeCellView +{ + uiprivColumnCellView *cv; -void uiTableAppendCheckboxTextColumn(uiTable *t, - const char *name, - int checkboxModelColumn, - int checkboxEditableModelColumn, - int textModelColumn, - int textEditableModelColumn, - uiTableTextColumnOptionalParams *textParams); + cv = [[uiprivButtonTableCellView alloc] initWithFrame:NSZeroRect modelColumn:self->modelColumn editableColumn:self->editableColumn]; + [cv setIdentifier:[self identifier]]; + return cv; +} -void uiTableAppendProgressBarColumn(uiTable *t, - const char *name, - int progressModelColumn); +@end -void uiTableAppendButtonColumn(uiTable *t, - const char *name, - int buttonTextModelColumn, - int buttonClickableModelColumn); +void uiTableAppendTextColumn(uiTable *t, const char *name, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *params) +{ + struct textColumnCreateParams p; + uiprivTableColumn *col; + NSString *str; + + memset(&p, 0, sizeof (struct textColumnCreateParams)); + p.t = t; + p.m = t->m; + + p.makeTextField = YES; + p.textModelColumn = textModelColumn; + p.textEditableModelColumn = textEditableModelColumn; + if (params == NULL) + params = &defaultTextColumnOptionalParams; + p.textParams = *params; + + str = [NSString stringWithUTF8String:name]; + col = [[uiprivTextImageCheckboxTableColum alloc] initWithIdentifier:str params:&p]; + [col setTitle:str]; + return col; +} + +void uiTableAppendImageColumn(uiTable *t, const char *name, int imageModelColumn) +{ + struct textColumnCreateParams p; + uiprivTableColumn *col; + NSString *str; + + memset(&p, 0, sizeof (struct textColumnCreateParams)); + p.t = t; + p.m = t->m; + + p.makeImage = YES; + p.imageModelColumn = imageModelColumn; + + str = [NSString stringWithUTF8String:name]; + col = [[uiprivTextImageCheckboxTableColum alloc] initWithIdentifier:str params:&p]; + [col setTitle:str]; + return col; +} + +void uiTableAppendImageTextColumn(uiTable *t, const char *name, int imageModelColumn, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *textParams) +{ + struct textColumnCreateParams p; + uiprivTableColumn *col; + NSString *str; + + memset(&p, 0, sizeof (struct textColumnCreateParams)); + p.t = t; + p.m = t->m; + + p.makeTextField = YES; + p.textModelColumn = textModelColumn; + p.textEditableModelColumn = textEditableModelColumn; + if (params == NULL) + params = &defaultTextColumnOptionalParams; + p.textParams = *params; + + p.makeImage = YES; + p.imageModelColumn = imageModelColumn; + + str = [NSString stringWithUTF8String:name]; + col = [[uiprivTextImageCheckboxTableColum alloc] initWithIdentifier:str params:&p]; + [col setTitle:str]; + return col; +} + +void uiTableAppendCheckboxColumn(uiTable *t, const char *name, int checkboxModelColumn, int checkboxEditableModelColumn) +{ + struct textColumnCreateParams p; + uiprivTableColumn *col; + NSString *str; + + memset(&p, 0, sizeof (struct textColumnCreateParams)); + p.t = t; + p.m = t->m; + + p.makeCheckbox = YES; + p.checkboxModelColumn = checkboxModelColumn; + p.checkboxEditableColumn = checkboxEditableColumn; + + str = [NSString stringWithUTF8String:name]; + col = [[uiprivTextImageCheckboxTableColum alloc] initWithIdentifier:str params:&p]; + [col setTitle:str]; + return col; +} + +void uiTableAppendCheckboxTextColumn(uiTable *t, const char *name, int checkboxModelColumn, int checkboxEditableModelColumn, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *textParams) +{ + struct textColumnCreateParams p; + uiprivTableColumn *col; + NSString *str; + + memset(&p, 0, sizeof (struct textColumnCreateParams)); + p.t = t; + p.m = t->m; + + p.makeTextField = YES; + p.textModelColumn = textModelColumn; + p.textEditableModelColumn = textEditableModelColumn; + if (params == NULL) + params = &defaultTextColumnOptionalParams; + p.textParams = *params; + + p.makeCheckbox = YES; + p.checkboxModelColumn = checkboxModelColumn; + p.checkboxEditableColumn = checkboxEditableColumn; + + str = [NSString stringWithUTF8String:name]; + col = [[uiprivTextImageCheckboxTableColum alloc] initWithIdentifier:str params:&p]; + [col setTitle:str]; + return col; +} + +void uiTableAppendProgressBarColumn(uiTable *t, const char *name, int progressModelColumn) +{ + uiprivTableColumn *col; + NSString *str; + + str = [NSString stringWithUTF8String:name]; + col = [[uiprivProgressBarTableColum alloc] initWithIdentifier:str modelColumn:progressModelColumn]; + [col setTitle:str]; + return col; +} + +void uiTableAppendButtonColumn(uiTable *t, const char *name, int buttonTextModelColumn, int buttonClickableModelColumn) +{ + uiprivTableColumn *col; + NSString *str; + + str = [NSString stringWithUTF8String:name]; + col = [[uiprivButtonTableColum alloc] initWithIdentifier:str modelColumn:buttonTextModelColumn editableColumn:buttonClickableModelColumn]; + [col setTitle:str]; + return col; +} From 2b428d50f6fc5135c6618f82c194004ece358827 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 3 Jun 2018 23:06:44 -0400 Subject: [PATCH 1136/1329] And fixed build errors. Now I just need to rewrite the tester and try this out... --- common/tabledata.c | 4 +- darwin/CMakeLists.txt | 1 + darwin/table.h | 2 +- darwin/table.m | 18 +++---- darwin/tablecolumn.m | 114 +++++++++++++++++++++++------------------- 5 files changed, 75 insertions(+), 64 deletions(-) diff --git a/common/tabledata.c b/common/tabledata.c index 25710bcf..e66d4b4b 100644 --- a/common/tabledata.c +++ b/common/tabledata.c @@ -17,7 +17,7 @@ struct uiTableData { } u; }; -static uiTableData *newTableData(uiTableData type) +static uiTableData *newTableData(uiTableDataType type) { uiTableData *d; @@ -26,7 +26,7 @@ static uiTableData *newTableData(uiTableData type) return d; } -void uiFreeAttribute(uiTableData *a) +void uiFreeTableData(uiTableData *d) { switch (d->type) { case uiTableDataTypeString: diff --git a/darwin/CMakeLists.txt b/darwin/CMakeLists.txt index a262064e..bd7d576a 100644 --- a/darwin/CMakeLists.txt +++ b/darwin/CMakeLists.txt @@ -44,6 +44,7 @@ list(APPEND _LIBUI_SOURCES darwin/stddialogs.m darwin/tab.m darwin/table.m + darwin/tablecolumn.m darwin/text.m darwin/undocumented.m darwin/util.m diff --git a/darwin/table.h b/darwin/table.h index 0871a740..b38e9378 100644 --- a/darwin/table.h +++ b/darwin/table.h @@ -22,5 +22,5 @@ struct uiTable { - (void)uiprivUpdate:(NSInteger)row; @end @interface uiprivTableColumn : NSTableColumn -- (uiprivColumnCellView *)uiprivMakeCellView; +- (uiprivTableCellView *)uiprivMakeCellView; @end diff --git a/darwin/table.m b/darwin/table.m index b046c6db..5a7e7500 100644 --- a/darwin/table.m +++ b/darwin/table.m @@ -10,11 +10,11 @@ @implementation uiprivTableModel -- (id)initWithModel:(uiTableModel *)m +- (id)initWithModel:(uiTableModel *)model { self = [super init]; if (self) - self->libui_m = m; + self->m = model; return self; } @@ -27,9 +27,9 @@ { uiprivTableColumn *c = (uiprivTableColumn *) cc; // TODO consider renaming this type to uiprivTableCellView - uiprivColumnCellView *cv; + uiprivTableCellView *cv; - cv = (uiprivColumnCellView *) [tv makeViewWithIdentifier:[c identifier] owner:self]; + cv = (uiprivTableCellView *) [tv makeViewWithIdentifier:[c identifier] owner:self]; if (cv == nil) cv = [c uiprivMakeCellView]; [cv uiprivUpdate:row]; @@ -38,7 +38,7 @@ - (void)tableView:(NSTableView *)nstv didAddRowView:(NSTableRowView *)rv forRow:(NSInteger)row { - xx TODO set background color + // TODO set background color } @end @@ -79,16 +79,16 @@ void uiTableModelRowChanged(uiTableModel *m, int index) NSTableView *tv; NSTableRowView *rv; NSUInteger i, n; - uiprivTableColumnView *cv; + uiprivTableCellView *cv; for (tv in m->tables) { - rv = [tv rowViewForRow:index makeIfNecessary:NO]; + rv = [tv rowViewAtRow:index makeIfNecessary:NO]; if (rv != nil) { - xx TODO update colors + // TODO update colors } n = [[tv tableColumns] count]; for (i = 0; i < n; i++) { - cv = (uiprivTableCellView *) [tv viewForColumn:i row:index makeIfNecessary:NO]; + cv = (uiprivTableCellView *) [tv viewAtColumn:i row:index makeIfNecessary:NO]; if (cv != nil) [cv uiprivUpdate:index]; } diff --git a/darwin/tablecolumn.m b/darwin/tablecolumn.m index 339d4a6a..fe71c79e 100644 --- a/darwin/tablecolumn.m +++ b/darwin/tablecolumn.m @@ -58,6 +58,7 @@ static void layoutCellSubview(NSView *superview, NSView *subview, NSView *leadin - (uiprivTableCellView *)uiprivMakeCellView { [self doesNotRecognizeSelector:_cmd]; + return nil; // appease compiler } @end @@ -122,11 +123,11 @@ struct textColumnCreateParams { - (IBAction)uiprivOnCheckboxAction:(id)sender; @end -@implementation uiprivTextTableCellView +@implementation uiprivTextImageCheckboxTableCellView - (id)initWithFrame:(NSRect)r params:(struct textColumnCreateParams *)p { - self = [super initWithFrame:frame]; + self = [super initWithFrame:r]; if (self) { NSView *left; CGFloat leftConstant; @@ -151,7 +152,8 @@ struct textColumnCreateParams { left = nil; self->iv = nil; - if (p->makeImageView) { + // TODO rename to makeImageView + if (p->makeImage) { self->iv = [[NSImageView alloc] initWithFrame:NSZeroRect]; [self->iv setImageFrameStyle:NSImageFrameNone]; [self->iv setImageAlignment:NSImageAlignCenter]; @@ -239,11 +241,10 @@ struct textColumnCreateParams { - (void)uiprivUpdate:(NSInteger)row { uiTableData *data; - BOOL editable; - if (self->tv != nil) { + if (self->tf != nil) { NSString *str; - BOOL editable; + NSColor *color; data = (*(self->m->mh->CellValue))(self->m->mh, self->m, row, self->textModelColumn); str = uiprivToNSString(uiTableDataString(data)); @@ -263,7 +264,7 @@ struct textColumnCreateParams { } if (color == nil) color = [NSColor controlTextColor]; - [self->tf setColor:color]; + [self->tf setTextColor:color]; // we don't own color in ether case; don't release } if (self->iv != nil) { @@ -282,7 +283,7 @@ struct textColumnCreateParams { [self->cb setState:NSOffState]; uiFreeTableData(data); - [self->cb setEditable:isCellEditable(self->m, row, self->checkboxEditableColumn)]; + [self->cb setEnabled:isCellEditable(self->m, row, self->checkboxEditableColumn)]; } } @@ -332,9 +333,9 @@ struct textColumnCreateParams { return self; } -- (uiprivColumnCellView *)uiprivMakeCellView +- (uiprivTableCellView *)uiprivMakeCellView { - uiprivColumnCellView *cv; + uiprivTableCellView *cv; cv = [[uiprivTextImageCheckboxTableCellView alloc] initWithFrame:NSZeroRect params:&(self->params)]; [cv setIdentifier:[self identifier]]; @@ -408,26 +409,32 @@ struct textColumnCreateParams { @end @interface uiprivProgressBarTableColumn : uiprivTableColumn { + uiTable *t; + // TODO remove the need for this given t (or make t not require m, one of the two) + uiTableModel *m; int modelColumn; } -- (id)initWithIdentifier:(NSString *)ident modelColumn:(int)mc; +- (id)initWithIdentifier:(NSString *)ident table:(uiTable *)table model:(uiTableModel *)model modelColumn:(int)mc; @end @implementation uiprivProgressBarTableColumn -- (id)initWithIdentifier:(NSString *)ident modelColumn:(int)mc +- (id)initWithIdentifier:(NSString *)ident table:(uiTable *)table model:(uiTableModel *)model modelColumn:(int)mc { self = [super initWithIdentifier:ident]; - if (self) + if (self) { + self->t = table; + self->m = model; self->modelColumn = mc; + } return self; } -- (uiprivColumnCellView *)uiprivMakeCellView +- (uiprivTableCellView *)uiprivMakeCellView { - uiprivColumnCellView *cv; + uiprivTableCellView *cv; - cv = [[uiprivProgressBarTableCellView alloc] initWithFrame:NSZeroRect modelColumn:self->modelColumn]; + cv = [[uiprivProgressBarTableCellView alloc] initWithFrame:NSZeroRect table:self->t model:self->m modelColumn:self->modelColumn]; [cv setIdentifier:[self identifier]]; return cv; } @@ -445,7 +452,7 @@ struct textColumnCreateParams { - (IBAction)uiprivOnClicked:(id)sender; @end -@implementation uiprivProgressBarTableCellView +@implementation uiprivButtonTableCellView - (id)initWithFrame:(NSRect)r table:(uiTable *)table model:(uiTableModel *)model modelColumn:(int)mc editableColumn:(int)ec { @@ -473,8 +480,8 @@ struct textColumnCreateParams { - (void)dealloc { - [self->p release]; - self->p = nil; + [self->b release]; + self->b = nil; [super dealloc]; } @@ -482,17 +489,16 @@ struct textColumnCreateParams { { uiTableData *data; NSString *str; - BOOL editable; data = (*(self->m->mh->CellValue))(self->m->mh, self->m, row, self->modelColumn); str = uiprivToNSString(uiTableDataString(data)); uiFreeTableData(data); [self->b setTitle:str]; - [self->b setEditable:isCellEditable(self->m, row, self->editableColumn)]; + [self->b setEnabled:isCellEditable(self->m, row, self->editableColumn)]; } -- (id)uiprivOnClicked:(id)sender +- (IBAction)uiprivOnClicked:(id)sender { // TODO } @@ -500,29 +506,33 @@ struct textColumnCreateParams { @end @interface uiprivButtonTableColumn : uiprivTableColumn { + uiTable *t; + uiTableModel *m; int modelColumn; int editableColumn; } -- (id)initWithIdentifier:(NSString *)ident modelColumn:(int)mc editableColumn:(int)ec; +- (id)initWithIdentifier:(NSString *)ident table:(uiTable *)table model:(uiTableModel *)model modelColumn:(int)mc editableColumn:(int)ec; @end @implementation uiprivButtonTableColumn -- (id)initWithIdentifier:(NSString *)ident modelColumn:(int)mc editableColumn:(int)ec +- (id)initWithIdentifier:(NSString *)ident table:(uiTable *)table model:(uiTableModel *)model modelColumn:(int)mc editableColumn:(int)ec { self = [super initWithIdentifier:ident]; if (self) { + self->t = table; + self->m = model; self->modelColumn = mc; self->editableColumn = ec; } return self; } -- (uiprivColumnCellView *)uiprivMakeCellView +- (uiprivTableCellView *)uiprivMakeCellView { - uiprivColumnCellView *cv; + uiprivTableCellView *cv; - cv = [[uiprivButtonTableCellView alloc] initWithFrame:NSZeroRect modelColumn:self->modelColumn editableColumn:self->editableColumn]; + cv = [[uiprivButtonTableCellView alloc] initWithFrame:NSZeroRect table:self->t model:self->m modelColumn:self->modelColumn editableColumn:self->editableColumn]; [cv setIdentifier:[self identifier]]; return cv; } @@ -541,15 +551,15 @@ void uiTableAppendTextColumn(uiTable *t, const char *name, int textModelColumn, p.makeTextField = YES; p.textModelColumn = textModelColumn; - p.textEditableModelColumn = textEditableModelColumn; + p.textEditableColumn = textEditableModelColumn; if (params == NULL) params = &defaultTextColumnOptionalParams; p.textParams = *params; str = [NSString stringWithUTF8String:name]; - col = [[uiprivTextImageCheckboxTableColum alloc] initWithIdentifier:str params:&p]; + col = [[uiprivTextImageCheckboxTableColumn alloc] initWithIdentifier:str params:&p]; [col setTitle:str]; - return col; + [t->tv addTableColumn:col]; } void uiTableAppendImageColumn(uiTable *t, const char *name, int imageModelColumn) @@ -566,9 +576,9 @@ void uiTableAppendImageColumn(uiTable *t, const char *name, int imageModelColumn p.imageModelColumn = imageModelColumn; str = [NSString stringWithUTF8String:name]; - col = [[uiprivTextImageCheckboxTableColum alloc] initWithIdentifier:str params:&p]; + col = [[uiprivTextImageCheckboxTableColumn alloc] initWithIdentifier:str params:&p]; [col setTitle:str]; - return col; + [t->tv addTableColumn:col]; } void uiTableAppendImageTextColumn(uiTable *t, const char *name, int imageModelColumn, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *textParams) @@ -583,18 +593,18 @@ void uiTableAppendImageTextColumn(uiTable *t, const char *name, int imageModelCo p.makeTextField = YES; p.textModelColumn = textModelColumn; - p.textEditableModelColumn = textEditableModelColumn; - if (params == NULL) - params = &defaultTextColumnOptionalParams; - p.textParams = *params; + p.textEditableColumn = textEditableModelColumn; + if (textParams == NULL) + textParams = &defaultTextColumnOptionalParams; + p.textParams = *textParams; p.makeImage = YES; p.imageModelColumn = imageModelColumn; str = [NSString stringWithUTF8String:name]; - col = [[uiprivTextImageCheckboxTableColum alloc] initWithIdentifier:str params:&p]; + col = [[uiprivTextImageCheckboxTableColumn alloc] initWithIdentifier:str params:&p]; [col setTitle:str]; - return col; + [t->tv addTableColumn:col]; } void uiTableAppendCheckboxColumn(uiTable *t, const char *name, int checkboxModelColumn, int checkboxEditableModelColumn) @@ -609,12 +619,12 @@ void uiTableAppendCheckboxColumn(uiTable *t, const char *name, int checkboxModel p.makeCheckbox = YES; p.checkboxModelColumn = checkboxModelColumn; - p.checkboxEditableColumn = checkboxEditableColumn; + p.checkboxEditableColumn = checkboxEditableModelColumn; str = [NSString stringWithUTF8String:name]; - col = [[uiprivTextImageCheckboxTableColum alloc] initWithIdentifier:str params:&p]; + col = [[uiprivTextImageCheckboxTableColumn alloc] initWithIdentifier:str params:&p]; [col setTitle:str]; - return col; + [t->tv addTableColumn:col]; } void uiTableAppendCheckboxTextColumn(uiTable *t, const char *name, int checkboxModelColumn, int checkboxEditableModelColumn, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *textParams) @@ -629,19 +639,19 @@ void uiTableAppendCheckboxTextColumn(uiTable *t, const char *name, int checkboxM p.makeTextField = YES; p.textModelColumn = textModelColumn; - p.textEditableModelColumn = textEditableModelColumn; - if (params == NULL) - params = &defaultTextColumnOptionalParams; - p.textParams = *params; + p.textEditableColumn = textEditableModelColumn; + if (textParams == NULL) + textParams = &defaultTextColumnOptionalParams; + p.textParams = *textParams; p.makeCheckbox = YES; p.checkboxModelColumn = checkboxModelColumn; - p.checkboxEditableColumn = checkboxEditableColumn; + p.checkboxEditableColumn = checkboxEditableModelColumn; str = [NSString stringWithUTF8String:name]; - col = [[uiprivTextImageCheckboxTableColum alloc] initWithIdentifier:str params:&p]; + col = [[uiprivTextImageCheckboxTableColumn alloc] initWithIdentifier:str params:&p]; [col setTitle:str]; - return col; + [t->tv addTableColumn:col]; } void uiTableAppendProgressBarColumn(uiTable *t, const char *name, int progressModelColumn) @@ -650,9 +660,9 @@ void uiTableAppendProgressBarColumn(uiTable *t, const char *name, int progressMo NSString *str; str = [NSString stringWithUTF8String:name]; - col = [[uiprivProgressBarTableColum alloc] initWithIdentifier:str modelColumn:progressModelColumn]; + col = [[uiprivProgressBarTableColumn alloc] initWithIdentifier:str table:t model:t->m modelColumn:progressModelColumn]; [col setTitle:str]; - return col; + [t->tv addTableColumn:col]; } void uiTableAppendButtonColumn(uiTable *t, const char *name, int buttonTextModelColumn, int buttonClickableModelColumn) @@ -661,7 +671,7 @@ void uiTableAppendButtonColumn(uiTable *t, const char *name, int buttonTextModel NSString *str; str = [NSString stringWithUTF8String:name]; - col = [[uiprivButtonTableColum alloc] initWithIdentifier:str modelColumn:buttonTextModelColumn editableColumn:buttonClickableModelColumn]; + col = [[uiprivButtonTableColumn alloc] initWithIdentifier:str table:t model:t->m modelColumn:buttonTextModelColumn editableColumn:buttonClickableModelColumn]; [col setTitle:str]; - return col; + [t->tv addTableColumn:col]; } From 3bb050777b5fadaf867d9eb9237fd625fcedc18a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 3 Jun 2018 23:23:48 -0400 Subject: [PATCH 1137/1329] Started converting the test program. First column works fine, second segfaults. --- test/OLD_page16.c | 143 ++++++++++++++++++++++++++++++++++++++++++++++ test/page16.c | 64 +++++++++++---------- uitable.h | 8 --- 3 files changed, 177 insertions(+), 38 deletions(-) create mode 100644 test/OLD_page16.c diff --git a/test/OLD_page16.c b/test/OLD_page16.c new file mode 100644 index 00000000..80ac0139 --- /dev/null +++ b/test/OLD_page16.c @@ -0,0 +1,143 @@ +// 21 june 2016 +#include "test.h" + +static uiTableModelHandler mh; + +static int modelNumColumns(uiTableModelHandler *mh, uiTableModel *m) +{ + return 9; +} + +static uiTableModelColumnType modelColumnType(uiTableModelHandler *mh, uiTableModel *m, int column) +{ + if (column == 3 || column == 4) + return uiTableModelColumnColor; + if (column == 5) + return uiTableModelColumnImage; + if (column == 7 || column == 8) + return uiTableModelColumnInt; + return uiTableModelColumnString; +} + +static int modelNumRows(uiTableModelHandler *mh, uiTableModel *m) +{ + return 15; +} + +static uiImage *img[2]; +static char row9text[1024]; +static int yellowRow = -1; +static int checkStates[15]; + +static void *modelCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, int col) +{ + char buf[256]; + + if (col == 3) { + if (row == yellowRow) + return uiTableModelGiveColor(1, 1, 0, 1); + if (row == 3) + return uiTableModelGiveColor(1, 0, 0, 1); + if (row == 11) + return uiTableModelGiveColor(0, 0.5, 1, 0.5); + return NULL; + } + if (col == 4) { + if ((row % 2) == 1) + return uiTableModelGiveColor(0.5, 0, 0.75, 1); + return NULL; + } + if (col == 5) { + if (row < 8) + return img[0]; + return img[1]; + } + if (col == 7) + return uiTableModelGiveInt(checkStates[row]); + if (col == 8) { + if (row == 0) + return uiTableModelGiveInt(0); + if (row == 13) + return uiTableModelGiveInt(100); + if (row == 14) + return uiTableModelGiveInt(-1); + return uiTableModelGiveInt(50); + } + switch (col) { + case 0: + sprintf(buf, "Row %d", row); + break; + case 2: + if (row == 9) + return uiTableModelStrdup(row9text); + // fall through + case 1: + strcpy(buf, "Part"); + break; + case 6: + strcpy(buf, "Make Yellow"); + break; + } + return uiTableModelStrdup(buf); +} + +static void modelSetCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, int col, const void *val) +{ + if (row == 9 && col == 2) + strcpy(row9text, (const char *) val); + if (col == 6) + yellowRow = row; + if (col == 7) + checkStates[row] = uiTableModelTakeInt(val); +} + +uiBox *makePage16(void) +{ + uiBox *page16; + uiTableModel *m; + uiTable *t; + uiTableColumn *tc; + + img[0] = uiNewImage(16, 16); + appendImageNamed(img[0], "andlabs_16x16test_24june2016.png"); + appendImageNamed(img[0], "andlabs_32x32test_24june2016.png"); + img[1] = uiNewImage(16, 16); + appendImageNamed(img[1], "tango-icon-theme-0.8.90_16x16_x-office-spreadsheet.png"); + appendImageNamed(img[1], "tango-icon-theme-0.8.90_32x32_x-office-spreadsheet.png"); + + strcpy(row9text, "Part"); + + memset(checkStates, 0, 15 * sizeof (int)); + + page16 = newVerticalBox(); + + mh.NumColumns = modelNumColumns; + mh.ColumnType = modelColumnType; + mh.NumRows = modelNumRows; + mh.CellValue = modelCellValue; + mh.SetCellValue = modelSetCellValue; + m = uiNewTableModel(&mh); + + t = uiNewTable(m); + uiBoxAppend(page16, uiControl(t), 1); + + uiTableAppendTextColumn(t, "Column 1", 0); + + tc = uiTableAppendColumn(t, "Column 2"); + uiTableColumnAppendImagePart(tc, 5, 0); + uiTableColumnAppendTextPart(tc, 1, 0); + uiTableColumnAppendTextPart(tc, 2, 1); + uiTableColumnPartSetTextColor(tc, 1, 4); + uiTableColumnPartSetEditable(tc, 2, 1); + + uiTableSetRowBackgroundColorModelColumn(t, 3); + + tc = uiTableAppendColumn(t, "Buttons"); + uiTableColumnAppendCheckboxPart(tc, 7, 0); + uiTableColumnAppendButtonPart(tc, 6, 1); + + tc = uiTableAppendColumn(t, "Progress Bar"); + uiTableColumnAppendProgressBarPart(tc, 8, 0); + + return page16; +} diff --git a/test/page16.c b/test/page16.c index 80ac0139..6db33864 100644 --- a/test/page16.c +++ b/test/page16.c @@ -8,15 +8,15 @@ static int modelNumColumns(uiTableModelHandler *mh, uiTableModel *m) return 9; } -static uiTableModelColumnType modelColumnType(uiTableModelHandler *mh, uiTableModel *m, int column) +static uiTableDataType modelColumnType(uiTableModelHandler *mh, uiTableModel *m, int column) { if (column == 3 || column == 4) - return uiTableModelColumnColor; + return uiTableDataTypeColor; if (column == 5) - return uiTableModelColumnImage; + return uiTableDataTypeImage; if (column == 7 || column == 8) - return uiTableModelColumnInt; - return uiTableModelColumnString; + return uiTableDataTypeInt; + return uiTableDataTypeString; } static int modelNumRows(uiTableModelHandler *mh, uiTableModel *m) @@ -29,39 +29,39 @@ static char row9text[1024]; static int yellowRow = -1; static int checkStates[15]; -static void *modelCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, int col) +static uiTableData *modelCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, int col) { char buf[256]; if (col == 3) { if (row == yellowRow) - return uiTableModelGiveColor(1, 1, 0, 1); + return uiNewTableDataColor(1, 1, 0, 1); if (row == 3) - return uiTableModelGiveColor(1, 0, 0, 1); + return uiNewTableDataColor(1, 0, 0, 1); if (row == 11) - return uiTableModelGiveColor(0, 0.5, 1, 0.5); + return uiNewTableDataColor(0, 0.5, 1, 0.5); return NULL; } if (col == 4) { if ((row % 2) == 1) - return uiTableModelGiveColor(0.5, 0, 0.75, 1); + return uiNewTableDataColor(0.5, 0, 0.75, 1); return NULL; } if (col == 5) { if (row < 8) - return img[0]; - return img[1]; + return uiNewTableDataImage(img[0]); + return uiNewTableDataImage(img[1]); } if (col == 7) - return uiTableModelGiveInt(checkStates[row]); + return uiNewTableDataInt(checkStates[row]); if (col == 8) { if (row == 0) - return uiTableModelGiveInt(0); + return uiNewTableDataInt(0); if (row == 13) - return uiTableModelGiveInt(100); + return uiNewTableDataInt(100); if (row == 14) - return uiTableModelGiveInt(-1); - return uiTableModelGiveInt(50); + return uiNewTableDataInt(-1); + return uiNewTableDataInt(50); } switch (col) { case 0: @@ -69,7 +69,7 @@ static void *modelCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, i break; case 2: if (row == 9) - return uiTableModelStrdup(row9text); + return uiNewTableDataString(row9text); // fall through case 1: strcpy(buf, "Part"); @@ -78,17 +78,17 @@ static void *modelCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, i strcpy(buf, "Make Yellow"); break; } - return uiTableModelStrdup(buf); + return uiNewTableDataString(buf); } -static void modelSetCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, int col, const void *val) +static void modelSetCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, int col, const uiTableData *val) { if (row == 9 && col == 2) - strcpy(row9text, (const char *) val); + strcpy(row9text, uiTableDataString(val)); if (col == 6) yellowRow = row; if (col == 7) - checkStates[row] = uiTableModelTakeInt(val); + checkStates[row] = uiTableDataInt(val); } uiBox *makePage16(void) @@ -96,7 +96,7 @@ uiBox *makePage16(void) uiBox *page16; uiTableModel *m; uiTable *t; - uiTableColumn *tc; + uiTableTextColumnOptionalParams p; img[0] = uiNewImage(16, 16); appendImageNamed(img[0], "andlabs_16x16test_24june2016.png"); @@ -121,15 +121,18 @@ uiBox *makePage16(void) t = uiNewTable(m); uiBoxAppend(page16, uiControl(t), 1); - uiTableAppendTextColumn(t, "Column 1", 0); + uiTableAppendTextColumn(t, "Column 1", + 0, uiTableModelColumnNeverEditable, NULL); - tc = uiTableAppendColumn(t, "Column 2"); - uiTableColumnAppendImagePart(tc, 5, 0); - uiTableColumnAppendTextPart(tc, 1, 0); - uiTableColumnAppendTextPart(tc, 2, 1); - uiTableColumnPartSetTextColor(tc, 1, 4); - uiTableColumnPartSetEditable(tc, 2, 1); + memset(&p, 0, sizeof (uiTableTextColumnOptionalParams)); + p.ColorModelColumn = 4; + uiTableAppendImageTextColumn(t, "Column 2", + 5, + 1, uiTableModelColumnNeverEditable, &p); + uiTableAppendTextColumn(t, "Editable", + 2, uiTableModelColumnAlwaysEditable, NULL); +#if 0 uiTableSetRowBackgroundColorModelColumn(t, 3); tc = uiTableAppendColumn(t, "Buttons"); @@ -138,6 +141,7 @@ uiBox *makePage16(void) tc = uiTableAppendColumn(t, "Progress Bar"); uiTableColumnAppendProgressBarPart(tc, 8, 0); +#endif return page16; } diff --git a/uitable.h b/uitable.h index bd95cb53..077e77f9 100644 --- a/uitable.h +++ b/uitable.h @@ -47,14 +47,6 @@ struct uiTableModelHandler { void (*SetCellValue)(uiTableModelHandler *, uiTableModel *, int, int, const uiTableData *); }; -_UI_EXTERN void *uiTableModelStrdup(const char *str); -// TODO rename the strdup one to this too -_UI_EXTERN void *uiTableModelGiveColor(double r, double g, double b, double a); -_UI_EXTERN void *uiTableModelGiveInt(int i); -// TODO TakeString -// TODO add const -_UI_EXTERN int uiTableModelTakeInt(void *v); - _UI_EXTERN uiTableModel *uiNewTableModel(uiTableModelHandler *mh); _UI_EXTERN void uiFreeTableModel(uiTableModel *m); _UI_EXTERN void uiTableModelRowInserted(uiTableModel *m, int newIndex); From 2cfbb0144e9fae814267153e2c18cc9bc0634f14 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 4 Jun 2018 18:17:05 -0400 Subject: [PATCH 1138/1329] Fixed segfaults. Now to fix logic errors. (Technically one of the two changes here is a logic error too.) --- darwin/tablecolumn.m | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/darwin/tablecolumn.m b/darwin/tablecolumn.m index fe71c79e..95217c13 100644 --- a/darwin/tablecolumn.m +++ b/darwin/tablecolumn.m @@ -154,6 +154,8 @@ struct textColumnCreateParams { self->iv = nil; // TODO rename to makeImageView if (p->makeImage) { + self->imageModelColumn = p->imageModelColumn; + self->iv = [[NSImageView alloc] initWithFrame:NSZeroRect]; [self->iv setImageFrameStyle:NSImageFrameNone]; [self->iv setImageAlignment:NSImageAlignCenter]; @@ -173,6 +175,9 @@ struct textColumnCreateParams { self->cb = nil; if (p->makeCheckbox) { + self->checkboxModelColumn = p->checkboxModelColumn; + self->checkboxEditableColumn = p->checkboxEditableColumn; + self->cb = [[NSButton alloc] initWithFrame:NSZeroRect]; [self->cb setTitle:@""]; [self->cb setButtonType:NSSwitchButton]; @@ -258,9 +263,12 @@ struct textColumnCreateParams { double r, g, b, a; data = (*(self->m->mh->CellValue))(self->m->mh, self->m, row, self->textParams.ColorModelColumn); - uiTableDataColor(data, &r, &g, &b, &a); - uiFreeTableData(data); - color = [NSColor colorWithSRGBRed:r green:g blue:b alpha:a]; + // TODO document this is allowed + if (data != NULL) { + uiTableDataColor(data, &r, &g, &b, &a); + uiFreeTableData(data); + color = [NSColor colorWithSRGBRed:r green:g blue:b alpha:a]; + } } if (color == nil) color = [NSColor controlTextColor]; From 8a0ca54e93d6f29a3ef062b8a99a1fb31056d9b7 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 4 Jun 2018 20:03:05 -0400 Subject: [PATCH 1139/1329] Fixed constraint issues in tablecolumn.m for text-containing columns. I guess that one constraint function needs to go... --- darwin/tablecolumn.m | 125 ++++++++++++++++++++++++++++++------------- 1 file changed, 87 insertions(+), 38 deletions(-) diff --git a/darwin/tablecolumn.m b/darwin/tablecolumn.m index 95217c13..bbe11b4d 100644 --- a/darwin/tablecolumn.m +++ b/darwin/tablecolumn.m @@ -129,12 +129,11 @@ struct textColumnCreateParams { { self = [super initWithFrame:r]; if (self) { - NSView *left; - CGFloat leftConstant; - CGFloat leftTextConstant; + NSMutableArray *constraints; self->t = p->t; self->m = p->m; + constraints = [NSMutableArray new]; self->tf = nil; if (p->makeTextField) { @@ -146,10 +145,30 @@ struct textColumnCreateParams { // TODO set wrap and ellipsize modes? [self->tf setTarget:self]; [self->tf setAction:@selector(uiprivOnTextFieldAction:)]; + [self->tf setTranslatesAutoresizingMaskIntoConstraints:NO]; [self addSubview:self->tf]; - } - left = nil; + [constraints addObject:uiprivMkConstraint(self, NSLayoutAttributeLeading, + NSLayoutRelationEqual, + self->tf, NSLayoutAttributeLeading, + 1, -textColumnLeading, + @"uiTable cell text leading constraint")]; + [constraints addObject:uiprivMkConstraint(self, NSLayoutAttributeTop, + NSLayoutRelationEqual, + self->tf, NSLayoutAttributeTop, + 1, 0, + @"uiTable cell text top constraint")]; + [constraints addObject:uiprivMkConstraint(self, NSLayoutAttributeTrailing, + NSLayoutRelationEqual, + self->tf, NSLayoutAttributeTrailing, + 1, textColumnTrailing, + @"uiTable cell text trailing constraint")]; + [constraints addObject:uiprivMkConstraint(self, NSLayoutAttributeBottom, + NSLayoutRelationEqual, + self->tf, NSLayoutAttributeBottom, + 1, 0, + @"uiTable cell text bottom constraint")]; + } self->iv = nil; // TODO rename to makeImageView @@ -162,15 +181,42 @@ struct textColumnCreateParams { [self->iv setImageScaling:NSImageScaleProportionallyDown]; [self->iv setAnimates:NO]; [self->iv setEditable:NO]; - [self->iv addConstraint:uiprivMkConstraint(self->iv, NSLayoutAttributeWidth, + [self->iv setTranslatesAutoresizingMaskIntoConstraints:NO]; + [self addSubview:self->iv]; + + [constraints addObject:uiprivMkConstraint(self->iv, NSLayoutAttributeWidth, NSLayoutRelationEqual, self->iv, NSLayoutAttributeHeight, 1, 0, @"uiTable image squareness constraint")]; - [self addSubview:self->iv]; - left = self->iv; - leftConstant = imageColumnLeading; - leftTextConstant = imageTextColumnLeading; + if (self->tf != nil) { + [constraints addObject:uiprivMkConstraint(self, NSLayoutAttributeLeading, + NSLayoutRelationEqual, + self->iv, NSLayoutAttributeLeading, + 1, -imageColumnLeading, + @"uiTable cell image leading constraint")]; + [constraints replaceObjectAtIndex:0 + withObject:uiprivMkConstraint(self->iv, NSLayoutAttributeTrailing, + NSLayoutRelationEqual, + self->tf, NSLayoutAttributeLeading, + 1, -imageTextColumnLeading, + @"uiTable cell image-text spacing constraint")]; + } else + [constraints addObject:uiprivMkConstraint(self, NSLayoutAttributeCenterX, + NSLayoutRelationEqual, + self->iv, NSLayoutAttributeCenterX, + 1, 0, + @"uiTable cell image centering constraint")]; + [constraints addObject:uiprivMkConstraint(self, NSLayoutAttributeTop, + NSLayoutRelationEqual, + self->iv, NSLayoutAttributeTop, + 1, 0, + @"uiTable cell image top constraint")]; + [constraints addObject:uiprivMkConstraint(self, NSLayoutAttributeBottom, + NSLayoutRelationEqual, + self->iv, NSLayoutAttributeBottom, + 1, 0, + @"uiTable cell image bottom constraint")]; } self->cb = nil; @@ -185,37 +231,40 @@ struct textColumnCreateParams { [self->cb setBordered:NO]; [self->cb setTransparent:NO]; uiDarwinSetControlFont(self->cb, NSRegularControlSize); + [self->cb setTranslatesAutoresizingMaskIntoConstraints:NO]; [self addSubview:self->cb]; - left = self->cb; - leftConstant = checkboxColumnLeading; - leftTextConstant = checkboxTextColumnLeading; + + if (self->tf != nil) { + [constraints addObject:uiprivMkConstraint(self, NSLayoutAttributeLeading, + NSLayoutRelationEqual, + self->cb, NSLayoutAttributeLeading, + 1, -imageColumnLeading, + @"uiTable cell checkbox leading constraint")]; + [constraints replaceObjectAtIndex:0 + withObject:uiprivMkConstraint(self->cb, NSLayoutAttributeTrailing, + NSLayoutRelationEqual, + self->tf, NSLayoutAttributeLeading, + 1, -imageTextColumnLeading, + @"uiTable cell checkbox-text spacing constraint")]; + } else + [constraints addObject:uiprivMkConstraint(self, NSLayoutAttributeCenterX, + NSLayoutRelationEqual, + self->cb, NSLayoutAttributeCenterX, + 1, 0, + @"uiTable cell checkbox centering constraint")]; + [constraints addObject:uiprivMkConstraint(self, NSLayoutAttributeTop, + NSLayoutRelationEqual, + self->cb, NSLayoutAttributeTop, + 1, 0, + @"uiTable cell checkbox top constraint")]; + [constraints addObject:uiprivMkConstraint(self, NSLayoutAttributeBottom, + NSLayoutRelationEqual, + self->cb, NSLayoutAttributeBottom, + 1, 0, + @"uiTable cell checkbox bottom constraint")]; } - if (self->tf != nil && left == nil) - layoutCellSubview(self, self->tf, - self, textColumnLeading, - self, textColumnTrailing, - YES); - else if (self->tf != nil) { - layoutCellSubview(self, left, - self, leftConstant, - nil, 0, - NO); - layoutCellSubview(self, self->tf, - left, leftTextConstant, - self, textColumnTrailing, - YES); - } else { - layoutCellSubview(self, left, - nil, 0, - nil, 0, - NO); - [self addConstraint:uiprivMkConstraint(self, NSLayoutAttributeCenterX, - NSLayoutRelationEqual, - left, NSLayoutAttributeCenterX, - 1, 0, - @"uiTable image/checkbox centering constraint")]; - } + [self addConstraints:constraints]; // take advantage of NSTableCellView-provided accessibility features if (self->tf != nil) From 71e02a5c6e39e883f8a36d2f14ca6692d13e87fa Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 4 Jun 2018 20:09:09 -0400 Subject: [PATCH 1140/1329] And set up the constraints for the other column types. --- darwin/tablecolumn.m | 90 +++++++++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 39 deletions(-) diff --git a/darwin/tablecolumn.m b/darwin/tablecolumn.m index bbe11b4d..f8af5e11 100644 --- a/darwin/tablecolumn.m +++ b/darwin/tablecolumn.m @@ -12,37 +12,8 @@ #define checkboxColumnLeading imageColumnLeading #define progressBarColumnLeading imageColumnLeading #define progressBarColumnTrailing progressBarColumnLeading - -static void layoutCellSubview(NSView *superview, NSView *subview, NSView *leading, CGFloat leadingConstant, NSView *trailing, CGFloat trailingConstant, BOOL stretchy) -{ - [subview setTranslatesAutoresizingMaskIntoConstraints:NO]; - if (stretchy) - [subview setContentHuggingPriority:NSLayoutPriorityDefaultLow forOrientation:NSLayoutConstraintOrientationHorizontal]; - else - [subview setContentHuggingPriority:NSLayoutPriorityRequired forOrientation:NSLayoutConstraintOrientationHorizontal]; - if (leading != nil) - [superview addConstraint:uiprivMkConstraint(leading, NSLayoutAttributeLeading, - NSLayoutRelationEqual, - subview, NSLayoutAttributeLeading, - 1, -leadingConstant, - @"uiTable cell subview leading constraint")]; - [superview addConstraint:uiprivMkConstraint(superview, NSLayoutAttributeTop, - NSLayoutRelationEqual, - subview, NSLayoutAttributeTop, - 1, 0, - @"uiTable cell subview top constraint")]; - if (trailing != nil) - [superview addConstraint:uiprivMkConstraint(trailing, NSLayoutAttributeTrailing, - NSLayoutRelationEqual, - subview, NSLayoutAttributeLeading, - 1, trailingConstant, - @"uiTable cell subview trailing constraint")]; - [superview addConstraint:uiprivMkConstraint(superview, NSLayoutAttributeBottom, - NSLayoutRelationEqual, - subview, NSLayoutAttributeBottom, - 1, 0, - @"uiTable cell subview bottom constraint")]; -} +#define buttonColumnLeading imageColumnLeading +#define buttonColumnTrailing buttonColumnLeading @implementation uiprivTableCellView @@ -148,6 +119,7 @@ struct textColumnCreateParams { [self->tf setTranslatesAutoresizingMaskIntoConstraints:NO]; [self addSubview:self->tf]; + // TODO for all three controls: set hugging and compression resistance properly [constraints addObject:uiprivMkConstraint(self, NSLayoutAttributeLeading, NSLayoutRelationEqual, self->tf, NSLayoutAttributeLeading, @@ -424,10 +396,30 @@ struct textColumnCreateParams { [self->p setControlSize:NSRegularControlSize]; [self->p setBezeled:YES]; [self->p setStyle:NSProgressIndicatorBarStyle]; - layoutCellSubview(self, self->p, - self, progressBarColumnLeading, - self, progressBarColumnTrailing, - YES); + [self->p setTranslatesAutoresizingMaskIntoConstraints:NO]; + [self addSubview:self->p]; + + // TODO set hugging and compression resistance properly + [self addConstraint:uiprivMkConstraint(self, NSLayoutAttributeLeading, + NSLayoutRelationEqual, + self->p, NSLayoutAttributeLeading, + 1, -progressBarColumnLeading, + @"uiTable cell progressbar leading constraint")]; + [self addConstraint:uiprivMkConstraint(self, NSLayoutAttributeTop, + NSLayoutRelationEqual, + self->p, NSLayoutAttributeTop, + 1, 0, + @"uiTable cell progressbar top constraint")]; + [self addConstraint:uiprivMkConstraint(self, NSLayoutAttributeTrailing, + NSLayoutRelationEqual, + self->p, NSLayoutAttributeTrailing, + 1, progressBarColumnTrailing, + @"uiTable cell progressbar trailing constraint")]; + [self addConstraint:uiprivMkConstraint(self, NSLayoutAttributeBottom, + NSLayoutRelationEqual, + self->p, NSLayoutAttributeBottom, + 1, 0, + @"uiTable cell progressbar bottom constraint")]; } return self; } @@ -527,10 +519,30 @@ struct textColumnCreateParams { uiDarwinSetControlFont(self->b, NSRegularControlSize); [self->b setTarget:self]; [self->b setAction:@selector(uiprivOnClicked:)]; - layoutCellSubview(self, self->b, - self, progressBarColumnLeading, - self, progressBarColumnTrailing, - YES); + [self->b setTranslatesAutoresizingMaskIntoConstraints:NO]; + [self addSubview:self->b]; + + // TODO set hugging and compression resistance properly + [self addConstraint:uiprivMkConstraint(self, NSLayoutAttributeLeading, + NSLayoutRelationEqual, + self->b, NSLayoutAttributeLeading, + 1, -buttonColumnLeading, + @"uiTable cell button leading constraint")]; + [self addConstraint:uiprivMkConstraint(self, NSLayoutAttributeTop, + NSLayoutRelationEqual, + self->b, NSLayoutAttributeTop, + 1, 0, + @"uiTable cell button top constraint")]; + [self addConstraint:uiprivMkConstraint(self, NSLayoutAttributeTrailing, + NSLayoutRelationEqual, + self->b, NSLayoutAttributeTrailing, + 1, buttonColumnTrailing, + @"uiTable cell button trailing constraint")]; + [self addConstraint:uiprivMkConstraint(self, NSLayoutAttributeBottom, + NSLayoutRelationEqual, + self->b, NSLayoutAttributeBottom, + 1, 0, + @"uiTable cell button bottom constraint")]; } return self; } From 8c611cf95d2190165f64495c03f9078da1bcd2f6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 4 Jun 2018 20:13:35 -0400 Subject: [PATCH 1141/1329] And added the rest of the column types to the tester. --- test/page16.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/test/page16.c b/test/page16.c index 6db33864..434cad60 100644 --- a/test/page16.c +++ b/test/page16.c @@ -134,14 +134,15 @@ uiBox *makePage16(void) #if 0 uiTableSetRowBackgroundColorModelColumn(t, 3); - - tc = uiTableAppendColumn(t, "Buttons"); - uiTableColumnAppendCheckboxPart(tc, 7, 0); - uiTableColumnAppendButtonPart(tc, 6, 1); - - tc = uiTableAppendColumn(t, "Progress Bar"); - uiTableColumnAppendProgressBarPart(tc, 8, 0); #endif + uiTableAppendCheckboxColumn(t, "Checkboxes", + 7, uiTableModelColumnAlwaysEditable); + uiTableAppendButtonColumn(t, "Buttons", + 6, uiTableModelColumnAlwaysEditable); + + uiTableAppendProgressBarColumn(t, "Progress Bar", + 8); + return page16; } From 3fa15d5277c4299e1b2aefe5100c74463c2d6ba5 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 4 Jun 2018 20:17:15 -0400 Subject: [PATCH 1142/1329] Fixed graphical glitches in the OS X Table. Finally. SOMEHOW. --- darwin/table.m | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/darwin/table.m b/darwin/table.m index 5a7e7500..5d5d8d5e 100644 --- a/darwin/table.m +++ b/darwin/table.m @@ -161,6 +161,11 @@ uiTable *uiNewTable(uiTableModel *model) p.VScroll = YES; t->sv = uiprivMkScrollView(&p, &(t->d)); + // TODO WHY DOES THIS REMOVE ALL GRAPHICAL GLITCHES? + // I got the idea from http://jwilling.com/blog/optimized-nstableview-scrolling/ but that was on an unrelated problem I didn't seem to have (although I have small-ish tables to start with) + // I don't get layer-backing... am I supposed to layer-back EVERYTHING manually? I need to check Interface Builder again... + [t->sv setWantsLayer:YES]; + t->backgroundColumn = -1; return t; From 0a6a781be75a458fbbd4ca2f683fc080fa0a1468 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 4 Jun 2018 22:14:05 -0400 Subject: [PATCH 1143/1329] And implemented row background colors and button clicks. OS X implementation done for now! --- darwin/table.m | 63 +++++++++++++++++++++++++++++++++++++++----- darwin/tablecolumn.m | 7 ++++- test/page16.c | 11 +++++--- 3 files changed, 70 insertions(+), 11 deletions(-) diff --git a/darwin/table.m b/darwin/table.m index 5d5d8d5e..1d5093f9 100644 --- a/darwin/table.m +++ b/darwin/table.m @@ -8,6 +8,56 @@ - (id)initWithModel:(uiTableModel *)model; @end +// TODO we really need to clean up the sharing of the table and model variables... +@interface uiprivTableView : NSTableView { + uiTable *uiprivT; + uiTableModel *uiprivM; +} +- (id)initWithFrame:(NSRect)r uiprivT:(uiTable *)t uiprivM:(uiTableModel *)m; +@end + +@implementation uiprivTableView + +- (id)initWithFrame:(NSRect)r uiprivT:(uiTable *)t uiprivM:(uiTableModel *)m +{ + self = [super initWithFrame:r]; + if (self) { + self->uiprivT = t; + self->uiprivM = m; + } + return self; +} + +// TODO is this correct for overflow scrolling? +static void setBackgroundColor(uiprivTableView *t, NSTableRowView *rv, NSInteger row) +{ + uiTableData *data; + NSColor *color; + double r, g, b, a; + + if (t->uiprivT->backgroundColumn == -1) + return; + data = (*(t->uiprivM->mh->CellValue))(t->uiprivM->mh, t->uiprivM, row, t->uiprivT->backgroundColumn); + if (data != NULL) { + uiTableDataColor(data, &r, &g, &b, &a); + uiFreeTableData(data); + color = [NSColor colorWithSRGBRed:r green:g blue:b alpha:a]; + } else { + NSArray *colors; + NSInteger index; + + // this usage is primarily a guess; hopefully it is correct for the non-two color case... (TODO) + // it does seem to be correct for the two-color case, judging from comparing against the value of backgroundColor before changing it (and no, nil does not work; it just sets to white) + colors = [NSColor controlAlternatingRowBackgroundColors]; + index = row % [colors count]; + color = (NSColor *) [colors objectAtIndex:index]; + } + [rv setBackgroundColor:color]; + // color is autoreleased in all cases +} + +@end + @implementation uiprivTableModel - (id)initWithModel:(uiTableModel *)model @@ -36,9 +86,9 @@ return cv; } -- (void)tableView:(NSTableView *)nstv didAddRowView:(NSTableRowView *)rv forRow:(NSInteger)row +- (void)tableView:(NSTableView *)tv didAddRowView:(NSTableRowView *)rv forRow:(NSInteger)row { - // TODO set background color + setBackgroundColor((uiprivTableView *) tv, rv, row); } @end @@ -76,16 +126,15 @@ void uiTableModelRowInserted(uiTableModel *m, int newIndex) void uiTableModelRowChanged(uiTableModel *m, int index) { - NSTableView *tv; + uiprivTableView *tv; NSTableRowView *rv; NSUInteger i, n; uiprivTableCellView *cv; for (tv in m->tables) { rv = [tv rowViewAtRow:index makeIfNecessary:NO]; - if (rv != nil) { - // TODO update colors - } + if (rv != nil) + setBackgroundColor(tv, rv, index); n = [[tv tableColumns] count]; for (i = 0; i < n; i++) { cv = (uiprivTableCellView *) [tv viewAtColumn:i row:index makeIfNecessary:NO]; @@ -131,7 +180,7 @@ uiTable *uiNewTable(uiTableModel *model) uiDarwinNewControl(uiTable, t); t->m = model; - t->tv = [[NSTableView alloc] initWithFrame:NSZeroRect]; + t->tv = [[uiprivTableView alloc] initWithFrame:NSZeroRect uiprivT:t uiprivM:t->m]; [t->tv setDataSource:model->m]; [t->tv setDelegate:model->m]; diff --git a/darwin/tablecolumn.m b/darwin/tablecolumn.m index f8af5e11..531137f0 100644 --- a/darwin/tablecolumn.m +++ b/darwin/tablecolumn.m @@ -569,7 +569,12 @@ struct textColumnCreateParams { - (IBAction)uiprivOnClicked:(id)sender { - // TODO + NSInteger row; + + row = [self->t->tv rowForView:self->b]; + (*(self->m->mh->SetCellValue))(self->m->mh, self->m, + row, self->modelColumn, NULL); + // TODO document we DON'T update the cell after doing this } @end diff --git a/test/page16.c b/test/page16.c index 434cad60..d00bd39c 100644 --- a/test/page16.c +++ b/test/page16.c @@ -85,8 +85,15 @@ static void modelSetCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, { if (row == 9 && col == 2) strcpy(row9text, uiTableDataString(val)); - if (col == 6) + if (col == 6) { + int prevYellowRow; + + prevYellowRow = yellowRow; yellowRow = row; + if (prevYellowRow != -1) + uiTableModelRowChanged(m, prevYellowRow); + uiTableModelRowChanged(m, yellowRow); + } if (col == 7) checkStates[row] = uiTableDataInt(val); } @@ -132,9 +139,7 @@ uiBox *makePage16(void) uiTableAppendTextColumn(t, "Editable", 2, uiTableModelColumnAlwaysEditable, NULL); -#if 0 uiTableSetRowBackgroundColorModelColumn(t, 3); -#endif uiTableAppendCheckboxColumn(t, "Checkboxes", 7, uiTableModelColumnAlwaysEditable); From 3c063c71d5f29539a66b99843e365c1115e81e7d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 4 Jun 2018 22:15:05 -0400 Subject: [PATCH 1144/1329] And one more TODO before we move on. --- darwin/tablecolumn.m | 1 + 1 file changed, 1 insertion(+) diff --git a/darwin/tablecolumn.m b/darwin/tablecolumn.m index 531137f0..dbc85360 100644 --- a/darwin/tablecolumn.m +++ b/darwin/tablecolumn.m @@ -327,6 +327,7 @@ struct textColumnCreateParams { row, self->textModelColumn, data); uiFreeTableData(data); // always refresh the value in case the model rejected it + // TODO document that we do this, but not for the whole row (or decide to do both, or do neither...) [self uiprivUpdate:row]; } From 7a5577db947aa28382ccda56ce197c99f3419724 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 4 Jun 2018 23:28:46 -0400 Subject: [PATCH 1145/1329] Deleted now-irrelevant code from OLD_table.m. --- darwin/OLD_table.m | 543 --------------------------------------------- 1 file changed, 543 deletions(-) diff --git a/darwin/OLD_table.m b/darwin/OLD_table.m index 66666863..18231f55 100644 --- a/darwin/OLD_table.m +++ b/darwin/OLD_table.m @@ -2,519 +2,24 @@ #import "uipriv_darwin.h" // TODOs -// - initial state of table view is off // - header cell seems off // - background color shows up for a line or two below selection // - editable NSTextFields have no intrinsic width -// - changing a part property does not refresh views // - is the Y position of checkbox cells correct? -// - progressbars appear ABOVE the table header -// - threaded animation (which was known to have some issues: https://stackoverflow.com/questions/18142801/nstableview-nsprogressindicator-flickering-issues) is NOT the cause; happens regardless of setting (or does the setting not stick? TODO) - -// LONGTERM -// - reuse row views instead of creating a new one each time - -@interface tableModel : NSObject { - uiTableModel *libui_m; -} -- (id)initWithModel:(uiTableModel *)m; -- (IBAction)onAction:(id)sender; -@end - -enum { - partText, - partImage, - partButton, - partCheckbox, - partProgressBar, -}; - -@interface tablePart : NSObject -@property int type; -@property int textColumn; -@property int textColorColumn; -@property int imageColumn; -@property int valueColumn; -@property int expand; -@property int editable; -- (NSView *)mkView:(uiTableModel *)m row:(int)row; -@end - -@interface tableColumn : NSTableColumn -@property uiTableColumn *libui_col; -@end - -@interface tableView : NSTableView -@property uiTable *libui_t; -@end - -struct uiTableModel { - uiTableModelHandler *mh; - tableModel *m; - NSMutableArray *tables; -}; - -struct uiTableColumn { - tableColumn *c; - NSMutableArray *parts; -}; - -struct uiTable { - uiDarwinControl c; - NSScrollView *sv; - tableView *tv; - uiprivScrollViewData *d; - int backgroundColumn; -}; - -@implementation tableModel - -- (id)initWithModel:(uiTableModel *)m -{ - self = [super init]; - if (self) - self->libui_m = m; - return self; -} - -- (NSInteger)numberOfRowsInTableView:(NSTableView *)tv -{ - uiTableModelHandler *mh = self->libui_m->mh; - - return (*(mh->NumRows))(mh, self->libui_m); -} - -// these are according to Interface Builder -#define xleft 2 -#define xmiddle 7 /* between images and text, anyway; let's just use it for everything to be simpler */ -#define xright 3 - - - (NSView *)tableView:(NSTableView *)tv viewForTableColumn:(NSTableColumn *)cc row:(NSInteger)row -{ - NSTableCellView *v; - tableColumn *c = (tableColumn *) cc; - tablePart *part; - NSMutableArray *views; - NSView *view, *prev; - - v = [[NSTableCellView alloc] initWithFrame:NSZeroRect]; - - views = [NSMutableArray new]; - for (part in c.libui_col->parts) - [views addObject:[part mkView:self->libui_m row:row]]; - if ([views count] == 0) // empty (TODO allow?) - goto done; - - // add to v and arrange horizontally - prev = nil; - for (view in views) { - [v addSubview:view]; - // TODO set [v imageView] and [v textField] as appropriate? - if (prev == nil) { // first view - [v addConstraint:uiprivMkConstraint(v, NSLayoutAttributeLeading, - NSLayoutRelationEqual, - view, NSLayoutAttributeLeading, - 1, -xleft, - @"uiTableColumn first part horizontal constraint")]; - prev = view; - continue; - } - [v addConstraint:uiprivMkConstraint(prev, NSLayoutAttributeTrailing, - NSLayoutRelationEqual, - view, NSLayoutAttributeLeading, - 1, -xmiddle, - @"uiTableColumn middle horizontal constraint")]; - prev = view; - } - [v addConstraint:uiprivMkConstraint(prev, NSLayoutAttributeTrailing, - NSLayoutRelationEqual, - v, NSLayoutAttributeTrailing, - 1, -xright, - @"uiTableColumn last part horizontal constraint")]; - - // and vertically - for (view in views) { - [v addConstraint:uiprivMkConstraint(view, NSLayoutAttributeCenterY, - NSLayoutRelationEqual, - v, NSLayoutAttributeCenterY, - 1, 0, - @"uiTableColumn part vertical constraint")]; - // TODO avoid the need for this hack - if ([view isKindOfClass:[NSImageView class]]) - [v addConstraint:uiprivMkConstraint(view, NSLayoutAttributeTop, - NSLayoutRelationEqual, - v, NSLayoutAttributeTop, - 1, 0, - @"uiTableColumn part vertical top constraint")]; - } - -done: - [views release]; - [v setTranslatesAutoresizingMaskIntoConstraints:NO]; - // TODO autorelease? - return v; -} - -- (void)tableView:(NSTableView *)nstv didAddRowView:(NSTableRowView *)rv forRow:(NSInteger)row -{ - uiTableModel *m = self->libui_m; - tableView *tv = (tableView *) nstv; - uiTable *t = tv.libui_t; - NSColor *color; - - if (t->backgroundColumn == -1) - return; - color = (NSColor *) ((*(m->mh->CellValue))(m->mh, m, row, t->backgroundColumn)); - if (color == nil) - return; - [rv setBackgroundColor:color]; - // TODO autorelease color? or release it? -} - -- (IBAction)onAction:(id)sender -{ - uiTableModel *m = self->libui_m; - NSView *view = (NSView *) sender; - NSTableView *tv; - NSInteger row; - const void *data; - - row = -1; - for (tv in m->tables) { - row = [tv rowForView:view]; - if (row != -1) - break; - } - if (row == -1) - uiprivImplBug("table model action triggered on view with no associated table"); - - if ([view isKindOfClass:[NSTextField class]]) - data = [[((NSTextField *) view) stringValue] UTF8String]; - else if ([view isKindOfClass:[NSButton class]]) { - NSButton *b; - - b = (NSButton *) view; -// if ([b buttonType] == NSSwitchButton) - data = uiTableModelGiveInt([b state] == NSOnState); -// TODO there is no buttonType getter -if(1); else - data = NULL; - } else - uiprivImplBug("table model editing action triggered on non-editable view"); - - // note the use of [view tag] — we need the model column, which we store in the view tag for relevant views below - (*(m->mh->SetCellValue))(m->mh, m, - row, [view tag], - data); - // always refresh the value in case the model rejected it - // TODO only affect tv? - uiTableModelRowChanged(m, row); -} - -@end @implementation tablePart -- (id)init -{ - self = [super init]; - if (self) { - self.textColumn = -1; - self.textColorColumn = -1; - } - return self; -} - - (NSView *)mkView:(uiTableModel *)m row:(int)row { - void *data; - NSString *str; - NSView *view; - NSTextField *tf; - NSImageView *iv; - NSButton *b; - NSProgressIndicator *p; - int value; - - switch (self.type) { - case partText: - data = (*(m->mh->CellValue))(m->mh, m, row, self.textColumn); - str = uiprivToNSString((char *) data); - uiprivFree(data); - tf = uiprivNewLabel(str); - // TODO set wrap and ellipsize modes? - if (self.textColorColumn != -1) { - NSColor *color; - - color = (NSColor *) ((*(m->mh->CellValue))(m->mh, m, row, self.textColorColumn)); - if (color != nil) - [tf setTextColor:color]; - // TODO release color - } - if (self.editable) { - [tf setEditable:YES]; - [tf setTarget:m->m]; - [tf setAction:@selector(onAction:)]; - } - [tf setTag:self.textColumn]; - view = tf; - break; - case partImage: - data = (*(m->mh->CellValue))(m->mh, m, row, self.imageColumn); - iv = [[NSImageView alloc] initWithFrame:NSZeroRect]; - [iv setImage:uiprivImageNSImage((uiImage *) data)]; - [iv setImageFrameStyle:NSImageFrameNone]; - [iv setImageAlignment:NSImageAlignCenter]; - [iv setImageScaling:NSImageScaleProportionallyDown]; - [iv setAnimates:NO]; - [iv setEditable:NO]; - [iv addConstraint:uiprivMkConstraint(iv, NSLayoutAttributeWidth, - NSLayoutRelationEqual, - iv, NSLayoutAttributeHeight, - 1, 0, - @"uiTable image squareness constraint")]; - [iv setTag:self.imageColumn]; - view = iv; - break; - case partButton: - // TODO buttons get clipped - data = (*(m->mh->CellValue))(m->mh, m, row, self.textColumn); - str = uiprivToNSString((char *) data); - b = [[NSButton alloc] initWithFrame:NSZeroRect]; - [b setTitle:str]; - [b setButtonType:NSMomentaryPushInButton]; - [b setBordered:YES]; - [b setBezelStyle:NSRoundRectBezelStyle]; - uiDarwinSetControlFont(b, NSRegularControlSize); - if (self.editable) { - [b setTarget:m->m]; - [b setAction:@selector(onAction:)]; - } else - [b setEnabled:NO]; - [b setTag:self.textColumn]; - view = b; - break; - case partCheckbox: - data = (*(m->mh->CellValue))(m->mh, m, row, self.valueColumn); - b = [[NSButton alloc] initWithFrame:NSZeroRect]; - [b setTitle:@""]; - [b setButtonType:NSSwitchButton]; - // doesn't seem to have an associated bezel style - [b setBordered:NO]; - [b setTransparent:NO]; - uiDarwinSetControlFont(b, NSRegularControlSize); - if (uiTableModelTakeInt(data) != 0) - [b setState:NSOnState]; - else - [b setState:NSOffState]; - if (self.editable) { - [b setTarget:m->m]; - [b setAction:@selector(onAction:)]; - } else - [b setEnabled:NO]; - [b setTag:self.valueColumn]; - view = b; - break; - case partProgressBar: - data = (*(m->mh->CellValue))(m->mh, m, row, self.valueColumn); - value = uiTableModelTakeInt(data); - // TODO no intrinsic width - p = [[NSProgressIndicator alloc] initWithFrame:NSZeroRect]; - [p setControlSize:NSRegularControlSize]; - [p setBezeled:YES]; - [p setStyle:NSProgressIndicatorBarStyle]; - if (value == -1) { - [p setIndeterminate:YES]; - [p startAnimation:p]; - } else if (value == 100) { - [p setIndeterminate:NO]; - [p setMaxValue:101]; - [p setDoubleValue:101]; - [p setDoubleValue:100]; - [p setMaxValue:100]; - } else { - [p setIndeterminate:NO]; - [p setDoubleValue:(value + 1)]; - [p setDoubleValue:value]; - } - view = p; - break; - } - // if stretchy, don't hug, otherwise hug forcibly if (self.expand) [view setContentHuggingPriority:NSLayoutPriorityDefaultLow forOrientation:NSLayoutConstraintOrientationHorizontal]; else [view setContentHuggingPriority:NSLayoutPriorityRequired forOrientation:NSLayoutConstraintOrientationHorizontal]; - [view setTranslatesAutoresizingMaskIntoConstraints:NO]; - // TODO autorelease? - return view; } @end -@implementation tableColumn -@end - -@implementation tableView -@end - -void *uiTableModelStrdup(const char *str) -{ - // TODO don't we have this already? - char *dup; - - dup = (char *) uiprivAlloc((strlen(str) + 1) * sizeof (char), "char[]"); - strcpy(dup, str); - return dup; -} - -uiTableModel *uiNewTableModel(uiTableModelHandler *mh) -{ - uiTableModel *m; - - m = uiprivNew(uiTableModel); - m->mh = mh; - m->m = [[tableModel alloc] initWithModel:m]; - m->tables = [NSMutableArray new]; - return m; -} - -void *uiTableModelGiveColor(double r, double g, double b, double a) -{ - return [[NSColor colorWithSRGBRed:r green:g blue:b alpha:a] retain]; -} - -void uiFreeTableModel(uiTableModel *m) -{ - if ([m->tables count] != 0) - uiprivUserBug("You cannot free a uiTableModel while uiTables are using it."); - [m->tables release]; - [m->m release]; - uiprivFree(m); -} - -void uiTableModelRowInserted(uiTableModel *m, int newIndex) -{ - NSTableView *tv; - NSIndexSet *set; - - set = [NSIndexSet indexSetWithIndex:newIndex]; - for (tv in m->tables) - [tv insertRowsAtIndexes:set withAnimation:NSTableViewAnimationEffectNone]; - // set is autoreleased -} - -void uiTableModelRowChanged(uiTableModel *m, int index) -{ - NSTableView *tv; - NSIndexSet *set, *cols; - - set = [NSIndexSet indexSetWithIndex:index]; - for (tv in m->tables) { - cols = [[NSIndexSet alloc] initWithIndexesInRange:NSMakeRange(0, [[tv tableColumns] count])]; - [tv reloadDataForRowIndexes:set columnIndexes:cols]; - // TODO this isn't enough - [cols release]; - } - // set is autoreleased -} - -void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) -{ - NSTableView *tv; - NSIndexSet *set; - - set = [NSIndexSet indexSetWithIndex:oldIndex]; - for (tv in m->tables) - [tv removeRowsAtIndexes:set withAnimation:NSTableViewAnimationEffectNone]; - // set is autoreleased -} - -void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand) -{ - tablePart *part; - - part = [tablePart new]; - part.type = partText; - part.textColumn = modelColumn; - part.expand = expand; - [c->parts addObject:part]; -} - -void uiTableColumnAppendImagePart(uiTableColumn *c, int modelColumn, int expand) -{ - tablePart *part; - - part = [tablePart new]; - part.type = partImage; - part.imageColumn = modelColumn; - part.expand = expand; - [c->parts addObject:part]; -} - -void uiTableColumnAppendButtonPart(uiTableColumn *c, int modelColumn, int expand) -{ - tablePart *part; - - part = [tablePart new]; - part.type = partButton; - part.textColumn = modelColumn; - part.expand = expand; - part.editable = 1; // editable by default - [c->parts addObject:part]; -} - -void uiTableColumnAppendCheckboxPart(uiTableColumn *c, int modelColumn, int expand) -{ - tablePart *part; - - part = [tablePart new]; - part.type = partCheckbox; - part.valueColumn = modelColumn; - part.expand = expand; - part.editable = 1; // editable by default - [c->parts addObject:part]; -} - -void uiTableColumnAppendProgressBarPart(uiTableColumn *c, int modelColumn, int expand) -{ - tablePart *part; - - part = [tablePart new]; - part.type = partProgressBar; - part.valueColumn = modelColumn; - part.expand = expand; - [c->parts addObject:part]; -} - -void uiTableColumnPartSetEditable(uiTableColumn *c, int part, int editable) -{ - tablePart *p; - - p = (tablePart *) [c->parts objectAtIndex:part]; - p.editable = editable; -} - -void uiTableColumnPartSetTextColor(uiTableColumn *c, int part, int modelColumn) -{ - tablePart *p; - - p = (tablePart *) [c->parts objectAtIndex:part]; - p.textColorColumn = modelColumn; -} - -uiDarwinControlAllDefaultsExceptDestroy(uiTable, sv) - -static void uiTableDestroy(uiControl *c) -{ - uiTable *t = uiTable(c); - - // TODO - [t->sv release]; - uiFreeControl(uiControl(t)); -} - uiTableColumn *uiTableAppendColumn(uiTable *t, const char *name) { uiTableColumn *c; @@ -532,51 +37,3 @@ uiTableColumn *uiTableAppendColumn(uiTable *t, const char *name) [t->tv addTableColumn:c->c]; return c; } - -void uiTableSetRowBackgroundColorModelColumn(uiTable *t, int modelColumn) -{ - t->backgroundColumn = modelColumn; -} - -uiTable *uiNewTable(uiTableModel *model) -{ - uiTable *t; - uiprivScrollViewCreateParams p; - - uiDarwinNewControl(uiTable, t); - - t->tv = [[tableView alloc] initWithFrame:NSZeroRect]; - t->tv.libui_t = t; - - [t->tv setDataSource:model->m]; - [t->tv setDelegate:model->m]; - [t->tv reloadData]; - [model->tables addObject:t->tv]; - - // TODO is this sufficient? - [t->tv setAllowsColumnReordering:NO]; - [t->tv setAllowsColumnResizing:YES]; - [t->tv setAllowsMultipleSelection:NO]; - [t->tv setAllowsEmptySelection:YES]; - [t->tv setAllowsColumnSelection:NO]; - [t->tv setUsesAlternatingRowBackgroundColors:YES]; - [t->tv setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleRegular]; - [t->tv setGridStyleMask:NSTableViewGridNone]; - [t->tv setAllowsTypeSelect:YES]; - // TODO floatsGroupRows — do we even allow group rows? - - memset(&p, 0, sizeof (uiprivScrollViewCreateParams)); - p.DocumentView = t->tv; - // this is what Interface Builder sets it to - // TODO verify - p.BackgroundColor = [NSColor colorWithCalibratedWhite:1.0 alpha:1.0]; - p.DrawsBackground = YES; - p.Bordered = YES; - p.HScroll = YES; - p.VScroll = YES; - t->sv = uiprivMkScrollView(&p, &(t->d)); - - t->backgroundColumn = -1; - - return t; -} From 94fa10b35ebc6908683370cbe1f511b2fabdf8f1 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 4 Jun 2018 23:39:52 -0400 Subject: [PATCH 1146/1329] Started rewriting GTK+ uiTable. Did uiTableModel first. --- unix/OLD_table.c | 406 +++++++++++++++++++++++++++++++++++++++++++++++ unix/table.c | 51 +++--- 2 files changed, 432 insertions(+), 25 deletions(-) create mode 100644 unix/OLD_table.c diff --git a/unix/OLD_table.c b/unix/OLD_table.c new file mode 100644 index 00000000..3774345c --- /dev/null +++ b/unix/OLD_table.c @@ -0,0 +1,406 @@ +// 26 june 2016 +#include "uipriv_unix.h" + +void *uiTableModelStrdup(const char *str) +{ + return g_strdup(str); +} + +void *uiTableModelGiveColor(double r, double g, double b, double a) +{ + GdkRGBA rgba; + + rgba.red = r; + rgba.green = g; + rgba.blue = b; + rgba.alpha = a; + return gdk_rgba_copy(&rgba); +} + +uiTableModel *uiNewTableModel(uiTableModelHandler *mh) +{ + uiTableModel *m; + + m = uiTableModel(g_object_new(uiTableModelType, NULL)); + m->mh = mh; + return m; +} + +void uiFreeTableModel(uiTableModel *m) +{ + g_object_unref(m); +} + +void uiTableModelRowInserted(uiTableModel *m, int newIndex) +{ + GtkTreePath *path; + GtkTreeIter iter; + + path = gtk_tree_path_new_from_indices(newIndex, -1); + iter.stamp = STAMP_GOOD; + iter.user_data = GINT_TO_POINTER(newIndex); + gtk_tree_model_row_inserted(GTK_TREE_MODEL(m), path, &iter); + gtk_tree_path_free(path); +} + +void uiTableModelRowChanged(uiTableModel *m, int index) +{ + GtkTreePath *path; + GtkTreeIter iter; + + path = gtk_tree_path_new_from_indices(index, -1); + iter.stamp = STAMP_GOOD; + iter.user_data = GINT_TO_POINTER(index); + gtk_tree_model_row_changed(GTK_TREE_MODEL(m), path, &iter); + gtk_tree_path_free(path); +} + +void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) +{ + GtkTreePath *path; + + path = gtk_tree_path_new_from_indices(oldIndex, -1); + gtk_tree_model_row_deleted(GTK_TREE_MODEL(m), path); + gtk_tree_path_free(path); +} + +enum { + partText, + partImage, + partButton, + partCheckbox, + partProgressBar, +}; + +struct tablePart { + int type; + int textColumn; + int imageColumn; + int valueColumn; + int colorColumn; + GtkCellRenderer *r; + uiTable *tv; // for pixbufs and background color +}; + +struct uiTableColumn { + GtkTreeViewColumn *c; + uiTable *tv; // for pixbufs and background color + GPtrArray *parts; +}; + +struct uiTable { + uiUnixControl c; + GtkWidget *widget; + GtkContainer *scontainer; + GtkScrolledWindow *sw; + GtkWidget *treeWidget; + GtkTreeView *tv; + GPtrArray *columns; + uiTableModel *model; + int backgroundColumn; +}; + +// use the same size as GtkFileChooserWidget's treeview +// TODO refresh when icon theme changes +// TODO doesn't work when scaled +// TODO is this even necessary? +static void setImageSize(GtkCellRenderer *r) +{ + gint size; + gint width, height; + gint xpad, ypad; + + size = 16; // fallback used by GtkFileChooserWidget + if (gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height) != FALSE) + size = MAX(width, height); + gtk_cell_renderer_get_padding(r, &xpad, &ypad); + gtk_cell_renderer_set_fixed_size(r, + 2 * xpad + size, + 2 * ypad + size); +} + +static void applyColor(GtkTreeModel *mm, GtkTreeIter *iter, int modelColumn, GtkCellRenderer *r, const char *prop, const char *propSet) +{ + GValue value = G_VALUE_INIT; + GdkRGBA *rgba; + + gtk_tree_model_get_value(mm, iter, modelColumn, &value); + rgba = (GdkRGBA *) g_value_get_boxed(&value); + if (rgba != NULL) + g_object_set(r, prop, rgba, NULL); + else + g_object_set(r, propSet, FALSE, NULL); + g_value_unset(&value); +} + +static void dataFunc(GtkTreeViewColumn *c, GtkCellRenderer *r, GtkTreeModel *mm, GtkTreeIter *iter, gpointer data) +{ + struct tablePart *part = (struct tablePart *) data; + GValue value = G_VALUE_INIT; + const gchar *str; + uiImage *img; + int pval; + + switch (part->type) { + case partText: + gtk_tree_model_get_value(mm, iter, part->textColumn, &value); + str = g_value_get_string(&value); + g_object_set(r, "text", str, NULL); + if (part->colorColumn != -1) + applyColor(mm, iter, + part->colorColumn, + r, "foreground-rgba", "foreground-set"); + break; + case partImage: +//TODO setImageSize(r); + gtk_tree_model_get_value(mm, iter, part->imageColumn, &value); + img = (uiImage *) g_value_get_pointer(&value); + g_object_set(r, "surface", + uiprivImageAppropriateSurface(img, part->tv->treeWidget), + NULL); + break; + case partButton: + gtk_tree_model_get_value(mm, iter, part->textColumn, &value); + str = g_value_get_string(&value); + g_object_set(r, "text", str, NULL); + break; + case partCheckbox: + gtk_tree_model_get_value(mm, iter, part->valueColumn, &value); + g_object_set(r, "active", g_value_get_int(&value) != 0, NULL); + break; + case partProgressBar: + gtk_tree_model_get_value(mm, iter, part->valueColumn, &value); + pval = g_value_get_int(&value); + if (pval == -1) { + // TODO + } else + g_object_set(r, + "pulse", -1, + "value", pval, + NULL); + break; + } + g_value_unset(&value); + + if (part->tv->backgroundColumn != -1) + applyColor(mm, iter, + part->tv->backgroundColumn, + r, "cell-background-rgba", "cell-background-set"); +} + +static void onEdited(struct tablePart *part, int column, const char *pathstr, const void *data) +{ + GtkTreePath *path; + int row; + uiTableModel *m; + + path = gtk_tree_path_new_from_string(pathstr); + row = gtk_tree_path_get_indices(path)[0]; + gtk_tree_path_free(path); + m = part->tv->model; + (*(m->mh->SetCellValue))(m->mh, m, row, column, data); + // and update + uiTableModelRowChanged(m, row); +} + +static void appendPart(uiTableColumn *c, struct tablePart *part, GtkCellRenderer *r, int expand) +{ + part->r = r; + gtk_tree_view_column_pack_start(c->c, part->r, expand != 0); + gtk_tree_view_column_set_cell_data_func(c->c, part->r, dataFunc, part, NULL); + g_ptr_array_add(c->parts, part); +} + +static void textEdited(GtkCellRendererText *renderer, gchar *path, gchar *newText, gpointer data) +{ + struct tablePart *part = (struct tablePart *) data; + + onEdited(part, part->textColumn, path, newText); +} + +void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand) +{ + struct tablePart *part; + GtkCellRenderer *r; + + part = uiprivNew(struct tablePart); + part->type = partText; + part->textColumn = modelColumn; + part->tv = c->tv; + part->colorColumn = -1; + + r = gtk_cell_renderer_text_new(); + g_object_set(r, "editable", FALSE, NULL); + g_signal_connect(r, "edited", G_CALLBACK(textEdited), part); + + appendPart(c, part, r, expand); +} + +void uiTableColumnAppendImagePart(uiTableColumn *c, int modelColumn, int expand) +{ + struct tablePart *part; + + part = uiprivNew(struct tablePart); + part->type = partImage; + part->imageColumn = modelColumn; + part->tv = c->tv; + appendPart(c, part, + gtk_cell_renderer_pixbuf_new(), + expand); +} + +// TODO wrong type here +static void buttonClicked(GtkCellRenderer *r, gchar *pathstr, gpointer data) +{ + struct tablePart *part = (struct tablePart *) data; + + onEdited(part, part->textColumn, pathstr, NULL); +} + +void uiTableColumnAppendButtonPart(uiTableColumn *c, int modelColumn, int expand) +{ + struct tablePart *part; + GtkCellRenderer *r; + + part = uiprivNew(struct tablePart); + part->type = partButton; + part->textColumn = modelColumn; + part->tv = c->tv; + + r = uiprivNewCellRendererButton(); + g_object_set(r, "sensitive", TRUE, NULL); // editable by default + g_signal_connect(r, "clicked", G_CALLBACK(buttonClicked), part); + + appendPart(c, part, r, expand); +} + +// yes, we need to do all this twice :| +static void checkboxToggled(GtkCellRendererToggle *r, gchar *pathstr, gpointer data) +{ + struct tablePart *part = (struct tablePart *) data; + GtkTreePath *path; + int row; + uiTableModel *m; + void *value; + int intval; + + path = gtk_tree_path_new_from_string(pathstr); + row = gtk_tree_path_get_indices(path)[0]; + gtk_tree_path_free(path); + m = part->tv->model; + value = (*(m->mh->CellValue))(m->mh, m, row, part->valueColumn); + intval = !uiTableModelTakeInt(value); + onEdited(part, part->valueColumn, pathstr, uiTableModelGiveInt(intval)); +} + +void uiTableColumnAppendCheckboxPart(uiTableColumn *c, int modelColumn, int expand) +{ + struct tablePart *part; + GtkCellRenderer *r; + + part = uiprivNew(struct tablePart); + part->type = partCheckbox; + part->valueColumn = modelColumn; + part->tv = c->tv; + + r = gtk_cell_renderer_toggle_new(); + g_object_set(r, "sensitive", TRUE, NULL); // editable by default + g_signal_connect(r, "toggled", G_CALLBACK(checkboxToggled), part); + + appendPart(c, part, r, expand); +} + +void uiTableColumnAppendProgressBarPart(uiTableColumn *c, int modelColumn, int expand) +{ + struct tablePart *part; + + part = uiprivNew(struct tablePart); + part->type = partProgressBar; + part->valueColumn = modelColumn; + part->tv = c->tv; + appendPart(c, part, + gtk_cell_renderer_progress_new(), + expand); +} + +void uiTableColumnPartSetEditable(uiTableColumn *c, int part, int editable) +{ + struct tablePart *p; + + p = (struct tablePart *) g_ptr_array_index(c->parts, part); + switch (p->type) { + case partImage: + case partProgressBar: + return; + case partButton: + case partCheckbox: + g_object_set(p->r, "sensitive", editable != 0, NULL); + return; + } + g_object_set(p->r, "editable", editable != 0, NULL); +} + +void uiTableColumnPartSetTextColor(uiTableColumn *c, int part, int modelColumn) +{ + struct tablePart *p; + + p = (struct tablePart *) g_ptr_array_index(c->parts, part); + p->colorColumn = modelColumn; + // TODO refresh table +} + +uiUnixControlAllDefaultsExceptDestroy(uiTable) + +static void uiTableDestroy(uiControl *c) +{ + uiTable *t = uiTable(c); + + // TODO + g_object_unref(t->widget); + uiFreeControl(uiControl(t)); +} + +uiTableColumn *uiTableAppendColumn(uiTable *t, const char *name) +{ + uiTableColumn *c; + + c = uiprivNew(uiTableColumn); + c->c = gtk_tree_view_column_new(); + gtk_tree_view_column_set_resizable(c->c, TRUE); + gtk_tree_view_column_set_title(c->c, name); + gtk_tree_view_append_column(t->tv, c->c); + c->tv = t; // TODO rename field to t, cascade + c->parts = g_ptr_array_new(); + return c; +} + +void uiTableSetRowBackgroundColorModelColumn(uiTable *t, int modelColumn) +{ + t->backgroundColumn = modelColumn; + // TODO refresh table +} + +uiTable *uiNewTable(uiTableModel *model) +{ + uiTable *t; + + uiUnixNewControl(uiTable, t); + + t->model = model; + t->backgroundColumn = -1; + + t->widget = gtk_scrolled_window_new(NULL, NULL); + t->scontainer = GTK_CONTAINER(t->widget); + t->sw = GTK_SCROLLED_WINDOW(t->widget); + gtk_scrolled_window_set_shadow_type(t->sw, GTK_SHADOW_IN); + + t->treeWidget = gtk_tree_view_new_with_model(GTK_TREE_MODEL(t->model)); + t->tv = GTK_TREE_VIEW(t->treeWidget); + // TODO set up t->tv + + gtk_container_add(t->scontainer, t->treeWidget); + // and make the tree view visible; only the scrolled window's visibility is controlled by libui + gtk_widget_show(t->treeWidget); + + return t; +} diff --git a/unix/table.c b/unix/table.c index d8cbceaf..179d36b3 100644 --- a/unix/table.c +++ b/unix/table.c @@ -56,13 +56,13 @@ static GType uiTableModel_get_column_type(GtkTreeModel *mm, gint index) uiTableModel *m = uiTableModel(mm); switch ((*(m->mh->ColumnType))(m->mh, m, index)) { - case uiTableModelColumnString: + case uiTableDataTypeString: return G_TYPE_STRING; - case uiTableModelColumnImage: + case uiTableDataTypeImage: return G_TYPE_POINTER; - case uiTableModelColumnInt: + case uiTableDataTypeInt: return G_TYPE_INT; - case uiTableModelColumnColor: + case uiTableDataTypeColor: return GDK_TYPE_RGBA; } // TODO @@ -108,7 +108,9 @@ static void uiTableModel_get_value(GtkTreeModel *mm, GtkTreeIter *iter, gint col { uiTableModel *m = uiTableModel(mm); gint row; - void *data; + uiTableData *data; + double r, g, b, a; + GdkRGBA rgba; if (iter->stamp != STAMP_GOOD) return; @@ -117,19 +119,32 @@ static void uiTableModel_get_value(GtkTreeModel *mm, GtkTreeIter *iter, gint col switch ((*(m->mh->ColumnType))(m->mh, m, column)) { case uiTableModelColumnString: g_value_init(value, G_TYPE_STRING); - g_value_take_string(value, (char *) data); + g_value_set_string(value, uiTableDataString(data)); + uiFreeTableData(data); return; case uiTableModelColumnImage: g_value_init(value, G_TYPE_POINTER); - g_value_set_pointer(value, data); + g_value_set_pointer(value, uiTableDataImage(data)); + uiFreeTableData(data); return; case uiTableModelColumnInt: g_value_init(value, G_TYPE_INT); - g_value_set_int(value, uiTableModelTakeInt(data)); + g_value_set_int(value, uiTableDataInt(data)); + uiFreeTableData(data); return; case uiTableModelColumnColor: g_value_init(value, GDK_TYPE_RGBA); - g_value_take_boxed(value, data); + if (data == NULL) { + g_value_set_boxed(value, NULL); + return; + } + uiTableDataColor(data, &r, &g, &b, &a); + uiFreeTableData(data); + rgba.red = r; + rgba.green = g; + rgba.blue = b; + rgba.alpha = a; + g_value_set_boxed(value, &rgba); return; } // TODO @@ -237,22 +252,6 @@ static void uiTableModel_gtk_tree_model_interface_init(GtkTreeModelIface *iface) // don't specify ref_node() or unref_node() } -void *uiTableModelStrdup(const char *str) -{ - return g_strdup(str); -} - -void *uiTableModelGiveColor(double r, double g, double b, double a) -{ - GdkRGBA rgba; - - rgba.red = r; - rgba.green = g; - rgba.blue = b; - rgba.alpha = a; - return gdk_rgba_copy(&rgba); -} - uiTableModel *uiNewTableModel(uiTableModelHandler *mh) { uiTableModel *m; @@ -300,6 +299,8 @@ void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) gtk_tree_path_free(path); } +============================ TODOTODO + enum { partText, partImage, From 75a5a050cbfbe38ab67b198e1fe0d21837f3261c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 4 Jun 2018 23:46:30 -0400 Subject: [PATCH 1147/1329] Split the uiTableModel GTK+ code into its own file, tablemodel.c. --- unix/CMakeLists.txt | 1 + unix/table.c | 301 +------------------------------------------- unix/table.h | 18 +++ unix/tablemodel.c | 283 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 303 insertions(+), 300 deletions(-) create mode 100644 unix/table.h create mode 100644 unix/tablemodel.c diff --git a/unix/CMakeLists.txt b/unix/CMakeLists.txt index 1393b92a..5c9425d2 100644 --- a/unix/CMakeLists.txt +++ b/unix/CMakeLists.txt @@ -44,6 +44,7 @@ list(APPEND _LIBUI_SOURCES unix/stddialogs.c unix/tab.c unix/table.c + unix/tablemodel.c unix/text.c unix/util.c unix/window.c diff --git a/unix/table.c b/unix/table.c index 179d36b3..f9d593ae 100644 --- a/unix/table.c +++ b/unix/table.c @@ -1,305 +1,6 @@ // 26 june 2016 #include "uipriv_unix.h" - -#define uiTableModelType (uiTableModel_get_type()) -#define uiTableModel(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), uiTableModelType, uiTableModel)) -#define isuiTableModel(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), uiTableModelType)) -#define uiTableModelClass(class) (G_TYPE_CHECK_CLASS_CAST((class), uiTableModelType, uiTableModelClass)) -#define isuiTableModelClass(class) (G_TYPE_CHECK_CLASS_TYPE((class), uiTableModel)) -#define getuiTableModelClass(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), uiTableModelType, uiTableModelClass)) - -typedef struct uiTableModelClass uiTableModelClass; - -struct uiTableModel { - GObject parent_instance; - uiTableModelHandler *mh; -}; - -struct uiTableModelClass { - GObjectClass parent_class; -}; - -static void uiTableModel_gtk_tree_model_interface_init(GtkTreeModelIface *iface); - -G_DEFINE_TYPE_WITH_CODE(uiTableModel, uiTableModel, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE(GTK_TYPE_TREE_MODEL, uiTableModel_gtk_tree_model_interface_init)) - -static void uiTableModel_init(uiTableModel *m) -{ - // nothing to do -} - -static void uiTableModel_dispose(GObject *obj) -{ - G_OBJECT_CLASS(uiTableModel_parent_class)->dispose(obj); -} - -static void uiTableModel_finalize(GObject *obj) -{ - G_OBJECT_CLASS(uiTableModel_parent_class)->finalize(obj); -} - -static GtkTreeModelFlags uiTableModel_get_flags(GtkTreeModel *mm) -{ - return GTK_TREE_MODEL_LIST_ONLY; -} - -static gint uiTableModel_get_n_columns(GtkTreeModel *mm) -{ - uiTableModel *m = uiTableModel(mm); - - return (*(m->mh->NumColumns))(m->mh, m); -} - -static GType uiTableModel_get_column_type(GtkTreeModel *mm, gint index) -{ - uiTableModel *m = uiTableModel(mm); - - switch ((*(m->mh->ColumnType))(m->mh, m, index)) { - case uiTableDataTypeString: - return G_TYPE_STRING; - case uiTableDataTypeImage: - return G_TYPE_POINTER; - case uiTableDataTypeInt: - return G_TYPE_INT; - case uiTableDataTypeColor: - return GDK_TYPE_RGBA; - } - // TODO - return G_TYPE_INVALID; -} - -#define STAMP_GOOD 0x1234 -#define STAMP_BAD 0x5678 - -static gboolean uiTableModel_get_iter(GtkTreeModel *mm, GtkTreeIter *iter, GtkTreePath *path) -{ - uiTableModel *m = uiTableModel(mm); - gint row; - - if (gtk_tree_path_get_depth(path) != 1) - goto bad; - row = gtk_tree_path_get_indices(path)[0]; - if (row < 0) - goto bad; - if (row >= (*(m->mh->NumRows))(m->mh, m)) - goto bad; - iter->stamp = STAMP_GOOD; - iter->user_data = GINT_TO_POINTER(row); - return TRUE; -bad: - iter->stamp = STAMP_BAD; - return FALSE; -} - -// GtkListStore returns NULL on error; let's do that too -static GtkTreePath *uiTableModel_get_path(GtkTreeModel *mm, GtkTreeIter *iter) -{ - gint row; - - if (iter->stamp != STAMP_GOOD) - return NULL; - row = GPOINTER_TO_INT(iter->user_data); - return gtk_tree_path_new_from_indices(row, -1); -} - -// GtkListStore leaves value empty on failure; let's do the same -static void uiTableModel_get_value(GtkTreeModel *mm, GtkTreeIter *iter, gint column, GValue *value) -{ - uiTableModel *m = uiTableModel(mm); - gint row; - uiTableData *data; - double r, g, b, a; - GdkRGBA rgba; - - if (iter->stamp != STAMP_GOOD) - return; - row = GPOINTER_TO_INT(iter->user_data); - data = (*(m->mh->CellValue))(m->mh, m, row, column); - switch ((*(m->mh->ColumnType))(m->mh, m, column)) { - case uiTableModelColumnString: - g_value_init(value, G_TYPE_STRING); - g_value_set_string(value, uiTableDataString(data)); - uiFreeTableData(data); - return; - case uiTableModelColumnImage: - g_value_init(value, G_TYPE_POINTER); - g_value_set_pointer(value, uiTableDataImage(data)); - uiFreeTableData(data); - return; - case uiTableModelColumnInt: - g_value_init(value, G_TYPE_INT); - g_value_set_int(value, uiTableDataInt(data)); - uiFreeTableData(data); - return; - case uiTableModelColumnColor: - g_value_init(value, GDK_TYPE_RGBA); - if (data == NULL) { - g_value_set_boxed(value, NULL); - return; - } - uiTableDataColor(data, &r, &g, &b, &a); - uiFreeTableData(data); - rgba.red = r; - rgba.green = g; - rgba.blue = b; - rgba.alpha = a; - g_value_set_boxed(value, &rgba); - return; - } - // TODO -} - -static gboolean uiTableModel_iter_next(GtkTreeModel *mm, GtkTreeIter *iter) -{ - uiTableModel *m = uiTableModel(mm); - gint row; - - if (iter->stamp != STAMP_GOOD) - return FALSE; - row = GPOINTER_TO_INT(iter->user_data); - row++; - if (row >= (*(m->mh->NumRows))(m->mh, m)) { - iter->stamp = STAMP_BAD; - return FALSE; - } - iter->user_data = GINT_TO_POINTER(row); - return TRUE; -} - -static gboolean uiTableModel_iter_previous(GtkTreeModel *mm, GtkTreeIter *iter) -{ - gint row; - - if (iter->stamp != STAMP_GOOD) - return FALSE; - row = GPOINTER_TO_INT(iter->user_data); - row--; - if (row < 0) { - iter->stamp = STAMP_BAD; - return FALSE; - } - iter->user_data = GINT_TO_POINTER(row); - return TRUE; -} - -static gboolean uiTableModel_iter_children(GtkTreeModel *mm, GtkTreeIter *iter, GtkTreeIter *parent) -{ - return gtk_tree_model_iter_nth_child(mm, iter, parent, 0); -} - -static gboolean uiTableModel_iter_has_child(GtkTreeModel *mm, GtkTreeIter *iter) -{ - return FALSE; -} - -static gint uiTableModel_iter_n_children(GtkTreeModel *mm, GtkTreeIter *iter) -{ - uiTableModel *m = uiTableModel(mm); - - if (iter != NULL) - return 0; - return (*(m->mh->NumRows))(m->mh, m); -} - -static gboolean uiTableModel_iter_nth_child(GtkTreeModel *mm, GtkTreeIter *iter, GtkTreeIter *parent, gint n) -{ - uiTableModel *m = uiTableModel(mm); - - if (iter->stamp != STAMP_GOOD) - return FALSE; - if (parent != NULL) - goto bad; - if (n < 0) - goto bad; - if (n >= (*(m->mh->NumRows))(m->mh, m)) - goto bad; - iter->stamp = STAMP_GOOD; - iter->user_data = GINT_TO_POINTER(n); - return TRUE; -bad: - iter->stamp = STAMP_BAD; - return FALSE; -} - -gboolean uiTableModel_iter_parent(GtkTreeModel *mm, GtkTreeIter *iter, GtkTreeIter *child) -{ - iter->stamp = STAMP_BAD; - return FALSE; -} - -static void uiTableModel_class_init(uiTableModelClass *class) -{ - G_OBJECT_CLASS(class)->dispose = uiTableModel_dispose; - G_OBJECT_CLASS(class)->finalize = uiTableModel_finalize; -} - -static void uiTableModel_gtk_tree_model_interface_init(GtkTreeModelIface *iface) -{ - iface->get_flags = uiTableModel_get_flags; - iface->get_n_columns = uiTableModel_get_n_columns; - iface->get_column_type = uiTableModel_get_column_type; - iface->get_iter = uiTableModel_get_iter; - iface->get_path = uiTableModel_get_path; - iface->get_value = uiTableModel_get_value; - iface->iter_next = uiTableModel_iter_next; - iface->iter_previous = uiTableModel_iter_previous; - iface->iter_children = uiTableModel_iter_children; - iface->iter_has_child = uiTableModel_iter_has_child; - iface->iter_n_children = uiTableModel_iter_n_children; - iface->iter_nth_child = uiTableModel_iter_nth_child; - iface->iter_parent = uiTableModel_iter_parent; - // don't specify ref_node() or unref_node() -} - -uiTableModel *uiNewTableModel(uiTableModelHandler *mh) -{ - uiTableModel *m; - - m = uiTableModel(g_object_new(uiTableModelType, NULL)); - m->mh = mh; - return m; -} - -void uiFreeTableModel(uiTableModel *m) -{ - g_object_unref(m); -} - -void uiTableModelRowInserted(uiTableModel *m, int newIndex) -{ - GtkTreePath *path; - GtkTreeIter iter; - - path = gtk_tree_path_new_from_indices(newIndex, -1); - iter.stamp = STAMP_GOOD; - iter.user_data = GINT_TO_POINTER(newIndex); - gtk_tree_model_row_inserted(GTK_TREE_MODEL(m), path, &iter); - gtk_tree_path_free(path); -} - -void uiTableModelRowChanged(uiTableModel *m, int index) -{ - GtkTreePath *path; - GtkTreeIter iter; - - path = gtk_tree_path_new_from_indices(index, -1); - iter.stamp = STAMP_GOOD; - iter.user_data = GINT_TO_POINTER(index); - gtk_tree_model_row_changed(GTK_TREE_MODEL(m), path, &iter); - gtk_tree_path_free(path); -} - -void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) -{ - GtkTreePath *path; - - path = gtk_tree_path_new_from_indices(oldIndex, -1); - gtk_tree_model_row_deleted(GTK_TREE_MODEL(m), path); - gtk_tree_path_free(path); -} - -============================ TODOTODO +#include "table.h" enum { partText, diff --git a/unix/table.h b/unix/table.h new file mode 100644 index 00000000..a023e7f5 --- /dev/null +++ b/unix/table.h @@ -0,0 +1,18 @@ +// 4 june 2018 + +// tablemodel.c +#define uiTableModelType (uiTableModel_get_type()) +#define uiTableModel(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), uiTableModelType, uiTableModel)) +#define isuiTableModel(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), uiTableModelType)) +#define uiTableModelClass(class) (G_TYPE_CHECK_CLASS_CAST((class), uiTableModelType, uiTableModelClass)) +#define isuiTableModelClass(class) (G_TYPE_CHECK_CLASS_TYPE((class), uiTableModel)) +#define getuiTableModelClass(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), uiTableModelType, uiTableModelClass)) +typedef struct uiTableModelClass uiTableModelClass; +struct uiTableModel { + GObject parent_instance; + uiTableModelHandler *mh; +}; +struct uiTableModelClass { + GObjectClass parent_class; +}; +extern GType uiTableModel_get_type(void); diff --git a/unix/tablemodel.c b/unix/tablemodel.c new file mode 100644 index 00000000..7ecc05ed --- /dev/null +++ b/unix/tablemodel.c @@ -0,0 +1,283 @@ +// 26 june 2016 +#include "uipriv_unix.h" +#include "table.h" + +static void uiTableModel_gtk_tree_model_interface_init(GtkTreeModelIface *iface); + +G_DEFINE_TYPE_WITH_CODE(uiTableModel, uiTableModel, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE(GTK_TYPE_TREE_MODEL, uiTableModel_gtk_tree_model_interface_init)) + +static void uiTableModel_init(uiTableModel *m) +{ + // nothing to do +} + +static void uiTableModel_dispose(GObject *obj) +{ + G_OBJECT_CLASS(uiTableModel_parent_class)->dispose(obj); +} + +static void uiTableModel_finalize(GObject *obj) +{ + G_OBJECT_CLASS(uiTableModel_parent_class)->finalize(obj); +} + +static GtkTreeModelFlags uiTableModel_get_flags(GtkTreeModel *mm) +{ + return GTK_TREE_MODEL_LIST_ONLY; +} + +static gint uiTableModel_get_n_columns(GtkTreeModel *mm) +{ + uiTableModel *m = uiTableModel(mm); + + return (*(m->mh->NumColumns))(m->mh, m); +} + +static GType uiTableModel_get_column_type(GtkTreeModel *mm, gint index) +{ + uiTableModel *m = uiTableModel(mm); + + switch ((*(m->mh->ColumnType))(m->mh, m, index)) { + case uiTableDataTypeString: + return G_TYPE_STRING; + case uiTableDataTypeImage: + return G_TYPE_POINTER; + case uiTableDataTypeInt: + return G_TYPE_INT; + case uiTableDataTypeColor: + return GDK_TYPE_RGBA; + } + // TODO + return G_TYPE_INVALID; +} + +#define STAMP_GOOD 0x1234 +#define STAMP_BAD 0x5678 + +static gboolean uiTableModel_get_iter(GtkTreeModel *mm, GtkTreeIter *iter, GtkTreePath *path) +{ + uiTableModel *m = uiTableModel(mm); + gint row; + + if (gtk_tree_path_get_depth(path) != 1) + goto bad; + row = gtk_tree_path_get_indices(path)[0]; + if (row < 0) + goto bad; + if (row >= (*(m->mh->NumRows))(m->mh, m)) + goto bad; + iter->stamp = STAMP_GOOD; + iter->user_data = GINT_TO_POINTER(row); + return TRUE; +bad: + iter->stamp = STAMP_BAD; + return FALSE; +} + +// GtkListStore returns NULL on error; let's do that too +static GtkTreePath *uiTableModel_get_path(GtkTreeModel *mm, GtkTreeIter *iter) +{ + gint row; + + if (iter->stamp != STAMP_GOOD) + return NULL; + row = GPOINTER_TO_INT(iter->user_data); + return gtk_tree_path_new_from_indices(row, -1); +} + +// GtkListStore leaves value empty on failure; let's do the same +static void uiTableModel_get_value(GtkTreeModel *mm, GtkTreeIter *iter, gint column, GValue *value) +{ + uiTableModel *m = uiTableModel(mm); + gint row; + uiTableData *data; + double r, g, b, a; + GdkRGBA rgba; + + if (iter->stamp != STAMP_GOOD) + return; + row = GPOINTER_TO_INT(iter->user_data); + data = (*(m->mh->CellValue))(m->mh, m, row, column); + switch ((*(m->mh->ColumnType))(m->mh, m, column)) { + case uiTableModelColumnString: + g_value_init(value, G_TYPE_STRING); + g_value_set_string(value, uiTableDataString(data)); + uiFreeTableData(data); + return; + case uiTableModelColumnImage: + g_value_init(value, G_TYPE_POINTER); + g_value_set_pointer(value, uiTableDataImage(data)); + uiFreeTableData(data); + return; + case uiTableModelColumnInt: + g_value_init(value, G_TYPE_INT); + g_value_set_int(value, uiTableDataInt(data)); + uiFreeTableData(data); + return; + case uiTableModelColumnColor: + g_value_init(value, GDK_TYPE_RGBA); + if (data == NULL) { + g_value_set_boxed(value, NULL); + return; + } + uiTableDataColor(data, &r, &g, &b, &a); + uiFreeTableData(data); + rgba.red = r; + rgba.green = g; + rgba.blue = b; + rgba.alpha = a; + g_value_set_boxed(value, &rgba); + return; + } + // TODO +} + +static gboolean uiTableModel_iter_next(GtkTreeModel *mm, GtkTreeIter *iter) +{ + uiTableModel *m = uiTableModel(mm); + gint row; + + if (iter->stamp != STAMP_GOOD) + return FALSE; + row = GPOINTER_TO_INT(iter->user_data); + row++; + if (row >= (*(m->mh->NumRows))(m->mh, m)) { + iter->stamp = STAMP_BAD; + return FALSE; + } + iter->user_data = GINT_TO_POINTER(row); + return TRUE; +} + +static gboolean uiTableModel_iter_previous(GtkTreeModel *mm, GtkTreeIter *iter) +{ + gint row; + + if (iter->stamp != STAMP_GOOD) + return FALSE; + row = GPOINTER_TO_INT(iter->user_data); + row--; + if (row < 0) { + iter->stamp = STAMP_BAD; + return FALSE; + } + iter->user_data = GINT_TO_POINTER(row); + return TRUE; +} + +static gboolean uiTableModel_iter_children(GtkTreeModel *mm, GtkTreeIter *iter, GtkTreeIter *parent) +{ + return gtk_tree_model_iter_nth_child(mm, iter, parent, 0); +} + +static gboolean uiTableModel_iter_has_child(GtkTreeModel *mm, GtkTreeIter *iter) +{ + return FALSE; +} + +static gint uiTableModel_iter_n_children(GtkTreeModel *mm, GtkTreeIter *iter) +{ + uiTableModel *m = uiTableModel(mm); + + if (iter != NULL) + return 0; + return (*(m->mh->NumRows))(m->mh, m); +} + +static gboolean uiTableModel_iter_nth_child(GtkTreeModel *mm, GtkTreeIter *iter, GtkTreeIter *parent, gint n) +{ + uiTableModel *m = uiTableModel(mm); + + if (iter->stamp != STAMP_GOOD) + return FALSE; + if (parent != NULL) + goto bad; + if (n < 0) + goto bad; + if (n >= (*(m->mh->NumRows))(m->mh, m)) + goto bad; + iter->stamp = STAMP_GOOD; + iter->user_data = GINT_TO_POINTER(n); + return TRUE; +bad: + iter->stamp = STAMP_BAD; + return FALSE; +} + +gboolean uiTableModel_iter_parent(GtkTreeModel *mm, GtkTreeIter *iter, GtkTreeIter *child) +{ + iter->stamp = STAMP_BAD; + return FALSE; +} + +static void uiTableModel_class_init(uiTableModelClass *class) +{ + G_OBJECT_CLASS(class)->dispose = uiTableModel_dispose; + G_OBJECT_CLASS(class)->finalize = uiTableModel_finalize; +} + +static void uiTableModel_gtk_tree_model_interface_init(GtkTreeModelIface *iface) +{ + iface->get_flags = uiTableModel_get_flags; + iface->get_n_columns = uiTableModel_get_n_columns; + iface->get_column_type = uiTableModel_get_column_type; + iface->get_iter = uiTableModel_get_iter; + iface->get_path = uiTableModel_get_path; + iface->get_value = uiTableModel_get_value; + iface->iter_next = uiTableModel_iter_next; + iface->iter_previous = uiTableModel_iter_previous; + iface->iter_children = uiTableModel_iter_children; + iface->iter_has_child = uiTableModel_iter_has_child; + iface->iter_n_children = uiTableModel_iter_n_children; + iface->iter_nth_child = uiTableModel_iter_nth_child; + iface->iter_parent = uiTableModel_iter_parent; + // don't specify ref_node() or unref_node() +} + +uiTableModel *uiNewTableModel(uiTableModelHandler *mh) +{ + uiTableModel *m; + + m = uiTableModel(g_object_new(uiTableModelType, NULL)); + m->mh = mh; + return m; +} + +void uiFreeTableModel(uiTableModel *m) +{ + g_object_unref(m); +} + +void uiTableModelRowInserted(uiTableModel *m, int newIndex) +{ + GtkTreePath *path; + GtkTreeIter iter; + + path = gtk_tree_path_new_from_indices(newIndex, -1); + iter.stamp = STAMP_GOOD; + iter.user_data = GINT_TO_POINTER(newIndex); + gtk_tree_model_row_inserted(GTK_TREE_MODEL(m), path, &iter); + gtk_tree_path_free(path); +} + +void uiTableModelRowChanged(uiTableModel *m, int index) +{ + GtkTreePath *path; + GtkTreeIter iter; + + path = gtk_tree_path_new_from_indices(index, -1); + iter.stamp = STAMP_GOOD; + iter.user_data = GINT_TO_POINTER(index); + gtk_tree_model_row_changed(GTK_TREE_MODEL(m), path, &iter); + gtk_tree_path_free(path); +} + +void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) +{ + GtkTreePath *path; + + path = gtk_tree_path_new_from_indices(oldIndex, -1); + gtk_tree_model_row_deleted(GTK_TREE_MODEL(m), path); + gtk_tree_path_free(path); +} From 18d8a8fe2271b28c4fa91a55e694786059561427 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 5 Jun 2018 22:00:54 -0400 Subject: [PATCH 1148/1329] Converted column functions and editable handlers on GTK+. --- unix/table.c | 298 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 194 insertions(+), 104 deletions(-) diff --git a/unix/table.c b/unix/table.c index f9d593ae..698a0237 100644 --- a/unix/table.c +++ b/unix/table.c @@ -2,30 +2,6 @@ #include "uipriv_unix.h" #include "table.h" -enum { - partText, - partImage, - partButton, - partCheckbox, - partProgressBar, -}; - -struct tablePart { - int type; - int textColumn; - int imageColumn; - int valueColumn; - int colorColumn; - GtkCellRenderer *r; - uiTable *tv; // for pixbufs and background color -}; - -struct uiTableColumn { - GtkTreeViewColumn *c; - uiTable *tv; // for pixbufs and background color - GPtrArray *parts; -}; - struct uiTable { uiUnixControl c; GtkWidget *widget; @@ -33,14 +9,13 @@ struct uiTable { GtkScrolledWindow *sw; GtkWidget *treeWidget; GtkTreeView *tv; - GPtrArray *columns; uiTableModel *model; int backgroundColumn; }; // use the same size as GtkFileChooserWidget's treeview // TODO refresh when icon theme changes -// TODO doesn't work when scaled +// TODO doesn't work when scaled? // TODO is this even necessary? static void setImageSize(GtkCellRenderer *r) { @@ -57,12 +32,12 @@ static void setImageSize(GtkCellRenderer *r) 2 * ypad + size); } -static void applyColor(GtkTreeModel *mm, GtkTreeIter *iter, int modelColumn, GtkCellRenderer *r, const char *prop, const char *propSet) +static void applyColor(GtkTreeModel *m, GtkTreeIter *iter, int modelColumn, GtkCellRenderer *r, const char *prop, const char *propSet) { GValue value = G_VALUE_INIT; GdkRGBA *rgba; - gtk_tree_model_get_value(mm, iter, modelColumn, &value); + gtk_tree_model_get_value(m, iter, modelColumn, &value); rgba = (GdkRGBA *) g_value_get_boxed(&value); if (rgba != NULL) g_object_set(r, prop, rgba, NULL); @@ -71,76 +46,217 @@ static void applyColor(GtkTreeModel *mm, GtkTreeIter *iter, int modelColumn, Gtk g_value_unset(&value); } -static void dataFunc(GtkTreeViewColumn *c, GtkCellRenderer *r, GtkTreeModel *mm, GtkTreeIter *iter, gpointer data) +static void setEditable(uiTableModel *m, GtkTreeIter *iter, int modelColumn, GtkCellRenderer *r, const char *prop) { - struct tablePart *part = (struct tablePart *) data; + uiTableData *data; GValue value = G_VALUE_INIT; - const gchar *str; - uiImage *img; - int pval; + int value; + gboolean editable; - switch (part->type) { - case partText: - gtk_tree_model_get_value(mm, iter, part->textColumn, &value); - str = g_value_get_string(&value); - g_object_set(r, "text", str, NULL); - if (part->colorColumn != -1) - applyColor(mm, iter, - part->colorColumn, - r, "foreground-rgba", "foreground-set"); + switch (modelColumn) { + case uiTableModelColumnNeverEditable: + editable = FALSE; break; - case partImage: -//TODO setImageSize(r); - gtk_tree_model_get_value(mm, iter, part->imageColumn, &value); - img = (uiImage *) g_value_get_pointer(&value); - g_object_set(r, "surface", - uiprivImageAppropriateSurface(img, part->tv->treeWidget), - NULL); - break; - case partButton: - gtk_tree_model_get_value(mm, iter, part->textColumn, &value); - str = g_value_get_string(&value); - g_object_set(r, "text", str, NULL); - break; - case partCheckbox: - gtk_tree_model_get_value(mm, iter, part->valueColumn, &value); - g_object_set(r, "active", g_value_get_int(&value) != 0, NULL); - break; - case partProgressBar: - gtk_tree_model_get_value(mm, iter, part->valueColumn, &value); - pval = g_value_get_int(&value); - if (pval == -1) { - // TODO - } else - g_object_set(r, - "pulse", -1, - "value", pval, - NULL); + case uiTableModelColumnAlwaysEditable: + editable = TRUE; break; + default: + gtk_tree_model_get_value(m, iter, p->editableColumn, &value); + editable = gtk_value_get_int(&value) != 0; + g_value_unset(&value); } - g_value_unset(&value); + g_object_set(r, "editable", editable, NULL); +} - if (part->tv->backgroundColumn != -1) - applyColor(mm, iter, - part->tv->backgroundColumn, +static void applyBackgroundColor(uiTable *t, GtkTreeModel *m, GtkTreeIter *iter, GtkCellRenderer *r) +{ + if (t->backgroundColumn != -1) + applyColor(m, iter, t->backgroundColumn, r, "cell-background-rgba", "cell-background-set"); } -static void onEdited(struct tablePart *part, int column, const char *pathstr, const void *data) +static void onEdited(uiTableModel *m, int column, const char *pathstr, const uiTableData *data, GtkTreeIter *iter) { GtkTreePath *path; int row; - uiTableModel *m; path = gtk_tree_path_new_from_string(pathstr); row = gtk_tree_path_get_indices(path)[0]; + if (iter != NULL) + gtk_tree_model_convert_path_to_iter(m, path, iter); gtk_tree_path_free(path); - m = part->tv->model; (*(m->mh->SetCellValue))(m->mh, m, row, column, data); - // and update - uiTableModelRowChanged(m, row); } +// TODO deduplicate this between platforms +static uiTableTextColumnOptionalParams defaultTextColumnOptionalParams = { + .ColorModelColumn = -1, +}; + +struct textColumnParams { + uiTable *t; + uiTableModel *m; + int modelColumn; + int editableColumn; + uiTableTextColumnOptionalParams params; +}; + +static void textColumnDataFunc(GtkTreeViewColumn *c, GtkCellRenderer *r, GtkTreeModel *m, GtkTreeIter *iter, gpointer data) +{ + struct textColumnParams *p = (struct textColumnParams *) data; + GValue value = G_VALUE_INIT; + const gchar *str; + gboolean editable; + + gtk_tree_model_get_value(m, iter, p->modelColumn, &value); + str = g_value_get_string(&value); + g_object_set(r, "text", str, NULL); + g_value_unset(&value); + + setEditable(m, iter, p->editableColumn, r, "editable"); + + if (p->params.ColorModelColumn != -1) + applyColor(m, iter, p->params.ColorModelColumn, + r, "foreground-rgba", "foreground-set"); + + applyBackgroundColor(p->t, m, iter, r); +} + +static void textColumnEdited(GtkCellRendererText *renderer, gchar *path, gchar *newText, gpointer data) +{ + struct textColumnParams *p = (struct textColumnParams *) data; + uiTableData *data; + GtkTreeIter iter; + + data = uiNewTableDataString(newText); + onEdited(p->m, p->textColumn, path, data, &iter); + uiFreeData(data); + // and update the column TODO copy comment here + textColumnDataFunc(NULL, r, GTK_TREE_MODEL(p->m), &iter, data); +} + +struct imageColumnParams { + uiTable *t; + int modelColumn; +}; + +static void imageColumnDataFunc(GtkTreeViewColumn *c, GtkCellRenderer *r, GtkTreeModel *m, GtkTreeIter *iter, gpointer data) +{ + struct imageColumnParams *p = (struct imageColumnParams *) data; + GValue value = G_VALUE_INIT; + uiImage *img; + +//TODO setImageSize(r); + gtk_tree_model_get_value(m, iter, p->modelColumn, &value); + img = (uiImage *) g_value_get_pointer(&value); + g_object_set(r, "surface", + uiprivImageAppropriateSurface(img, p->t->treeWidget), + NULL); + g_value_unset(&value); + + applyBackgroundColor(p->t, m, iter, r); +} + +struct checkboxColumnParams { + uiTable *t; + uiTableModel *m; + int modelColumn; + int editableColumn; +}; + +static void checkboxColumnDataFunc(GtkTreeViewColumn *c, GtkCellRenderer *r, GtkTreeModel *m, GtkTreeIter *iter, gpointer data) +{ + struct checkboxColumnParams *p = (struct checkboxColumnParams *) data; + GValue value = G_VALUE_INIT; + gboolean active; + + gtk_tree_model_get_value(m, iter, p->modelColumn, &value); + active = g_value_get_int(&value) != 0; + g_object_set(r, "active", active, NULL); + g_value_unset(&value); + + setEditable(m, iter, p->editableColumn, r, "activatable"); + + applyBackgroundColor(p->t, m, iter, r); +} + +static void checkboxColumnToggled(GtkCellRendererToggle *r, gchar *pathstr, gpointer data) +{ + struct checkboxColumnParams *p = (struct checkboxColumnParams *) data; + GValue value = G_VALUE_INIT; + int v; + uiTableData *data; + GtkTreeIter iter; + + gtk_tree_model_get_value(p->m, iter, p->modelColumn, &value); + v = g_value_get_int(&value); + g_value_unset(&value); + data = uiNewTableDataInt(!v); + onEdited(p->m, p->modelColumn, path, data, &iter); + uiFreeData(data); + // and update the column TODO copy comment here + // TODO avoid fetching the model data twice + checkboxColumnDataFunc(NULL, r, GTK_TREE_MODEL(p->m), &iter, data); +} + +struct progressBarColumnParams { + uiTable *t; + int modelColumn; +}; + +static void progressBarDataFunc(GtkTreeViewColumn *c, GtkCellRenderer *r, GtkTreeModel *m, GtkTreeIter *iter, gpointer data) +{ + struct progressBarColumnParams *p = (struct progressBarColumnParams *) data; + GValue value = G_VALUE_INIT; + int pval; + + gtk_tree_model_get_value(m, iter, p->modelColumn, &value); + pval = g_value_get_int(&value); + if (pval == -1) { + // TODO + } else + g_object_set(r, + "pulse", -1, + "value", pval, + NULL); + g_value_unset(&value); + + applyBackgroundColor(p->t, m, iter, r); +} + +struct buttonColumnParams { + uiTable *t; + uiTableModel *m; + int modelColumn; + int clickableColumn; +}; + +static void buttonDataFunc(GtkTreeViewColumn *c, GtkCellRenderer *r, GtkTreeModel *m, GtkTreeIter *iter, gpointer data) +{ + struct buttonColumnParams *p = (struct buttonColumnParams *) data; + GValue value = G_VALUE_INIT; + const gchar *str; + gboolean clickable; + + gtk_tree_model_get_value(m, iter, p->modelColumn, &value); + str = g_value_get_string(&value); + g_object_set(r, "text", str, NULL); + g_value_unset(&value); + + setEditable(m, iter, p->clickableColumn, r, "clickable"); + + applyBackgroundColor(p->t, m, iter, r); +} + +static void buttonColumnClicked(uiprivCellRendererButton *r, gchar *pathstr, gpointer data) +{ + struct buttonColumnParams *p = (struct buttonColumnParams *) data; + + onEdited(p->m, p->modelColumn, path, NULL, NULL); +} + +=================== TODOTODO + static void appendPart(uiTableColumn *c, struct tablePart *part, GtkCellRenderer *r, int expand) { part->r = r; @@ -149,13 +265,6 @@ static void appendPart(uiTableColumn *c, struct tablePart *part, GtkCellRenderer g_ptr_array_add(c->parts, part); } -static void textEdited(GtkCellRendererText *renderer, gchar *path, gchar *newText, gpointer data) -{ - struct tablePart *part = (struct tablePart *) data; - - onEdited(part, part->textColumn, path, newText); -} - void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand) { struct tablePart *part; @@ -212,25 +321,6 @@ void uiTableColumnAppendButtonPart(uiTableColumn *c, int modelColumn, int expand appendPart(c, part, r, expand); } -// yes, we need to do all this twice :| -static void checkboxToggled(GtkCellRendererToggle *r, gchar *pathstr, gpointer data) -{ - struct tablePart *part = (struct tablePart *) data; - GtkTreePath *path; - int row; - uiTableModel *m; - void *value; - int intval; - - path = gtk_tree_path_new_from_string(pathstr); - row = gtk_tree_path_get_indices(path)[0]; - gtk_tree_path_free(path); - m = part->tv->model; - value = (*(m->mh->CellValue))(m->mh, m, row, part->valueColumn); - intval = !uiTableModelTakeInt(value); - onEdited(part, part->valueColumn, pathstr, uiTableModelGiveInt(intval)); -} - void uiTableColumnAppendCheckboxPart(uiTableColumn *c, int modelColumn, int expand) { struct tablePart *part; From b7151388e392836dc294d2253a959c94ba7a7412 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 5 Jun 2018 22:47:11 -0400 Subject: [PATCH 1149/1329] Started rewriting the column constructors. --- unix/table.c | 77 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 56 insertions(+), 21 deletions(-) diff --git a/unix/table.c b/unix/table.c index 698a0237..2806e743 100644 --- a/unix/table.c +++ b/unix/table.c @@ -10,6 +10,7 @@ struct uiTable { GtkWidget *treeWidget; GtkTreeView *tv; uiTableModel *model; + GPtrArray *columnParams; int backgroundColumn; }; @@ -255,34 +256,68 @@ static void buttonColumnClicked(uiprivCellRendererButton *r, gchar *pathstr, gpo onEdited(p->m, p->modelColumn, path, NULL, NULL); } -=================== TODOTODO - -static void appendPart(uiTableColumn *c, struct tablePart *part, GtkCellRenderer *r, int expand) +static void addTextColumn(uiTable *t, GtkTreeViewColumn *c, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *params) { - part->r = r; - gtk_tree_view_column_pack_start(c->c, part->r, expand != 0); - gtk_tree_view_column_set_cell_data_func(c->c, part->r, dataFunc, part, NULL); - g_ptr_array_add(c->parts, part); -} - -void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand) -{ - struct tablePart *part; + struct textColumnParams *p; GtkCellRenderer *r; - part = uiprivNew(struct tablePart); - part->type = partText; - part->textColumn = modelColumn; - part->tv = c->tv; - part->colorColumn = -1; + p = uiprivNew(struct textColumnParams); + p->t = t; + xx TODO get rid of these fields in favor of t->m + p->m = t->m; + p->modelColumn = textModelColumn; + p->editableColumn = textEditableModelColumn; + if (params != NULL) + p->params = *params; + else + p->params = defaultTextColumnOptionalParams; r = gtk_cell_renderer_text_new(); - g_object_set(r, "editable", FALSE, NULL); - g_signal_connect(r, "edited", G_CALLBACK(textEdited), part); - - appendPart(c, part, r, expand); + gtk_tree_view_column_pack_start(c, r, TRUE); + gtk_tree_view_column_set_cell_data_func(c, r, textColumnDataFunc, p, NULL); + g_signal_connect(r, "edited", G_CALLBACK(textColumnEdited), p); + g_ptr_array_add(c->columnParams, p); } +void uiTableAppendTextColumn(uiTable *t, const char *name, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *params) +{ + GtkTreeViewColumn *c; + + c = gtk_tree_view_column_new(); + gtk_tree_view_column_set_resizable(c, TRUE); + gtk_tree_view_column_set_title(c, name); + gtk_tree_view_append_column(t->tv, c); + addTextColumn(t, c, textModelColumn, textEditableModelColumn, params); +} + +_UI_EXTERN void uiTableAppendImageColumn(uiTable *t, + const char *name, + int imageModelColumn); +_UI_EXTERN void uiTableAppendImageTextColumn(uiTable *t, + const char *name, + int imageModelColumn, + int textModelColumn, + int textEditableModelColumn, + uiTableTextColumnOptionalParams *textParams); +_UI_EXTERN void uiTableAppendCheckboxColumn(uiTable *t, + const char *name, + int checkboxModelColumn, + int checkboxEditableModelColumn); +_UI_EXTERN void uiTableAppendCheckboxTextColumn(uiTable *t, + const char *name, + int checkboxModelColumn, + int checkboxEditableModelColumn, + int textModelColumn, + int textEditableModelColumn, + uiTableTextColumnOptionalParams *textParams); +_UI_EXTERN void uiTableAppendProgressBarColumn(uiTable *t, + const char *name, + int progressModelColumn); +_UI_EXTERN void uiTableAppendButtonColumn(uiTable *t, + const char *name, + int buttonTextModelColumn, + int buttonClickableModelColumn); + void uiTableColumnAppendImagePart(uiTableColumn *c, int modelColumn, int expand) { struct tablePart *part; From d6ac22a20a6423656e2d589e06b750d75146653b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 7 Jun 2018 21:50:38 -0400 Subject: [PATCH 1150/1329] Finished rewriting table.c. Now to test. --- unix/table.c | 219 +++++++++++++++++++++++---------------------------- 1 file changed, 100 insertions(+), 119 deletions(-) diff --git a/unix/table.c b/unix/table.c index 2806e743..e8d5a52b 100644 --- a/unix/table.c +++ b/unix/table.c @@ -205,7 +205,7 @@ struct progressBarColumnParams { int modelColumn; }; -static void progressBarDataFunc(GtkTreeViewColumn *c, GtkCellRenderer *r, GtkTreeModel *m, GtkTreeIter *iter, gpointer data) +static void progressBarColumnDataFunc(GtkTreeViewColumn *c, GtkCellRenderer *r, GtkTreeModel *m, GtkTreeIter *iter, gpointer data) { struct progressBarColumnParams *p = (struct progressBarColumnParams *) data; GValue value = G_VALUE_INIT; @@ -232,7 +232,7 @@ struct buttonColumnParams { int clickableColumn; }; -static void buttonDataFunc(GtkTreeViewColumn *c, GtkCellRenderer *r, GtkTreeModel *m, GtkTreeIter *iter, gpointer data) +static void buttonColumnDataFunc(GtkTreeViewColumn *c, GtkCellRenderer *r, GtkTreeModel *m, GtkTreeIter *iter, gpointer data) { struct buttonColumnParams *p = (struct buttonColumnParams *) data; GValue value = G_VALUE_INIT; @@ -244,11 +244,12 @@ static void buttonDataFunc(GtkTreeViewColumn *c, GtkCellRenderer *r, GtkTreeMode g_object_set(r, "text", str, NULL); g_value_unset(&value); - setEditable(m, iter, p->clickableColumn, r, "clickable"); + setEditable(m, iter, p->clickableColumn, r, "sensitive"); applyBackgroundColor(p->t, m, iter, r); } +// TODO wrong type here static void buttonColumnClicked(uiprivCellRendererButton *r, gchar *pathstr, gpointer data) { struct buttonColumnParams *p = (struct buttonColumnParams *) data; @@ -256,6 +257,17 @@ static void buttonColumnClicked(uiprivCellRendererButton *r, gchar *pathstr, gpo onEdited(p->m, p->modelColumn, path, NULL, NULL); } +static GtkTreeViewColumn *addColumn(uiTable *t, const char *name) +{ + GtkTreeViewColumn *c; + + c = gtk_tree_view_column_new(); + gtk_tree_view_column_set_resizable(c, TRUE); + gtk_tree_view_column_set_title(c, name); + gtk_tree_view_append_column(t->tv, c); + return c; +} + static void addTextColumn(uiTable *t, GtkTreeViewColumn *c, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *params) { struct textColumnParams *p; @@ -263,7 +275,7 @@ static void addTextColumn(uiTable *t, GtkTreeViewColumn *c, int textModelColumn, p = uiprivNew(struct textColumnParams); p->t = t; - xx TODO get rid of these fields in favor of t->m + // TODO get rid of these fields in favor of t->m p->m = t->m; p->modelColumn = textModelColumn; p->editableColumn = textEditableModelColumn; @@ -279,137 +291,120 @@ static void addTextColumn(uiTable *t, GtkTreeViewColumn *c, int textModelColumn, g_ptr_array_add(c->columnParams, p); } +// TODO rename modelCOlumn and params everywhere void uiTableAppendTextColumn(uiTable *t, const char *name, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *params) { GtkTreeViewColumn *c; - c = gtk_tree_view_column_new(); - gtk_tree_view_column_set_resizable(c, TRUE); - gtk_tree_view_column_set_title(c, name); - gtk_tree_view_append_column(t->tv, c); + c = addColumn(t, name); addTextColumn(t, c, textModelColumn, textEditableModelColumn, params); } -_UI_EXTERN void uiTableAppendImageColumn(uiTable *t, - const char *name, - int imageModelColumn); -_UI_EXTERN void uiTableAppendImageTextColumn(uiTable *t, - const char *name, - int imageModelColumn, - int textModelColumn, - int textEditableModelColumn, - uiTableTextColumnOptionalParams *textParams); -_UI_EXTERN void uiTableAppendCheckboxColumn(uiTable *t, - const char *name, - int checkboxModelColumn, - int checkboxEditableModelColumn); -_UI_EXTERN void uiTableAppendCheckboxTextColumn(uiTable *t, - const char *name, - int checkboxModelColumn, - int checkboxEditableModelColumn, - int textModelColumn, - int textEditableModelColumn, - uiTableTextColumnOptionalParams *textParams); -_UI_EXTERN void uiTableAppendProgressBarColumn(uiTable *t, - const char *name, - int progressModelColumn); -_UI_EXTERN void uiTableAppendButtonColumn(uiTable *t, - const char *name, - int buttonTextModelColumn, - int buttonClickableModelColumn); - -void uiTableColumnAppendImagePart(uiTableColumn *c, int modelColumn, int expand) +static void addImageColumn(uiTable *t, GtkTreeViewColumn *c, int imageModelColumn) { - struct tablePart *part; - - part = uiprivNew(struct tablePart); - part->type = partImage; - part->imageColumn = modelColumn; - part->tv = c->tv; - appendPart(c, part, - gtk_cell_renderer_pixbuf_new(), - expand); -} - -// TODO wrong type here -static void buttonClicked(GtkCellRenderer *r, gchar *pathstr, gpointer data) -{ - struct tablePart *part = (struct tablePart *) data; - - onEdited(part, part->textColumn, pathstr, NULL); -} - -void uiTableColumnAppendButtonPart(uiTableColumn *c, int modelColumn, int expand) -{ - struct tablePart *part; + struct imageColumnParams *p; GtkCellRenderer *r; - part = uiprivNew(struct tablePart); - part->type = partButton; - part->textColumn = modelColumn; - part->tv = c->tv; + p = uiprivNew(struct imageColumnParams); + p->t = t; + p->modelColumn = imageModelColumn; - r = uiprivNewCellRendererButton(); - g_object_set(r, "sensitive", TRUE, NULL); // editable by default - g_signal_connect(r, "clicked", G_CALLBACK(buttonClicked), part); - - appendPart(c, part, r, expand); + r = gtk_cell_renderer_pixbuf_new(); + gtk_tree_view_column_pack_start(c, r, FALSE); + gtk_tree_view_column_set_cell_data_func(c, r, imageColumnDataFunc, p, NULL); + g_ptr_array_add(c->columnParams, p); } -void uiTableColumnAppendCheckboxPart(uiTableColumn *c, int modelColumn, int expand) +void uiTableAppendImageColumn(uiTable *t, const char *name, int imageModelColumn) { - struct tablePart *part; + GtkTreeViewColumn *c; + + c = addColumn(t, name); + addImageColumn(t, c, imageModelColumn); +} + +void uiTableAppendImageTextColumn(uiTable *t, const char *name, int imageModelColumn, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *textParams) +{ + GtkTreeViewColumn *c; + + c = addColumn(t, name); + addImageColumn(t, c, imageModelColumn); + addTextColumn(t, c, textModelColumn, textEditableModelColumn, textParams); +} + +static void uiTableAppendCheckboxColumn(uiTable *t, GtkTableViewColumn *c, int checkboxModelColumn, int checkboxEditableModelColumn) +{ + struct checkboxColumnParams *p; GtkCellRenderer *r; - part = uiprivNew(struct tablePart); - part->type = partCheckbox; - part->valueColumn = modelColumn; - part->tv = c->tv; + p = uiprivNew(struct checkboxColumnParams); + p->t = t; + p->m = t->m; + p->modelColumn = checkboxModelColumn; + p->editableColumn = checkboxEditableModelColumn; r = gtk_cell_renderer_toggle_new(); - g_object_set(r, "sensitive", TRUE, NULL); // editable by default - g_signal_connect(r, "toggled", G_CALLBACK(checkboxToggled), part); - - appendPart(c, part, r, expand); + gtk_tree_view_column_pack_start(c, r, FALSE); + gtk_tree_view_column_set_cell_data_func(c, r, checkboxColumnDataFunc, p, NULL); + g_signal_connect(r, "toggled", G_CALLBACK(checkboxColumnToggled), part); + g_ptr_array_add(c->columnParams, p); } -void uiTableColumnAppendProgressBarPart(uiTableColumn *c, int modelColumn, int expand) +void uiTableAppendCheckboxColumn(uiTable *t, const char *name, int checkboxModelColumn, int checkboxEditableModelColumn) { - struct tablePart *part; + GtkTreeViewColumn *c; - part = uiprivNew(struct tablePart); - part->type = partProgressBar; - part->valueColumn = modelColumn; - part->tv = c->tv; - appendPart(c, part, - gtk_cell_renderer_progress_new(), - expand); + c = addColumn(t, name); + addCheckboxColumn(t, c, checkboxModelColumn, checkboxEditableModelColumn); } -void uiTableColumnPartSetEditable(uiTableColumn *c, int part, int editable) +void uiTableAppendCheckboxTextColumn(uiTable *t, const char *name, int checkboxModelColumn, int checkboxEditableModelColumn, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *textParams) { - struct tablePart *p; + GtkTreeViewColumn *c; - p = (struct tablePart *) g_ptr_array_index(c->parts, part); - switch (p->type) { - case partImage: - case partProgressBar: - return; - case partButton: - case partCheckbox: - g_object_set(p->r, "sensitive", editable != 0, NULL); - return; - } - g_object_set(p->r, "editable", editable != 0, NULL); + c = addColumn(t, name); + addCheckboxColumn(t, c, checkboxModelColumn, checkboxEditableModelColumn); + addTextColumn(t, c, textModelColumn, textEditableModelColumn, textParams); } -void uiTableColumnPartSetTextColor(uiTableColumn *c, int part, int modelColumn) +void uiTableAppendProgressBarColumn(uiTable *t, const char *name, int progressModelColumn) { - struct tablePart *p; + GtkTreeViewColumn *c; + struct progressBarColumnParams *p; + GtkCellRenderer *r; - p = (struct tablePart *) g_ptr_array_index(c->parts, part); - p->colorColumn = modelColumn; - // TODO refresh table + c = addColumn(t, name); + + p = uiprivNew(struct progressBarColumnParams); + p->t = t; + // TODO make progress and progressBar consistent everywhere + p->modelColumn = progressModelColumn; + + r = gtk_cell_renderer_progress_new(); + gtk_tree_view_column_pack_start(c, r, TRUE); + gtk_tree_view_column_set_cell_data_func(c, r, progressBarColumnDataFunc, p, NULL); + g_ptr_array_add(c->columnParams, p); +} + +void uiTableAppendButtonColumn(uiTable *t, const char *name, int buttonTextModelColumn, int buttonClickableModelColumn) +{ + GtkTreeViewColumn *c; + struct buttonColumnParams *p; + GtkCellRenderer *r; + + c = addColumn(t, name); + + p = uiprivNew(struct buttonColumnParams); + p->t = t; + p->m = t->m; + p->modelColumn = buttonModelColumn; + p->clickableColumn = buttonClickableModelColumn; + + r = uiprivNewCellRendererButton(); + gtk_tree_view_column_pack_start(c, r, TRUE); + gtk_tree_view_column_set_cell_data_func(c, r, buttonColumnDataFunc, p, NULL); + g_signal_connect(r, "clicked", G_CALLBACK(buttonColumnClicked), p); + g_ptr_array_add(c->columnParams, p); } uiUnixControlAllDefaultsExceptDestroy(uiTable) @@ -423,20 +418,6 @@ static void uiTableDestroy(uiControl *c) uiFreeControl(uiControl(t)); } -uiTableColumn *uiTableAppendColumn(uiTable *t, const char *name) -{ - uiTableColumn *c; - - c = uiprivNew(uiTableColumn); - c->c = gtk_tree_view_column_new(); - gtk_tree_view_column_set_resizable(c->c, TRUE); - gtk_tree_view_column_set_title(c->c, name); - gtk_tree_view_append_column(t->tv, c->c); - c->tv = t; // TODO rename field to t, cascade - c->parts = g_ptr_array_new(); - return c; -} - void uiTableSetRowBackgroundColorModelColumn(uiTable *t, int modelColumn) { t->backgroundColumn = modelColumn; From 66ca3315cf53b229fdf73a9d6a9d7342afdc75ca Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 7 Jun 2018 22:25:17 -0400 Subject: [PATCH 1151/1329] Fixed compile errors. Now for runtime errors, if any! --- unix/table.c | 73 ++++++++++++++++++++++++----------------------- unix/tablemodel.c | 8 +++--- 2 files changed, 41 insertions(+), 40 deletions(-) diff --git a/unix/table.c b/unix/table.c index e8d5a52b..fe347e4e 100644 --- a/unix/table.c +++ b/unix/table.c @@ -49,9 +49,7 @@ static void applyColor(GtkTreeModel *m, GtkTreeIter *iter, int modelColumn, GtkC static void setEditable(uiTableModel *m, GtkTreeIter *iter, int modelColumn, GtkCellRenderer *r, const char *prop) { - uiTableData *data; GValue value = G_VALUE_INIT; - int value; gboolean editable; switch (modelColumn) { @@ -62,8 +60,8 @@ static void setEditable(uiTableModel *m, GtkTreeIter *iter, int modelColumn, Gtk editable = TRUE; break; default: - gtk_tree_model_get_value(m, iter, p->editableColumn, &value); - editable = gtk_value_get_int(&value) != 0; + gtk_tree_model_get_value(GTK_TREE_MODEL(m), iter, modelColumn, &value); + editable = g_value_get_int(&value) != 0; g_value_unset(&value); } g_object_set(r, "editable", editable, NULL); @@ -84,7 +82,7 @@ static void onEdited(uiTableModel *m, int column, const char *pathstr, const uiT path = gtk_tree_path_new_from_string(pathstr); row = gtk_tree_path_get_indices(path)[0]; if (iter != NULL) - gtk_tree_model_convert_path_to_iter(m, path, iter); + gtk_tree_model_get_iter(GTK_TREE_MODEL(m), iter, path); gtk_tree_path_free(path); (*(m->mh->SetCellValue))(m->mh, m, row, column, data); } @@ -107,14 +105,13 @@ static void textColumnDataFunc(GtkTreeViewColumn *c, GtkCellRenderer *r, GtkTree struct textColumnParams *p = (struct textColumnParams *) data; GValue value = G_VALUE_INIT; const gchar *str; - gboolean editable; gtk_tree_model_get_value(m, iter, p->modelColumn, &value); str = g_value_get_string(&value); g_object_set(r, "text", str, NULL); g_value_unset(&value); - setEditable(m, iter, p->editableColumn, r, "editable"); + setEditable(p->m, iter, p->editableColumn, r, "editable"); if (p->params.ColorModelColumn != -1) applyColor(m, iter, p->params.ColorModelColumn, @@ -123,17 +120,17 @@ static void textColumnDataFunc(GtkTreeViewColumn *c, GtkCellRenderer *r, GtkTree applyBackgroundColor(p->t, m, iter, r); } -static void textColumnEdited(GtkCellRendererText *renderer, gchar *path, gchar *newText, gpointer data) +static void textColumnEdited(GtkCellRendererText *r, gchar *path, gchar *newText, gpointer data) { struct textColumnParams *p = (struct textColumnParams *) data; - uiTableData *data; + uiTableData *tdata; GtkTreeIter iter; - data = uiNewTableDataString(newText); - onEdited(p->m, p->textColumn, path, data, &iter); - uiFreeData(data); + tdata = uiNewTableDataString(newText); + onEdited(p->m, p->modelColumn, path, tdata, &iter); + uiFreeTableData(tdata); // and update the column TODO copy comment here - textColumnDataFunc(NULL, r, GTK_TREE_MODEL(p->m), &iter, data); + textColumnDataFunc(NULL, GTK_CELL_RENDERER(r), GTK_TREE_MODEL(p->m), &iter, data); } struct imageColumnParams { @@ -176,7 +173,7 @@ static void checkboxColumnDataFunc(GtkTreeViewColumn *c, GtkCellRenderer *r, Gtk g_object_set(r, "active", active, NULL); g_value_unset(&value); - setEditable(m, iter, p->editableColumn, r, "activatable"); + setEditable(p->m, iter, p->editableColumn, r, "activatable"); applyBackgroundColor(p->t, m, iter, r); } @@ -186,18 +183,22 @@ static void checkboxColumnToggled(GtkCellRendererToggle *r, gchar *pathstr, gpoi struct checkboxColumnParams *p = (struct checkboxColumnParams *) data; GValue value = G_VALUE_INIT; int v; - uiTableData *data; + uiTableData *tdata; + GtkTreePath *path; GtkTreeIter iter; - gtk_tree_model_get_value(p->m, iter, p->modelColumn, &value); + path = gtk_tree_path_new_from_string(pathstr); + gtk_tree_model_get_iter(GTK_TREE_MODEL(p->m), &iter, path); + gtk_tree_path_free(path); + gtk_tree_model_get_value(GTK_TREE_MODEL(p->m), &iter, p->modelColumn, &value); v = g_value_get_int(&value); g_value_unset(&value); - data = uiNewTableDataInt(!v); - onEdited(p->m, p->modelColumn, path, data, &iter); - uiFreeData(data); + tdata = uiNewTableDataInt(!v); + onEdited(p->m, p->modelColumn, pathstr, tdata, NULL); + uiFreeTableData(tdata); // and update the column TODO copy comment here // TODO avoid fetching the model data twice - checkboxColumnDataFunc(NULL, r, GTK_TREE_MODEL(p->m), &iter, data); + checkboxColumnDataFunc(NULL, GTK_CELL_RENDERER(r), GTK_TREE_MODEL(p->m), &iter, data); } struct progressBarColumnParams { @@ -237,24 +238,23 @@ static void buttonColumnDataFunc(GtkTreeViewColumn *c, GtkCellRenderer *r, GtkTr struct buttonColumnParams *p = (struct buttonColumnParams *) data; GValue value = G_VALUE_INIT; const gchar *str; - gboolean clickable; gtk_tree_model_get_value(m, iter, p->modelColumn, &value); str = g_value_get_string(&value); g_object_set(r, "text", str, NULL); g_value_unset(&value); - setEditable(m, iter, p->clickableColumn, r, "sensitive"); + setEditable(p->m, iter, p->clickableColumn, r, "sensitive"); applyBackgroundColor(p->t, m, iter, r); } // TODO wrong type here -static void buttonColumnClicked(uiprivCellRendererButton *r, gchar *pathstr, gpointer data) +static void buttonColumnClicked(GtkCellRenderer *r, gchar *pathstr, gpointer data) { struct buttonColumnParams *p = (struct buttonColumnParams *) data; - onEdited(p->m, p->modelColumn, path, NULL, NULL); + onEdited(p->m, p->modelColumn, pathstr, NULL, NULL); } static GtkTreeViewColumn *addColumn(uiTable *t, const char *name) @@ -275,8 +275,8 @@ static void addTextColumn(uiTable *t, GtkTreeViewColumn *c, int textModelColumn, p = uiprivNew(struct textColumnParams); p->t = t; - // TODO get rid of these fields in favor of t->m - p->m = t->m; + // TODO get rid of these fields AND rename t->model in favor of t->m + p->m = t->model; p->modelColumn = textModelColumn; p->editableColumn = textEditableModelColumn; if (params != NULL) @@ -288,7 +288,7 @@ static void addTextColumn(uiTable *t, GtkTreeViewColumn *c, int textModelColumn, gtk_tree_view_column_pack_start(c, r, TRUE); gtk_tree_view_column_set_cell_data_func(c, r, textColumnDataFunc, p, NULL); g_signal_connect(r, "edited", G_CALLBACK(textColumnEdited), p); - g_ptr_array_add(c->columnParams, p); + g_ptr_array_add(t->columnParams, p); } // TODO rename modelCOlumn and params everywhere @@ -312,7 +312,7 @@ static void addImageColumn(uiTable *t, GtkTreeViewColumn *c, int imageModelColum r = gtk_cell_renderer_pixbuf_new(); gtk_tree_view_column_pack_start(c, r, FALSE); gtk_tree_view_column_set_cell_data_func(c, r, imageColumnDataFunc, p, NULL); - g_ptr_array_add(c->columnParams, p); + g_ptr_array_add(t->columnParams, p); } void uiTableAppendImageColumn(uiTable *t, const char *name, int imageModelColumn) @@ -332,22 +332,22 @@ void uiTableAppendImageTextColumn(uiTable *t, const char *name, int imageModelCo addTextColumn(t, c, textModelColumn, textEditableModelColumn, textParams); } -static void uiTableAppendCheckboxColumn(uiTable *t, GtkTableViewColumn *c, int checkboxModelColumn, int checkboxEditableModelColumn) +static void addCheckboxColumn(uiTable *t, GtkTreeViewColumn *c, int checkboxModelColumn, int checkboxEditableModelColumn) { struct checkboxColumnParams *p; GtkCellRenderer *r; p = uiprivNew(struct checkboxColumnParams); p->t = t; - p->m = t->m; + p->m = t->model; p->modelColumn = checkboxModelColumn; p->editableColumn = checkboxEditableModelColumn; r = gtk_cell_renderer_toggle_new(); gtk_tree_view_column_pack_start(c, r, FALSE); gtk_tree_view_column_set_cell_data_func(c, r, checkboxColumnDataFunc, p, NULL); - g_signal_connect(r, "toggled", G_CALLBACK(checkboxColumnToggled), part); - g_ptr_array_add(c->columnParams, p); + g_signal_connect(r, "toggled", G_CALLBACK(checkboxColumnToggled), p); + g_ptr_array_add(t->columnParams, p); } void uiTableAppendCheckboxColumn(uiTable *t, const char *name, int checkboxModelColumn, int checkboxEditableModelColumn) @@ -383,7 +383,7 @@ void uiTableAppendProgressBarColumn(uiTable *t, const char *name, int progressMo r = gtk_cell_renderer_progress_new(); gtk_tree_view_column_pack_start(c, r, TRUE); gtk_tree_view_column_set_cell_data_func(c, r, progressBarColumnDataFunc, p, NULL); - g_ptr_array_add(c->columnParams, p); + g_ptr_array_add(t->columnParams, p); } void uiTableAppendButtonColumn(uiTable *t, const char *name, int buttonTextModelColumn, int buttonClickableModelColumn) @@ -396,15 +396,15 @@ void uiTableAppendButtonColumn(uiTable *t, const char *name, int buttonTextModel p = uiprivNew(struct buttonColumnParams); p->t = t; - p->m = t->m; - p->modelColumn = buttonModelColumn; + p->m = t->model; + p->modelColumn = buttonTextModelColumn; p->clickableColumn = buttonClickableModelColumn; r = uiprivNewCellRendererButton(); gtk_tree_view_column_pack_start(c, r, TRUE); gtk_tree_view_column_set_cell_data_func(c, r, buttonColumnDataFunc, p, NULL); g_signal_connect(r, "clicked", G_CALLBACK(buttonColumnClicked), p); - g_ptr_array_add(c->columnParams, p); + g_ptr_array_add(t->columnParams, p); } uiUnixControlAllDefaultsExceptDestroy(uiTable) @@ -431,6 +431,7 @@ uiTable *uiNewTable(uiTableModel *model) uiUnixNewControl(uiTable, t); t->model = model; + t->columnParams = g_ptr_array_new(); t->backgroundColumn = -1; t->widget = gtk_scrolled_window_new(NULL, NULL); diff --git a/unix/tablemodel.c b/unix/tablemodel.c index 7ecc05ed..3c6ee6d3 100644 --- a/unix/tablemodel.c +++ b/unix/tablemodel.c @@ -100,22 +100,22 @@ static void uiTableModel_get_value(GtkTreeModel *mm, GtkTreeIter *iter, gint col row = GPOINTER_TO_INT(iter->user_data); data = (*(m->mh->CellValue))(m->mh, m, row, column); switch ((*(m->mh->ColumnType))(m->mh, m, column)) { - case uiTableModelColumnString: + case uiTableDataTypeString: g_value_init(value, G_TYPE_STRING); g_value_set_string(value, uiTableDataString(data)); uiFreeTableData(data); return; - case uiTableModelColumnImage: + case uiTableDataTypeImage: g_value_init(value, G_TYPE_POINTER); g_value_set_pointer(value, uiTableDataImage(data)); uiFreeTableData(data); return; - case uiTableModelColumnInt: + case uiTableDataTypeInt: g_value_init(value, G_TYPE_INT); g_value_set_int(value, uiTableDataInt(data)); uiFreeTableData(data); return; - case uiTableModelColumnColor: + case uiTableDataTypeColor: g_value_init(value, GDK_TYPE_RGBA); if (data == NULL) { g_value_set_boxed(value, NULL); From 7a40bdfb3f073a94bcd513204747ca61d6643d99 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 7 Jun 2018 22:30:43 -0400 Subject: [PATCH 1152/1329] Amazingly, it worked the first time! Just a quick logic error here. --- unix/table.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unix/table.c b/unix/table.c index fe347e4e..2e85fbd8 100644 --- a/unix/table.c +++ b/unix/table.c @@ -64,7 +64,7 @@ static void setEditable(uiTableModel *m, GtkTreeIter *iter, int modelColumn, Gtk editable = g_value_get_int(&value) != 0; g_value_unset(&value); } - g_object_set(r, "editable", editable, NULL); + g_object_set(r, prop, editable, NULL); } static void applyBackgroundColor(uiTable *t, GtkTreeModel *m, GtkTreeIter *iter, GtkCellRenderer *r) From 0adad7743a211efe8a95a2ee8eb923aa8d198588 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 7 Jun 2018 22:54:01 -0400 Subject: [PATCH 1153/1329] Drop mixing of C and C++ class allocations, including placement new. This is the easiest change I can make to the Windows table code for now... --- windows/table.cpp | 41 +++++++++++++++++------------------------ 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/windows/table.cpp b/windows/table.cpp index c5dc9ef3..db070f44 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -2,7 +2,7 @@ struct uiTableModel { uiTableModelHandler *mh; - std::vector tables; + std::vector *tables; }; struct uiTableColumn { @@ -16,31 +16,23 @@ struct uiTable { uiWindowsControl c; uiTableModel *model; HWND hwnd; - std::vector columns; + std::vector *columns; }; -void *uiTableModelStrdup(const char *str) -{ - return strdup(str); -} - -void *uiTableModelGiveColor(double r, double g, double b, double a) -{ - return 0; // not implemented -} - uiTableModel *uiNewTableModel(uiTableModelHandler *mh) { uiTableModel *m; - m = new uiTableModel(); + m = uiprivNew(uiTableModel); m->mh = mh; + m->tables = new std::vector; return m; } void uiFreeTableModel(uiTableModel *m) { - delete m; + delete m->tables; + uiprivFree(m); } void uiTableModelRowInserted(uiTableModel *m, int newIndex) @@ -51,21 +43,21 @@ void uiTableModelRowInserted(uiTableModel *m, int newIndex) item.mask = 0; item.iItem = newIndex; item.iSubItem = 0; - for (auto t : m->tables) + for (auto t : *(m->tables)) if (SendMessageW(t->hwnd, LVM_INSERTITEM, 0, (LPARAM) (&item)) == (LRESULT) (-1)) logLastError(L"error calling LVM_INSERTITEM in uiTableModelRowInserted()"); } void uiTableModelRowChanged(uiTableModel *m, int index) { - for (auto t : m->tables) + for (auto t : *(m->tables)) if (SendMessageW(t->hwnd, LVM_UPDATE, (WPARAM) index, 0) == (LRESULT) (-1)) logLastError(L"error calling LVM_UPDATE in uiTableModelRowChanged()"); } void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) { - for (auto t : m->tables) + for (auto t : *(m->tables)) if (SendMessageW(t->hwnd, LVM_DELETEITEM, (WPARAM) oldIndex, 0) == (LRESULT) (-1)) logLastError(L"error calling LVM_DELETEITEM in uiTableModelRowDeleted()"); } @@ -81,7 +73,7 @@ void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand) c->modelColumn = modelColumn; // work out appropriate listview index for the column - for (auto candidate : t->columns) { + for (auto candidate : *(t->columns)) { if (candidate == c) break; if (candidate->modelColumn >= 0) @@ -140,7 +132,7 @@ uiTableColumn *uiTableAppendColumn(uiTable *t, const char *name) c->t = t; c->modelColumn = -1; // -1 = unassigned // we defer the actual ListView_InsertColumn call until a part is added... - t->columns.push_back(c); + t->columns->push_back(c); return c; } @@ -165,11 +157,11 @@ static void uiTableDestroy(uiControl *c) } } // free the columns - for (auto col : t->columns) { + for (auto col : *(t->columns)) { uiprivFree(col->name); uiprivFree(col); } - t->columns.~vector(); // (created with placement new, so just call dtor directly) + delete t->columns; uiFreeControl(uiControl(t)); } @@ -217,9 +209,9 @@ static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult) break; row = item->iItem; col = item->iSubItem; - if (col < 0 || col >= (int)t->columns.size()) + if (col < 0 || col >= (int)t->columns->size()) break; - tc = (uiTableColumn *)t->columns[col]; + tc = (uiTableColumn *)t->columns->at(col); mcol = tc->modelColumn; typ = (*mh->ColumnType)(mh, t->model, mcol); @@ -247,7 +239,8 @@ uiTable *uiNewTable(uiTableModel *model) int n; uiWindowsNewControl(uiTable, t); - new(&t->columns) std::vector(); // (initialising in place) + + t->columns = new std::vector; t->model = model; t->hwnd = uiWindowsEnsureCreateControlHWND(WS_EX_CLIENTEDGE, WC_LISTVIEW, L"", From ca2115ca5773036f1326ea95b1f2368d9f2c89f3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 8 Jun 2018 01:23:11 -0400 Subject: [PATCH 1154/1329] Removed some old files from wintable that we won't use anymore. --- windows/tablepart.cpp | 95 ------------------------------------------- windows/tableutil.cpp | 32 --------------- 2 files changed, 127 deletions(-) delete mode 100644 windows/tablepart.cpp delete mode 100644 windows/tableutil.cpp diff --git a/windows/tablepart.cpp b/windows/tablepart.cpp deleted file mode 100644 index c31932d7..00000000 --- a/windows/tablepart.cpp +++ /dev/null @@ -1,95 +0,0 @@ -// 30 june 2016 -// TODO includes - -typedef struct tablePartDrawParams tablePartDrawParams; -typedef struct tablePartMinimumSizeParams tableDrawMinimumSizeParams; -typedef struct tablePartEditingParams tablePartEditingParams; - -struct tablePartDrawParams { - HWND hwnd; - HDC hdc; - RECT *r; - bool selected; - bool focused; - bool hovering; - uiTableModel *model; - int row; -}; - -struct tablePartMinimumSizeParams { - HWND hwnd; - HDC hdc; - uiTableModel *model; - int row; -}; - -enum { - partEventDoNothing, - partEventRedraw, - partEventEdit, -}; - -struct tablePartEditingParams { - HWND newHWND; -}; - -enum { - partEditContinue, - partEditDone, -}; - -class tablePart { -public: - // needed so we can delete a tablePart - virtual ~tablePart() {} - - virtual HRESULT Draw(tablePartDrawParams *p) = 0; - virtual HRESULT MinimumSize(tablePartMinimumSizeParams *p, int *width, int *height) = 0; - - // returns a partEvent constant - virtual int MouseMove(int x, int y, RECT *cell) = 0; - virtual int MouseLeave(void) = 0; - virtual int LButtonDown(int x, int y, int count, RECT *cell) = 0; - virtual int LButtonUp(int x, int y, RECT *cell) = 0; - virtual int CaptureBroken(void) = 0; - virtual int KeyDown(void) = 0; - virtual int KeyUp(void) = 0; - - // editing; all optional - virtual int StartEditing(tablePartEditingParams *p) { return editDone; } - virtual int EditChildWM_COMMAND(WORD code, LRESULT *lr) { return editDone; } - virtual void FinishEditing(uiTableModel *model, int row) {} - virtual void CancelEditing(void) {} - - // TODO tooltips - // TODO timers and animations - - // optional methods - virtual void SetTextColorColumn(int col) {} - virtual void SetEditable(bool editable) {} -}; - -class tablePartText : public tablePart { - int textColumn; - int colorColumn; -public: - tablePartText(int tc) - { - this->textColumn = tc; - this->colorColumn = -1; - } - - // TODO figure out vertical alignment - virtual HRESULT Draw(tablePartDrawParams *p) - { - } - - virtual HRESULT MinimumSize(tablePartMinimumSizeParams *p, int *width, int *height) - { - } -}; - -tablePart *newTablePartText(int tc) -{ - return new tablePartText(tc); -} diff --git a/windows/tableutil.cpp b/windows/tableutil.cpp deleted file mode 100644 index e6e5bb14..00000000 --- a/windows/tableutil.cpp +++ /dev/null @@ -1,32 +0,0 @@ -// 1 july 2016 -// TODO includes - -void tableGetClientRect(HWND hwnd, RECT *r) -{ - if (GetClientRect(hwnd, r) == 0) { - r->left = 0; - r->top = 0; - r->right = 0; - r->bottom = 0; - } -} - -void tableGetWindowRect(HWND hwnd, RECT *r) -{ - if (GetWindowRect(hwnd, r) == 0) { - r->left = 0; - r->top = 0; - r->right = 0; - r->bottom = 0; - } -} - -void tableGetTextExtentPoint32W(HDC dc, const WSTR *str, int len, SIZE *s) -{ - if (len == -1) - len = wcslen(str); - if (GetTextExtentPoint32W(dc, str, len, s) == 0) { - s->cx = 0; - s->cy = 0; - } -} From 3aa16e844d36a5b9b13f9fb0a6a95a4acee74f45 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 8 Jun 2018 01:35:23 -0400 Subject: [PATCH 1155/1329] Adjusted uiTableModel on Windows for owner-data list views. --- windows/table.cpp | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/windows/table.cpp b/windows/table.cpp index db070f44..a71e9be9 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -35,19 +35,32 @@ void uiFreeTableModel(uiTableModel *m) uiprivFree(m); } +// TODO document that when this is called, the model must return the new row count when asked void uiTableModelRowInserted(uiTableModel *m, int newIndex) { LVITEMW item; + int newCount; + newCount = (*(m->mh->NumRows))(m->mh, m); ZeroMemory(&item, sizeof (LVITEMW)); item.mask = 0; item.iItem = newIndex; item.iSubItem = 0; - for (auto t : *(m->tables)) + for (auto t : *(m->tables)) { + // actually insert the rows + if (SendMessageW(t->hwnd, LVM_SETITEMCOUNT, (WPARAM) newCount, LVSICF_NOINVALIDATEALL) == 0) + logLastError(L"error calling LVM_SETITEMCOUNT in uiTableModelRowInserted()"); + // and redraw every row from the new row down to simulate adding it + if (SendMessageW(t->hwnd, LVM_REDRAWITEMS, (WPARAM) newIndex, (LPARAM) (newCount - 1)) == FALSE) + logLastError(L"error calling LVM_REDRAWITEMS in uiTableModelRowInserted()"); + + // update selection state if (SendMessageW(t->hwnd, LVM_INSERTITEM, 0, (LPARAM) (&item)) == (LRESULT) (-1)) - logLastError(L"error calling LVM_INSERTITEM in uiTableModelRowInserted()"); + logLastError(L"error calling LVM_INSERTITEM in uiTableModelRowInserted() to update selection state"); + } } +// TODO compare LVM_UPDATE and LVM_REDRAWITEMS void uiTableModelRowChanged(uiTableModel *m, int index) { for (auto t : *(m->tables)) @@ -55,11 +68,26 @@ void uiTableModelRowChanged(uiTableModel *m, int index) logLastError(L"error calling LVM_UPDATE in uiTableModelRowChanged()"); } +// TODO document that when this is called, the model must return the OLD row count when asked +// TODO for this and the above, see what GTK+ requires and adjust accordingly void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) { - for (auto t : *(m->tables)) + int newCount; + + newCount = (*(m->mh->NumRows))(m->mh, m); + newCount--; + for (auto t : *(m->tables)) { + // update selection state if (SendMessageW(t->hwnd, LVM_DELETEITEM, (WPARAM) oldIndex, 0) == (LRESULT) (-1)) - logLastError(L"error calling LVM_DELETEITEM in uiTableModelRowDeleted()"); + logLastError(L"error calling LVM_DELETEITEM in uiTableModelRowDeleted() to update selection state"); + + // actually delete the rows + if (SendMessageW(t->hwnd, LVM_SETITEMCOUNT, (WPARAM) newCount, LVSICF_NOINVALIDATEALL) == 0) + logLastError(L"error calling LVM_SETITEMCOUNT in uiTableModelRowDeleted()"); + // and redraw every row from the new nth row down to simulate removing the old nth row + if (SendMessageW(t->hwnd, LVM_REDRAWITEMS, (WPARAM) oldIndex, (LPARAM) (newCount - 1)) == FALSE) + logLastError(L"error calling LVM_REDRAWITEMS in uiTableModelRowDeleted()"); + } } void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand) From 516eb312fc325b5e049420b070b98eb19412bbb0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 8 Jun 2018 21:16:06 -0400 Subject: [PATCH 1156/1329] Rearranged stuff in table.cpp on Windows and started rewriting it to handle owner-data in the new API. --- windows/table.cpp | 221 ++++++++++++++++++++-------------------------- 1 file changed, 94 insertions(+), 127 deletions(-) diff --git a/windows/table.cpp b/windows/table.cpp index a71e9be9..529485f9 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -5,18 +5,19 @@ struct uiTableModel { std::vector *tables; }; -struct uiTableColumn { - uiTable *t; - WCHAR *name; - // don't really support parts (but this would part=>column mappings if we did) - int modelColumn; // -1 = none +struct columnParams { + int textModelColumn; + int textEditableColumn; }; struct uiTable { uiWindowsControl c; uiTableModel *model; HWND hwnd; - std::vector *columns; + std::vector *columns; + // MSDN says we have to keep LVN_GETDISPINFO strings we allocate around at least until "two additional LVN_GETDISPINFO messages have been sent". + // we'll use this queue to do so; the "two additional" part is encoded in the initial state of the queue + std::queue *dispinfoStrings; }; uiTableModel *uiNewTableModel(uiTableModelHandler *mh) @@ -90,6 +91,93 @@ void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) } } +static LRESULT onLVN_GETDISPINFO(uiTable *t, NMLVDISPINFOW *nm) +{ + struct columnParams *p; + uiTableData *data; + WCHAR *wstr; + + wstr = t->dipsinfoString->front(); + t->dispinfoString->pop(); + uiprivFree(wstr); + + p = (*(t->columns))[nm->item.iSubItem]; + // TODO is this condition ever not going to be true for libui? + if ((nm->item.mask & LVIF_TEXT) != 0) + if (p->textModelColumn != -1) { + data = (*(t->model->mh->Value))(t->model->mh, t->model, nm->item.iItem, p->textModelColumn); + wstr = toUTF16(uiTableDataString(data)); + uiFreeTableData(data); + nm->item.pszText = wstr; + t->dispinfoString->push(wstr); + } + + return 0; +} + +static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult) +{ + uiTable *t = uiTable(c); + uiTableModelHandler *mh = t->model->mh; + + switch (nmhdr->code) { + case LVN_GETDISPINFO: + *lResult = onLVN_GETDISPINFO(t, (NMLVDISPINFOW *) nmhdr); + return TRUE; + } + return FALSE; +} + +$ TODO =================== + +static void uiTableDestroy(uiControl *c) +{ + uiTable *t = uiTable(c); + uiTableModel *model = t->model; + std::vector::iterator it; + + uiWindowsUnregisterWM_NOTIFYHandler(t->hwnd); + uiWindowsEnsureDestroyWindow(t->hwnd); + // detach table from model + for (it = model->tables.begin(); it != model->tables.end(); it++) { + if (*it == t) { + model->tables.erase(it); + break; + } + } + // free the columns + for (auto col : *(t->columns)) { + uiprivFree(col->name); + uiprivFree(col); + } + delete t->columns; + uiFreeControl(uiControl(t)); +} + +uiWindowsControlAllDefaultsExceptDestroy(uiTable) + +// suggested listview sizing from http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing: +// "columns widths that avoid truncated data x an integral number of items" +// Don't think that'll cut it when some cells have overlong data (eg +// stupidly long URLs). So for now, just hardcode a minimum. +// TODO: Investigate using LVM_GETHEADER/HDM_LAYOUT here... +#define tableMinWidth 107 /* in line with other controls */ +#define tableMinHeight (14*3) /* header + 2 lines (roughly) */ + +static void uiTableMinimumSize(uiWindowsControl *c, int *width, int *height) +{ + uiTable *t = uiTable(c); + uiWindowsSizing sizing; + int x, y; + + x = tableMinWidth; + y = tableMinHeight; + uiWindowsGetSizing(t->hwnd, &sizing); + uiWindowsSizingDlgUnitsToPixels(&sizing, &x, &y); + *width = x; + *height = y; +} + void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand) { uiTable *t = c->t; @@ -117,40 +205,11 @@ void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand) logLastError(L"error calling LVM_INSERTCOLUMN in uiTableColumnPartSetTextPart()"); } -void uiTableColumnAppendImagePart(uiTableColumn *c, int modelColumn, int expand) -{ - // not implemented -} - -void uiTableColumnAppendButtonPart(uiTableColumn *c, int modelColumn, int expand) -{ - // not implemented -} - -void uiTableColumnAppendCheckboxPart(uiTableColumn *c, int modelColumn, int expand) -{ - // not implemented -} - -void uiTableColumnAppendProgressBarPart(uiTableColumn *c, int modelColumn, int expand) -{ - // not implemented -} - void uiTableColumnPartSetEditable(uiTableColumn *c, int part, int editable) { // TODO } -void uiTableColumnPartSetTextColor(uiTableColumn *c, int part, int modelColumn) -{ - // not implemented -} - -// uiTable implementation - -uiWindowsControlAllDefaultsExceptDestroy(uiTable) - uiTableColumn *uiTableAppendColumn(uiTable *t, const char *name) { uiTableColumn *c; @@ -169,98 +228,6 @@ void uiTableSetRowBackgroundColorModelColumn(uiTable *t, int modelColumn) // not implemented } -static void uiTableDestroy(uiControl *c) -{ - uiTable *t = uiTable(c); - uiTableModel *model = t->model; - std::vector::iterator it; - - uiWindowsUnregisterWM_NOTIFYHandler(t->hwnd); - uiWindowsEnsureDestroyWindow(t->hwnd); - // detach table from model - for (it = model->tables.begin(); it != model->tables.end(); it++) { - if (*it == t) { - model->tables.erase(it); - break; - } - } - // free the columns - for (auto col : *(t->columns)) { - uiprivFree(col->name); - uiprivFree(col); - } - delete t->columns; - uiFreeControl(uiControl(t)); -} - -// suggested listview sizing from http://msdn.microsoft.com/en-us/library/windows/desktop/dn742486.aspx#sizingandspacing: -// "columns widths that avoid truncated data x an integral number of items" -// Don't think that'll cut it when some cells have overlong data (eg -// stupidly long URLs). So for now, just hardcode a minimum. -// TODO: Investigate using LVM_GETHEADER/HDM_LAYOUT here... -#define tableMinWidth 107 /* in line with other controls */ -#define tableMinHeight (14*3) /* header + 2 lines (roughly) */ - -static void uiTableMinimumSize(uiWindowsControl *c, int *width, int *height) -{ - uiTable *t = uiTable(c); - uiWindowsSizing sizing; - int x, y; - - x = tableMinWidth; - y = tableMinHeight; - uiWindowsGetSizing(t->hwnd, &sizing); - uiWindowsSizingDlgUnitsToPixels(&sizing, &x, &y); - *width = x; - *height = y; -} - -static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult) -{ - uiTable *t = uiTable(c); - uiTableModelHandler *mh = t->model->mh; - BOOL ret = FALSE; - - switch (nmhdr->code) { - case LVN_GETDISPINFO: - { - NMLVDISPINFOW *di; - LVITEMW *item; - int row, col; - uiTableColumn *tc; - int mcol; - uiTableModelColumnType typ; - - di = (NMLVDISPINFOW *)nmhdr; - item = &(di->item); - if (!(item->mask & LVIF_TEXT)) - break; - row = item->iItem; - col = item->iSubItem; - if (col < 0 || col >= (int)t->columns->size()) - break; - tc = (uiTableColumn *)t->columns->at(col); - mcol = tc->modelColumn; - typ = (*mh->ColumnType)(mh, t->model, mcol); - - if (typ == uiTableModelColumnString) { - void* data; - int n; - - data = (*(mh->CellValue))(mh, t->model, row, mcol); - n = MultiByteToWideChar(CP_UTF8, 0, (const char *)data, -1, item->pszText, item->cchTextMax); - // make sure clipped strings are nul-terminated - if (n >= item->cchTextMax) - item->pszText[item->cchTextMax-1] = L'\0'; - } else - item->pszText[0] = L'\0'; - break; - } - } - *lResult = 0; - return ret; -} - uiTable *uiNewTable(uiTableModel *model) { uiTable *t; From 41d63bd0cf7b8cc9b53dd2442cf04a9bf0236284 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 8 Jun 2018 21:44:55 -0400 Subject: [PATCH 1157/1329] And finished rewriting table.cpp. That wasn't too bad for just text columns. Let's hope it works. --- windows/table.cpp | 153 +++++++++++++++++++++++++++++++++------------- 1 file changed, 110 insertions(+), 43 deletions(-) diff --git a/windows/table.cpp b/windows/table.cpp index 529485f9..63288773 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -5,9 +5,24 @@ struct uiTableModel { std::vector *tables; }; +static uiTableTextColumnOptionalParams defaultTextColumnOptionalParams = { + .ColorModelColumn = -1, +}; + struct columnParams { int textModelColumn; int textEditableColumn; + uiTableTextColumnOptionalParams textParams; + + int imageModelColumn; + + int checkboxModelColumn; + int checkboxEditableModelColumn; + + int progressBarModelColumn; + + int buttonModelColumn; + int buttonClickableModelColumn; }; struct uiTable { @@ -15,6 +30,7 @@ struct uiTable { uiTableModel *model; HWND hwnd; std::vector *columns; + WPARAM nColumns; // MSDN says we have to keep LVN_GETDISPINFO strings we allocate around at least until "two additional LVN_GETDISPINFO messages have been sent". // we'll use this queue to do so; the "two additional" part is encoded in the initial state of the queue std::queue *dispinfoStrings; @@ -98,8 +114,9 @@ static LRESULT onLVN_GETDISPINFO(uiTable *t, NMLVDISPINFOW *nm) WCHAR *wstr; wstr = t->dipsinfoString->front(); + if (wstr != NULL) + uiprivFree(wstr); t->dispinfoString->pop(); - uiprivFree(wstr); p = (*(t->columns))[nm->item.iSubItem]; // TODO is this condition ever not going to be true for libui? @@ -128,28 +145,33 @@ static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult) return FALSE; } -$ TODO =================== - static void uiTableDestroy(uiControl *c) { uiTable *t = uiTable(c); uiTableModel *model = t->model; std::vector::iterator it; + WCHAR *wstr; uiWindowsUnregisterWM_NOTIFYHandler(t->hwnd); uiWindowsEnsureDestroyWindow(t->hwnd); // detach table from model for (it = model->tables.begin(); it != model->tables.end(); it++) { if (*it == t) { - model->tables.erase(it); + model->tables->erase(it); break; } } - // free the columns - for (auto col : *(t->columns)) { - uiprivFree(col->name); - uiprivFree(col); + // empty the string queue + while (t->dispinfoStrings->size() != 0) { + wstr = t->dispinfoStrings->front(); + if (wstr != NULL) + uiprivFree(wstr); + t->dispinfoStrings->pop(); } + delete t->dispinfoStrings; + // free the columns + for (auto col : *(t->columns)) + uiprivFree(col); delete t->columns; uiFreeControl(uiControl(t)); } @@ -162,7 +184,7 @@ uiWindowsControlAllDefaultsExceptDestroy(uiTable) // stupidly long URLs). So for now, just hardcode a minimum. // TODO: Investigate using LVM_GETHEADER/HDM_LAYOUT here... #define tableMinWidth 107 /* in line with other controls */ -#define tableMinHeight (14*3) /* header + 2 lines (roughly) */ +#define tableMinHeight (14 * 3) /* header + 2 lines (roughly) */ static void uiTableMinimumSize(uiWindowsControl *c, int *width, int *height) { @@ -176,51 +198,97 @@ static void uiTableMinimumSize(uiWindowsControl *c, int *width, int *height) uiWindowsSizingDlgUnitsToPixels(&sizing, &x, &y); *width = x; *height = y; -} +}' -void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand) +$ TODO =================== + +static struct columnParams *appendColumn(uiTable *t, const char *name, int colfmt) { - uiTable *t = c->t; - int lvIndex = 0; + WCHAR *wstr; LVCOLUMNW lvc; - - if (c->modelColumn >= 0) - return; // multiple parts not implemented - c->modelColumn = modelColumn; - - // work out appropriate listview index for the column - for (auto candidate : *(t->columns)) { - if (candidate == c) - break; - if (candidate->modelColumn >= 0) - lvIndex++; - } + struct columnParams *p; ZeroMemory(&lvc, sizeof (LVCOLUMNW)); - lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT; /* | LVCF_SUBITEM; */ - lvc.fmt = LVCFMT_LEFT; - lvc.cx = 120; // TODO - lvc.pszText = c->name; - if (SendMessageW(c->t->hwnd, LVM_INSERTCOLUMN, (WPARAM) lvIndex, (LPARAM) (&lvc)) == (LRESULT) (-1)) - logLastError(L"error calling LVM_INSERTCOLUMN in uiTableColumnPartSetTextPart()"); + lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT; + lvc.fmt = colfmt; + lvc.cx = 120; // TODO + wstr = toUTF16(name); + lvc.pszText = wstr; + if (SendMessageW(t->hwnd, LVM_INSERTCOLUMNW, t->nColumns, (LPARAM) (&lvc)) == (LRESULT) (-1)) + logLastError(L"error calling LVM_INSERTCOLUMNW in appendColumn()"); + uiprivFree(wstr); + t->nColumns++; + + p = uiprivNew(struct columnParams); + p->textModelColumn = -1; + p->imageModelColumn = -1; + p->checkboxModelColumn = -1; + p->progressBarModelColumn = -1; + p->buttonModelColumn = -1; + t->columns->push_back(p); + return p; } -void uiTableColumnPartSetEditable(uiTableColumn *c, int part, int editable) +void uiTableAppendTextColumn(uiTable *t, const char *name, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *params) +{ + struct columnParams *p; + + p = appendColumn(t, name, LVCFMT_LEFT); + p->textModelColumn = textModelColumn; + p->textEditableColumn = textEditableModelColumn; + if (params != NULL) + p->textParams = *params; + else + p->textParams = defaultTextColumnOptionalParams; +} + +void uiTableAppendImageColumn(uiTable *t, const char *name, int imageModelColumn) { // TODO } -uiTableColumn *uiTableAppendColumn(uiTable *t, const char *name) +void uiTableAppendImageTextColumn(uiTable *t, const char *name, int imageModelColumn, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *textParams) { - uiTableColumn *c; - - c = uiprivNew(uiTableColumn); - c->name = toUTF16(name); - c->t = t; - c->modelColumn = -1; // -1 = unassigned - // we defer the actual ListView_InsertColumn call until a part is added... - t->columns->push_back(c); - return c; + struct columnParams *p; + + p = appendColumn(t, name, LVCFMT_LEFT | LVCFMT_IMAGE); + p->textModelColumn = textModelColumn; + p->textEditableColumn = textEditableModelColumn; + if (params != NULL) + p->textParams = *params; + else + p->textParams = defaultTextColumnOptionalParams; + p->imageModelColumn = imageModelColumn; +} + +void uiTableAppendCheckboxColumn(uiTable *t, const char *name, int checkboxModelColumn, int checkboxEditableModelColumn) +{ + // TODO +} + +void uiTableAppendCheckboxTextColumn(uiTable *t, const char *name, int checkboxModelColumn, int checkboxEditableModelColumn, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *textParams) +{ + struct columnParams *p; + + p = appendColumn(t, name, LVCFMT_LEFT); + p->textModelColumn = textModelColumn; + p->textEditableColumn = textEditableModelColumn; + if (params != NULL) + p->textParams = *params; + else + p->textParams = defaultTextColumnOptionalParams; + p->checkboxModelColumn = checkboxModelColumn; + p->checkboxEditableColumn = checkboxEditableModelColumn; +} + +void uiTableAppendProgressBarColumn(uiTable *t, const char *name, int progressModelColumn) +{ + // TODO +} + +void uiTableAppendButtonColumn(uiTable *t, const char *name, int buttonTextModelColumn, int buttonClickableModelColumn) +{ + // TODO } void uiTableSetRowBackgroundColorModelColumn(uiTable *t, int modelColumn) @@ -254,4 +322,3 @@ uiTable *uiNewTable(uiTableModel *model) logLastError(L"error calling LVM_SETITEMCOUNT in uiNewTable()"); return t; } - From 71a310909d59e9e10487256609759717240666e6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 8 Jun 2018 21:45:30 -0400 Subject: [PATCH 1158/1329] Oops --- windows/table.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/windows/table.cpp b/windows/table.cpp index 63288773..6c4b198f 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -198,9 +198,7 @@ static void uiTableMinimumSize(uiWindowsControl *c, int *width, int *height) uiWindowsSizingDlgUnitsToPixels(&sizing, &x, &y); *width = x; *height = y; -}' - -$ TODO =================== +} static struct columnParams *appendColumn(uiTable *t, const char *name, int colfmt) { From 69e91a96789d55797336c5ace2cd86da703df767 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 8 Jun 2018 22:05:11 -0400 Subject: [PATCH 1159/1329] Fixed build errors and some logic errors. Let's hope it works! --- windows/table.cpp | 36 +++++++++++++++++++++--------------- windows/winapi.hpp | 1 + 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/windows/table.cpp b/windows/table.cpp index 6c4b198f..79c0bdb0 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -6,7 +6,7 @@ struct uiTableModel { }; static uiTableTextColumnOptionalParams defaultTextColumnOptionalParams = { - .ColorModelColumn = -1, + /*TODO.ColorModelColumn = */-1, }; struct columnParams { @@ -17,7 +17,7 @@ struct columnParams { int imageModelColumn; int checkboxModelColumn; - int checkboxEditableModelColumn; + int checkboxEditableColumn; int progressBarModelColumn; @@ -29,7 +29,7 @@ struct uiTable { uiWindowsControl c; uiTableModel *model; HWND hwnd; - std::vector *columns; + std::vector *columns; WPARAM nColumns; // MSDN says we have to keep LVN_GETDISPINFO strings we allocate around at least until "two additional LVN_GETDISPINFO messages have been sent". // we'll use this queue to do so; the "two additional" part is encoded in the initial state of the queue @@ -113,20 +113,20 @@ static LRESULT onLVN_GETDISPINFO(uiTable *t, NMLVDISPINFOW *nm) uiTableData *data; WCHAR *wstr; - wstr = t->dipsinfoString->front(); + wstr = t->dispinfoStrings->front(); if (wstr != NULL) uiprivFree(wstr); - t->dispinfoString->pop(); + t->dispinfoStrings->pop(); p = (*(t->columns))[nm->item.iSubItem]; // TODO is this condition ever not going to be true for libui? if ((nm->item.mask & LVIF_TEXT) != 0) if (p->textModelColumn != -1) { - data = (*(t->model->mh->Value))(t->model->mh, t->model, nm->item.iItem, p->textModelColumn); + data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->item.iItem, p->textModelColumn); wstr = toUTF16(uiTableDataString(data)); uiFreeTableData(data); nm->item.pszText = wstr; - t->dispinfoString->push(wstr); + t->dispinfoStrings->push(wstr); } return 0; @@ -135,7 +135,6 @@ static LRESULT onLVN_GETDISPINFO(uiTable *t, NMLVDISPINFOW *nm) static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult) { uiTable *t = uiTable(c); - uiTableModelHandler *mh = t->model->mh; switch (nmhdr->code) { case LVN_GETDISPINFO: @@ -155,7 +154,7 @@ static void uiTableDestroy(uiControl *c) uiWindowsUnregisterWM_NOTIFYHandler(t->hwnd); uiWindowsEnsureDestroyWindow(t->hwnd); // detach table from model - for (it = model->tables.begin(); it != model->tables.end(); it++) { + for (it = model->tables->begin(); it != model->tables->end(); it++) { if (*it == t) { model->tables->erase(it); break; @@ -252,8 +251,8 @@ void uiTableAppendImageTextColumn(uiTable *t, const char *name, int imageModelCo p = appendColumn(t, name, LVCFMT_LEFT | LVCFMT_IMAGE); p->textModelColumn = textModelColumn; p->textEditableColumn = textEditableModelColumn; - if (params != NULL) - p->textParams = *params; + if (textParams != NULL) + p->textParams = *textParams; else p->textParams = defaultTextColumnOptionalParams; p->imageModelColumn = imageModelColumn; @@ -271,8 +270,8 @@ void uiTableAppendCheckboxTextColumn(uiTable *t, const char *name, int checkboxM p = appendColumn(t, name, LVCFMT_LEFT); p->textModelColumn = textModelColumn; p->textEditableColumn = textEditableModelColumn; - if (params != NULL) - p->textParams = *params; + if (textParams != NULL) + p->textParams = *textParams; else p->textParams = defaultTextColumnOptionalParams; p->checkboxModelColumn = checkboxModelColumn; @@ -301,14 +300,14 @@ uiTable *uiNewTable(uiTableModel *model) uiWindowsNewControl(uiTable, t); - t->columns = new std::vector; + t->columns = new std::vector; t->model = model; t->hwnd = uiWindowsEnsureCreateControlHWND(WS_EX_CLIENTEDGE, WC_LISTVIEW, L"", LVS_REPORT | LVS_OWNERDATA | LVS_SINGLESEL | WS_TABSTOP | WS_HSCROLL | WS_VSCROLL, hInstance, NULL, TRUE); - model->tables.push_back(t); + model->tables->push_back(t); uiWindowsRegisterWM_NOTIFYHandler(t->hwnd, onWM_NOTIFY, uiControl(t)); // TODO: try LVS_EX_AUTOSIZECOLUMNS @@ -318,5 +317,12 @@ uiTable *uiNewTable(uiTableModel *model) n = (*(model->mh->NumRows))(model->mh, model); if (SendMessageW(t->hwnd, LVM_SETITEMCOUNT, (WPARAM) n, 0) == 0) logLastError(L"error calling LVM_SETITEMCOUNT in uiNewTable()"); + + t->dispinfoStrings = new std::queue; + // this encodes the idea that two LVN_GETDISPINFOs must complete before we can free a string: the first real one is for the fourth call to free + t->dispinfoStrings->push(NULL); + t->dispinfoStrings->push(NULL); + t->dispinfoStrings->push(NULL); + return t; } diff --git a/windows/winapi.hpp b/windows/winapi.hpp index 4f24f607..ef595bba 100644 --- a/windows/winapi.hpp +++ b/windows/winapi.hpp @@ -57,4 +57,5 @@ #include #include #include +#include #endif From 06a8044c2c35639d1e39fd355b424880e404ff6b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 8 Jun 2018 22:11:46 -0400 Subject: [PATCH 1160/1329] Oops, forgot to keep the string queue full. It works! Now to switch to custom draw. --- windows/table.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/windows/table.cpp b/windows/table.cpp index 79c0bdb0..e60ad3c5 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -112,6 +112,7 @@ static LRESULT onLVN_GETDISPINFO(uiTable *t, NMLVDISPINFOW *nm) struct columnParams *p; uiTableData *data; WCHAR *wstr; + bool queueUpdated = false; wstr = t->dispinfoStrings->front(); if (wstr != NULL) @@ -127,8 +128,12 @@ static LRESULT onLVN_GETDISPINFO(uiTable *t, NMLVDISPINFOW *nm) uiFreeTableData(data); nm->item.pszText = wstr; t->dispinfoStrings->push(wstr); + queueUpdated = true; } + // we don't want to pop from an empty queue, so if nothing updated the queue (no info was filled in above), just push NULL + if (!queueUpdated) + t->dispinfoStrings->push(NULL); return 0; } From c6c4dbd58083de07602ad91840af5765e0738c85 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 9 Jun 2018 10:40:42 -0400 Subject: [PATCH 1161/1329] Started handling colors in the Windows table code. --- windows/table.cpp | 73 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 71 insertions(+), 2 deletions(-) diff --git a/windows/table.cpp b/windows/table.cpp index e60ad3c5..c311e7b3 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -34,6 +34,7 @@ struct uiTable { // MSDN says we have to keep LVN_GETDISPINFO strings we allocate around at least until "two additional LVN_GETDISPINFO messages have been sent". // we'll use this queue to do so; the "two additional" part is encoded in the initial state of the queue std::queue *dispinfoStrings; + int backgroundColumn; }; uiTableModel *uiNewTableModel(uiTableModelHandler *mh) @@ -109,7 +110,7 @@ void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) static LRESULT onLVN_GETDISPINFO(uiTable *t, NMLVDISPINFOW *nm) { - struct columnParams *p; + static struct columnParams *p; uiTableData *data; WCHAR *wstr; bool queueUpdated = false; @@ -137,6 +138,67 @@ static LRESULT onLVN_GETDISPINFO(uiTable *t, NMLVDISPINFOW *nm) return 0; } +static COLORREF blend(COLORREF base, double r, double g, double b, double a) +{ + double br, bg, bb; + + // TODO find a better fix than this; is listview already alphablending? + if (base != 0xFF000000) { + br = ((double) GetRValue(base)) / 255.0; + bg = ((double) GetGValue(base)) / 255.0; + bb = ((double) GetBValue(base)) / 255.0; + } else { + // TODO find the right color here + br = 1.0; + bg = 1.0; + bb = 1.0; + } + + br = (r * a) + (br * (1.0 - a)); + bg = (g * a) + (bg * (1.0 - a)); + bb = (b * a) + (bb * (1.0 - a)); + return RGB((BYTE) (br * 255), + (BYTE) (bg * 255), + (BYTE) (bb * 255)); +} + +static LRESULT onNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm) +{ + struct columnParams *p; + uiTableData *data; + double r, g, b, a; + LRESULT ret; + + switch (nm->nmcd.dwDrawStage) { + case CDDS_PREPAINT: + return CDRF_NOTIFYITEMDRAW; + case CDDS_ITEMPREPAINT: + if (t->backgroundColumn != -1) { + data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->nmcd.dwItemSpec, t->backgroundColumn); + if (data != NULL) { + uiTableDataColor(data, &r, &g, &b, &a); + uiFreeTableData(data); + nm->clrTextBk = blend(nm->clrTextBk, r, g, b, a); + } + } + return CDRF_NEWFONT | CDRF_NOTIFYSUBITEMDRAW; + case CDDS_SUBITEM | CDDS_ITEMPREPAINT: + p = (*(t->columns))[nm->iSubItem]; + if (p->textParams.ColorModelColumn != -1) { + data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->nmcd.dwItemSpec, p->textParams.ColorModelColumn); + if (data != NULL) { + uiTableDataColor(data, &r, &g, &b, &a); + uiFreeTableData(data); + // TODO the fallback here isn't correct + nm->clrText = blend(nm->clrText, r, g, b, a); + } + } + // TODO this keeps the color for the rest of the row + return CDRF_NEWFONT; + } + return CDRF_DODEFAULT; +} + static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult) { uiTable *t = uiTable(c); @@ -145,6 +207,9 @@ static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult) case LVN_GETDISPINFO: *lResult = onLVN_GETDISPINFO(t, (NMLVDISPINFOW *) nmhdr); return TRUE; + case NM_CUSTOMDRAW: + *lResult = onNM_CUSTOMDRAW(t, (NMLVCUSTOMDRAW *) nmhdr); + return TRUE; } return FALSE; } @@ -295,7 +360,9 @@ void uiTableAppendButtonColumn(uiTable *t, const char *name, int buttonTextModel void uiTableSetRowBackgroundColorModelColumn(uiTable *t, int modelColumn) { - // not implemented + // TODO make the names consistent + t->backgroundColumn = modelColumn; + // TODO redraw? } uiTable *uiNewTable(uiTableModel *model) @@ -329,5 +396,7 @@ uiTable *uiNewTable(uiTableModel *model) t->dispinfoStrings->push(NULL); t->dispinfoStrings->push(NULL); + t->backgroundColumn = -1; + return t; } From 1c9f9627c0bab2fc11531f15c477e0f1d6809c3a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 9 Jun 2018 12:15:50 -0400 Subject: [PATCH 1162/1329] Started image support for Windows tables. --- windows/table.cpp | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/windows/table.cpp b/windows/table.cpp index c311e7b3..e0fafc51 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -35,6 +35,9 @@ struct uiTable { // we'll use this queue to do so; the "two additional" part is encoded in the initial state of the queue std::queue *dispinfoStrings; int backgroundColumn; + + // custom draw state + COLORREF clrItemText; }; uiTableModel *uiNewTableModel(uiTableModelHandler *mh) @@ -132,6 +135,10 @@ static LRESULT onLVN_GETDISPINFO(uiTable *t, NMLVDISPINFOW *nm) queueUpdated = true; } + if ((nm->item.mask & LVIF_IMAGE) != 0) + if (p->imageModelColumn != -1) + nm->item.iImage = 1; + // we don't want to pop from an empty queue, so if nothing updated the queue (no info was filled in above), just push NULL if (!queueUpdated) t->dispinfoStrings->push(NULL); @@ -142,17 +149,14 @@ static COLORREF blend(COLORREF base, double r, double g, double b, double a) { double br, bg, bb; - // TODO find a better fix than this; is listview already alphablending? - if (base != 0xFF000000) { - br = ((double) GetRValue(base)) / 255.0; - bg = ((double) GetGValue(base)) / 255.0; - bb = ((double) GetBValue(base)) / 255.0; - } else { - // TODO find the right color here - br = 1.0; - bg = 1.0; - bb = 1.0; - } + // TODO find a better fix than this + // TODO s listview already alphablending? + // TODO find the right color here + if (base == 0xFF000000) + base = GetSysColor(COLOR_WINDOW); + br = ((double) GetRValue(base)) / 255.0; + bg = ((double) GetGValue(base)) / 255.0; + bb = ((double) GetBValue(base)) / 255.0; br = (r * a) + (br * (1.0 - a)); bg = (g * a) + (bg * (1.0 - a)); @@ -181,19 +185,20 @@ static LRESULT onNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm) nm->clrTextBk = blend(nm->clrTextBk, r, g, b, a); } } + t->clrItemText = nm->clrText; return CDRF_NEWFONT | CDRF_NOTIFYSUBITEMDRAW; case CDDS_SUBITEM | CDDS_ITEMPREPAINT: p = (*(t->columns))[nm->iSubItem]; + // we need this as previous subitems will persist their colors + nm->clrText = t->clrItemText; if (p->textParams.ColorModelColumn != -1) { data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->nmcd.dwItemSpec, p->textParams.ColorModelColumn); if (data != NULL) { uiTableDataColor(data, &r, &g, &b, &a); uiFreeTableData(data); - // TODO the fallback here isn't correct - nm->clrText = blend(nm->clrText, r, g, b, a); + nm->clrText = blend(nm->clrTextBk, r, g, b, a); } } - // TODO this keeps the color for the rest of the row return CDRF_NEWFONT; } return CDRF_DODEFAULT; @@ -318,7 +323,7 @@ void uiTableAppendImageTextColumn(uiTable *t, const char *name, int imageModelCo { struct columnParams *p; - p = appendColumn(t, name, LVCFMT_LEFT | LVCFMT_IMAGE); + p = appendColumn(t, name, LVCFMT_LEFT); p->textModelColumn = textModelColumn; p->textEditableColumn = textEditableModelColumn; if (textParams != NULL) @@ -384,8 +389,8 @@ uiTable *uiNewTable(uiTableModel *model) // TODO: try LVS_EX_AUTOSIZECOLUMNS SendMessageW(t->hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, - (WPARAM) (LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP), - (LPARAM) (LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP)); + (WPARAM) (LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP | LVS_EX_SUBITEMIMAGES), + (LPARAM) (LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP | LVS_EX_SUBITEMIMAGES)); n = (*(model->mh->NumRows))(model->mh, model); if (SendMessageW(t->hwnd, LVM_SETITEMCOUNT, (WPARAM) n, 0) == 0) logLastError(L"error calling LVM_SETITEMCOUNT in uiNewTable()"); From ff6468565587ceb88905d453e657a09c13a96cf9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 9 Jun 2018 13:57:43 -0400 Subject: [PATCH 1163/1329] More work. List View is starting to show its dumbness again. --- windows/table.cpp | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/windows/table.cpp b/windows/table.cpp index e0fafc51..9ef253bb 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -37,6 +37,8 @@ struct uiTable { int backgroundColumn; // custom draw state + HIMAGELIST dummyLarge; + HIMAGELIST dummySmall; COLORREF clrItemText; }; @@ -137,7 +139,7 @@ static LRESULT onLVN_GETDISPINFO(uiTable *t, NMLVDISPINFOW *nm) if ((nm->item.mask & LVIF_IMAGE) != 0) if (p->imageModelColumn != -1) - nm->item.iImage = 1; + nm->item.iImage = 0; // we don't want to pop from an empty queue, so if nothing updated the queue (no info was filled in above), just push NULL if (!queueUpdated) @@ -152,7 +154,7 @@ static COLORREF blend(COLORREF base, double r, double g, double b, double a) // TODO find a better fix than this // TODO s listview already alphablending? // TODO find the right color here - if (base == 0xFF000000) + if (base == CLR_DEFAULT) base = GetSysColor(COLOR_WINDOW); br = ((double) GetRValue(base)) / 255.0; bg = ((double) GetGValue(base)) / 255.0; @@ -199,6 +201,7 @@ static LRESULT onNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm) nm->clrText = blend(nm->clrTextBk, r, g, b, a); } } + // TODO draw background on image columns if needed return CDRF_NEWFONT; } return CDRF_DODEFAULT; @@ -247,6 +250,7 @@ static void uiTableDestroy(uiControl *c) for (auto col : *(t->columns)) uiprivFree(col); delete t->columns; + // t->dummyLarge and t->dummySmall will be automatically destroyed uiFreeControl(uiControl(t)); } @@ -403,5 +407,23 @@ uiTable *uiNewTable(uiTableModel *model) t->backgroundColumn = -1; + // TODO update these when the DPI changes + t->dummyLarge = ImageList_Create( + GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), + ILC_COLOR32, + 1, 0); + if (t->dummyLarge == NULL) + logLastError(L"error calling ImageList_Create() for dummy large image list in uiNewTable()"); + // TODO will this return NULL here because it's an initial state? + SendMessageW(t->hwnd, LVM_SETIMAGELIST, LVSIL_NORMAL, (LPARAM) (t->dummyLarge)); + t->dummySmall = ImageList_Create( + GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), + ILC_COLOR32, + 1, 0); + if (t->dummySmall == NULL) + logLastError(L"error calling ImageList_Create() for dummy small image list in uiNewTable()"); + // TODO will this return NULL here because it's an initial state? + SendMessageW(t->hwnd, LVM_SETIMAGELIST, LVSIL_SMALL, (LPARAM) (t->dummySmall)); + return t; } From 3aee505f4e38e37ff25d69bd5c1be5d89caf053d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 9 Jun 2018 14:32:04 -0400 Subject: [PATCH 1164/1329] Weirdness workarounds. --- windows/table.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/windows/table.cpp b/windows/table.cpp index 9ef253bb..37b5b700 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -141,6 +141,14 @@ static LRESULT onLVN_GETDISPINFO(uiTable *t, NMLVDISPINFOW *nm) if (p->imageModelColumn != -1) nm->item.iImage = 0; + // having an image list always leaves space for an image on the main item :| + // other places on the internet imply that you should be able to do this but that it shouldn't work + // but it works perfectly (and pixel-perfectly too) for me, so... + if (nm->item.iSubItem == 0 && p->imageModelColumn == -1) { + nm->item.mask |= LVIF_INDENT; + nm->item.iIndent = -1; + } + // we don't want to pop from an empty queue, so if nothing updated the queue (no info was filled in above), just push NULL if (!queueUpdated) t->dispinfoStrings->push(NULL); From a858300f257959793df085cd80569f466635a336 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 9 Jun 2018 19:24:36 -0400 Subject: [PATCH 1165/1329] Filled in image.cpp. Also switched on WIC since we'll need it for uiArea, though uiTable will still need classic GDI. Now let's integrate this into uiTable. --- windows/CMakeLists.txt | 2 +- windows/image.cpp | 137 +++++++++++++++++++++++++++++++++++-- windows/init.cpp | 5 ++ windows/table.cpp | 2 + windows/uipriv_windows.hpp | 7 ++ windows/winapi.hpp | 1 + 6 files changed, 148 insertions(+), 6 deletions(-) diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index b871b529..31ba2c93 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -78,7 +78,7 @@ set(_LIBUI_INCLUDEDIRS _LIBUI_INCLUDEDIRS PARENT_SCOPE) # TODO prune this list set(_LIBUI_LIBS - user32 kernel32 gdi32 comctl32 uxtheme msimg32 comdlg32 d2d1 dwrite ole32 oleaut32 oleacc uuid + user32 kernel32 gdi32 comctl32 uxtheme msimg32 comdlg32 d2d1 dwrite ole32 oleaut32 oleacc uuid windowscodecs PARENT_SCOPE) if(NOT MSVC) diff --git a/windows/image.cpp b/windows/image.cpp index e3b78475..a2fb2853 100644 --- a/windows/image.cpp +++ b/windows/image.cpp @@ -1,12 +1,23 @@ #include "uipriv_windows.hpp" -// stubbed out windows image list implementation. -// Required for uiTable control, but windows implemenation -// doesn't currently have image support. + +IWICImagingFactory *uiprivWICFactory = NULL; + +HRESULT uiprivInitImage(void) +{ + return CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, + IID_IWICImagingFactory, (void **) (&uiprivWICFactory)); +} + +void uiprivUninitImage(void) +{ + uiprivWICFactory->Release(); + uiprivWICFactory = NULL; +} struct uiImage { double width; double height; - // HIMAGELIST images; + std::vector *bitmaps; }; uiImage *uiNewImage(double width, double height) @@ -16,16 +27,132 @@ uiImage *uiNewImage(double width, double height) i = uiprivNew(uiImage); i->width = width; i->height = height; + i->bitmaps = new std::vector; return i; } void uiFreeImage(uiImage *i) { + for (IWICBitmap *b : *(i->bitmaps)) + b->Release(); + delete i->bitmaps; uiprivFree(i); } void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int pixelStride) { - // not implemented + IWICBitmap *b; + HRESULT hr; + + hr = uiprivWICFactory->CreateBitmapFromMemory(pixelWidth, pixelHeight, + GUID_WICPixelFormat32bppRGBA, pixelStride, + pixelStride * pixelHeight, (BYTE *) pixels, + &b); + if (hr != S_OK) + logHRESULT(L"error calling CreateBitmapFromMemory() in uiImageAppend()", hr); + i->bitmaps->push_back(b); } +struct matcher { + IWICBitmap *best; + int distX; + int distY; + int targetX; + int targetY; + bool foundLarger; +}; + +// TODO is this the right algorithm? +static void match(IWICBitmap *b, struct matcher *m) +{ + UINT ux, uy; + int x, y; + int x2, y2; + HRESULT hr; + + hr = b->GetSize(&ux, &uy); + if (hr != S_OK) + logHRESULT(L"error calling GetSize() in match()", hr); + x = ux; + y = uy; + if (m->best == NULL) + goto writeMatch; + + if (x < m->targetX && y < m->targetY) + if (m->foundLarger) + // always prefer larger ones + return; + if (x >= m->targetX && y >= m->targetY && !m->foundLarger) + // we set foundLarger below + goto writeMatch; + +// TODO +#define abs(x) ((x) < 0 ? -(x) : (x)) + x2 = abs(m->targetX - x); + y2 = abs(m->targetY - y); + if (x2 < m->distX && y2 < m->distY) + goto writeMatch; + + // TODO weight one dimension? threshhold? + return; + +writeMatch: + // must set this here too; otherwise the first image will never have ths set + if (x >= m->targetX && y >= m->targetY && !m->foundLarger) + m->foundLarger = true; + m->best = b; + m->distX = abs(m->targetX - x); + m->distY = abs(m->targetY - y); +} + +IWICBitmap *uiprivImageAppropriateForDC(uiImage *i, HDC dc) +{ + struct matcher m; + + m.best = NULL; + m.distX = INT_MAX; + m.distY = INT_MAX; + m.targetX = i->width * GetDeviceCaps(dc, LOGPIXELSX); + m.targetY = i->height * GetDeviceCaps(dc, LOGPIXELSY); + m.foundLarger = false; + for (IWICBitmap *b : *(i->bitmaps)) + match(b, &m); + return m.best; +} + +// TODO see if we can really pass NULL to CreateDIBSection()'s HDC parameter, and if so, use HBITMAPs before WIC maybe? +HBITMAP uiprivWICToGDI(IWICBitmap *b, HDC dc) +{ + BITMAPINFO bmi; + UINT width, height; + HBITMAP hb; + VOID *bits; + BITMAP bmp; + HRESULT hr; + + ZeroMemory(&bmi, sizeof (BITMAPINFO)); + bmi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER); + hr = b->GetSize(&width, &height); + if (hr != S_OK) + logHRESULT(L"error calling GetSize() in uiprivWICToGDI()", hr); + bmi.bmiHeader.biWidth = width; + bmi.bmiHeader.biHeight = -((int) height); + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + hb = CreateDIBSection(dc, &bmi, DIB_RGB_COLORS, + &bits, NULL, 0); + if (hb == NULL) + logLastError(L"error calling CreateDIBSection() in uiprivWICToGDI()"); + + // now we need to figure out the stride of the image data GDI gave us + // TODO find out if CreateDIBSection fills that in bmi for us + if (GetObject(hb, sizeof (BITMAP), &bmp) == 0) + logLastError(L"error calling GetObject() in uiprivWICToGDI()"); + hr = b->CopyPixels(NULL, bmp.bmWidthBytes, + bmp.bmWidthBytes * bmp.bmHeight, (BYTE *) bits); + if (hr != S_OK) + logHRESULT(L"error calling CopyPixels() in uiprivWICToGDI()", hr); + + return hb; +} diff --git a/windows/init.cpp b/windows/init.cpp index 24831143..2fb52c0f 100644 --- a/windows/init.cpp +++ b/windows/init.cpp @@ -131,11 +131,16 @@ const char *uiInit(uiInitOptions *o) if (registerD2DScratchClass(hDefaultIcon, hDefaultCursor) == 0) return ieLastErr("initializing D2D scratch window class"); + hr = uiprivInitImage(); + if (hr != S_OK) + return ieHRESULT("initializing WIC", hr); + return NULL; } void uiUninit(void) { + uiprivUninitImage(); uninitMenus(); unregisterD2DScratchClass(); unregisterMessageFilter(); diff --git a/windows/table.cpp b/windows/table.cpp index 37b5b700..e2fa606c 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -416,6 +416,8 @@ uiTable *uiNewTable(uiTableModel *model) t->backgroundColumn = -1; // TODO update these when the DPI changes + // TODO handle errors + // TODO try adding a real transparent image t->dummyLarge = ImageList_Create( GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), ILC_COLOR32, diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index 77982322..aab50aa7 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -163,3 +163,10 @@ extern D2D1_SIZE_F realGetSize(ID2D1RenderTarget *rt); // draw.cpp extern ID2D1DCRenderTarget *makeHDCRenderTarget(HDC dc, RECT *r); + +// image.cpp +extern IWICImagingFactory *uiprivWICFactory; +extern HRESULT uiprivInitImage(void); +extern void uiprivUninitImage(void); +extern IWICBitmap *uiprivImageAppropriateForDC(uiImage *i, HDC dc); +extern HBITMAP uiprivWICToGDI(IWICBitmap *b, HDC dc); diff --git a/windows/winapi.hpp b/windows/winapi.hpp index ef595bba..f1306db7 100644 --- a/windows/winapi.hpp +++ b/windows/winapi.hpp @@ -42,6 +42,7 @@ #include #include #include +#include #include #include From b43fb4b247c03379bb3b84eb67fc7c86bd944450 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 9 Jun 2018 20:35:05 -0400 Subject: [PATCH 1166/1329] And added images to uiTable. Right now it's loading the 32x32 images into the table instead of the 16x16 ones, but hey, it works! --- windows/table.cpp | 60 ++++++++++++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 21 deletions(-) diff --git a/windows/table.cpp b/windows/table.cpp index e2fa606c..47cedb63 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -31,14 +31,17 @@ struct uiTable { HWND hwnd; std::vector *columns; WPARAM nColumns; + int backgroundColumn; + + // owner data state // MSDN says we have to keep LVN_GETDISPINFO strings we allocate around at least until "two additional LVN_GETDISPINFO messages have been sent". // we'll use this queue to do so; the "two additional" part is encoded in the initial state of the queue std::queue *dispinfoStrings; - int backgroundColumn; + // likewise here, though the docs aren't as clear + HIMAGELIST smallImages; + int smallIndex; // custom draw state - HIMAGELIST dummyLarge; - HIMAGELIST dummySmall; COLORREF clrItemText; }; @@ -118,6 +121,9 @@ static LRESULT onLVN_GETDISPINFO(uiTable *t, NMLVDISPINFOW *nm) static struct columnParams *p; uiTableData *data; WCHAR *wstr; + HDC dc; + IWICBitmap *wb; + HBITMAP b; bool queueUpdated = false; wstr = t->dispinfoStrings->front(); @@ -138,8 +144,28 @@ static LRESULT onLVN_GETDISPINFO(uiTable *t, NMLVDISPINFOW *nm) } if ((nm->item.mask & LVIF_IMAGE) != 0) - if (p->imageModelColumn != -1) - nm->item.iImage = 0; + if (p->imageModelColumn != -1) { + dc = GetDC(t->hwnd); + if (dc == NULL) + logLastError(L"error getting DC for uiTable in onLVN_GETDISPINFO()"); + data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->item.iItem, p->imageModelColumn); + wb = uiprivImageAppropriateForDC(uiTableDataImage(data), dc); + uiFreeTableData(data); + b = uiprivWICToGDI(wb, dc); + if (ReleaseDC(t->hwnd, dc) == 0) + logLastError(L"error calling ReleaseDC() in onLVN_GETDISPINFO()"); + if (ImageList_GetImageCount(t->smallImages) <= t->smallIndex) { + if (ImageList_Add(t->smallImages, b, NULL) == -1) + logLastError(L"error calling ImageList_Add() in onLVN_GETDISPINFO()"); + } else + if (ImageList_Replace(t->smallImages, t->smallIndex, b, NULL) == 0) + logLastError(L"error calling ImageList_Replace() in onLVN_GETDISPINFO()"); + // TODO error check + DeleteObject(b); + nm->item.iImage = t->smallIndex; + t->smallIndex++; + t->smallIndex %= 3; // TODO don't hardcode this + } // having an image list always leaves space for an image on the main item :| // other places on the internet imply that you should be able to do this but that it shouldn't work @@ -258,7 +284,7 @@ static void uiTableDestroy(uiControl *c) for (auto col : *(t->columns)) uiprivFree(col); delete t->columns; - // t->dummyLarge and t->dummySmall will be automatically destroyed + // t->smallImages will be automatically destroyed uiFreeControl(uiControl(t)); } @@ -407,33 +433,25 @@ uiTable *uiNewTable(uiTableModel *model) if (SendMessageW(t->hwnd, LVM_SETITEMCOUNT, (WPARAM) n, 0) == 0) logLastError(L"error calling LVM_SETITEMCOUNT in uiNewTable()"); + t->backgroundColumn = -1; + t->dispinfoStrings = new std::queue; // this encodes the idea that two LVN_GETDISPINFOs must complete before we can free a string: the first real one is for the fourth call to free t->dispinfoStrings->push(NULL); t->dispinfoStrings->push(NULL); t->dispinfoStrings->push(NULL); - t->backgroundColumn = -1; - // TODO update these when the DPI changes // TODO handle errors // TODO try adding a real transparent image - t->dummyLarge = ImageList_Create( - GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), - ILC_COLOR32, - 1, 0); - if (t->dummyLarge == NULL) - logLastError(L"error calling ImageList_Create() for dummy large image list in uiNewTable()"); - // TODO will this return NULL here because it's an initial state? - SendMessageW(t->hwnd, LVM_SETIMAGELIST, LVSIL_NORMAL, (LPARAM) (t->dummyLarge)); - t->dummySmall = ImageList_Create( + t->smallImages = ImageList_Create( GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR32, - 1, 0); - if (t->dummySmall == NULL) - logLastError(L"error calling ImageList_Create() for dummy small image list in uiNewTable()"); + 0, 3); + if (t->smallImages == NULL) + logLastError(L"error calling ImageList_Create() in uiNewTable()"); // TODO will this return NULL here because it's an initial state? - SendMessageW(t->hwnd, LVM_SETIMAGELIST, LVSIL_SMALL, (LPARAM) (t->dummySmall)); + SendMessageW(t->hwnd, LVM_SETIMAGELIST, LVSIL_SMALL, (LPARAM) (t->smallImages)); return t; } From 0f59bf7399b6c377d6add7bce382d16b975d4c69 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 9 Jun 2018 20:42:13 -0400 Subject: [PATCH 1167/1329] More TODOs. --- windows/image.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/windows/image.cpp b/windows/image.cpp index a2fb2853..a85207f5 100644 --- a/windows/image.cpp +++ b/windows/image.cpp @@ -121,6 +121,7 @@ IWICBitmap *uiprivImageAppropriateForDC(uiImage *i, HDC dc) } // TODO see if we can really pass NULL to CreateDIBSection()'s HDC parameter, and if so, use HBITMAPs before WIC maybe? +// TODO this needs to actually scale down to fit if the image size isn't perfectly equal to a requested size I need to pass as a parameter here... HBITMAP uiprivWICToGDI(IWICBitmap *b, HDC dc) { BITMAPINFO bmi; From 4c5f0961feaded4b80868c9a94231c85d67034ca Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 9 Jun 2018 23:02:37 -0400 Subject: [PATCH 1168/1329] Started an implementation of checkboxes in table.cpp. List View simply does not seem to support state images in subitems, so we'll have to improvise. Hopefully this won't screw accessibility. --- windows/table.cpp | 120 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 112 insertions(+), 8 deletions(-) diff --git a/windows/table.cpp b/windows/table.cpp index 47cedb63..96e9011b 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -38,11 +38,15 @@ struct uiTable { // we'll use this queue to do so; the "two additional" part is encoded in the initial state of the queue std::queue *dispinfoStrings; // likewise here, though the docs aren't as clear + // TODO make sure what we're doing is even allowed HIMAGELIST smallImages; int smallIndex; // custom draw state COLORREF clrItemText; + + // checkbox + HIMAGELIST checkboxImages; }; uiTableModel *uiNewTableModel(uiTableModelHandler *mh) @@ -124,6 +128,7 @@ static LRESULT onLVN_GETDISPINFO(uiTable *t, NMLVDISPINFOW *nm) HDC dc; IWICBitmap *wb; HBITMAP b; + int checked; bool queueUpdated = false; wstr = t->dispinfoStrings->front(); @@ -132,7 +137,7 @@ static LRESULT onLVN_GETDISPINFO(uiTable *t, NMLVDISPINFOW *nm) t->dispinfoStrings->pop(); p = (*(t->columns))[nm->item.iSubItem]; - // TODO is this condition ever not going to be true for libui? +nm->item.pszText=L"abcdefg"; if ((nm->item.mask & LVIF_TEXT) != 0) if (p->textModelColumn != -1) { data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->item.iItem, p->textModelColumn); @@ -175,6 +180,21 @@ static LRESULT onLVN_GETDISPINFO(uiTable *t, NMLVDISPINFOW *nm) nm->item.iIndent = -1; } + if ((nm->item.mask & LVIF_STATE) != 0) + if (p->checkboxModelColumn != -1) { + // TODO handle enabled + data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->item.iItem, p->textModelColumn); + checked = uiTableDataInt(data) != 0; + uiFreeTableData(data); + nm->item.state = INDEXTOSTATEIMAGEMASK(1); + if (checked) + nm->item.state = INDEXTOSTATEIMAGEMASK(2); + nm->item.stateMask = LVIS_STATEIMAGEMASK; + } else { + nm->item.state = INDEXTOSTATEIMAGEMASK(0); + nm->item.stateMask = LVIS_STATEIMAGEMASK; + } + // we don't want to pop from an empty queue, so if nothing updated the queue (no info was filled in above), just push NULL if (!queueUpdated) t->dispinfoStrings->push(NULL); @@ -285,6 +305,7 @@ static void uiTableDestroy(uiControl *c) uiprivFree(col); delete t->columns; // t->smallImages will be automatically destroyed + // TODO will t->checkboxImages? uiFreeControl(uiControl(t)); } @@ -331,8 +352,11 @@ static struct columnParams *appendColumn(uiTable *t, const char *name, int colfm p = uiprivNew(struct columnParams); p->textModelColumn = -1; + p->textEditableColumn = -1; + p->textParams = defaultTextColumnOptionalParams; p->imageModelColumn = -1; p->checkboxModelColumn = -1; + p->checkboxEditableColumn = -1; p->progressBarModelColumn = -1; p->buttonModelColumn = -1; t->columns->push_back(p); @@ -348,8 +372,6 @@ void uiTableAppendTextColumn(uiTable *t, const char *name, int textModelColumn, p->textEditableColumn = textEditableModelColumn; if (params != NULL) p->textParams = *params; - else - p->textParams = defaultTextColumnOptionalParams; } void uiTableAppendImageColumn(uiTable *t, const char *name, int imageModelColumn) @@ -366,14 +388,16 @@ void uiTableAppendImageTextColumn(uiTable *t, const char *name, int imageModelCo p->textEditableColumn = textEditableModelColumn; if (textParams != NULL) p->textParams = *textParams; - else - p->textParams = defaultTextColumnOptionalParams; p->imageModelColumn = imageModelColumn; } void uiTableAppendCheckboxColumn(uiTable *t, const char *name, int checkboxModelColumn, int checkboxEditableModelColumn) { - // TODO + struct columnParams *p; + + p = appendColumn(t, name, LVCFMT_LEFT); + p->checkboxModelColumn = checkboxModelColumn; + p->checkboxEditableColumn = checkboxEditableModelColumn; } void uiTableAppendCheckboxTextColumn(uiTable *t, const char *name, int checkboxModelColumn, int checkboxEditableModelColumn, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *textParams) @@ -385,8 +409,6 @@ void uiTableAppendCheckboxTextColumn(uiTable *t, const char *name, int checkboxM p->textEditableColumn = textEditableModelColumn; if (textParams != NULL) p->textParams = *textParams; - else - p->textParams = defaultTextColumnOptionalParams; p->checkboxModelColumn = checkboxModelColumn; p->checkboxEditableColumn = checkboxEditableModelColumn; } @@ -408,6 +430,83 @@ void uiTableSetRowBackgroundColorModelColumn(uiTable *t, int modelColumn) // TODO redraw? } +// see https://blogs.msdn.microsoft.com/oldnewthing/20171129-00/?p=97485 +static UINT unthemedStates[] = { + 0, + DFCS_CHECKED, + DFCS_INACTIVE, + DFCS_CHECKED | DFCS_INACTIVE, +}; + +// TODO call this whenever either theme, system colors (maybe? TODO), or DPI change +static void mkCheckboxesUnthemed(uiTable *t, int cx, int cy) +{ + HDC dc; + BITMAPINFO bmi; + HBITMAP b; + VOID *bits; + HDC cdc; + HBITMAP prevBitmap; + RECT r; + int i, n; + + // + 1 because we can't actually use image index 0 — that index is used to mean "no state image" in LVITEMW + n = ARRAYSIZE(unthemedStates) + 1; + + dc = GetDC(t->hwnd); + if (dc == NULL) + logLastError(L"error calling GetDC() in mkCheckboxesUnthemed()"); + ZeroMemory(&bmi, sizeof (BITMAPINFO)); + bmi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = cx * n; + bmi.bmiHeader.biHeight = cy; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + b = CreateDIBSection(dc, &bmi, DIB_RGB_COLORS, + &bits, NULL, 0); + if (b == NULL) + logLastError(L"error calling CreateDIBSection() in mkCheckboxesUnthemed()"); + + cdc = CreateCompatibleDC(dc); + if (cdc == NULL) + logLastError(L"error calling CreateCompatibleDC() in mkCheckboxesUnthemed()"); + // TODO error check + prevBitmap = (HBITMAP) SelectObject(cdc, b); + + // note we start at cx to start at index 1 + r.left = cx; + r.top = 0; + r.right = cx * 2; + r.bottom = cy; + for (i = 0; i < ARRAYSIZE(unthemedStates); i++) { + if (DrawFrameControl(cdc, &r, + DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_FLAT | unthemedStates[i]) == 0) + logLastError(L"error calling DrawFrameControl() in mkCheckboxesUnthemed()"); + r.left += cx; + r.right += cx; + } + + // TODO error check + SelectObject(cdc, prevBitmap); + if (DeleteDC(cdc) == 0) + logLastError(L"error calling DeleteDC() in mkCheckboxesUnthemed()"); + if (ReleaseDC(t->hwnd, dc) == 0) + logLastError(L"error calling ReleaseDC() in mkCheckboxesUnthemed()"); + + t->checkboxImages = ImageList_Create(cx, cy, ILC_COLOR32, + n, n); + if (t->checkboxImages == NULL) + logLastError(L"error calling ImageList_Create() in mkCheckboxesUnthemed()"); + if (ImageList_Add(t->checkboxImages, b, NULL) == -1) + logLastError(L"error calling ImageList_Add() in mkCheckboxesUnthemed()"); + // TODO will this return NULL here because it's an initial state? + SendMessageW(t->hwnd, LVM_SETIMAGELIST, LVSIL_STATE, (LPARAM) (t->checkboxImages)); + + // TODO error check + DeleteObject(b); +} + uiTable *uiNewTable(uiTableModel *model) { uiTable *t; @@ -453,5 +552,10 @@ uiTable *uiNewTable(uiTableModel *model) // TODO will this return NULL here because it's an initial state? SendMessageW(t->hwnd, LVM_SETIMAGELIST, LVSIL_SMALL, (LPARAM) (t->smallImages)); + mkCheckboxesUnthemed(t, 16, 16); + // and we need to enable LVN_GETDISPINFO for states + if (SendMessageW(t->hwnd, LVM_SETCALLBACKMASK, LVIS_STATEIMAGEMASK, 0) == FALSE) + logLastError(L"error calling LVM_SETCALLBACKMASK in uiTableAdd()"); + return t; } From 405a6defd257c0690e96792d5edd5534b8b75723 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 9 Jun 2018 23:25:16 -0400 Subject: [PATCH 1169/1329] Started using normal images for checkboxes in Windows uiTables. It doesn't quite work yet. --- windows/table.cpp | 75 ++++++++++++++++++----------------------------- 1 file changed, 28 insertions(+), 47 deletions(-) diff --git a/windows/table.cpp b/windows/table.cpp index 96e9011b..49eb9d79 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -9,6 +9,9 @@ static uiTableTextColumnOptionalParams defaultTextColumnOptionalParams = { /*TODO.ColorModelColumn = */-1, }; +#define nCheckboxImages 4 +#define nLVN_GETDISPINFOSkip 3 + struct columnParams { int textModelColumn; int textEditableColumn; @@ -159,17 +162,13 @@ nm->item.pszText=L"abcdefg"; b = uiprivWICToGDI(wb, dc); if (ReleaseDC(t->hwnd, dc) == 0) logLastError(L"error calling ReleaseDC() in onLVN_GETDISPINFO()"); - if (ImageList_GetImageCount(t->smallImages) <= t->smallIndex) { - if (ImageList_Add(t->smallImages, b, NULL) == -1) - logLastError(L"error calling ImageList_Add() in onLVN_GETDISPINFO()"); - } else - if (ImageList_Replace(t->smallImages, t->smallIndex, b, NULL) == 0) - logLastError(L"error calling ImageList_Replace() in onLVN_GETDISPINFO()"); + if (ImageList_Replace(t->smallImages, t->smallIndex + nCheckboxImages, b, NULL) == 0) + logLastError(L"error calling ImageList_Replace() in onLVN_GETDISPINFO()"); // TODO error check DeleteObject(b); - nm->item.iImage = t->smallIndex; + nm->item.iImage = t->smallIndex + nCheckboxImages; t->smallIndex++; - t->smallIndex %= 3; // TODO don't hardcode this + t->smallIndex %= nLVN_GETDISPINFOSkip; } // having an image list always leaves space for an image on the main item :| @@ -180,20 +179,16 @@ nm->item.pszText=L"abcdefg"; nm->item.iIndent = -1; } - if ((nm->item.mask & LVIF_STATE) != 0) - if (p->checkboxModelColumn != -1) { - // TODO handle enabled - data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->item.iItem, p->textModelColumn); - checked = uiTableDataInt(data) != 0; - uiFreeTableData(data); - nm->item.state = INDEXTOSTATEIMAGEMASK(1); - if (checked) - nm->item.state = INDEXTOSTATEIMAGEMASK(2); - nm->item.stateMask = LVIS_STATEIMAGEMASK; - } else { - nm->item.state = INDEXTOSTATEIMAGEMASK(0); - nm->item.stateMask = LVIS_STATEIMAGEMASK; - } + if (p->checkboxModelColumn != -1) { + // TODO handle enabled + data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->item.iItem, p->textModelColumn); + checked = uiTableDataInt(data) != 0; + uiFreeTableData(data); + nm->item.iImage = 0; + if (checked) + nm->item.iImage = 1; + nm->item.mask |= LVIF_IMAGE; + } // we don't want to pop from an empty queue, so if nothing updated the queue (no info was filled in above), just push NULL if (!queueUpdated) @@ -448,17 +443,14 @@ static void mkCheckboxesUnthemed(uiTable *t, int cx, int cy) HDC cdc; HBITMAP prevBitmap; RECT r; - int i, n; - - // + 1 because we can't actually use image index 0 — that index is used to mean "no state image" in LVITEMW - n = ARRAYSIZE(unthemedStates) + 1; + int i; dc = GetDC(t->hwnd); if (dc == NULL) logLastError(L"error calling GetDC() in mkCheckboxesUnthemed()"); ZeroMemory(&bmi, sizeof (BITMAPINFO)); bmi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER); - bmi.bmiHeader.biWidth = cx * n; + bmi.bmiHeader.biWidth = cx * nCheckboxImages; bmi.bmiHeader.biHeight = cy; bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = 32; @@ -474,12 +466,11 @@ static void mkCheckboxesUnthemed(uiTable *t, int cx, int cy) // TODO error check prevBitmap = (HBITMAP) SelectObject(cdc, b); - // note we start at cx to start at index 1 - r.left = cx; + r.left = 0; r.top = 0; - r.right = cx * 2; + r.right = cx; r.bottom = cy; - for (i = 0; i < ARRAYSIZE(unthemedStates); i++) { + for (i = 0; i < nCheckboxImages; i++) { if (DrawFrameControl(cdc, &r, DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_FLAT | unthemedStates[i]) == 0) logLastError(L"error calling DrawFrameControl() in mkCheckboxesUnthemed()"); @@ -494,14 +485,8 @@ static void mkCheckboxesUnthemed(uiTable *t, int cx, int cy) if (ReleaseDC(t->hwnd, dc) == 0) logLastError(L"error calling ReleaseDC() in mkCheckboxesUnthemed()"); - t->checkboxImages = ImageList_Create(cx, cy, ILC_COLOR32, - n, n); - if (t->checkboxImages == NULL) - logLastError(L"error calling ImageList_Create() in mkCheckboxesUnthemed()"); - if (ImageList_Add(t->checkboxImages, b, NULL) == -1) - logLastError(L"error calling ImageList_Add() in mkCheckboxesUnthemed()"); - // TODO will this return NULL here because it's an initial state? - SendMessageW(t->hwnd, LVM_SETIMAGELIST, LVSIL_STATE, (LPARAM) (t->checkboxImages)); + if (ImageList_Replace(t->smallImages, 0, b, NULL) == 0) + logLastError(L"error calling ImageList_Replace() in mkCheckboxesUnthemed()"); // TODO error check DeleteObject(b); @@ -511,6 +496,7 @@ uiTable *uiNewTable(uiTableModel *model) { uiTable *t; int n; + int i; uiWindowsNewControl(uiTable, t); @@ -536,26 +522,21 @@ uiTable *uiNewTable(uiTableModel *model) t->dispinfoStrings = new std::queue; // this encodes the idea that two LVN_GETDISPINFOs must complete before we can free a string: the first real one is for the fourth call to free - t->dispinfoStrings->push(NULL); - t->dispinfoStrings->push(NULL); - t->dispinfoStrings->push(NULL); + for (i = 0; i < nLVN_GETDISPINFOSkip; i++) + t->dispinfoStrings->push(NULL); // TODO update these when the DPI changes // TODO handle errors - // TODO try adding a real transparent image t->smallImages = ImageList_Create( GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR32, - 0, 3); + nCheckboxImages + nLVN_GETDISPINFOSkip, nCheckboxImages + nLVN_GETDISPINFOSkip); if (t->smallImages == NULL) logLastError(L"error calling ImageList_Create() in uiNewTable()"); // TODO will this return NULL here because it's an initial state? SendMessageW(t->hwnd, LVM_SETIMAGELIST, LVSIL_SMALL, (LPARAM) (t->smallImages)); mkCheckboxesUnthemed(t, 16, 16); - // and we need to enable LVN_GETDISPINFO for states - if (SendMessageW(t->hwnd, LVM_SETCALLBACKMASK, LVIS_STATEIMAGEMASK, 0) == FALSE) - logLastError(L"error calling LVM_SETCALLBACKMASK in uiTableAdd()"); return t; } From 8d43b55ff8bbf168b3cc5e3d15e039e84c171959 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 9 Jun 2018 23:55:43 -0400 Subject: [PATCH 1170/1329] Removed some now-unused stuff. --- windows/table.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/windows/table.cpp b/windows/table.cpp index 49eb9d79..ab68374b 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -47,9 +47,6 @@ struct uiTable { // custom draw state COLORREF clrItemText; - - // checkbox - HIMAGELIST checkboxImages; }; uiTableModel *uiNewTableModel(uiTableModelHandler *mh) @@ -300,7 +297,6 @@ static void uiTableDestroy(uiControl *c) uiprivFree(col); delete t->columns; // t->smallImages will be automatically destroyed - // TODO will t->checkboxImages? uiFreeControl(uiControl(t)); } From a3feb425a1e05404f7006b702b07be0bdb63723e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 10 Jun 2018 00:49:44 -0400 Subject: [PATCH 1171/1329] Started splitting image and checkbox stuff into its own file. Also started adopting HRESULT returns everywhere, because why not make the conversion to it later slightly easier by starting now. --- windows/CMakeLists.txt | 1 + windows/table.cpp | 108 ++++++---------------------------------- windows/table.hpp | 47 +++++++++++++++++ windows/tableimages.cpp | 89 +++++++++++++++++++++++++++++++++ 4 files changed, 152 insertions(+), 93 deletions(-) create mode 100644 windows/table.hpp create mode 100644 windows/tableimages.cpp diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 31ba2c93..4cee5d68 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -51,6 +51,7 @@ list(APPEND _LIBUI_SOURCES windows/stddialogs.cpp windows/tab.cpp windows/table.cpp + windows/tableimages.cpp windows/tabpage.cpp windows/text.cpp windows/utf16.cpp diff --git a/windows/table.cpp b/windows/table.cpp index ab68374b..d0f2210f 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -1,54 +1,10 @@ #include "uipriv_windows.hpp" - -struct uiTableModel { - uiTableModelHandler *mh; - std::vector *tables; -}; +#include "table.hpp" static uiTableTextColumnOptionalParams defaultTextColumnOptionalParams = { /*TODO.ColorModelColumn = */-1, }; -#define nCheckboxImages 4 -#define nLVN_GETDISPINFOSkip 3 - -struct columnParams { - int textModelColumn; - int textEditableColumn; - uiTableTextColumnOptionalParams textParams; - - int imageModelColumn; - - int checkboxModelColumn; - int checkboxEditableColumn; - - int progressBarModelColumn; - - int buttonModelColumn; - int buttonClickableModelColumn; -}; - -struct uiTable { - uiWindowsControl c; - uiTableModel *model; - HWND hwnd; - std::vector *columns; - WPARAM nColumns; - int backgroundColumn; - - // owner data state - // MSDN says we have to keep LVN_GETDISPINFO strings we allocate around at least until "two additional LVN_GETDISPINFO messages have been sent". - // we'll use this queue to do so; the "two additional" part is encoded in the initial state of the queue - std::queue *dispinfoStrings; - // likewise here, though the docs aren't as clear - // TODO make sure what we're doing is even allowed - HIMAGELIST smallImages; - int smallIndex; - - // custom draw state - COLORREF clrItemText; -}; - uiTableModel *uiNewTableModel(uiTableModelHandler *mh) { uiTableModel *m; @@ -122,7 +78,7 @@ void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) static LRESULT onLVN_GETDISPINFO(uiTable *t, NMLVDISPINFOW *nm) { - static struct columnParams *p; + static uiprivTableColumnParams *p; uiTableData *data; WCHAR *wstr; HDC dc; @@ -148,43 +104,9 @@ nm->item.pszText=L"abcdefg"; queueUpdated = true; } - if ((nm->item.mask & LVIF_IMAGE) != 0) - if (p->imageModelColumn != -1) { - dc = GetDC(t->hwnd); - if (dc == NULL) - logLastError(L"error getting DC for uiTable in onLVN_GETDISPINFO()"); - data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->item.iItem, p->imageModelColumn); - wb = uiprivImageAppropriateForDC(uiTableDataImage(data), dc); - uiFreeTableData(data); - b = uiprivWICToGDI(wb, dc); - if (ReleaseDC(t->hwnd, dc) == 0) - logLastError(L"error calling ReleaseDC() in onLVN_GETDISPINFO()"); - if (ImageList_Replace(t->smallImages, t->smallIndex + nCheckboxImages, b, NULL) == 0) - logLastError(L"error calling ImageList_Replace() in onLVN_GETDISPINFO()"); - // TODO error check - DeleteObject(b); - nm->item.iImage = t->smallIndex + nCheckboxImages; - t->smallIndex++; - t->smallIndex %= nLVN_GETDISPINFOSkip; - } - - // having an image list always leaves space for an image on the main item :| - // other places on the internet imply that you should be able to do this but that it shouldn't work - // but it works perfectly (and pixel-perfectly too) for me, so... - if (nm->item.iSubItem == 0 && p->imageModelColumn == -1) { - nm->item.mask |= LVIF_INDENT; - nm->item.iIndent = -1; - } - - if (p->checkboxModelColumn != -1) { - // TODO handle enabled - data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->item.iItem, p->textModelColumn); - checked = uiTableDataInt(data) != 0; - uiFreeTableData(data); - nm->item.iImage = 0; - if (checked) - nm->item.iImage = 1; - nm->item.mask |= LVIF_IMAGE; + hr = uiprivLVN_GETDISPINFOImagesCheckboxes(t, nm, p); + if (hr != S_OK) { + // TODO } // we don't want to pop from an empty queue, so if nothing updated the queue (no info was filled in above), just push NULL @@ -216,7 +138,7 @@ static COLORREF blend(COLORREF base, double r, double g, double b, double a) static LRESULT onNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm) { - struct columnParams *p; + uiprivTableColumnParams *p; uiTableData *data; double r, g, b, a; LRESULT ret; @@ -324,11 +246,11 @@ static void uiTableMinimumSize(uiWindowsControl *c, int *width, int *height) *height = y; } -static struct columnParams *appendColumn(uiTable *t, const char *name, int colfmt) +static uiprivTableColumnParams *appendColumn(uiTable *t, const char *name, int colfmt) { WCHAR *wstr; LVCOLUMNW lvc; - struct columnParams *p; + uiprivTableColumnParams *p; ZeroMemory(&lvc, sizeof (LVCOLUMNW)); lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT; @@ -341,7 +263,7 @@ static struct columnParams *appendColumn(uiTable *t, const char *name, int colfm uiprivFree(wstr); t->nColumns++; - p = uiprivNew(struct columnParams); + p = uiprivNew(uiprivTableColumnParams); p->textModelColumn = -1; p->textEditableColumn = -1; p->textParams = defaultTextColumnOptionalParams; @@ -356,7 +278,7 @@ static struct columnParams *appendColumn(uiTable *t, const char *name, int colfm void uiTableAppendTextColumn(uiTable *t, const char *name, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *params) { - struct columnParams *p; + uiprivTableColumnParams *p; p = appendColumn(t, name, LVCFMT_LEFT); p->textModelColumn = textModelColumn; @@ -372,7 +294,7 @@ void uiTableAppendImageColumn(uiTable *t, const char *name, int imageModelColumn void uiTableAppendImageTextColumn(uiTable *t, const char *name, int imageModelColumn, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *textParams) { - struct columnParams *p; + uiprivTableColumnParams *p; p = appendColumn(t, name, LVCFMT_LEFT); p->textModelColumn = textModelColumn; @@ -384,7 +306,7 @@ void uiTableAppendImageTextColumn(uiTable *t, const char *name, int imageModelCo void uiTableAppendCheckboxColumn(uiTable *t, const char *name, int checkboxModelColumn, int checkboxEditableModelColumn) { - struct columnParams *p; + uiprivTableColumnParams *p; p = appendColumn(t, name, LVCFMT_LEFT); p->checkboxModelColumn = checkboxModelColumn; @@ -496,7 +418,7 @@ uiTable *uiNewTable(uiTableModel *model) uiWindowsNewControl(uiTable, t); - t->columns = new std::vector; + t->columns = new std::vector; t->model = model; t->hwnd = uiWindowsEnsureCreateControlHWND(WS_EX_CLIENTEDGE, WC_LISTVIEW, L"", @@ -518,7 +440,7 @@ uiTable *uiNewTable(uiTableModel *model) t->dispinfoStrings = new std::queue; // this encodes the idea that two LVN_GETDISPINFOs must complete before we can free a string: the first real one is for the fourth call to free - for (i = 0; i < nLVN_GETDISPINFOSkip; i++) + for (i = 0; i < uiprivNumLVN_GETDISPINFOSkip; i++) t->dispinfoStrings->push(NULL); // TODO update these when the DPI changes @@ -526,7 +448,7 @@ uiTable *uiNewTable(uiTableModel *model) t->smallImages = ImageList_Create( GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR32, - nCheckboxImages + nLVN_GETDISPINFOSkip, nCheckboxImages + nLVN_GETDISPINFOSkip); + nCheckboxImages + uiprivNumLVN_GETDISPINFOSkip, nCheckboxImages + uiprivNumLVN_GETDISPINFOSkip); if (t->smallImages == NULL) logLastError(L"error calling ImageList_Create() in uiNewTable()"); // TODO will this return NULL here because it's an initial state? diff --git a/windows/table.hpp b/windows/table.hpp new file mode 100644 index 00000000..51994bb5 --- /dev/null +++ b/windows/table.hpp @@ -0,0 +1,47 @@ +// 10 june 2018 + +// table.cpp +#define uiprivNumLVN_GETDISPINFOSkip 3 +struct uiTableModel { + uiTableModelHandler *mh; + std::vector *tables; +}; +typedef struct uiprivTableColumnParams uiprivTableColumnParams; +struct uiprivTableColumnParams { + int textModelColumn; + int textEditableColumn; + uiTableTextColumnOptionalParams textParams; + + int imageModelColumn; + + int checkboxModelColumn; + int checkboxEditableColumn; + + int progressBarModelColumn; + + int buttonModelColumn; + int buttonClickableModelColumn; +}; +struct uiTable { + uiWindowsControl c; + uiTableModel *model; + HWND hwnd; + std::vector *columns; + WPARAM nColumns; + int backgroundColumn; + + // owner data state + // MSDN says we have to keep LVN_GETDISPINFO strings we allocate around at least until "two additional LVN_GETDISPINFO messages have been sent". + // we'll use this queue to do so; the "two additional" part is encoded in the initial state of the queue + std::queue *dispinfoStrings; + // likewise here, though the docs aren't as clear + // TODO make sure what we're doing is even allowed + HIMAGELIST smallImages; + int smallIndex; + + // custom draw state + COLORREF clrItemText; +}; + +// tableimage.cpp +extern HRESULT uiprivLVN_GETDISPINFOImagesCheckboxes(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnParams *p); diff --git a/windows/tableimages.cpp b/windows/tableimages.cpp new file mode 100644 index 00000000..47d392c6 --- /dev/null +++ b/windows/tableimages.cpp @@ -0,0 +1,89 @@ +// 10 june 2018 +#include "uipriv_windows.hpp" +#include "table.hpp" + +/* +This file handles both images and checkboxes in tables. + +For images, we'll do a similar thing to what text columns do: cycle out images from the small image list every few LVN_GETDISPINFO notifications. + +For checkboxes, the native list view checkbox functionality uses state images, but those are only supported on the main item, not on subitems. So instead, we'll do them on normal images instead. +TODO will this affect accessibility? + +We'll use the small image list. For this, the first few items will be reserved for checkboxes, and the last few for cell images. +*/ + +#define nCheckboxImages 4 + +static HRESULT setCellImage(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnParams *p, uiTableData *data) +{ + int index; + HDC dc; + IWICImage *wb; + HBITMAP b; + + index = t->smallIndex + nCheckboxImages; + t->smallIndex++; + t->smallIndex %= uiprivNumLVN_GETDISPINFOSkip; + nm->item.iImage = index; + + dc = GetDC(t->hwnd); + if (dc == NULL) { + logLastError(L"GetDC()"); + return E_FAIL; + } + + wb = uiprivImageAppropriateForDC(uiTableDataImage(data), dc); + b = uiprivWICToGDI(wb, dc); + if (ImageList_Replace(t->smallImages, index, b, NULL) == 0) { + logLastError(L"ImageList_Replace()"); + return E_FAIL; + } + + if (ReleaseDC(t->hwnd, dc) == 0) { + logLastError(L"ReleaseDC()"); + return E_FAIL; + } + + return S_OK; +} + +HRESULT uiprivLVN_GETDISPINFOImagesCheckboxes(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnParams *p) +{ + uiTableData *data; + HRESULT hr; + + if ((nm->item.mask & LVIF_IMAGE) == 0) + return S_OK; // nothing to do here + + if (p->imageModelColumn != -1) { + data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->item.iItem, p->imageModelColumn); + hr = setCellImage(t, nm, p, data); + uiFreeTableData(data); + return hr; + } + +#if 0 + if (p->checkboxModelColumn != -1) { + // TODO handle enabled + data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->item.iItem, p->textModelColumn); + checked = uiTableDataInt(data) != 0; + uiFreeTableData(data); + nm->item.iImage = 0; + if (checked) + nm->item.iImage = 1; + nm->item.mask |= LVIF_IMAGE; + } +#endif + + // if we got here, there's no image in this cell + nm->item.iImage = 0; + // having an image list always leaves space for an image on the main item :| + // other places on the internet imply that you should be able to do this but that it shouldn't work + // but it works perfectly (and pixel-perfectly too) for me, so... + if (nm->item.iSubItem == 0) { + nm->item.mask |= LVIF_INDENT; + nm->item.iIndent = -1; + } + return S_OK; +} From c22f643df709b02b36e8f88d865707211b7ea05e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 10 Jun 2018 10:43:29 -0400 Subject: [PATCH 1172/1329] More image and checkbox fixups. Next: themed checkboxes. --- windows/table.cpp | 87 ++--------------------- windows/table.hpp | 6 +- windows/tableimages.cpp | 153 +++++++++++++++++++++++++++++++++++++--- 3 files changed, 156 insertions(+), 90 deletions(-) diff --git a/windows/table.cpp b/windows/table.cpp index d0f2210f..f6fe1c94 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -86,6 +86,7 @@ static LRESULT onLVN_GETDISPINFO(uiTable *t, NMLVDISPINFOW *nm) HBITMAP b; int checked; bool queueUpdated = false; + HRESULT hr; wstr = t->dispinfoStrings->front(); if (wstr != NULL) @@ -315,7 +316,7 @@ void uiTableAppendCheckboxColumn(uiTable *t, const char *name, int checkboxModel void uiTableAppendCheckboxTextColumn(uiTable *t, const char *name, int checkboxModelColumn, int checkboxEditableModelColumn, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *textParams) { - struct columnParams *p; + uiprivTableColumnParams *p; p = appendColumn(t, name, LVCFMT_LEFT); p->textModelColumn = textModelColumn; @@ -343,78 +344,12 @@ void uiTableSetRowBackgroundColorModelColumn(uiTable *t, int modelColumn) // TODO redraw? } -// see https://blogs.msdn.microsoft.com/oldnewthing/20171129-00/?p=97485 -static UINT unthemedStates[] = { - 0, - DFCS_CHECKED, - DFCS_INACTIVE, - DFCS_CHECKED | DFCS_INACTIVE, -}; - -// TODO call this whenever either theme, system colors (maybe? TODO), or DPI change -static void mkCheckboxesUnthemed(uiTable *t, int cx, int cy) -{ - HDC dc; - BITMAPINFO bmi; - HBITMAP b; - VOID *bits; - HDC cdc; - HBITMAP prevBitmap; - RECT r; - int i; - - dc = GetDC(t->hwnd); - if (dc == NULL) - logLastError(L"error calling GetDC() in mkCheckboxesUnthemed()"); - ZeroMemory(&bmi, sizeof (BITMAPINFO)); - bmi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER); - bmi.bmiHeader.biWidth = cx * nCheckboxImages; - bmi.bmiHeader.biHeight = cy; - bmi.bmiHeader.biPlanes = 1; - bmi.bmiHeader.biBitCount = 32; - bmi.bmiHeader.biCompression = BI_RGB; - b = CreateDIBSection(dc, &bmi, DIB_RGB_COLORS, - &bits, NULL, 0); - if (b == NULL) - logLastError(L"error calling CreateDIBSection() in mkCheckboxesUnthemed()"); - - cdc = CreateCompatibleDC(dc); - if (cdc == NULL) - logLastError(L"error calling CreateCompatibleDC() in mkCheckboxesUnthemed()"); - // TODO error check - prevBitmap = (HBITMAP) SelectObject(cdc, b); - - r.left = 0; - r.top = 0; - r.right = cx; - r.bottom = cy; - for (i = 0; i < nCheckboxImages; i++) { - if (DrawFrameControl(cdc, &r, - DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_FLAT | unthemedStates[i]) == 0) - logLastError(L"error calling DrawFrameControl() in mkCheckboxesUnthemed()"); - r.left += cx; - r.right += cx; - } - - // TODO error check - SelectObject(cdc, prevBitmap); - if (DeleteDC(cdc) == 0) - logLastError(L"error calling DeleteDC() in mkCheckboxesUnthemed()"); - if (ReleaseDC(t->hwnd, dc) == 0) - logLastError(L"error calling ReleaseDC() in mkCheckboxesUnthemed()"); - - if (ImageList_Replace(t->smallImages, 0, b, NULL) == 0) - logLastError(L"error calling ImageList_Replace() in mkCheckboxesUnthemed()"); - - // TODO error check - DeleteObject(b); -} - uiTable *uiNewTable(uiTableModel *model) { uiTable *t; int n; int i; + HRESULT hr; uiWindowsNewControl(uiTable, t); @@ -443,18 +378,10 @@ uiTable *uiNewTable(uiTableModel *model) for (i = 0; i < uiprivNumLVN_GETDISPINFOSkip; i++) t->dispinfoStrings->push(NULL); - // TODO update these when the DPI changes - // TODO handle errors - t->smallImages = ImageList_Create( - GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), - ILC_COLOR32, - nCheckboxImages + uiprivNumLVN_GETDISPINFOSkip, nCheckboxImages + uiprivNumLVN_GETDISPINFOSkip); - if (t->smallImages == NULL) - logLastError(L"error calling ImageList_Create() in uiNewTable()"); - // TODO will this return NULL here because it's an initial state? - SendMessageW(t->hwnd, LVM_SETIMAGELIST, LVSIL_SMALL, (LPARAM) (t->smallImages)); - - mkCheckboxesUnthemed(t, 16, 16); + hr = uiprivTableSetupImagesCheckboxes(t); + if (hr != S_OK) { + // TODO + } return t; } diff --git a/windows/table.hpp b/windows/table.hpp index 51994bb5..98ede2f1 100644 --- a/windows/table.hpp +++ b/windows/table.hpp @@ -34,7 +34,8 @@ struct uiTable { // MSDN says we have to keep LVN_GETDISPINFO strings we allocate around at least until "two additional LVN_GETDISPINFO messages have been sent". // we'll use this queue to do so; the "two additional" part is encoded in the initial state of the queue std::queue *dispinfoStrings; - // likewise here, though the docs aren't as clear + + // tableimages.cpp // TODO make sure what we're doing is even allowed HIMAGELIST smallImages; int smallIndex; @@ -43,5 +44,6 @@ struct uiTable { COLORREF clrItemText; }; -// tableimage.cpp +// tableimages.cpp extern HRESULT uiprivLVN_GETDISPINFOImagesCheckboxes(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnParams *p); +extern HRESULT uiprivTableSetupImagesCheckboxes(uiTable *t); diff --git a/windows/tableimages.cpp b/windows/tableimages.cpp index 47d392c6..8142aec7 100644 --- a/windows/tableimages.cpp +++ b/windows/tableimages.cpp @@ -13,13 +13,17 @@ TODO will this affect accessibility? We'll use the small image list. For this, the first few items will be reserved for checkboxes, and the last few for cell images. */ +// checkboxes TODOs: +// - see if we need to get rid of the extra margin in subitems +// - see if we need to get rid of the glow effect + #define nCheckboxImages 4 static HRESULT setCellImage(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnParams *p, uiTableData *data) { int index; HDC dc; - IWICImage *wb; + IWICBitmap *wb; HBITMAP b; index = t->smallIndex + nCheckboxImages; @@ -35,10 +39,17 @@ static HRESULT setCellImage(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnPara wb = uiprivImageAppropriateForDC(uiTableDataImage(data), dc); b = uiprivWICToGDI(wb, dc); - if (ImageList_Replace(t->smallImages, index, b, NULL) == 0) { - logLastError(L"ImageList_Replace()"); - return E_FAIL; - } + // TODO rewrite this condition to make more sense; possibly swap the if and else blocks too + if (ImageList_GetImageCount(t->smallImages) > index) { + if (ImageList_Replace(t->smallImages, index, b, NULL) == 0) { + logLastError(L"ImageList_Replace()"); + return E_FAIL; + } + } else + if (ImageList_Add(t->smallImages, b, NULL) == -1) { + logLastError(L"ImageList_Add()"); + return E_FAIL; + } if (ReleaseDC(t->hwnd, dc) == 0) { logLastError(L"ReleaseDC()"); @@ -54,6 +65,7 @@ HRESULT uiprivLVN_GETDISPINFOImagesCheckboxes(uiTable *t, NMLVDISPINFOW *nm, uip HRESULT hr; if ((nm->item.mask & LVIF_IMAGE) == 0) + // TODO we actually need to do the first column fix here too... return S_OK; // nothing to do here if (p->imageModelColumn != -1) { @@ -63,8 +75,8 @@ HRESULT uiprivLVN_GETDISPINFOImagesCheckboxes(uiTable *t, NMLVDISPINFOW *nm, uip return hr; } -#if 0 if (p->checkboxModelColumn != -1) { +#if 0 // TODO handle enabled data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->item.iItem, p->textModelColumn); checked = uiTableDataInt(data) != 0; @@ -73,17 +85,142 @@ HRESULT uiprivLVN_GETDISPINFOImagesCheckboxes(uiTable *t, NMLVDISPINFOW *nm, uip if (checked) nm->item.iImage = 1; nm->item.mask |= LVIF_IMAGE; - } #endif + nm->item.mask |= LVIF_IMAGE; + nm->item.iImage = 1; + return S_OK; + } // if we got here, there's no image in this cell - nm->item.iImage = 0; + nm->item.mask &= ~LVIF_IMAGE; // having an image list always leaves space for an image on the main item :| // other places on the internet imply that you should be able to do this but that it shouldn't work // but it works perfectly (and pixel-perfectly too) for me, so... + // TODO it doesn't work anymore... if (nm->item.iSubItem == 0) { nm->item.mask |= LVIF_INDENT; nm->item.iIndent = -1; } return S_OK; } + +// see https://blogs.msdn.microsoft.com/oldnewthing/20171129-00/?p=97485 +static UINT unthemedStates[] = { + 0, + DFCS_CHECKED, + DFCS_INACTIVE, + DFCS_CHECKED | DFCS_INACTIVE, +}; + +// TODO call this whenever either theme, system colors (maybe? TODO), or DPI change +// TODO properly clean up on failure +static HRESULT mkCheckboxesUnthemed(uiTable *t, int cx, int cy) +{ + HDC dc; + BITMAPINFO bmi; + HBITMAP b; + VOID *bits; + HDC cdc; + HBITMAP prevBitmap; + RECT r; + int i; + + dc = GetDC(t->hwnd); + if (dc == NULL) { + logLastError(L"GetDC()"); + return E_FAIL; + } + ZeroMemory(&bmi, sizeof (BITMAPINFO)); + bmi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = cx * nCheckboxImages; + bmi.bmiHeader.biHeight = cy; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + b = CreateDIBSection(dc, &bmi, DIB_RGB_COLORS, + &bits, NULL, 0); + if (b == NULL) { + logLastError(L"CreateDIBSection()"); + return E_FAIL; + } + + cdc = CreateCompatibleDC(dc); + if (cdc == NULL) { + logLastError(L"CreateCompatibleDC()"); + return E_FAIL; + } + prevBitmap = (HBITMAP) SelectObject(cdc, b); + if (prevBitmap == NULL) { + logLastError(L"SelectObject() b"); + return E_FAIL; + } + + // the actual list view LVS_EX_CHECKBOXES code does this to ensure the entire image is valid, not just the parts that are drawn after resizing + // TODO find a better, alpha-friendly way to do this + r.left = 0; + r.top = 0; + r.right = cx * nCheckboxImages; + r.bottom = cy; + FillRect(cdc, &r, GetSysColorBrush(COLOR_WINDOW)); + + r.left = 0; + r.top = 0; + r.right = cx; + r.bottom = cy; + // this is what the actual list view LVS_EX_CHECKBOXES code does to more correctly size the checkboxes + // TODO check errors + InflateRect(&r, -GetSystemMetrics(SM_CXEDGE), -GetSystemMetrics(SM_CYEDGE)); + r.right++; + r.bottom++; + for (i = 0; i < nCheckboxImages; i++) { + if (DrawFrameControl(cdc, &r, + DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_FLAT | unthemedStates[i]) == 0) { + logLastError(L"DrawFrameControl()"); + return E_FAIL; + } + r.left += cx; + r.right += cx; + } + + if (SelectObject(cdc, prevBitmap) != ((HGDIOBJ) b)) { + logLastError(L"SelectObject() prev"); + return E_FAIL; + } + if (DeleteDC(cdc) == 0) { + logLastError(L"DeleteDC()"); + return E_FAIL; + } + if (ReleaseDC(t->hwnd, dc) == 0) { + logLastError(L"ReleaseDC()"); + return E_FAIL; + } + + if (ImageList_Add(t->smallImages, b, NULL) == -1) { + logLastError(L"ImageList_Add()"); + return E_FAIL; + } + + // TODO error check + DeleteObject(b); + return S_OK; +} + +// TODO run again when the DPI changes +HRESULT uiprivTableSetupImagesCheckboxes(uiTable *t) +{ + int cx, cy; + + cx = GetSystemMetrics(SM_CXSMICON); + cy = GetSystemMetrics(SM_CYSMICON); + // TODO handle errors + t->smallImages = ImageList_Create(cx, cy, + ILC_COLOR32, + nCheckboxImages + uiprivNumLVN_GETDISPINFOSkip, nCheckboxImages + uiprivNumLVN_GETDISPINFOSkip); + if (t->smallImages == NULL) { + logLastError(L"ImageList_Create()"); + return E_FAIL; + } + // TODO will this return NULL here because it's an initial state? + SendMessageW(t->hwnd, LVM_SETIMAGELIST, LVSIL_SMALL, (LPARAM) (t->smallImages)); + return mkCheckboxesUnthemed(t, cx, cy); +} From 8dd9f08ba42238b0d00f9581b9a89b15196e5e31 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 10 Jun 2018 10:45:50 -0400 Subject: [PATCH 1173/1329] Actually one more quick test fix to cycle through all the images before continuing. Okay, NOW for themed checkboxes. --- windows/tableimages.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/tableimages.cpp b/windows/tableimages.cpp index 8142aec7..8c3377a8 100644 --- a/windows/tableimages.cpp +++ b/windows/tableimages.cpp @@ -87,7 +87,7 @@ HRESULT uiprivLVN_GETDISPINFOImagesCheckboxes(uiTable *t, NMLVDISPINFOW *nm, uip nm->item.mask |= LVIF_IMAGE; #endif nm->item.mask |= LVIF_IMAGE; - nm->item.iImage = 1; + nm->item.iImage = nm->item.iItem % 4; return S_OK; } From 5a5f9ba9accc76c4804137b1f437f6dca03f0e15 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 10 Jun 2018 13:15:21 -0400 Subject: [PATCH 1174/1329] And added themed checkboxes. --- windows/tableimages.cpp | 145 +++++++++++++++++++++++++++++----------- windows/winapi.hpp | 2 + 2 files changed, 108 insertions(+), 39 deletions(-) diff --git a/windows/tableimages.cpp b/windows/tableimages.cpp index 8c3377a8..ac096d66 100644 --- a/windows/tableimages.cpp +++ b/windows/tableimages.cpp @@ -104,7 +104,10 @@ HRESULT uiprivLVN_GETDISPINFOImagesCheckboxes(uiTable *t, NMLVDISPINFOW *nm, uip return S_OK; } -// see https://blogs.msdn.microsoft.com/oldnewthing/20171129-00/?p=97485 +// references for checkbox drawing: +// - https://blogs.msdn.microsoft.com/oldnewthing/20171129-00/?p=97485 +// - https://blogs.msdn.microsoft.com/oldnewthing/20171201-00/?p=97505 + static UINT unthemedStates[] = { 0, DFCS_CHECKED, @@ -112,11 +115,16 @@ static UINT unthemedStates[] = { DFCS_CHECKED | DFCS_INACTIVE, }; -// TODO call this whenever either theme, system colors (maybe? TODO), or DPI change +static int themedStates[] = { + CBS_UNCHECKEDNORMAL, + CBS_CHECKEDNORMAL, + CBS_UNCHECKEDDISABLED, + CBS_CHECKEDDISABLED, +}; + // TODO properly clean up on failure -static HRESULT mkCheckboxesUnthemed(uiTable *t, int cx, int cy) +static HRESULT mkCheckboxes(uiTable *t, HTHEME theme, HDC dc, int cxList, int cyList, int cxCheck, int cyCheck) { - HDC dc; BITMAPINFO bmi; HBITMAP b; VOID *bits; @@ -124,16 +132,12 @@ static HRESULT mkCheckboxesUnthemed(uiTable *t, int cx, int cy) HBITMAP prevBitmap; RECT r; int i; + HRESULT hr; - dc = GetDC(t->hwnd); - if (dc == NULL) { - logLastError(L"GetDC()"); - return E_FAIL; - } ZeroMemory(&bmi, sizeof (BITMAPINFO)); bmi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER); - bmi.bmiHeader.biWidth = cx * nCheckboxImages; - bmi.bmiHeader.biHeight = cy; + bmi.bmiHeader.biWidth = cxList * nCheckboxImages; + bmi.bmiHeader.biHeight = cyList; bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = 32; bmi.bmiHeader.biCompression = BI_RGB; @@ -157,29 +161,54 @@ static HRESULT mkCheckboxesUnthemed(uiTable *t, int cx, int cy) // the actual list view LVS_EX_CHECKBOXES code does this to ensure the entire image is valid, not just the parts that are drawn after resizing // TODO find a better, alpha-friendly way to do this - r.left = 0; - r.top = 0; - r.right = cx * nCheckboxImages; - r.bottom = cy; - FillRect(cdc, &r, GetSysColorBrush(COLOR_WINDOW)); + // note that the actual list view does this only if unthemed, but it can get away with that since its image lists only contain checkmarks + // ours don't, so we have to compromise until the above TODO is resolved so we don't draw alpha stuff on top of garbage + if (theme == NULL || cxList != cxCheck || cyList != cyCheck) { + r.left = 0; + r.top = 0; + r.right = cxList * nCheckboxImages; + r.bottom = cyList; + FillRect(cdc, &r, GetSysColorBrush(COLOR_WINDOW)); + } r.left = 0; r.top = 0; - r.right = cx; - r.bottom = cy; - // this is what the actual list view LVS_EX_CHECKBOXES code does to more correctly size the checkboxes - // TODO check errors - InflateRect(&r, -GetSystemMetrics(SM_CXEDGE), -GetSystemMetrics(SM_CYEDGE)); - r.right++; - r.bottom++; - for (i = 0; i < nCheckboxImages; i++) { - if (DrawFrameControl(cdc, &r, - DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_FLAT | unthemedStates[i]) == 0) { - logLastError(L"DrawFrameControl()"); - return E_FAIL; + r.right = cxCheck; + r.bottom = cyCheck; + if (theme != NULL) { + // because we're not making an image list exactly the correct size, we'll need to manually position the checkbox correctly + // let's just center it for now + // TODO make sure this is correct... + r.left = (cxList - cxCheck) / 2; + r.top = (cyList - cyCheck) / 2; + r.right += r.left; + r.bottom += r.top; + for (i = 0; i < nCheckboxImages; i++) { + hr = DrawThemeBackground(theme, cdc, + BP_CHECKBOX, themedStates[i], + &r, NULL); + if (hr != S_OK) { + logHRESULT(L"DrawThemeBackground()", hr); + return hr; + } + r.left += cxList; + r.right += cxList; + } + } else { + // this is what the actual list view LVS_EX_CHECKBOXES code does to more correctly size the checkboxes + // TODO check errors + InflateRect(&r, -GetSystemMetrics(SM_CXEDGE), -GetSystemMetrics(SM_CYEDGE)); + r.right++; + r.bottom++; + for (i = 0; i < nCheckboxImages; i++) { + if (DrawFrameControl(cdc, &r, + DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_FLAT | unthemedStates[i]) == 0) { + logLastError(L"DrawFrameControl()"); + return E_FAIL; + } + r.left += cxList; + r.right += cxList; } - r.left += cx; - r.right += cx; } if (SelectObject(cdc, prevBitmap) != ((HGDIOBJ) b)) { @@ -190,10 +219,6 @@ static HRESULT mkCheckboxesUnthemed(uiTable *t, int cx, int cy) logLastError(L"DeleteDC()"); return E_FAIL; } - if (ReleaseDC(t->hwnd, dc) == 0) { - logLastError(L"ReleaseDC()"); - return E_FAIL; - } if (ImageList_Add(t->smallImages, b, NULL) == -1) { logLastError(L"ImageList_Add()"); @@ -208,12 +233,41 @@ static HRESULT mkCheckboxesUnthemed(uiTable *t, int cx, int cy) // TODO run again when the DPI changes HRESULT uiprivTableSetupImagesCheckboxes(uiTable *t) { - int cx, cy; + HDC dc; + int cxList, cyList; + HTHEME theme; + SIZE sizeCheck; + HRESULT hr; + + dc = GetDC(t->hwnd); + if (dc == NULL) { + logLastError(L"GetDC()"); + return E_FAIL; + } + + cxList = GetSystemMetrics(SM_CXSMICON); + cyList = GetSystemMetrics(SM_CYSMICON); + sizeCheck.cx = cxList; + sizeCheck.cy = cyList; + theme = OpenThemeData(t->hwnd, L"button"); + if (theme != NULL) { + hr = GetThemePartSize(theme, dc, + BP_CHECKBOX, CBS_UNCHECKEDNORMAL, + NULL, TS_DRAW, &sizeCheck); + if (hr != S_OK) { + logHRESULT(L"GetThemePartSize()", hr); + return hr; // TODO fall back? + } + // make sure these checkmarks fit + // unthemed checkmarks will by the code above be smaller than cxList/cyList here + if (cxList < sizeCheck.cx) + cxList = sizeCheck.cx; + if (cyList < sizeCheck.cy) + cyList = sizeCheck.cy; + } - cx = GetSystemMetrics(SM_CXSMICON); - cy = GetSystemMetrics(SM_CYSMICON); // TODO handle errors - t->smallImages = ImageList_Create(cx, cy, + t->smallImages = ImageList_Create(cxList, cyList, ILC_COLOR32, nCheckboxImages + uiprivNumLVN_GETDISPINFOSkip, nCheckboxImages + uiprivNumLVN_GETDISPINFOSkip); if (t->smallImages == NULL) { @@ -222,5 +276,18 @@ HRESULT uiprivTableSetupImagesCheckboxes(uiTable *t) } // TODO will this return NULL here because it's an initial state? SendMessageW(t->hwnd, LVM_SETIMAGELIST, LVSIL_SMALL, (LPARAM) (t->smallImages)); - return mkCheckboxesUnthemed(t, cx, cy); + hr = mkCheckboxes(t, theme, dc, cxList, cyList, sizeCheck.cx, sizeCheck.cy); + if (hr != S_OK) + return hr; + + hr = CloseThemeData(theme); + if (hr != S_OK) { + logHRESULT(L"CloseThemeData()", hr); + return hr; + } + if (ReleaseDC(t->hwnd, dc) == 0) { + logLastError(L"ReleaseDC()"); + return E_FAIL; + } + return S_OK; } diff --git a/windows/winapi.hpp b/windows/winapi.hpp index f1306db7..297726e3 100644 --- a/windows/winapi.hpp +++ b/windows/winapi.hpp @@ -37,6 +37,8 @@ #ifndef RC_INVOKED #include #include +#include +#include #include #include #include From 94a33978949b0c080e8d6f4455b9523e35dbceae Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 10 Jun 2018 17:38:51 -0400 Subject: [PATCH 1175/1329] Tried to resolve checkboxes appearing selected. It doesn't work fully yet, so the actual drawing that makes it work is disabled for now. But the handler for NM_CUSTOMDRAW now has a hook to become cleaner in the future. --- windows/table.cpp | 20 ++++++++--- windows/table.hpp | 1 + windows/tableimages.cpp | 73 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 89 insertions(+), 5 deletions(-) diff --git a/windows/table.cpp b/windows/table.cpp index f6fe1c94..d66f9170 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -143,10 +143,12 @@ static LRESULT onNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm) uiTableData *data; double r, g, b, a; LRESULT ret; + HRESULT hr; switch (nm->nmcd.dwDrawStage) { case CDDS_PREPAINT: - return CDRF_NOTIFYITEMDRAW; + ret = CDRF_NOTIFYITEMDRAW; + break; case CDDS_ITEMPREPAINT: if (t->backgroundColumn != -1) { data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->nmcd.dwItemSpec, t->backgroundColumn); @@ -157,7 +159,8 @@ static LRESULT onNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm) } } t->clrItemText = nm->clrText; - return CDRF_NEWFONT | CDRF_NOTIFYSUBITEMDRAW; + ret = CDRF_NEWFONT | CDRF_NOTIFYSUBITEMDRAW; + break; case CDDS_SUBITEM | CDDS_ITEMPREPAINT: p = (*(t->columns))[nm->iSubItem]; // we need this as previous subitems will persist their colors @@ -171,9 +174,17 @@ static LRESULT onNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm) } } // TODO draw background on image columns if needed - return CDRF_NEWFONT; + ret = CDRF_NEWFONT; + break; + default: + ret = CDRF_DODEFAULT; } - return CDRF_DODEFAULT; + + hr = uiprivNM_CUSTOMDRAWImagesCheckboxes(t, nm, &ret); + if (hr != S_OK) { + // TODO + } + return ret; } static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult) @@ -364,6 +375,7 @@ uiTable *uiNewTable(uiTableModel *model) uiWindowsRegisterWM_NOTIFYHandler(t->hwnd, onWM_NOTIFY, uiControl(t)); // TODO: try LVS_EX_AUTOSIZECOLUMNS + // TODO check error SendMessageW(t->hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, (WPARAM) (LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP | LVS_EX_SUBITEMIMAGES), (LPARAM) (LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP | LVS_EX_SUBITEMIMAGES)); diff --git a/windows/table.hpp b/windows/table.hpp index 98ede2f1..0be01d8f 100644 --- a/windows/table.hpp +++ b/windows/table.hpp @@ -46,4 +46,5 @@ struct uiTable { // tableimages.cpp extern HRESULT uiprivLVN_GETDISPINFOImagesCheckboxes(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnParams *p); +extern HRESULT uiprivNM_CUSTOMDRAWImagesCheckboxes(uiTable *t, NMLVCUSTOMDRAW *nm, LRESULT *lResult); extern HRESULT uiprivTableSetupImagesCheckboxes(uiTable *t); diff --git a/windows/tableimages.cpp b/windows/tableimages.cpp index ac096d66..2544e1e7 100644 --- a/windows/tableimages.cpp +++ b/windows/tableimages.cpp @@ -15,7 +15,6 @@ We'll use the small image list. For this, the first few items will be reserved f // checkboxes TODOs: // - see if we need to get rid of the extra margin in subitems -// - see if we need to get rid of the glow effect #define nCheckboxImages 4 @@ -59,6 +58,36 @@ static HRESULT setCellImage(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnPara return S_OK; } +#define stateUnchecked 0 +#define stateChecked 1 +#define stateDisabled 2 + +static int checkboxIndex(uiTableModel *m, int row, int checkboxModelColumn, int checkboxEditableColumn) +{ + uiTableData *data; + int ret; + + ret = stateUnchecked; + data = (*(m->mh->CellValue))(m->mh, m, row, checkboxModelColumn); + if (uiTableDataInt(data) != 0) + ret = stateChecked; + uiFreeTableData(data); + + switch (checkboxEditableColumn) { + case uiTableModelColumnNeverEditable: + ret += stateDisabled; + break; + case uiTableModelColumnAlwaysEditable: + break; + default: + data = (*(m->mh->CellValue))(m->mh, m, row, checkboxEditableColumn); + if (uiTableDataInt(data) != 0) + ret += stateDisabled; + } + + return ret; +} + HRESULT uiprivLVN_GETDISPINFOImagesCheckboxes(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnParams *p) { uiTableData *data; @@ -104,6 +133,48 @@ HRESULT uiprivLVN_GETDISPINFOImagesCheckboxes(uiTable *t, NMLVDISPINFOW *nm, uip return S_OK; } +// in order to properly look like checkboxes, we need to exclude them from being colored in by the selection rect +// however, there seems to be no way to do this natively, so we have to draw over ourselves (TODO?) +// hopefully the performance won't be too bad +// see also https://www.codeproject.com/Articles/79/Neat-Stuff-to-Do-in-List-Controls-Using-Custom-Dra +HRESULT uiprivNM_CUSTOMDRAWImagesCheckboxes(uiTable *t, NMLVCUSTOMDRAW *nm, LRESULT *lResult) +{ + uiprivTableColumnParams *p; + int index; + RECT r; + + if (nm->nmcd.dwDrawStage == (CDDS_SUBITEM | CDDS_ITEMPREPAINT)) { + *lResult |= CDRF_NOTIFYPOSTPAINT; + return S_OK; + } + if (nm->nmcd.dwDrawStage != (CDDS_SUBITEM | CDDS_ITEMPOSTPAINT)) + return S_OK; + + // only draw over checkboxes + p = (*(t->columns))[nm->iSubItem]; + if (p->checkboxModelColumn == -1) + return S_OK; + + index = checkboxIndex(t->model, nm->nmcd.dwItemSpec, + p->checkboxModelColumn, p->checkboxEditableColumn); + ZeroMemory(&r, sizeof (RECT)); + r.left = LVIR_ICON; + r.top = nm->iSubItem; + if (SendMessageW(t->hwnd, LVM_GETSUBITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == 0) { + logLastError(L"LVM_GETSUBITEMRECT"); + return E_FAIL; + } +#if 0 + // TODO this is offset by one pixel on my system and everything I've found indicates this should not be happening??? + if (ImageList_Draw(t->smallImages, index, nm->nmcd.hdc, + r.left, r.top, ILD_NORMAL) == 0) { + logLastError(L"ImageList_Draw()"); + return E_FAIL; + } +#endif + return S_OK; +} + // references for checkbox drawing: // - https://blogs.msdn.microsoft.com/oldnewthing/20171129-00/?p=97485 // - https://blogs.msdn.microsoft.com/oldnewthing/20171201-00/?p=97505 From 2a2990f19cb966e4b26803b25aa2b7fa6d8957c4 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 10 Jun 2018 19:07:34 -0400 Subject: [PATCH 1176/1329] I have no clue anymore. Tempted to undo checkbox stuff entirely for now. --- windows/table.cpp | 15 +++++++++++++++ windows/tableimages.cpp | 2 ++ 2 files changed, 17 insertions(+) diff --git a/windows/table.cpp b/windows/table.cpp index d66f9170..12deab16 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -163,6 +163,7 @@ static LRESULT onNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm) break; case CDDS_SUBITEM | CDDS_ITEMPREPAINT: p = (*(t->columns))[nm->iSubItem]; + // TODO none of this runs on the first item // we need this as previous subitems will persist their colors nm->clrText = t->clrItemText; if (p->textParams.ColorModelColumn != -1) { @@ -173,6 +174,20 @@ static LRESULT onNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm) nm->clrText = blend(nm->clrTextBk, r, g, b, a); } } +if (nm->iSubItem != 0) { +HWND header; +LRESULT margin; +header = (HWND) SendMessageW(t->hwnd, LVM_GETHEADER, NULL, NULL); +margin = SendMessageW(header, HDM_GETBITMAPMARGIN, 0, 0); +//nm->rcText.left -= margin; +printf("%d %d %d %d\n", +(int)(nm->nmcd.rc.left), +(int)(nm->nmcd.rc.top), +(int)(nm->nmcd.rc.right), +(int)(nm->nmcd.rc.bottom)); +FillRect(nm->nmcd.hdc, &(nm->nmcd.rc), GetSysColorBrush(COLOR_ACTIVECAPTION)); +return CDRF_SKIPDEFAULT; +} // TODO draw background on image columns if needed ret = CDRF_NEWFONT; break; diff --git a/windows/tableimages.cpp b/windows/tableimages.cpp index 2544e1e7..b8db37af 100644 --- a/windows/tableimages.cpp +++ b/windows/tableimages.cpp @@ -15,6 +15,8 @@ We'll use the small image list. For this, the first few items will be reserved f // checkboxes TODOs: // - see if we need to get rid of the extra margin in subitems +// - get rid of the extra bitmap margin space before text +// - get rid of extra whitespace before text on subitems #define nCheckboxImages 4 From cd2a6f7c29447e55563a9e287a4d4b2e962558ad Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 10 Jun 2018 19:54:04 -0400 Subject: [PATCH 1177/1329] Fixed the checkbox y-offset issue. There are other issues, but this is more hopeful already... --- windows/table.cpp | 14 -------------- windows/tableimages.cpp | 17 ++++++++++++++--- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/windows/table.cpp b/windows/table.cpp index 12deab16..66de886e 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -174,20 +174,6 @@ static LRESULT onNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm) nm->clrText = blend(nm->clrTextBk, r, g, b, a); } } -if (nm->iSubItem != 0) { -HWND header; -LRESULT margin; -header = (HWND) SendMessageW(t->hwnd, LVM_GETHEADER, NULL, NULL); -margin = SendMessageW(header, HDM_GETBITMAPMARGIN, 0, 0); -//nm->rcText.left -= margin; -printf("%d %d %d %d\n", -(int)(nm->nmcd.rc.left), -(int)(nm->nmcd.rc.top), -(int)(nm->nmcd.rc.right), -(int)(nm->nmcd.rc.bottom)); -FillRect(nm->nmcd.hdc, &(nm->nmcd.rc), GetSysColorBrush(COLOR_ACTIVECAPTION)); -return CDRF_SKIPDEFAULT; -} // TODO draw background on image columns if needed ret = CDRF_NEWFONT; break; diff --git a/windows/tableimages.cpp b/windows/tableimages.cpp index b8db37af..0923f409 100644 --- a/windows/tableimages.cpp +++ b/windows/tableimages.cpp @@ -144,6 +144,8 @@ HRESULT uiprivNM_CUSTOMDRAWImagesCheckboxes(uiTable *t, NMLVCUSTOMDRAW *nm, LRES uiprivTableColumnParams *p; int index; RECT r; + RECT cellRect; + LONG yoff; if (nm->nmcd.dwDrawStage == (CDDS_SUBITEM | CDDS_ITEMPREPAINT)) { *lResult |= CDRF_NOTIFYPOSTPAINT; @@ -166,14 +168,23 @@ HRESULT uiprivNM_CUSTOMDRAWImagesCheckboxes(uiTable *t, NMLVCUSTOMDRAW *nm, LRES logLastError(L"LVM_GETSUBITEMRECT"); return E_FAIL; } -#if 0 - // TODO this is offset by one pixel on my system and everything I've found indicates this should not be happening??? + // the real listview also does this :| + ZeroMemory(&cellRect, sizeof (RECT)); + r.left = LVIR_BOUNDS; + r.top = nm->iSubItem; + if (SendMessageW(t->hwnd, LVM_GETSUBITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&cellRect)) == 0) { + logLastError(L"LVM_GETSUBITEMRECT cell"); + return E_FAIL; + } + yoff = ((cellRect.bottom - cellRect.top) - (r.bottom - r.top)) / 2; + r.top += yoff; + r.bottom += yoff; +if ((nm->nmcd.dwItemSpec%2)==0) if (ImageList_Draw(t->smallImages, index, nm->nmcd.hdc, r.left, r.top, ILD_NORMAL) == 0) { logLastError(L"ImageList_Draw()"); return E_FAIL; } -#endif return S_OK; } From dfb3bd39f1360ed6d0f399b307dd46109744a3e3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 10 Jun 2018 20:18:07 -0400 Subject: [PATCH 1178/1329] Some more TODO work. I might as well try custom drawing text now. --- windows/tableimages.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/tableimages.cpp b/windows/tableimages.cpp index 0923f409..a42307b1 100644 --- a/windows/tableimages.cpp +++ b/windows/tableimages.cpp @@ -16,7 +16,7 @@ We'll use the small image list. For this, the first few items will be reserved f // checkboxes TODOs: // - see if we need to get rid of the extra margin in subitems // - get rid of the extra bitmap margin space before text -// - get rid of extra whitespace before text on subitems +// - get rid of extra whitespace before text on subitems (this might not be necessary if we can fill the background of images AND this amount is the same as on the first column; it is a hardcoded 2 logical units in the real list view code) #define nCheckboxImages 4 From 0c6e7add012d365de455ace73f34e15b5510c0d1 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 10 Jun 2018 23:03:54 -0400 Subject: [PATCH 1179/1329] Experimented with custom-drawing the text, this time in a nicer place thatn the default. Okay, this isn't too bad, especially now that it seems everything is vertically centered... --- windows/table.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/windows/table.cpp b/windows/table.cpp index 66de886e..bfb9751e 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -177,6 +177,18 @@ static LRESULT onNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm) // TODO draw background on image columns if needed ret = CDRF_NEWFONT; break; +case CDDS_SUBITEM | CDDS_ITEMPOSTPAINT: +if(nm->iSubItem == 1) { +RECT r, r2; +r.left = LVIR_LABEL; +r.top = 1; +SendMessageW(t->hwnd, LVM_GETSUBITEMRECT, nm->nmcd.dwItemSpec, (LPARAM)(&r)); +r2.left = LVIR_ICON; +r2.top = 1; +SendMessageW(t->hwnd, LVM_GETSUBITEMRECT, nm->nmcd.dwItemSpec, (LPARAM)(&r2)); +r.left = r2.right + 2; +DrawTextW(nm->nmcd.hdc, L"Part", -1, +&r, DT_LEFT | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE | DT_NOPREFIX | DT_EDITCONTROL);} default: ret = CDRF_DODEFAULT; } From c843f1e62d3febe39d24952fcf5ebaafb970b855 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 11 Jun 2018 08:01:18 -0400 Subject: [PATCH 1180/1329] More TODOs. --- windows/table.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/windows/table.cpp b/windows/table.cpp index bfb9751e..32c60244 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -253,7 +253,8 @@ uiWindowsControlAllDefaultsExceptDestroy(uiTable) // "columns widths that avoid truncated data x an integral number of items" // Don't think that'll cut it when some cells have overlong data (eg // stupidly long URLs). So for now, just hardcode a minimum. -// TODO: Investigate using LVM_GETHEADER/HDM_LAYOUT here... +// TODO Investigate using LVM_GETHEADER/HDM_LAYOUT here +// TODO investigate using LVM_APPROXIMATEVIEWRECT here #define tableMinWidth 107 /* in line with other controls */ #define tableMinHeight (14 * 3) /* header + 2 lines (roughly) */ From 8f0019af10765ada02eb354a1e8a8fef175d4105 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 11 Jun 2018 22:22:54 -0400 Subject: [PATCH 1181/1329] Tried to combine all the Windows table metrics stuff. This broke things, so we'll have to go back to the drawing board here. --- wintablemetrics | 153 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 wintablemetrics diff --git a/wintablemetrics b/wintablemetrics new file mode 100644 index 00000000..5c00e252 --- /dev/null +++ b/wintablemetrics @@ -0,0 +1,153 @@ +diff --git a/windows/table.cpp b/windows/table.cpp +index 32c60244..d33cb22f 100644 +--- a/windows/table.cpp ++++ b/windows/table.cpp +@@ -137,11 +137,71 @@ static COLORREF blend(COLORREF base, double r, double g, double b, double a) + (BYTE) (bb * 255)); + } + ++static HRESULT fillSubitemDrawParams(HWND hwnd, NMLVCUSTOMDRAW *nm, uiprivSubitemDrawParams *dp) ++{ ++ RECT r; ++ HRESULT hr; ++ ++ // note that we can't just copy nm->nmcd.rc into p->bounds because that is only defined during prepaint stages ++ ++ if (nm->iSubItem == 0) {return S_OK; ++ ZeroMemory(&r, sizeof (RECT)); ++ r.left = LVIR_BOUNDS; ++ if (SendMessageW(hwnd, LVM_GETITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == FALSE) { ++ logLastError(L"LVM_GETITEMRECT LVIR_BOUNDS"); ++ return E_FAIL; ++ } ++ dp->bounds = r; ++ ZeroMemory(&r, sizeof (RECT)); ++ r.left = LVIR_ICON; ++ if (SendMessageW(hwnd, LVM_GETITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == FALSE) { ++ logLastError(L"LVM_GETITEMRECT LVIR_ICON"); ++ return E_FAIL; ++ } ++ dp->icon = r; ++ ZeroMemory(&r, sizeof (RECT)); ++ r.left = LVIR_LABEL; ++ if (SendMessageW(hwnd, LVM_GETITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == FALSE) { ++ logLastError(L"LVM_GETITEMRECT LVIR_LABEL"); ++ return E_FAIL; ++ } ++ dp->label = r; ++ return S_OK; ++ } ++ ++ ZeroMemory(&r, sizeof (RECT)); ++ r.left = LVIR_BOUNDS; ++ r.top = nm->iSubItem; ++ if (SendMessageW(hwnd, LVM_GETSUBITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == 0) { ++ logLastError(L"LVM_GETSUBITEMRECT LVIR_BOUNDS"); ++ return E_FAIL; ++ } ++ dp->bounds = r; ++ ZeroMemory(&r, sizeof (RECT)); ++ r.left = LVIR_ICON; ++ r.top = nm->iSubItem; ++ if (SendMessageW(hwnd, LVM_GETSUBITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == 0) { ++ logLastError(L"LVM_GETSUBITEMRECT LVIR_ICON"); ++ return E_FAIL; ++ } ++ dp->icon = r; ++ // LVIR_LABEL is treated as LVIR_BOUNDS for LVM_GETSUBITEMRECT, but it doesn't matter because the label rect is uses isn't what we want anyway ++ // there's a hardocded 2-logical unit gap between the icon and text for subitems, AND the text starts being drawn (in the background) one bitmap margin to the right of that ++ // with normal items, there's no gap, and only the 2-logical unit gap after the background starts (TODO confirm this part) ++ // let's copy that to look nicer, even if it's not "accurate" ++ // TODO check against accessibility ++ dp->label = dp->bounds; ++ // because we want the 2 extra logical units to be included with the background, we don't include them here ++ dp->label.left = dp->icon.right; ++ return S_OK; ++} ++ + static LRESULT onNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm) + { + uiprivTableColumnParams *p; + uiTableData *data; + double r, g, b, a; ++ uiprivSubitemDrawParams dp; + LRESULT ret; + HRESULT hr; + +@@ -193,7 +253,12 @@ DrawTextW(nm->nmcd.hdc, L"Part", -1, + ret = CDRF_DODEFAULT; + } + +- hr = uiprivNM_CUSTOMDRAWImagesCheckboxes(t, nm, &ret); ++ ZeroMemory(&dp, sizeof (uiprivSubitemDrawParams)); ++ hr = fillSubitemDrawParams(t->hwnd, nm, &dp); ++ if (hr != S_OK) { ++ // TODO ++ } ++ hr = uiprivNM_CUSTOMDRAWImagesCheckboxes(t, nm, &dp, &ret); + if (hr != S_OK) { + // TODO + } +diff --git a/windows/table.hpp b/windows/table.hpp +index 0be01d8f..2f4f6936 100644 +--- a/windows/table.hpp ++++ b/windows/table.hpp +@@ -43,8 +43,14 @@ struct uiTable { + // custom draw state + COLORREF clrItemText; + }; ++typedef struct uiprivSubitemDrawParams uiprivSubitemDrawParams; ++struct uiprivSubitemDrawParams { ++ RECT bounds; ++ RECT icon; ++ RECT label; ++}; + + // tableimages.cpp + extern HRESULT uiprivLVN_GETDISPINFOImagesCheckboxes(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnParams *p); +-extern HRESULT uiprivNM_CUSTOMDRAWImagesCheckboxes(uiTable *t, NMLVCUSTOMDRAW *nm, LRESULT *lResult); ++extern HRESULT uiprivNM_CUSTOMDRAWImagesCheckboxes(uiTable *t, NMLVCUSTOMDRAW *nm, uiprivSubitemDrawParams *dp, LRESULT *lResult); + extern HRESULT uiprivTableSetupImagesCheckboxes(uiTable *t); +diff --git a/windows/tableimages.cpp b/windows/tableimages.cpp +index a42307b1..14bae8c8 100644 +--- a/windows/tableimages.cpp ++++ b/windows/tableimages.cpp +@@ -139,12 +139,11 @@ HRESULT uiprivLVN_GETDISPINFOImagesCheckboxes(uiTable *t, NMLVDISPINFOW *nm, uip + // however, there seems to be no way to do this natively, so we have to draw over ourselves (TODO?) + // hopefully the performance won't be too bad + // see also https://www.codeproject.com/Articles/79/Neat-Stuff-to-Do-in-List-Controls-Using-Custom-Dra +-HRESULT uiprivNM_CUSTOMDRAWImagesCheckboxes(uiTable *t, NMLVCUSTOMDRAW *nm, LRESULT *lResult) ++HRESULT uiprivNM_CUSTOMDRAWImagesCheckboxes(uiTable *t, NMLVCUSTOMDRAW *nm, uiprivSubitemDrawParams *dp, LRESULT *lResult) + { + uiprivTableColumnParams *p; + int index; + RECT r; +- RECT cellRect; + LONG yoff; + + if (nm->nmcd.dwDrawStage == (CDDS_SUBITEM | CDDS_ITEMPREPAINT)) { +@@ -161,22 +160,9 @@ HRESULT uiprivNM_CUSTOMDRAWImagesCheckboxes(uiTable *t, NMLVCUSTOMDRAW *nm, LRES + + index = checkboxIndex(t->model, nm->nmcd.dwItemSpec, + p->checkboxModelColumn, p->checkboxEditableColumn); +- ZeroMemory(&r, sizeof (RECT)); +- r.left = LVIR_ICON; +- r.top = nm->iSubItem; +- if (SendMessageW(t->hwnd, LVM_GETSUBITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == 0) { +- logLastError(L"LVM_GETSUBITEMRECT"); +- return E_FAIL; +- } ++ r = dp->icon; + // the real listview also does this :| +- ZeroMemory(&cellRect, sizeof (RECT)); +- r.left = LVIR_BOUNDS; +- r.top = nm->iSubItem; +- if (SendMessageW(t->hwnd, LVM_GETSUBITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&cellRect)) == 0) { +- logLastError(L"LVM_GETSUBITEMRECT cell"); +- return E_FAIL; +- } +- yoff = ((cellRect.bottom - cellRect.top) - (r.bottom - r.top)) / 2; ++ yoff = ((dp->bounds.bottom - dp->bounds.top) - (r.bottom - r.top)) / 2; + r.top += yoff; + r.bottom += yoff; + if ((nm->nmcd.dwItemSpec%2)==0) From 0f89418a9559c86135e21bf40fa29808248a2aa9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 12 Jun 2018 01:40:26 -0400 Subject: [PATCH 1182/1329] Fixed a typo in tableimages.cpp. Okay, so the code I had before worked purely by accident, and this code doesn't. Wonderful... --- windows/tableimages.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/windows/tableimages.cpp b/windows/tableimages.cpp index a42307b1..6bb168b8 100644 --- a/windows/tableimages.cpp +++ b/windows/tableimages.cpp @@ -170,8 +170,8 @@ HRESULT uiprivNM_CUSTOMDRAWImagesCheckboxes(uiTable *t, NMLVCUSTOMDRAW *nm, LRES } // the real listview also does this :| ZeroMemory(&cellRect, sizeof (RECT)); - r.left = LVIR_BOUNDS; - r.top = nm->iSubItem; + cellRect.left = LVIR_BOUNDS; + cellRect.top = nm->iSubItem; if (SendMessageW(t->hwnd, LVM_GETSUBITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&cellRect)) == 0) { logLastError(L"LVM_GETSUBITEMRECT cell"); return E_FAIL; From 59d8e81b854b1b185e83d1d016b09637aa02f7ac Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 12 Jun 2018 01:54:21 -0400 Subject: [PATCH 1183/1329] There, found the reason that code doesn't work (LVIF_ICON was as tall as LVIF_BOUNDS) and fixed it (use the actual icon size for vertical centering) --- windows/tableimages.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/windows/tableimages.cpp b/windows/tableimages.cpp index 6bb168b8..6c6f2df7 100644 --- a/windows/tableimages.cpp +++ b/windows/tableimages.cpp @@ -144,7 +144,7 @@ HRESULT uiprivNM_CUSTOMDRAWImagesCheckboxes(uiTable *t, NMLVCUSTOMDRAW *nm, LRES uiprivTableColumnParams *p; int index; RECT r; - RECT cellRect; + int cxIcon, cyIcon; LONG yoff; if (nm->nmcd.dwDrawStage == (CDDS_SUBITEM | CDDS_ITEMPREPAINT)) { @@ -169,14 +169,11 @@ HRESULT uiprivNM_CUSTOMDRAWImagesCheckboxes(uiTable *t, NMLVCUSTOMDRAW *nm, LRES return E_FAIL; } // the real listview also does this :| - ZeroMemory(&cellRect, sizeof (RECT)); - cellRect.left = LVIR_BOUNDS; - cellRect.top = nm->iSubItem; - if (SendMessageW(t->hwnd, LVM_GETSUBITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&cellRect)) == 0) { + if (ImageList_GetIconSize(t->smallImages, &cxIcon, &cyIcon) == 0) { logLastError(L"LVM_GETSUBITEMRECT cell"); return E_FAIL; } - yoff = ((cellRect.bottom - cellRect.top) - (r.bottom - r.top)) / 2; + yoff = ((r.bottom - r.top) - cyIcon) / 2; r.top += yoff; r.bottom += yoff; if ((nm->nmcd.dwItemSpec%2)==0) From e52373c59a88737c558690f161ab1e8b2cade0ed Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 12 Jun 2018 07:58:27 -0400 Subject: [PATCH 1184/1329] Reintegrated wintablemetrics, properly this time (for the most part). It works. Now we can start switching to custom-drawing everything. --- windows/table.cpp | 69 +++++++++++++++++- windows/table.hpp | 10 ++- windows/tableimages.cpp | 10 +-- wintablemetrics | 153 ---------------------------------------- 4 files changed, 78 insertions(+), 164 deletions(-) delete mode 100644 wintablemetrics diff --git a/windows/table.cpp b/windows/table.cpp index 32c60244..9689a023 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -137,11 +137,72 @@ static COLORREF blend(COLORREF base, double r, double g, double b, double a) (BYTE) (bb * 255)); } +static HRESULT fillSubitemDrawParams(HWND hwnd, NMLVCUSTOMDRAW *nm, uiprivSubitemDrawParams *dp) +{ + RECT r; + HRESULT hr; + + // note that we can't just copy nm->nmcd.rc into p->bounds because that is only defined during prepaint stages + + // TODO this corrupts memory + if (nm->iSubItem == 0) {return S_OK; + ZeroMemory(&r, sizeof (RECT)); + r.left = LVIR_BOUNDS; + if (SendMessageW(hwnd, LVM_GETITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == FALSE) { + logLastError(L"LVM_GETITEMRECT LVIR_BOUNDS"); + return E_FAIL; + } + dp->bounds = r; + ZeroMemory(&r, sizeof (RECT)); + r.left = LVIR_ICON; + if (SendMessageW(hwnd, LVM_GETITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == FALSE) { + logLastError(L"LVM_GETITEMRECT LVIR_ICON"); + return E_FAIL; + } + dp->icon = r; + ZeroMemory(&r, sizeof (RECT)); + r.left = LVIR_LABEL; + if (SendMessageW(hwnd, LVM_GETITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == FALSE) { + logLastError(L"LVM_GETITEMRECT LVIR_LABEL"); + return E_FAIL; + } + dp->label = r; + return S_OK; + } + + ZeroMemory(&r, sizeof (RECT)); + r.left = LVIR_BOUNDS; + r.top = nm->iSubItem; + if (SendMessageW(hwnd, LVM_GETSUBITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == 0) { + logLastError(L"LVM_GETSUBITEMRECT LVIR_BOUNDS"); + return E_FAIL; + } + dp->bounds = r; + ZeroMemory(&r, sizeof (RECT)); + r.left = LVIR_ICON; + r.top = nm->iSubItem; + if (SendMessageW(hwnd, LVM_GETSUBITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == 0) { + logLastError(L"LVM_GETSUBITEMRECT LVIR_ICON"); + return E_FAIL; + } + dp->icon = r; + // LVIR_LABEL is treated as LVIR_BOUNDS for LVM_GETSUBITEMRECT, but it doesn't matter because the label rect is uses isn't what we want anyway + // there's a hardocded 2-logical unit gap between the icon and text for subitems, AND the text starts being drawn (in the background) one bitmap margin to the right of that + // with normal items, there's no gap, and only the 2-logical unit gap after the background starts (TODO confirm this part) + // let's copy that to look nicer, even if it's not "accurate" + // TODO check against accessibility + dp->label = dp->bounds; + // because we want the 2 extra logical units to be included with the background, we don't include them here + dp->label.left = dp->icon.right; + return S_OK; +} + static LRESULT onNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm) { uiprivTableColumnParams *p; uiTableData *data; double r, g, b, a; + uiprivSubitemDrawParams dp; LRESULT ret; HRESULT hr; @@ -193,7 +254,13 @@ DrawTextW(nm->nmcd.hdc, L"Part", -1, ret = CDRF_DODEFAULT; } - hr = uiprivNM_CUSTOMDRAWImagesCheckboxes(t, nm, &ret); +if ((nm->nmcd.dwDrawStage & CDDS_SUBITEM) == 0)return ret; + ZeroMemory(&dp, sizeof (uiprivSubitemDrawParams)); + hr = fillSubitemDrawParams(t->hwnd, nm, &dp); + if (hr != S_OK) { + // TODO + } + hr = uiprivNM_CUSTOMDRAWImagesCheckboxes(t, nm, &dp, &ret); if (hr != S_OK) { // TODO } diff --git a/windows/table.hpp b/windows/table.hpp index 0be01d8f..4820eaad 100644 --- a/windows/table.hpp +++ b/windows/table.hpp @@ -43,8 +43,14 @@ struct uiTable { // custom draw state COLORREF clrItemText; }; - +typedef struct uiprivSubitemDrawParams uiprivSubitemDrawParams; +struct uiprivSubitemDrawParams { + RECT bounds; + RECT icon; + RECT label; +}; + // tableimages.cpp extern HRESULT uiprivLVN_GETDISPINFOImagesCheckboxes(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnParams *p); -extern HRESULT uiprivNM_CUSTOMDRAWImagesCheckboxes(uiTable *t, NMLVCUSTOMDRAW *nm, LRESULT *lResult); +extern HRESULT uiprivNM_CUSTOMDRAWImagesCheckboxes(uiTable *t, NMLVCUSTOMDRAW *nm, uiprivSubitemDrawParams *dp, LRESULT *lResult); extern HRESULT uiprivTableSetupImagesCheckboxes(uiTable *t); diff --git a/windows/tableimages.cpp b/windows/tableimages.cpp index 6c6f2df7..8bc563ac 100644 --- a/windows/tableimages.cpp +++ b/windows/tableimages.cpp @@ -139,7 +139,7 @@ HRESULT uiprivLVN_GETDISPINFOImagesCheckboxes(uiTable *t, NMLVDISPINFOW *nm, uip // however, there seems to be no way to do this natively, so we have to draw over ourselves (TODO?) // hopefully the performance won't be too bad // see also https://www.codeproject.com/Articles/79/Neat-Stuff-to-Do-in-List-Controls-Using-Custom-Dra -HRESULT uiprivNM_CUSTOMDRAWImagesCheckboxes(uiTable *t, NMLVCUSTOMDRAW *nm, LRESULT *lResult) +HRESULT uiprivNM_CUSTOMDRAWImagesCheckboxes(uiTable *t, NMLVCUSTOMDRAW *nm, uiprivSubitemDrawParams *dp, LRESULT *lResult) { uiprivTableColumnParams *p; int index; @@ -161,13 +161,7 @@ HRESULT uiprivNM_CUSTOMDRAWImagesCheckboxes(uiTable *t, NMLVCUSTOMDRAW *nm, LRES index = checkboxIndex(t->model, nm->nmcd.dwItemSpec, p->checkboxModelColumn, p->checkboxEditableColumn); - ZeroMemory(&r, sizeof (RECT)); - r.left = LVIR_ICON; - r.top = nm->iSubItem; - if (SendMessageW(t->hwnd, LVM_GETSUBITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == 0) { - logLastError(L"LVM_GETSUBITEMRECT"); - return E_FAIL; - } + r = dp->icon; // the real listview also does this :| if (ImageList_GetIconSize(t->smallImages, &cxIcon, &cyIcon) == 0) { logLastError(L"LVM_GETSUBITEMRECT cell"); diff --git a/wintablemetrics b/wintablemetrics deleted file mode 100644 index 5c00e252..00000000 --- a/wintablemetrics +++ /dev/null @@ -1,153 +0,0 @@ -diff --git a/windows/table.cpp b/windows/table.cpp -index 32c60244..d33cb22f 100644 ---- a/windows/table.cpp -+++ b/windows/table.cpp -@@ -137,11 +137,71 @@ static COLORREF blend(COLORREF base, double r, double g, double b, double a) - (BYTE) (bb * 255)); - } - -+static HRESULT fillSubitemDrawParams(HWND hwnd, NMLVCUSTOMDRAW *nm, uiprivSubitemDrawParams *dp) -+{ -+ RECT r; -+ HRESULT hr; -+ -+ // note that we can't just copy nm->nmcd.rc into p->bounds because that is only defined during prepaint stages -+ -+ if (nm->iSubItem == 0) {return S_OK; -+ ZeroMemory(&r, sizeof (RECT)); -+ r.left = LVIR_BOUNDS; -+ if (SendMessageW(hwnd, LVM_GETITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == FALSE) { -+ logLastError(L"LVM_GETITEMRECT LVIR_BOUNDS"); -+ return E_FAIL; -+ } -+ dp->bounds = r; -+ ZeroMemory(&r, sizeof (RECT)); -+ r.left = LVIR_ICON; -+ if (SendMessageW(hwnd, LVM_GETITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == FALSE) { -+ logLastError(L"LVM_GETITEMRECT LVIR_ICON"); -+ return E_FAIL; -+ } -+ dp->icon = r; -+ ZeroMemory(&r, sizeof (RECT)); -+ r.left = LVIR_LABEL; -+ if (SendMessageW(hwnd, LVM_GETITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == FALSE) { -+ logLastError(L"LVM_GETITEMRECT LVIR_LABEL"); -+ return E_FAIL; -+ } -+ dp->label = r; -+ return S_OK; -+ } -+ -+ ZeroMemory(&r, sizeof (RECT)); -+ r.left = LVIR_BOUNDS; -+ r.top = nm->iSubItem; -+ if (SendMessageW(hwnd, LVM_GETSUBITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == 0) { -+ logLastError(L"LVM_GETSUBITEMRECT LVIR_BOUNDS"); -+ return E_FAIL; -+ } -+ dp->bounds = r; -+ ZeroMemory(&r, sizeof (RECT)); -+ r.left = LVIR_ICON; -+ r.top = nm->iSubItem; -+ if (SendMessageW(hwnd, LVM_GETSUBITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == 0) { -+ logLastError(L"LVM_GETSUBITEMRECT LVIR_ICON"); -+ return E_FAIL; -+ } -+ dp->icon = r; -+ // LVIR_LABEL is treated as LVIR_BOUNDS for LVM_GETSUBITEMRECT, but it doesn't matter because the label rect is uses isn't what we want anyway -+ // there's a hardocded 2-logical unit gap between the icon and text for subitems, AND the text starts being drawn (in the background) one bitmap margin to the right of that -+ // with normal items, there's no gap, and only the 2-logical unit gap after the background starts (TODO confirm this part) -+ // let's copy that to look nicer, even if it's not "accurate" -+ // TODO check against accessibility -+ dp->label = dp->bounds; -+ // because we want the 2 extra logical units to be included with the background, we don't include them here -+ dp->label.left = dp->icon.right; -+ return S_OK; -+} -+ - static LRESULT onNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm) - { - uiprivTableColumnParams *p; - uiTableData *data; - double r, g, b, a; -+ uiprivSubitemDrawParams dp; - LRESULT ret; - HRESULT hr; - -@@ -193,7 +253,12 @@ DrawTextW(nm->nmcd.hdc, L"Part", -1, - ret = CDRF_DODEFAULT; - } - -- hr = uiprivNM_CUSTOMDRAWImagesCheckboxes(t, nm, &ret); -+ ZeroMemory(&dp, sizeof (uiprivSubitemDrawParams)); -+ hr = fillSubitemDrawParams(t->hwnd, nm, &dp); -+ if (hr != S_OK) { -+ // TODO -+ } -+ hr = uiprivNM_CUSTOMDRAWImagesCheckboxes(t, nm, &dp, &ret); - if (hr != S_OK) { - // TODO - } -diff --git a/windows/table.hpp b/windows/table.hpp -index 0be01d8f..2f4f6936 100644 ---- a/windows/table.hpp -+++ b/windows/table.hpp -@@ -43,8 +43,14 @@ struct uiTable { - // custom draw state - COLORREF clrItemText; - }; -+typedef struct uiprivSubitemDrawParams uiprivSubitemDrawParams; -+struct uiprivSubitemDrawParams { -+ RECT bounds; -+ RECT icon; -+ RECT label; -+}; - - // tableimages.cpp - extern HRESULT uiprivLVN_GETDISPINFOImagesCheckboxes(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnParams *p); --extern HRESULT uiprivNM_CUSTOMDRAWImagesCheckboxes(uiTable *t, NMLVCUSTOMDRAW *nm, LRESULT *lResult); -+extern HRESULT uiprivNM_CUSTOMDRAWImagesCheckboxes(uiTable *t, NMLVCUSTOMDRAW *nm, uiprivSubitemDrawParams *dp, LRESULT *lResult); - extern HRESULT uiprivTableSetupImagesCheckboxes(uiTable *t); -diff --git a/windows/tableimages.cpp b/windows/tableimages.cpp -index a42307b1..14bae8c8 100644 ---- a/windows/tableimages.cpp -+++ b/windows/tableimages.cpp -@@ -139,12 +139,11 @@ HRESULT uiprivLVN_GETDISPINFOImagesCheckboxes(uiTable *t, NMLVDISPINFOW *nm, uip - // however, there seems to be no way to do this natively, so we have to draw over ourselves (TODO?) - // hopefully the performance won't be too bad - // see also https://www.codeproject.com/Articles/79/Neat-Stuff-to-Do-in-List-Controls-Using-Custom-Dra --HRESULT uiprivNM_CUSTOMDRAWImagesCheckboxes(uiTable *t, NMLVCUSTOMDRAW *nm, LRESULT *lResult) -+HRESULT uiprivNM_CUSTOMDRAWImagesCheckboxes(uiTable *t, NMLVCUSTOMDRAW *nm, uiprivSubitemDrawParams *dp, LRESULT *lResult) - { - uiprivTableColumnParams *p; - int index; - RECT r; -- RECT cellRect; - LONG yoff; - - if (nm->nmcd.dwDrawStage == (CDDS_SUBITEM | CDDS_ITEMPREPAINT)) { -@@ -161,22 +160,9 @@ HRESULT uiprivNM_CUSTOMDRAWImagesCheckboxes(uiTable *t, NMLVCUSTOMDRAW *nm, LRES - - index = checkboxIndex(t->model, nm->nmcd.dwItemSpec, - p->checkboxModelColumn, p->checkboxEditableColumn); -- ZeroMemory(&r, sizeof (RECT)); -- r.left = LVIR_ICON; -- r.top = nm->iSubItem; -- if (SendMessageW(t->hwnd, LVM_GETSUBITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == 0) { -- logLastError(L"LVM_GETSUBITEMRECT"); -- return E_FAIL; -- } -+ r = dp->icon; - // the real listview also does this :| -- ZeroMemory(&cellRect, sizeof (RECT)); -- r.left = LVIR_BOUNDS; -- r.top = nm->iSubItem; -- if (SendMessageW(t->hwnd, LVM_GETSUBITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&cellRect)) == 0) { -- logLastError(L"LVM_GETSUBITEMRECT cell"); -- return E_FAIL; -- } -- yoff = ((cellRect.bottom - cellRect.top) - (r.bottom - r.top)) / 2; -+ yoff = ((dp->bounds.bottom - dp->bounds.top) - (r.bottom - r.top)) / 2; - r.top += yoff; - r.bottom += yoff; - if ((nm->nmcd.dwItemSpec%2)==0) From f28c97d4d62425f744328272c08293139bcd3f98 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 12 Jun 2018 08:17:31 -0400 Subject: [PATCH 1185/1329] Switched checkboxes from postpaint to prepaint and turned off drawing everything while we slowly transition everything over. --- windows/table.cpp | 4 ++-- windows/table.hpp | 2 +- windows/tableimages.cpp | 13 ++++--------- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/windows/table.cpp b/windows/table.cpp index 9689a023..7a16e02c 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -236,7 +236,7 @@ static LRESULT onNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm) } } // TODO draw background on image columns if needed - ret = CDRF_NEWFONT; + ret = CDRF_SKIPDEFAULT | CDRF_NEWFONT; break; case CDDS_SUBITEM | CDDS_ITEMPOSTPAINT: if(nm->iSubItem == 1) { @@ -260,7 +260,7 @@ if ((nm->nmcd.dwDrawStage & CDDS_SUBITEM) == 0)return ret; if (hr != S_OK) { // TODO } - hr = uiprivNM_CUSTOMDRAWImagesCheckboxes(t, nm, &dp, &ret); + hr = uiprivNM_CUSTOMDRAWImagesCheckboxes(t, nm, &dp); if (hr != S_OK) { // TODO } diff --git a/windows/table.hpp b/windows/table.hpp index 4820eaad..514af352 100644 --- a/windows/table.hpp +++ b/windows/table.hpp @@ -52,5 +52,5 @@ struct uiprivSubitemDrawParams { // tableimages.cpp extern HRESULT uiprivLVN_GETDISPINFOImagesCheckboxes(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnParams *p); -extern HRESULT uiprivNM_CUSTOMDRAWImagesCheckboxes(uiTable *t, NMLVCUSTOMDRAW *nm, uiprivSubitemDrawParams *dp, LRESULT *lResult); +extern HRESULT uiprivNM_CUSTOMDRAWImagesCheckboxes(uiTable *t, NMLVCUSTOMDRAW *nm, uiprivSubitemDrawParams *dp); extern HRESULT uiprivTableSetupImagesCheckboxes(uiTable *t); diff --git a/windows/tableimages.cpp b/windows/tableimages.cpp index 8bc563ac..278a88a5 100644 --- a/windows/tableimages.cpp +++ b/windows/tableimages.cpp @@ -136,10 +136,9 @@ HRESULT uiprivLVN_GETDISPINFOImagesCheckboxes(uiTable *t, NMLVDISPINFOW *nm, uip } // in order to properly look like checkboxes, we need to exclude them from being colored in by the selection rect -// however, there seems to be no way to do this natively, so we have to draw over ourselves (TODO?) -// hopefully the performance won't be too bad -// see also https://www.codeproject.com/Articles/79/Neat-Stuff-to-Do-in-List-Controls-Using-Custom-Dra -HRESULT uiprivNM_CUSTOMDRAWImagesCheckboxes(uiTable *t, NMLVCUSTOMDRAW *nm, uiprivSubitemDrawParams *dp, LRESULT *lResult) +// however, there seems to be no way to do this natively, so we have to draw the icons ourselves +// see also https://www.codeproject.com/Articles/79/Neat-Stuff-to-Do-in-List-Controls-Using-Custom-Dra (and while this uses postpaint to draw over the existing icon, we are drawing everything ourselves, so we only draw once) +HRESULT uiprivNM_CUSTOMDRAWImagesCheckboxes(uiTable *t, NMLVCUSTOMDRAW *nm, uiprivSubitemDrawParams *dp) { uiprivTableColumnParams *p; int index; @@ -147,11 +146,7 @@ HRESULT uiprivNM_CUSTOMDRAWImagesCheckboxes(uiTable *t, NMLVCUSTOMDRAW *nm, uipr int cxIcon, cyIcon; LONG yoff; - if (nm->nmcd.dwDrawStage == (CDDS_SUBITEM | CDDS_ITEMPREPAINT)) { - *lResult |= CDRF_NOTIFYPOSTPAINT; - return S_OK; - } - if (nm->nmcd.dwDrawStage != (CDDS_SUBITEM | CDDS_ITEMPOSTPAINT)) + if (nm->nmcd.dwDrawStage != (CDDS_SUBITEM | CDDS_ITEMPREPAINT)) return S_OK; // only draw over checkboxes From f216af94e747467db4bd856142702dcdcad97083 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 12 Jun 2018 08:20:15 -0400 Subject: [PATCH 1186/1329] Made our new custom draw code only run on item prepaint. --- windows/table.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/windows/table.cpp b/windows/table.cpp index 7a16e02c..6d5da0d7 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -208,8 +208,7 @@ static LRESULT onNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm) switch (nm->nmcd.dwDrawStage) { case CDDS_PREPAINT: - ret = CDRF_NOTIFYITEMDRAW; - break; + return CDRF_NOTIFYITEMDRAW; case CDDS_ITEMPREPAINT: if (t->backgroundColumn != -1) { data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->nmcd.dwItemSpec, t->backgroundColumn); @@ -251,7 +250,7 @@ r.left = r2.right + 2; DrawTextW(nm->nmcd.hdc, L"Part", -1, &r, DT_LEFT | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE | DT_NOPREFIX | DT_EDITCONTROL);} default: - ret = CDRF_DODEFAULT; + return CDRF_DODEFAULT; } if ((nm->nmcd.dwDrawStage & CDDS_SUBITEM) == 0)return ret; From c4251894b5870498d6596d28d88d5c86353c7233 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 12 Jun 2018 20:15:59 -0400 Subject: [PATCH 1187/1329] Flipped the memory corruption back on so we can debug it. --- windows/table.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/windows/table.cpp b/windows/table.cpp index 6d5da0d7..c573f97c 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -145,7 +145,7 @@ static HRESULT fillSubitemDrawParams(HWND hwnd, NMLVCUSTOMDRAW *nm, uiprivSubite // note that we can't just copy nm->nmcd.rc into p->bounds because that is only defined during prepaint stages // TODO this corrupts memory - if (nm->iSubItem == 0) {return S_OK; + if (nm->iSubItem == 0) { ZeroMemory(&r, sizeof (RECT)); r.left = LVIR_BOUNDS; if (SendMessageW(hwnd, LVM_GETITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == FALSE) { @@ -219,7 +219,7 @@ static LRESULT onNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm) } } t->clrItemText = nm->clrText; - ret = CDRF_NEWFONT | CDRF_NOTIFYSUBITEMDRAW; + ret = CDRF_NOTIFYSUBITEMDRAW; break; case CDDS_SUBITEM | CDDS_ITEMPREPAINT: p = (*(t->columns))[nm->iSubItem]; @@ -235,10 +235,10 @@ static LRESULT onNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm) } } // TODO draw background on image columns if needed - ret = CDRF_SKIPDEFAULT | CDRF_NEWFONT; + ret = /*CDRF_SKIPDEFAULT | */CDRF_NEWFONT; break; -case CDDS_SUBITEM | CDDS_ITEMPOSTPAINT: -if(nm->iSubItem == 1) { +//case CDDS_SUBITEM | CDDS_ITEMPOSTPAINT: +if(0){//nm->iSubItem == 1) { RECT r, r2; r.left = LVIR_LABEL; r.top = 1; @@ -253,7 +253,6 @@ DrawTextW(nm->nmcd.hdc, L"Part", -1, return CDRF_DODEFAULT; } -if ((nm->nmcd.dwDrawStage & CDDS_SUBITEM) == 0)return ret; ZeroMemory(&dp, sizeof (uiprivSubitemDrawParams)); hr = fillSubitemDrawParams(t->hwnd, nm, &dp); if (hr != S_OK) { From c01b010fd76421f7f498870e44d32f5a1c17027d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 13 Jun 2018 08:30:00 -0400 Subject: [PATCH 1188/1329] Fixed memory corruption issues. See code for details. --- windows/table.cpp | 37 ++++++------------------------------- windows/table.hpp | 5 ----- windows/winapi.hpp | 1 - 3 files changed, 6 insertions(+), 37 deletions(-) diff --git a/windows/table.cpp b/windows/table.cpp index c573f97c..26b8b406 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -81,28 +81,20 @@ static LRESULT onLVN_GETDISPINFO(uiTable *t, NMLVDISPINFOW *nm) static uiprivTableColumnParams *p; uiTableData *data; WCHAR *wstr; - HDC dc; - IWICBitmap *wb; - HBITMAP b; - int checked; - bool queueUpdated = false; HRESULT hr; - wstr = t->dispinfoStrings->front(); - if (wstr != NULL) - uiprivFree(wstr); - t->dispinfoStrings->pop(); - p = (*(t->columns))[nm->item.iSubItem]; -nm->item.pszText=L"abcdefg"; if ((nm->item.mask & LVIF_TEXT) != 0) if (p->textModelColumn != -1) { data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->item.iItem, p->textModelColumn); wstr = toUTF16(uiTableDataString(data)); uiFreeTableData(data); - nm->item.pszText = wstr; - t->dispinfoStrings->push(wstr); - queueUpdated = true; + // we could just make pszText into a freshly allocated conversion and avoid the limitation of cchTextMax + // but then we would have to keep things around for some amount of time (some pages on MSDN say 2 additional LVN_GETDISPINFO messages) + // and in practice, anything that results in extra LVN_GETDISPINFO messages (such as fillSubitemDrawParams() below) will break this counting + // TODO make it so we don't have to make a copy; instead we can convert directly into pszText (this will also avoid the risk of having a dangling surrogate pair at the end) + wcscpy_s(nm->item.pszText, nm->item.cchTextMax, wstr); + uiprivFree(wstr); } hr = uiprivLVN_GETDISPINFOImagesCheckboxes(t, nm, p); @@ -110,9 +102,6 @@ nm->item.pszText=L"abcdefg"; // TODO } - // we don't want to pop from an empty queue, so if nothing updated the queue (no info was filled in above), just push NULL - if (!queueUpdated) - t->dispinfoStrings->push(NULL); return 0; } @@ -285,7 +274,6 @@ static void uiTableDestroy(uiControl *c) uiTable *t = uiTable(c); uiTableModel *model = t->model; std::vector::iterator it; - WCHAR *wstr; uiWindowsUnregisterWM_NOTIFYHandler(t->hwnd); uiWindowsEnsureDestroyWindow(t->hwnd); @@ -296,14 +284,6 @@ static void uiTableDestroy(uiControl *c) break; } } - // empty the string queue - while (t->dispinfoStrings->size() != 0) { - wstr = t->dispinfoStrings->front(); - if (wstr != NULL) - uiprivFree(wstr); - t->dispinfoStrings->pop(); - } - delete t->dispinfoStrings; // free the columns for (auto col : *(t->columns)) uiprivFree(col); @@ -464,11 +444,6 @@ uiTable *uiNewTable(uiTableModel *model) t->backgroundColumn = -1; - t->dispinfoStrings = new std::queue; - // this encodes the idea that two LVN_GETDISPINFOs must complete before we can free a string: the first real one is for the fourth call to free - for (i = 0; i < uiprivNumLVN_GETDISPINFOSkip; i++) - t->dispinfoStrings->push(NULL); - hr = uiprivTableSetupImagesCheckboxes(t); if (hr != S_OK) { // TODO diff --git a/windows/table.hpp b/windows/table.hpp index 514af352..b1b53c66 100644 --- a/windows/table.hpp +++ b/windows/table.hpp @@ -30,11 +30,6 @@ struct uiTable { WPARAM nColumns; int backgroundColumn; - // owner data state - // MSDN says we have to keep LVN_GETDISPINFO strings we allocate around at least until "two additional LVN_GETDISPINFO messages have been sent". - // we'll use this queue to do so; the "two additional" part is encoded in the initial state of the queue - std::queue *dispinfoStrings; - // tableimages.cpp // TODO make sure what we're doing is even allowed HIMAGELIST smallImages; diff --git a/windows/winapi.hpp b/windows/winapi.hpp index 297726e3..19426844 100644 --- a/windows/winapi.hpp +++ b/windows/winapi.hpp @@ -60,5 +60,4 @@ #include #include #include -#include #endif From 15bc55dd5e747cf6d332a98f23f284f0d97e07ff Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 13 Jun 2018 10:09:13 -0400 Subject: [PATCH 1189/1329] Started work on backgrounds. This isn't ideal quite yet, but. --- windows/table.cpp | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/windows/table.cpp b/windows/table.cpp index 26b8b406..93b3c3e0 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -133,7 +133,6 @@ static HRESULT fillSubitemDrawParams(HWND hwnd, NMLVCUSTOMDRAW *nm, uiprivSubite // note that we can't just copy nm->nmcd.rc into p->bounds because that is only defined during prepaint stages - // TODO this corrupts memory if (nm->iSubItem == 0) { ZeroMemory(&r, sizeof (RECT)); r.left = LVIR_BOUNDS; @@ -207,8 +206,30 @@ static LRESULT onNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm) nm->clrTextBk = blend(nm->clrTextBk, r, g, b, a); } } + { + LRESULT state; + HBRUSH b; + bool freeBrush = false; + + // note: nm->nmcd.uItemState CDIS_SELECTED is unreliable for the listview configuration we have + state = SendMessageW(t->hwnd, LVM_GETITEMSTATE, nm->nmcd.dwItemSpec, LVIS_SELECTED); + if ((state & LVIS_SELECTED) != 0) + b = GetSysColorBrush(COLOR_HIGHLIGHT); + else if (nm->clrTextBk != CLR_DEFAULT) { + b = CreateSolidBrush(nm->clrTextBk); + if (b == NULL) + logLastError(L"CreateSolidBrush()"); + freeBrush = true; + } else + b = GetSysColorBrush(COLOR_WINDOW); + // TODO check error + FillRect(nm->nmcd.hdc, &(nm->nmcd.rc), b); + if (freeBrush) + if (DeleteObject(b) == 0) + logLastError(L"DeleteObject()"); + } t->clrItemText = nm->clrText; - ret = CDRF_NOTIFYSUBITEMDRAW; + ret = CDRF_NEWFONT | CDRF_NOTIFYSUBITEMDRAW; break; case CDDS_SUBITEM | CDDS_ITEMPREPAINT: p = (*(t->columns))[nm->iSubItem]; From caa06886874896503c822673f16407a407c426ae Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 13 Jun 2018 21:06:08 -0400 Subject: [PATCH 1190/1329] Started drawing uiTable text. We're drawing fully manually from here on out. Also fixed LVIF_INDENT settings and a few other things. --- windows/CMakeLists.txt | 1 + windows/table.cpp | 72 ++++++++++++++++----------------- windows/table.hpp | 8 +++- windows/tableimages.cpp | 20 +++++----- windows/tabletext.cpp | 88 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 139 insertions(+), 50 deletions(-) create mode 100644 windows/tabletext.cpp diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 4cee5d68..a896e700 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -52,6 +52,7 @@ list(APPEND _LIBUI_SOURCES windows/tab.cpp windows/table.cpp windows/tableimages.cpp + windows/tabletext.cpp windows/tabpage.cpp windows/text.cpp windows/utf16.cpp diff --git a/windows/table.cpp b/windows/table.cpp index 93b3c3e0..d3cd97f7 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -79,24 +79,13 @@ void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) static LRESULT onLVN_GETDISPINFO(uiTable *t, NMLVDISPINFOW *nm) { static uiprivTableColumnParams *p; - uiTableData *data; - WCHAR *wstr; HRESULT hr; p = (*(t->columns))[nm->item.iSubItem]; - if ((nm->item.mask & LVIF_TEXT) != 0) - if (p->textModelColumn != -1) { - data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->item.iItem, p->textModelColumn); - wstr = toUTF16(uiTableDataString(data)); - uiFreeTableData(data); - // we could just make pszText into a freshly allocated conversion and avoid the limitation of cchTextMax - // but then we would have to keep things around for some amount of time (some pages on MSDN say 2 additional LVN_GETDISPINFO messages) - // and in practice, anything that results in extra LVN_GETDISPINFO messages (such as fillSubitemDrawParams() below) will break this counting - // TODO make it so we don't have to make a copy; instead we can convert directly into pszText (this will also avoid the risk of having a dangling surrogate pair at the end) - wcscpy_s(nm->item.pszText, nm->item.cchTextMax, wstr); - uiprivFree(wstr); - } - + hr = uiprivLVN_GETDISPINFOText(t, nm, p); + if (hr != S_OK) { + // TODO + } hr = uiprivLVN_GETDISPINFOImagesCheckboxes(t, nm, p); if (hr != S_OK) { // TODO @@ -126,31 +115,47 @@ static COLORREF blend(COLORREF base, double r, double g, double b, double a) (BYTE) (bb * 255)); } -static HRESULT fillSubitemDrawParams(HWND hwnd, NMLVCUSTOMDRAW *nm, uiprivSubitemDrawParams *dp) +COLORREF uiprivTableBlendedColorFromModel(uiTable *t, NMLVCUSTOMDRAW *nm, int modelColumn, int fallbackSysColorID) { + uiTableData *data; + double r, g, b, a; + + data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->nmcd.dwItemSpec, modelColumn); + if (data == NULL) + return GetSysColor(fallbackSysColorID); + uiTableDataColor(data, &r, &g, &b, &a); + uiFreeTableData(data); + return blend(nm->clrTextBk, r, g, b, a); +} + +static HRESULT fillSubitemDrawParams(uiTable *t, NMLVCUSTOMDRAW *nm, uiprivSubitemDrawParams *dp) +{ + LRESULT state; RECT r; HRESULT hr; - // note that we can't just copy nm->nmcd.rc into p->bounds because that is only defined during prepaint stages + // note: nm->nmcd.uItemState CDIS_SELECTED is unreliable for the listview configuration we have + state = SendMessageW(t->hwnd, LVM_GETITEMSTATE, nm->nmcd.dwItemSpec, LVIS_SELECTED); + dp->selected = (state & LVIS_SELECTED) != 0; if (nm->iSubItem == 0) { ZeroMemory(&r, sizeof (RECT)); r.left = LVIR_BOUNDS; - if (SendMessageW(hwnd, LVM_GETITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == FALSE) { + if (SendMessageW(t->hwnd, LVM_GETITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == FALSE) { logLastError(L"LVM_GETITEMRECT LVIR_BOUNDS"); return E_FAIL; } dp->bounds = r; ZeroMemory(&r, sizeof (RECT)); r.left = LVIR_ICON; - if (SendMessageW(hwnd, LVM_GETITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == FALSE) { + if (SendMessageW(t->hwnd, LVM_GETITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == FALSE) { logLastError(L"LVM_GETITEMRECT LVIR_ICON"); return E_FAIL; } dp->icon = r; ZeroMemory(&r, sizeof (RECT)); r.left = LVIR_LABEL; - if (SendMessageW(hwnd, LVM_GETITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == FALSE) { + if (SendMessageW(t->hwnd, LVM_GETITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == FALSE) { logLastError(L"LVM_GETITEMRECT LVIR_LABEL"); return E_FAIL; } @@ -161,7 +166,7 @@ static HRESULT fillSubitemDrawParams(HWND hwnd, NMLVCUSTOMDRAW *nm, uiprivSubite ZeroMemory(&r, sizeof (RECT)); r.left = LVIR_BOUNDS; r.top = nm->iSubItem; - if (SendMessageW(hwnd, LVM_GETSUBITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == 0) { + if (SendMessageW(t->hwnd, LVM_GETSUBITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == 0) { logLastError(L"LVM_GETSUBITEMRECT LVIR_BOUNDS"); return E_FAIL; } @@ -169,7 +174,7 @@ static HRESULT fillSubitemDrawParams(HWND hwnd, NMLVCUSTOMDRAW *nm, uiprivSubite ZeroMemory(&r, sizeof (RECT)); r.left = LVIR_ICON; r.top = nm->iSubItem; - if (SendMessageW(hwnd, LVM_GETSUBITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == 0) { + if (SendMessageW(t->hwnd, LVM_GETSUBITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == 0) { logLastError(L"LVM_GETSUBITEMRECT LVIR_ICON"); return E_FAIL; } @@ -229,8 +234,7 @@ static LRESULT onNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm) logLastError(L"DeleteObject()"); } t->clrItemText = nm->clrText; - ret = CDRF_NEWFONT | CDRF_NOTIFYSUBITEMDRAW; - break; + return CDRF_NEWFONT | CDRF_NOTIFYSUBITEMDRAW; case CDDS_SUBITEM | CDDS_ITEMPREPAINT: p = (*(t->columns))[nm->iSubItem]; // TODO none of this runs on the first item @@ -245,26 +249,14 @@ static LRESULT onNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm) } } // TODO draw background on image columns if needed - ret = /*CDRF_SKIPDEFAULT | */CDRF_NEWFONT; + ret = CDRF_SKIPDEFAULT | CDRF_NEWFONT; break; -//case CDDS_SUBITEM | CDDS_ITEMPOSTPAINT: -if(0){//nm->iSubItem == 1) { -RECT r, r2; -r.left = LVIR_LABEL; -r.top = 1; -SendMessageW(t->hwnd, LVM_GETSUBITEMRECT, nm->nmcd.dwItemSpec, (LPARAM)(&r)); -r2.left = LVIR_ICON; -r2.top = 1; -SendMessageW(t->hwnd, LVM_GETSUBITEMRECT, nm->nmcd.dwItemSpec, (LPARAM)(&r2)); -r.left = r2.right + 2; -DrawTextW(nm->nmcd.hdc, L"Part", -1, -&r, DT_LEFT | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE | DT_NOPREFIX | DT_EDITCONTROL);} default: return CDRF_DODEFAULT; } ZeroMemory(&dp, sizeof (uiprivSubitemDrawParams)); - hr = fillSubitemDrawParams(t->hwnd, nm, &dp); + hr = fillSubitemDrawParams(t, nm, &dp); if (hr != S_OK) { // TODO } @@ -272,6 +264,10 @@ DrawTextW(nm->nmcd.hdc, L"Part", -1, if (hr != S_OK) { // TODO } + hr = uiprivNM_CUSTOMDRAWText(t, nm, p, &dp); + if (hr != S_OK) { + // TODO + } return ret; } diff --git a/windows/table.hpp b/windows/table.hpp index b1b53c66..8849a833 100644 --- a/windows/table.hpp +++ b/windows/table.hpp @@ -40,11 +40,17 @@ struct uiTable { }; typedef struct uiprivSubitemDrawParams uiprivSubitemDrawParams; struct uiprivSubitemDrawParams { + bool selected; RECT bounds; RECT icon; RECT label; }; - +extern COLORREF uiprivTableBlendedColorFromModel(uiTable *t, NMLVCUSTOMDRAW *nm, int modelColumn, int fallbackSysColorID); + +// tabletext.cpp +extern HRESULT uiprivLVN_GETDISPINFOText(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnParams *p); +extern HRESULT uiprivNM_CUSTOMDRAWText(uiTable *t, NMLVCUSTOMDRAW *nm, uiprivTableColumnParams *p, uiprivSubitemDrawParams *dp); + // tableimages.cpp extern HRESULT uiprivLVN_GETDISPINFOImagesCheckboxes(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnParams *p); extern HRESULT uiprivNM_CUSTOMDRAWImagesCheckboxes(uiTable *t, NMLVCUSTOMDRAW *nm, uiprivSubitemDrawParams *dp); diff --git a/windows/tableimages.cpp b/windows/tableimages.cpp index 278a88a5..69a23bb5 100644 --- a/windows/tableimages.cpp +++ b/windows/tableimages.cpp @@ -95,8 +95,14 @@ HRESULT uiprivLVN_GETDISPINFOImagesCheckboxes(uiTable *t, NMLVDISPINFOW *nm, uip uiTableData *data; HRESULT hr; + if (nm->item.iSubItem == 0 && p->imageModelColumn == -1 && p->checkboxModelColumn == -1) { + // having an image list always leaves space for an image on the main item :| + // other places on the internet imply that you should be able to do this but that it shouldn't work + // but it works perfectly (and pixel-perfectly too) for me, so... + nm->item.mask |= LVIF_INDENT; + nm->item.iIndent = -1; + } if ((nm->item.mask & LVIF_IMAGE) == 0) - // TODO we actually need to do the first column fix here too... return S_OK; // nothing to do here if (p->imageModelColumn != -1) { @@ -122,16 +128,8 @@ HRESULT uiprivLVN_GETDISPINFOImagesCheckboxes(uiTable *t, NMLVDISPINFOW *nm, uip return S_OK; } - // if we got here, there's no image in this cell - nm->item.mask &= ~LVIF_IMAGE; - // having an image list always leaves space for an image on the main item :| - // other places on the internet imply that you should be able to do this but that it shouldn't work - // but it works perfectly (and pixel-perfectly too) for me, so... - // TODO it doesn't work anymore... - if (nm->item.iSubItem == 0) { - nm->item.mask |= LVIF_INDENT; - nm->item.iIndent = -1; - } + // TODO see if this is correct + nm->item.iImage = -1; return S_OK; } diff --git a/windows/tabletext.cpp b/windows/tabletext.cpp new file mode 100644 index 00000000..9208553d --- /dev/null +++ b/windows/tabletext.cpp @@ -0,0 +1,88 @@ +// 13 june 2018 +#include "uipriv_windows.hpp" +#include "table.hpp" + +// This file handles text in tables. + +HRESULT uiprivLVN_GETDISPINFOText(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnParams *p) +{ + uiTableData *data; + WCHAR *wstr; + HRESULT hr; + + if ((nm->item.mask & LVIF_TEXT) == 0) + return S_OK; + if (p->textModelColumn != -1) + return S_OK; + + data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->item.iItem, p->textModelColumn); + wstr = toUTF16(uiTableDataString(data)); + uiFreeTableData(data); + // we could just make pszText into a freshly allocated conversion and avoid the limitation of cchTextMax + // but then we would have to keep things around for some amount of time (some pages on MSDN say 2 additional LVN_GETDISPINFO messages) + // and in practice, anything that results in extra LVN_GETDISPINFO messages (such as fillSubitemDrawParams() below) will break this counting + // TODO make it so we don't have to make a copy; instead we can convert directly into pszText (this will also avoid the risk of having a dangling surrogate pair at the end) + wcsncpy(nm->item.pszText, wstr, nm->item.cchTextMax); + nm->item.pszText[nm->item.cchTextMax - 1] = L'\0'; + uiprivFree(wstr); + return S_OK; +} + +HRESULT uiprivNM_CUSTOMDRAWText(uiTable *t, NMLVCUSTOMDRAW *nm, uiprivTableColumnParams *p, uiprivSubitemDrawParams *dp) +{ + COLORREF color; + COLORREF prev; + int prevMode; + RECT r; + uiTableData *data; + WCHAR *wstr; + + if (p->textModelColumn == -1) + return S_OK; + + if (dp->selected) + color = GetSysColor(COLOR_HIGHLIGHTTEXT); + else if (p->textParams.ColorModelColumn != -1) + color = uiprivTableBlendedColorFromModel(t, nm, p->textParams.ColorModelColumn, COLOR_WINDOWTEXT); + else + color = GetSysColor(COLOR_WINDOWTEXT); + prev = SetTextColor(nm->nmcd.hdc, color); + if (prev == CLR_INVALID) { + logLastError(L"SetTextColor()"); + return E_FAIL; + } + prevMode = SetBkMode(nm->nmcd.hdc, TRANSPARENT); + if (prevMode == 0) { + logLastError(L"SetBkMode()"); + return E_FAIL; + } + + // text is actually drawn two logical units to the right of the beginning of the text rect + // TODO confirm this for the first column on both image and imageless cases + // TODO actually this whole thing is wrong for imageless columns + r = dp->label; + r.left += 2; + + data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->nmcd.dwItemSpec, p->textModelColumn); + wstr = toUTF16(uiTableDataString(data)); + uiFreeTableData(data); + // these flags are a menagerie of flags from various sources: guessing, the Windows 2000 source leak, various custom draw examples on the web, etc. + // TODO find the real correct flags + if (DrawTextW(nm->nmcd.hdc, wstr, -1, &r, DT_LEFT | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE | DT_NOPREFIX | DT_EDITCONTROL) == 0) { + uiprivFree(wstr); + logLastError(L"DrawTextW()"); + return E_FAIL; + } + uiprivFree(wstr); + + // TODO decide once and for all what to compare to here and with SelectObject() + if (SetBkMode(nm->nmcd.hdc, prevMode) != TRANSPARENT) { + logLastError(L"SetBkMode() prev"); + return E_FAIL; + } + if (SetTextColor(nm->nmcd.hdc, prev) != color) { + logLastError(L"SetTextColor() prev"); + return E_FAIL; + } + return S_OK; +} From c79f9b4ecd40f0588e6bf6b9e9915bc8216fd808 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 13 Jun 2018 21:46:28 -0400 Subject: [PATCH 1191/1329] Added bitmap margins to the draw parameters. We'll need it later. --- windows/table.cpp | 4 ++++ windows/table.hpp | 1 + 2 files changed, 5 insertions(+) diff --git a/windows/table.cpp b/windows/table.cpp index d3cd97f7..ea3257de 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -131,6 +131,7 @@ COLORREF uiprivTableBlendedColorFromModel(uiTable *t, NMLVCUSTOMDRAW *nm, int mo static HRESULT fillSubitemDrawParams(uiTable *t, NMLVCUSTOMDRAW *nm, uiprivSubitemDrawParams *dp) { LRESULT state; + HWND header; RECT r; HRESULT hr; @@ -138,6 +139,9 @@ static HRESULT fillSubitemDrawParams(uiTable *t, NMLVCUSTOMDRAW *nm, uiprivSubit state = SendMessageW(t->hwnd, LVM_GETITEMSTATE, nm->nmcd.dwItemSpec, LVIS_SELECTED); dp->selected = (state & LVIS_SELECTED) != 0; + header = (HWND) SendMessageW(t->hwnd, LVM_GETHEADER, 0, 0); + dp->bitmapMargin = SendMessageW(header, HDM_GETBITMAPMARGIN, 0, 0); + if (nm->iSubItem == 0) { ZeroMemory(&r, sizeof (RECT)); r.left = LVIR_BOUNDS; diff --git a/windows/table.hpp b/windows/table.hpp index 8849a833..69b453be 100644 --- a/windows/table.hpp +++ b/windows/table.hpp @@ -41,6 +41,7 @@ struct uiTable { typedef struct uiprivSubitemDrawParams uiprivSubitemDrawParams; struct uiprivSubitemDrawParams { bool selected; + LRESULT bitmapMargin; RECT bounds; RECT icon; RECT label; From 75063ec266c0eae3dc49f88a1f2e23ea1ef5269a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 14 Jun 2018 20:26:51 -0400 Subject: [PATCH 1192/1329] Fixed text positioning in non-image columns. This separation probably isn't working... --- windows/tabletext.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/windows/tabletext.cpp b/windows/tabletext.cpp index 9208553d..04a38b5f 100644 --- a/windows/tabletext.cpp +++ b/windows/tabletext.cpp @@ -57,11 +57,14 @@ HRESULT uiprivNM_CUSTOMDRAWText(uiTable *t, NMLVCUSTOMDRAW *nm, uiprivTableColum return E_FAIL; } - // text is actually drawn two logical units to the right of the beginning of the text rect - // TODO confirm this for the first column on both image and imageless cases - // TODO actually this whole thing is wrong for imageless columns r = dp->label; - r.left += 2; + if (p->imageModelColumn != -1 || p->checkboxModelColumn != -1) + // text is actually drawn two logical units to the right of the beginning of the text rect + // TODO confirm this for the first column on both image and imageless cases + // TODO actually this whole thing is wrong for imageless columns + r.left += 2; + else + r.left = dp->bounds.left + dp->bitmapMargin; data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->nmcd.dwItemSpec, p->textModelColumn); wstr = toUTF16(uiTableDataString(data)); From fda8f2fbae32645fc52af8319cfea4b93da4f47b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 14 Jun 2018 21:31:45 -0400 Subject: [PATCH 1193/1329] Started a separate file just for drawing tables; integrated text. --- windows/table.cpp | 66 ----------- windows/tabledraw.cpp | 251 ++++++++++++++++++++++++++++++++++++++++++ windows/tabletext.cpp | 62 ----------- 3 files changed, 251 insertions(+), 128 deletions(-) create mode 100644 windows/tabledraw.cpp diff --git a/windows/table.cpp b/windows/table.cpp index ea3257de..19bacd4b 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -128,72 +128,6 @@ COLORREF uiprivTableBlendedColorFromModel(uiTable *t, NMLVCUSTOMDRAW *nm, int mo return blend(nm->clrTextBk, r, g, b, a); } -static HRESULT fillSubitemDrawParams(uiTable *t, NMLVCUSTOMDRAW *nm, uiprivSubitemDrawParams *dp) -{ - LRESULT state; - HWND header; - RECT r; - HRESULT hr; - - // note: nm->nmcd.uItemState CDIS_SELECTED is unreliable for the listview configuration we have - state = SendMessageW(t->hwnd, LVM_GETITEMSTATE, nm->nmcd.dwItemSpec, LVIS_SELECTED); - dp->selected = (state & LVIS_SELECTED) != 0; - - header = (HWND) SendMessageW(t->hwnd, LVM_GETHEADER, 0, 0); - dp->bitmapMargin = SendMessageW(header, HDM_GETBITMAPMARGIN, 0, 0); - - if (nm->iSubItem == 0) { - ZeroMemory(&r, sizeof (RECT)); - r.left = LVIR_BOUNDS; - if (SendMessageW(t->hwnd, LVM_GETITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == FALSE) { - logLastError(L"LVM_GETITEMRECT LVIR_BOUNDS"); - return E_FAIL; - } - dp->bounds = r; - ZeroMemory(&r, sizeof (RECT)); - r.left = LVIR_ICON; - if (SendMessageW(t->hwnd, LVM_GETITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == FALSE) { - logLastError(L"LVM_GETITEMRECT LVIR_ICON"); - return E_FAIL; - } - dp->icon = r; - ZeroMemory(&r, sizeof (RECT)); - r.left = LVIR_LABEL; - if (SendMessageW(t->hwnd, LVM_GETITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == FALSE) { - logLastError(L"LVM_GETITEMRECT LVIR_LABEL"); - return E_FAIL; - } - dp->label = r; - return S_OK; - } - - ZeroMemory(&r, sizeof (RECT)); - r.left = LVIR_BOUNDS; - r.top = nm->iSubItem; - if (SendMessageW(t->hwnd, LVM_GETSUBITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == 0) { - logLastError(L"LVM_GETSUBITEMRECT LVIR_BOUNDS"); - return E_FAIL; - } - dp->bounds = r; - ZeroMemory(&r, sizeof (RECT)); - r.left = LVIR_ICON; - r.top = nm->iSubItem; - if (SendMessageW(t->hwnd, LVM_GETSUBITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == 0) { - logLastError(L"LVM_GETSUBITEMRECT LVIR_ICON"); - return E_FAIL; - } - dp->icon = r; - // LVIR_LABEL is treated as LVIR_BOUNDS for LVM_GETSUBITEMRECT, but it doesn't matter because the label rect is uses isn't what we want anyway - // there's a hardocded 2-logical unit gap between the icon and text for subitems, AND the text starts being drawn (in the background) one bitmap margin to the right of that - // with normal items, there's no gap, and only the 2-logical unit gap after the background starts (TODO confirm this part) - // let's copy that to look nicer, even if it's not "accurate" - // TODO check against accessibility - dp->label = dp->bounds; - // because we want the 2 extra logical units to be included with the background, we don't include them here - dp->label.left = dp->icon.right; - return S_OK; -} - static LRESULT onNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm) { uiprivTableColumnParams *p; diff --git a/windows/tabledraw.cpp b/windows/tabledraw.cpp new file mode 100644 index 00000000..3036ac30 --- /dev/null +++ b/windows/tabledraw.cpp @@ -0,0 +1,251 @@ +// 14 june 2018 +#include "uipriv_windows.hpp" +#include "table.hpp" + +struct drawState { + uiTable *t; + uiTableModel *m; + uiprivTableColumnParams *p; + + HDC dc; + int iItem; + int iSubItem; + BOOL hasText; + BOOL hasImage; + BOOL selected; + BOOL focused; + + RECT itemBounds; + RECT itemIcon; + RECT itemText; + RECT subitemBounds; + RECT subitemIcon; + RECT subitemText; + + COLORREF bgColor; + HBRUSH bgBrush; + BOOL freeBgBrush; + COLORREF textColor; + HBRUSH textBrush; + BOOL freeTextBrush; + + LRESULT bitmapMargins; + int cxIcon; + int cyIcon; + + RECT realTextRect; + RECT focusRect; +}; + +static HRESULT computeAndDrawTextRect(struct drawState *s) +{ + RECT r; + + r = s->subitemText; + if (!s->hasText && !s->hasImage) + r = s->subitemBounds; + + if (FillRect(s->dc, &r, s->bgBrush) == 0) { + logLastError(L"FillRect()"); + return E_FAIL; + } + UnionRect(&(s->focusRect), &(s->focusRect), &r); + + s->realTextRect = r; + // TODO confirm whether this really happens on column 0 as well + if (s->hasImage && s->iSubItem != 0) + // Normally there's this many hard-coded logical units + // of blank space, followed by the background, followed + // by a bitmap margin's worth of space. This looks bad, + // so we overrule that to start the background immediately + // and the text after the hard-coded amount. + s->realTextRect.left += 2; + else if (s->iSubItem != 0) + // In the case of subitem text without an image, we draw + // text one bitmap margin away from the left edge. + s->realTextRect.left += s->bitmapMargin; + return S_OK; +} + +static HRESULT drawTextPart(struct drawState *s) +{ + COLORREF prevText; + int prevMode; + RECT r; + uiTableData *data; + WCHAR *wstr; + + if (!s->hasText) + return S_OK; + + prevText = SetTextColor(s->dc, s->textColor); + if (prevText == CLR_INVALID) { + logLastError(L"SetTextColor()"); + return E_FAIL; + } + prevMode = SetBkMode(s->dc, TRANSPARENT); + if (prevMode == 0) { + logLastError(L"SetBkMode()"); + return E_FAIL; + } + + data = (*(s->m->mh->CellValue))(s->m->mh, s->m, s->iItem, s->p->textModelColumn); + wstr = toUTF16(uiTableDataString(data)); + uiFreeTableData(data); + // These flags are a menagerie of flags from various sources: + // guessing, the Windows 2000 source leak, various custom + // draw examples on the web, etc. + // TODO find the real correct flags + if (DrawTextW(s->dc, wstr, -1, &(s->realTextRect), DT_LEFT | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE | DT_NOPREFIX | DT_EDITCONTROL) == 0) { + uiprivFree(wstr); + logLastError(L"DrawTextW()"); + return E_FAIL; + } + uiprivFree(wstr); + + // TODO decide once and for all what to compare to here and with SelectObject() + if (SetBkMode(nm->nmcd.hdc, prevMode) != TRANSPARENT) { + logLastError(L"SetBkMode() prev"); + return E_FAIL; + } + if (SetTextColor(nm->nmcd.hdc, prevText) != color) { + logLastError(L"SetTextColor() prev"); + return E_FAIL; + } + return S_OK; +} + +static HRESULT freeDrawState(struct drawState *s) +{ + HRESULT hr, hrret; + + hrret = S_OK; + if (p->freeTextBrush) { + if (DeleteObject(p->textBrush) == 0) { + logLastError(L"DeleteObject()"); + hrret = E_FAIL; + // continue cleaning up anyway + } + p->freeTextBrush = NO; + } + if (p->freeBgBrush) { + if (DeleteObject(p->bgBrush) == 0) { + logLastError(L"DeleteObject()"); + hrret = E_FAIL; + // continue cleaning up anyway + } + p->freeBgBrush = NO; + } + return hrret; +} + +static HRESULT itemRect(HRESULT hr, uiTable *t, UINT uMsg, WPARAM wParam, LONG left, LONG top, LRESULT bad, RECT *r) +{ + if (hr != S_OK) + return hr; + ZeroMemory(r, sizeof (RECT)); + r->left = left; + r->top = top; + if (SendMessageW(t->hwnd, uMsg, wParam, (LPARAM) r) == bad) { + logLastError(L"itemRect() message"); + return E_FAIL; + } + return S_OK; +} + +static HRESULT fillDrawState(struct drawState *s, uiTable *t, NMLVCUSTOMDRAW *nm, uiprivTableColumnParams *p) +{ + LRESULT state; + HWND header; + HRESULT hr; + + ZeroMemory(s, sizeof (struct drawState)); + s->t = t; + s->m = t->model; + s->p = p; + + s->dc = nm->nmcd.hdc; + s->iItem = nm->nmcd.dwItemSpec; + s->iSubItem = nm->iSubItem; + s->hasText = p->textModelColumn != -1; + s->hasImage = (p->imageModelColumn != -1) || (p->checkboxModelColumn != -1); + // nm->nmcd.uItemState CDIS_SELECTED is unreliable for the + // listview configuration we have, so we must do this. + state = SendMessageW(t->hwnd, LVM_GETITEMSTATE, nm->nmcd.dwItemSpec, LVIS_SELECTED); + dp->selected = (state & LVIS_SELECTED) != 0; + dp->focused = (nm->nmcd.uiTemState & CDIS_FOCUSED) != 0; + + hr = itemRect(S_OK, t, LVM_GETITEMRECT, LVIR_BOUNDS, + 0, &(s->itemBounds)); + hr = itemRect(hr, t, LVM_GETITEMRECT, LVIR_ICON, + 0, &(s->itemIcon)); + hr = itemRect(hr, t, LVM_GETITEMRECT, LVIR_LABEL, + 0, &(s->itemLabel)); + hr = itemRect(hr, t, LVM_GETSUBITEMRECT, LVIR_BOUNDS, + s->iSubItem, &(s->subitemBounds)); + hr = itemRect(hr, t, LVM_GETSUBITEMRECT, LVIR_ICON, + s->iSubItem, &(s->subitemIcon)); + if (hr != S_OK) + goto fail; + // LVM_GETSUBITEMRECT treats LVIR_LABEL as the same as + // LVIR_BOUNDS, so we can't use that directly. Instead, let's + // assume the text is immediately after the icon. The correct + // rect will be determined by computeAndDrawTextRect() + // above. + s->subitemLabel = s->subitemBounds; + s->subitemLabel.left = s->subitemIcon.right; + + if (s->selected) { + s->bgColor = GetSysColor(COLOR_HIGHLIGHT); + s->bgBrush = GetSysColorBrush(COLOR_HIGHLIGHT); + s->textColor = GetSysColor(COLOR_HIGHLIGHTTEXT); + s->textBrush = GetSysColorBrush(COLOR_HIGHLIGHTTEXT); + } else { + uiTableData *data; + + s->bgColor = GetSysColor(COLOR_WINDOW); + s->bgBrush = GetSysColorBrush(COLOR_WINDOW); + if (t->backgroundColumn != -1) { + data = (*(s->m->mh->CellValue))(s->m->mh, s->m, s->iItem, t->backgroundColumn); + if (data != NULL) { + uiTableDataColor(data, &r, &g, &b, &a); + uiFreeTableData(data); + s->bgColor = blend(s->bgColor, r, g, b, a); + s->bgBrush = CreateSolidBrush(s->bgBrush); + if (s->bgBrush == NULL) { + logLastError(L"CreateSolidBrush()"); + hr = E_FAIL; + goto fail; + } + s->freeBgBrush = TRUE; + } + } + s->textColor = GetSysColor(COLOR_WINDOWTEXT); + s->textBrush = GetSysColorBrush(COLOR_WINDOWTEXT); + if (p->textParams.ColorModelColumn != -1) { + data = (*(s->m->mh->CellValue))(s->m->mh, s->m, s->iItem, p->textParams.ColorModelColumn); + if (data != NULL) { + uiTableDataColor(data, &r, &g, &b, &a); + uiFreeTableData(data); + s->textColor = blend(s->bgColor, r, g, b, a); + s->textBrush = CreateSolidBrush(s->textColor); + if (s->textBrush == NULL) { + logLastError(L"CreateSolidBrush()"); + hr = E_FAIL; + goto fail; + } + s->freeTextBrush = TRUE; + } + } + } + + header = (HWND) SendMessageW(t->hwnd, LVM_GETHEADER, 0, 0); + s->bitmapMargin = SendMessageW(header, HDM_GETBITMAPMARGIN, 0, 0); + // TODO + + return S_OK; +fail: + // ignore the error; we need to return the one we got above + freeDrawState(s); + return hr; +} diff --git a/windows/tabletext.cpp b/windows/tabletext.cpp index 04a38b5f..d1feb336 100644 --- a/windows/tabletext.cpp +++ b/windows/tabletext.cpp @@ -27,65 +27,3 @@ HRESULT uiprivLVN_GETDISPINFOText(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColu uiprivFree(wstr); return S_OK; } - -HRESULT uiprivNM_CUSTOMDRAWText(uiTable *t, NMLVCUSTOMDRAW *nm, uiprivTableColumnParams *p, uiprivSubitemDrawParams *dp) -{ - COLORREF color; - COLORREF prev; - int prevMode; - RECT r; - uiTableData *data; - WCHAR *wstr; - - if (p->textModelColumn == -1) - return S_OK; - - if (dp->selected) - color = GetSysColor(COLOR_HIGHLIGHTTEXT); - else if (p->textParams.ColorModelColumn != -1) - color = uiprivTableBlendedColorFromModel(t, nm, p->textParams.ColorModelColumn, COLOR_WINDOWTEXT); - else - color = GetSysColor(COLOR_WINDOWTEXT); - prev = SetTextColor(nm->nmcd.hdc, color); - if (prev == CLR_INVALID) { - logLastError(L"SetTextColor()"); - return E_FAIL; - } - prevMode = SetBkMode(nm->nmcd.hdc, TRANSPARENT); - if (prevMode == 0) { - logLastError(L"SetBkMode()"); - return E_FAIL; - } - - r = dp->label; - if (p->imageModelColumn != -1 || p->checkboxModelColumn != -1) - // text is actually drawn two logical units to the right of the beginning of the text rect - // TODO confirm this for the first column on both image and imageless cases - // TODO actually this whole thing is wrong for imageless columns - r.left += 2; - else - r.left = dp->bounds.left + dp->bitmapMargin; - - data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->nmcd.dwItemSpec, p->textModelColumn); - wstr = toUTF16(uiTableDataString(data)); - uiFreeTableData(data); - // these flags are a menagerie of flags from various sources: guessing, the Windows 2000 source leak, various custom draw examples on the web, etc. - // TODO find the real correct flags - if (DrawTextW(nm->nmcd.hdc, wstr, -1, &r, DT_LEFT | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE | DT_NOPREFIX | DT_EDITCONTROL) == 0) { - uiprivFree(wstr); - logLastError(L"DrawTextW()"); - return E_FAIL; - } - uiprivFree(wstr); - - // TODO decide once and for all what to compare to here and with SelectObject() - if (SetBkMode(nm->nmcd.hdc, prevMode) != TRANSPARENT) { - logLastError(L"SetBkMode() prev"); - return E_FAIL; - } - if (SetTextColor(nm->nmcd.hdc, prev) != color) { - logLastError(L"SetTextColor() prev"); - return E_FAIL; - } - return S_OK; -} From e6da33121edeb4d0cf56ad4ea3df3317b59dcbf8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 15 Jun 2018 10:04:32 -0400 Subject: [PATCH 1194/1329] And integrated tabledraw.cpp. It works, barring some technical gltiches. It also makes me realize the alpha blending issue was my fault... --- windows/CMakeLists.txt | 1 + windows/table.cpp | 123 +++-------------------------------------- windows/table.hpp | 5 +- windows/tabledraw.cpp | 115 +++++++++++++++++++++++++++++--------- 4 files changed, 101 insertions(+), 143 deletions(-) diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index a896e700..d074ca87 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -51,6 +51,7 @@ list(APPEND _LIBUI_SOURCES windows/stddialogs.cpp windows/tab.cpp windows/table.cpp + windows/tabledraw.cpp windows/tableimages.cpp windows/tabletext.cpp windows/tabpage.cpp diff --git a/windows/table.cpp b/windows/table.cpp index 19bacd4b..a766010f 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -78,6 +78,7 @@ void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) static LRESULT onLVN_GETDISPINFO(uiTable *t, NMLVDISPINFOW *nm) { + // TODO remove static static uiprivTableColumnParams *p; HRESULT hr; @@ -94,131 +95,21 @@ static LRESULT onLVN_GETDISPINFO(uiTable *t, NMLVDISPINFOW *nm) return 0; } -static COLORREF blend(COLORREF base, double r, double g, double b, double a) -{ - double br, bg, bb; - - // TODO find a better fix than this - // TODO s listview already alphablending? - // TODO find the right color here - if (base == CLR_DEFAULT) - base = GetSysColor(COLOR_WINDOW); - br = ((double) GetRValue(base)) / 255.0; - bg = ((double) GetGValue(base)) / 255.0; - bb = ((double) GetBValue(base)) / 255.0; - - br = (r * a) + (br * (1.0 - a)); - bg = (g * a) + (bg * (1.0 - a)); - bb = (b * a) + (bb * (1.0 - a)); - return RGB((BYTE) (br * 255), - (BYTE) (bg * 255), - (BYTE) (bb * 255)); -} - -COLORREF uiprivTableBlendedColorFromModel(uiTable *t, NMLVCUSTOMDRAW *nm, int modelColumn, int fallbackSysColorID) -{ - uiTableData *data; - double r, g, b, a; - - data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->nmcd.dwItemSpec, modelColumn); - if (data == NULL) - return GetSysColor(fallbackSysColorID); - uiTableDataColor(data, &r, &g, &b, &a); - uiFreeTableData(data); - return blend(nm->clrTextBk, r, g, b, a); -} - -static LRESULT onNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm) -{ - uiprivTableColumnParams *p; - uiTableData *data; - double r, g, b, a; - uiprivSubitemDrawParams dp; - LRESULT ret; - HRESULT hr; - - switch (nm->nmcd.dwDrawStage) { - case CDDS_PREPAINT: - return CDRF_NOTIFYITEMDRAW; - case CDDS_ITEMPREPAINT: - if (t->backgroundColumn != -1) { - data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->nmcd.dwItemSpec, t->backgroundColumn); - if (data != NULL) { - uiTableDataColor(data, &r, &g, &b, &a); - uiFreeTableData(data); - nm->clrTextBk = blend(nm->clrTextBk, r, g, b, a); - } - } - { - LRESULT state; - HBRUSH b; - bool freeBrush = false; - - // note: nm->nmcd.uItemState CDIS_SELECTED is unreliable for the listview configuration we have - state = SendMessageW(t->hwnd, LVM_GETITEMSTATE, nm->nmcd.dwItemSpec, LVIS_SELECTED); - if ((state & LVIS_SELECTED) != 0) - b = GetSysColorBrush(COLOR_HIGHLIGHT); - else if (nm->clrTextBk != CLR_DEFAULT) { - b = CreateSolidBrush(nm->clrTextBk); - if (b == NULL) - logLastError(L"CreateSolidBrush()"); - freeBrush = true; - } else - b = GetSysColorBrush(COLOR_WINDOW); - // TODO check error - FillRect(nm->nmcd.hdc, &(nm->nmcd.rc), b); - if (freeBrush) - if (DeleteObject(b) == 0) - logLastError(L"DeleteObject()"); - } - t->clrItemText = nm->clrText; - return CDRF_NEWFONT | CDRF_NOTIFYSUBITEMDRAW; - case CDDS_SUBITEM | CDDS_ITEMPREPAINT: - p = (*(t->columns))[nm->iSubItem]; - // TODO none of this runs on the first item - // we need this as previous subitems will persist their colors - nm->clrText = t->clrItemText; - if (p->textParams.ColorModelColumn != -1) { - data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->nmcd.dwItemSpec, p->textParams.ColorModelColumn); - if (data != NULL) { - uiTableDataColor(data, &r, &g, &b, &a); - uiFreeTableData(data); - nm->clrText = blend(nm->clrTextBk, r, g, b, a); - } - } - // TODO draw background on image columns if needed - ret = CDRF_SKIPDEFAULT | CDRF_NEWFONT; - break; - default: - return CDRF_DODEFAULT; - } - - ZeroMemory(&dp, sizeof (uiprivSubitemDrawParams)); - hr = fillSubitemDrawParams(t, nm, &dp); - if (hr != S_OK) { - // TODO - } - hr = uiprivNM_CUSTOMDRAWImagesCheckboxes(t, nm, &dp); - if (hr != S_OK) { - // TODO - } - hr = uiprivNM_CUSTOMDRAWText(t, nm, p, &dp); - if (hr != S_OK) { - // TODO - } - return ret; -} - static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult) { uiTable *t = uiTable(c); + HRESULT hr; switch (nmhdr->code) { case LVN_GETDISPINFO: *lResult = onLVN_GETDISPINFO(t, (NMLVDISPINFOW *) nmhdr); return TRUE; case NM_CUSTOMDRAW: - *lResult = onNM_CUSTOMDRAW(t, (NMLVCUSTOMDRAW *) nmhdr); + hr = uiprivTableHandleNM_CUSTOMDRAW(t, (NMLVCUSTOMDRAW *) nmhdr, lResult); + if (hr != S_OK) { + // TODO + return FALSE; + } return TRUE; } return FALSE; diff --git a/windows/table.hpp b/windows/table.hpp index 69b453be..3ceeb977 100644 --- a/windows/table.hpp +++ b/windows/table.hpp @@ -46,13 +46,14 @@ struct uiprivSubitemDrawParams { RECT icon; RECT label; }; -extern COLORREF uiprivTableBlendedColorFromModel(uiTable *t, NMLVCUSTOMDRAW *nm, int modelColumn, int fallbackSysColorID); // tabletext.cpp extern HRESULT uiprivLVN_GETDISPINFOText(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnParams *p); -extern HRESULT uiprivNM_CUSTOMDRAWText(uiTable *t, NMLVCUSTOMDRAW *nm, uiprivTableColumnParams *p, uiprivSubitemDrawParams *dp); // tableimages.cpp extern HRESULT uiprivLVN_GETDISPINFOImagesCheckboxes(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnParams *p); extern HRESULT uiprivNM_CUSTOMDRAWImagesCheckboxes(uiTable *t, NMLVCUSTOMDRAW *nm, uiprivSubitemDrawParams *dp); extern HRESULT uiprivTableSetupImagesCheckboxes(uiTable *t); + +// tabledraw.cpp +extern HRESULT uiprivTableHandleNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm, LRESULT *lResult); diff --git a/windows/tabledraw.cpp b/windows/tabledraw.cpp index 3036ac30..3afa4370 100644 --- a/windows/tabledraw.cpp +++ b/windows/tabledraw.cpp @@ -17,10 +17,10 @@ struct drawState { RECT itemBounds; RECT itemIcon; - RECT itemText; + RECT itemLabel; RECT subitemBounds; RECT subitemIcon; - RECT subitemText; + RECT subitemLabel; COLORREF bgColor; HBRUSH bgBrush; @@ -29,7 +29,7 @@ struct drawState { HBRUSH textBrush; BOOL freeTextBrush; - LRESULT bitmapMargins; + LRESULT bitmapMargin; int cxIcon; int cyIcon; @@ -41,7 +41,7 @@ static HRESULT computeAndDrawTextRect(struct drawState *s) { RECT r; - r = s->subitemText; + r = s->subitemLabel; if (!s->hasText && !s->hasImage) r = s->subitemBounds; @@ -104,11 +104,11 @@ static HRESULT drawTextPart(struct drawState *s) uiprivFree(wstr); // TODO decide once and for all what to compare to here and with SelectObject() - if (SetBkMode(nm->nmcd.hdc, prevMode) != TRANSPARENT) { + if (SetBkMode(s->dc, prevMode) != TRANSPARENT) { logLastError(L"SetBkMode() prev"); return E_FAIL; } - if (SetTextColor(nm->nmcd.hdc, prevText) != color) { + if (SetTextColor(s->dc, prevText) != s->textColor) { logLastError(L"SetTextColor() prev"); return E_FAIL; } @@ -120,21 +120,21 @@ static HRESULT freeDrawState(struct drawState *s) HRESULT hr, hrret; hrret = S_OK; - if (p->freeTextBrush) { - if (DeleteObject(p->textBrush) == 0) { + if (s->freeTextBrush) { + if (DeleteObject(s->textBrush) == 0) { logLastError(L"DeleteObject()"); hrret = E_FAIL; // continue cleaning up anyway } - p->freeTextBrush = NO; + s->freeTextBrush = FALSE; } - if (p->freeBgBrush) { - if (DeleteObject(p->bgBrush) == 0) { + if (s->freeBgBrush) { + if (DeleteObject(s->bgBrush) == 0) { logLastError(L"DeleteObject()"); hrret = E_FAIL; // continue cleaning up anyway } - p->freeBgBrush = NO; + s->freeBgBrush = FALSE; } return hrret; } @@ -153,6 +153,22 @@ static HRESULT itemRect(HRESULT hr, uiTable *t, UINT uMsg, WPARAM wParam, LONG l return S_OK; } +static COLORREF blend(COLORREF base, double r, double g, double b, double a) +{ + double br, bg, bb; + + br = ((double) GetRValue(base)) / 255.0; + bg = ((double) GetGValue(base)) / 255.0; + bb = ((double) GetBValue(base)) / 255.0; + + br = (r * a) + (br * (1.0 - a)); + bg = (g * a) + (bg * (1.0 - a)); + bb = (b * a) + (bb * (1.0 - a)); + return RGB((BYTE) (br * 255), + (BYTE) (bg * 255), + (BYTE) (bb * 255)); +} + static HRESULT fillDrawState(struct drawState *s, uiTable *t, NMLVCUSTOMDRAW *nm, uiprivTableColumnParams *p) { LRESULT state; @@ -172,19 +188,20 @@ static HRESULT fillDrawState(struct drawState *s, uiTable *t, NMLVCUSTOMDRAW *nm // nm->nmcd.uItemState CDIS_SELECTED is unreliable for the // listview configuration we have, so we must do this. state = SendMessageW(t->hwnd, LVM_GETITEMSTATE, nm->nmcd.dwItemSpec, LVIS_SELECTED); - dp->selected = (state & LVIS_SELECTED) != 0; - dp->focused = (nm->nmcd.uiTemState & CDIS_FOCUSED) != 0; + s->selected = (state & LVIS_SELECTED) != 0; + s->focused = (nm->nmcd.uItemState & CDIS_FOCUS) != 0; - hr = itemRect(S_OK, t, LVM_GETITEMRECT, LVIR_BOUNDS, - 0, &(s->itemBounds)); - hr = itemRect(hr, t, LVM_GETITEMRECT, LVIR_ICON, - 0, &(s->itemIcon)); - hr = itemRect(hr, t, LVM_GETITEMRECT, LVIR_LABEL, - 0, &(s->itemLabel)); - hr = itemRect(hr, t, LVM_GETSUBITEMRECT, LVIR_BOUNDS, - s->iSubItem, &(s->subitemBounds)); - hr = itemRect(hr, t, LVM_GETSUBITEMRECT, LVIR_ICON, - s->iSubItem, &(s->subitemIcon)); + // TODO check LRESULT bad parameters here + hr = itemRect(S_OK, t, LVM_GETITEMRECT, s->iItem, + LVIR_BOUNDS, 0, FALSE, &(s->itemBounds)); + hr = itemRect(hr, t, LVM_GETITEMRECT, s->iItem, + LVIR_ICON, 0, FALSE, &(s->itemIcon)); + hr = itemRect(hr, t, LVM_GETITEMRECT, s->iItem, + LVIR_LABEL, 0, FALSE, &(s->itemLabel)); + hr = itemRect(hr, t, LVM_GETSUBITEMRECT, s->iItem, + LVIR_BOUNDS, s->iSubItem, 0, &(s->subitemBounds)); + hr = itemRect(hr, t, LVM_GETSUBITEMRECT, s->iItem, + LVIR_ICON, s->iSubItem, 0, &(s->subitemIcon)); if (hr != S_OK) goto fail; // LVM_GETSUBITEMRECT treats LVIR_LABEL as the same as @@ -194,6 +211,12 @@ static HRESULT fillDrawState(struct drawState *s, uiTable *t, NMLVCUSTOMDRAW *nm // above. s->subitemLabel = s->subitemBounds; s->subitemLabel.left = s->subitemIcon.right; + // And on iSubItem == 0, LVIF_GETSUBITEMRECT still includes + // all the subitems, which we don't want. + if (s->iSubItem == 0) { + s->subitemBounds.right = s->itemLabel.right; + s->subitemLabel.right = s->itemLabel.right; + } if (s->selected) { s->bgColor = GetSysColor(COLOR_HIGHLIGHT); @@ -202,6 +225,7 @@ static HRESULT fillDrawState(struct drawState *s, uiTable *t, NMLVCUSTOMDRAW *nm s->textBrush = GetSysColorBrush(COLOR_HIGHLIGHTTEXT); } else { uiTableData *data; + double r, g, b, a; s->bgColor = GetSysColor(COLOR_WINDOW); s->bgBrush = GetSysColorBrush(COLOR_WINDOW); @@ -211,7 +235,7 @@ static HRESULT fillDrawState(struct drawState *s, uiTable *t, NMLVCUSTOMDRAW *nm uiTableDataColor(data, &r, &g, &b, &a); uiFreeTableData(data); s->bgColor = blend(s->bgColor, r, g, b, a); - s->bgBrush = CreateSolidBrush(s->bgBrush); + s->bgBrush = CreateSolidBrush(s->bgColor); if (s->bgBrush == NULL) { logLastError(L"CreateSolidBrush()"); hr = E_FAIL; @@ -249,3 +273,44 @@ fail: freeDrawState(s); return hr; } + +HRESULT uiprivTableHandleNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm, LRESULT *lResult) +{ + struct drawState s; + uiprivTableColumnParams *p; + HRESULT hr; + + switch (nm->nmcd.dwDrawStage) { + case CDDS_PREPAINT: + *lResult = CDRF_NOTIFYITEMDRAW; + return S_OK; + case CDDS_ITEMPREPAINT: + *lResult = CDRF_NOTIFYSUBITEMDRAW; + return S_OK; + case CDDS_SUBITEM | CDDS_ITEMPREPAINT: + break; + default: + *lResult = CDRF_DODEFAULT; + return S_OK; + } + + p = (*(t->columns))[nm->iSubItem]; + hr = fillDrawState(&s, t, nm, p); + if (hr != S_OK) + return hr; + hr = computeAndDrawTextRect(&s); + if (hr != S_OK) + goto fail; + hr = drawTextPart(&s); + if (hr != S_OK) + goto fail; + hr = freeDrawState(&s); + if (hr != S_OK) // TODO really error out here? + return hr; + *lResult = CDRF_SKIPDEFAULT; + return S_OK; +fail: + // ignore error here + freeDrawState(&s); + return hr; +} From 32ee36eb22a05acdedd65aa8bf94fad9e381e7a1 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 15 Jun 2018 19:29:01 -0400 Subject: [PATCH 1195/1329] Fixed some of the technical glitches. --- windows/tabledraw.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/windows/tabledraw.cpp b/windows/tabledraw.cpp index 3afa4370..3344f8a2 100644 --- a/windows/tabledraw.cpp +++ b/windows/tabledraw.cpp @@ -44,6 +44,11 @@ static HRESULT computeAndDrawTextRect(struct drawState *s) r = s->subitemLabel; if (!s->hasText && !s->hasImage) r = s->subitemBounds; + else if (!s->hasImage && s->iSubItem != 0) + // By default, this will include images; we're not drawing + // images, so we will manually draw over the image area. + // There's a second part to this; see below. + r.left = s->subitemBounds.left; if (FillRect(s->dc, &r, s->bgBrush) == 0) { logLastError(L"FillRect()"); From d6cebf4ca02c90a5eaf0bf9d137404fc49b0605f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 15 Jun 2018 22:28:37 -0400 Subject: [PATCH 1196/1329] Added scaling to uiImage on Windows. We'll need this for what we're about to do. --- windows/image.cpp | 67 ++++++++++++++++++++++++++++++-------- windows/uipriv_windows.hpp | 2 +- 2 files changed, 54 insertions(+), 15 deletions(-) diff --git a/windows/image.cpp b/windows/image.cpp index a85207f5..3beedb0e 100644 --- a/windows/image.cpp +++ b/windows/image.cpp @@ -120,10 +120,12 @@ IWICBitmap *uiprivImageAppropriateForDC(uiImage *i, HDC dc) return m.best; } -// TODO see if we can really pass NULL to CreateDIBSection()'s HDC parameter, and if so, use HBITMAPs before WIC maybe? -// TODO this needs to actually scale down to fit if the image size isn't perfectly equal to a requested size I need to pass as a parameter here... -HBITMAP uiprivWICToGDI(IWICBitmap *b, HDC dc) +// TODO this needs to center images if the given size is not the same aspect ratio +HRESULT uiprivWICToGDI(IWICBitmap *b, HDC dc, int width, int height, HBITMAP *hb) { + UINT ux, uy; + int x, y; + IWICImageSource *src; BITMAPINFO bmi; UINT width, height; HBITMAP hb; @@ -131,29 +133,66 @@ HBITMAP uiprivWICToGDI(IWICBitmap *b, HDC dc) BITMAP bmp; HRESULT hr; + hr = b->GetSize(&ux, &uy); + if (hr != S_OK) + return hr; + x = ux; + y = uy; + if (width == 0) + width = x; + if (height == 0) + height = y; + + // special case: don't invoke a scaler if the size is the same + if (width == x && height == y) { + b->AddRef(); // for the Release() later + src = b; + } else { + IWICBitmapScaler *scaler; + + hr = uiprivWICFactory->CreateBitmapScaler(&scaler); + if (hr != S_OK) + return hr; + hr = scaler->Initialize(b, width, height, + // according to https://stackoverflow.com/questions/4250738/is-stretchblt-halftone-bilinear-for-all-scaling, this is what StretchBlt(COLORONCOLOR) does (with COLORONCOLOR being what's supported by AlphaBlend()) + WICBitmapInterpolationModeNearestNeighbor); + if (hr != S_OK) { + scaler->Release(); + return hr; + } + src = scaler; + } + ZeroMemory(&bmi, sizeof (BITMAPINFO)); bmi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER); - hr = b->GetSize(&width, &height); - if (hr != S_OK) - logHRESULT(L"error calling GetSize() in uiprivWICToGDI()", hr); bmi.bmiHeader.biWidth = width; bmi.bmiHeader.biHeight = -((int) height); bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = 32; bmi.bmiHeader.biCompression = BI_RGB; - hb = CreateDIBSection(dc, &bmi, DIB_RGB_COLORS, + *hb = CreateDIBSection(dc, &bmi, DIB_RGB_COLORS, &bits, NULL, 0); - if (hb == NULL) - logLastError(L"error calling CreateDIBSection() in uiprivWICToGDI()"); + if (*hb == NULL) { + logLastError(L"CreateDIBSection()"); + hr = E_FAIL; + goto fail; + } // now we need to figure out the stride of the image data GDI gave us - // TODO find out if CreateDIBSection fills that in bmi for us - if (GetObject(hb, sizeof (BITMAP), &bmp) == 0) + // TODO find out if CreateDIBSection() fills that in bmi for us + // TODO fill in the error returns here too + if (GetObject(*hb, sizeof (BITMAP), &bmp) == 0) logLastError(L"error calling GetObject() in uiprivWICToGDI()"); hr = b->CopyPixels(NULL, bmp.bmWidthBytes, bmp.bmWidthBytes * bmp.bmHeight, (BYTE *) bits); - if (hr != S_OK) - logHRESULT(L"error calling CopyPixels() in uiprivWICToGDI()", hr); - return hb; + hr = S_OK; +fail: + if (*hb != NULL && hr != S_OK) { + // don't bother with the error returned here + DeleteObject(*hb); + *hb = NULL; + } + src->Release(); + return hr; } diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index aab50aa7..a1c2bc29 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -169,4 +169,4 @@ extern IWICImagingFactory *uiprivWICFactory; extern HRESULT uiprivInitImage(void); extern void uiprivUninitImage(void); extern IWICBitmap *uiprivImageAppropriateForDC(uiImage *i, HDC dc); -extern HBITMAP uiprivWICToGDI(IWICBitmap *b, HDC dc); +extern HRESULT uiprivWICToGDI(IWICBitmap *b, HDC dc, int width, int height, HBITMAP *hb); From 43bb983f5bb95db98a1c6bc90124b2b2b722bafe Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 15 Jun 2018 22:50:19 -0400 Subject: [PATCH 1197/1329] Wrote new (incomplete) image drawing code. Now to build and test. --- windows/table.cpp | 2 +- windows/table.hpp | 2 +- windows/tabledraw.cpp | 129 ++++++++++++++++++++++++++++++++++++++-- windows/tableimages.cpp | 105 ++------------------------------ 4 files changed, 131 insertions(+), 107 deletions(-) diff --git a/windows/table.cpp b/windows/table.cpp index a766010f..e1eb011d 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -290,7 +290,7 @@ uiTable *uiNewTable(uiTableModel *model) t->backgroundColumn = -1; - hr = uiprivTableSetupImagesCheckboxes(t); + hr = uiprivUpdateImageListSize(t); if (hr != S_OK) { // TODO } diff --git a/windows/table.hpp b/windows/table.hpp index 3ceeb977..056cb884 100644 --- a/windows/table.hpp +++ b/windows/table.hpp @@ -53,7 +53,7 @@ extern HRESULT uiprivLVN_GETDISPINFOText(uiTable *t, NMLVDISPINFOW *nm, uiprivTa // tableimages.cpp extern HRESULT uiprivLVN_GETDISPINFOImagesCheckboxes(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnParams *p); extern HRESULT uiprivNM_CUSTOMDRAWImagesCheckboxes(uiTable *t, NMLVCUSTOMDRAW *nm, uiprivSubitemDrawParams *dp); -extern HRESULT uiprivTableSetupImagesCheckboxes(uiTable *t); // tabledraw.cpp extern HRESULT uiprivTableHandleNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm, LRESULT *lResult); +extern HRESULT uiprivUpdateImageListSize(uiTable *t); diff --git a/windows/tabledraw.cpp b/windows/tabledraw.cpp index 3344f8a2..cb4a9fce 100644 --- a/windows/tabledraw.cpp +++ b/windows/tabledraw.cpp @@ -37,7 +37,7 @@ struct drawState { RECT focusRect; }; -static HRESULT computeAndDrawTextRect(struct drawState *s) +static HRESULT computeOtherRectsAndDrawBackgrounds(struct drawState *s) { RECT r; @@ -50,6 +50,11 @@ static HRESULT computeAndDrawTextRect(struct drawState *s) // There's a second part to this; see below. r.left = s->subitemBounds.left; + if (s->hasImage) + if (FillRect(s->dc, &(s->subitemIcon), GetSysColorBrush(COLOR_WINDOW)) == 0) { + logLastError(L"FillRect() icon"); + return E_FAIL; + } if (FillRect(s->dc, &r, s->bgBrush) == 0) { logLastError(L"FillRect()"); return E_FAIL; @@ -72,6 +77,52 @@ static HRESULT computeAndDrawTextRect(struct drawState *s) return S_OK; } +static HRESULT drawImagePart(struct drawState *s) +{ + uiTableData *data; + IWICBitmap *wb; + HBITMAP b; + UINT fStyle; + HRESULT hr; + + if (s->p->imageModelColumn == -1) + return S_OK; + + data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->item.iItem, p->imageModelColumn); + wb = uiprivImageAppropriateForDC(uiTableDataImage(data), s->dc); + uiFreeTableData(data); + + hr = uiprivWICToGDI(wb, s->cxIcon, s->cyIcon, &b); + if (hr != S_OK) + return hr; + // TODO rewrite this condition to make more sense; possibly swap the if and else blocks too + // TODO proper cleanup + if (ImageList_GetImageCount(t->imagelist) > 1) { + if (ImageList_Replace(t->imagelist, 0, b, NULL) == 0) { + logLastError(L"ImageList_Replace()"); + return E_FAIL; + } + } else + if (ImageList_Add(t->imagelist, b, NULL) == -1) { + logLastError(L"ImageList_Add()"); + return E_FAIL; + } + // TODO error check + DeleteObject(b); + + fStyle = ILD_NORMAL; + if (s->selected) + fStyle = ILD_SELECTED; + // TODO copy the centering code from tableimage.cpp + if (ImageList_Draw(t->imagelist, 0, + s->dc, s->subitemIcon.left, s->subitemIcon.top, + fStyle) == 0) { + logLastError(L"ImageList_Draw()"); + return E_FAIL; + } + return S_OK; +} + static HRESULT drawTextPart(struct drawState *s) { COLORREF prevText; @@ -212,8 +263,8 @@ static HRESULT fillDrawState(struct drawState *s, uiTable *t, NMLVCUSTOMDRAW *nm // LVM_GETSUBITEMRECT treats LVIR_LABEL as the same as // LVIR_BOUNDS, so we can't use that directly. Instead, let's // assume the text is immediately after the icon. The correct - // rect will be determined by computeAndDrawTextRect() - // above. + // rect will be determined by + // computeOtherRectsAndDrawBackgrounds() above. s->subitemLabel = s->subitemBounds; s->subitemLabel.left = s->subitemIcon.right; // And on iSubItem == 0, LVIF_GETSUBITEMRECT still includes @@ -270,7 +321,11 @@ static HRESULT fillDrawState(struct drawState *s, uiTable *t, NMLVCUSTOMDRAW *nm header = (HWND) SendMessageW(t->hwnd, LVM_GETHEADER, 0, 0); s->bitmapMargin = SendMessageW(header, HDM_GETBITMAPMARGIN, 0, 0); - // TODO + if (ImageList_GetIconSize(t->imagelist, &(s->cxIcon), &(s->cyIcon)) == 0) { + logLastError(L"ImageList_GetIconSize()"); + hr = E_FAIL; + goto fail; + } return S_OK; fail: @@ -303,7 +358,10 @@ HRESULT uiprivTableHandleNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm, LRESULT * hr = fillDrawState(&s, t, nm, p); if (hr != S_OK) return hr; - hr = computeAndDrawTextRect(&s); + hr = computeOtherRectsAndDrawBackgrounds(&s); + if (hr != S_OK) + goto fail; + hr = drawImagePart(&s); if (hr != S_OK) goto fail; hr = drawTextPart(&s); @@ -319,3 +377,64 @@ fail: freeDrawState(&s); return hr; } + +// TODO run again when the DPI or the theme changes +// TODO properly clean things up here +// TODO properly destroy the old lists here too +HRESULT uiprivUpdateImageListSize(uiTable *t) +{ + HDC dc; + int cxList, cyList; + HTHEME theme; + SIZE sizeCheck; + HRESULT hr; + + dc = GetDC(t->hwnd); + if (dc == NULL) { + logLastError(L"GetDC()"); + return E_FAIL; + } + + cxList = GetSystemMetrics(SM_CXSMICON); + cyList = GetSystemMetrics(SM_CYSMICON); + sizeCheck.cx = cxList; + sizeCheck.cy = cyList; + theme = OpenThemeData(t->hwnd, L"button"); + if (theme != NULL) { + hr = GetThemePartSize(theme, dc, + BP_CHECKBOX, CBS_UNCHECKEDNORMAL, + NULL, TS_DRAW, &sizeCheck); + if (hr != S_OK) { + logHRESULT(L"GetThemePartSize()", hr); + return hr; // TODO fall back? + } + // make sure these checkmarks fit + // unthemed checkmarks will by the code above be smaller than cxList/cyList here + if (cxList < sizeCheck.cx) + cxList = sizeCheck.cx; + if (cyList < sizeCheck.cy) + cyList = sizeCheck.cy; + } + + // TODO handle errors + t->imagelist = ImageList_Create(cxList, cyList, + ILC_COLOR32, + 1, 1); + if (t->smallImages == NULL) { + logLastError(L"ImageList_Create()"); + return E_FAIL; + } + // TODO will this return NULL here because it's an initial state? + SendMessageW(t->hwnd, LVM_SETIMAGELIST, LVSIL_SMALL, (LPARAM) (t->smallImages)); + + hr = CloseThemeData(theme); + if (hr != S_OK) { + logHRESULT(L"CloseThemeData()", hr); + return hr; + } + if (ReleaseDC(t->hwnd, dc) == 0) { + logLastError(L"ReleaseDC()"); + return E_FAIL; + } + return S_OK; +} diff --git a/windows/tableimages.cpp b/windows/tableimages.cpp index 69a23bb5..fb97b596 100644 --- a/windows/tableimages.cpp +++ b/windows/tableimages.cpp @@ -21,44 +21,7 @@ We'll use the small image list. For this, the first few items will be reserved f #define nCheckboxImages 4 static HRESULT setCellImage(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnParams *p, uiTableData *data) -{ - int index; - HDC dc; - IWICBitmap *wb; - HBITMAP b; - - index = t->smallIndex + nCheckboxImages; - t->smallIndex++; - t->smallIndex %= uiprivNumLVN_GETDISPINFOSkip; - nm->item.iImage = index; - - dc = GetDC(t->hwnd); - if (dc == NULL) { - logLastError(L"GetDC()"); - return E_FAIL; - } - - wb = uiprivImageAppropriateForDC(uiTableDataImage(data), dc); - b = uiprivWICToGDI(wb, dc); - // TODO rewrite this condition to make more sense; possibly swap the if and else blocks too - if (ImageList_GetImageCount(t->smallImages) > index) { - if (ImageList_Replace(t->smallImages, index, b, NULL) == 0) { - logLastError(L"ImageList_Replace()"); - return E_FAIL; - } - } else - if (ImageList_Add(t->smallImages, b, NULL) == -1) { - logLastError(L"ImageList_Add()"); - return E_FAIL; - } - - if (ReleaseDC(t->hwnd, dc) == 0) { - logLastError(L"ReleaseDC()"); - return E_FAIL; - } - - return S_OK; -} +{return E_NOTIMPL;/*TODO*/} #define stateUnchecked 0 #define stateChecked 1 @@ -105,6 +68,10 @@ HRESULT uiprivLVN_GETDISPINFOImagesCheckboxes(uiTable *t, NMLVDISPINFOW *nm, uip if ((nm->item.mask & LVIF_IMAGE) == 0) return S_OK; // nothing to do here + // TODO + nm->item.iImage = 0; + return S_OK; + if (p->imageModelColumn != -1) { data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->item.iItem, p->imageModelColumn); hr = setCellImage(t, nm, p, data); @@ -297,65 +264,3 @@ static HRESULT mkCheckboxes(uiTable *t, HTHEME theme, HDC dc, int cxList, int cy DeleteObject(b); return S_OK; } - -// TODO run again when the DPI changes -HRESULT uiprivTableSetupImagesCheckboxes(uiTable *t) -{ - HDC dc; - int cxList, cyList; - HTHEME theme; - SIZE sizeCheck; - HRESULT hr; - - dc = GetDC(t->hwnd); - if (dc == NULL) { - logLastError(L"GetDC()"); - return E_FAIL; - } - - cxList = GetSystemMetrics(SM_CXSMICON); - cyList = GetSystemMetrics(SM_CYSMICON); - sizeCheck.cx = cxList; - sizeCheck.cy = cyList; - theme = OpenThemeData(t->hwnd, L"button"); - if (theme != NULL) { - hr = GetThemePartSize(theme, dc, - BP_CHECKBOX, CBS_UNCHECKEDNORMAL, - NULL, TS_DRAW, &sizeCheck); - if (hr != S_OK) { - logHRESULT(L"GetThemePartSize()", hr); - return hr; // TODO fall back? - } - // make sure these checkmarks fit - // unthemed checkmarks will by the code above be smaller than cxList/cyList here - if (cxList < sizeCheck.cx) - cxList = sizeCheck.cx; - if (cyList < sizeCheck.cy) - cyList = sizeCheck.cy; - } - - // TODO handle errors - t->smallImages = ImageList_Create(cxList, cyList, - ILC_COLOR32, - nCheckboxImages + uiprivNumLVN_GETDISPINFOSkip, nCheckboxImages + uiprivNumLVN_GETDISPINFOSkip); - if (t->smallImages == NULL) { - logLastError(L"ImageList_Create()"); - return E_FAIL; - } - // TODO will this return NULL here because it's an initial state? - SendMessageW(t->hwnd, LVM_SETIMAGELIST, LVSIL_SMALL, (LPARAM) (t->smallImages)); - hr = mkCheckboxes(t, theme, dc, cxList, cyList, sizeCheck.cx, sizeCheck.cy); - if (hr != S_OK) - return hr; - - hr = CloseThemeData(theme); - if (hr != S_OK) { - logHRESULT(L"CloseThemeData()", hr); - return hr; - } - if (ReleaseDC(t->hwnd, dc) == 0) { - logLastError(L"ReleaseDC()"); - return E_FAIL; - } - return S_OK; -} From f852359acbee65e49427632d2eb94a09e50e55e6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 15 Jun 2018 23:00:39 -0400 Subject: [PATCH 1198/1329] Fixed build errors. The image list selection part works, at least!! Let's figure out why nothing else does. --- windows/image.cpp | 4 +--- windows/table.hpp | 10 ++-------- windows/tabledraw.cpp | 16 ++++++++-------- windows/tableimages.cpp | 8 +++++++- 4 files changed, 18 insertions(+), 20 deletions(-) diff --git a/windows/image.cpp b/windows/image.cpp index 3beedb0e..d94de789 100644 --- a/windows/image.cpp +++ b/windows/image.cpp @@ -125,10 +125,8 @@ HRESULT uiprivWICToGDI(IWICBitmap *b, HDC dc, int width, int height, HBITMAP *hb { UINT ux, uy; int x, y; - IWICImageSource *src; + IWICBitmapSource *src; BITMAPINFO bmi; - UINT width, height; - HBITMAP hb; VOID *bits; BITMAP bmp; HRESULT hr; diff --git a/windows/table.hpp b/windows/table.hpp index 056cb884..4e33d70c 100644 --- a/windows/table.hpp +++ b/windows/table.hpp @@ -29,14 +29,8 @@ struct uiTable { std::vector *columns; WPARAM nColumns; int backgroundColumn; - - // tableimages.cpp - // TODO make sure what we're doing is even allowed - HIMAGELIST smallImages; - int smallIndex; - - // custom draw state - COLORREF clrItemText; + // TODO make sure replacing images while selected in the listview is even allowed + HIMAGELIST imagelist; }; typedef struct uiprivSubitemDrawParams uiprivSubitemDrawParams; struct uiprivSubitemDrawParams { diff --git a/windows/tabledraw.cpp b/windows/tabledraw.cpp index cb4a9fce..62cd3123 100644 --- a/windows/tabledraw.cpp +++ b/windows/tabledraw.cpp @@ -88,22 +88,22 @@ static HRESULT drawImagePart(struct drawState *s) if (s->p->imageModelColumn == -1) return S_OK; - data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->item.iItem, p->imageModelColumn); + data = (*(s->m->mh->CellValue))(s->m->mh, s->m, s->iItem, s->p->imageModelColumn); wb = uiprivImageAppropriateForDC(uiTableDataImage(data), s->dc); uiFreeTableData(data); - hr = uiprivWICToGDI(wb, s->cxIcon, s->cyIcon, &b); + hr = uiprivWICToGDI(wb, s->dc, s->cxIcon, s->cyIcon, &b); if (hr != S_OK) return hr; // TODO rewrite this condition to make more sense; possibly swap the if and else blocks too // TODO proper cleanup - if (ImageList_GetImageCount(t->imagelist) > 1) { - if (ImageList_Replace(t->imagelist, 0, b, NULL) == 0) { + if (ImageList_GetImageCount(s->t->imagelist) > 1) { + if (ImageList_Replace(s->t->imagelist, 0, b, NULL) == 0) { logLastError(L"ImageList_Replace()"); return E_FAIL; } } else - if (ImageList_Add(t->imagelist, b, NULL) == -1) { + if (ImageList_Add(s->t->imagelist, b, NULL) == -1) { logLastError(L"ImageList_Add()"); return E_FAIL; } @@ -114,7 +114,7 @@ static HRESULT drawImagePart(struct drawState *s) if (s->selected) fStyle = ILD_SELECTED; // TODO copy the centering code from tableimage.cpp - if (ImageList_Draw(t->imagelist, 0, + if (ImageList_Draw(s->t->imagelist, 0, s->dc, s->subitemIcon.left, s->subitemIcon.top, fStyle) == 0) { logLastError(L"ImageList_Draw()"); @@ -420,12 +420,12 @@ HRESULT uiprivUpdateImageListSize(uiTable *t) t->imagelist = ImageList_Create(cxList, cyList, ILC_COLOR32, 1, 1); - if (t->smallImages == NULL) { + if (t->imagelist == NULL) { logLastError(L"ImageList_Create()"); return E_FAIL; } // TODO will this return NULL here because it's an initial state? - SendMessageW(t->hwnd, LVM_SETIMAGELIST, LVSIL_SMALL, (LPARAM) (t->smallImages)); + SendMessageW(t->hwnd, LVM_SETIMAGELIST, LVSIL_SMALL, (LPARAM) (t->imagelist)); hr = CloseThemeData(theme); if (hr != S_OK) { diff --git a/windows/tableimages.cpp b/windows/tableimages.cpp index fb97b596..b7fed3bd 100644 --- a/windows/tableimages.cpp +++ b/windows/tableimages.cpp @@ -69,7 +69,9 @@ HRESULT uiprivLVN_GETDISPINFOImagesCheckboxes(uiTable *t, NMLVDISPINFOW *nm, uip return S_OK; // nothing to do here // TODO - nm->item.iImage = 0; + nm->item.iImage = -1; + if (p->imageModelColumn != -1 || p->checkboxModelColumn != -1) + nm->item.iImage = 0; return S_OK; if (p->imageModelColumn != -1) { @@ -100,6 +102,8 @@ HRESULT uiprivLVN_GETDISPINFOImagesCheckboxes(uiTable *t, NMLVDISPINFOW *nm, uip return S_OK; } +#if 0 + // in order to properly look like checkboxes, we need to exclude them from being colored in by the selection rect // however, there seems to be no way to do this natively, so we have to draw the icons ourselves // see also https://www.codeproject.com/Articles/79/Neat-Stuff-to-Do-in-List-Controls-Using-Custom-Dra (and while this uses postpaint to draw over the existing icon, we are drawing everything ourselves, so we only draw once) @@ -264,3 +268,5 @@ static HRESULT mkCheckboxes(uiTable *t, HTHEME theme, HDC dc, int cxList, int cy DeleteObject(b); return S_OK; } + +#endif From f1341a04856b81edf8c9a2b2e717c3916e28433b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 16 Jun 2018 04:26:36 -0400 Subject: [PATCH 1199/1329] Fixed image drawing. Still need to figure out why it's using the wrong size... --- windows/image.cpp | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/windows/image.cpp b/windows/image.cpp index d94de789..2997b092 100644 --- a/windows/image.cpp +++ b/windows/image.cpp @@ -147,6 +147,8 @@ HRESULT uiprivWICToGDI(IWICBitmap *b, HDC dc, int width, int height, HBITMAP *hb src = b; } else { IWICBitmapScaler *scaler; + WICPixelFormatGUID guid; + IWICFormatConverter *conv; hr = uiprivWICFactory->CreateBitmapScaler(&scaler); if (hr != S_OK) @@ -158,7 +160,35 @@ HRESULT uiprivWICToGDI(IWICBitmap *b, HDC dc, int width, int height, HBITMAP *hb scaler->Release(); return hr; } - src = scaler; + + // But we are not done yet! IWICBitmapScaler can use an + // entirely different pixel format than what we gave it, + // and by extension, what GDI wants. See also: + // - https://stackoverflow.com/questions/28323228/iwicbitmapscaler-doesnt-work-for-96bpprgbfloat-format + // - https://github.com/Microsoft/DirectXTex/blob/0d94e9469bc3e6080a71145f35efa559f8f2e522/DirectXTex/DirectXTexResize.cpp#L83 + hr = scaler->GetPixelFormat(&guid); + if (hr != S_OK) { + scaler->Release(); + return hr; + } + if (IsEqualGUID(guid, GUID_WICPixelFormat32bppRGBA)) + src = scaler; + else { + hr = uiprivWICFactory->CreateFormatConverter(&conv); + if (hr != S_OK) { + scaler->Release(); + return hr; + } + hr = conv->Initialize(scaler, GUID_WICPixelFormat32bppRGBA, + // TODO is the dither type correct in all cases? + WICBitmapDitherTypeNone, NULL, 0, WICBitmapPaletteTypeMedianCut); + scaler->Release(); + if (hr != S_OK) { + conv->Release(); + return hr; + } + src = conv; + } } ZeroMemory(&bmi, sizeof (BITMAPINFO)); @@ -181,10 +211,9 @@ HRESULT uiprivWICToGDI(IWICBitmap *b, HDC dc, int width, int height, HBITMAP *hb // TODO fill in the error returns here too if (GetObject(*hb, sizeof (BITMAP), &bmp) == 0) logLastError(L"error calling GetObject() in uiprivWICToGDI()"); - hr = b->CopyPixels(NULL, bmp.bmWidthBytes, + hr = src->CopyPixels(NULL, bmp.bmWidthBytes, bmp.bmWidthBytes * bmp.bmHeight, (BYTE *) bits); - hr = S_OK; fail: if (*hb != NULL && hr != S_OK) { // don't bother with the error returned here From f92c83992e61e69d29c2bd58eb8cc65ebe5a1e54 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 16 Jun 2018 08:52:55 -0400 Subject: [PATCH 1200/1329] Fixed image matching. --- windows/image.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/windows/image.cpp b/windows/image.cpp index 2997b092..0058aa13 100644 --- a/windows/image.cpp +++ b/windows/image.cpp @@ -112,8 +112,9 @@ IWICBitmap *uiprivImageAppropriateForDC(uiImage *i, HDC dc) m.best = NULL; m.distX = INT_MAX; m.distY = INT_MAX; - m.targetX = i->width * GetDeviceCaps(dc, LOGPIXELSX); - m.targetY = i->height * GetDeviceCaps(dc, LOGPIXELSY); + // TODO explain this + m.targetX = MulDiv(i->width, GetDeviceCaps(dc, LOGPIXELSX), 96); + m.targetY = MulDiv(i->height, GetDeviceCaps(dc, LOGPIXELSY), 96); m.foundLarger = false; for (IWICBitmap *b : *(i->bitmaps)) match(b, &m); From 4bfd950caa2265feb682b96d764dc89ce30b9629 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 16 Jun 2018 10:22:41 -0400 Subject: [PATCH 1201/1329] Centered the table image. --- windows/tabledraw.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/windows/tabledraw.cpp b/windows/tabledraw.cpp index 62cd3123..6887bb54 100644 --- a/windows/tabledraw.cpp +++ b/windows/tabledraw.cpp @@ -77,11 +77,21 @@ static HRESULT computeOtherRectsAndDrawBackgrounds(struct drawState *s) return S_OK; } +static void centerImageRect(RECT *image, RECT *space) +{ + LONG yoff; + + yoff = ((space->bottom - space->top) - (image->bottom - image->top)) / 2; + image->top += yoff; + image->bottom += yoff; +} + static HRESULT drawImagePart(struct drawState *s) { uiTableData *data; IWICBitmap *wb; HBITMAP b; + RECT r; UINT fStyle; HRESULT hr; @@ -110,13 +120,15 @@ static HRESULT drawImagePart(struct drawState *s) // TODO error check DeleteObject(b); + r = s->subitemIcon; + r.right = r.left + s->cxIcon; + r.bottom = r.top + s->cyIcon; + centerImageRect(&r, &(s->subitemIcon)); fStyle = ILD_NORMAL; if (s->selected) fStyle = ILD_SELECTED; - // TODO copy the centering code from tableimage.cpp if (ImageList_Draw(s->t->imagelist, 0, - s->dc, s->subitemIcon.left, s->subitemIcon.top, - fStyle) == 0) { + s->dc, r.left, r.top, fStyle) == 0) { logLastError(L"ImageList_Draw()"); return E_FAIL; } From b9289c93a6dd7ec3aade7641adafc02ce1d515e6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 16 Jun 2018 11:59:17 -0400 Subject: [PATCH 1202/1329] And drew checkboxes. We can FINALLY move on to other data types! ...almost. First we have to consolidate LVN_GETDISPINFO handlers. --- windows/tabledraw.cpp | 142 +++++++++++++++++++++++++-- windows/tableimages.cpp | 208 +--------------------------------------- 2 files changed, 138 insertions(+), 212 deletions(-) diff --git a/windows/tabledraw.cpp b/windows/tabledraw.cpp index 6887bb54..ad8b4b46 100644 --- a/windows/tabledraw.cpp +++ b/windows/tabledraw.cpp @@ -79,10 +79,22 @@ static HRESULT computeOtherRectsAndDrawBackgrounds(struct drawState *s) static void centerImageRect(RECT *image, RECT *space) { - LONG yoff; + LONG xoff, yoff; + // first make sure both have the same upper-left + xoff = image->left - space->left; + yoff = image->top - space->top; + image->left -= xoff; + image->top -= yoff; + image->right -= xoff; + image->bottom -= yoff; + + // now center + xoff = ((space->right - space->left) - (image->right - image->left)) / 2; yoff = ((space->bottom - space->top) - (image->bottom - image->top)) / 2; + image->left += xoff; image->top += yoff; + image->right += xoff; image->bottom += yoff; } @@ -135,6 +147,121 @@ static HRESULT drawImagePart(struct drawState *s) return S_OK; } +// references for checkbox drawing: +// - https://blogs.msdn.microsoft.com/oldnewthing/20171129-00/?p=97485 +// - https://blogs.msdn.microsoft.com/oldnewthing/20171201-00/?p=97505 + +static HRESULT drawUnthemedCheckbox(struct drawState *s, int checked, int enabled) +{ + RECT r; + UINT state; + + r = s->subitemIcon; + // this is what the actual list view LVS_EX_CHECKBOXES code does to size the checkboxes + // TODO reverify the initial size + r.right = r.left + GetSystemMetrics(SM_CXSMICON); + r.bottom = r.top + GetSystemMetrics(SM_CYSMICON); + if (InflateRect(&r, -GetSystemMetrics(SM_CXEDGE), -GetSystemMetrics(SM_CYEDGE)) == 0) { + logLastError(L"InflateRect()"); + return E_FAIL; + } + r.right++; + r.bottom++; + + centerImageRect(&r, &(s->subitemIcon)); + state = DFCS_BUTTONCHECK | DFCS_FLAT; + if (checked) + state |= DFCS_CHECKED; + if (!enabled) + state |= DFCS_INACTIVE; + if (DrawFrameControl(s->dc, &r, DFC_BUTTON, state) == 0) { + logLastError(L"DrawFrameControl()"); + return E_FAIL; + } + return S_OK; +} + +static HRESULT drawThemedCheckbox(struct drawState *s, HTHEME theme, int checked, int enabled) +{ + RECT r; + SIZE size; + int state; + HRESULT hr; + + hr = GetThemePartSize(theme, s->dc, + BP_CHECKBOX, CBS_UNCHECKEDNORMAL, + NULL, TS_DRAW, &size); + if (hr != S_OK) { + logHRESULT(L"GetThemePartSize()", hr); + return hr; // TODO fall back? + } + r = s->subitemIcon; + r.right = r.left + size.cx; + r.bottom = r.top + size.cy; + + centerImageRect(&r, &(s->subitemIcon)); + if (!checked && enabled) + state = CBS_UNCHECKEDNORMAL; + else if (checked && enabled) + state = CBS_CHECKEDNORMAL; + else if (!checked && !enabled) + state = CBS_UNCHECKEDDISABLED; + else + state = CBS_CHECKEDDISABLED; + hr = DrawThemeBackground(theme, s->dc, + BP_CHECKBOX, state, + &r, NULL); + if (hr != S_OK) { + logHRESULT(L"DrawThemeBackground()", hr); + return hr; + } + return S_OK; +} + +static HRESULT drawCheckboxPart(struct drawState *s) +{ + uiTableData *data; + int checked, enabled; + HTHEME theme; + HRESULT hr; + + if (s->p->checkboxModelColumn == -1) + return S_OK; + + data = (*(s->m->mh->CellValue))(s->m->mh, s->m, s->iItem, s->p->checkboxModelColumn); + checked = uiTableDataInt(data); + uiFreeTableData(data); + switch (s->p->checkboxEditableColumn) { + case uiTableModelColumnNeverEditable: + enabled = 0; + break; + case uiTableModelColumnAlwaysEditable: + enabled = 1; + break; + default: + data = (*(s->m->mh->CellValue))(s->m->mh, s->m, s->iItem, s->p->checkboxEditableColumn); + enabled = uiTableDataInt(data); + uiFreeTableData(data); + } + + theme = OpenThemeData(s->t->hwnd, L"button"); + if (theme != NULL) { + hr = drawThemedCheckbox(s, theme, checked, enabled); + if (hr != S_OK) + return hr; + hr = CloseThemeData(theme); + if (hr != S_OK) { + logHRESULT(L"CloseThemeData()", hr); + return hr; + } + } else { + hr = drawUnthemedCheckbox(s, checked, enabled); + if (hr != S_OK) + return hr; + } + return S_OK; +} + static HRESULT drawTextPart(struct drawState *s) { COLORREF prevText; @@ -374,6 +501,9 @@ HRESULT uiprivTableHandleNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm, LRESULT * if (hr != S_OK) goto fail; hr = drawImagePart(&s); + if (hr != S_OK) + goto fail; + hr = drawCheckboxPart(&s); if (hr != S_OK) goto fail; hr = drawTextPart(&s); @@ -426,6 +556,11 @@ HRESULT uiprivUpdateImageListSize(uiTable *t) cxList = sizeCheck.cx; if (cyList < sizeCheck.cy) cyList = sizeCheck.cy; + hr = CloseThemeData(theme); + if (hr != S_OK) { + logHRESULT(L"CloseThemeData()", hr); + return hr; + } } // TODO handle errors @@ -439,11 +574,6 @@ HRESULT uiprivUpdateImageListSize(uiTable *t) // TODO will this return NULL here because it's an initial state? SendMessageW(t->hwnd, LVM_SETIMAGELIST, LVSIL_SMALL, (LPARAM) (t->imagelist)); - hr = CloseThemeData(theme); - if (hr != S_OK) { - logHRESULT(L"CloseThemeData()", hr); - return hr; - } if (ReleaseDC(t->hwnd, dc) == 0) { logLastError(L"ReleaseDC()"); return E_FAIL; diff --git a/windows/tableimages.cpp b/windows/tableimages.cpp index b7fed3bd..7f163ec0 100644 --- a/windows/tableimages.cpp +++ b/windows/tableimages.cpp @@ -20,39 +20,6 @@ We'll use the small image list. For this, the first few items will be reserved f #define nCheckboxImages 4 -static HRESULT setCellImage(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnParams *p, uiTableData *data) -{return E_NOTIMPL;/*TODO*/} - -#define stateUnchecked 0 -#define stateChecked 1 -#define stateDisabled 2 - -static int checkboxIndex(uiTableModel *m, int row, int checkboxModelColumn, int checkboxEditableColumn) -{ - uiTableData *data; - int ret; - - ret = stateUnchecked; - data = (*(m->mh->CellValue))(m->mh, m, row, checkboxModelColumn); - if (uiTableDataInt(data) != 0) - ret = stateChecked; - uiFreeTableData(data); - - switch (checkboxEditableColumn) { - case uiTableModelColumnNeverEditable: - ret += stateDisabled; - break; - case uiTableModelColumnAlwaysEditable: - break; - default: - data = (*(m->mh->CellValue))(m->mh, m, row, checkboxEditableColumn); - if (uiTableDataInt(data) != 0) - ret += stateDisabled; - } - - return ret; -} - HRESULT uiprivLVN_GETDISPINFOImagesCheckboxes(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnParams *p) { uiTableData *data; @@ -75,10 +42,8 @@ HRESULT uiprivLVN_GETDISPINFOImagesCheckboxes(uiTable *t, NMLVDISPINFOW *nm, uip return S_OK; if (p->imageModelColumn != -1) { - data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->item.iItem, p->imageModelColumn); - hr = setCellImage(t, nm, p, data); - uiFreeTableData(data); - return hr; + nm->item.iImage = 0; + return S_OK; } if (p->checkboxModelColumn != -1) { @@ -101,172 +66,3 @@ HRESULT uiprivLVN_GETDISPINFOImagesCheckboxes(uiTable *t, NMLVDISPINFOW *nm, uip nm->item.iImage = -1; return S_OK; } - -#if 0 - -// in order to properly look like checkboxes, we need to exclude them from being colored in by the selection rect -// however, there seems to be no way to do this natively, so we have to draw the icons ourselves -// see also https://www.codeproject.com/Articles/79/Neat-Stuff-to-Do-in-List-Controls-Using-Custom-Dra (and while this uses postpaint to draw over the existing icon, we are drawing everything ourselves, so we only draw once) -HRESULT uiprivNM_CUSTOMDRAWImagesCheckboxes(uiTable *t, NMLVCUSTOMDRAW *nm, uiprivSubitemDrawParams *dp) -{ - uiprivTableColumnParams *p; - int index; - RECT r; - int cxIcon, cyIcon; - LONG yoff; - - if (nm->nmcd.dwDrawStage != (CDDS_SUBITEM | CDDS_ITEMPREPAINT)) - return S_OK; - - // only draw over checkboxes - p = (*(t->columns))[nm->iSubItem]; - if (p->checkboxModelColumn == -1) - return S_OK; - - index = checkboxIndex(t->model, nm->nmcd.dwItemSpec, - p->checkboxModelColumn, p->checkboxEditableColumn); - r = dp->icon; - // the real listview also does this :| - if (ImageList_GetIconSize(t->smallImages, &cxIcon, &cyIcon) == 0) { - logLastError(L"LVM_GETSUBITEMRECT cell"); - return E_FAIL; - } - yoff = ((r.bottom - r.top) - cyIcon) / 2; - r.top += yoff; - r.bottom += yoff; -if ((nm->nmcd.dwItemSpec%2)==0) - if (ImageList_Draw(t->smallImages, index, nm->nmcd.hdc, - r.left, r.top, ILD_NORMAL) == 0) { - logLastError(L"ImageList_Draw()"); - return E_FAIL; - } - return S_OK; -} - -// references for checkbox drawing: -// - https://blogs.msdn.microsoft.com/oldnewthing/20171129-00/?p=97485 -// - https://blogs.msdn.microsoft.com/oldnewthing/20171201-00/?p=97505 - -static UINT unthemedStates[] = { - 0, - DFCS_CHECKED, - DFCS_INACTIVE, - DFCS_CHECKED | DFCS_INACTIVE, -}; - -static int themedStates[] = { - CBS_UNCHECKEDNORMAL, - CBS_CHECKEDNORMAL, - CBS_UNCHECKEDDISABLED, - CBS_CHECKEDDISABLED, -}; - -// TODO properly clean up on failure -static HRESULT mkCheckboxes(uiTable *t, HTHEME theme, HDC dc, int cxList, int cyList, int cxCheck, int cyCheck) -{ - BITMAPINFO bmi; - HBITMAP b; - VOID *bits; - HDC cdc; - HBITMAP prevBitmap; - RECT r; - int i; - HRESULT hr; - - ZeroMemory(&bmi, sizeof (BITMAPINFO)); - bmi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER); - bmi.bmiHeader.biWidth = cxList * nCheckboxImages; - bmi.bmiHeader.biHeight = cyList; - bmi.bmiHeader.biPlanes = 1; - bmi.bmiHeader.biBitCount = 32; - bmi.bmiHeader.biCompression = BI_RGB; - b = CreateDIBSection(dc, &bmi, DIB_RGB_COLORS, - &bits, NULL, 0); - if (b == NULL) { - logLastError(L"CreateDIBSection()"); - return E_FAIL; - } - - cdc = CreateCompatibleDC(dc); - if (cdc == NULL) { - logLastError(L"CreateCompatibleDC()"); - return E_FAIL; - } - prevBitmap = (HBITMAP) SelectObject(cdc, b); - if (prevBitmap == NULL) { - logLastError(L"SelectObject() b"); - return E_FAIL; - } - - // the actual list view LVS_EX_CHECKBOXES code does this to ensure the entire image is valid, not just the parts that are drawn after resizing - // TODO find a better, alpha-friendly way to do this - // note that the actual list view does this only if unthemed, but it can get away with that since its image lists only contain checkmarks - // ours don't, so we have to compromise until the above TODO is resolved so we don't draw alpha stuff on top of garbage - if (theme == NULL || cxList != cxCheck || cyList != cyCheck) { - r.left = 0; - r.top = 0; - r.right = cxList * nCheckboxImages; - r.bottom = cyList; - FillRect(cdc, &r, GetSysColorBrush(COLOR_WINDOW)); - } - - r.left = 0; - r.top = 0; - r.right = cxCheck; - r.bottom = cyCheck; - if (theme != NULL) { - // because we're not making an image list exactly the correct size, we'll need to manually position the checkbox correctly - // let's just center it for now - // TODO make sure this is correct... - r.left = (cxList - cxCheck) / 2; - r.top = (cyList - cyCheck) / 2; - r.right += r.left; - r.bottom += r.top; - for (i = 0; i < nCheckboxImages; i++) { - hr = DrawThemeBackground(theme, cdc, - BP_CHECKBOX, themedStates[i], - &r, NULL); - if (hr != S_OK) { - logHRESULT(L"DrawThemeBackground()", hr); - return hr; - } - r.left += cxList; - r.right += cxList; - } - } else { - // this is what the actual list view LVS_EX_CHECKBOXES code does to more correctly size the checkboxes - // TODO check errors - InflateRect(&r, -GetSystemMetrics(SM_CXEDGE), -GetSystemMetrics(SM_CYEDGE)); - r.right++; - r.bottom++; - for (i = 0; i < nCheckboxImages; i++) { - if (DrawFrameControl(cdc, &r, - DFC_BUTTON, DFCS_BUTTONCHECK | DFCS_FLAT | unthemedStates[i]) == 0) { - logLastError(L"DrawFrameControl()"); - return E_FAIL; - } - r.left += cxList; - r.right += cxList; - } - } - - if (SelectObject(cdc, prevBitmap) != ((HGDIOBJ) b)) { - logLastError(L"SelectObject() prev"); - return E_FAIL; - } - if (DeleteDC(cdc) == 0) { - logLastError(L"DeleteDC()"); - return E_FAIL; - } - - if (ImageList_Add(t->smallImages, b, NULL) == -1) { - logLastError(L"ImageList_Add()"); - return E_FAIL; - } - - // TODO error check - DeleteObject(b); - return S_OK; -} - -#endif From bcab52131143fc9e4a3a0f788790951ece7f2360 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 16 Jun 2018 12:33:16 -0400 Subject: [PATCH 1203/1329] And consolidated the LVN_DISPINFO handlers. Everything's a lot cleaner now too, woo! --- windows/CMakeLists.txt | 3 +- windows/table.cpp | 25 +++----------- windows/table.hpp | 16 ++------- windows/tabledispinfo.cpp | 73 +++++++++++++++++++++++++++++++++++++++ windows/tableimages.cpp | 68 ------------------------------------ windows/tabletext.cpp | 29 ---------------- 6 files changed, 81 insertions(+), 133 deletions(-) create mode 100644 windows/tabledispinfo.cpp delete mode 100644 windows/tableimages.cpp delete mode 100644 windows/tabletext.cpp diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index d074ca87..5a15459c 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -51,9 +51,8 @@ list(APPEND _LIBUI_SOURCES windows/stddialogs.cpp windows/tab.cpp windows/table.cpp + windows/tabledispinfo.cpp windows/tabledraw.cpp - windows/tableimages.cpp - windows/tabletext.cpp windows/tabpage.cpp windows/text.cpp windows/utf16.cpp diff --git a/windows/table.cpp b/windows/table.cpp index e1eb011d..89276fee 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -76,25 +76,6 @@ void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) } } -static LRESULT onLVN_GETDISPINFO(uiTable *t, NMLVDISPINFOW *nm) -{ - // TODO remove static - static uiprivTableColumnParams *p; - HRESULT hr; - - p = (*(t->columns))[nm->item.iSubItem]; - hr = uiprivLVN_GETDISPINFOText(t, nm, p); - if (hr != S_OK) { - // TODO - } - hr = uiprivLVN_GETDISPINFOImagesCheckboxes(t, nm, p); - if (hr != S_OK) { - // TODO - } - - return 0; -} - static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult) { uiTable *t = uiTable(c); @@ -102,7 +83,11 @@ static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult) switch (nmhdr->code) { case LVN_GETDISPINFO: - *lResult = onLVN_GETDISPINFO(t, (NMLVDISPINFOW *) nmhdr); + hr = uiprivTableHandleLVN_GETDISPINFO(t, (NMLVDISPINFOW *) nmhdr, lResult); + if (hr != S_OK) { + // TODO + return FALSE; + } return TRUE; case NM_CUSTOMDRAW: hr = uiprivTableHandleNM_CUSTOMDRAW(t, (NMLVCUSTOMDRAW *) nmhdr, lResult); diff --git a/windows/table.hpp b/windows/table.hpp index 4e33d70c..42eb04be 100644 --- a/windows/table.hpp +++ b/windows/table.hpp @@ -32,21 +32,9 @@ struct uiTable { // TODO make sure replacing images while selected in the listview is even allowed HIMAGELIST imagelist; }; -typedef struct uiprivSubitemDrawParams uiprivSubitemDrawParams; -struct uiprivSubitemDrawParams { - bool selected; - LRESULT bitmapMargin; - RECT bounds; - RECT icon; - RECT label; -}; -// tabletext.cpp -extern HRESULT uiprivLVN_GETDISPINFOText(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnParams *p); - -// tableimages.cpp -extern HRESULT uiprivLVN_GETDISPINFOImagesCheckboxes(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnParams *p); -extern HRESULT uiprivNM_CUSTOMDRAWImagesCheckboxes(uiTable *t, NMLVCUSTOMDRAW *nm, uiprivSubitemDrawParams *dp); +// tabledispinfo.cpp +extern HRESULT uiprivTableHandleLVN_GETDISPINFO(uiTable *t, NMLVDISPINFOW *nm, LRESULT *lResult); // tabledraw.cpp extern HRESULT uiprivTableHandleNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm, LRESULT *lResult); diff --git a/windows/tabledispinfo.cpp b/windows/tabledispinfo.cpp new file mode 100644 index 00000000..87cdd0d1 --- /dev/null +++ b/windows/tabledispinfo.cpp @@ -0,0 +1,73 @@ +// 13 june 2018 +#include "uipriv_windows.hpp" +#include "table.hpp" + +static HRESULT handleLVIF_TEXT(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnParams *p) +{ + uiTableData *data; + WCHAR *wstr; + HRESULT hr; + + if ((nm->item.mask & LVIF_TEXT) == 0) + return S_OK; + if (p->textModelColumn != -1) + return S_OK; + + data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->item.iItem, p->textModelColumn); + wstr = toUTF16(uiTableDataString(data)); + uiFreeTableData(data); + // We *could* just make pszText into a freshly allocated + // conversion and avoid the limitation of cchTextMax. + // But then, we would have to keep things around for some + // amount of time (some pages on MSDN say 2 additional + // LVN_GETDISPINFO messages). And in practice, anything + // that results in extra LVN_GETDISPINFO messages (such as + // LVN_GETITEMRECT with LVIR_LABEL) will break this + // counting. + // TODO make it so we don't have to make a copy; instead we can convert directly into pszText (this will also avoid the risk of having a dangling surrogate pair at the end) + wcsncpy(nm->item.pszText, wstr, nm->item.cchTextMax); + nm->item.pszText[nm->item.cchTextMax - 1] = L'\0'; + uiprivFree(wstr); + return S_OK; +} + +static HRESULT handleLVIF_IMAGE(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnParams *p) +{ + uiTableData *data; + HRESULT hr; + + if (nm->item.iSubItem == 0 && p->imageModelColumn == -1 && p->checkboxModelColumn == -1) { + // Having an image list always leaves space for an image + // on the main item :| + // Other places on the internet imply that you should be + // able to do this but that it shouldn't work, but it works + // perfectly (and pixel-perfectly too) for me, so... + nm->item.mask |= LVIF_INDENT; + nm->item.iIndent = -1; + } + if ((nm->item.mask & LVIF_IMAGE) == 0) + return S_OK; // nothing to do here + + // TODO see if the -1 part is correct + // TODO see if we should use state instead of images for checkbox data + nm->item.iImage = -1; + if (p->imageModelColumn != -1 || p->checkboxModelColumn != -1) + nm->item.iImage = 0; + return S_OK; +} + +HRESULT uiprivTableHandleLVN_GETDISPINFO(uiTable *t, NMLVDISPINFOW *nm, LRESULT *lResult) +{ + uiprivTableColumnParams *p; + HRESULT hr; + + p = (*(t->columns))[nm->item.iSubItem]; + hr = handleLVIF_TEXT(t, nm, p); + if (hr != S_OK) + return hr; + hr = handleLVIF_IMAGE(t, nm, p); + if (hr != S_OK) + return hr; + *lResult = 0; + return S_OK; +} diff --git a/windows/tableimages.cpp b/windows/tableimages.cpp deleted file mode 100644 index 7f163ec0..00000000 --- a/windows/tableimages.cpp +++ /dev/null @@ -1,68 +0,0 @@ -// 10 june 2018 -#include "uipriv_windows.hpp" -#include "table.hpp" - -/* -This file handles both images and checkboxes in tables. - -For images, we'll do a similar thing to what text columns do: cycle out images from the small image list every few LVN_GETDISPINFO notifications. - -For checkboxes, the native list view checkbox functionality uses state images, but those are only supported on the main item, not on subitems. So instead, we'll do them on normal images instead. -TODO will this affect accessibility? - -We'll use the small image list. For this, the first few items will be reserved for checkboxes, and the last few for cell images. -*/ - -// checkboxes TODOs: -// - see if we need to get rid of the extra margin in subitems -// - get rid of the extra bitmap margin space before text -// - get rid of extra whitespace before text on subitems (this might not be necessary if we can fill the background of images AND this amount is the same as on the first column; it is a hardcoded 2 logical units in the real list view code) - -#define nCheckboxImages 4 - -HRESULT uiprivLVN_GETDISPINFOImagesCheckboxes(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnParams *p) -{ - uiTableData *data; - HRESULT hr; - - if (nm->item.iSubItem == 0 && p->imageModelColumn == -1 && p->checkboxModelColumn == -1) { - // having an image list always leaves space for an image on the main item :| - // other places on the internet imply that you should be able to do this but that it shouldn't work - // but it works perfectly (and pixel-perfectly too) for me, so... - nm->item.mask |= LVIF_INDENT; - nm->item.iIndent = -1; - } - if ((nm->item.mask & LVIF_IMAGE) == 0) - return S_OK; // nothing to do here - - // TODO - nm->item.iImage = -1; - if (p->imageModelColumn != -1 || p->checkboxModelColumn != -1) - nm->item.iImage = 0; - return S_OK; - - if (p->imageModelColumn != -1) { - nm->item.iImage = 0; - return S_OK; - } - - if (p->checkboxModelColumn != -1) { -#if 0 - // TODO handle enabled - data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->item.iItem, p->textModelColumn); - checked = uiTableDataInt(data) != 0; - uiFreeTableData(data); - nm->item.iImage = 0; - if (checked) - nm->item.iImage = 1; - nm->item.mask |= LVIF_IMAGE; -#endif - nm->item.mask |= LVIF_IMAGE; - nm->item.iImage = nm->item.iItem % 4; - return S_OK; - } - - // TODO see if this is correct - nm->item.iImage = -1; - return S_OK; -} diff --git a/windows/tabletext.cpp b/windows/tabletext.cpp deleted file mode 100644 index d1feb336..00000000 --- a/windows/tabletext.cpp +++ /dev/null @@ -1,29 +0,0 @@ -// 13 june 2018 -#include "uipriv_windows.hpp" -#include "table.hpp" - -// This file handles text in tables. - -HRESULT uiprivLVN_GETDISPINFOText(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnParams *p) -{ - uiTableData *data; - WCHAR *wstr; - HRESULT hr; - - if ((nm->item.mask & LVIF_TEXT) == 0) - return S_OK; - if (p->textModelColumn != -1) - return S_OK; - - data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->item.iItem, p->textModelColumn); - wstr = toUTF16(uiTableDataString(data)); - uiFreeTableData(data); - // we could just make pszText into a freshly allocated conversion and avoid the limitation of cchTextMax - // but then we would have to keep things around for some amount of time (some pages on MSDN say 2 additional LVN_GETDISPINFO messages) - // and in practice, anything that results in extra LVN_GETDISPINFO messages (such as fillSubitemDrawParams() below) will break this counting - // TODO make it so we don't have to make a copy; instead we can convert directly into pszText (this will also avoid the risk of having a dangling surrogate pair at the end) - wcsncpy(nm->item.pszText, wstr, nm->item.cchTextMax); - nm->item.pszText[nm->item.cchTextMax - 1] = L'\0'; - uiprivFree(wstr); - return S_OK; -} From d63af885ba6fd36479cc87a8fe1a6c3c1685ba63 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 16 Jun 2018 12:52:16 -0400 Subject: [PATCH 1204/1329] Oops, that's why tooltips weren't working right: I had inverted a test in tabledispinfo.cpp. Fixed. --- windows/tabledispinfo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/tabledispinfo.cpp b/windows/tabledispinfo.cpp index 87cdd0d1..9912bebf 100644 --- a/windows/tabledispinfo.cpp +++ b/windows/tabledispinfo.cpp @@ -10,7 +10,7 @@ static HRESULT handleLVIF_TEXT(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnP if ((nm->item.mask & LVIF_TEXT) == 0) return S_OK; - if (p->textModelColumn != -1) + if (p->textModelColumn == -1) return S_OK; data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->item.iItem, p->textModelColumn); From f96c0f410ee8294dd66fa42b924b1458b75c8186 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 16 Jun 2018 13:06:20 -0400 Subject: [PATCH 1205/1329] Started the implementation of progressbar columns. This handles LVN_GETDISPINFO. --- windows/table.cpp | 5 +++- windows/tabledispinfo.cpp | 53 ++++++++++++++++++++++++++------------- 2 files changed, 40 insertions(+), 18 deletions(-) diff --git a/windows/table.cpp b/windows/table.cpp index 89276fee..b3409907 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -230,7 +230,10 @@ void uiTableAppendCheckboxTextColumn(uiTable *t, const char *name, int checkboxM void uiTableAppendProgressBarColumn(uiTable *t, const char *name, int progressModelColumn) { - // TODO + uiprivTableColumnParams *p; + + p = appendColumn(t, name, LVCFMT_LEFT); + p->progressBarModelColumn = progressModelColumn; } void uiTableAppendButtonColumn(uiTable *t, const char *name, int buttonTextModelColumn, int buttonClickableModelColumn) diff --git a/windows/tabledispinfo.cpp b/windows/tabledispinfo.cpp index 9912bebf..2e8a06e2 100644 --- a/windows/tabledispinfo.cpp +++ b/windows/tabledispinfo.cpp @@ -6,28 +6,47 @@ static HRESULT handleLVIF_TEXT(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnP { uiTableData *data; WCHAR *wstr; + int progress; HRESULT hr; if ((nm->item.mask & LVIF_TEXT) == 0) return S_OK; - if (p->textModelColumn == -1) - return S_OK; - data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->item.iItem, p->textModelColumn); - wstr = toUTF16(uiTableDataString(data)); - uiFreeTableData(data); - // We *could* just make pszText into a freshly allocated - // conversion and avoid the limitation of cchTextMax. - // But then, we would have to keep things around for some - // amount of time (some pages on MSDN say 2 additional - // LVN_GETDISPINFO messages). And in practice, anything - // that results in extra LVN_GETDISPINFO messages (such as - // LVN_GETITEMRECT with LVIR_LABEL) will break this - // counting. - // TODO make it so we don't have to make a copy; instead we can convert directly into pszText (this will also avoid the risk of having a dangling surrogate pair at the end) - wcsncpy(nm->item.pszText, wstr, nm->item.cchTextMax); - nm->item.pszText[nm->item.cchTextMax - 1] = L'\0'; - uiprivFree(wstr); + if (p->textModelColumn != -1) { + data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->item.iItem, p->textModelColumn); + wstr = toUTF16(uiTableDataString(data)); + uiFreeTableData(data); + // We *could* just make pszText into a freshly allocated + // conversion and avoid the limitation of cchTextMax. + // But then, we would have to keep things around for some + // amount of time (some pages on MSDN say 2 additional + // LVN_GETDISPINFO messages). And in practice, anything + // that results in extra LVN_GETDISPINFO messages (such + // as LVN_GETITEMRECT with LVIR_LABEL) will break this + // counting. + // TODO make it so we don't have to make a copy; instead we can convert directly into pszText (this will also avoid the risk of having a dangling surrogate pair at the end) + wcsncpy(nm->item.pszText, wstr, nm->item.cchTextMax); + nm->item.pszText[nm->item.cchTextMax - 1] = L'\0'; + uiprivFree(wstr); + return S_OK; + } + + if (p->progressBarModelColumn != -1) { + data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->item.iItem, p->progressBarModelColumn); + progress = uiTableDataInt(data); + uiFreeTableData(data); + + if (progress == -1) { + // TODO either localize this or replace it with something that's language-neutral + // TODO ensure null terminator + wcsncpy(nm->item.pszText, L"Indeterminate", nm->item.cchTextMax); + return S_OK; + } + // TODO ensure null terminator + _snwprintf(nm->item.pszText, nm->item.cchTextMax, L"%d%%", progress); + return S_OK; + } + return S_OK; } From 8769bea3a074aa8484c4d8d235d465a211f53da0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 16 Jun 2018 13:57:46 -0400 Subject: [PATCH 1206/1329] Added code for unthemed definite progressbars. --- windows/tabledraw.cpp | 77 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/windows/tabledraw.cpp b/windows/tabledraw.cpp index ad8b4b46..7dbd7177 100644 --- a/windows/tabledraw.cpp +++ b/windows/tabledraw.cpp @@ -310,6 +310,80 @@ static HRESULT drawTextPart(struct drawState *s) return S_OK; } +// much of this is to imitate what shell32.dll's CDrawProgressBar does +static HRESULT drawProgressBarPart(struct drawState *s) +{ + uiTableData *data; + int progress; + HTHEME theme; + RECT r; + RECT rBorder, rFill; + TEXTMETRICW tm; + int sysColor; + HRESULT hr; + + if (s->p->progressBarModelColumn == -1) + return S_OK; + data = (*(s->m->mh->CellValue))(s->m->mh, s->m, s->iItem, s->p->progressBarModelColumn); + progress = uiTableDataInt(data); + uiFreeTableData(data); + if (progress == -1) + return S_OK; // TODO + + theme = OpenThemeData(s->t->hwnd, L"TODO"); + + if (GetTextMetricsW(s->dc, &tm) == 0) { + logLastError(L"GetTextMetricsW()"); + hr = E_FAIL; + goto fail; + } + r = s->subitemBounds; + // this sets the height of the progressbar and vertically centers it in one fell swoop + r.top += (r.bottom - tm.tmHeight - r.top) / 2; + r.bottom = r.top + tm.tmHeight; + + // TODO check errors + rBorder = r; + InflateRect(&rBorder, -1, -1); + if (theme != NULL) { + // TODO + }/* else */{ + HPEN pen, prevPen; + HBRUSH brush, prevBrush; + + sysColor = COLOR_HIGHLIGHT; + if (s->selected) + sysColor = COLOR_HIGHLIGHTTEXT; + + // TODO check errors everywhere + pen = CreatePen(PS_SOLID, 1, GetSysColor(sysColor)); + prevPen = (HPEN) SelectObject(s->dc, pen); + brush = (HBRUSH) GetStockObject(NULL_BRUSH); + prevBrush = (HBRUSH) SelectObject(s->dc, brush); + Rectangle(s->dc, rBorder.left, rBorder.top, rBorder.right, rBorder.bottom); + SelectObject(s->dc, prevBrush); + SelectObject(s->dc, prevPen); + DeleteObject(pen); + } + + rFill = r; + // TODO check error + InflateRect(&rFill, -1, -1); + rFill.right -= (rFill.right - rFill.left) * (100 - progress) / 100; + if (theme != NULL) { + // TODO + }/* else*/ + // TODO check errors + FillRect(s->dc, &rFill, GetSysColorBrush(sysColor)); + + hr = S_OK; +fail: + // TODO check errors + if (theme != NULL) + CloseThemeData(theme); + return hr; +} + static HRESULT freeDrawState(struct drawState *s) { HRESULT hr, hrret; @@ -507,6 +581,9 @@ HRESULT uiprivTableHandleNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm, LRESULT * if (hr != S_OK) goto fail; hr = drawTextPart(&s); + if (hr != S_OK) + goto fail; + hr = drawProgressBarPart(&s); if (hr != S_OK) goto fail; hr = freeDrawState(&s); From c978f6fece2a484d9ea696595999b4e06887e876 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 16 Jun 2018 18:06:44 -0400 Subject: [PATCH 1207/1329] Started indeterminate progress bars. This is gonna be interesting. --- windows/table.hpp | 1 + windows/tabledraw.cpp | 42 +++++++++++++++++++++++++++++++----------- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/windows/table.hpp b/windows/table.hpp index 42eb04be..bab46f0e 100644 --- a/windows/table.hpp +++ b/windows/table.hpp @@ -31,6 +31,7 @@ struct uiTable { int backgroundColumn; // TODO make sure replacing images while selected in the listview is even allowed HIMAGELIST imagelist; + LONG indeterminatePosition; }; // tabledispinfo.cpp diff --git a/windows/tabledraw.cpp b/windows/tabledraw.cpp index 7dbd7177..adaf5feb 100644 --- a/windows/tabledraw.cpp +++ b/windows/tabledraw.cpp @@ -311,13 +311,16 @@ static HRESULT drawTextPart(struct drawState *s) } // much of this is to imitate what shell32.dll's CDrawProgressBar does +#define indeterminateSegments 8 + static HRESULT drawProgressBarPart(struct drawState *s) { uiTableData *data; int progress; HTHEME theme; RECT r; - RECT rBorder, rFill; + RECT rBorder, rFill[2]; + int i, nFill; TEXTMETRICW tm; int sysColor; HRESULT hr; @@ -327,8 +330,6 @@ static HRESULT drawProgressBarPart(struct drawState *s) data = (*(s->m->mh->CellValue))(s->m->mh, s->m, s->iItem, s->p->progressBarModelColumn); progress = uiTableDataInt(data); uiFreeTableData(data); - if (progress == -1) - return S_OK; // TODO theme = OpenThemeData(s->t->hwnd, L"TODO"); @@ -366,16 +367,35 @@ static HRESULT drawProgressBarPart(struct drawState *s) DeleteObject(pen); } - rFill = r; + nFill = 1; + rFill[0] = r; // TODO check error - InflateRect(&rFill, -1, -1); - rFill.right -= (rFill.right - rFill.left) * (100 - progress) / 100; - if (theme != NULL) { - // TODO - }/* else*/ - // TODO check errors - FillRect(s->dc, &rFill, GetSysColorBrush(sysColor)); + InflateRect(&rFill[0], -1, -1); + if (progress != -1) + rFill[0].right -= (rFill[0].right - rFill[0].left) * (100 - progress) / 100; + else { + LONG barWidth; + LONG pieceWidth; + // TODO explain all this + rFill[1] = rFill[0]; // save in case we need it + barWidth = rFill[0].right - rFill[0].left; + pieceWidth = barWidth / indeterminateSegments; + rFill[0].left += s->t->indeterminatePosition % barWidth; + if ((rFill[0].left + pieceWidth) >= rFill[0].right) { + // make this piece wrap back around + nFill++; + rFill[1].right = rFill[1].left + (pieceWidth - (rFill[0].right - rFill[0].left)); + } else + rFill[0].right = rFill[0].left + pieceWidth; + } + for (i = 0; i < nFill; i++) +{ if (theme != NULL) { + // TODO + }/* else*/ + // TODO check errors + FillRect(s->dc, &rFill[i], GetSysColorBrush(sysColor)); +} hr = S_OK; fail: // TODO check errors From c7555dcfd3aa22f7a662d546f58b9cd69685dbe2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 16 Jun 2018 19:05:36 -0400 Subject: [PATCH 1208/1329] Started a more flexible indeterminate-state implementation. Now to build and test it. --- windows/table.cpp | 69 ++++++++++++++++++++++++++++++++++++++- windows/table.hpp | 4 ++- windows/tabledispinfo.cpp | 4 +-- windows/tabledraw.cpp | 8 ++--- windows/winapi.hpp | 1 + 5 files changed, 76 insertions(+), 10 deletions(-) diff --git a/windows/table.cpp b/windows/table.cpp index b3409907..5fcd7c6b 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -76,6 +76,68 @@ void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) } } +static LRESULT CALLBACK tableSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIDSubclass, DWORD_PTR dwRefData) +{ + uiTable *t = (uiTable *) dwRefData; + + switch (uMsg) { + case WM_TIMER: + if (wParam != (WPARAM) t) + break; + for (auto &i : t->indeterminatePositions) { + i->second++; + // TODO check errors + SendMessageW(hwnd, LVM_UPDATE, (WPARAM) (i->first.first), 0); + } + return 0; + case WM_NCDESTROY: + if (RemoveWindowSubclass(hwnd, tableSubProc, uIDSubclass) == FALSE) + logLastError(L"RemoveWindowSubclass()"); + // fall through + } + return DefSubclassProc(hwnd, uMsg, wParam, lParam); +} + +int uiprivTableProgress(uiTable *t, int item, int subitem, int modelColumn, LONG *pos) +{ + uiTableData *data; + int progress; + std::pair p; + std::map, LONG>::iterator iter; + bool startTimer = false; + bool stopTimer = false; + + data = (*(t->model->mh->CellValue))(t->model->mh, t->model, item, modelColumn); + progress = uiTableModelInt(data); + uiFreeTableData(data); + + p.first = item; + p.second = subitem; + iter = t->indeterminatePositions->find(p); + if (iter == t->indeterminatePositions->end()) { + if (progress == -1) { + startTimer = t->indeterminatePositions->size() == 0; + (*(t->indeterminatePositions))[p] = 0; + if (pos != NULL) + *pos = 0; + } + } else + if (progress != -1) { + t->indeterminatePositions->erase(p); + stopTimer = t->indeterminatePositions->size() == 0; + } else if (pos != NULL) + *pos = iter->second; + + if (startTimer) + // the interval shown here is PBM_SETMARQUEE's default + // TODO should we pass a function here instead? it seems to be called by DispatchMessage(), not DefWindowProc(), but I'm still unsure + if (SetTimer(t->hwnd, (UINT_PTR) (&t), 30, NULL) == 0) + logLastError(L"SetTimer()"); + if (stopTimer) + if (KillTimer(t->hwnd, (UINT_PTR) (&t)) == 0) + logLastError(L"KillTimer()"); +} + static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult) { uiTable *t = uiTable(c); @@ -119,7 +181,8 @@ static void uiTableDestroy(uiControl *c) for (auto col : *(t->columns)) uiprivFree(col); delete t->columns; - // t->smallImages will be automatically destroyed + // t->imagelist will be automatically destroyed + delete t->indeterminatePositions; uiFreeControl(uiControl(t)); } @@ -283,5 +346,9 @@ uiTable *uiNewTable(uiTableModel *model) // TODO } + t->indeterminatePositions = new std::map, LONG>; + if (SetWindowSubclass(t->hwnd, tableSubProc, 0, (DWORD_PTR) t) == FALSE) + logLastError(L"SetWindowSubclass()"); + return t; } diff --git a/windows/table.hpp b/windows/table.hpp index bab46f0e..bf55ef6f 100644 --- a/windows/table.hpp +++ b/windows/table.hpp @@ -31,8 +31,10 @@ struct uiTable { int backgroundColumn; // TODO make sure replacing images while selected in the listview is even allowed HIMAGELIST imagelist; - LONG indeterminatePosition; + // TODO document all this + std::map, LONG> *indeterminatePositions; }; +extern int uiprivTableProgress(uiTable *t, int item, int subitem, int modelColumn, LONG *pos); // tabledispinfo.cpp extern HRESULT uiprivTableHandleLVN_GETDISPINFO(uiTable *t, NMLVDISPINFOW *nm, LRESULT *lResult); diff --git a/windows/tabledispinfo.cpp b/windows/tabledispinfo.cpp index 2e8a06e2..33559bb3 100644 --- a/windows/tabledispinfo.cpp +++ b/windows/tabledispinfo.cpp @@ -32,9 +32,7 @@ static HRESULT handleLVIF_TEXT(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnP } if (p->progressBarModelColumn != -1) { - data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->item.iItem, p->progressBarModelColumn); - progress = uiTableDataInt(data); - uiFreeTableData(data); + progress = uiprivTableProgress(t, nm->item.iItem, p->progressBarModelColumn, NULL); if (progress == -1) { // TODO either localize this or replace it with something that's language-neutral diff --git a/windows/tabledraw.cpp b/windows/tabledraw.cpp index adaf5feb..59b9d90c 100644 --- a/windows/tabledraw.cpp +++ b/windows/tabledraw.cpp @@ -315,8 +315,8 @@ static HRESULT drawTextPart(struct drawState *s) static HRESULT drawProgressBarPart(struct drawState *s) { - uiTableData *data; int progress; + LONG indeterminatePos; HTHEME theme; RECT r; RECT rBorder, rFill[2]; @@ -327,9 +327,7 @@ static HRESULT drawProgressBarPart(struct drawState *s) if (s->p->progressBarModelColumn == -1) return S_OK; - data = (*(s->m->mh->CellValue))(s->m->mh, s->m, s->iItem, s->p->progressBarModelColumn); - progress = uiTableDataInt(data); - uiFreeTableData(data); + progress = uiprivTableProgress(s->t, s->iItem, s->p->progressBarModelColumn, &indeterminatePos); theme = OpenThemeData(s->t->hwnd, L"TODO"); @@ -381,7 +379,7 @@ static HRESULT drawProgressBarPart(struct drawState *s) rFill[1] = rFill[0]; // save in case we need it barWidth = rFill[0].right - rFill[0].left; pieceWidth = barWidth / indeterminateSegments; - rFill[0].left += s->t->indeterminatePosition % barWidth; + rFill[0].left += indeterminatePos % barWidth; if ((rFill[0].left + pieceWidth) >= rFill[0].right) { // make this piece wrap back around nFill++; diff --git a/windows/winapi.hpp b/windows/winapi.hpp index 19426844..92c08230 100644 --- a/windows/winapi.hpp +++ b/windows/winapi.hpp @@ -60,4 +60,5 @@ #include #include #include +#include #endif From 301376706fff9b9eb020c75d6aeb3972c61a9ff8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 16 Jun 2018 19:40:06 -0400 Subject: [PATCH 1209/1329] And made indeterminate progressbars work. Now to theme them. --- windows/table.cpp | 15 +++++++++------ windows/tabledispinfo.cpp | 2 +- windows/tabledraw.cpp | 3 ++- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/windows/table.cpp b/windows/table.cpp index 5fcd7c6b..9dba46b7 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -76,6 +76,7 @@ void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) } } +// TODO explain all this static LRESULT CALLBACK tableSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIDSubclass, DWORD_PTR dwRefData) { uiTable *t = (uiTable *) dwRefData; @@ -84,10 +85,11 @@ static LRESULT CALLBACK tableSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM case WM_TIMER: if (wParam != (WPARAM) t) break; - for (auto &i : t->indeterminatePositions) { - i->second++; + // TODO only increment and update if visible? + for (auto &i : *(t->indeterminatePositions)) { + i.second++; // TODO check errors - SendMessageW(hwnd, LVM_UPDATE, (WPARAM) (i->first.first), 0); + SendMessageW(hwnd, LVM_UPDATE, (WPARAM) (i.first.first), 0); } return 0; case WM_NCDESTROY: @@ -108,7 +110,7 @@ int uiprivTableProgress(uiTable *t, int item, int subitem, int modelColumn, LONG bool stopTimer = false; data = (*(t->model->mh->CellValue))(t->model->mh, t->model, item, modelColumn); - progress = uiTableModelInt(data); + progress = uiTableDataInt(data); uiFreeTableData(data); p.first = item; @@ -131,11 +133,13 @@ int uiprivTableProgress(uiTable *t, int item, int subitem, int modelColumn, LONG if (startTimer) // the interval shown here is PBM_SETMARQUEE's default // TODO should we pass a function here instead? it seems to be called by DispatchMessage(), not DefWindowProc(), but I'm still unsure - if (SetTimer(t->hwnd, (UINT_PTR) (&t), 30, NULL) == 0) + if (SetTimer(t->hwnd, (UINT_PTR) t, 30, NULL) == 0) logLastError(L"SetTimer()"); if (stopTimer) if (KillTimer(t->hwnd, (UINT_PTR) (&t)) == 0) logLastError(L"KillTimer()"); + + return progress; } static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult) @@ -315,7 +319,6 @@ uiTable *uiNewTable(uiTableModel *model) { uiTable *t; int n; - int i; HRESULT hr; uiWindowsNewControl(uiTable, t); diff --git a/windows/tabledispinfo.cpp b/windows/tabledispinfo.cpp index 33559bb3..2e3ae4b9 100644 --- a/windows/tabledispinfo.cpp +++ b/windows/tabledispinfo.cpp @@ -32,7 +32,7 @@ static HRESULT handleLVIF_TEXT(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnP } if (p->progressBarModelColumn != -1) { - progress = uiprivTableProgress(t, nm->item.iItem, p->progressBarModelColumn, NULL); + progress = uiprivTableProgress(t, nm->item.iItem, nm->item.iSubItem, p->progressBarModelColumn, NULL); if (progress == -1) { // TODO either localize this or replace it with something that's language-neutral diff --git a/windows/tabledraw.cpp b/windows/tabledraw.cpp index 59b9d90c..bc2e76d9 100644 --- a/windows/tabledraw.cpp +++ b/windows/tabledraw.cpp @@ -327,7 +327,7 @@ static HRESULT drawProgressBarPart(struct drawState *s) if (s->p->progressBarModelColumn == -1) return S_OK; - progress = uiprivTableProgress(s->t, s->iItem, s->p->progressBarModelColumn, &indeterminatePos); + progress = uiprivTableProgress(s->t, s->iItem, s->iSubItem, s->p->progressBarModelColumn, &indeterminatePos); theme = OpenThemeData(s->t->hwnd, L"TODO"); @@ -376,6 +376,7 @@ static HRESULT drawProgressBarPart(struct drawState *s) LONG pieceWidth; // TODO explain all this + // TODO this should really start the progressbar scrolling into view instead of already on screen when first set rFill[1] = rFill[0]; // save in case we need it barWidth = rFill[0].right - rFill[0].left; pieceWidth = barWidth / indeterminateSegments; From a00ca05136f4e339152ca4bb6df1d097441bc588 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 16 Jun 2018 20:10:39 -0400 Subject: [PATCH 1210/1329] Added themed progressbars. --- windows/tabledraw.cpp | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/windows/tabledraw.cpp b/windows/tabledraw.cpp index bc2e76d9..8e6cb940 100644 --- a/windows/tabledraw.cpp +++ b/windows/tabledraw.cpp @@ -329,7 +329,7 @@ static HRESULT drawProgressBarPart(struct drawState *s) return S_OK; progress = uiprivTableProgress(s->t, s->iItem, s->iSubItem, s->p->progressBarModelColumn, &indeterminatePos); - theme = OpenThemeData(s->t->hwnd, L"TODO"); + theme = OpenThemeData(s->t->hwnd, L"PROGRESS"); if (GetTextMetricsW(s->dc, &tm) == 0) { logLastError(L"GetTextMetricsW()"); @@ -345,8 +345,23 @@ static HRESULT drawProgressBarPart(struct drawState *s) rBorder = r; InflateRect(&rBorder, -1, -1); if (theme != NULL) { - // TODO - }/* else */{ + RECT crect; + + hr = GetThemeBackgroundContentRect(theme, s->dc, + PP_TRANSPARENTBAR, PBBS_NORMAL, + &rBorder, &crect); + if (hr != S_OK) { + logHRESULT(L"GetThemeBackgroundContentRect()", hr); + goto fail; + } + hr = DrawThemeBackground(theme, s->dc, + PP_TRANSPARENTBAR, PBBS_NORMAL, + &crect, NULL); + if (hr != S_OK) { + logHRESULT(L"DrawThemeBackground() border", hr); + goto fail; + } + } else { HPEN pen, prevPen; HBRUSH brush, prevBrush; @@ -389,12 +404,18 @@ static HRESULT drawProgressBarPart(struct drawState *s) rFill[0].right = rFill[0].left + pieceWidth; } for (i = 0; i < nFill; i++) -{ if (theme != NULL) { - // TODO - }/* else*/ + if (theme != NULL) { + hr = DrawThemeBackground(theme, s->dc, + PP_FILL, PBFS_NORMAL, + &rFill[i], NULL); + if (hr != S_OK) { + logHRESULT(L"DrawThemeBackground() fill", hr); + goto fail; + } + } else // TODO check errors FillRect(s->dc, &rFill[i], GetSysColorBrush(sysColor)); -} + hr = S_OK; fail: // TODO check errors From 9fba903c4a4e02b965842192901d0b5b2cbe39eb Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 16 Jun 2018 22:12:54 -0400 Subject: [PATCH 1211/1329] Started button columns. LVN_GETDISPINFO handled. Also filled in the rest of the new column functions. --- windows/table.cpp | 12 ++++++++++-- windows/tabledispinfo.cpp | 13 +++++++++++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/windows/table.cpp b/windows/table.cpp index 9dba46b7..8e0348d2 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -258,7 +258,10 @@ void uiTableAppendTextColumn(uiTable *t, const char *name, int textModelColumn, void uiTableAppendImageColumn(uiTable *t, const char *name, int imageModelColumn) { - // TODO + uiprivTableColumnParams *p; + + p = appendColumn(t, name, LVCFMT_LEFT); + p->imageModelColumn = imageModelColumn; } void uiTableAppendImageTextColumn(uiTable *t, const char *name, int imageModelColumn, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *textParams) @@ -305,7 +308,12 @@ void uiTableAppendProgressBarColumn(uiTable *t, const char *name, int progressMo void uiTableAppendButtonColumn(uiTable *t, const char *name, int buttonTextModelColumn, int buttonClickableModelColumn) { - // TODO + uiprivTableColumnParams *p; + + // TODO see if we can get rid of this parameter + p = appendColumn(t, name, LVCFMT_LEFT); + p->buttonModelColumn = buttonTextModelColumn; + p->buttonClickableModelColumn = buttonClickableModelColumn; } void uiTableSetRowBackgroundColorModelColumn(uiTable *t, int modelColumn) diff --git a/windows/tabledispinfo.cpp b/windows/tabledispinfo.cpp index 2e3ae4b9..f8a06842 100644 --- a/windows/tabledispinfo.cpp +++ b/windows/tabledispinfo.cpp @@ -2,8 +2,12 @@ #include "uipriv_windows.hpp" #include "table.hpp" +// further reading: +// - https://msdn.microsoft.com/en-us/library/ye4z8x58.aspx + static HRESULT handleLVIF_TEXT(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnParams *p) { + int strcol; uiTableData *data; WCHAR *wstr; int progress; @@ -12,8 +16,13 @@ static HRESULT handleLVIF_TEXT(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnP if ((nm->item.mask & LVIF_TEXT) == 0) return S_OK; - if (p->textModelColumn != -1) { - data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->item.iItem, p->textModelColumn); + strcol = -1; + if (p->textModelColumn != -1) + strcol = p->textModelColumn; + else if (p->buttonModelColumn != -1) + strcol = p->buttonModelColumn; + if (strcol != -1) { + data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->item.iItem, strcol); wstr = toUTF16(uiTableDataString(data)); uiFreeTableData(data); // We *could* just make pszText into a freshly allocated From 7bc121b1ec26f676916c71d383611b488c559cf2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 17 Jun 2018 09:01:24 -0400 Subject: [PATCH 1212/1329] And drew buttons. Woo! Now for the harder part: editing. --- windows/tabledraw.cpp | 108 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/windows/tabledraw.cpp b/windows/tabledraw.cpp index 8e6cb940..ca1982f8 100644 --- a/windows/tabledraw.cpp +++ b/windows/tabledraw.cpp @@ -327,6 +327,7 @@ static HRESULT drawProgressBarPart(struct drawState *s) if (s->p->progressBarModelColumn == -1) return S_OK; + progress = uiprivTableProgress(s->t, s->iItem, s->iSubItem, s->p->progressBarModelColumn, &indeterminatePos); theme = OpenThemeData(s->t->hwnd, L"PROGRESS"); @@ -424,6 +425,110 @@ fail: return hr; } +static HRESULT drawButtonPart(struct drawState *s) +{ + uiTableData *data; + WCHAR *wstr; + bool enabled; + HTHEME theme; + RECT r; + TEXTMETRICW tm; + HRESULT hr; + + if (s->p->buttonModelColumn == -1) + return S_OK; + + data = (*(s->m->mh->CellValue))(s->m->mh, s->m, s->iItem, s->p->buttonModelColumn); + wstr = toUTF16(uiTableDataString(data)); + uiFreeTableData(data); + switch (s->p->buttonClickableModelColumn) { + case uiTableModelColumnNeverEditable: + enabled = 0; + break; + case uiTableModelColumnAlwaysEditable: + enabled = 1; + break; + default: + data = (*(s->m->mh->CellValue))(s->m->mh, s->m, s->iItem, s->p->checkboxEditableColumn); + enabled = uiTableDataInt(data); + uiFreeTableData(data); + } + + theme = OpenThemeData(s->t->hwnd, L"button"); + + if (GetTextMetricsW(s->dc, &tm) == 0) { + logLastError(L"GetTextMetricsW()"); + hr = E_FAIL; + goto fail; + } + r = s->subitemBounds; + + if (theme != NULL) { + int state; + + state = PBS_NORMAL; + if (!enabled) + state = PBS_DISABLED; + hr = DrawThemeBackground(theme, s->dc, + BP_PUSHBUTTON, state, + &r, NULL); + if (hr != S_OK) { + logHRESULT(L"DrawThemeBackground()", hr); + goto fail; + } + // TODO DT_EDITCONTROL? + // TODO DT_PATH_ELLIPSIS DT_WORD_ELLIPSIS instead of DT_END_ELLIPSIS? a middle-ellipsis option would be ideal here + // TODO is there a theme property we can get instead of hardcoding these flags? if not, make these flags a macro + hr = DrawThemeText(theme, s->dc, + BP_PUSHBUTTON, state, + wstr, -1, + DT_CENTER | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE | DT_NOPREFIX, 0, + &r); + if (hr != S_OK) { + logHRESULT(L"DrawThemeText()", hr); + goto fail; + } + } else { + UINT state; + HBRUSH color, prevColor; + int prevBkMode; + + // TODO check errors + // TODO explain why we're not doing this in the themed case (it has to do with extra transparent pixels) + InflateRect(&r, -1, -1); + state = DFCS_BUTTONPUSH; + if (!enabled) + state |= DFCS_INACTIVE; + if (DrawFrameControl(s->dc, &r, DFC_BUTTON, state) == 0) { + logLastError(L"DrawFrameControl()"); + hr = E_FAIL; + goto fail; + } + color = GetSysColorBrush(COLOR_BTNTEXT); + // TODO check errors for these two + prevColor = (HBRUSH) SelectObject(s->dc, color); + prevBkMode = SetBkMode(s->dc, TRANSPARENT); + // TODO DT_EDITCONTROL? + // TODO DT_PATH_ELLIPSIS DT_WORD_ELLIPSIS instead of DT_END_ELLIPSIS? a middle-ellipsis option would be ideal here + if (DrawTextW(s->dc, wstr, -1, &r, DT_CENTER | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE | DT_NOPREFIX) == 0) { + logLastError(L"DrawTextW()"); + hr = E_FAIL; + goto fail; + } + // TODO check errors for these two + SetBkMode(s->dc, prevBkMode); + SelectObject(s->dc, prevColor); + } + + hr = S_OK; +fail: + // TODO check errors + if (theme != NULL) + CloseThemeData(theme); + uiprivFree(wstr); + return hr; +} + static HRESULT freeDrawState(struct drawState *s) { HRESULT hr, hrret; @@ -624,6 +729,9 @@ HRESULT uiprivTableHandleNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm, LRESULT * if (hr != S_OK) goto fail; hr = drawProgressBarPart(&s); + if (hr != S_OK) + goto fail; + hr = drawButtonPart(&s); if (hr != S_OK) goto fail; hr = freeDrawState(&s); From db2f3352c422190ee5af9b5b810d9b27da3ce654 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 17 Jun 2018 09:29:29 -0400 Subject: [PATCH 1213/1329] Added dummy code to evaluate LVM_SUBITEMHITTEST. The code will remain, but will be #if'd out. --- windows/table.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/windows/table.cpp b/windows/table.cpp index 8e0348d2..2104d893 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -162,6 +162,27 @@ static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult) return FALSE; } return TRUE; + case NM_CLICK: +#if 1 + { + NMITEMACTIVATE *nm = (NMITEMACTIVATE *) nmhdr; + LVHITTESTINFO ht; + WCHAR buf[256]; + + ZeroMemory(&ht, sizeof (LVHITTESTINFO)); + ht.pt = nm->ptAction; + if (SendMessageW(t->hwnd, LVM_SUBITEMHITTEST, 0, (LPARAM) (&ht)) == (LRESULT) (-1)) + MessageBoxW(GetAncestor(t->hwnd, GA_ROOT), L"No hit", L"No hit", MB_OK); + else { + wsprintf(buf, L"item %d subitem %d htflags 0x%I32X", + ht.iItem, ht.iSubItem, ht.flags); + MessageBoxW(GetAncestor(t->hwnd, GA_ROOT), buf, buf, MB_OK); + } + } +#else +#endif + *lResult = 0; + return TRUE; } return FALSE; } From 2fb3676a8fcc54765bbda6093ea9f7b76c549165 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 17 Jun 2018 10:50:24 -0400 Subject: [PATCH 1214/1329] More TODOs. --- windows/table.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/windows/table.cpp b/windows/table.cpp index 2104d893..42dd57d7 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -1,6 +1,9 @@ #include "uipriv_windows.hpp" #include "table.hpp" +// general TODOs: +// - tooltips don't work properly on columns with icons (the listview always thinks there's enough room for a short label because it's not taking the icon into account); is this a bug in our LVN_GETDISPINFO handler or something else? + static uiTableTextColumnOptionalParams defaultTextColumnOptionalParams = { /*TODO.ColorModelColumn = */-1, }; From 6d0b276d6d0727f3adfe21a1843af856af136888 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 17 Jun 2018 11:48:39 -0400 Subject: [PATCH 1215/1329] Started handling table events. This covers checkboxes and buttons. --- windows/CMakeLists.txt | 1 + windows/table.cpp | 12 ++++++-- windows/table.hpp | 3 ++ windows/tableevents.cpp | 67 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 windows/tableevents.cpp diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 5a15459c..c1747c50 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -53,6 +53,7 @@ list(APPEND _LIBUI_SOURCES windows/table.cpp windows/tabledispinfo.cpp windows/tabledraw.cpp + windows/tableevents.cpp windows/tabpage.cpp windows/text.cpp windows/utf16.cpp diff --git a/windows/table.cpp b/windows/table.cpp index 42dd57d7..4dd1370d 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -166,7 +166,7 @@ static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult) } return TRUE; case NM_CLICK: -#if 1 +#if 0 { NMITEMACTIVATE *nm = (NMITEMACTIVATE *) nmhdr; LVHITTESTINFO ht; @@ -182,10 +182,16 @@ static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult) MessageBoxW(GetAncestor(t->hwnd, GA_ROOT), buf, buf, MB_OK); } } -#else -#endif *lResult = 0; return TRUE; +#else + hr = uiprivTableHandleNM_CLICK(t, (NMITEMACTIVATE *) nmhdr, lResult); + if (hr != S_OK) { + // TODO + return FALSE; + } + return TRUE; +#endif } return FALSE; } diff --git a/windows/table.hpp b/windows/table.hpp index bf55ef6f..30bea161 100644 --- a/windows/table.hpp +++ b/windows/table.hpp @@ -42,3 +42,6 @@ extern HRESULT uiprivTableHandleLVN_GETDISPINFO(uiTable *t, NMLVDISPINFOW *nm, L // tabledraw.cpp extern HRESULT uiprivTableHandleNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm, LRESULT *lResult); extern HRESULT uiprivUpdateImageListSize(uiTable *t); + +// tableevents.cpp +extern HRESULT uiprivTableHandleNM_CLICK(uiTable *t, NMITEMACTIVATE *nm, LRESULT *lResult); diff --git a/windows/tableevents.cpp b/windows/tableevents.cpp new file mode 100644 index 00000000..eae3a096 --- /dev/null +++ b/windows/tableevents.cpp @@ -0,0 +1,67 @@ +// 17 june 2018 +#include "uipriv_windows.hpp" +#include "table.hpp" + +HRESULT uiprivTableHandleNM_CLICK(uiTable *t, NMITEMACTIVATE *nm, LRESULT *lResult) +{ + LVHITTESTINFO ht; + uiprivTableColumnParams *p; + int modelColumn, editableColumn; + bool checkbox; + uiTableData *data; + int checked, editable; + + ZeroMemory(&ht, sizeof (LVHITTESTINFO)); + ht.pt = nm->ptAction; + if (SendMessageW(t->hwnd, LVM_SUBITEMHITTEST, 0, (LPARAM) (&ht)) == (LRESULT) (-1)) + goto done; + + modelColumn = -1; + editableColumn = -1; + checkbox = false; + p = (*(t->columns))[ht.iSubItem]; + if (p->checkboxModelColumn != -1) { + modelColumn = p->checkboxModelColumn; + editableColumn = p->checkboxEditableColumn; + checkbox = true; + } else if (p->buttonModelColumn != -1) { + modelColumn = p->buttonModelColumn; + editableColumn = p->buttonClickableModelColumn; + } + if (modelColumn == -1) + goto done; + + switch (editableColumn) { + case uiTableModelColumnNeverEditable: + goto done; + case uiTableModelColumnAlwaysEditable: + break; + default: + data = (*(t->model->mh->CellValue))(t->model->mh, t->model, ht.iItem, editableColumn); + editable = uiTableDataInt(data); + uiFreeTableData(data); + if (!editable) + goto done; + } + + if (checkbox) { + if ((ht.flags & LVHT_ONITEMICON) == 0) + goto done; + data = (*(t->model->mh->CellValue))(t->model->mh, t->model, ht.iItem, modelColumn); + checked = uiTableDataInt(data); + uiFreeTableData(data); + data = uiNewTableDataInt(!checked); + (*(t->model->mh->SetCellValue))(t->model->mh, t->model, ht.iItem, modelColumn, data); + uiFreeTableData(data); + } else + (*(t->model->mh->SetCellValue))(t->model->mh, t->model, ht.iItem, modelColumn, NULL); + // always refresh the value in case the model rejected it + if (SendMessageW(t->hwnd, LVM_UPDATE, (WPARAM) (ht.iItem), 0) == (LRESULT) (-1)) { + logLastError(L"LVM_UPDATE"); + return E_FAIL; + } + +done: + *lResult = 0; + return S_OK; +} From 799c613a6faa700cb44a28bc0382cbad88753e08 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 17 Jun 2018 15:06:45 -0400 Subject: [PATCH 1216/1329] Added code for detecting that text is to be edited. Now for actually implementing editing text. --- windows/table.cpp | 31 +++++++++++++++++++++++++++++++ windows/table.hpp | 2 ++ windows/tableevents.cpp | 17 ++++++++++++++--- 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/windows/table.cpp b/windows/table.cpp index 4dd1370d..40716fc2 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -83,9 +83,16 @@ void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) static LRESULT CALLBACK tableSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIDSubclass, DWORD_PTR dwRefData) { uiTable *t = (uiTable *) dwRefData; + LRESULT lResult; switch (uMsg) { case WM_TIMER: + if (wParam == (WPARAM) (&(t->inDoubleClickTimer))) { + t->inDoubleClickTimer = FALSE; + // TODO check errors + KillTimer(hwnd, wParam); + return 0; + } if (wParam != (WPARAM) t) break; // TODO only increment and update if visible? @@ -95,6 +102,11 @@ static LRESULT CALLBACK tableSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM SendMessageW(hwnd, LVM_UPDATE, (WPARAM) (i.first.first), 0); } return 0; + case WM_LBUTTONDOWN: + t->inLButtonDown = TRUE; + lResult = DefSubclassProc(hwnd, uMsg, wParam, lParam); + t->inLButtonDown = FALSE; + return lResult; case WM_NCDESTROY: if (RemoveWindowSubclass(hwnd, tableSubProc, uIDSubclass) == FALSE) logLastError(L"RemoveWindowSubclass()"); @@ -192,6 +204,25 @@ static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult) } return TRUE; #endif + case LVN_ITEMCHANGED: + { + NMLISTVIEW *nm = (NMLISTVIEW *) nmhdr; + UINT oldSelected, newSelected; + + if (!t->inLButtonDown) + return FALSE; + oldSelected = nm->uOldState & LVIS_SELECTED; + newSelected = nm->uNewState & LVIS_SELECTED; + if (oldSelected == 0 && newSelected != 0) { + t->inDoubleClickTimer = TRUE; + // TODO check error + SetTimer(t->hwnd, (UINT_PTR) (&(t->inDoubleClickTimer)), + GetDoubleClickTime(), NULL); + *lResult = 0; + return TRUE; + } + return FALSE; + } } return FALSE; } diff --git a/windows/table.hpp b/windows/table.hpp index 30bea161..3cfd8fa7 100644 --- a/windows/table.hpp +++ b/windows/table.hpp @@ -33,6 +33,8 @@ struct uiTable { HIMAGELIST imagelist; // TODO document all this std::map, LONG> *indeterminatePositions; + BOOL inLButtonDown; + BOOL inDoubleClickTimer; }; extern int uiprivTableProgress(uiTable *t, int item, int subitem, int modelColumn, LONG *pos); diff --git a/windows/tableevents.cpp b/windows/tableevents.cpp index eae3a096..189b005c 100644 --- a/windows/tableevents.cpp +++ b/windows/tableevents.cpp @@ -7,7 +7,7 @@ HRESULT uiprivTableHandleNM_CLICK(uiTable *t, NMITEMACTIVATE *nm, LRESULT *lResu LVHITTESTINFO ht; uiprivTableColumnParams *p; int modelColumn, editableColumn; - bool checkbox; + bool text, checkbox; uiTableData *data; int checked, editable; @@ -18,9 +18,14 @@ HRESULT uiprivTableHandleNM_CLICK(uiTable *t, NMITEMACTIVATE *nm, LRESULT *lResu modelColumn = -1; editableColumn = -1; + text = false; checkbox = false; p = (*(t->columns))[ht.iSubItem]; - if (p->checkboxModelColumn != -1) { + if (p->textModelColumn != -1) { + modelColumn = p->textModelColumn; + editableColumn = p->textEditableColumn; + text = true; + } else if (p->checkboxModelColumn != -1) { modelColumn = p->checkboxModelColumn; editableColumn = p->checkboxEditableColumn; checkbox = true; @@ -31,6 +36,10 @@ HRESULT uiprivTableHandleNM_CLICK(uiTable *t, NMITEMACTIVATE *nm, LRESULT *lResu if (modelColumn == -1) goto done; + if (text && t->inDoubleClickTimer) + // don't even ask for info if it's too soon to edit text + goto done; + switch (editableColumn) { case uiTableModelColumnNeverEditable: goto done; @@ -44,7 +53,9 @@ HRESULT uiprivTableHandleNM_CLICK(uiTable *t, NMITEMACTIVATE *nm, LRESULT *lResu goto done; } - if (checkbox) { + if (text) { + MessageBoxW(NULL, L"editing text", L"ok", MB_OK); + } else if (checkbox) { if ((ht.flags & LVHT_ONITEMICON) == 0) goto done; data = (*(t->model->mh->CellValue))(t->model->mh, t->model, ht.iItem, modelColumn); From 51f98d1f7b5500d9e5b918f44c8936f82b61a9dd Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 17 Jun 2018 17:56:45 -0400 Subject: [PATCH 1217/1329] Started implementing the edit control for our table view. --- windows/table.hpp | 1 + windows/tableevents.cpp | 99 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 99 insertions(+), 1 deletion(-) diff --git a/windows/table.hpp b/windows/table.hpp index 3cfd8fa7..6ab77d57 100644 --- a/windows/table.hpp +++ b/windows/table.hpp @@ -35,6 +35,7 @@ struct uiTable { std::map, LONG> *indeterminatePositions; BOOL inLButtonDown; BOOL inDoubleClickTimer; + HWND edit; }; extern int uiprivTableProgress(uiTable *t, int item, int subitem, int modelColumn, LONG *pos); diff --git a/windows/tableevents.cpp b/windows/tableevents.cpp index 189b005c..cceb1598 100644 --- a/windows/tableevents.cpp +++ b/windows/tableevents.cpp @@ -2,6 +2,100 @@ #include "uipriv_windows.hpp" #include "table.hpp" +// TODO deduplicate this with tabledraw.cpp +static HRESULT itemRect(HRESULT hr, uiTable *t, UINT uMsg, WPARAM wParam, LONG left, LONG top, LRESULT bad, RECT *r) +{ + if (hr != S_OK) + return hr; + ZeroMemory(r, sizeof (RECT)); + r->left = left; + r->top = top; + if (SendMessageW(t->hwnd, uMsg, wParam, (LPARAM) r) == bad) { + logLastError(L"itemRect() message"); + return E_FAIL; + } + return S_OK; +} + +static HRESULT openEditControl(uiTable *t, int iItem, int iSubItem, uiprivTableColumnParams *p) +{ + RECT itemLabel; + RECT subitemBounds, subitemIcon, subitemLabel; + uiTableData *data; + WCHAR *wstr; + RECT r; + LONG xInflate, yInflate; + HRESULT hr; + + // compute this in advance so we don't have to needlessly call DestroyWindow() later + // TODO deduplicate this code with tabledraw.cpp + // TODO check LRESULT bad parameters here + hr = itemRect(S_OK, t, LVM_GETITEMRECT, iItem, + LVIR_LABEL, 0, FALSE, &itemLabel); + hr = itemRect(hr, t, LVM_GETSUBITEMRECT, iItem, + LVIR_BOUNDS, iSubItem, 0, &subitemBounds); + hr = itemRect(hr, t, LVM_GETSUBITEMRECT, iItem, + LVIR_ICON, iSubItem, 0, &subitemIcon); + if (hr != S_OK) + return hr; + // LVM_GETSUBITEMRECT treats LVIR_LABEL as the same as + // LVIR_BOUNDS, so we can't use that directly. Instead, let's + // assume the text is immediately after the icon. The correct + // rect will be determined by + // computeOtherRectsAndDrawBackgrounds() above. + subitemLabel = subitemBounds; + subitemLabel.left = subitemIcon.right; + // And on iSubItem == 0, LVIF_GETSUBITEMRECT still includes + // all the subitems, which we don't want. + if (iSubItem == 0) { + subitemBounds.right = itemLabel.right; + subitemLabel.right = itemLabel.right; + } + if ((p->imageModelColumn == -1 && p->checkboxModelColumn == -1) && iSubItem != 0) + // By default, this will include images; we're not drawing + // images, so we will manually draw over the image area. + // There's a second part to this; see below. + subitemLabel.left = subitemBounds.left; + + // the real list view creates the edit control with the string + data = (*(t->model->mh->CellValue))(t->model->mh, t->model, iItem, p->textModelColumn); + wstr = toUTF16(uiTableDataString(data)); + uiFreeTableData(data); + // TODO copy WS_EX_RTLREADING + t->edit = CreateWindowExW(0, + L"EDIT", wstr, + // these styles are what the normal listview edit uses + WS_CHILD | WS_CLIPSIBLINGS | WS_BORDER | ES_AUTOHSCROLL, + // as is this size + 0, 0, 16384, 16384, + // and this control ID + t->hwnd, (HMENU) 1, hInstance, NULL); + if (t->edit == NULL) { + logLastError(L"CreateWindowExW()"); + uiprivFree(wstr); + return E_FAIL; + } + uiprivFree(wstr); + // TODO set font here + + // and this is what the real list view does to size the edit control + SendMessageW(t->edit, EM_GETRECT, 0, (LPARAM) (&r)); + xInflate = -(GetSystemMetrics(SM_CXEDGE) + GetSystemMetrics(SM_CXBORDER)); + yInflate = -r.top; + // TODO check error + InflateRect(&subitemLabel, xInflate, yInflate); + + // TODO check error or use the right function + SetWindowPos(t->edit, NULL, + subitemLabel.left, subitemLabel.top, + subitemLabel.right - subitemLabel.left, subitemLabel.bottom - subitemLabel.top, + SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER); + // TODO get the correct constant from the real list view + ShowWindow(t->edit, SW_SHOWDEFAULT); + + return S_OK; +} + HRESULT uiprivTableHandleNM_CLICK(uiTable *t, NMITEMACTIVATE *nm, LRESULT *lResult) { LVHITTESTINFO ht; @@ -10,6 +104,7 @@ HRESULT uiprivTableHandleNM_CLICK(uiTable *t, NMITEMACTIVATE *nm, LRESULT *lResu bool text, checkbox; uiTableData *data; int checked, editable; + HRESULT hr; ZeroMemory(&ht, sizeof (LVHITTESTINFO)); ht.pt = nm->ptAction; @@ -54,7 +149,9 @@ HRESULT uiprivTableHandleNM_CLICK(uiTable *t, NMITEMACTIVATE *nm, LRESULT *lResu } if (text) { - MessageBoxW(NULL, L"editing text", L"ok", MB_OK); + hr = openEditControl(t, ht.iItem, ht.iSubItem, p); + if (hr != S_OK) + return hr; } else if (checkbox) { if ((ht.flags & LVHT_ONITEMICON) == 0) goto done; From d81b3653964fe00e2084a12f240b41ea09cab5fe Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 17 Jun 2018 21:22:57 -0400 Subject: [PATCH 1218/1329] More edit control stuff. We've almost got it, but it's too wide and the text is still aligned wrong. --- windows/tableevents.cpp | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/windows/tableevents.cpp b/windows/tableevents.cpp index cceb1598..ac043fd1 100644 --- a/windows/tableevents.cpp +++ b/windows/tableevents.cpp @@ -76,7 +76,7 @@ static HRESULT openEditControl(uiTable *t, int iItem, int iSubItem, uiprivTableC return E_FAIL; } uiprivFree(wstr); - // TODO set font here + SendMessageW(t->edit, WM_SETFONT, (WPARAM) hMessageFont, (LPARAM) TRUE); // and this is what the real list view does to size the edit control SendMessageW(t->edit, EM_GETRECT, 0, (LPARAM) (&r)); @@ -85,10 +85,42 @@ static HRESULT openEditControl(uiTable *t, int iItem, int iSubItem, uiprivTableC // TODO check error InflateRect(&subitemLabel, xInflate, yInflate); + // TODO rewrite and integrate the variables of this; I'm not fully comfortable keeping it as it is + // TODO check errors everywhere + { + HDC dc; + HFONT prevFont; + RECT textRect, editRect; + int cxIconSpacing; + LONG offsetY; + + // yes, the list view control uses the list view DC for this + dc = GetDC(t->hwnd); + prevFont = (HFONT) SelectObject(dc, hMessageFont); + ZeroMemory(&textRect, sizeof (RECT)); + cxIconSpacing = GetSystemMetrics(SM_CXICONSPACING); + textRect.right = cxIconSpacing - 2 * GetSystemMetrics(SM_CXEDGE); + // yes, the real edit control uses DT_CENTER for some reason + // TODO the real edit control filters out certain types of characters but I'm not sure what + DrawTextW(dc, wstr, -1, &textRect, DT_CENTER | DT_NOPREFIX | DT_SINGLELINE | DT_EDITCONTROL | DT_CALCRECT); + ReleaseDC(t->hwnd, dc); + if (textRect.right < cxIconSpacing / 4) + textRect.right = cxIconSpacing / 4; + offsetY = subitemLabel.top + ((textRect.bottom - textRect.top) - (subitemLabel.bottom - subitemLabel.top)) / 2; + OffsetRect(&textRect, subitemLabel.left, offsetY); + textRect.right += 4 * GetSystemMetrics(SM_CXEDGE) + GetSystemMetrics(SM_CYEDGE); + SendMessageW(t->edit, EM_GETRECT, 0, (LPARAM) (&editRect)); + editRect.left = -editRect.left; + editRect.top = -editRect.top; + AdjustWindowRectEx(&editRect, getStyle(t->edit), FALSE, getExStyle(t->edit)); + r = textRect; + InflateRect(&r, -editRect.left, -editRect.top); + } + // TODO check error or use the right function SetWindowPos(t->edit, NULL, - subitemLabel.left, subitemLabel.top, - subitemLabel.right - subitemLabel.left, subitemLabel.bottom - subitemLabel.top, + r.left, r.top, + r.right - r.left, r.bottom - r.top, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER); // TODO get the correct constant from the real list view ShowWindow(t->edit, SW_SHOWDEFAULT); From 1edb406045ce77f4b729789decd40296beb28cc6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 18 Jun 2018 09:38:34 -0400 Subject: [PATCH 1219/1329] Let's start over with this edit control sizing stuff. --- windows/tableevents.cpp | 56 +++++++++++++---------------------------- 1 file changed, 18 insertions(+), 38 deletions(-) diff --git a/windows/tableevents.cpp b/windows/tableevents.cpp index ac043fd1..a73055c8 100644 --- a/windows/tableevents.cpp +++ b/windows/tableevents.cpp @@ -56,7 +56,23 @@ static HRESULT openEditControl(uiTable *t, int iItem, int iSubItem, uiprivTableC // images, so we will manually draw over the image area. // There's a second part to this; see below. subitemLabel.left = subitemBounds.left; +/* + if ((p->imageModelColumn != -1 || p->checkboxModelColumn != -1) && iSubItem != 0) + // Normally there's this many hard-coded logical units + // of blank space, followed by the background, followed + // by a bitmap margin's worth of space. This looks bad, + // so we overrule that to start the background immediately + // and the text after the hard-coded amount. + subitemLabel.left += 2; + else if (iSubItem != 0) { + HWND header; + // In the case of subitem text without an image, we draw + // text one bitmap margin away from the left edge. + header = (HWND) SendMessageW(t->hwnd, LVM_GETHEADER, 0, 0); + subitemLabel.left += SendMessageW(header, HDM_GETBITMAPMARGIN, 0, 0); + } +*/ // the real list view creates the edit control with the string data = (*(t->model->mh->CellValue))(t->model->mh, t->model, iItem, p->textModelColumn); wstr = toUTF16(uiTableDataString(data)); @@ -78,44 +94,8 @@ static HRESULT openEditControl(uiTable *t, int iItem, int iSubItem, uiprivTableC uiprivFree(wstr); SendMessageW(t->edit, WM_SETFONT, (WPARAM) hMessageFont, (LPARAM) TRUE); - // and this is what the real list view does to size the edit control - SendMessageW(t->edit, EM_GETRECT, 0, (LPARAM) (&r)); - xInflate = -(GetSystemMetrics(SM_CXEDGE) + GetSystemMetrics(SM_CXBORDER)); - yInflate = -r.top; - // TODO check error - InflateRect(&subitemLabel, xInflate, yInflate); - - // TODO rewrite and integrate the variables of this; I'm not fully comfortable keeping it as it is - // TODO check errors everywhere - { - HDC dc; - HFONT prevFont; - RECT textRect, editRect; - int cxIconSpacing; - LONG offsetY; - - // yes, the list view control uses the list view DC for this - dc = GetDC(t->hwnd); - prevFont = (HFONT) SelectObject(dc, hMessageFont); - ZeroMemory(&textRect, sizeof (RECT)); - cxIconSpacing = GetSystemMetrics(SM_CXICONSPACING); - textRect.right = cxIconSpacing - 2 * GetSystemMetrics(SM_CXEDGE); - // yes, the real edit control uses DT_CENTER for some reason - // TODO the real edit control filters out certain types of characters but I'm not sure what - DrawTextW(dc, wstr, -1, &textRect, DT_CENTER | DT_NOPREFIX | DT_SINGLELINE | DT_EDITCONTROL | DT_CALCRECT); - ReleaseDC(t->hwnd, dc); - if (textRect.right < cxIconSpacing / 4) - textRect.right = cxIconSpacing / 4; - offsetY = subitemLabel.top + ((textRect.bottom - textRect.top) - (subitemLabel.bottom - subitemLabel.top)) / 2; - OffsetRect(&textRect, subitemLabel.left, offsetY); - textRect.right += 4 * GetSystemMetrics(SM_CXEDGE) + GetSystemMetrics(SM_CYEDGE); - SendMessageW(t->edit, EM_GETRECT, 0, (LPARAM) (&editRect)); - editRect.left = -editRect.left; - editRect.top = -editRect.top; - AdjustWindowRectEx(&editRect, getStyle(t->edit), FALSE, getExStyle(t->edit)); - r = textRect; - InflateRect(&r, -editRect.left, -editRect.top); - } + // TODO + r = subitemLabel; // TODO check error or use the right function SetWindowPos(t->edit, NULL, From 4c107997d259a3a277f049e5e2e11cbbb73fdeed Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 18 Jun 2018 23:51:58 -0400 Subject: [PATCH 1220/1329] Round two: fully custom edit sizing code. We're getting somewhere now. Now we'll need to actually manage this thing =P --- windows/tableevents.cpp | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/windows/tableevents.cpp b/windows/tableevents.cpp index a73055c8..4441da2e 100644 --- a/windows/tableevents.cpp +++ b/windows/tableevents.cpp @@ -56,7 +56,6 @@ static HRESULT openEditControl(uiTable *t, int iItem, int iSubItem, uiprivTableC // images, so we will manually draw over the image area. // There's a second part to this; see below. subitemLabel.left = subitemBounds.left; -/* if ((p->imageModelColumn != -1 || p->checkboxModelColumn != -1) && iSubItem != 0) // Normally there's this many hard-coded logical units // of blank space, followed by the background, followed @@ -72,7 +71,7 @@ static HRESULT openEditControl(uiTable *t, int iItem, int iSubItem, uiprivTableC header = (HWND) SendMessageW(t->hwnd, LVM_GETHEADER, 0, 0); subitemLabel.left += SendMessageW(header, HDM_GETBITMAPMARGIN, 0, 0); } -*/ + // the real list view creates the edit control with the string data = (*(t->model->mh->CellValue))(t->model->mh, t->model, iItem, p->textModelColumn); wstr = toUTF16(uiTableDataString(data)); @@ -91,11 +90,38 @@ static HRESULT openEditControl(uiTable *t, int iItem, int iSubItem, uiprivTableC uiprivFree(wstr); return E_FAIL; } - uiprivFree(wstr); SendMessageW(t->edit, WM_SETFONT, (WPARAM) hMessageFont, (LPARAM) TRUE); - // TODO - r = subitemLabel; + // this is not how the real list view positions and sizes the edit control, but this is a) close enough b) a lot easier to follow c) something I can actually get working d) something I'm slightly more comfortable including in libui + { + HDC dc; + HFONT prevFont; + TEXTMETRICW tm; + SIZE textSize; + RECT editRect; + + // TODO deduplicate this with tabledraw.cpp + // TODO check errors for all these + dc = GetDC(t->hwnd); // yes, real list view uses itself here + prevFont = (HFONT) SelectObject(dc, hMessageFont); + GetTextMetricsW(dc, &tm); + GetTextExtentPoint32W(dc, wstr, wcslen(wstr), &textSize); + SelectObject(dc, prevFont); + ReleaseDC(t->hwnd, dc); + + SendMessageW(t->edit, EM_GETRECT, 0, (LPARAM) (&editRect)); + r.left = subitemLabel.left - editRect.left; + // find the top of the text + r.top = subitemLabel.top + ((subitemLabel.bottom - subitemLabel.top) - tm.tmHeight) / 2; + // and move THAT by the right offset + r.top = r.top - editRect.top; + r.right = subitemLabel.left + textSize.cx; + // the real listview does this to add some extra space at the end + // TODO this still isn't enough space + r.right += 4 * GetSystemMetrics(SM_CXEDGE) + GetSystemMetrics(SM_CYEDGE); + // and make the bottom equally positioned to the top + r.bottom = r.top + editRect.top + tm.tmHeight + editRect.top; + } // TODO check error or use the right function SetWindowPos(t->edit, NULL, @@ -105,6 +131,7 @@ static HRESULT openEditControl(uiTable *t, int iItem, int iSubItem, uiprivTableC // TODO get the correct constant from the real list view ShowWindow(t->edit, SW_SHOWDEFAULT); + uiprivFree(wstr); return S_OK; } From ff4b424ab001d77d4c5dec512f5b752111ba25aa Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 19 Jun 2018 07:51:34 -0400 Subject: [PATCH 1221/1329] Added WS_CLIPCHILDREN to uiTable to prevent drawing over children. --- windows/table.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/windows/table.cpp b/windows/table.cpp index 40716fc2..b3b397e8 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -394,9 +394,10 @@ uiTable *uiNewTable(uiTableModel *model) t->columns = new std::vector; t->model = model; + // WS_CLIPCHILDREN is here to prevent drawing over the edit box used for editing text t->hwnd = uiWindowsEnsureCreateControlHWND(WS_EX_CLIENTEDGE, WC_LISTVIEW, L"", - LVS_REPORT | LVS_OWNERDATA | LVS_SINGLESEL | WS_TABSTOP | WS_HSCROLL | WS_VSCROLL, + LVS_REPORT | LVS_OWNERDATA | LVS_SINGLESEL | WS_CLIPCHILDREN | WS_TABSTOP | WS_HSCROLL | WS_VSCROLL, hInstance, NULL, TRUE); model->tables->push_back(t); From 25a443f4f2d89c7dfea4ef244052d73dd04e91c8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 19 Jun 2018 23:07:24 -0400 Subject: [PATCH 1222/1329] Handled the finished-editing cases I can right now. It is... mostly good???????????? IListView is very tantalizing now... --- windows/table.cpp | 98 ++++++++++++++++++++++++++++++++++++++++- windows/table.hpp | 4 ++ windows/tableevents.cpp | 61 +++++++++++++++++++++++++ 3 files changed, 161 insertions(+), 2 deletions(-) diff --git a/windows/table.cpp b/windows/table.cpp index b3b397e8..5704b189 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -3,6 +3,7 @@ // general TODOs: // - tooltips don't work properly on columns with icons (the listview always thinks there's enough room for a short label because it's not taking the icon into account); is this a bug in our LVN_GETDISPINFO handler or something else? +// - should clicking on some other column of the same row, even one that doesn't edit, cancel editing? static uiTableTextColumnOptionalParams defaultTextColumnOptionalParams = { /*TODO.ColorModelColumn = */-1, @@ -83,8 +84,14 @@ void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) static LRESULT CALLBACK tableSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIDSubclass, DWORD_PTR dwRefData) { uiTable *t = (uiTable *) dwRefData; + NMHDR *nmhdr = (NMHDR *) lParam; + bool finishEdit, abortEdit; + HWND header; LRESULT lResult; + HRESULT hr; + finishEdit = false; + abortEdit = false; switch (uMsg) { case WM_TIMER: if (wParam == (WPARAM) (&(t->inDoubleClickTimer))) { @@ -107,11 +114,69 @@ static LRESULT CALLBACK tableSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lResult = DefSubclassProc(hwnd, uMsg, wParam, lParam); t->inLButtonDown = FALSE; return lResult; + case WM_COMMAND: + if (HIWORD(wParam) == EN_UPDATE) { + // the real list view resizes the edit control on this notification specifically + // TODO + } + // the real list view accepts changes in this case + if (HIWORD(wParam) == EN_KILLFOCUS) + finishEdit = true; + break; // don't override default handling + case WM_NOTIFY: + // list view accepts changes on column resize, but does not provide such notifications :/ + header = (HWND) SendMessageW(t->hwnd, LVM_GETHEADER, 0, 0); + if (nmhdr->hwndFrom == header) { + NMHEADERW *nm = (NMHEADERW *) nmhdr; + + switch (nmhdr->code) { + case HDN_ITEMCHANGED: + if ((nm->pitem->mask & HDI_WIDTH) == 0) + break; + // fall through + case HDN_DIVIDERDBLCLICK: + case HDN_TRACK: + case HDN_ENDTRACK: + finishEdit = true; + } + } + // I think this mirrors the WM_COMMAND one above... TODO + if (nmhdr->code == NM_KILLFOCUS) + finishEdit = true; + break; // don't override default handling + case LVM_CANCELEDITLABEL: + finishEdit = true; + // TODO properly imitate notifiactions + break; // don't override default handling + // TODO finish edit on WM_WINDOWPOSCHANGING and WM_SIZE? + // for the next three: this item is about to go away; don't bother keeping changes + case LVM_SETITEMCOUNT: + if (wParam <= t->editedItem) + abortEdit = true; + break; // don't override default handling + case LVM_DELETEITEM: + if (wParam == t->editedItem) + abortEdit = true; + break; // don't override default handling + case LVM_DELETEALLITEMS: + abortEdit = true; + break; // don't override default handling case WM_NCDESTROY: if (RemoveWindowSubclass(hwnd, tableSubProc, uIDSubclass) == FALSE) logLastError(L"RemoveWindowSubclass()"); // fall through } + if (finishEdit) { + hr = uiprivTableFinishEditingText(t); + if (hr != S_OK) { + // TODO + } + } else if (abortEdit) { + hr = uiprivTableAbortEditingText(t); + if (hr != S_OK) { + // TODO + } + } return DefSubclassProc(hwnd, uMsg, wParam, lParam); } @@ -157,6 +222,7 @@ int uiprivTableProgress(uiTable *t, int item, int subitem, int modelColumn, LONG return progress; } +// TODO properly integrate compound statements static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult) { uiTable *t = uiTable(c); @@ -208,12 +274,14 @@ static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult) { NMLISTVIEW *nm = (NMLISTVIEW *) nmhdr; UINT oldSelected, newSelected; + HRESULT hr; - if (!t->inLButtonDown) + // TODO clean up these if cases + if (!t->inLButtonDown && t->edit == NULL) return FALSE; oldSelected = nm->uOldState & LVIS_SELECTED; newSelected = nm->uNewState & LVIS_SELECTED; - if (oldSelected == 0 && newSelected != 0) { + if (t->inLButtonDown && oldSelected == 0 && newSelected != 0) { t->inDoubleClickTimer = TRUE; // TODO check error SetTimer(t->hwnd, (UINT_PTR) (&(t->inDoubleClickTimer)), @@ -221,8 +289,29 @@ static BOOL onWM_NOTIFY(uiControl *c, HWND hwnd, NMHDR *nmhdr, LRESULT *lResult) *lResult = 0; return TRUE; } + // the nm->iItem == -1 case is because that is used if "the change has been applied to all items in the list view" + if (t->edit != NULL && oldSelected != 0 && newSelected == 0 && (t->editedItem == nm->iItem || nm->iItem == -1)) { + // TODO see if the real list view accepts or rejects changes here; Windows Explorer accepts + hr = uiprivTableFinishEditingText(t); + if (hr != S_OK) { + // TODO + return FALSE; + } + *lResult = 0; + return TRUE; + } return FALSE; } + // the real list view accepts changes when scrolling or clicking column headers + case LVN_BEGINSCROLL: + case LVN_COLUMNCLICK: + hr = uiprivTableFinishEditingText(t); + if (hr != S_OK) { + // TODO + return FALSE; + } + *lResult = 0; + return TRUE; } return FALSE; } @@ -232,7 +321,12 @@ static void uiTableDestroy(uiControl *c) uiTable *t = uiTable(c); uiTableModel *model = t->model; std::vector::iterator it; + HRESULT hr; + hr = uiprivTableAbortEditingText(t); + if (hr != S_OK) { + // TODO + } uiWindowsUnregisterWM_NOTIFYHandler(t->hwnd); uiWindowsEnsureDestroyWindow(t->hwnd); // detach table from model diff --git a/windows/table.hpp b/windows/table.hpp index 6ab77d57..67b166f8 100644 --- a/windows/table.hpp +++ b/windows/table.hpp @@ -36,6 +36,8 @@ struct uiTable { BOOL inLButtonDown; BOOL inDoubleClickTimer; HWND edit; + int editedItem; + int editedSubitem; }; extern int uiprivTableProgress(uiTable *t, int item, int subitem, int modelColumn, LONG *pos); @@ -48,3 +50,5 @@ extern HRESULT uiprivUpdateImageListSize(uiTable *t); // tableevents.cpp extern HRESULT uiprivTableHandleNM_CLICK(uiTable *t, NMITEMACTIVATE *nm, LRESULT *lResult); +extern HRESULT uiprivTableFinishEditingText(uiTable *t); +extern HRESULT uiprivTableAbortEditingText(uiTable *t); diff --git a/windows/tableevents.cpp b/windows/tableevents.cpp index 4441da2e..d77e1ab6 100644 --- a/windows/tableevents.cpp +++ b/windows/tableevents.cpp @@ -17,6 +17,41 @@ static HRESULT itemRect(HRESULT hr, uiTable *t, UINT uMsg, WPARAM wParam, LONG l return S_OK; } +// the real list view intercepts these keys to control editing +static LRESULT CALLBACK editSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIDSubclass, DWORD_PTR dwRefData) +{ + uiTable *t = (uiTable *) dwRefData; + HRESULT hr; + + switch (uMsg) { + case WM_KEYDOWN: + switch (wParam) { + // TODO handle VK_TAB and VK_SHIFT+VK_TAB + case VK_RETURN: + hr = uiprivTableFinishEditingText(t); + if (hr != S_OK) { + // TODO + } + return 0; // yes, the real list view just returns here + case VK_ESCAPE: + hr = uiprivTableAbortEditingText(t); + if (hr != S_OK) { + // TODO + } + return 0; + } + break; + // the real list view also forces these flags + case WM_GETDLGCODE: + return DLGC_HASSETSEL | DLGC_WANTALLKEYS; + case WM_NCDESTROY: + if (RemoveWindowSubclass(hwnd, editSubProc, uIDSubclass) == FALSE) + logLastError(L"RemoveWindowSubclass()"); + // fall through + } + return DefSubclassProc(hwnd, uMsg, wParam, lParam); +} + static HRESULT openEditControl(uiTable *t, int iItem, int iSubItem, uiprivTableColumnParams *p) { RECT itemLabel; @@ -27,6 +62,11 @@ static HRESULT openEditControl(uiTable *t, int iItem, int iSubItem, uiprivTableC LONG xInflate, yInflate; HRESULT hr; + // the real list view accepts changes to the existing item when editing a new item + hr = uiprivTableFinishEditingText(t); + if (hr != S_OK) + return hr; + // compute this in advance so we don't have to needlessly call DestroyWindow() later // TODO deduplicate this code with tabledraw.cpp // TODO check LRESULT bad parameters here @@ -132,6 +172,27 @@ static HRESULT openEditControl(uiTable *t, int iItem, int iSubItem, uiprivTableC ShowWindow(t->edit, SW_SHOWDEFAULT); uiprivFree(wstr); + t->editedItem = iItem; + t->editedSubitem = iSubItem; + return S_OK; +} + +HRESULT uiprivTableFinishEditingText(uiTable *t) +{ + if (t->edit == NULL) + return S_OK; + return uiprivTableAbortEditingText(t); +} + +HRESULT uiprivTableAbortEditingText(uiTable *t) +{ + if (t->edit == NULL) + return S_OK; + if (DestroyWindow(t->edit) == 0) { + logLastError(L"DestroyWindow()"); + return E_FAIL; + } + t->edit = NULL; return S_OK; } From bff9d0e31144898ee5548a944890c359a82033b3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 20 Jun 2018 10:39:27 -0400 Subject: [PATCH 1223/1329] More work on table edit controls, including: subclassing the edit control to actually handle escape and enter, setting focus on the eidt control, selecting all text in the edit control, and splitting the sizing stuff into a separate function. We'll have to split the rect-gathering code into a separate file before we can add live resize to the edit control... which will probably be useful because then I could just write a function to enumerate focus rects later. --- windows/tableevents.cpp | 89 +++++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 38 deletions(-) diff --git a/windows/tableevents.cpp b/windows/tableevents.cpp index d77e1ab6..b8582fba 100644 --- a/windows/tableevents.cpp +++ b/windows/tableevents.cpp @@ -17,6 +17,47 @@ static HRESULT itemRect(HRESULT hr, uiTable *t, UINT uMsg, WPARAM wParam, LONG l return S_OK; } +// this is not how the real list view positions and sizes the edit control, but this is a) close enough b) a lot easier to follow c) something I can actually get working d) something I'm slightly more comfortable including in libui +// r should be the subitem label rect +static HRESULT resizeEdit(uiTable *t, WCHAR *wstr, RECT *r) +{ + HDC dc; + HFONT prevFont; + TEXTMETRICW tm; + SIZE textSize; + RECT editRect; + + // TODO check errors for all these + dc = GetDC(t->hwnd); // use the list view DC since we're using its coordinate space + prevFont = (HFONT) SelectObject(dc, hMessageFont); + GetTextMetricsW(dc, &tm); + GetTextExtentPoint32W(dc, wstr, wcslen(wstr), &textSize); + SelectObject(dc, prevFont); + ReleaseDC(t->hwnd, dc); + + SendMessageW(t->edit, EM_GETRECT, 0, (LPARAM) (&editRect)); + r->left -= editRect.left; + // find the top of the text + r->top += ((r->bottom - r->top) - tm.tmHeight) / 2; + // and move THAT by the right offset + r->top -= editRect.top; + r->right = r->left + textSize.cx; + // the real listview does this to add some extra space at the end + // TODO this still isn't enough space + r->right += 4 * GetSystemMetrics(SM_CXEDGE) + GetSystemMetrics(SM_CYEDGE); + // and make the bottom equally positioned to the top + r->bottom = r->top + editRect.top + tm.tmHeight + editRect.top; + + // TODO intersect r with the list view's client rect to prevent clipping + + // TODO check error or use the right function + SetWindowPos(t->edit, NULL, + r->left, r->top, + r->right - r->left, r->bottom - r->top, + SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER); + return S_OK; +} + // the real list view intercepts these keys to control editing static LRESULT CALLBACK editSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIDSubclass, DWORD_PTR dwRefData) { @@ -131,45 +172,17 @@ static HRESULT openEditControl(uiTable *t, int iItem, int iSubItem, uiprivTableC return E_FAIL; } SendMessageW(t->edit, WM_SETFONT, (WPARAM) hMessageFont, (LPARAM) TRUE); + // TODO check errors + SetWindowSubclass(t->edit, editSubProc, 0, (DWORD_PTR) t); - // this is not how the real list view positions and sizes the edit control, but this is a) close enough b) a lot easier to follow c) something I can actually get working d) something I'm slightly more comfortable including in libui - { - HDC dc; - HFONT prevFont; - TEXTMETRICW tm; - SIZE textSize; - RECT editRect; - - // TODO deduplicate this with tabledraw.cpp - // TODO check errors for all these - dc = GetDC(t->hwnd); // yes, real list view uses itself here - prevFont = (HFONT) SelectObject(dc, hMessageFont); - GetTextMetricsW(dc, &tm); - GetTextExtentPoint32W(dc, wstr, wcslen(wstr), &textSize); - SelectObject(dc, prevFont); - ReleaseDC(t->hwnd, dc); - - SendMessageW(t->edit, EM_GETRECT, 0, (LPARAM) (&editRect)); - r.left = subitemLabel.left - editRect.left; - // find the top of the text - r.top = subitemLabel.top + ((subitemLabel.bottom - subitemLabel.top) - tm.tmHeight) / 2; - // and move THAT by the right offset - r.top = r.top - editRect.top; - r.right = subitemLabel.left + textSize.cx; - // the real listview does this to add some extra space at the end - // TODO this still isn't enough space - r.right += 4 * GetSystemMetrics(SM_CXEDGE) + GetSystemMetrics(SM_CYEDGE); - // and make the bottom equally positioned to the top - r.bottom = r.top + editRect.top + tm.tmHeight + editRect.top; - } - - // TODO check error or use the right function - SetWindowPos(t->edit, NULL, - r.left, r.top, - r.right - r.left, r.bottom - r.top, - SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER); - // TODO get the correct constant from the real list view - ShowWindow(t->edit, SW_SHOWDEFAULT); + hr = resizeEdit(t, wstr, &subitemLabel); + if (hr != S_OK) + // TODO proper cleanup + return hr; + // TODO check errors on these two, if any + SetFocus(t->edit); + ShowWindow(t->edit, SW_SHOW); + SendMessageW(t->edit, EM_SETSEL, 0, (LPARAM) (-1)); uiprivFree(wstr); t->editedItem = iItem; From df59eee7834451bbc8762cea8b5c107e1b028e5b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 20 Jun 2018 10:45:14 -0400 Subject: [PATCH 1224/1329] Renamed tableevents.cpp to the more accurate tableediting.cpp. --- windows/CMakeLists.txt | 2 +- windows/table.hpp | 2 +- windows/{tableevents.cpp => tableediting.cpp} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename windows/{tableevents.cpp => tableediting.cpp} (100%) diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index c1747c50..bec8a2f6 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -53,7 +53,7 @@ list(APPEND _LIBUI_SOURCES windows/table.cpp windows/tabledispinfo.cpp windows/tabledraw.cpp - windows/tableevents.cpp + windows/tableediting.cpp windows/tabpage.cpp windows/text.cpp windows/utf16.cpp diff --git a/windows/table.hpp b/windows/table.hpp index 67b166f8..2a210a8c 100644 --- a/windows/table.hpp +++ b/windows/table.hpp @@ -48,7 +48,7 @@ extern HRESULT uiprivTableHandleLVN_GETDISPINFO(uiTable *t, NMLVDISPINFOW *nm, L extern HRESULT uiprivTableHandleNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm, LRESULT *lResult); extern HRESULT uiprivUpdateImageListSize(uiTable *t); -// tableevents.cpp +// tableediting.cpp extern HRESULT uiprivTableHandleNM_CLICK(uiTable *t, NMITEMACTIVATE *nm, LRESULT *lResult); extern HRESULT uiprivTableFinishEditingText(uiTable *t); extern HRESULT uiprivTableAbortEditingText(uiTable *t); diff --git a/windows/tableevents.cpp b/windows/tableediting.cpp similarity index 100% rename from windows/tableevents.cpp rename to windows/tableediting.cpp From ec07b12295b8024b2036d4c1d8dd6209f42b7ebf Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 20 Jun 2018 11:05:34 -0400 Subject: [PATCH 1225/1329] Split table cell metrics into its own file. We still need to actually integrate this with everything. --- windows/tablemetrics.cpp | 135 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 windows/tablemetrics.cpp diff --git a/windows/tablemetrics.cpp b/windows/tablemetrics.cpp new file mode 100644 index 00000000..76c879c7 --- /dev/null +++ b/windows/tablemetrics.cpp @@ -0,0 +1,135 @@ +// 14 june 2018 +#include "uipriv_windows.hpp" +#include "table.hpp" + +struct uiprivTableMetrics { + uiTable *t; + uiTableModel *m; + uiprivTableColumnParams *p; + + int iItem; + int iSubItem; + BOOL hasText; + BOOL hasImage; + BOOL focused; + BOOL selected; + + RECT itemBounds; + RECT itemIcon; + RECT itemLabel; + RECT subitemBounds; + RECT subitemIcon; + RECT subitemLabel; + + LRESULT bitmapMargin; + int cxIcon; + int cyIcon; + + RECT realTextBackground; + RECT realTextRect; +}; + +static HRESULT itemRect(HRESULT hr, uiTable *t, UINT uMsg, WPARAM wParam, LONG left, LONG top, LRESULT bad, RECT *r) +{ + if (hr != S_OK) + return hr; + ZeroMemory(r, sizeof (RECT)); + r->left = left; + r->top = top; + if (SendMessageW(t->hwnd, uMsg, wParam, (LPARAM) r) == bad) { + logLastError(L"itemRect() message"); + return E_FAIL; + } + return S_OK; +} + +HRESULT uiprivTableGetMetrics(uiTable *t, int iItem, int iSubItem, uiprivTableMetrics **mout) +{ + uiprivTableMetrics *m; + LRESULT state; + HWND header; + RECT r; + HRESULT hr; + + if (mout == NULL) + return E_POINTER; + + m = uiprivNew(uiprivTableMetrics); + m->t = t; + m->m = t->model; + m->p = (*(t->columns))[iSubItem]; + + m->iItem = iItem; + m->iSubItem = iSubItem; + m->hasText = m->p->textModelColumn != -1; + m->hasImage = (m->p->imageModelColumn != -1) || (m->p->checkboxModelColumn != -1); + state = SendMessageW(t->hwnd, LVM_GETITEMSTATE, iItem, LVIS_FOCUSED | LVIS_SELECTED); + m->focused = (state & LVIS_FOCUSED) != 0; + m->selected = (state & LVIS_SELECTED) != 0; + + // TODO check LRESULT bad parameters here + hr = itemRect(S_OK, t, LVM_GETITEMRECT, s->iItem, + LVIR_BOUNDS, 0, FALSE, &(m->itemBounds)); + hr = itemRect(hr, t, LVM_GETITEMRECT, s->iItem, + LVIR_ICON, 0, FALSE, &(m->itemIcon)); + hr = itemRect(hr, t, LVM_GETITEMRECT, s->iItem, + LVIR_LABEL, 0, FALSE, &(m->itemLabel)); + hr = itemRect(hr, t, LVM_GETSUBITEMRECT, s->iItem, + LVIR_BOUNDS, s->iSubItem, 0, &(m->subitemBounds)); + hr = itemRect(hr, t, LVM_GETSUBITEMRECT, s->iItem, + LVIR_ICON, s->iSubItem, 0, &(m->subitemIcon)); + if (hr != S_OK) + goto fail; + // LVM_GETSUBITEMRECT treats LVIR_LABEL as the same as + // LVIR_BOUNDS, so we can't use that directly. Instead, let's + // assume the text is immediately after the icon. The correct + // rect will be determined by + // computeOtherRectsAndDrawBackgrounds() above. + m->subitemLabel = m->subitemBounds; + m->subitemLabel.left = m->subitemIcon.right; + // And on iSubItem == 0, LVIF_GETSUBITEMRECT still includes + // all the subitems, which we don't want. + if (iSubItem == 0) { + m->subitemBounds.right = m->itemLabel.right; + m->subitemLabel.right = m->itemLabel.right; + } + + header = (HWND) SendMessageW(t->hwnd, LVM_GETHEADER, 0, 0); + m->bitmapMargin = SendMessageW(header, HDM_GETBITMAPMARGIN, 0, 0); + if (ImageList_GetIconSize(t->imagelist, &(m->cxIcon), &(m->cyIcon)) == 0) { + logLastError(L"ImageList_GetIconSize()"); + hr = E_FAIL; + goto fail; + } + + r = m->subitemLabel; + if (!m->hasText && !m->hasImage) + r = s->subitemBounds; + else if (!m->hasImage && iSubItem != 0) + // By default, this will include images; we're not drawing + // images, so we will manually draw over the image area. + // There's a second part to this; see below. + r.left = m->subitemBounds.left; + m->realTextBackground = r; + + m->realTextRect = r; + // TODO confirm whether this really happens on column 0 as well + if (m->hasImage && iSubItem != 0) + // Normally there's this many hard-coded logical units + // of blank space, followed by the background, followed + // by a bitmap margin's worth of space. This looks bad, + // so we overrule that to start the background immediately + // and the text after the hard-coded amount. + m->realTextRect.left += 2; + else if (iSubItem != 0) + // In the case of subitem text without an image, we draw + // text one bitmap margin away from the left edge. + m->realTextRect.left += m->bitmapMargin; + + *mout = m; + return S_OK; +fail: + uiprivFree(m); + *mout = NULL; + return hr; +} From 5ae45a1fcb8e7e6832882dcc72b1354cd8ec53b2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 20 Jun 2018 18:03:56 -0400 Subject: [PATCH 1226/1329] Integrated tablemetrics.cpp with tabledraw.cpp. --- windows/CMakeLists.txt | 1 + windows/table.hpp | 25 ++++++ windows/tabledraw.cpp | 167 ++++++++++----------------------------- windows/tablemetrics.cpp | 54 +++---------- 4 files changed, 78 insertions(+), 169 deletions(-) diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index bec8a2f6..ac9af411 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -54,6 +54,7 @@ list(APPEND _LIBUI_SOURCES windows/tabledispinfo.cpp windows/tabledraw.cpp windows/tableediting.cpp + windows/tablemetrics.cpp windows/tabpage.cpp windows/text.cpp windows/utf16.cpp diff --git a/windows/table.hpp b/windows/table.hpp index 2a210a8c..c1de0393 100644 --- a/windows/table.hpp +++ b/windows/table.hpp @@ -34,6 +34,7 @@ struct uiTable { // TODO document all this std::map, LONG> *indeterminatePositions; BOOL inLButtonDown; + // TODO is this even necessary? it seems NM_CLICK is not sent if NM_DBLCLICK or LVN_ITEMACTIVATE (one of the two) happens... BOOL inDoubleClickTimer; HWND edit; int editedItem; @@ -52,3 +53,27 @@ extern HRESULT uiprivUpdateImageListSize(uiTable *t); extern HRESULT uiprivTableHandleNM_CLICK(uiTable *t, NMITEMACTIVATE *nm, LRESULT *lResult); extern HRESULT uiprivTableFinishEditingText(uiTable *t); extern HRESULT uiprivTableAbortEditingText(uiTable *t); + +// tablemetrics.cpp +typedef struct uiprivTableMetrics uiprivTableMetrics; +struct uiprivTableMetrics { + BOOL hasText; + BOOL hasImage; + BOOL focused; + BOOL selected; + + RECT itemBounds; + RECT itemIcon; + RECT itemLabel; + RECT subitemBounds; + RECT subitemIcon; + RECT subitemLabel; + + LRESULT bitmapMargin; + int cxIcon; + int cyIcon; + + RECT realTextBackground; + RECT realTextRect; +}; +extern HRESULT uiprivTableGetMetrics(uiTable *t, int iItem, int iSubItem, uiprivTableMetrics **mout); diff --git a/windows/tabledraw.cpp b/windows/tabledraw.cpp index ca1982f8..64044455 100644 --- a/windows/tabledraw.cpp +++ b/windows/tabledraw.cpp @@ -2,25 +2,19 @@ #include "uipriv_windows.hpp" #include "table.hpp" +// TODO +#define cellValue(model, row, column) ((*((model)->mh->CellValue))((model)->mh, (model), (row), (column))) + struct drawState { uiTable *t; - uiTableModel *m; + uiTableModel *model; uiprivTableColumnParams *p; HDC dc; int iItem; int iSubItem; - BOOL hasText; - BOOL hasImage; - BOOL selected; - BOOL focused; - RECT itemBounds; - RECT itemIcon; - RECT itemLabel; - RECT subitemBounds; - RECT subitemIcon; - RECT subitemLabel; + uiprivTableMetrics *m; COLORREF bgColor; HBRUSH bgBrush; @@ -28,52 +22,19 @@ struct drawState { COLORREF textColor; HBRUSH textBrush; BOOL freeTextBrush; - - LRESULT bitmapMargin; - int cxIcon; - int cyIcon; - - RECT realTextRect; - RECT focusRect; }; -static HRESULT computeOtherRectsAndDrawBackgrounds(struct drawState *s) +static HRESULT drawBackgrounds(struct drawState *s) { - RECT r; - - r = s->subitemLabel; - if (!s->hasText && !s->hasImage) - r = s->subitemBounds; - else if (!s->hasImage && s->iSubItem != 0) - // By default, this will include images; we're not drawing - // images, so we will manually draw over the image area. - // There's a second part to this; see below. - r.left = s->subitemBounds.left; - - if (s->hasImage) - if (FillRect(s->dc, &(s->subitemIcon), GetSysColorBrush(COLOR_WINDOW)) == 0) { + if (s->m->hasImage) + if (FillRect(s->dc, &(s->m->subitemIcon), GetSysColorBrush(COLOR_WINDOW)) == 0) { logLastError(L"FillRect() icon"); return E_FAIL; } - if (FillRect(s->dc, &r, s->bgBrush) == 0) { + if (FillRect(s->dc, &(s->m->realTextBackground), s->bgBrush) == 0) { logLastError(L"FillRect()"); return E_FAIL; } - UnionRect(&(s->focusRect), &(s->focusRect), &r); - - s->realTextRect = r; - // TODO confirm whether this really happens on column 0 as well - if (s->hasImage && s->iSubItem != 0) - // Normally there's this many hard-coded logical units - // of blank space, followed by the background, followed - // by a bitmap margin's worth of space. This looks bad, - // so we overrule that to start the background immediately - // and the text after the hard-coded amount. - s->realTextRect.left += 2; - else if (s->iSubItem != 0) - // In the case of subitem text without an image, we draw - // text one bitmap margin away from the left edge. - s->realTextRect.left += s->bitmapMargin; return S_OK; } @@ -110,11 +71,11 @@ static HRESULT drawImagePart(struct drawState *s) if (s->p->imageModelColumn == -1) return S_OK; - data = (*(s->m->mh->CellValue))(s->m->mh, s->m, s->iItem, s->p->imageModelColumn); + data = cellValue(s->model, s->iItem, s->p->imageModelColumn); wb = uiprivImageAppropriateForDC(uiTableDataImage(data), s->dc); uiFreeTableData(data); - hr = uiprivWICToGDI(wb, s->dc, s->cxIcon, s->cyIcon, &b); + hr = uiprivWICToGDI(wb, s->dc, s->m->cxIcon, s->m->cyIcon, &b); if (hr != S_OK) return hr; // TODO rewrite this condition to make more sense; possibly swap the if and else blocks too @@ -132,12 +93,12 @@ static HRESULT drawImagePart(struct drawState *s) // TODO error check DeleteObject(b); - r = s->subitemIcon; - r.right = r.left + s->cxIcon; - r.bottom = r.top + s->cyIcon; - centerImageRect(&r, &(s->subitemIcon)); + r = s->m->subitemIcon; + r.right = r.left + s->m->cxIcon; + r.bottom = r.top + s->m->cyIcon; + centerImageRect(&r, &(s->m->subitemIcon)); fStyle = ILD_NORMAL; - if (s->selected) + if (s->m->selected) fStyle = ILD_SELECTED; if (ImageList_Draw(s->t->imagelist, 0, s->dc, r.left, r.top, fStyle) == 0) { @@ -156,7 +117,7 @@ static HRESULT drawUnthemedCheckbox(struct drawState *s, int checked, int enable RECT r; UINT state; - r = s->subitemIcon; + r = s->m->subitemIcon; // this is what the actual list view LVS_EX_CHECKBOXES code does to size the checkboxes // TODO reverify the initial size r.right = r.left + GetSystemMetrics(SM_CXSMICON); @@ -168,7 +129,7 @@ static HRESULT drawUnthemedCheckbox(struct drawState *s, int checked, int enable r.right++; r.bottom++; - centerImageRect(&r, &(s->subitemIcon)); + centerImageRect(&r, &(s->m->subitemIcon)); state = DFCS_BUTTONCHECK | DFCS_FLAT; if (checked) state |= DFCS_CHECKED; @@ -195,11 +156,11 @@ static HRESULT drawThemedCheckbox(struct drawState *s, HTHEME theme, int checked logHRESULT(L"GetThemePartSize()", hr); return hr; // TODO fall back? } - r = s->subitemIcon; + r = s->m->subitemIcon; r.right = r.left + size.cx; r.bottom = r.top + size.cy; - centerImageRect(&r, &(s->subitemIcon)); + centerImageRect(&r, &(s->m->subitemIcon)); if (!checked && enabled) state = CBS_UNCHECKEDNORMAL; else if (checked && enabled) @@ -228,7 +189,7 @@ static HRESULT drawCheckboxPart(struct drawState *s) if (s->p->checkboxModelColumn == -1) return S_OK; - data = (*(s->m->mh->CellValue))(s->m->mh, s->m, s->iItem, s->p->checkboxModelColumn); + data = cellValue(s->model, s->iItem, s->p->checkboxModelColumn); checked = uiTableDataInt(data); uiFreeTableData(data); switch (s->p->checkboxEditableColumn) { @@ -239,7 +200,7 @@ static HRESULT drawCheckboxPart(struct drawState *s) enabled = 1; break; default: - data = (*(s->m->mh->CellValue))(s->m->mh, s->m, s->iItem, s->p->checkboxEditableColumn); + data = cellValue(s->model, s->iItem, s->p->checkboxEditableColumn); enabled = uiTableDataInt(data); uiFreeTableData(data); } @@ -270,7 +231,7 @@ static HRESULT drawTextPart(struct drawState *s) uiTableData *data; WCHAR *wstr; - if (!s->hasText) + if (!s->m->hasText) return S_OK; prevText = SetTextColor(s->dc, s->textColor); @@ -284,14 +245,14 @@ static HRESULT drawTextPart(struct drawState *s) return E_FAIL; } - data = (*(s->m->mh->CellValue))(s->m->mh, s->m, s->iItem, s->p->textModelColumn); + data = cellValue(s->model, s->iItem, s->p->textModelColumn); wstr = toUTF16(uiTableDataString(data)); uiFreeTableData(data); // These flags are a menagerie of flags from various sources: // guessing, the Windows 2000 source leak, various custom // draw examples on the web, etc. // TODO find the real correct flags - if (DrawTextW(s->dc, wstr, -1, &(s->realTextRect), DT_LEFT | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE | DT_NOPREFIX | DT_EDITCONTROL) == 0) { + if (DrawTextW(s->dc, wstr, -1, &(s->m->realTextRect), DT_LEFT | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE | DT_NOPREFIX | DT_EDITCONTROL) == 0) { uiprivFree(wstr); logLastError(L"DrawTextW()"); return E_FAIL; @@ -337,7 +298,7 @@ static HRESULT drawProgressBarPart(struct drawState *s) hr = E_FAIL; goto fail; } - r = s->subitemBounds; + r = s->m->subitemBounds; // this sets the height of the progressbar and vertically centers it in one fell swoop r.top += (r.bottom - tm.tmHeight - r.top) / 2; r.bottom = r.top + tm.tmHeight; @@ -367,7 +328,7 @@ static HRESULT drawProgressBarPart(struct drawState *s) HBRUSH brush, prevBrush; sysColor = COLOR_HIGHLIGHT; - if (s->selected) + if (s->m->selected) sysColor = COLOR_HIGHLIGHTTEXT; // TODO check errors everywhere @@ -438,7 +399,7 @@ static HRESULT drawButtonPart(struct drawState *s) if (s->p->buttonModelColumn == -1) return S_OK; - data = (*(s->m->mh->CellValue))(s->m->mh, s->m, s->iItem, s->p->buttonModelColumn); + data = cellValue(s->model, s->iItem, s->p->buttonModelColumn); wstr = toUTF16(uiTableDataString(data)); uiFreeTableData(data); switch (s->p->buttonClickableModelColumn) { @@ -449,7 +410,7 @@ static HRESULT drawButtonPart(struct drawState *s) enabled = 1; break; default: - data = (*(s->m->mh->CellValue))(s->m->mh, s->m, s->iItem, s->p->checkboxEditableColumn); + data = cellValue(s->model, s->iItem, s->p->checkboxEditableColumn); enabled = uiTableDataInt(data); uiFreeTableData(data); } @@ -461,7 +422,7 @@ static HRESULT drawButtonPart(struct drawState *s) hr = E_FAIL; goto fail; } - r = s->subitemBounds; + r = s->m->subitemBounds; if (theme != NULL) { int state; @@ -534,6 +495,10 @@ static HRESULT freeDrawState(struct drawState *s) HRESULT hr, hrret; hrret = S_OK; + if (s->m != NULL) { + uiprivFree(s->m); + s->m = NULL; + } if (s->freeTextBrush) { if (DeleteObject(s->textBrush) == 0) { logLastError(L"DeleteObject()"); @@ -553,20 +518,6 @@ static HRESULT freeDrawState(struct drawState *s) return hrret; } -static HRESULT itemRect(HRESULT hr, uiTable *t, UINT uMsg, WPARAM wParam, LONG left, LONG top, LRESULT bad, RECT *r) -{ - if (hr != S_OK) - return hr; - ZeroMemory(r, sizeof (RECT)); - r->left = left; - r->top = top; - if (SendMessageW(t->hwnd, uMsg, wParam, (LPARAM) r) == bad) { - logLastError(L"itemRect() message"); - return E_FAIL; - } - return S_OK; -} - static COLORREF blend(COLORREF base, double r, double g, double b, double a) { double br, bg, bb; @@ -591,48 +542,18 @@ static HRESULT fillDrawState(struct drawState *s, uiTable *t, NMLVCUSTOMDRAW *nm ZeroMemory(s, sizeof (struct drawState)); s->t = t; - s->m = t->model; + s->model = t->model; s->p = p; s->dc = nm->nmcd.hdc; s->iItem = nm->nmcd.dwItemSpec; s->iSubItem = nm->iSubItem; - s->hasText = p->textModelColumn != -1; - s->hasImage = (p->imageModelColumn != -1) || (p->checkboxModelColumn != -1); - // nm->nmcd.uItemState CDIS_SELECTED is unreliable for the - // listview configuration we have, so we must do this. - state = SendMessageW(t->hwnd, LVM_GETITEMSTATE, nm->nmcd.dwItemSpec, LVIS_SELECTED); - s->selected = (state & LVIS_SELECTED) != 0; - s->focused = (nm->nmcd.uItemState & CDIS_FOCUS) != 0; - // TODO check LRESULT bad parameters here - hr = itemRect(S_OK, t, LVM_GETITEMRECT, s->iItem, - LVIR_BOUNDS, 0, FALSE, &(s->itemBounds)); - hr = itemRect(hr, t, LVM_GETITEMRECT, s->iItem, - LVIR_ICON, 0, FALSE, &(s->itemIcon)); - hr = itemRect(hr, t, LVM_GETITEMRECT, s->iItem, - LVIR_LABEL, 0, FALSE, &(s->itemLabel)); - hr = itemRect(hr, t, LVM_GETSUBITEMRECT, s->iItem, - LVIR_BOUNDS, s->iSubItem, 0, &(s->subitemBounds)); - hr = itemRect(hr, t, LVM_GETSUBITEMRECT, s->iItem, - LVIR_ICON, s->iSubItem, 0, &(s->subitemIcon)); + hr = uiprivTableGetMetrics(t, s->iItem, s->iSubItem, &(s->m)); if (hr != S_OK) goto fail; - // LVM_GETSUBITEMRECT treats LVIR_LABEL as the same as - // LVIR_BOUNDS, so we can't use that directly. Instead, let's - // assume the text is immediately after the icon. The correct - // rect will be determined by - // computeOtherRectsAndDrawBackgrounds() above. - s->subitemLabel = s->subitemBounds; - s->subitemLabel.left = s->subitemIcon.right; - // And on iSubItem == 0, LVIF_GETSUBITEMRECT still includes - // all the subitems, which we don't want. - if (s->iSubItem == 0) { - s->subitemBounds.right = s->itemLabel.right; - s->subitemLabel.right = s->itemLabel.right; - } - if (s->selected) { + if (s->m->selected) { s->bgColor = GetSysColor(COLOR_HIGHLIGHT); s->bgBrush = GetSysColorBrush(COLOR_HIGHLIGHT); s->textColor = GetSysColor(COLOR_HIGHLIGHTTEXT); @@ -644,7 +565,7 @@ static HRESULT fillDrawState(struct drawState *s, uiTable *t, NMLVCUSTOMDRAW *nm s->bgColor = GetSysColor(COLOR_WINDOW); s->bgBrush = GetSysColorBrush(COLOR_WINDOW); if (t->backgroundColumn != -1) { - data = (*(s->m->mh->CellValue))(s->m->mh, s->m, s->iItem, t->backgroundColumn); + data = cellValue(s->model, s->iItem, t->backgroundColumn); if (data != NULL) { uiTableDataColor(data, &r, &g, &b, &a); uiFreeTableData(data); @@ -661,7 +582,7 @@ static HRESULT fillDrawState(struct drawState *s, uiTable *t, NMLVCUSTOMDRAW *nm s->textColor = GetSysColor(COLOR_WINDOWTEXT); s->textBrush = GetSysColorBrush(COLOR_WINDOWTEXT); if (p->textParams.ColorModelColumn != -1) { - data = (*(s->m->mh->CellValue))(s->m->mh, s->m, s->iItem, p->textParams.ColorModelColumn); + data = cellValue(s->model, s->iItem, p->textParams.ColorModelColumn); if (data != NULL) { uiTableDataColor(data, &r, &g, &b, &a); uiFreeTableData(data); @@ -677,14 +598,6 @@ static HRESULT fillDrawState(struct drawState *s, uiTable *t, NMLVCUSTOMDRAW *nm } } - header = (HWND) SendMessageW(t->hwnd, LVM_GETHEADER, 0, 0); - s->bitmapMargin = SendMessageW(header, HDM_GETBITMAPMARGIN, 0, 0); - if (ImageList_GetIconSize(t->imagelist, &(s->cxIcon), &(s->cyIcon)) == 0) { - logLastError(L"ImageList_GetIconSize()"); - hr = E_FAIL; - goto fail; - } - return S_OK; fail: // ignore the error; we need to return the one we got above @@ -716,7 +629,7 @@ HRESULT uiprivTableHandleNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm, LRESULT * hr = fillDrawState(&s, t, nm, p); if (hr != S_OK) return hr; - hr = computeOtherRectsAndDrawBackgrounds(&s); + hr = drawBackgrounds(&s); if (hr != S_OK) goto fail; hr = drawImagePart(&s); diff --git a/windows/tablemetrics.cpp b/windows/tablemetrics.cpp index 76c879c7..64236b9f 100644 --- a/windows/tablemetrics.cpp +++ b/windows/tablemetrics.cpp @@ -2,33 +2,6 @@ #include "uipriv_windows.hpp" #include "table.hpp" -struct uiprivTableMetrics { - uiTable *t; - uiTableModel *m; - uiprivTableColumnParams *p; - - int iItem; - int iSubItem; - BOOL hasText; - BOOL hasImage; - BOOL focused; - BOOL selected; - - RECT itemBounds; - RECT itemIcon; - RECT itemLabel; - RECT subitemBounds; - RECT subitemIcon; - RECT subitemLabel; - - LRESULT bitmapMargin; - int cxIcon; - int cyIcon; - - RECT realTextBackground; - RECT realTextRect; -}; - static HRESULT itemRect(HRESULT hr, uiTable *t, UINT uMsg, WPARAM wParam, LONG left, LONG top, LRESULT bad, RECT *r) { if (hr != S_OK) @@ -46,6 +19,7 @@ static HRESULT itemRect(HRESULT hr, uiTable *t, UINT uMsg, WPARAM wParam, LONG l HRESULT uiprivTableGetMetrics(uiTable *t, int iItem, int iSubItem, uiprivTableMetrics **mout) { uiprivTableMetrics *m; + uiprivTableColumnParams *p; LRESULT state; HWND header; RECT r; @@ -55,29 +29,25 @@ HRESULT uiprivTableGetMetrics(uiTable *t, int iItem, int iSubItem, uiprivTableMe return E_POINTER; m = uiprivNew(uiprivTableMetrics); - m->t = t; - m->m = t->model; - m->p = (*(t->columns))[iSubItem]; - m->iItem = iItem; - m->iSubItem = iSubItem; - m->hasText = m->p->textModelColumn != -1; - m->hasImage = (m->p->imageModelColumn != -1) || (m->p->checkboxModelColumn != -1); + p = (*(t->columns))[iSubItem]; + m->hasText = p->textModelColumn != -1; + m->hasImage = (p->imageModelColumn != -1) || (p->checkboxModelColumn != -1); state = SendMessageW(t->hwnd, LVM_GETITEMSTATE, iItem, LVIS_FOCUSED | LVIS_SELECTED); m->focused = (state & LVIS_FOCUSED) != 0; m->selected = (state & LVIS_SELECTED) != 0; // TODO check LRESULT bad parameters here - hr = itemRect(S_OK, t, LVM_GETITEMRECT, s->iItem, + hr = itemRect(S_OK, t, LVM_GETITEMRECT, iItem, LVIR_BOUNDS, 0, FALSE, &(m->itemBounds)); - hr = itemRect(hr, t, LVM_GETITEMRECT, s->iItem, + hr = itemRect(hr, t, LVM_GETITEMRECT, iItem, LVIR_ICON, 0, FALSE, &(m->itemIcon)); - hr = itemRect(hr, t, LVM_GETITEMRECT, s->iItem, + hr = itemRect(hr, t, LVM_GETITEMRECT, iItem, LVIR_LABEL, 0, FALSE, &(m->itemLabel)); - hr = itemRect(hr, t, LVM_GETSUBITEMRECT, s->iItem, - LVIR_BOUNDS, s->iSubItem, 0, &(m->subitemBounds)); - hr = itemRect(hr, t, LVM_GETSUBITEMRECT, s->iItem, - LVIR_ICON, s->iSubItem, 0, &(m->subitemIcon)); + hr = itemRect(hr, t, LVM_GETSUBITEMRECT, iItem, + LVIR_BOUNDS, iSubItem, 0, &(m->subitemBounds)); + hr = itemRect(hr, t, LVM_GETSUBITEMRECT, iItem, + LVIR_ICON, iSubItem, 0, &(m->subitemIcon)); if (hr != S_OK) goto fail; // LVM_GETSUBITEMRECT treats LVIR_LABEL as the same as @@ -104,7 +74,7 @@ HRESULT uiprivTableGetMetrics(uiTable *t, int iItem, int iSubItem, uiprivTableMe r = m->subitemLabel; if (!m->hasText && !m->hasImage) - r = s->subitemBounds; + r = m->subitemBounds; else if (!m->hasImage && iSubItem != 0) // By default, this will include images; we're not drawing // images, so we will manually draw over the image area. From f180423096242aa00a1d088eb0b492d307523ee3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 20 Jun 2018 18:47:55 -0400 Subject: [PATCH 1227/1329] Integrated tablemetrics.cpp into tableediting.cpp and added resize-on-type. --- windows/table.cpp | 6 ++- windows/table.hpp | 1 + windows/tableediting.cpp | 107 ++++++++++++--------------------------- 3 files changed, 38 insertions(+), 76 deletions(-) diff --git a/windows/table.cpp b/windows/table.cpp index 5704b189..a070c82b 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -117,7 +117,11 @@ static LRESULT CALLBACK tableSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM case WM_COMMAND: if (HIWORD(wParam) == EN_UPDATE) { // the real list view resizes the edit control on this notification specifically - // TODO + hr = uiprivTableResizeWhileEditing(t); + if (hr != S_OK) { + // TODO + } + break; } // the real list view accepts changes in this case if (HIWORD(wParam) == EN_KILLFOCUS) diff --git a/windows/table.hpp b/windows/table.hpp index c1de0393..e997395f 100644 --- a/windows/table.hpp +++ b/windows/table.hpp @@ -50,6 +50,7 @@ extern HRESULT uiprivTableHandleNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm, LR extern HRESULT uiprivUpdateImageListSize(uiTable *t); // tableediting.cpp +extern HRESULT uiprivTableResizeWhileEditing(uiTable *t); extern HRESULT uiprivTableHandleNM_CLICK(uiTable *t, NMITEMACTIVATE *nm, LRESULT *lResult); extern HRESULT uiprivTableFinishEditingText(uiTable *t); extern HRESULT uiprivTableAbortEditingText(uiTable *t); diff --git a/windows/tableediting.cpp b/windows/tableediting.cpp index b8582fba..35540e1f 100644 --- a/windows/tableediting.cpp +++ b/windows/tableediting.cpp @@ -2,30 +2,23 @@ #include "uipriv_windows.hpp" #include "table.hpp" -// TODO deduplicate this with tabledraw.cpp -static HRESULT itemRect(HRESULT hr, uiTable *t, UINT uMsg, WPARAM wParam, LONG left, LONG top, LRESULT bad, RECT *r) -{ - if (hr != S_OK) - return hr; - ZeroMemory(r, sizeof (RECT)); - r->left = left; - r->top = top; - if (SendMessageW(t->hwnd, uMsg, wParam, (LPARAM) r) == bad) { - logLastError(L"itemRect() message"); - return E_FAIL; - } - return S_OK; -} - // this is not how the real list view positions and sizes the edit control, but this is a) close enough b) a lot easier to follow c) something I can actually get working d) something I'm slightly more comfortable including in libui -// r should be the subitem label rect -static HRESULT resizeEdit(uiTable *t, WCHAR *wstr, RECT *r) +static HRESULT resizeEdit(uiTable *t, WCHAR *wstr, int iItem, int iSubItem) { + uiprivTableMetrics *m; + RECT r; HDC dc; HFONT prevFont; TEXTMETRICW tm; SIZE textSize; RECT editRect; + HRESULT hr; + + hr = uiprivTableGetMetrics(t, iItem, iSubItem, &m); + if (hr != S_OK) + return hr; + r = m->realTextRect; + uiprivFree(m); // TODO check errors for all these dc = GetDC(t->hwnd); // use the list view DC since we're using its coordinate space @@ -36,24 +29,24 @@ static HRESULT resizeEdit(uiTable *t, WCHAR *wstr, RECT *r) ReleaseDC(t->hwnd, dc); SendMessageW(t->edit, EM_GETRECT, 0, (LPARAM) (&editRect)); - r->left -= editRect.left; + r.left -= editRect.left; // find the top of the text - r->top += ((r->bottom - r->top) - tm.tmHeight) / 2; + r.top += ((r.bottom - r.top) - tm.tmHeight) / 2; // and move THAT by the right offset - r->top -= editRect.top; - r->right = r->left + textSize.cx; + r.top -= editRect.top; + r.right = r.left + textSize.cx; // the real listview does this to add some extra space at the end // TODO this still isn't enough space - r->right += 4 * GetSystemMetrics(SM_CXEDGE) + GetSystemMetrics(SM_CYEDGE); + r.right += 4 * GetSystemMetrics(SM_CXEDGE) + GetSystemMetrics(SM_CYEDGE); // and make the bottom equally positioned to the top - r->bottom = r->top + editRect.top + tm.tmHeight + editRect.top; + r.bottom = r.top + editRect.top + tm.tmHeight + editRect.top; // TODO intersect r with the list view's client rect to prevent clipping // TODO check error or use the right function SetWindowPos(t->edit, NULL, - r->left, r->top, - r->right - r->left, r->bottom - r->top, + r.left, r.top, + r.right - r.left, r.bottom - r.top, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER); return S_OK; } @@ -95,12 +88,8 @@ static LRESULT CALLBACK editSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM static HRESULT openEditControl(uiTable *t, int iItem, int iSubItem, uiprivTableColumnParams *p) { - RECT itemLabel; - RECT subitemBounds, subitemIcon, subitemLabel; uiTableData *data; WCHAR *wstr; - RECT r; - LONG xInflate, yInflate; HRESULT hr; // the real list view accepts changes to the existing item when editing a new item @@ -108,51 +97,6 @@ static HRESULT openEditControl(uiTable *t, int iItem, int iSubItem, uiprivTableC if (hr != S_OK) return hr; - // compute this in advance so we don't have to needlessly call DestroyWindow() later - // TODO deduplicate this code with tabledraw.cpp - // TODO check LRESULT bad parameters here - hr = itemRect(S_OK, t, LVM_GETITEMRECT, iItem, - LVIR_LABEL, 0, FALSE, &itemLabel); - hr = itemRect(hr, t, LVM_GETSUBITEMRECT, iItem, - LVIR_BOUNDS, iSubItem, 0, &subitemBounds); - hr = itemRect(hr, t, LVM_GETSUBITEMRECT, iItem, - LVIR_ICON, iSubItem, 0, &subitemIcon); - if (hr != S_OK) - return hr; - // LVM_GETSUBITEMRECT treats LVIR_LABEL as the same as - // LVIR_BOUNDS, so we can't use that directly. Instead, let's - // assume the text is immediately after the icon. The correct - // rect will be determined by - // computeOtherRectsAndDrawBackgrounds() above. - subitemLabel = subitemBounds; - subitemLabel.left = subitemIcon.right; - // And on iSubItem == 0, LVIF_GETSUBITEMRECT still includes - // all the subitems, which we don't want. - if (iSubItem == 0) { - subitemBounds.right = itemLabel.right; - subitemLabel.right = itemLabel.right; - } - if ((p->imageModelColumn == -1 && p->checkboxModelColumn == -1) && iSubItem != 0) - // By default, this will include images; we're not drawing - // images, so we will manually draw over the image area. - // There's a second part to this; see below. - subitemLabel.left = subitemBounds.left; - if ((p->imageModelColumn != -1 || p->checkboxModelColumn != -1) && iSubItem != 0) - // Normally there's this many hard-coded logical units - // of blank space, followed by the background, followed - // by a bitmap margin's worth of space. This looks bad, - // so we overrule that to start the background immediately - // and the text after the hard-coded amount. - subitemLabel.left += 2; - else if (iSubItem != 0) { - HWND header; - - // In the case of subitem text without an image, we draw - // text one bitmap margin away from the left edge. - header = (HWND) SendMessageW(t->hwnd, LVM_GETHEADER, 0, 0); - subitemLabel.left += SendMessageW(header, HDM_GETBITMAPMARGIN, 0, 0); - } - // the real list view creates the edit control with the string data = (*(t->model->mh->CellValue))(t->model->mh, t->model, iItem, p->textModelColumn); wstr = toUTF16(uiTableDataString(data)); @@ -175,7 +119,7 @@ static HRESULT openEditControl(uiTable *t, int iItem, int iSubItem, uiprivTableC // TODO check errors SetWindowSubclass(t->edit, editSubProc, 0, (DWORD_PTR) t); - hr = resizeEdit(t, wstr, &subitemLabel); + hr = resizeEdit(t, wstr, iItem, iSubItem); if (hr != S_OK) // TODO proper cleanup return hr; @@ -190,6 +134,19 @@ static HRESULT openEditControl(uiTable *t, int iItem, int iSubItem, uiprivTableC return S_OK; } +HRESULT uiprivTableResizeWhileEditing(uiTable *t) +{ + WCHAR *text; + HRESULT hr; + + if (t->edit == NULL) + return S_OK; + text = windowText(t->edit); + hr = resizeEdit(t, text, t->editedItem, t->editedSubitem); + uiprivFree(text); + return hr; +} + HRESULT uiprivTableFinishEditingText(uiTable *t) { if (t->edit == NULL) From ccb7005dc6325fc863a97876cd51dbc5a72cb167 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 20 Jun 2018 18:56:03 -0400 Subject: [PATCH 1228/1329] Properly clipped the edit within the listview. --- windows/tableediting.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/windows/tableediting.cpp b/windows/tableediting.cpp index 35540e1f..bff84937 100644 --- a/windows/tableediting.cpp +++ b/windows/tableediting.cpp @@ -11,7 +11,7 @@ static HRESULT resizeEdit(uiTable *t, WCHAR *wstr, int iItem, int iSubItem) HFONT prevFont; TEXTMETRICW tm; SIZE textSize; - RECT editRect; + RECT editRect, clientRect; HRESULT hr; hr = uiprivTableGetMetrics(t, iItem, iSubItem, &m); @@ -41,7 +41,11 @@ static HRESULT resizeEdit(uiTable *t, WCHAR *wstr, int iItem, int iSubItem) // and make the bottom equally positioned to the top r.bottom = r.top + editRect.top + tm.tmHeight + editRect.top; - // TODO intersect r with the list view's client rect to prevent clipping + // make sure the edit box doesn't stretch outside the listview + // the list view just does this, which is dumb for when the list view wouldn't be visible at all, but given that it doesn't scroll the edit into view either... + // TODO check errors + GetClientRect(t->hwnd, &clientRect); + IntersectRect(&r, &r, &clientRect); // TODO check error or use the right function SetWindowPos(t->edit, NULL, From 5854b0f63858fbd4fc1c35e89983e1e10aca729d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 20 Jun 2018 19:31:21 -0400 Subject: [PATCH 1229/1329] Loose ends for editing: drew the proper background for text and committing text works now. --- windows/tabledraw.cpp | 3 +++ windows/tableediting.cpp | 24 ++++++++++++++++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/windows/tabledraw.cpp b/windows/tabledraw.cpp index 64044455..062926e9 100644 --- a/windows/tabledraw.cpp +++ b/windows/tabledraw.cpp @@ -233,6 +233,9 @@ static HRESULT drawTextPart(struct drawState *s) if (!s->m->hasText) return S_OK; + // don't draw the text underneath an edit control + if (s->t->edit != NULL && s->t->editedItem == s->iItem && s->t->editedSubitem == s->iSubItem) + return S_OK; prevText = SetTextColor(s->dc, s->textColor); if (prevText == CLR_INVALID) { diff --git a/windows/tableediting.cpp b/windows/tableediting.cpp index bff84937..4e1131ab 100644 --- a/windows/tableediting.cpp +++ b/windows/tableediting.cpp @@ -153,20 +153,40 @@ HRESULT uiprivTableResizeWhileEditing(uiTable *t) HRESULT uiprivTableFinishEditingText(uiTable *t) { + uiprivTableColumnParams *p; + uiTableData *data; + char *text; + if (t->edit == NULL) return S_OK; + text = uiWindowsWindowText(t->edit); + data = uiNewTableDataString(text); + uiFreeText(text); + p = (*(t->columns))[t->editedSubitem]; + (*(t->model->mh->SetCellValue))(t->model->mh, t->model, t->editedItem, p->textModelColumn, data); + uiFreeTableData(data); + // always refresh the value in case the model rejected it + if (SendMessageW(t->hwnd, LVM_UPDATE, (WPARAM) (t->editedItem), 0) == (LRESULT) (-1)) { + logLastError(L"LVM_UPDATE"); + return E_FAIL; + } return uiprivTableAbortEditingText(t); } HRESULT uiprivTableAbortEditingText(uiTable *t) { + HWND edit; + if (t->edit == NULL) return S_OK; - if (DestroyWindow(t->edit) == 0) { + // set t->edit to NULL now so we don't trigger commits on focus killed + edit = t->edit; + t->edit = NULL; + + if (DestroyWindow(edit) == 0) { logLastError(L"DestroyWindow()"); return E_FAIL; } - t->edit = NULL; return S_OK; } From 4dc7f4c2def7ab987c3da0b92866d17d527a6277 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 20 Jun 2018 21:11:15 -0400 Subject: [PATCH 1230/1329] Wrote the initial code to draw the focus rect. --- windows/tabledraw.cpp | 55 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/windows/tabledraw.cpp b/windows/tabledraw.cpp index 062926e9..4660f6b8 100644 --- a/windows/tabledraw.cpp +++ b/windows/tabledraw.cpp @@ -608,6 +608,52 @@ fail: return hr; } +// TODO this has overdraw issues on non-selected rows; do they happen on selected rows too? +static HRESULT drawFocusRects(uiTable *t, NMLVCUSTOMDRAW *nm) +{ + RECT r; + bool first; + size_t i, n; + uiprivTableMetrics *m; + HRESULT hr; + + // TODO check error here? + if (GetFocus() != t->hwnd) + return S_OK; + // TODO only if the current item is focused + + ZeroMemory(&r, sizeof (RECT)); + first = true; + n = t->columns->size(); + for (i = 0; i < n; i++) { + RECT b; + + hr = uiprivTableGetMetrics(t, nm->nmcd.dwItemSpec, i, &m); + if (hr != S_OK) + return hr; + b = m->realTextBackground; + uiprivFree(m); + if (first) { + r = b; + first = false; + } else if (r.right == b.left) + r.right = b.right; + else { + if (DrawFocusRect(nm->nmcd.hdc, &r) == 0) { + logLastError(L"DrawFocusRect()"); + return E_FAIL; + } + r = b; + } + } + // and draw the last rect + if (DrawFocusRect(nm->nmcd.hdc, &r) == 0) { + logLastError(L"DrawFocusRect() last"); + return E_FAIL; + } + return S_OK; +} + HRESULT uiprivTableHandleNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm, LRESULT *lResult) { struct drawState s; @@ -619,10 +665,17 @@ HRESULT uiprivTableHandleNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm, LRESULT * *lResult = CDRF_NOTIFYITEMDRAW; return S_OK; case CDDS_ITEMPREPAINT: - *lResult = CDRF_NOTIFYSUBITEMDRAW; + *lResult = CDRF_NOTIFYSUBITEMDRAW | CDRF_NOTIFYPOSTPAINT; return S_OK; case CDDS_SUBITEM | CDDS_ITEMPREPAINT: break; + case CDDS_ITEMPOSTPAINT: + // draw the focus rects only at the end, so they are drawn over subitems + hr = drawFocusRects(t, nm); + if (hr != S_OK) + return hr; + *lResult = CDRF_SKIPDEFAULT; + return S_OK; default: *lResult = CDRF_DODEFAULT; return S_OK; From c2000ea54dca5dc0627045c1c3f1162911c8291e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 21 Jun 2018 21:56:24 -0400 Subject: [PATCH 1231/1329] Gave up with CDDS_SUBITEM; it just refuses to play nice with focus rects. Will try drawing focus rects again next commit. --- windows/tabledraw.cpp | 75 +++++++++++++++++++++------------------- windows/tableediting.cpp | 3 ++ 2 files changed, 43 insertions(+), 35 deletions(-) diff --git a/windows/tabledraw.cpp b/windows/tabledraw.cpp index 4660f6b8..29b181bc 100644 --- a/windows/tabledraw.cpp +++ b/windows/tabledraw.cpp @@ -654,10 +654,19 @@ static HRESULT drawFocusRects(uiTable *t, NMLVCUSTOMDRAW *nm) return S_OK; } +// normally we would only draw stuff in subitem stages +// this broke when we tried drawing focus rects in postpaint; the drawing either kept getting wiped or overdrawn, mouse hovering had something to do with it, and none of the "solutions" to this or similar problems on the internet worked +// so now we do everything in the item prepaint stage +// TODO consider switching to full-on owner draw +// TODO only compute the background brushes once? +// TODO integrate the focus rect stuff above +// TODO do hr chaining like in tablemetrics.cpp HRESULT uiprivTableHandleNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm, LRESULT *lResult) { struct drawState s; uiprivTableColumnParams *p; + NMLVCUSTOMDRAW b; + size_t i, n; HRESULT hr; switch (nm->nmcd.dwDrawStage) { @@ -665,51 +674,47 @@ HRESULT uiprivTableHandleNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm, LRESULT * *lResult = CDRF_NOTIFYITEMDRAW; return S_OK; case CDDS_ITEMPREPAINT: - *lResult = CDRF_NOTIFYSUBITEMDRAW | CDRF_NOTIFYPOSTPAINT; - return S_OK; - case CDDS_SUBITEM | CDDS_ITEMPREPAINT: break; - case CDDS_ITEMPOSTPAINT: - // draw the focus rects only at the end, so they are drawn over subitems - hr = drawFocusRects(t, nm); - if (hr != S_OK) - return hr; - *lResult = CDRF_SKIPDEFAULT; - return S_OK; default: *lResult = CDRF_DODEFAULT; return S_OK; } - p = (*(t->columns))[nm->iSubItem]; - hr = fillDrawState(&s, t, nm, p); - if (hr != S_OK) - return hr; - hr = drawBackgrounds(&s); - if (hr != S_OK) - goto fail; - hr = drawImagePart(&s); - if (hr != S_OK) - goto fail; - hr = drawCheckboxPart(&s); - if (hr != S_OK) - goto fail; - hr = drawTextPart(&s); - if (hr != S_OK) - goto fail; - hr = drawProgressBarPart(&s); - if (hr != S_OK) - goto fail; - hr = drawButtonPart(&s); - if (hr != S_OK) - goto fail; - hr = freeDrawState(&s); - if (hr != S_OK) // TODO really error out here? - return hr; + n = t->columns->size(); + b = *nm; + for (i = 0; i < n; i++) { + b.iSubItem = i; + p = (*(t->columns))[i]; + hr = fillDrawState(&s, t, &b, p); + if (hr != S_OK) + return hr; + hr = drawBackgrounds(&s); + if (hr != S_OK) + goto fail; + hr = drawImagePart(&s); + if (hr != S_OK) + goto fail; + hr = drawCheckboxPart(&s); + if (hr != S_OK) + goto fail; + hr = drawTextPart(&s); + if (hr != S_OK) + goto fail; + hr = drawProgressBarPart(&s); + if (hr != S_OK) + goto fail; + hr = drawButtonPart(&s); + if (hr != S_OK) + goto fail; + hr = freeDrawState(&s); + if (hr != S_OK) // TODO really error out here? + return hr; + } *lResult = CDRF_SKIPDEFAULT; return S_OK; fail: // ignore error here + // TODO this is awkward cleanup placement for something that only really exists in a for loop freeDrawState(&s); return hr; } diff --git a/windows/tableediting.cpp b/windows/tableediting.cpp index 4e1131ab..4eb8d2d2 100644 --- a/windows/tableediting.cpp +++ b/windows/tableediting.cpp @@ -2,6 +2,9 @@ #include "uipriv_windows.hpp" #include "table.hpp" +// TODOs +// - clicking on the same item restarts editing instead of cancels it + // this is not how the real list view positions and sizes the edit control, but this is a) close enough b) a lot easier to follow c) something I can actually get working d) something I'm slightly more comfortable including in libui static HRESULT resizeEdit(uiTable *t, WCHAR *wstr, int iItem, int iSubItem) { From d0f7cf81c5458837ff1d17dcb852345110578374 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 21 Jun 2018 23:47:24 -0400 Subject: [PATCH 1232/1329] And rewired the focus rect. --- windows/tabledraw.cpp | 65 +++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 34 deletions(-) diff --git a/windows/tabledraw.cpp b/windows/tabledraw.cpp index 29b181bc..ddeac51e 100644 --- a/windows/tabledraw.cpp +++ b/windows/tabledraw.cpp @@ -2,6 +2,9 @@ #include "uipriv_windows.hpp" #include "table.hpp" +// TODOs: +// - properly hide selection when not focused (or switch on LVS_SHOWSELALWAYS and draw that state) + // TODO #define cellValue(model, row, column) ((*((model)->mh->CellValue))((model)->mh, (model), (row), (column))) @@ -608,49 +611,33 @@ fail: return hr; } -// TODO this has overdraw issues on non-selected rows; do they happen on selected rows too? -static HRESULT drawFocusRects(uiTable *t, NMLVCUSTOMDRAW *nm) +static HRESULT updateAndDrawFocusRects(uiTable *t, HDC dc, int iItem, RECT *realTextBackground, RECT *focus, bool *first) { - RECT r; - bool first; - size_t i, n; - uiprivTableMetrics *m; - HRESULT hr; + LRESULT state; - // TODO check error here? if (GetFocus() != t->hwnd) return S_OK; - // TODO only if the current item is focused + // uItemState CDIS_FOCUS doesn't quite work right because of bugs in the Windows list view that causes spurious redraws without the flag while we hover over the focused item + // TODO only call this once + state = SendMessageW(t->hwnd, LVM_GETITEMSTATE, (WPARAM) iItem, (LRESULT) (LVIS_FOCUSED)); + if ((state & LVIS_FOCUSED) == 0) + return S_OK; - ZeroMemory(&r, sizeof (RECT)); - first = true; - n = t->columns->size(); - for (i = 0; i < n; i++) { - RECT b; - - hr = uiprivTableGetMetrics(t, nm->nmcd.dwItemSpec, i, &m); - if (hr != S_OK) - return hr; - b = m->realTextBackground; - uiprivFree(m); - if (first) { - r = b; + if (realTextBackground != NULL) + if (*first) { + *focus = *realTextBackground; first = false; - } else if (r.right == b.left) - r.right = b.right; - else { - if (DrawFocusRect(nm->nmcd.hdc, &r) == 0) { - logLastError(L"DrawFocusRect()"); - return E_FAIL; - } - r = b; + return S_OK; + } else if (focus->right == realTextBackground->left) { + focus->right = realTextBackground->right; + return S_OK; } - } - // and draw the last rect - if (DrawFocusRect(nm->nmcd.hdc, &r) == 0) { - logLastError(L"DrawFocusRect() last"); + if (DrawFocusRect(dc, focus) == 0) { + logLastError(L"DrawFocusRect()"); return E_FAIL; } + if (realTextBackground != NULL) + *focus = *realTextBackground; return S_OK; } @@ -667,6 +654,8 @@ HRESULT uiprivTableHandleNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm, LRESULT * uiprivTableColumnParams *p; NMLVCUSTOMDRAW b; size_t i, n; + RECT focus; + bool focusFirst; HRESULT hr; switch (nm->nmcd.dwDrawStage) { @@ -682,6 +671,7 @@ HRESULT uiprivTableHandleNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm, LRESULT * n = t->columns->size(); b = *nm; + focusFirst = false; for (i = 0; i < n; i++) { b.iSubItem = i; p = (*(t->columns))[i]; @@ -704,12 +694,19 @@ HRESULT uiprivTableHandleNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm, LRESULT * if (hr != S_OK) goto fail; hr = drawButtonPart(&s); + if (hr != S_OK) + goto fail; + hr = updateAndDrawFocusRects(s.t, s.dc, nm->nmcd.dwItemSpec, &(s.m->realTextBackground), &focus, &focusFirst); if (hr != S_OK) goto fail; hr = freeDrawState(&s); if (hr != S_OK) // TODO really error out here? return hr; } + // and draw the last focus rect + hr = updateAndDrawFocusRects(t, nm->nmcd.hdc, nm->nmcd.dwItemSpec, NULL, &focus, &focusFirst); + if (hr != S_OK) + return hr; *lResult = CDRF_SKIPDEFAULT; return S_OK; fail: From 7d17df712147ffdb09eb8ea0344015bd0970ea41 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 22 Jun 2018 21:50:04 -0400 Subject: [PATCH 1233/1329] HRESULT-chained the drawing functions together and fixed bugs in the focus drawing code. --- windows/tabledraw.cpp | 66 ++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 35 deletions(-) diff --git a/windows/tabledraw.cpp b/windows/tabledraw.cpp index ddeac51e..190a6c0b 100644 --- a/windows/tabledraw.cpp +++ b/windows/tabledraw.cpp @@ -27,8 +27,10 @@ struct drawState { BOOL freeTextBrush; }; -static HRESULT drawBackgrounds(struct drawState *s) +static HRESULT drawBackgrounds(HRESULT hr, struct drawState *s) { + if (hr != S_OK) + return hr; if (s->m->hasImage) if (FillRect(s->dc, &(s->m->subitemIcon), GetSysColorBrush(COLOR_WINDOW)) == 0) { logLastError(L"FillRect() icon"); @@ -62,15 +64,16 @@ static void centerImageRect(RECT *image, RECT *space) image->bottom += yoff; } -static HRESULT drawImagePart(struct drawState *s) +static HRESULT drawImagePart(HRESULT hr, struct drawState *s) { uiTableData *data; IWICBitmap *wb; HBITMAP b; RECT r; UINT fStyle; - HRESULT hr; + if (hr != S_OK) + return hr; if (s->p->imageModelColumn == -1) return S_OK; @@ -182,13 +185,14 @@ static HRESULT drawThemedCheckbox(struct drawState *s, HTHEME theme, int checked return S_OK; } -static HRESULT drawCheckboxPart(struct drawState *s) +static HRESULT drawCheckboxPart(HRESULT hr, struct drawState *s) { uiTableData *data; int checked, enabled; HTHEME theme; - HRESULT hr; + if (hr != S_OK) + return hr; if (s->p->checkboxModelColumn == -1) return S_OK; @@ -226,7 +230,7 @@ static HRESULT drawCheckboxPart(struct drawState *s) return S_OK; } -static HRESULT drawTextPart(struct drawState *s) +static HRESULT drawTextPart(HRESULT hr, struct drawState *s) { COLORREF prevText; int prevMode; @@ -234,6 +238,8 @@ static HRESULT drawTextPart(struct drawState *s) uiTableData *data; WCHAR *wstr; + if (hr != S_OK) + return hr; if (!s->m->hasText) return S_OK; // don't draw the text underneath an edit control @@ -280,7 +286,7 @@ static HRESULT drawTextPart(struct drawState *s) // much of this is to imitate what shell32.dll's CDrawProgressBar does #define indeterminateSegments 8 -static HRESULT drawProgressBarPart(struct drawState *s) +static HRESULT drawProgressBarPart(HRESULT hr, struct drawState *s) { int progress; LONG indeterminatePos; @@ -290,8 +296,9 @@ static HRESULT drawProgressBarPart(struct drawState *s) int i, nFill; TEXTMETRICW tm; int sysColor; - HRESULT hr; + if (hr != S_OK) + return hr; if (s->p->progressBarModelColumn == -1) return S_OK; @@ -392,7 +399,7 @@ fail: return hr; } -static HRESULT drawButtonPart(struct drawState *s) +static HRESULT drawButtonPart(HRESULT hr, struct drawState *s) { uiTableData *data; WCHAR *wstr; @@ -400,8 +407,9 @@ static HRESULT drawButtonPart(struct drawState *s) HTHEME theme; RECT r; TEXTMETRICW tm; - HRESULT hr; + if (hr != S_OK) + return hr; if (s->p->buttonModelColumn == -1) return S_OK; @@ -611,10 +619,12 @@ fail: return hr; } -static HRESULT updateAndDrawFocusRects(uiTable *t, HDC dc, int iItem, RECT *realTextBackground, RECT *focus, bool *first) +static HRESULT updateAndDrawFocusRects(HRESULT hr, uiTable *t, HDC dc, int iItem, RECT *realTextBackground, RECT *focus, bool *first) { LRESULT state; + if (hr != S_OK) + return hr; if (GetFocus() != t->hwnd) return S_OK; // uItemState CDIS_FOCUS doesn't quite work right because of bugs in the Windows list view that causes spurious redraws without the flag while we hover over the focused item @@ -626,7 +636,7 @@ static HRESULT updateAndDrawFocusRects(uiTable *t, HDC dc, int iItem, RECT *real if (realTextBackground != NULL) if (*first) { *focus = *realTextBackground; - first = false; + *first = false; return S_OK; } else if (focus->right == realTextBackground->left) { focus->right = realTextBackground->right; @@ -646,8 +656,6 @@ static HRESULT updateAndDrawFocusRects(uiTable *t, HDC dc, int iItem, RECT *real // so now we do everything in the item prepaint stage // TODO consider switching to full-on owner draw // TODO only compute the background brushes once? -// TODO integrate the focus rect stuff above -// TODO do hr chaining like in tablemetrics.cpp HRESULT uiprivTableHandleNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm, LRESULT *lResult) { struct drawState s; @@ -671,32 +679,20 @@ HRESULT uiprivTableHandleNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm, LRESULT * n = t->columns->size(); b = *nm; - focusFirst = false; + focusFirst = true; for (i = 0; i < n; i++) { b.iSubItem = i; p = (*(t->columns))[i]; hr = fillDrawState(&s, t, &b, p); if (hr != S_OK) return hr; - hr = drawBackgrounds(&s); - if (hr != S_OK) - goto fail; - hr = drawImagePart(&s); - if (hr != S_OK) - goto fail; - hr = drawCheckboxPart(&s); - if (hr != S_OK) - goto fail; - hr = drawTextPart(&s); - if (hr != S_OK) - goto fail; - hr = drawProgressBarPart(&s); - if (hr != S_OK) - goto fail; - hr = drawButtonPart(&s); - if (hr != S_OK) - goto fail; - hr = updateAndDrawFocusRects(s.t, s.dc, nm->nmcd.dwItemSpec, &(s.m->realTextBackground), &focus, &focusFirst); + hr = drawBackgrounds(S_OK, &s); + hr = drawImagePart(hr, &s); + hr = drawCheckboxPart(hr, &s); + hr = drawTextPart(hr, &s); + hr = drawProgressBarPart(hr, &s); + hr = drawButtonPart(hr, &s); + hr = updateAndDrawFocusRects(hr, s.t, s.dc, nm->nmcd.dwItemSpec, &(s.m->realTextBackground), &focus, &focusFirst); if (hr != S_OK) goto fail; hr = freeDrawState(&s); @@ -704,7 +700,7 @@ HRESULT uiprivTableHandleNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm, LRESULT * return hr; } // and draw the last focus rect - hr = updateAndDrawFocusRects(t, nm->nmcd.hdc, nm->nmcd.dwItemSpec, NULL, &focus, &focusFirst); + hr = updateAndDrawFocusRects(hr, t, nm->nmcd.hdc, nm->nmcd.dwItemSpec, NULL, &focus, &focusFirst); if (hr != S_OK) return hr; *lResult = CDRF_SKIPDEFAULT; From 888bb450b36dcc226e6bbaf184d2675fe3536e0e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 23 Jun 2018 11:26:14 -0400 Subject: [PATCH 1234/1329] More TODOs. --- windows/tabledraw.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/windows/tabledraw.cpp b/windows/tabledraw.cpp index 190a6c0b..c693d794 100644 --- a/windows/tabledraw.cpp +++ b/windows/tabledraw.cpp @@ -8,6 +8,7 @@ // TODO #define cellValue(model, row, column) ((*((model)->mh->CellValue))((model)->mh, (model), (row), (column))) +// TODO maybe split this into item and subitem structs? struct drawState { uiTable *t; uiTableModel *model; From ac27e24add9ac7992447efe8642bd9e1a5d59514 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 23 Jun 2018 11:27:02 -0400 Subject: [PATCH 1235/1329] And even more still. I'm just gonna clean everything up for a merge at this point. --- windows/table.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/windows/table.cpp b/windows/table.cpp index a070c82b..315f5e37 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -4,6 +4,8 @@ // general TODOs: // - tooltips don't work properly on columns with icons (the listview always thinks there's enough room for a short label because it's not taking the icon into account); is this a bug in our LVN_GETDISPINFO handler or something else? // - should clicking on some other column of the same row, even one that doesn't edit, cancel editing? +// - implement keyboard accessibility +// - implement accessibility in general (Dynamic Annotations maybe?) static uiTableTextColumnOptionalParams defaultTextColumnOptionalParams = { /*TODO.ColorModelColumn = */-1, From 9a79eed2ac635612f169163ae152957a9df90e88 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 23 Jun 2018 20:19:30 -0400 Subject: [PATCH 1236/1329] Final cleanup, first part: renamed uiTableData to uiTableValue. --- common/CMakeLists.txt | 4 +- common/tabledata.c | 105 -------------------------------------- common/tablevalue.c | 105 ++++++++++++++++++++++++++++++++++++++ darwin/table.m | 10 ++-- darwin/tablecolumn.m | 83 +++++++++++++++--------------- test/page16.c | 44 ++++++++-------- uitable.h | 38 +++++++------- unix/table.c | 20 ++++---- unix/tablemodel.c | 38 +++++++------- windows/table.cpp | 8 +-- windows/tabledispinfo.cpp | 12 ++--- windows/tabledraw.cpp | 62 +++++++++++----------- windows/tableediting.cpp | 36 ++++++------- 13 files changed, 281 insertions(+), 284 deletions(-) delete mode 100644 common/tabledata.c create mode 100644 common/tablevalue.c diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 99ceda09..785c70db 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -7,12 +7,10 @@ list(APPEND _LIBUI_SOURCES common/areaevents.c common/control.c common/debug.c -# common/drawtext.c common/matrix.c common/opentype.c common/shouldquit.c -# common/table.c - common/tabledata.c + common/tablevalue.c common/userbugs.c common/utf.c ) diff --git a/common/tabledata.c b/common/tabledata.c deleted file mode 100644 index e66d4b4b..00000000 --- a/common/tabledata.c +++ /dev/null @@ -1,105 +0,0 @@ -// 3 june 2018 -#include "../ui.h" -#include "uipriv.h" - -struct uiTableData { - uiTableDataType type; - union { - char *str; - uiImage *img; - int i; - struct { - double r; - double g; - double b; - double a; - } color; - } u; -}; - -static uiTableData *newTableData(uiTableDataType type) -{ - uiTableData *d; - - d = uiprivNew(uiTableData); - d->type = type; - return d; -} - -void uiFreeTableData(uiTableData *d) -{ - switch (d->type) { - case uiTableDataTypeString: - uiprivFree(d->u.str); - break; - } - uiprivFree(d); -} - -uiTableDataType uiTableDataGetType(const uiTableData *d) -{ - return d->type; -} - -uiTableData *uiNewTableDataString(const char *str) -{ - uiTableData *d; - - d = newTableData(uiTableDataTypeString); - d->u.str = (char *) uiprivAlloc((strlen(str) + 1) * sizeof (char), "char[] (uiTableData)"); - strcpy(d->u.str, str); - return d; -} - -const char *uiTableDataString(const uiTableData *d) -{ - return d->u.str; -} - -uiTableData *uiNewTableDataImage(uiImage *img) -{ - uiTableData *d; - - d = newTableData(uiTableDataTypeImage); - d->u.img = img; - return d; -} - -uiImage *uiTableDataImage(const uiTableData *d) -{ - return d->u.img; -} - -uiTableData *uiNewTableDataInt(int i) -{ - uiTableData *d; - - d = newTableData(uiTableDataTypeInt); - d->u.i = i; - return d; -} - -int uiTableDataInt(const uiTableData *d) -{ - return d->u.i; -} - -uiTableData *uiNewTableDataColor(double r, double g, double b, double a) -{ - uiTableData *d; - - d = newTableData(uiTableDataTypeColor); - d->u.color.r = r; - d->u.color.g = g; - d->u.color.b = b; - d->u.color.a = a; - return d; -} - -void uiTableDataColor(const uiTableData *d, double *r, double *g, double *b, double *a) -{ - *r = d->u.color.r; - *g = d->u.color.g; - *b = d->u.color.b; - *a = d->u.color.a; -} diff --git a/common/tablevalue.c b/common/tablevalue.c new file mode 100644 index 00000000..b73a920e --- /dev/null +++ b/common/tablevalue.c @@ -0,0 +1,105 @@ +// 3 june 2018 +#include "../ui.h" +#include "uipriv.h" + +struct uiTableValue { + uiTableValueType type; + union { + char *str; + uiImage *img; + int i; + struct { + double r; + double g; + double b; + double a; + } color; + } u; +}; + +static uiTableValue *newTableValue(uiTableValueType type) +{ + uiTableValue *v; + + v = uiprivNew(uiTableValue); + v->type = type; + return v; +} + +void uiFreeTableValue(uiTableValue *v) +{ + switch (v->type) { + case uiTableValueTypeString: + uiprivFree(v->u.str); + break; + } + uiprivFree(v); +} + +uiTableValueType uiTableValueGetType(const uiTableValue *v) +{ + return v->type; +} + +uiTableValue *uiNewTableValueString(const char *str) +{ + uiTableValue *v; + + v = newTableValue(uiTableValueTypeString); + v->u.str = (char *) uiprivAlloc((strlen(str) + 1) * sizeof (char), "char[] (uiTableValue)"); + strcpy(v->u.str, str); + return v; +} + +const char *uiTableValueString(const uiTableValue *v) +{ + return v->u.str; +} + +uiTableValue *uiNewTableValueImage(uiImage *img) +{ + uiTableValue *v; + + v = newTableValue(uiTableValueTypeImage); + v->u.img = img; + return v; +} + +uiImage *uiTableValueImage(const uiTableValue *v) +{ + return v->u.img; +} + +uiTableValue *uiNewTableValueInt(int i) +{ + uiTableValue *v; + + v = newTableValue(uiTableValueTypeInt); + v->u.i = i; + return v; +} + +int uiTableValueInt(const uiTableValue *v) +{ + return v->u.i; +} + +uiTableValue *uiNewTableValueColor(double r, double g, double b, double a) +{ + uiTableValue *v; + + v = newTableValue(uiTableValueTypeColor); + v->u.color.r = r; + v->u.color.g = g; + v->u.color.b = b; + v->u.color.a = a; + return v; +} + +void uiTableValueColor(const uiTableValue *v, double *r, double *g, double *b, double *a) +{ + *r = v->u.color.r; + *g = v->u.color.g; + *b = v->u.color.b; + *a = v->u.color.a; +} diff --git a/darwin/table.m b/darwin/table.m index 1d5093f9..378fb95d 100644 --- a/darwin/table.m +++ b/darwin/table.m @@ -31,16 +31,16 @@ // TODO is this correct for overflow scrolling? static void setBackgroundColor(uiprivTableView *t, NSTableRowView *rv, NSInteger row) { - uiTableData *data; + uiTableValue *value; NSColor *color; double r, g, b, a; if (t->uiprivT->backgroundColumn == -1) return; - data = (*(t->uiprivM->mh->CellValue))(t->uiprivM->mh, t->uiprivM, row, t->uiprivT->backgroundColumn); - if (data != NULL) { - uiTableDataColor(data, &r, &g, &b, &a); - uiFreeTableData(data); + value = (*(t->uiprivM->mh->CellValue))(t->uiprivM->mh, t->uiprivM, row, t->uiprivT->backgroundColumn); + if (value != NULL) { + uiTableValueColor(value, &r, &g, &b, &a); + uiFreeTableValue(value); color = [NSColor colorWithSRGBRed:r green:g blue:b alpha:a]; } else { NSArray *colors; diff --git a/darwin/tablecolumn.m b/darwin/tablecolumn.m index dbc85360..2e2e8299 100644 --- a/darwin/tablecolumn.m +++ b/darwin/tablecolumn.m @@ -36,8 +36,8 @@ static BOOL isCellEditable(uiTableModel *m, NSInteger row, int modelColumn) { - uiTableData *data; - int value; + uiTableValue *value; + int editable; switch (modelColumn) { case uiTableModelColumnNeverEditable: @@ -45,11 +45,10 @@ static BOOL isCellEditable(uiTableModel *m, NSInteger row, int modelColumn) case uiTableModelColumnAlwaysEditable: return YES; } - data = (*(m->mh->CellValue))(m->mh, m, row, modelColumn); - value = uiTableDataInt(data); - uiFreeTableData(data); - return value != 0; - // TODO free data + value = (*(m->mh->CellValue))(m->mh, m, row, modelColumn); + editable = uiTableValueInt(value); + uiFreeTableValue(value); + return editable != 0; } static uiTableTextColumnOptionalParams defaultTextColumnOptionalParams = { @@ -266,15 +265,15 @@ struct textColumnCreateParams { - (void)uiprivUpdate:(NSInteger)row { - uiTableData *data; + uiTableValue *value; if (self->tf != nil) { NSString *str; NSColor *color; - data = (*(self->m->mh->CellValue))(self->m->mh, self->m, row, self->textModelColumn); - str = uiprivToNSString(uiTableDataString(data)); - uiFreeTableData(data); + value = (*(self->m->mh->CellValue))(self->m->mh, self->m, row, self->textModelColumn); + str = uiprivToNSString(uiTableValueString(value)); + uiFreeTableValue(value); [self->tf setStringValue:str]; [self->tf setEditable:isCellEditable(self->m, row, self->textEditableColumn)]; @@ -283,11 +282,11 @@ struct textColumnCreateParams { if (self->textParams.ColorModelColumn != -1) { double r, g, b, a; - data = (*(self->m->mh->CellValue))(self->m->mh, self->m, row, self->textParams.ColorModelColumn); + value = (*(self->m->mh->CellValue))(self->m->mh, self->m, row, self->textParams.ColorModelColumn); // TODO document this is allowed - if (data != NULL) { - uiTableDataColor(data, &r, &g, &b, &a); - uiFreeTableData(data); + if (value != NULL) { + uiTableValueColor(value, &r, &g, &b, &a); + uiFreeTableValue(value); color = [NSColor colorWithSRGBRed:r green:g blue:b alpha:a]; } } @@ -299,18 +298,18 @@ struct textColumnCreateParams { if (self->iv != nil) { uiImage *img; - data = (*(self->m->mh->CellValue))(self->m->mh, self->m, row, self->imageModelColumn); - img = uiTableDataImage(data); - uiFreeTableData(data); + value = (*(self->m->mh->CellValue))(self->m->mh, self->m, row, self->imageModelColumn); + img = uiTableValueImage(value); + uiFreeTableValue(value); [self->iv setImage:uiprivImageNSImage(img)]; } if (self->cb != nil) { - data = (*(self->m->mh->CellValue))(self->m->mh, self->m, row, self->imageModelColumn); - if (uiTableDataInt(data) != 0) + value = (*(self->m->mh->CellValue))(self->m->mh, self->m, row, self->imageModelColumn); + if (uiTableValueInt(value) != 0) [self->cb setState:NSOnState]; else [self->cb setState:NSOffState]; - uiFreeTableData(data); + uiFreeTableValue(value); [self->cb setEnabled:isCellEditable(self->m, row, self->checkboxEditableColumn)]; } @@ -319,13 +318,13 @@ struct textColumnCreateParams { - (IBAction)uiprivOnTextFieldAction:(id)sender { NSInteger row; - uiTableData *data; + uiTableValue *value; row = [self->t->tv rowForView:self->tf]; - data = uiNewTableDataString([[self->tf stringValue] UTF8String]); + value = uiNewTableValueString([[self->tf stringValue] UTF8String]); (*(self->m->mh->SetCellValue))(self->m->mh, self->m, - row, self->textModelColumn, data); - uiFreeTableData(data); + row, self->textModelColumn, value); + uiFreeTableValue(value); // always refresh the value in case the model rejected it // TODO document that we do this, but not for the whole row (or decide to do both, or do neither...) [self uiprivUpdate:row]; @@ -334,13 +333,13 @@ struct textColumnCreateParams { - (IBAction)uiprivOnCheckboxAction:(id)sender { NSInteger row; - void *data; + uiTableValue *value; row = [self->t->tv rowForView:self->cb]; - data = uiNewTableDataInt([self->cb state] != NSOffState); + value = uiNewTableValueInt([self->cb state] != NSOffState); (*(self->m->mh->SetCellValue))(self->m->mh, self->m, - row, self->checkboxModelColumn, data); - uiFreeTableData(data); + row, self->checkboxModelColumn, value); + uiFreeTableValue(value); // always refresh the value in case the model rejected it [self uiprivUpdate:row]; } @@ -434,16 +433,16 @@ struct textColumnCreateParams { - (void)uiprivUpdate:(NSInteger)row { - uiTableData *data; - int value; + uiTableValue *value; + int progress; - data = (*(self->m->mh->CellValue))(self->m->mh, self->m, row, self->modelColumn); - value = uiTableDataInt(data); - uiFreeTableData(data); - if (value == -1) { + value = (*(self->m->mh->CellValue))(self->m->mh, self->m, row, self->modelColumn); + progress = uiTableValueInt(value); + uiFreeTableValue(value); + if (progress == -1) { [self->p setIndeterminate:YES]; [self->p startAnimation:self->p]; - } else if (value == 100) { + } else if (progress == 100) { [self->p setIndeterminate:NO]; [self->p setMaxValue:101]; [self->p setDoubleValue:101]; @@ -451,8 +450,8 @@ struct textColumnCreateParams { [self->p setMaxValue:100]; } else { [self->p setIndeterminate:NO]; - [self->p setDoubleValue:(value + 1)]; - [self->p setDoubleValue:value]; + [self->p setDoubleValue:(progress + 1)]; + [self->p setDoubleValue:progress]; } } @@ -557,12 +556,12 @@ struct textColumnCreateParams { - (void)uiprivUpdate:(NSInteger)row { - uiTableData *data; + uiTableValue *value; NSString *str; - data = (*(self->m->mh->CellValue))(self->m->mh, self->m, row, self->modelColumn); - str = uiprivToNSString(uiTableDataString(data)); - uiFreeTableData(data); + value = (*(self->m->mh->CellValue))(self->m->mh, self->m, row, self->modelColumn); + str = uiprivToNSString(uiTableValueString(value)); + uiFreeTableValue(value); [self->b setTitle:str]; [self->b setEnabled:isCellEditable(self->m, row, self->editableColumn)]; diff --git a/test/page16.c b/test/page16.c index d00bd39c..51aa3a59 100644 --- a/test/page16.c +++ b/test/page16.c @@ -8,15 +8,15 @@ static int modelNumColumns(uiTableModelHandler *mh, uiTableModel *m) return 9; } -static uiTableDataType modelColumnType(uiTableModelHandler *mh, uiTableModel *m, int column) +static uiTableValueType modelColumnType(uiTableModelHandler *mh, uiTableModel *m, int column) { if (column == 3 || column == 4) - return uiTableDataTypeColor; + return uiTableValueTypeColor; if (column == 5) - return uiTableDataTypeImage; + return uiTableValueTypeImage; if (column == 7 || column == 8) - return uiTableDataTypeInt; - return uiTableDataTypeString; + return uiTableValueTypeInt; + return uiTableValueTypeString; } static int modelNumRows(uiTableModelHandler *mh, uiTableModel *m) @@ -29,39 +29,39 @@ static char row9text[1024]; static int yellowRow = -1; static int checkStates[15]; -static uiTableData *modelCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, int col) +static uiTableValue *modelCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, int col) { char buf[256]; if (col == 3) { if (row == yellowRow) - return uiNewTableDataColor(1, 1, 0, 1); + return uiNewTableValueColor(1, 1, 0, 1); if (row == 3) - return uiNewTableDataColor(1, 0, 0, 1); + return uiNewTableValueColor(1, 0, 0, 1); if (row == 11) - return uiNewTableDataColor(0, 0.5, 1, 0.5); + return uiNewTableValueColor(0, 0.5, 1, 0.5); return NULL; } if (col == 4) { if ((row % 2) == 1) - return uiNewTableDataColor(0.5, 0, 0.75, 1); + return uiNewTableValueColor(0.5, 0, 0.75, 1); return NULL; } if (col == 5) { if (row < 8) - return uiNewTableDataImage(img[0]); - return uiNewTableDataImage(img[1]); + return uiNewTableValueImage(img[0]); + return uiNewTableValueImage(img[1]); } if (col == 7) - return uiNewTableDataInt(checkStates[row]); + return uiNewTableValueInt(checkStates[row]); if (col == 8) { if (row == 0) - return uiNewTableDataInt(0); + return uiNewTableValueInt(0); if (row == 13) - return uiNewTableDataInt(100); + return uiNewTableValueInt(100); if (row == 14) - return uiNewTableDataInt(-1); - return uiNewTableDataInt(50); + return uiNewTableValueInt(-1); + return uiNewTableValueInt(50); } switch (col) { case 0: @@ -69,7 +69,7 @@ static uiTableData *modelCellValue(uiTableModelHandler *mh, uiTableModel *m, int break; case 2: if (row == 9) - return uiNewTableDataString(row9text); + return uiNewTableValueString(row9text); // fall through case 1: strcpy(buf, "Part"); @@ -78,13 +78,13 @@ static uiTableData *modelCellValue(uiTableModelHandler *mh, uiTableModel *m, int strcpy(buf, "Make Yellow"); break; } - return uiNewTableDataString(buf); + return uiNewTableValueString(buf); } -static void modelSetCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, int col, const uiTableData *val) +static void modelSetCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, int col, const uiTableValue *val) { if (row == 9 && col == 2) - strcpy(row9text, uiTableDataString(val)); + strcpy(row9text, uiTableValueString(val)); if (col == 6) { int prevYellowRow; @@ -95,7 +95,7 @@ static void modelSetCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, uiTableModelRowChanged(m, yellowRow); } if (col == 7) - checkStates[row] = uiTableDataInt(val); + checkStates[row] = uiTableValueInt(val); } uiBox *makePage16(void) diff --git a/uitable.h b/uitable.h index 077e77f9..7a58b792 100644 --- a/uitable.h +++ b/uitable.h @@ -8,32 +8,32 @@ _UI_EXTERN uiImage *uiNewImage(double width, double height); _UI_EXTERN void uiFreeImage(uiImage *i); _UI_EXTERN void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int pixelStride); -typedef struct uiTableData uiTableData; +typedef struct uiTableValue uiTableValue; -_UI_EXTERN void uiFreeTableData(uiTableData *d); +_UI_EXTERN void uiFreeTableValue(uiTableValue *v); // TODO actually validate these -_UI_ENUM(uiTableDataType) { - uiTableDataTypeString, - uiTableDataTypeImage, - uiTableDataTypeInt, - uiTableDataTypeColor, +_UI_ENUM(uiTableValueType) { + uiTableValueTypeString, + uiTableValueTypeImage, + uiTableValueTypeInt, + uiTableValueTypeColor, }; // TODO I don't like this name -_UI_EXTERN uiTableDataType uiTableDataGetType(const uiTableData *d); +_UI_EXTERN uiTableValueType uiTableValueGetType(const uiTableValue *v); -_UI_EXTERN uiTableData *uiNewTableDataString(const char *str); -_UI_EXTERN const char *uiTableDataString(const uiTableData *d); +_UI_EXTERN uiTableValue *uiNewTableValueString(const char *str); +_UI_EXTERN const char *uiTableValueString(const uiTableValue *v); -_UI_EXTERN uiTableData *uiNewTableDataImage(uiImage *img); -_UI_EXTERN uiImage *uiTableDataImage(const uiTableData *d); +_UI_EXTERN uiTableValue *uiNewTableValueImage(uiImage *img); +_UI_EXTERN uiImage *uiTableValueImage(const uiTableValue *v); -_UI_EXTERN uiTableData *uiNewTableDataInt(int i); -_UI_EXTERN int uiTableDataInt(const uiTableData *d); +_UI_EXTERN uiTableValue *uiNewTableValueInt(int i); +_UI_EXTERN int uiTableValueInt(const uiTableValue *v); -_UI_EXTERN uiTableData *uiNewTableDataColor(double r, double g, double b, double a); -_UI_EXTERN void uiTableDataColor(const uiTableData *d, double *r, double *g, double *b, double *a); +_UI_EXTERN uiTableValue *uiNewTableValueColor(double r, double g, double b, double a); +_UI_EXTERN void uiTableValueColor(const uiTableValue *v, double *r, double *g, double *b, double *a); typedef struct uiTableModel uiTableModel; typedef struct uiTableModelHandler uiTableModelHandler; @@ -41,10 +41,10 @@ typedef struct uiTableModelHandler uiTableModelHandler; // TODO validate ranges; validate types on each getter/setter call (? table columns only?) struct uiTableModelHandler { int (*NumColumns)(uiTableModelHandler *, uiTableModel *); - uiTableDataType (*ColumnType)(uiTableModelHandler *, uiTableModel *, int); + uiTableValueType (*ColumnType)(uiTableModelHandler *, uiTableModel *, int); int (*NumRows)(uiTableModelHandler *, uiTableModel *); - uiTableData *(*CellValue)(uiTableModelHandler *, uiTableModel *, int, int); - void (*SetCellValue)(uiTableModelHandler *, uiTableModel *, int, int, const uiTableData *); + uiTableValue *(*CellValue)(uiTableModelHandler *, uiTableModel *, int, int); + void (*SetCellValue)(uiTableModelHandler *, uiTableModel *, int, int, const uiTableValue *); }; _UI_EXTERN uiTableModel *uiNewTableModel(uiTableModelHandler *mh); diff --git a/unix/table.c b/unix/table.c index 2e85fbd8..82755fb0 100644 --- a/unix/table.c +++ b/unix/table.c @@ -74,7 +74,7 @@ static void applyBackgroundColor(uiTable *t, GtkTreeModel *m, GtkTreeIter *iter, r, "cell-background-rgba", "cell-background-set"); } -static void onEdited(uiTableModel *m, int column, const char *pathstr, const uiTableData *data, GtkTreeIter *iter) +static void onEdited(uiTableModel *m, int column, const char *pathstr, const uiTableValue *tvalue, GtkTreeIter *iter) { GtkTreePath *path; int row; @@ -84,7 +84,7 @@ static void onEdited(uiTableModel *m, int column, const char *pathstr, const uiT if (iter != NULL) gtk_tree_model_get_iter(GTK_TREE_MODEL(m), iter, path); gtk_tree_path_free(path); - (*(m->mh->SetCellValue))(m->mh, m, row, column, data); + (*(m->mh->SetCellValue))(m->mh, m, row, column, tvalue); } // TODO deduplicate this between platforms @@ -123,12 +123,12 @@ static void textColumnDataFunc(GtkTreeViewColumn *c, GtkCellRenderer *r, GtkTree static void textColumnEdited(GtkCellRendererText *r, gchar *path, gchar *newText, gpointer data) { struct textColumnParams *p = (struct textColumnParams *) data; - uiTableData *tdata; + uiTableValue *tvalue; GtkTreeIter iter; - tdata = uiNewTableDataString(newText); - onEdited(p->m, p->modelColumn, path, tdata, &iter); - uiFreeTableData(tdata); + tvalue = uiNewTableValueString(newText); + onEdited(p->m, p->modelColumn, path, tvalue, &iter); + uiFreeTableValue(tvalue); // and update the column TODO copy comment here textColumnDataFunc(NULL, GTK_CELL_RENDERER(r), GTK_TREE_MODEL(p->m), &iter, data); } @@ -183,7 +183,7 @@ static void checkboxColumnToggled(GtkCellRendererToggle *r, gchar *pathstr, gpoi struct checkboxColumnParams *p = (struct checkboxColumnParams *) data; GValue value = G_VALUE_INIT; int v; - uiTableData *tdata; + uiTableValue *tvalue; GtkTreePath *path; GtkTreeIter iter; @@ -193,9 +193,9 @@ static void checkboxColumnToggled(GtkCellRendererToggle *r, gchar *pathstr, gpoi gtk_tree_model_get_value(GTK_TREE_MODEL(p->m), &iter, p->modelColumn, &value); v = g_value_get_int(&value); g_value_unset(&value); - tdata = uiNewTableDataInt(!v); - onEdited(p->m, p->modelColumn, pathstr, tdata, NULL); - uiFreeTableData(tdata); + tvalue = uiNewTableValueInt(!v); + onEdited(p->m, p->modelColumn, pathstr, tvalue, NULL); + uiFreeTableValue(tvalue); // and update the column TODO copy comment here // TODO avoid fetching the model data twice checkboxColumnDataFunc(NULL, GTK_CELL_RENDERER(r), GTK_TREE_MODEL(p->m), &iter, data); diff --git a/unix/tablemodel.c b/unix/tablemodel.c index 3c6ee6d3..fa36a14a 100644 --- a/unix/tablemodel.c +++ b/unix/tablemodel.c @@ -39,13 +39,13 @@ static GType uiTableModel_get_column_type(GtkTreeModel *mm, gint index) uiTableModel *m = uiTableModel(mm); switch ((*(m->mh->ColumnType))(m->mh, m, index)) { - case uiTableDataTypeString: + case uiTableValueTypeString: return G_TYPE_STRING; - case uiTableDataTypeImage: + case uiTableValueTypeImage: return G_TYPE_POINTER; - case uiTableDataTypeInt: + case uiTableValueTypeInt: return G_TYPE_INT; - case uiTableDataTypeColor: + case uiTableValueTypeColor: return GDK_TYPE_RGBA; } // TODO @@ -91,38 +91,38 @@ static void uiTableModel_get_value(GtkTreeModel *mm, GtkTreeIter *iter, gint col { uiTableModel *m = uiTableModel(mm); gint row; - uiTableData *data; + uiTableValue *tvalue; double r, g, b, a; GdkRGBA rgba; if (iter->stamp != STAMP_GOOD) return; row = GPOINTER_TO_INT(iter->user_data); - data = (*(m->mh->CellValue))(m->mh, m, row, column); + tvalue = (*(m->mh->CellValue))(m->mh, m, row, column); switch ((*(m->mh->ColumnType))(m->mh, m, column)) { - case uiTableDataTypeString: + case uiTableValueTypeString: g_value_init(value, G_TYPE_STRING); - g_value_set_string(value, uiTableDataString(data)); - uiFreeTableData(data); + g_value_set_string(value, uiTableValueString(tvalue)); + uiFreeTableValue(tvalue); return; - case uiTableDataTypeImage: + case uiTableValueTypeImage: g_value_init(value, G_TYPE_POINTER); - g_value_set_pointer(value, uiTableDataImage(data)); - uiFreeTableData(data); + g_value_set_pointer(value, uiTableValueImage(tvalue)); + uiFreeTableValue(tvalue); return; - case uiTableDataTypeInt: + case uiTableValueTypeInt: g_value_init(value, G_TYPE_INT); - g_value_set_int(value, uiTableDataInt(data)); - uiFreeTableData(data); + g_value_set_int(value, uiTableValueInt(tvalue)); + uiFreeTableValue(tvalue); return; - case uiTableDataTypeColor: + case uiTableValueTypeColor: g_value_init(value, GDK_TYPE_RGBA); - if (data == NULL) { + if (tvalue == NULL) { g_value_set_boxed(value, NULL); return; } - uiTableDataColor(data, &r, &g, &b, &a); - uiFreeTableData(data); + uiTableValueColor(tvalue, &r, &g, &b, &a); + uiFreeTableValue(tvalue); rgba.red = r; rgba.green = g; rgba.blue = b; diff --git a/windows/table.cpp b/windows/table.cpp index 315f5e37..625ee8d3 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -188,16 +188,16 @@ static LRESULT CALLBACK tableSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM int uiprivTableProgress(uiTable *t, int item, int subitem, int modelColumn, LONG *pos) { - uiTableData *data; + uiTableValue *value; int progress; std::pair p; std::map, LONG>::iterator iter; bool startTimer = false; bool stopTimer = false; - data = (*(t->model->mh->CellValue))(t->model->mh, t->model, item, modelColumn); - progress = uiTableDataInt(data); - uiFreeTableData(data); + value = (*(t->model->mh->CellValue))(t->model->mh, t->model, item, modelColumn); + progress = uiTableValueInt(value); + uiFreeTableValue(value); p.first = item; p.second = subitem; diff --git a/windows/tabledispinfo.cpp b/windows/tabledispinfo.cpp index f8a06842..25606903 100644 --- a/windows/tabledispinfo.cpp +++ b/windows/tabledispinfo.cpp @@ -8,7 +8,7 @@ static HRESULT handleLVIF_TEXT(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnParams *p) { int strcol; - uiTableData *data; + uiTableValue *value; WCHAR *wstr; int progress; HRESULT hr; @@ -22,9 +22,9 @@ static HRESULT handleLVIF_TEXT(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnP else if (p->buttonModelColumn != -1) strcol = p->buttonModelColumn; if (strcol != -1) { - data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->item.iItem, strcol); - wstr = toUTF16(uiTableDataString(data)); - uiFreeTableData(data); + value = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->item.iItem, strcol); + wstr = toUTF16(uiTableValueString(value)); + uiFreeTableValue(value); // We *could* just make pszText into a freshly allocated // conversion and avoid the limitation of cchTextMax. // But then, we would have to keep things around for some @@ -59,7 +59,7 @@ static HRESULT handleLVIF_TEXT(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnP static HRESULT handleLVIF_IMAGE(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnParams *p) { - uiTableData *data; + uiTableValue *value; HRESULT hr; if (nm->item.iSubItem == 0 && p->imageModelColumn == -1 && p->checkboxModelColumn == -1) { @@ -75,7 +75,7 @@ static HRESULT handleLVIF_IMAGE(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumn return S_OK; // nothing to do here // TODO see if the -1 part is correct - // TODO see if we should use state instead of images for checkbox data + // TODO see if we should use state instead of images for checkbox value nm->item.iImage = -1; if (p->imageModelColumn != -1 || p->checkboxModelColumn != -1) nm->item.iImage = 0; diff --git a/windows/tabledraw.cpp b/windows/tabledraw.cpp index c693d794..bf0f730d 100644 --- a/windows/tabledraw.cpp +++ b/windows/tabledraw.cpp @@ -67,7 +67,7 @@ static void centerImageRect(RECT *image, RECT *space) static HRESULT drawImagePart(HRESULT hr, struct drawState *s) { - uiTableData *data; + uiTableValue *value; IWICBitmap *wb; HBITMAP b; RECT r; @@ -78,9 +78,9 @@ static HRESULT drawImagePart(HRESULT hr, struct drawState *s) if (s->p->imageModelColumn == -1) return S_OK; - data = cellValue(s->model, s->iItem, s->p->imageModelColumn); - wb = uiprivImageAppropriateForDC(uiTableDataImage(data), s->dc); - uiFreeTableData(data); + value = cellValue(s->model, s->iItem, s->p->imageModelColumn); + wb = uiprivImageAppropriateForDC(uiTableValueImage(value), s->dc); + uiFreeTableValue(value); hr = uiprivWICToGDI(wb, s->dc, s->m->cxIcon, s->m->cyIcon, &b); if (hr != S_OK) @@ -188,7 +188,7 @@ static HRESULT drawThemedCheckbox(struct drawState *s, HTHEME theme, int checked static HRESULT drawCheckboxPart(HRESULT hr, struct drawState *s) { - uiTableData *data; + uiTableValue *value; int checked, enabled; HTHEME theme; @@ -197,9 +197,9 @@ static HRESULT drawCheckboxPart(HRESULT hr, struct drawState *s) if (s->p->checkboxModelColumn == -1) return S_OK; - data = cellValue(s->model, s->iItem, s->p->checkboxModelColumn); - checked = uiTableDataInt(data); - uiFreeTableData(data); + value = cellValue(s->model, s->iItem, s->p->checkboxModelColumn); + checked = uiTableValueInt(value); + uiFreeTableValue(value); switch (s->p->checkboxEditableColumn) { case uiTableModelColumnNeverEditable: enabled = 0; @@ -208,9 +208,9 @@ static HRESULT drawCheckboxPart(HRESULT hr, struct drawState *s) enabled = 1; break; default: - data = cellValue(s->model, s->iItem, s->p->checkboxEditableColumn); - enabled = uiTableDataInt(data); - uiFreeTableData(data); + value = cellValue(s->model, s->iItem, s->p->checkboxEditableColumn); + enabled = uiTableValueInt(value); + uiFreeTableValue(value); } theme = OpenThemeData(s->t->hwnd, L"button"); @@ -236,7 +236,7 @@ static HRESULT drawTextPart(HRESULT hr, struct drawState *s) COLORREF prevText; int prevMode; RECT r; - uiTableData *data; + uiTableValue *value; WCHAR *wstr; if (hr != S_OK) @@ -258,9 +258,9 @@ static HRESULT drawTextPart(HRESULT hr, struct drawState *s) return E_FAIL; } - data = cellValue(s->model, s->iItem, s->p->textModelColumn); - wstr = toUTF16(uiTableDataString(data)); - uiFreeTableData(data); + value = cellValue(s->model, s->iItem, s->p->textModelColumn); + wstr = toUTF16(uiTableValueString(value)); + uiFreeTableValue(value); // These flags are a menagerie of flags from various sources: // guessing, the Windows 2000 source leak, various custom // draw examples on the web, etc. @@ -402,7 +402,7 @@ fail: static HRESULT drawButtonPart(HRESULT hr, struct drawState *s) { - uiTableData *data; + uiTableValue *value; WCHAR *wstr; bool enabled; HTHEME theme; @@ -414,9 +414,9 @@ static HRESULT drawButtonPart(HRESULT hr, struct drawState *s) if (s->p->buttonModelColumn == -1) return S_OK; - data = cellValue(s->model, s->iItem, s->p->buttonModelColumn); - wstr = toUTF16(uiTableDataString(data)); - uiFreeTableData(data); + value = cellValue(s->model, s->iItem, s->p->buttonModelColumn); + wstr = toUTF16(uiTableValueString(value)); + uiFreeTableValue(value); switch (s->p->buttonClickableModelColumn) { case uiTableModelColumnNeverEditable: enabled = 0; @@ -425,9 +425,9 @@ static HRESULT drawButtonPart(HRESULT hr, struct drawState *s) enabled = 1; break; default: - data = cellValue(s->model, s->iItem, s->p->checkboxEditableColumn); - enabled = uiTableDataInt(data); - uiFreeTableData(data); + value = cellValue(s->model, s->iItem, s->p->checkboxEditableColumn); + enabled = uiTableValueInt(value); + uiFreeTableValue(value); } theme = OpenThemeData(s->t->hwnd, L"button"); @@ -574,16 +574,16 @@ static HRESULT fillDrawState(struct drawState *s, uiTable *t, NMLVCUSTOMDRAW *nm s->textColor = GetSysColor(COLOR_HIGHLIGHTTEXT); s->textBrush = GetSysColorBrush(COLOR_HIGHLIGHTTEXT); } else { - uiTableData *data; + uiTableValue *value; double r, g, b, a; s->bgColor = GetSysColor(COLOR_WINDOW); s->bgBrush = GetSysColorBrush(COLOR_WINDOW); if (t->backgroundColumn != -1) { - data = cellValue(s->model, s->iItem, t->backgroundColumn); - if (data != NULL) { - uiTableDataColor(data, &r, &g, &b, &a); - uiFreeTableData(data); + value = cellValue(s->model, s->iItem, t->backgroundColumn); + if (value != NULL) { + uiTableValueColor(value, &r, &g, &b, &a); + uiFreeTableValue(value); s->bgColor = blend(s->bgColor, r, g, b, a); s->bgBrush = CreateSolidBrush(s->bgColor); if (s->bgBrush == NULL) { @@ -597,10 +597,10 @@ static HRESULT fillDrawState(struct drawState *s, uiTable *t, NMLVCUSTOMDRAW *nm s->textColor = GetSysColor(COLOR_WINDOWTEXT); s->textBrush = GetSysColorBrush(COLOR_WINDOWTEXT); if (p->textParams.ColorModelColumn != -1) { - data = cellValue(s->model, s->iItem, p->textParams.ColorModelColumn); - if (data != NULL) { - uiTableDataColor(data, &r, &g, &b, &a); - uiFreeTableData(data); + value = cellValue(s->model, s->iItem, p->textParams.ColorModelColumn); + if (value != NULL) { + uiTableValueColor(value, &r, &g, &b, &a); + uiFreeTableValue(value); s->textColor = blend(s->bgColor, r, g, b, a); s->textBrush = CreateSolidBrush(s->textColor); if (s->textBrush == NULL) { diff --git a/windows/tableediting.cpp b/windows/tableediting.cpp index 4eb8d2d2..ccbfb825 100644 --- a/windows/tableediting.cpp +++ b/windows/tableediting.cpp @@ -95,7 +95,7 @@ static LRESULT CALLBACK editSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM static HRESULT openEditControl(uiTable *t, int iItem, int iSubItem, uiprivTableColumnParams *p) { - uiTableData *data; + uiTableValue *value; WCHAR *wstr; HRESULT hr; @@ -105,9 +105,9 @@ static HRESULT openEditControl(uiTable *t, int iItem, int iSubItem, uiprivTableC return hr; // the real list view creates the edit control with the string - data = (*(t->model->mh->CellValue))(t->model->mh, t->model, iItem, p->textModelColumn); - wstr = toUTF16(uiTableDataString(data)); - uiFreeTableData(data); + value = (*(t->model->mh->CellValue))(t->model->mh, t->model, iItem, p->textModelColumn); + wstr = toUTF16(uiTableValueString(value)); + uiFreeTableValue(value); // TODO copy WS_EX_RTLREADING t->edit = CreateWindowExW(0, L"EDIT", wstr, @@ -157,17 +157,17 @@ HRESULT uiprivTableResizeWhileEditing(uiTable *t) HRESULT uiprivTableFinishEditingText(uiTable *t) { uiprivTableColumnParams *p; - uiTableData *data; + uiTableValue *value; char *text; if (t->edit == NULL) return S_OK; text = uiWindowsWindowText(t->edit); - data = uiNewTableDataString(text); + value = uiNewTableValueString(text); uiFreeText(text); p = (*(t->columns))[t->editedSubitem]; - (*(t->model->mh->SetCellValue))(t->model->mh, t->model, t->editedItem, p->textModelColumn, data); - uiFreeTableData(data); + (*(t->model->mh->SetCellValue))(t->model->mh, t->model, t->editedItem, p->textModelColumn, value); + uiFreeTableValue(value); // always refresh the value in case the model rejected it if (SendMessageW(t->hwnd, LVM_UPDATE, (WPARAM) (t->editedItem), 0) == (LRESULT) (-1)) { logLastError(L"LVM_UPDATE"); @@ -199,7 +199,7 @@ HRESULT uiprivTableHandleNM_CLICK(uiTable *t, NMITEMACTIVATE *nm, LRESULT *lResu uiprivTableColumnParams *p; int modelColumn, editableColumn; bool text, checkbox; - uiTableData *data; + uiTableValue *value; int checked, editable; HRESULT hr; @@ -238,9 +238,9 @@ HRESULT uiprivTableHandleNM_CLICK(uiTable *t, NMITEMACTIVATE *nm, LRESULT *lResu case uiTableModelColumnAlwaysEditable: break; default: - data = (*(t->model->mh->CellValue))(t->model->mh, t->model, ht.iItem, editableColumn); - editable = uiTableDataInt(data); - uiFreeTableData(data); + value = (*(t->model->mh->CellValue))(t->model->mh, t->model, ht.iItem, editableColumn); + editable = uiTableValueInt(value); + uiFreeTableValue(value); if (!editable) goto done; } @@ -252,12 +252,12 @@ HRESULT uiprivTableHandleNM_CLICK(uiTable *t, NMITEMACTIVATE *nm, LRESULT *lResu } else if (checkbox) { if ((ht.flags & LVHT_ONITEMICON) == 0) goto done; - data = (*(t->model->mh->CellValue))(t->model->mh, t->model, ht.iItem, modelColumn); - checked = uiTableDataInt(data); - uiFreeTableData(data); - data = uiNewTableDataInt(!checked); - (*(t->model->mh->SetCellValue))(t->model->mh, t->model, ht.iItem, modelColumn, data); - uiFreeTableData(data); + value = (*(t->model->mh->CellValue))(t->model->mh, t->model, ht.iItem, modelColumn); + checked = uiTableValueInt(value); + uiFreeTableValue(value); + value = uiNewTableValueInt(!checked); + (*(t->model->mh->SetCellValue))(t->model->mh, t->model, ht.iItem, modelColumn, value); + uiFreeTableValue(value); } else (*(t->model->mh->SetCellValue))(t->model->mh, t->model, ht.iItem, modelColumn, NULL); // always refresh the value in case the model rejected it From 74ec21f4c715341c1e375a735376cf872119c3b3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 23 Jun 2018 23:35:42 -0400 Subject: [PATCH 1237/1329] Turned the direct method calls on uiTableModel into functions to make the call sites look a bit cleaner. More stuff will go into common/tablemodel.c... --- common/CMakeLists.txt | 1 + common/table.h | 17 +++++++++++++++ common/tablemodel.c | 44 +++++++++++++++++++++++++++++++++++++++ common/tablevalue.c | 1 + darwin/table.h | 1 + darwin/table.m | 9 ++++++-- darwin/tablecolumn.m | 23 +++++++++----------- unix/table.c | 2 +- unix/table.h | 1 + unix/tablemodel.c | 21 ++++++++++++------- windows/table.cpp | 13 ++++++++---- windows/table.hpp | 1 + windows/tabledispinfo.cpp | 2 +- windows/tabledraw.cpp | 19 +++++++---------- windows/tableediting.cpp | 12 +++++------ 15 files changed, 121 insertions(+), 46 deletions(-) create mode 100644 common/table.h create mode 100644 common/tablemodel.c diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 785c70db..0e1360d8 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -10,6 +10,7 @@ list(APPEND _LIBUI_SOURCES common/matrix.c common/opentype.c common/shouldquit.c + common/tablemodel.c common/tablevalue.c common/userbugs.c common/utf.c diff --git a/common/table.h b/common/table.h new file mode 100644 index 00000000..3c603674 --- /dev/null +++ b/common/table.h @@ -0,0 +1,17 @@ +// 23 june 2018 + +#ifdef __cplusplus +extern "C" { +#endif + +// tablemodel.c +extern uiTableModelHandler *uiprivTableModelHandler(uiTableModel *m); +extern int uiprivTableModelNumColumns(uiTableModel *m); +extern uiTableValueType uiprivTableModelColumnType(uiTableModel *m, int column); +extern int uiprivTableModelNumRows(uiTableModel *m); +extern uiTableValue *uiprivTableModelCellValue(uiTableModel *m, int row, int column); +extern void uiprivTableModelSetCellValue(uiTableModel *m, int row, int column, const uiTableValue *value); + +#ifdef __cplusplus +} +#endif diff --git a/common/tablemodel.c b/common/tablemodel.c new file mode 100644 index 00000000..c7c52a3f --- /dev/null +++ b/common/tablemodel.c @@ -0,0 +1,44 @@ +// 23 june 2018 +#include "../ui.h" +#include "uipriv.h" +#include "table.h" + +int uiprivTableModelNumColumns(uiTableModel *m) +{ + uiTableModelHandler *mh; + + mh = uiprivTableModelHandler(m); + return (*(mh->NumColumns))(mh, m); +} + +uiTableValueType uiprivTableModelColumnType(uiTableModel *m, int column) +{ + uiTableModelHandler *mh; + + mh = uiprivTableModelHandler(m); + return (*(mh->ColumnType))(mh, m, column); +} + +int uiprivTableModelNumRows(uiTableModel *m) +{ + uiTableModelHandler *mh; + + mh = uiprivTableModelHandler(m); + return (*(mh->NumRows))(mh, m); +} + +uiTableValue *uiprivTableModelCellValue(uiTableModel *m, int row, int column) +{ + uiTableModelHandler *mh; + + mh = uiprivTableModelHandler(m); + return (*(mh->CellValue))(mh, m, row, column); +} + +void uiprivTableModelSetCellValue(uiTableModel *m, int row, int column, const uiTableValue *value) +{ + uiTableModelHandler *mh; + + mh = uiprivTableModelHandler(m); + (*(mh->SetCellValue))(mh, m, row, column, value); +} diff --git a/common/tablevalue.c b/common/tablevalue.c index b73a920e..10043de6 100644 --- a/common/tablevalue.c +++ b/common/tablevalue.c @@ -1,6 +1,7 @@ // 3 june 2018 #include "../ui.h" #include "uipriv.h" +#include "table.h" struct uiTableValue { uiTableValueType type; diff --git a/darwin/table.h b/darwin/table.h index b38e9378..4146ab71 100644 --- a/darwin/table.h +++ b/darwin/table.h @@ -1,4 +1,5 @@ // 3 june 2018 +#import "../common/table.h" // table.m // TODO get rid of forward declaration diff --git a/darwin/table.m b/darwin/table.m index 378fb95d..f148472d 100644 --- a/darwin/table.m +++ b/darwin/table.m @@ -37,7 +37,7 @@ static void setBackgroundColor(uiprivTableView *t, NSTableRowView *rv, NSInteger if (t->uiprivT->backgroundColumn == -1) return; - value = (*(t->uiprivM->mh->CellValue))(t->uiprivM->mh, t->uiprivM, row, t->uiprivT->backgroundColumn); + value = uiprivTableModelCellValue(t->uiprivM, row, t->uiprivT->backgroundColumn); if (value != NULL) { uiTableValueColor(value, &r, &g, &b, &a); uiFreeTableValue(value); @@ -70,7 +70,7 @@ static void setBackgroundColor(uiprivTableView *t, NSTableRowView *rv, NSInteger - (NSInteger)numberOfRowsInTableView:(NSTableView *)tv { - return (*(self->m->mh->NumRows))(self->m->mh, self->m); + return uiprivTableModelNumRows(self->m); } - (NSView *)tableView:(NSTableView *)tv viewForTableColumn:(NSTableColumn *)cc row:(NSInteger)row @@ -155,6 +155,11 @@ void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) // set is autoreleased } +uiTableModelHandler *uiprivTableModelHandler(uiTableModel *m) +{ + return m->mh; +} + uiDarwinControlAllDefaultsExceptDestroy(uiTable, sv) static void uiTableDestroy(uiControl *c) diff --git a/darwin/tablecolumn.m b/darwin/tablecolumn.m index 2e2e8299..d2ff2577 100644 --- a/darwin/tablecolumn.m +++ b/darwin/tablecolumn.m @@ -45,7 +45,7 @@ static BOOL isCellEditable(uiTableModel *m, NSInteger row, int modelColumn) case uiTableModelColumnAlwaysEditable: return YES; } - value = (*(m->mh->CellValue))(m->mh, m, row, modelColumn); + value = uiprivTableModelCellValue(m, row, modelColumn); editable = uiTableValueInt(value); uiFreeTableValue(value); return editable != 0; @@ -271,7 +271,7 @@ struct textColumnCreateParams { NSString *str; NSColor *color; - value = (*(self->m->mh->CellValue))(self->m->mh, self->m, row, self->textModelColumn); + value = uiprivTableModelCellValue(self->m, row, self->textModelColumn); str = uiprivToNSString(uiTableValueString(value)); uiFreeTableValue(value); [self->tf setStringValue:str]; @@ -282,7 +282,7 @@ struct textColumnCreateParams { if (self->textParams.ColorModelColumn != -1) { double r, g, b, a; - value = (*(self->m->mh->CellValue))(self->m->mh, self->m, row, self->textParams.ColorModelColumn); + value = uiprivTableModelCellValue(self->m, row, self->textParams.ColorModelColumn); // TODO document this is allowed if (value != NULL) { uiTableValueColor(value, &r, &g, &b, &a); @@ -298,13 +298,13 @@ struct textColumnCreateParams { if (self->iv != nil) { uiImage *img; - value = (*(self->m->mh->CellValue))(self->m->mh, self->m, row, self->imageModelColumn); + value = uiprivTableModelCellValue(self->m, row, self->imageModelColumn); img = uiTableValueImage(value); uiFreeTableValue(value); [self->iv setImage:uiprivImageNSImage(img)]; } if (self->cb != nil) { - value = (*(self->m->mh->CellValue))(self->m->mh, self->m, row, self->imageModelColumn); + value = uiprivTableModelCellValue(self->m, row, self->imageModelColumn); if (uiTableValueInt(value) != 0) [self->cb setState:NSOnState]; else @@ -322,8 +322,7 @@ struct textColumnCreateParams { row = [self->t->tv rowForView:self->tf]; value = uiNewTableValueString([[self->tf stringValue] UTF8String]); - (*(self->m->mh->SetCellValue))(self->m->mh, self->m, - row, self->textModelColumn, value); + uiprivTableModelSetCellValue(self->m, row, self->textModelColumn, value); uiFreeTableValue(value); // always refresh the value in case the model rejected it // TODO document that we do this, but not for the whole row (or decide to do both, or do neither...) @@ -337,8 +336,7 @@ struct textColumnCreateParams { row = [self->t->tv rowForView:self->cb]; value = uiNewTableValueInt([self->cb state] != NSOffState); - (*(self->m->mh->SetCellValue))(self->m->mh, self->m, - row, self->checkboxModelColumn, value); + uiprivTableModelSetCellValue(self->m, row, self->checkboxModelColumn, value); uiFreeTableValue(value); // always refresh the value in case the model rejected it [self uiprivUpdate:row]; @@ -436,7 +434,7 @@ struct textColumnCreateParams { uiTableValue *value; int progress; - value = (*(self->m->mh->CellValue))(self->m->mh, self->m, row, self->modelColumn); + value = uiprivTableModelCellValue(self->m, row, self->modelColumn); progress = uiTableValueInt(value); uiFreeTableValue(value); if (progress == -1) { @@ -559,7 +557,7 @@ struct textColumnCreateParams { uiTableValue *value; NSString *str; - value = (*(self->m->mh->CellValue))(self->m->mh, self->m, row, self->modelColumn); + value = uiprivTableModelCellValue(self->m, row, self->modelColumn); str = uiprivToNSString(uiTableValueString(value)); uiFreeTableValue(value); [self->b setTitle:str]; @@ -572,8 +570,7 @@ struct textColumnCreateParams { NSInteger row; row = [self->t->tv rowForView:self->b]; - (*(self->m->mh->SetCellValue))(self->m->mh, self->m, - row, self->modelColumn, NULL); + uiprivTableModelSetCellValue(self->m, row, self->modelColumn, NULL); // TODO document we DON'T update the cell after doing this } diff --git a/unix/table.c b/unix/table.c index 82755fb0..cd9734d3 100644 --- a/unix/table.c +++ b/unix/table.c @@ -84,7 +84,7 @@ static void onEdited(uiTableModel *m, int column, const char *pathstr, const uiT if (iter != NULL) gtk_tree_model_get_iter(GTK_TREE_MODEL(m), iter, path); gtk_tree_path_free(path); - (*(m->mh->SetCellValue))(m->mh, m, row, column, tvalue); + uiprivTableModelSetCellValue(m, row, column, tvalue); } // TODO deduplicate this between platforms diff --git a/unix/table.h b/unix/table.h index a023e7f5..af7e954a 100644 --- a/unix/table.h +++ b/unix/table.h @@ -1,4 +1,5 @@ // 4 june 2018 +#include "../common/table.h" // tablemodel.c #define uiTableModelType (uiTableModel_get_type()) diff --git a/unix/tablemodel.c b/unix/tablemodel.c index fa36a14a..ee957b58 100644 --- a/unix/tablemodel.c +++ b/unix/tablemodel.c @@ -31,14 +31,14 @@ static gint uiTableModel_get_n_columns(GtkTreeModel *mm) { uiTableModel *m = uiTableModel(mm); - return (*(m->mh->NumColumns))(m->mh, m); + return uiprivTableModelNumColumns(m); } static GType uiTableModel_get_column_type(GtkTreeModel *mm, gint index) { uiTableModel *m = uiTableModel(mm); - switch ((*(m->mh->ColumnType))(m->mh, m, index)) { + switch (uiprivTableModelColumnType(m, index)) { case uiTableValueTypeString: return G_TYPE_STRING; case uiTableValueTypeImage: @@ -65,7 +65,7 @@ static gboolean uiTableModel_get_iter(GtkTreeModel *mm, GtkTreeIter *iter, GtkTr row = gtk_tree_path_get_indices(path)[0]; if (row < 0) goto bad; - if (row >= (*(m->mh->NumRows))(m->mh, m)) + if (row >= uiprivTableModelNumRows(m)) goto bad; iter->stamp = STAMP_GOOD; iter->user_data = GINT_TO_POINTER(row); @@ -98,8 +98,8 @@ static void uiTableModel_get_value(GtkTreeModel *mm, GtkTreeIter *iter, gint col if (iter->stamp != STAMP_GOOD) return; row = GPOINTER_TO_INT(iter->user_data); - tvalue = (*(m->mh->CellValue))(m->mh, m, row, column); - switch ((*(m->mh->ColumnType))(m->mh, m, column)) { + tvalue = uiprivTableModelCellValue(m, row, column); + switch (uiprivTableModelColumnType(m, column)) { case uiTableValueTypeString: g_value_init(value, G_TYPE_STRING); g_value_set_string(value, uiTableValueString(tvalue)); @@ -142,7 +142,7 @@ static gboolean uiTableModel_iter_next(GtkTreeModel *mm, GtkTreeIter *iter) return FALSE; row = GPOINTER_TO_INT(iter->user_data); row++; - if (row >= (*(m->mh->NumRows))(m->mh, m)) { + if (row >= uiprivTableModelNumRows(m)) { iter->stamp = STAMP_BAD; return FALSE; } @@ -182,7 +182,7 @@ static gint uiTableModel_iter_n_children(GtkTreeModel *mm, GtkTreeIter *iter) if (iter != NULL) return 0; - return (*(m->mh->NumRows))(m->mh, m); + return uiprivTableModelNumRows(m); } static gboolean uiTableModel_iter_nth_child(GtkTreeModel *mm, GtkTreeIter *iter, GtkTreeIter *parent, gint n) @@ -195,7 +195,7 @@ static gboolean uiTableModel_iter_nth_child(GtkTreeModel *mm, GtkTreeIter *iter, goto bad; if (n < 0) goto bad; - if (n >= (*(m->mh->NumRows))(m->mh, m)) + if (n >= uiprivTableModelNumRows(m)) goto bad; iter->stamp = STAMP_GOOD; iter->user_data = GINT_TO_POINTER(n); @@ -281,3 +281,8 @@ void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) gtk_tree_model_row_deleted(GTK_TREE_MODEL(m), path); gtk_tree_path_free(path); } + +uiTableModelHandler *uiprivTableModelHandler(uiTableModel *m) +{ + return m->mh; +} diff --git a/windows/table.cpp b/windows/table.cpp index 625ee8d3..372a699b 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -33,7 +33,7 @@ void uiTableModelRowInserted(uiTableModel *m, int newIndex) LVITEMW item; int newCount; - newCount = (*(m->mh->NumRows))(m->mh, m); + newCount = uiprivTableModelNumRows(m); ZeroMemory(&item, sizeof (LVITEMW)); item.mask = 0; item.iItem = newIndex; @@ -66,7 +66,7 @@ void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) { int newCount; - newCount = (*(m->mh->NumRows))(m->mh, m); + newCount = uiprivTableModelNumRows(m); newCount--; for (auto t : *(m->tables)) { // update selection state @@ -82,6 +82,11 @@ void uiTableModelRowDeleted(uiTableModel *m, int oldIndex) } } +uiTableModelHandler *uiprivTableModelHandler(uiTableModel *m) +{ + return m->mh; +} + // TODO explain all this static LRESULT CALLBACK tableSubProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIDSubclass, DWORD_PTR dwRefData) { @@ -195,7 +200,7 @@ int uiprivTableProgress(uiTable *t, int item, int subitem, int modelColumn, LONG bool startTimer = false; bool stopTimer = false; - value = (*(t->model->mh->CellValue))(t->model->mh, t->model, item, modelColumn); + value = uiprivTableModelCellValue(t->model, item, modelColumn); progress = uiTableValueInt(value); uiFreeTableValue(value); @@ -508,7 +513,7 @@ uiTable *uiNewTable(uiTableModel *model) SendMessageW(t->hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, (WPARAM) (LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP | LVS_EX_SUBITEMIMAGES), (LPARAM) (LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP | LVS_EX_SUBITEMIMAGES)); - n = (*(model->mh->NumRows))(model->mh, model); + n = uiprivTableModelNumRows(model); if (SendMessageW(t->hwnd, LVM_SETITEMCOUNT, (WPARAM) n, 0) == 0) logLastError(L"error calling LVM_SETITEMCOUNT in uiNewTable()"); diff --git a/windows/table.hpp b/windows/table.hpp index e997395f..65a70fae 100644 --- a/windows/table.hpp +++ b/windows/table.hpp @@ -1,4 +1,5 @@ // 10 june 2018 +#include "../common/table.h" // table.cpp #define uiprivNumLVN_GETDISPINFOSkip 3 diff --git a/windows/tabledispinfo.cpp b/windows/tabledispinfo.cpp index 25606903..4198a1a2 100644 --- a/windows/tabledispinfo.cpp +++ b/windows/tabledispinfo.cpp @@ -22,7 +22,7 @@ static HRESULT handleLVIF_TEXT(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColumnP else if (p->buttonModelColumn != -1) strcol = p->buttonModelColumn; if (strcol != -1) { - value = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->item.iItem, strcol); + value = uiprivTableModelCellValue(t->model, nm->item.iItem, strcol); wstr = toUTF16(uiTableValueString(value)); uiFreeTableValue(value); // We *could* just make pszText into a freshly allocated diff --git a/windows/tabledraw.cpp b/windows/tabledraw.cpp index bf0f730d..ffa901fc 100644 --- a/windows/tabledraw.cpp +++ b/windows/tabledraw.cpp @@ -5,9 +5,6 @@ // TODOs: // - properly hide selection when not focused (or switch on LVS_SHOWSELALWAYS and draw that state) -// TODO -#define cellValue(model, row, column) ((*((model)->mh->CellValue))((model)->mh, (model), (row), (column))) - // TODO maybe split this into item and subitem structs? struct drawState { uiTable *t; @@ -78,7 +75,7 @@ static HRESULT drawImagePart(HRESULT hr, struct drawState *s) if (s->p->imageModelColumn == -1) return S_OK; - value = cellValue(s->model, s->iItem, s->p->imageModelColumn); + value = uiprivTableModelCellValue(s->model, s->iItem, s->p->imageModelColumn); wb = uiprivImageAppropriateForDC(uiTableValueImage(value), s->dc); uiFreeTableValue(value); @@ -197,7 +194,7 @@ static HRESULT drawCheckboxPart(HRESULT hr, struct drawState *s) if (s->p->checkboxModelColumn == -1) return S_OK; - value = cellValue(s->model, s->iItem, s->p->checkboxModelColumn); + value = uiprivTableModelCellValue(s->model, s->iItem, s->p->checkboxModelColumn); checked = uiTableValueInt(value); uiFreeTableValue(value); switch (s->p->checkboxEditableColumn) { @@ -208,7 +205,7 @@ static HRESULT drawCheckboxPart(HRESULT hr, struct drawState *s) enabled = 1; break; default: - value = cellValue(s->model, s->iItem, s->p->checkboxEditableColumn); + value = uiprivTableModelCellValue(s->model, s->iItem, s->p->checkboxEditableColumn); enabled = uiTableValueInt(value); uiFreeTableValue(value); } @@ -258,7 +255,7 @@ static HRESULT drawTextPart(HRESULT hr, struct drawState *s) return E_FAIL; } - value = cellValue(s->model, s->iItem, s->p->textModelColumn); + value = uiprivTableModelCellValue(s->model, s->iItem, s->p->textModelColumn); wstr = toUTF16(uiTableValueString(value)); uiFreeTableValue(value); // These flags are a menagerie of flags from various sources: @@ -414,7 +411,7 @@ static HRESULT drawButtonPart(HRESULT hr, struct drawState *s) if (s->p->buttonModelColumn == -1) return S_OK; - value = cellValue(s->model, s->iItem, s->p->buttonModelColumn); + value = uiprivTableModelCellValue(s->model, s->iItem, s->p->buttonModelColumn); wstr = toUTF16(uiTableValueString(value)); uiFreeTableValue(value); switch (s->p->buttonClickableModelColumn) { @@ -425,7 +422,7 @@ static HRESULT drawButtonPart(HRESULT hr, struct drawState *s) enabled = 1; break; default: - value = cellValue(s->model, s->iItem, s->p->checkboxEditableColumn); + value = uiprivTableModelCellValue(s->model, s->iItem, s->p->checkboxEditableColumn); enabled = uiTableValueInt(value); uiFreeTableValue(value); } @@ -580,7 +577,7 @@ static HRESULT fillDrawState(struct drawState *s, uiTable *t, NMLVCUSTOMDRAW *nm s->bgColor = GetSysColor(COLOR_WINDOW); s->bgBrush = GetSysColorBrush(COLOR_WINDOW); if (t->backgroundColumn != -1) { - value = cellValue(s->model, s->iItem, t->backgroundColumn); + value = uiprivTableModelCellValue(s->model, s->iItem, t->backgroundColumn); if (value != NULL) { uiTableValueColor(value, &r, &g, &b, &a); uiFreeTableValue(value); @@ -597,7 +594,7 @@ static HRESULT fillDrawState(struct drawState *s, uiTable *t, NMLVCUSTOMDRAW *nm s->textColor = GetSysColor(COLOR_WINDOWTEXT); s->textBrush = GetSysColorBrush(COLOR_WINDOWTEXT); if (p->textParams.ColorModelColumn != -1) { - value = cellValue(s->model, s->iItem, p->textParams.ColorModelColumn); + value = uiprivTableModelCellValue(s->model, s->iItem, p->textParams.ColorModelColumn); if (value != NULL) { uiTableValueColor(value, &r, &g, &b, &a); uiFreeTableValue(value); diff --git a/windows/tableediting.cpp b/windows/tableediting.cpp index ccbfb825..f96e70e6 100644 --- a/windows/tableediting.cpp +++ b/windows/tableediting.cpp @@ -105,7 +105,7 @@ static HRESULT openEditControl(uiTable *t, int iItem, int iSubItem, uiprivTableC return hr; // the real list view creates the edit control with the string - value = (*(t->model->mh->CellValue))(t->model->mh, t->model, iItem, p->textModelColumn); + value = uiprivTableModelCellValue(t->model, iItem, p->textModelColumn); wstr = toUTF16(uiTableValueString(value)); uiFreeTableValue(value); // TODO copy WS_EX_RTLREADING @@ -166,7 +166,7 @@ HRESULT uiprivTableFinishEditingText(uiTable *t) value = uiNewTableValueString(text); uiFreeText(text); p = (*(t->columns))[t->editedSubitem]; - (*(t->model->mh->SetCellValue))(t->model->mh, t->model, t->editedItem, p->textModelColumn, value); + uiprivTableModelSetCellValue(t->model, t->editedItem, p->textModelColumn, value); uiFreeTableValue(value); // always refresh the value in case the model rejected it if (SendMessageW(t->hwnd, LVM_UPDATE, (WPARAM) (t->editedItem), 0) == (LRESULT) (-1)) { @@ -238,7 +238,7 @@ HRESULT uiprivTableHandleNM_CLICK(uiTable *t, NMITEMACTIVATE *nm, LRESULT *lResu case uiTableModelColumnAlwaysEditable: break; default: - value = (*(t->model->mh->CellValue))(t->model->mh, t->model, ht.iItem, editableColumn); + value = uiprivTableModelCellValue(t->model, ht.iItem, editableColumn); editable = uiTableValueInt(value); uiFreeTableValue(value); if (!editable) @@ -252,14 +252,14 @@ HRESULT uiprivTableHandleNM_CLICK(uiTable *t, NMITEMACTIVATE *nm, LRESULT *lResu } else if (checkbox) { if ((ht.flags & LVHT_ONITEMICON) == 0) goto done; - value = (*(t->model->mh->CellValue))(t->model->mh, t->model, ht.iItem, modelColumn); + value = uiprivTableModelCellValue(t->model, ht.iItem, modelColumn); checked = uiTableValueInt(value); uiFreeTableValue(value); value = uiNewTableValueInt(!checked); - (*(t->model->mh->SetCellValue))(t->model->mh, t->model, ht.iItem, modelColumn, value); + uiprivTableModelSetCellValue(t->model, ht.iItem, modelColumn, value); uiFreeTableValue(value); } else - (*(t->model->mh->SetCellValue))(t->model->mh, t->model, ht.iItem, modelColumn, NULL); + uiprivTableModelSetCellValue(t->model, ht.iItem, modelColumn, NULL); // always refresh the value in case the model rejected it if (SendMessageW(t->hwnd, LVM_UPDATE, (WPARAM) (ht.iItem), 0) == (LRESULT) (-1)) { logLastError(L"LVM_UPDATE"); From 8f4598f6419cfaeb02950b1cc7856895dc1411ce Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 23 Jun 2018 23:45:58 -0400 Subject: [PATCH 1238/1329] Finally removed the extra uiImage declarations that were there for long-irrelevant compiler errors. --- darwin/uipriv_darwin.h | 2 -- unix/uipriv_unix.h | 1 - 2 files changed, 3 deletions(-) diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h index bc8b1837..5d50f623 100644 --- a/darwin/uipriv_darwin.h +++ b/darwin/uipriv_darwin.h @@ -23,8 +23,6 @@ #define NSAppKitVersionNumber10_9 1265 #endif -/*TODO remove this*/typedef struct uiImage uiImage; - // map.m typedef struct uiprivMap uiprivMap; extern uiprivMap *uiprivNewMap(void); diff --git a/unix/uipriv_unix.h b/unix/uipriv_unix.h index 27fd673b..de604ced 100644 --- a/unix/uipriv_unix.h +++ b/unix/uipriv_unix.h @@ -48,7 +48,6 @@ extern uiDrawContext *uiprivNewContext(cairo_t *cr, GtkStyleContext *style); extern void uiprivFreeContext(uiDrawContext *); // image.c -/*TODO remove this*/typedef struct uiImage uiImage; extern cairo_surface_t *uiprivImageAppropriateSurface(uiImage *i, GtkWidget *w); // cellrendererbutton.c From 9b340ed40c40254b0df2490565e00735d34eab8c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 23 Jun 2018 23:48:47 -0400 Subject: [PATCH 1239/1329] Fixed checkboxes on Mac OS X. --- darwin/tablecolumn.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/darwin/tablecolumn.m b/darwin/tablecolumn.m index d2ff2577..d37daf8a 100644 --- a/darwin/tablecolumn.m +++ b/darwin/tablecolumn.m @@ -304,7 +304,7 @@ struct textColumnCreateParams { [self->iv setImage:uiprivImageNSImage(img)]; } if (self->cb != nil) { - value = uiprivTableModelCellValue(self->m, row, self->imageModelColumn); + value = uiprivTableModelCellValue(self->m, row, self->checkboxModelColumn); if (uiTableValueInt(value) != 0) [self->cb setState:NSOnState]; else From 4dbf1994a63d3c7a8e9ca90237b1bbcf08fd01bd Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 24 Jun 2018 00:45:54 -0400 Subject: [PATCH 1240/1329] Deduplicated the default text column parameters across platforms. --- common/table.h | 1 + common/tablemodel.c | 4 ++++ darwin/tablecolumn.m | 25 ++++++++++++------------- unix/table.c | 7 +------ windows/table.cpp | 6 +----- 5 files changed, 19 insertions(+), 24 deletions(-) diff --git a/common/table.h b/common/table.h index 3c603674..21ce2d11 100644 --- a/common/table.h +++ b/common/table.h @@ -11,6 +11,7 @@ extern uiTableValueType uiprivTableModelColumnType(uiTableModel *m, int column); extern int uiprivTableModelNumRows(uiTableModel *m); extern uiTableValue *uiprivTableModelCellValue(uiTableModel *m, int row, int column); extern void uiprivTableModelSetCellValue(uiTableModel *m, int row, int column, const uiTableValue *value); +extern const uiTableTextColumnOptionalParams uiprivDefaultTextColumnOptionalParams; #ifdef __cplusplus } diff --git a/common/tablemodel.c b/common/tablemodel.c index c7c52a3f..b1cd1e89 100644 --- a/common/tablemodel.c +++ b/common/tablemodel.c @@ -42,3 +42,7 @@ void uiprivTableModelSetCellValue(uiTableModel *m, int row, int column, const ui mh = uiprivTableModelHandler(m); (*(mh->SetCellValue))(mh, m, row, column, value); } + +const uiTableTextColumnOptionalParams uiprivDefaultTextColumnOptionalParams = { + .ColorModelColumn = -1, +}; diff --git a/darwin/tablecolumn.m b/darwin/tablecolumn.m index d37daf8a..5547336d 100644 --- a/darwin/tablecolumn.m +++ b/darwin/tablecolumn.m @@ -51,10 +51,6 @@ static BOOL isCellEditable(uiTableModel *m, NSInteger row, int modelColumn) return editable != 0; } -static uiTableTextColumnOptionalParams defaultTextColumnOptionalParams = { - .ColorModelColumn = -1, -}; - struct textColumnCreateParams { uiTable *t; uiTableModel *m; @@ -623,9 +619,10 @@ void uiTableAppendTextColumn(uiTable *t, const char *name, int textModelColumn, p.makeTextField = YES; p.textModelColumn = textModelColumn; p.textEditableColumn = textEditableModelColumn; - if (params == NULL) - params = &defaultTextColumnOptionalParams; - p.textParams = *params; + if (params != NULL) + p.textParams = *params; + else + p.textParams = uiprivDefaultTextColumnOptionalParams; str = [NSString stringWithUTF8String:name]; col = [[uiprivTextImageCheckboxTableColumn alloc] initWithIdentifier:str params:&p]; @@ -665,9 +662,10 @@ void uiTableAppendImageTextColumn(uiTable *t, const char *name, int imageModelCo p.makeTextField = YES; p.textModelColumn = textModelColumn; p.textEditableColumn = textEditableModelColumn; - if (textParams == NULL) - textParams = &defaultTextColumnOptionalParams; - p.textParams = *textParams; + if (textParams != NULL) + p.textParams = *textParams; + else + p.textParams = uiprivDefaultTextColumnOptionalParams; p.makeImage = YES; p.imageModelColumn = imageModelColumn; @@ -711,9 +709,10 @@ void uiTableAppendCheckboxTextColumn(uiTable *t, const char *name, int checkboxM p.makeTextField = YES; p.textModelColumn = textModelColumn; p.textEditableColumn = textEditableModelColumn; - if (textParams == NULL) - textParams = &defaultTextColumnOptionalParams; - p.textParams = *textParams; + if (textParams != NULL) + p.textParams = *textParams; + else + p.textParams = uiprivDefaultTextColumnOptionalParams; p.makeCheckbox = YES; p.checkboxModelColumn = checkboxModelColumn; diff --git a/unix/table.c b/unix/table.c index cd9734d3..8fae7960 100644 --- a/unix/table.c +++ b/unix/table.c @@ -87,11 +87,6 @@ static void onEdited(uiTableModel *m, int column, const char *pathstr, const uiT uiprivTableModelSetCellValue(m, row, column, tvalue); } -// TODO deduplicate this between platforms -static uiTableTextColumnOptionalParams defaultTextColumnOptionalParams = { - .ColorModelColumn = -1, -}; - struct textColumnParams { uiTable *t; uiTableModel *m; @@ -282,7 +277,7 @@ static void addTextColumn(uiTable *t, GtkTreeViewColumn *c, int textModelColumn, if (params != NULL) p->params = *params; else - p->params = defaultTextColumnOptionalParams; + p->params = uiprivDefaultTextColumnOptionalParams; r = gtk_cell_renderer_text_new(); gtk_tree_view_column_pack_start(c, r, TRUE); diff --git a/windows/table.cpp b/windows/table.cpp index 372a699b..801f65d0 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -7,10 +7,6 @@ // - implement keyboard accessibility // - implement accessibility in general (Dynamic Annotations maybe?) -static uiTableTextColumnOptionalParams defaultTextColumnOptionalParams = { - /*TODO.ColorModelColumn = */-1, -}; - uiTableModel *uiNewTableModel(uiTableModelHandler *mh) { uiTableModel *m; @@ -401,7 +397,7 @@ static uiprivTableColumnParams *appendColumn(uiTable *t, const char *name, int c p = uiprivNew(uiprivTableColumnParams); p->textModelColumn = -1; p->textEditableColumn = -1; - p->textParams = defaultTextColumnOptionalParams; + p->textParams = uiprivDefaultTextColumnOptionalParams; p->imageModelColumn = -1; p->checkboxModelColumn = -1; p->checkboxEditableColumn = -1; From 72c7c05f0458039d5e44a5ed8760c25ce66fd767 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 24 Jun 2018 09:52:01 -0400 Subject: [PATCH 1241/1329] Fixed up AddColumn parameter and private fields names; made them consistent. --- darwin/tablecolumn.m | 36 ++++++++++++++++++------------------ uitable.h | 4 ++-- unix/table.c | 14 +++++++------- windows/table.cpp | 24 ++++++++++++------------ windows/table.hpp | 4 ++-- windows/tabledraw.cpp | 6 +++--- windows/tableediting.cpp | 4 ++-- 7 files changed, 46 insertions(+), 46 deletions(-) diff --git a/darwin/tablecolumn.m b/darwin/tablecolumn.m index 5547336d..7dac3304 100644 --- a/darwin/tablecolumn.m +++ b/darwin/tablecolumn.m @@ -57,7 +57,7 @@ struct textColumnCreateParams { BOOL makeTextField; int textModelColumn; - int textEditableColumn; + int textEditableModelColumn; uiTableTextColumnOptionalParams textParams; BOOL makeImage; @@ -65,7 +65,7 @@ struct textColumnCreateParams { BOOL makeCheckbox; int checkboxModelColumn; - int checkboxEditableColumn; + int checkboxEditableModelColumn; }; @interface uiprivTextImageCheckboxTableCellView : uiprivTableCellView { @@ -74,7 +74,7 @@ struct textColumnCreateParams { NSTextField *tf; int textModelColumn; - int textEditableColumn; + int textEditableModelColumn; uiTableTextColumnOptionalParams textParams; NSImageView *iv; @@ -82,7 +82,7 @@ struct textColumnCreateParams { NSButton *cb; int checkboxModelColumn; - int checkboxEditableColumn; + int checkboxEditableModelColumn; } - (id)initWithFrame:(NSRect)r params:(struct textColumnCreateParams *)p; - (IBAction)uiprivOnTextFieldAction:(id)sender; @@ -104,7 +104,7 @@ struct textColumnCreateParams { self->tf = nil; if (p->makeTextField) { self->textModelColumn = p->textModelColumn; - self->textEditableColumn = p->textEditableColumn; + self->textEditableModelColumn = p->textEditableModelColumn; self->textParams = p->textParams; self->tf = uiprivNewLabel(@""); @@ -189,7 +189,7 @@ struct textColumnCreateParams { self->cb = nil; if (p->makeCheckbox) { self->checkboxModelColumn = p->checkboxModelColumn; - self->checkboxEditableColumn = p->checkboxEditableColumn; + self->checkboxEditableModelColumn = p->checkboxEditableModelColumn; self->cb = [[NSButton alloc] initWithFrame:NSZeroRect]; [self->cb setTitle:@""]; @@ -272,7 +272,7 @@ struct textColumnCreateParams { uiFreeTableValue(value); [self->tf setStringValue:str]; - [self->tf setEditable:isCellEditable(self->m, row, self->textEditableColumn)]; + [self->tf setEditable:isCellEditable(self->m, row, self->textEditableModelColumn)]; color = nil; if (self->textParams.ColorModelColumn != -1) { @@ -307,7 +307,7 @@ struct textColumnCreateParams { [self->cb setState:NSOffState]; uiFreeTableValue(value); - [self->cb setEnabled:isCellEditable(self->m, row, self->checkboxEditableColumn)]; + [self->cb setEnabled:isCellEditable(self->m, row, self->checkboxEditableModelColumn)]; } } @@ -606,7 +606,7 @@ struct textColumnCreateParams { @end -void uiTableAppendTextColumn(uiTable *t, const char *name, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *params) +void uiTableAppendTextColumn(uiTable *t, const char *name, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *textParams) { struct textColumnCreateParams p; uiprivTableColumn *col; @@ -618,9 +618,9 @@ void uiTableAppendTextColumn(uiTable *t, const char *name, int textModelColumn, p.makeTextField = YES; p.textModelColumn = textModelColumn; - p.textEditableColumn = textEditableModelColumn; - if (params != NULL) - p.textParams = *params; + p.textEditableModelColumn = textEditableModelColumn; + if (textParams != NULL) + p.textParams = *textParams; else p.textParams = uiprivDefaultTextColumnOptionalParams; @@ -661,7 +661,7 @@ void uiTableAppendImageTextColumn(uiTable *t, const char *name, int imageModelCo p.makeTextField = YES; p.textModelColumn = textModelColumn; - p.textEditableColumn = textEditableModelColumn; + p.textEditableModelColumn = textEditableModelColumn; if (textParams != NULL) p.textParams = *textParams; else @@ -688,7 +688,7 @@ void uiTableAppendCheckboxColumn(uiTable *t, const char *name, int checkboxModel p.makeCheckbox = YES; p.checkboxModelColumn = checkboxModelColumn; - p.checkboxEditableColumn = checkboxEditableModelColumn; + p.checkboxEditableModelColumn = checkboxEditableModelColumn; str = [NSString stringWithUTF8String:name]; col = [[uiprivTextImageCheckboxTableColumn alloc] initWithIdentifier:str params:&p]; @@ -708,7 +708,7 @@ void uiTableAppendCheckboxTextColumn(uiTable *t, const char *name, int checkboxM p.makeTextField = YES; p.textModelColumn = textModelColumn; - p.textEditableColumn = textEditableModelColumn; + p.textEditableModelColumn = textEditableModelColumn; if (textParams != NULL) p.textParams = *textParams; else @@ -716,7 +716,7 @@ void uiTableAppendCheckboxTextColumn(uiTable *t, const char *name, int checkboxM p.makeCheckbox = YES; p.checkboxModelColumn = checkboxModelColumn; - p.checkboxEditableColumn = checkboxEditableModelColumn; + p.checkboxEditableModelColumn = checkboxEditableModelColumn; str = [NSString stringWithUTF8String:name]; col = [[uiprivTextImageCheckboxTableColumn alloc] initWithIdentifier:str params:&p]; @@ -735,13 +735,13 @@ void uiTableAppendProgressBarColumn(uiTable *t, const char *name, int progressMo [t->tv addTableColumn:col]; } -void uiTableAppendButtonColumn(uiTable *t, const char *name, int buttonTextModelColumn, int buttonClickableModelColumn) +void uiTableAppendButtonColumn(uiTable *t, const char *name, int buttonModelColumn, int buttonClickableModelColumn) { uiprivTableColumn *col; NSString *str; str = [NSString stringWithUTF8String:name]; - col = [[uiprivButtonTableColumn alloc] initWithIdentifier:str table:t model:t->m modelColumn:buttonTextModelColumn editableColumn:buttonClickableModelColumn]; + col = [[uiprivButtonTableColumn alloc] initWithIdentifier:str table:t model:t->m modelColumn:buttonModelColumn editableColumn:buttonClickableModelColumn]; [col setTitle:str]; [t->tv addTableColumn:col]; } diff --git a/uitable.h b/uitable.h index 7a58b792..80ed1887 100644 --- a/uitable.h +++ b/uitable.h @@ -69,7 +69,7 @@ _UI_EXTERN void uiTableAppendTextColumn(uiTable *t, const char *name, int textModelColumn, int textEditableModelColumn, - uiTableTextColumnOptionalParams *params); + uiTableTextColumnOptionalParams *textParams); _UI_EXTERN void uiTableAppendImageColumn(uiTable *t, const char *name, int imageModelColumn); @@ -95,7 +95,7 @@ _UI_EXTERN void uiTableAppendProgressBarColumn(uiTable *t, int progressModelColumn); _UI_EXTERN void uiTableAppendButtonColumn(uiTable *t, const char *name, - int buttonTextModelColumn, + int buttonModelColumn, int buttonClickableModelColumn); // TODO getter? _UI_EXTERN void uiTableSetRowBackgroundColorModelColumn(uiTable *t, int modelColumn); diff --git a/unix/table.c b/unix/table.c index 8fae7960..97750cae 100644 --- a/unix/table.c +++ b/unix/table.c @@ -263,7 +263,7 @@ static GtkTreeViewColumn *addColumn(uiTable *t, const char *name) return c; } -static void addTextColumn(uiTable *t, GtkTreeViewColumn *c, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *params) +static void addTextColumn(uiTable *t, GtkTreeViewColumn *c, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *textParams) { struct textColumnParams *p; GtkCellRenderer *r; @@ -274,8 +274,8 @@ static void addTextColumn(uiTable *t, GtkTreeViewColumn *c, int textModelColumn, p->m = t->model; p->modelColumn = textModelColumn; p->editableColumn = textEditableModelColumn; - if (params != NULL) - p->params = *params; + if (textParams != NULL) + p->params = *textParams; else p->params = uiprivDefaultTextColumnOptionalParams; @@ -287,12 +287,12 @@ static void addTextColumn(uiTable *t, GtkTreeViewColumn *c, int textModelColumn, } // TODO rename modelCOlumn and params everywhere -void uiTableAppendTextColumn(uiTable *t, const char *name, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *params) +void uiTableAppendTextColumn(uiTable *t, const char *name, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *textParams) { GtkTreeViewColumn *c; c = addColumn(t, name); - addTextColumn(t, c, textModelColumn, textEditableModelColumn, params); + addTextColumn(t, c, textModelColumn, textEditableModelColumn, textParams); } static void addImageColumn(uiTable *t, GtkTreeViewColumn *c, int imageModelColumn) @@ -381,7 +381,7 @@ void uiTableAppendProgressBarColumn(uiTable *t, const char *name, int progressMo g_ptr_array_add(t->columnParams, p); } -void uiTableAppendButtonColumn(uiTable *t, const char *name, int buttonTextModelColumn, int buttonClickableModelColumn) +void uiTableAppendButtonColumn(uiTable *t, const char *name, int buttonModelColumn, int buttonClickableModelColumn) { GtkTreeViewColumn *c; struct buttonColumnParams *p; @@ -392,7 +392,7 @@ void uiTableAppendButtonColumn(uiTable *t, const char *name, int buttonTextModel p = uiprivNew(struct buttonColumnParams); p->t = t; p->m = t->model; - p->modelColumn = buttonTextModelColumn; + p->modelColumn = buttonModelColumn; p->clickableColumn = buttonClickableModelColumn; r = uiprivNewCellRendererButton(); diff --git a/windows/table.cpp b/windows/table.cpp index 801f65d0..2c821b6e 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -396,26 +396,26 @@ static uiprivTableColumnParams *appendColumn(uiTable *t, const char *name, int c p = uiprivNew(uiprivTableColumnParams); p->textModelColumn = -1; - p->textEditableColumn = -1; + p->textEditableModelColumn = -1; p->textParams = uiprivDefaultTextColumnOptionalParams; p->imageModelColumn = -1; p->checkboxModelColumn = -1; - p->checkboxEditableColumn = -1; + p->checkboxEditableModelColumn = -1; p->progressBarModelColumn = -1; p->buttonModelColumn = -1; t->columns->push_back(p); return p; } -void uiTableAppendTextColumn(uiTable *t, const char *name, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *params) +void uiTableAppendTextColumn(uiTable *t, const char *name, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *textParams) { uiprivTableColumnParams *p; p = appendColumn(t, name, LVCFMT_LEFT); p->textModelColumn = textModelColumn; - p->textEditableColumn = textEditableModelColumn; - if (params != NULL) - p->textParams = *params; + p->textEditableModelColumn = textEditableModelColumn; + if (textParams != NULL) + p->textParams = *textParams; } void uiTableAppendImageColumn(uiTable *t, const char *name, int imageModelColumn) @@ -432,7 +432,7 @@ void uiTableAppendImageTextColumn(uiTable *t, const char *name, int imageModelCo p = appendColumn(t, name, LVCFMT_LEFT); p->textModelColumn = textModelColumn; - p->textEditableColumn = textEditableModelColumn; + p->textEditableModelColumn = textEditableModelColumn; if (textParams != NULL) p->textParams = *textParams; p->imageModelColumn = imageModelColumn; @@ -444,7 +444,7 @@ void uiTableAppendCheckboxColumn(uiTable *t, const char *name, int checkboxModel p = appendColumn(t, name, LVCFMT_LEFT); p->checkboxModelColumn = checkboxModelColumn; - p->checkboxEditableColumn = checkboxEditableModelColumn; + p->checkboxEditableModelColumn = checkboxEditableModelColumn; } void uiTableAppendCheckboxTextColumn(uiTable *t, const char *name, int checkboxModelColumn, int checkboxEditableModelColumn, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *textParams) @@ -453,11 +453,11 @@ void uiTableAppendCheckboxTextColumn(uiTable *t, const char *name, int checkboxM p = appendColumn(t, name, LVCFMT_LEFT); p->textModelColumn = textModelColumn; - p->textEditableColumn = textEditableModelColumn; + p->textEditableModelColumn = textEditableModelColumn; if (textParams != NULL) p->textParams = *textParams; p->checkboxModelColumn = checkboxModelColumn; - p->checkboxEditableColumn = checkboxEditableModelColumn; + p->checkboxEditableModelColumn = checkboxEditableModelColumn; } void uiTableAppendProgressBarColumn(uiTable *t, const char *name, int progressModelColumn) @@ -468,13 +468,13 @@ void uiTableAppendProgressBarColumn(uiTable *t, const char *name, int progressMo p->progressBarModelColumn = progressModelColumn; } -void uiTableAppendButtonColumn(uiTable *t, const char *name, int buttonTextModelColumn, int buttonClickableModelColumn) +void uiTableAppendButtonColumn(uiTable *t, const char *name, int buttonModelColumn, int buttonClickableModelColumn) { uiprivTableColumnParams *p; // TODO see if we can get rid of this parameter p = appendColumn(t, name, LVCFMT_LEFT); - p->buttonModelColumn = buttonTextModelColumn; + p->buttonModelColumn = buttonModelColumn; p->buttonClickableModelColumn = buttonClickableModelColumn; } diff --git a/windows/table.hpp b/windows/table.hpp index 65a70fae..71946d62 100644 --- a/windows/table.hpp +++ b/windows/table.hpp @@ -10,13 +10,13 @@ struct uiTableModel { typedef struct uiprivTableColumnParams uiprivTableColumnParams; struct uiprivTableColumnParams { int textModelColumn; - int textEditableColumn; + int textEditableModelColumn; uiTableTextColumnOptionalParams textParams; int imageModelColumn; int checkboxModelColumn; - int checkboxEditableColumn; + int checkboxEditableModelColumn; int progressBarModelColumn; diff --git a/windows/tabledraw.cpp b/windows/tabledraw.cpp index ffa901fc..d8ab07e9 100644 --- a/windows/tabledraw.cpp +++ b/windows/tabledraw.cpp @@ -197,7 +197,7 @@ static HRESULT drawCheckboxPart(HRESULT hr, struct drawState *s) value = uiprivTableModelCellValue(s->model, s->iItem, s->p->checkboxModelColumn); checked = uiTableValueInt(value); uiFreeTableValue(value); - switch (s->p->checkboxEditableColumn) { + switch (s->p->checkboxEditableModelColumn) { case uiTableModelColumnNeverEditable: enabled = 0; break; @@ -205,7 +205,7 @@ static HRESULT drawCheckboxPart(HRESULT hr, struct drawState *s) enabled = 1; break; default: - value = uiprivTableModelCellValue(s->model, s->iItem, s->p->checkboxEditableColumn); + value = uiprivTableModelCellValue(s->model, s->iItem, s->p->checkboxEditableModelColumn); enabled = uiTableValueInt(value); uiFreeTableValue(value); } @@ -422,7 +422,7 @@ static HRESULT drawButtonPart(HRESULT hr, struct drawState *s) enabled = 1; break; default: - value = uiprivTableModelCellValue(s->model, s->iItem, s->p->checkboxEditableColumn); + value = uiprivTableModelCellValue(s->model, s->iItem, s->p->checkboxEditableModelColumn); enabled = uiTableValueInt(value); uiFreeTableValue(value); } diff --git a/windows/tableediting.cpp b/windows/tableediting.cpp index f96e70e6..00b07992 100644 --- a/windows/tableediting.cpp +++ b/windows/tableediting.cpp @@ -215,11 +215,11 @@ HRESULT uiprivTableHandleNM_CLICK(uiTable *t, NMITEMACTIVATE *nm, LRESULT *lResu p = (*(t->columns))[ht.iSubItem]; if (p->textModelColumn != -1) { modelColumn = p->textModelColumn; - editableColumn = p->textEditableColumn; + editableColumn = p->textEditableModelColumn; text = true; } else if (p->checkboxModelColumn != -1) { modelColumn = p->checkboxModelColumn; - editableColumn = p->checkboxEditableColumn; + editableColumn = p->checkboxEditableModelColumn; checkbox = true; } else if (p->buttonModelColumn != -1) { modelColumn = p->buttonModelColumn; From fb67c429d818af4f0e74d9b8d0a452112fcbc3e2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 24 Jun 2018 10:28:41 -0400 Subject: [PATCH 1242/1329] Made background color columns only settable at creation time; added a uiTableParams struct for the purpose. This should end edits to uitable.h for now (until we're ready to document it). Now to just clean up all the implementations. --- darwin/table.m | 37 +++++++++++++++---------------------- test/page16.c | 16 +++++++++------- uitable.h | 10 +++++++--- unix/table.c | 12 +++--------- windows/table.cpp | 19 ++++++------------- 5 files changed, 40 insertions(+), 54 deletions(-) diff --git a/darwin/table.m b/darwin/table.m index f148472d..41726b6b 100644 --- a/darwin/table.m +++ b/darwin/table.m @@ -171,26 +171,21 @@ static void uiTableDestroy(uiControl *c) uiFreeControl(uiControl(t)); } -void uiTableSetRowBackgroundColorModelColumn(uiTable *t, int modelColumn) -{ - t->backgroundColumn = modelColumn; - // TODO update all rows -} - -uiTable *uiNewTable(uiTableModel *model) +uiTable *uiNewTable(uiTableParams *p) { uiTable *t; - uiprivScrollViewCreateParams p; + uiprivScrollViewCreateParams sp; uiDarwinNewControl(uiTable, t); - t->m = model; + t->m = p->Model; + t->backgroundColumn = p->RowBackgroundColorModelColumn; t->tv = [[uiprivTableView alloc] initWithFrame:NSZeroRect uiprivT:t uiprivM:t->m]; - [t->tv setDataSource:model->m]; - [t->tv setDelegate:model->m]; + [t->tv setDataSource:t->m->m]; + [t->tv setDelegate:t->m->m]; [t->tv reloadData]; - [model->tables addObject:t->tv]; + [t->m->tables addObject:t->tv]; // TODO is this sufficient? [t->tv setAllowsColumnReordering:NO]; @@ -204,23 +199,21 @@ uiTable *uiNewTable(uiTableModel *model) [t->tv setAllowsTypeSelect:YES]; // TODO floatsGroupRows — do we even allow group rows? - memset(&p, 0, sizeof (uiprivScrollViewCreateParams)); - p.DocumentView = t->tv; + memset(&sp, 0, sizeof (uiprivScrollViewCreateParams)); + sp.DocumentView = t->tv; // this is what Interface Builder sets it to // TODO verify - p.BackgroundColor = [NSColor colorWithCalibratedWhite:1.0 alpha:1.0]; - p.DrawsBackground = YES; - p.Bordered = YES; - p.HScroll = YES; - p.VScroll = YES; - t->sv = uiprivMkScrollView(&p, &(t->d)); + sp.BackgroundColor = [NSColor colorWithCalibratedWhite:1.0 alpha:1.0]; + sp.DrawsBackground = YES; + sp.Bordered = YES; + sp.HScroll = YES; + sp.VScroll = YES; + t->sv = uiprivMkScrollView(&sp, &(t->d)); // TODO WHY DOES THIS REMOVE ALL GRAPHICAL GLITCHES? // I got the idea from http://jwilling.com/blog/optimized-nstableview-scrolling/ but that was on an unrelated problem I didn't seem to have (although I have small-ish tables to start with) // I don't get layer-backing... am I supposed to layer-back EVERYTHING manually? I need to check Interface Builder again... [t->sv setWantsLayer:YES]; - t->backgroundColumn = -1; - return t; } diff --git a/test/page16.c b/test/page16.c index 51aa3a59..c0f438f4 100644 --- a/test/page16.c +++ b/test/page16.c @@ -103,7 +103,8 @@ uiBox *makePage16(void) uiBox *page16; uiTableModel *m; uiTable *t; - uiTableTextColumnOptionalParams p; + uiTableParams p; + uiTableTextColumnOptionalParams tp; img[0] = uiNewImage(16, 16); appendImageNamed(img[0], "andlabs_16x16test_24june2016.png"); @@ -125,22 +126,23 @@ uiBox *makePage16(void) mh.SetCellValue = modelSetCellValue; m = uiNewTableModel(&mh); - t = uiNewTable(m); + memset(&p, 0, sizeof (uiTableParams)); + p.Model = m; + p.RowBackgroundColorModelColumn = 3; + t = uiNewTable(&p); uiBoxAppend(page16, uiControl(t), 1); uiTableAppendTextColumn(t, "Column 1", 0, uiTableModelColumnNeverEditable, NULL); - memset(&p, 0, sizeof (uiTableTextColumnOptionalParams)); - p.ColorModelColumn = 4; + memset(&tp, 0, sizeof (uiTableTextColumnOptionalParams)); + tp.ColorModelColumn = 4; uiTableAppendImageTextColumn(t, "Column 2", 5, - 1, uiTableModelColumnNeverEditable, &p); + 1, uiTableModelColumnNeverEditable, &tp); uiTableAppendTextColumn(t, "Editable", 2, uiTableModelColumnAlwaysEditable, NULL); - uiTableSetRowBackgroundColorModelColumn(t, 3); - uiTableAppendCheckboxColumn(t, "Checkboxes", 7, uiTableModelColumnAlwaysEditable); uiTableAppendButtonColumn(t, "Buttons", diff --git a/uitable.h b/uitable.h index 80ed1887..5302edfe 100644 --- a/uitable.h +++ b/uitable.h @@ -58,11 +58,17 @@ _UI_EXTERN void uiTableModelRowDeleted(uiTableModel *m, int oldIndex); #define uiTableModelColumnAlwaysEditable (-2) typedef struct uiTableTextColumnOptionalParams uiTableTextColumnOptionalParams; +typedef struct uiTableParams uiTableParams; struct uiTableTextColumnOptionalParams { int ColorModelColumn; }; +struct uiTableParams { + uiTableModel *Model; + int RowBackgroundColorModelColumn; +}; + typedef struct uiTable uiTable; #define uiTable(this) ((uiTable *) (this)) _UI_EXTERN void uiTableAppendTextColumn(uiTable *t, @@ -97,6 +103,4 @@ _UI_EXTERN void uiTableAppendButtonColumn(uiTable *t, const char *name, int buttonModelColumn, int buttonClickableModelColumn); -// TODO getter? -_UI_EXTERN void uiTableSetRowBackgroundColorModelColumn(uiTable *t, int modelColumn); -_UI_EXTERN uiTable *uiNewTable(uiTableModel *model); +_UI_EXTERN uiTable *uiNewTable(uiTableParams *params); diff --git a/unix/table.c b/unix/table.c index 97750cae..010b1b39 100644 --- a/unix/table.c +++ b/unix/table.c @@ -413,21 +413,15 @@ static void uiTableDestroy(uiControl *c) uiFreeControl(uiControl(t)); } -void uiTableSetRowBackgroundColorModelColumn(uiTable *t, int modelColumn) -{ - t->backgroundColumn = modelColumn; - // TODO refresh table -} - -uiTable *uiNewTable(uiTableModel *model) +uiTable *uiNewTable(uiTableParams *p) { uiTable *t; uiUnixNewControl(uiTable, t); - t->model = model; + t->model = p->Model; t->columnParams = g_ptr_array_new(); - t->backgroundColumn = -1; + t->backgroundColumn = p->RowBackgroundColorModelColumn; t->widget = gtk_scrolled_window_new(NULL, NULL); t->scontainer = GTK_CONTAINER(t->widget); diff --git a/windows/table.cpp b/windows/table.cpp index 2c821b6e..b8690a73 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -478,14 +478,7 @@ void uiTableAppendButtonColumn(uiTable *t, const char *name, int buttonModelColu p->buttonClickableModelColumn = buttonClickableModelColumn; } -void uiTableSetRowBackgroundColorModelColumn(uiTable *t, int modelColumn) -{ - // TODO make the names consistent - t->backgroundColumn = modelColumn; - // TODO redraw? -} - -uiTable *uiNewTable(uiTableModel *model) +uiTable *uiNewTable(uiTableParams *p) { uiTable *t; int n; @@ -494,14 +487,16 @@ uiTable *uiNewTable(uiTableModel *model) uiWindowsNewControl(uiTable, t); t->columns = new std::vector; - t->model = model; + t->model = p->Model; + t->backgroundColumn = p->RowBackgroundColorModelColumn; + // WS_CLIPCHILDREN is here to prevent drawing over the edit box used for editing text t->hwnd = uiWindowsEnsureCreateControlHWND(WS_EX_CLIENTEDGE, WC_LISTVIEW, L"", LVS_REPORT | LVS_OWNERDATA | LVS_SINGLESEL | WS_CLIPCHILDREN | WS_TABSTOP | WS_HSCROLL | WS_VSCROLL, hInstance, NULL, TRUE); - model->tables->push_back(t); + t->model->tables->push_back(t); uiWindowsRegisterWM_NOTIFYHandler(t->hwnd, onWM_NOTIFY, uiControl(t)); // TODO: try LVS_EX_AUTOSIZECOLUMNS @@ -509,12 +504,10 @@ uiTable *uiNewTable(uiTableModel *model) SendMessageW(t->hwnd, LVM_SETEXTENDEDLISTVIEWSTYLE, (WPARAM) (LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP | LVS_EX_SUBITEMIMAGES), (LPARAM) (LVS_EX_FULLROWSELECT | LVS_EX_LABELTIP | LVS_EX_SUBITEMIMAGES)); - n = uiprivTableModelNumRows(model); + n = uiprivTableModelNumRows(t->model); if (SendMessageW(t->hwnd, LVM_SETITEMCOUNT, (WPARAM) n, 0) == 0) logLastError(L"error calling LVM_SETITEMCOUNT in uiNewTable()"); - t->backgroundColumn = -1; - hr = uiprivUpdateImageListSize(t); if (hr != S_OK) { // TODO From acb40964f3bd0c7e6e5e015248ef40b47516873f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 24 Jun 2018 11:41:19 -0400 Subject: [PATCH 1243/1329] Deduplicated editable logic across platforms. --- common/table.h | 1 + common/tablemodel.c | 17 +++++++++++++++++ darwin/tablecolumn.m | 23 +++-------------------- unix/table.c | 19 ++++++------------- windows/tabledraw.cpp | 26 ++------------------------ windows/tableediting.cpp | 12 +----------- 6 files changed, 30 insertions(+), 68 deletions(-) diff --git a/common/table.h b/common/table.h index 21ce2d11..a7e2d8ff 100644 --- a/common/table.h +++ b/common/table.h @@ -12,6 +12,7 @@ extern int uiprivTableModelNumRows(uiTableModel *m); extern uiTableValue *uiprivTableModelCellValue(uiTableModel *m, int row, int column); extern void uiprivTableModelSetCellValue(uiTableModel *m, int row, int column, const uiTableValue *value); extern const uiTableTextColumnOptionalParams uiprivDefaultTextColumnOptionalParams; +extern int uiprivTableModelCellEditable(uiTableModel *m, int row, int column); #ifdef __cplusplus } diff --git a/common/tablemodel.c b/common/tablemodel.c index b1cd1e89..e1de4896 100644 --- a/common/tablemodel.c +++ b/common/tablemodel.c @@ -46,3 +46,20 @@ void uiprivTableModelSetCellValue(uiTableModel *m, int row, int column, const ui const uiTableTextColumnOptionalParams uiprivDefaultTextColumnOptionalParams = { .ColorModelColumn = -1, }; + +int uiprivTableModelCellEditable(uiTableModel *m, int row, int column) +{ + uiTableValue *value; + int editable; + + switch (column) { + case uiTableModelColumnNeverEditable: + return 0; + case uiTableModelColumnAlwaysEditable: + return 1; + } + value = uiprivTableModelCellValue(m, row, column); + editable = uiTableValueInt(value); + uiFreeTableValue(value); + return editable; +} diff --git a/darwin/tablecolumn.m b/darwin/tablecolumn.m index 7dac3304..4b3eb11d 100644 --- a/darwin/tablecolumn.m +++ b/darwin/tablecolumn.m @@ -34,23 +34,6 @@ @end -static BOOL isCellEditable(uiTableModel *m, NSInteger row, int modelColumn) -{ - uiTableValue *value; - int editable; - - switch (modelColumn) { - case uiTableModelColumnNeverEditable: - return NO; - case uiTableModelColumnAlwaysEditable: - return YES; - } - value = uiprivTableModelCellValue(m, row, modelColumn); - editable = uiTableValueInt(value); - uiFreeTableValue(value); - return editable != 0; -} - struct textColumnCreateParams { uiTable *t; uiTableModel *m; @@ -272,7 +255,7 @@ struct textColumnCreateParams { uiFreeTableValue(value); [self->tf setStringValue:str]; - [self->tf setEditable:isCellEditable(self->m, row, self->textEditableModelColumn)]; + [self->tf setEditable:uiprivTableModelCellEditable(self->m, row, self->textEditableModelColumn)]; color = nil; if (self->textParams.ColorModelColumn != -1) { @@ -307,7 +290,7 @@ struct textColumnCreateParams { [self->cb setState:NSOffState]; uiFreeTableValue(value); - [self->cb setEnabled:isCellEditable(self->m, row, self->checkboxEditableModelColumn)]; + [self->cb setEnabled:uiprivTableModelCellEditable(self->m, row, self->checkboxEditableModelColumn)]; } } @@ -558,7 +541,7 @@ struct textColumnCreateParams { uiFreeTableValue(value); [self->b setTitle:str]; - [self->b setEnabled:isCellEditable(self->m, row, self->editableColumn)]; + [self->b setEnabled:uiprivTableModelCellEditable(self->m, row, self->editableColumn)]; } - (IBAction)uiprivOnClicked:(id)sender diff --git a/unix/table.c b/unix/table.c index 010b1b39..9e659163 100644 --- a/unix/table.c +++ b/unix/table.c @@ -49,21 +49,14 @@ static void applyColor(GtkTreeModel *m, GtkTreeIter *iter, int modelColumn, GtkC static void setEditable(uiTableModel *m, GtkTreeIter *iter, int modelColumn, GtkCellRenderer *r, const char *prop) { - GValue value = G_VALUE_INIT; + GtkTreePath *path; + int row; gboolean editable; - switch (modelColumn) { - case uiTableModelColumnNeverEditable: - editable = FALSE; - break; - case uiTableModelColumnAlwaysEditable: - editable = TRUE; - break; - default: - gtk_tree_model_get_value(GTK_TREE_MODEL(m), iter, modelColumn, &value); - editable = g_value_get_int(&value) != 0; - g_value_unset(&value); - } + // TODO avoid the need for this + path = gtk_tree_model_get_path(GTK_TREE_MODEL(m), iter); + row = gtk_tree_path_get_indices(path)[0]; + editable = uiprivTableModelCellEditable(m, row, modelColumn) != 0; g_object_set(r, prop, editable, NULL); } diff --git a/windows/tabledraw.cpp b/windows/tabledraw.cpp index d8ab07e9..33eeb700 100644 --- a/windows/tabledraw.cpp +++ b/windows/tabledraw.cpp @@ -197,18 +197,7 @@ static HRESULT drawCheckboxPart(HRESULT hr, struct drawState *s) value = uiprivTableModelCellValue(s->model, s->iItem, s->p->checkboxModelColumn); checked = uiTableValueInt(value); uiFreeTableValue(value); - switch (s->p->checkboxEditableModelColumn) { - case uiTableModelColumnNeverEditable: - enabled = 0; - break; - case uiTableModelColumnAlwaysEditable: - enabled = 1; - break; - default: - value = uiprivTableModelCellValue(s->model, s->iItem, s->p->checkboxEditableModelColumn); - enabled = uiTableValueInt(value); - uiFreeTableValue(value); - } + enabled = uiprivTableModelCellEditable(s->model, s->iItem, s->p->checkboxEditableModelColumn); theme = OpenThemeData(s->t->hwnd, L"button"); if (theme != NULL) { @@ -414,18 +403,7 @@ static HRESULT drawButtonPart(HRESULT hr, struct drawState *s) value = uiprivTableModelCellValue(s->model, s->iItem, s->p->buttonModelColumn); wstr = toUTF16(uiTableValueString(value)); uiFreeTableValue(value); - switch (s->p->buttonClickableModelColumn) { - case uiTableModelColumnNeverEditable: - enabled = 0; - break; - case uiTableModelColumnAlwaysEditable: - enabled = 1; - break; - default: - value = uiprivTableModelCellValue(s->model, s->iItem, s->p->checkboxEditableModelColumn); - enabled = uiTableValueInt(value); - uiFreeTableValue(value); - } + enabled = uiprivTableModelCellEditable(s->model, s->iItem, s->p->buttonClickableModelColumn); theme = OpenThemeData(s->t->hwnd, L"button"); diff --git a/windows/tableediting.cpp b/windows/tableediting.cpp index 00b07992..7bbb450c 100644 --- a/windows/tableediting.cpp +++ b/windows/tableediting.cpp @@ -232,18 +232,8 @@ HRESULT uiprivTableHandleNM_CLICK(uiTable *t, NMITEMACTIVATE *nm, LRESULT *lResu // don't even ask for info if it's too soon to edit text goto done; - switch (editableColumn) { - case uiTableModelColumnNeverEditable: + if (!uiprivTableModelCellEditable(t->model, ht.iItem, editableColumn)) goto done; - case uiTableModelColumnAlwaysEditable: - break; - default: - value = uiprivTableModelCellValue(t->model, ht.iItem, editableColumn); - editable = uiTableValueInt(value); - uiFreeTableValue(value); - if (!editable) - goto done; - } if (text) { hr = openEditControl(t, ht.iItem, ht.iSubItem, p); From f3882d6124adada301f29733cb5bc20a5c8ccc27 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 24 Jun 2018 14:22:05 -0400 Subject: [PATCH 1244/1329] Cleaned up color access across platforms. --- common/table.h | 1 + common/tablemodel.c | 14 ++++++++++++++ darwin/table.m | 10 +++------- darwin/tablecolumn.m | 18 ++++-------------- windows/tabledraw.cpp | 43 ++++++++++++++++--------------------------- 5 files changed, 38 insertions(+), 48 deletions(-) diff --git a/common/table.h b/common/table.h index a7e2d8ff..f83205df 100644 --- a/common/table.h +++ b/common/table.h @@ -13,6 +13,7 @@ extern uiTableValue *uiprivTableModelCellValue(uiTableModel *m, int row, int col extern void uiprivTableModelSetCellValue(uiTableModel *m, int row, int column, const uiTableValue *value); extern const uiTableTextColumnOptionalParams uiprivDefaultTextColumnOptionalParams; extern int uiprivTableModelCellEditable(uiTableModel *m, int row, int column); +extern int uiprivTableModelColorIfProvided(uiTableModel *m, int row, int column, double *r, double *g, double *b, double *a); #ifdef __cplusplus } diff --git a/common/tablemodel.c b/common/tablemodel.c index e1de4896..dbda4a81 100644 --- a/common/tablemodel.c +++ b/common/tablemodel.c @@ -63,3 +63,17 @@ int uiprivTableModelCellEditable(uiTableModel *m, int row, int column) uiFreeTableValue(value); return editable; } + +int uiprivTableModelColorIfProvided(uiTableModel *m, int row, int column, double *r, double *g, double *b, double *a) +{ + uiTableValue *value; + + if (column == -1) + return 0; + value = uiprivTableModelCellValue(m, row, column); + if (value == NULL) + return 0; + uiTableValueColor(value, r, g, b, a); + uiFreeTableValue(value); + return 1; +} diff --git a/darwin/table.m b/darwin/table.m index 41726b6b..b23ed47d 100644 --- a/darwin/table.m +++ b/darwin/table.m @@ -31,18 +31,14 @@ // TODO is this correct for overflow scrolling? static void setBackgroundColor(uiprivTableView *t, NSTableRowView *rv, NSInteger row) { - uiTableValue *value; NSColor *color; double r, g, b, a; if (t->uiprivT->backgroundColumn == -1) - return; - value = uiprivTableModelCellValue(t->uiprivM, row, t->uiprivT->backgroundColumn); - if (value != NULL) { - uiTableValueColor(value, &r, &g, &b, &a); - uiFreeTableValue(value); + return; // let Cocoa do its default thing + if (uiprivTableModelColorIfProvided(t->uiprivM, row, t->uiprivT->backgroundColumn, &r, &g, &b, &a)) color = [NSColor colorWithSRGBRed:r green:g blue:b alpha:a]; - } else { + else { NSArray *colors; NSInteger index; diff --git a/darwin/tablecolumn.m b/darwin/tablecolumn.m index 4b3eb11d..b918c50c 100644 --- a/darwin/tablecolumn.m +++ b/darwin/tablecolumn.m @@ -249,6 +249,7 @@ struct textColumnCreateParams { if (self->tf != nil) { NSString *str; NSColor *color; + double r, g, b, a; value = uiprivTableModelCellValue(self->m, row, self->textModelColumn); str = uiprivToNSString(uiTableValueString(value)); @@ -257,20 +258,9 @@ struct textColumnCreateParams { [self->tf setEditable:uiprivTableModelCellEditable(self->m, row, self->textEditableModelColumn)]; - color = nil; - if (self->textParams.ColorModelColumn != -1) { - double r, g, b, a; - - value = uiprivTableModelCellValue(self->m, row, self->textParams.ColorModelColumn); - // TODO document this is allowed - if (value != NULL) { - uiTableValueColor(value, &r, &g, &b, &a); - uiFreeTableValue(value); - color = [NSColor colorWithSRGBRed:r green:g blue:b alpha:a]; - } - } - if (color == nil) - color = [NSColor controlTextColor]; + color = [NSColor controlTextColor]; + if (uiprivTableModelColorIfProvided(self->m, row, self->textParams.ColorModelColumn, &r, &g, &b, &a)) + color = [NSColor colorWithSRGBRed:r green:g blue:b alpha:a]; [self->tf setTextColor:color]; // we don't own color in ether case; don't release } diff --git a/windows/tabledraw.cpp b/windows/tabledraw.cpp index 33eeb700..eb50f28a 100644 --- a/windows/tabledraw.cpp +++ b/windows/tabledraw.cpp @@ -549,42 +549,31 @@ static HRESULT fillDrawState(struct drawState *s, uiTable *t, NMLVCUSTOMDRAW *nm s->textColor = GetSysColor(COLOR_HIGHLIGHTTEXT); s->textBrush = GetSysColorBrush(COLOR_HIGHLIGHTTEXT); } else { - uiTableValue *value; double r, g, b, a; s->bgColor = GetSysColor(COLOR_WINDOW); s->bgBrush = GetSysColorBrush(COLOR_WINDOW); - if (t->backgroundColumn != -1) { - value = uiprivTableModelCellValue(s->model, s->iItem, t->backgroundColumn); - if (value != NULL) { - uiTableValueColor(value, &r, &g, &b, &a); - uiFreeTableValue(value); - s->bgColor = blend(s->bgColor, r, g, b, a); - s->bgBrush = CreateSolidBrush(s->bgColor); - if (s->bgBrush == NULL) { - logLastError(L"CreateSolidBrush()"); - hr = E_FAIL; - goto fail; - } - s->freeBgBrush = TRUE; + if (uiprivTableModelColorIfProvided(s->model, s->iItem, t->backgroundColumn, &r, &g, &b, &a)) { + s->bgColor = blend(s->bgColor, r, g, b, a); + s->bgBrush = CreateSolidBrush(s->bgColor); + if (s->bgBrush == NULL) { + logLastError(L"CreateSolidBrush()"); + hr = E_FAIL; + goto fail; } + s->freeBgBrush = TRUE; } s->textColor = GetSysColor(COLOR_WINDOWTEXT); s->textBrush = GetSysColorBrush(COLOR_WINDOWTEXT); - if (p->textParams.ColorModelColumn != -1) { - value = uiprivTableModelCellValue(s->model, s->iItem, p->textParams.ColorModelColumn); - if (value != NULL) { - uiTableValueColor(value, &r, &g, &b, &a); - uiFreeTableValue(value); - s->textColor = blend(s->bgColor, r, g, b, a); - s->textBrush = CreateSolidBrush(s->textColor); - if (s->textBrush == NULL) { - logLastError(L"CreateSolidBrush()"); - hr = E_FAIL; - goto fail; - } - s->freeTextBrush = TRUE; + if (uiprivTableModelColorIfProvided(s->model, s->iItem, p->textParams.ColorModelColumn, &r, &g, &b, &a)) { + s->textColor = blend(s->bgColor, r, g, b, a); + s->textBrush = CreateSolidBrush(s->textColor); + if (s->textBrush == NULL) { + logLastError(L"CreateSolidBrush()"); + hr = E_FAIL; + goto fail; } + s->freeTextBrush = TRUE; } } From 4ed6e3ec8a37dd4a41c6b066449e3fd969963b1e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 24 Jun 2018 14:46:24 -0400 Subject: [PATCH 1245/1329] Minor TODO resolution. --- darwin/tablecolumn.m | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/darwin/tablecolumn.m b/darwin/tablecolumn.m index b918c50c..5038cc6b 100644 --- a/darwin/tablecolumn.m +++ b/darwin/tablecolumn.m @@ -43,7 +43,7 @@ struct textColumnCreateParams { int textEditableModelColumn; uiTableTextColumnOptionalParams textParams; - BOOL makeImage; + BOOL makeImageView; int imageModelColumn; BOOL makeCheckbox; @@ -121,8 +121,7 @@ struct textColumnCreateParams { } self->iv = nil; - // TODO rename to makeImageView - if (p->makeImage) { + if (p->makeImageView) { self->imageModelColumn = p->imageModelColumn; self->iv = [[NSImageView alloc] initWithFrame:NSZeroRect]; @@ -541,6 +540,7 @@ struct textColumnCreateParams { row = [self->t->tv rowForView:self->b]; uiprivTableModelSetCellValue(self->m, row, self->modelColumn, NULL); // TODO document we DON'T update the cell after doing this + // TODO or decide what to do instead } @end @@ -613,7 +613,7 @@ void uiTableAppendImageColumn(uiTable *t, const char *name, int imageModelColumn p.t = t; p.m = t->m; - p.makeImage = YES; + p.makeImageView = YES; p.imageModelColumn = imageModelColumn; str = [NSString stringWithUTF8String:name]; @@ -640,7 +640,7 @@ void uiTableAppendImageTextColumn(uiTable *t, const char *name, int imageModelCo else p.textParams = uiprivDefaultTextColumnOptionalParams; - p.makeImage = YES; + p.makeImageView = YES; p.imageModelColumn = imageModelColumn; str = [NSString stringWithUTF8String:name]; From bd685f24f9524fb43e52008db38f31eb8ca1023c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 24 Jun 2018 14:48:09 -0400 Subject: [PATCH 1246/1329] Removed a stale TODO; added more TODOs. --- darwin/table.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/darwin/table.m b/darwin/table.m index b23ed47d..07df7c0f 100644 --- a/darwin/table.m +++ b/darwin/table.m @@ -2,6 +2,8 @@ #import "uipriv_darwin.h" #import "table.h" +// TODO the initial scroll position is still wrong + @interface uiprivTableModel : NSObject { uiTableModel *m; } @@ -72,7 +74,6 @@ static void setBackgroundColor(uiprivTableView *t, NSTableRowView *rv, NSInteger - (NSView *)tableView:(NSTableView *)tv viewForTableColumn:(NSTableColumn *)cc row:(NSInteger)row { uiprivTableColumn *c = (uiprivTableColumn *) cc; - // TODO consider renaming this type to uiprivTableCellView uiprivTableCellView *cv; cv = (uiprivTableCellView *) [tv makeViewWithIdentifier:[c identifier] owner:self]; From 5d9928028f246813edf6a7cb72de0622bc146b8f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 24 Jun 2018 18:23:25 -0400 Subject: [PATCH 1247/1329] Wrote the initial version of the indeterminate progressbar in tables code on GTK+. VirtualBox is giving me issues when any indeterminate progressbar (real or table-based) is up; I wonder what's going on. --- unix/table.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 83 insertions(+), 2 deletions(-) diff --git a/unix/table.c b/unix/table.c index 9e659163..844f5925 100644 --- a/unix/table.c +++ b/unix/table.c @@ -12,6 +12,10 @@ struct uiTable { uiTableModel *model; GPtrArray *columnParams; int backgroundColumn; + // keys are struct rowcol, values are gint + // TODO document this properly + GHashTable *indeterminatePositions; + guint indeterminateTimer; }; // use the same size as GtkFileChooserWidget's treeview @@ -194,21 +198,95 @@ struct progressBarColumnParams { int modelColumn; }; +struct rowcol { + int row; + int col; +}; + +static guint rowcolHash(gconstpointer key) +{ + const struct rowcol *rc = (const struct rowcol *) key; + guint row, col; + + row = (guint) (rc->row); + col = (guint) (rc->col); + return row ^ col; +} + +static gboolean rowcolEqual(gconstpointer a, gconstpointer b) +{ + const struct rowcol *ra = (const struct rowcol *) a; + const struct rowcol *rb = (const struct rowcol *) b; + + return (ra->row == rb->row) && (ra->col == rb->col); +} + +static void pulseOne(gpointer key, gpointer value, gpointer data) +{ + uiTable *t = uiTable(data); + struct rowcol *rc = (struct rowcol *) key; + + // TODO this is bad: it produces changed handlers for every table because that's how GtkTreeModel works, yet this is per-table because that's how it works + // however, a proper fix would require decoupling progress from normal integers, which we could do... + uiTableModelRowChanged(t->model, rc->row); +} + +static gboolean indeterminatePulse(gpointer data) +{ + uiTable *t = uiTable(data); + + g_hash_table_foreach(t->indeterminatePositions, pulseOne, t); + return TRUE; +} + static void progressBarColumnDataFunc(GtkTreeViewColumn *c, GtkCellRenderer *r, GtkTreeModel *m, GtkTreeIter *iter, gpointer data) { struct progressBarColumnParams *p = (struct progressBarColumnParams *) data; GValue value = G_VALUE_INIT; int pval; + struct rowcol *rc; + gint *val; + GtkTreePath *path; gtk_tree_model_get_value(m, iter, p->modelColumn, &value); pval = g_value_get_int(&value); + rc = uiprivNew(struct rowcol); + // TODO avoid the need for this + path = gtk_tree_model_get_path(GTK_TREE_MODEL(m), iter); + rc->row = gtk_tree_path_get_indices(path)[0]; + rc->col = p->modelColumn; + val = (gint *) g_hash_table_lookup(p->t->indeterminatePositions, rc); if (pval == -1) { - // TODO - } else + if (val == NULL) { + val = uiprivNew(gint); + *val = 1; + g_hash_table_insert(p->t->indeterminatePositions, rc, val); + } else { + uiprivFree(rc); + (*val)++; + if (*val == G_MAXINT) + *val = 1; + } + g_object_set(r, + "pulse", *val, + NULL); + if (p->t->indeterminateTimer == 0) + // TODO verify the timeout + p->t->indeterminateTimer = g_timeout_add(100, indeterminatePulse, p->t); + } else { + if (val != NULL) { + g_hash_table_remove(p->t->indeterminatePositions, rc); + uiprivFree(rc); + if (g_hash_table_size(p->t->indeterminatePositions) == 0) { + g_source_remove(p->t->indeterminateTimer); + p->t->indeterminateTimer = 0; + } + } g_object_set(r, "pulse", -1, "value", pval, NULL); + } g_value_unset(&value); applyBackgroundColor(p->t, m, iter, r); @@ -429,5 +507,8 @@ uiTable *uiNewTable(uiTableParams *p) // and make the tree view visible; only the scrolled window's visibility is controlled by libui gtk_widget_show(t->treeWidget); + t->indeterminatePositions = g_hash_table_new_full(rowcolHash, rowcolEqual, + uiprivFree, uiprivFree); + return t; } From bd197684aa5c7905664ae9e7851c9f9e2a68d75f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 25 Jun 2018 09:11:07 -0400 Subject: [PATCH 1248/1329] Added internationalization notes in the meantime. --- _notes/i18n | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 _notes/i18n diff --git a/_notes/i18n b/_notes/i18n new file mode 100644 index 00000000..79fdfe9f --- /dev/null +++ b/_notes/i18n @@ -0,0 +1,15 @@ +https://msdn.microsoft.com/en-us/library/windows/desktop/dd319079(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/windows/desktop/dd318103(v=vs.85).aspx +https://stackoverflow.com/questions/4663855/is-there-a-repository-for-localized-common-text-in-winforms +https://stackoverflow.com/questions/2502375/find-localized-windows-strings +https://docs.microsoft.com/en-us/windows-hardware/customize/mobile/mcsf/create-a-resource-only-dll-for-localized-strings +https://msdn.microsoft.com/en-us/library/windows/desktop/ee845043(v=vs.85).aspx +https://msdn.microsoft.com/en-us/library/cc194807.aspx +https://www.codeproject.com/Articles/10542/Easily-Load-and-Format-Strings-from-the-String-Tab +https://www.codeproject.com/Tips/431045/The-inner-working-of-FindResource-and-LoadString-W +https://mihai-nita.net/2007/05/03/how-to-localize-an-rc-file/ +https://www.microsoft.com/en-us/language +https://www.microsoft.com/en-us/language/Terminology +https://www.microsoft.com/en-us/language/LicenseAgreement +https://www.microsoft.com/en-us/language/Translations +http://www.ttt.org/oscarstandards/tbx/ From 37bd4fc5a180f15c1d29bbb5ec8403b33626f451 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 25 Jun 2018 21:25:23 -0400 Subject: [PATCH 1249/1329] Added a test program to demonstrate the GTK+ progress bar cell renderer timer issues. --- doc/misctests/gtkprogresstable.c | 136 +++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 doc/misctests/gtkprogresstable.c diff --git a/doc/misctests/gtkprogresstable.c b/doc/misctests/gtkprogresstable.c new file mode 100644 index 00000000..220cb370 --- /dev/null +++ b/doc/misctests/gtkprogresstable.c @@ -0,0 +1,136 @@ +// 25 june 2018 +#include + +GtkWidget *mainwin; +GtkWidget *vbox; +GtkWidget *hbox; +GtkWidget *startProgress; +GtkWidget *startTable; +GtkWidget *progressbar; +GtkWidget *scrolledWindow; +GtkListStore *model; +GtkWidget *treeview; +GtkWidget *hbox2; + +static gboolean pulseProgress(gpointer data) +{ + gtk_progress_bar_pulse(GTK_PROGRESS_BAR(progressbar)); + return TRUE; +} + +static void onStartProgressClicked(GtkButton *button, gpointer data) +{ + gtk_widget_set_sensitive(startProgress, FALSE); + g_timeout_add(100, pulseProgress, NULL); +} + +gboolean pbarStarted = FALSE; +gint pbarValue; + +static void pbarDataFunc(GtkTreeViewColumn *col, GtkCellRenderer *r, GtkTreeModel *m, GtkTreeIter *iter, gpointer data) +{ + if (!pbarStarted) { + g_object_set(r, + "pulse", -1, + "value", 0, + NULL); + return; + } + pbarValue++; + if (pbarValue == G_MAXINT) + pbarValue = 1; + g_object_set(r, "pulse", pbarValue, NULL); +} + +static gboolean pulseTable(gpointer data) +{ + GtkTreePath *path; + GtkTreeIter iter; + + path = gtk_tree_path_new_from_indices(0, -1); + gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path); + gtk_tree_model_row_changed(GTK_TREE_MODEL(model), path, &iter); + gtk_tree_path_free(path); + return TRUE; +} + +static void onStartTableClicked(GtkButton *button, gpointer data) +{ + pbarStarted = TRUE; + pbarValue = 0; + + gtk_widget_set_sensitive(startTable, FALSE); + g_timeout_add(100, pulseTable, NULL); +} + +static gboolean onClosing(GtkWidget *win, GdkEvent *e, gpointer data) +{ + gtk_main_quit(); + return FALSE; +} + +int main(void) +{ + GtkTreeIter iter; + GtkTreeViewColumn *col; + GtkCellRenderer *r; + + gtk_init(NULL, NULL); + + mainwin = gtk_window_new(GTK_WINDOW_TOPLEVEL); + g_signal_connect(mainwin, "delete-event", G_CALLBACK(onClosing), NULL); + + vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 12); + gtk_container_add(GTK_CONTAINER(mainwin), vbox); + + hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); + gtk_widget_set_halign(hbox, GTK_ALIGN_CENTER); + gtk_container_add(GTK_CONTAINER(vbox), hbox); + + startProgress = gtk_button_new_with_label("Start Progress Bar"); + g_signal_connect(startProgress, "clicked", G_CALLBACK(onStartProgressClicked), NULL); + gtk_container_add(GTK_CONTAINER(hbox), startProgress); + + startTable = gtk_button_new_with_label("Start Table Cell Renderer"); + g_signal_connect(startTable, "clicked", G_CALLBACK(onStartTableClicked), NULL); + gtk_container_add(GTK_CONTAINER(hbox), startTable); + + progressbar = gtk_progress_bar_new(); + gtk_container_add(GTK_CONTAINER(vbox), progressbar); + + scrolledWindow = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledWindow), GTK_SHADOW_IN); + gtk_widget_set_vexpand(scrolledWindow, TRUE); + gtk_container_add(GTK_CONTAINER(vbox), scrolledWindow); + + model = gtk_list_store_new(1, G_TYPE_INT); + gtk_list_store_append(model, &iter); + gtk_list_store_set(model, &iter, + 0, 0, + -1); + + treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); + gtk_container_add(GTK_CONTAINER(scrolledWindow), treeview); + + col = gtk_tree_view_column_new(); + gtk_tree_view_column_set_resizable(col, TRUE); + gtk_tree_view_column_set_title(col, "Column"); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), col); + + r = gtk_cell_renderer_progress_new(); + gtk_tree_view_column_pack_start(col, r, TRUE); + gtk_tree_view_column_set_cell_data_func(col, r, pbarDataFunc, NULL, NULL); + + hbox2 = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); + gtk_widget_set_halign(hbox2, GTK_ALIGN_CENTER); + gtk_container_add(GTK_CONTAINER(vbox), hbox2); + + gtk_container_add(GTK_CONTAINER(hbox2), gtk_button_new_with_label("These buttons")); + gtk_container_add(GTK_CONTAINER(hbox2), gtk_button_new_with_label("do nothing")); + gtk_container_add(GTK_CONTAINER(hbox2), gtk_button_new_with_label("when clicked")); + + gtk_widget_show_all(mainwin); + gtk_main(); + return 0; +} From ce0168e1a5faa537425a219c9cbe345a0b4d94cc Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 1 Jul 2018 17:35:34 -0400 Subject: [PATCH 1250/1329] I give up --- unix/progressbar.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/unix/progressbar.c b/unix/progressbar.c index b3681a6f..46f0e103 100644 --- a/unix/progressbar.c +++ b/unix/progressbar.c @@ -1,6 +1,9 @@ // 11 june 2015 #include "uipriv_unix.h" +// LONGTERM: +// - in GTK+ 3.22 at least, running both a GtkProgressBar and a GtkCellRendererProgress in pulse mode with our code will cause the former to slow down and eventually stop, and I can't tell why at all + struct uiProgressBar { uiUnixControl c; GtkWidget *widget; From 5e2b8cb6ae84716e4a61eef32ba0c07d29ae870c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 8 Jul 2018 21:04:23 -0400 Subject: [PATCH 1251/1329] Removed nowintable.diff; we don't need it anymore. --- nowintable.diff | 50 ------------------------------------------------- 1 file changed, 50 deletions(-) delete mode 100644 nowintable.diff diff --git a/nowintable.diff b/nowintable.diff deleted file mode 100644 index cfbab07d..00000000 --- a/nowintable.diff +++ /dev/null @@ -1,50 +0,0 @@ -diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt -index 7f70403..e909569 100644 ---- a/common/CMakeLists.txt -+++ b/common/CMakeLists.txt -@@ -6,7 +6,7 @@ list(APPEND _LIBUI_SOURCES - common/debug.c - common/matrix.c - common/shouldquit.c -- common/table.c -+# common/table.c - common/userbugs.c - ) - set(_LIBUI_SOURCES ${_LIBUI_SOURCES} PARENT_SCOPE) -diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt -index b753a7d..a648c64 100644 ---- a/test/CMakeLists.txt -+++ b/test/CMakeLists.txt -@@ -6,7 +6,7 @@ endif() - - _add_exec(tester - drawtests.c -- images.c -+# images.c - main.c - menus.c - page1.c -@@ -27,7 +27,7 @@ _add_exec(tester - page13.c - page14.c - page15.c -- page16.c -+# page16.c - spaced.c - ${_TEST_RESOURCES_RC} - ) -diff --git a/test/main.c b/test/main.c -index f33f30a..18774dc 100644 ---- a/test/main.c -+++ b/test/main.c -@@ -159,8 +159,8 @@ int main(int argc, char *argv[]) - innerTab = newTab(); - uiTabAppend(outerTab, "Pages 16-?", uiControl(innerTab)); - -- page16 = makePage16(); -- uiTabAppend(innerTab, "Page 16", uiControl(page16)); -+// page16 = makePage16(); -+// uiTabAppend(innerTab, "Page 16", uiControl(page16)); - - if (startspaced) - setSpaced(1); From 1700c0cceabf67dd1b562c4c8652efdb77e09c3c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 8 Jul 2018 21:13:19 -0400 Subject: [PATCH 1252/1329] Reworded a TODO. --- darwin/table.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/darwin/table.m b/darwin/table.m index 07df7c0f..fc203514 100644 --- a/darwin/table.m +++ b/darwin/table.m @@ -2,7 +2,7 @@ #import "uipriv_darwin.h" #import "table.h" -// TODO the initial scroll position is still wrong +// TODO is the initial scroll position still wrong? @interface uiprivTableModel : NSObject { uiTableModel *m; From 095e63d522c5f83953a7420aea4e78b6c88dd268 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 8 Jul 2018 21:20:42 -0400 Subject: [PATCH 1253/1329] Fixed memory leaks in the tester and a symbol name flub in OS X's image.m. --- darwin/image.m | 2 +- test/main.c | 1 + test/page16.c | 10 +++++++++- test/test.h | 1 + 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/darwin/image.m b/darwin/image.m index ae5be6d9..8824f3c6 100644 --- a/darwin/image.m +++ b/darwin/image.m @@ -18,7 +18,7 @@ uiImage *uiNewImage(double width, double height) return i; } -void Image(uiImage *i) +void uiFreeImage(uiImage *i) { NSValue *v; diff --git a/test/main.c b/test/main.c index c760f493..2f66826f 100644 --- a/test/main.c +++ b/test/main.c @@ -174,6 +174,7 @@ int main(int argc, char *argv[]) ; } printf("after uiMain()\n"); + freePage16(); uiUninit(); printf("after uiUninit()\n"); return 0; diff --git a/test/page16.c b/test/page16.c index c0f438f4..f28ba3c7 100644 --- a/test/page16.c +++ b/test/page16.c @@ -98,10 +98,11 @@ static void modelSetCellValue(uiTableModelHandler *mh, uiTableModel *m, int row, checkStates[row] = uiTableValueInt(val); } +static uiTableModel *m; + uiBox *makePage16(void) { uiBox *page16; - uiTableModel *m; uiTable *t; uiTableParams p; uiTableTextColumnOptionalParams tp; @@ -153,3 +154,10 @@ uiBox *makePage16(void) return page16; } + +void freePage16(void) +{ + uiFreeTableModel(m); + uiFreeImage(img[1]); + uiFreeImage(img[0]); +} diff --git a/test/test.h b/test/test.h index 224ef667..42ec9314 100644 --- a/test/test.h +++ b/test/test.h @@ -92,6 +92,7 @@ extern uiBox *makePage15(uiWindow *); // page16.c extern uiBox *makePage16(void); +extern void freePage16(void); // images.c extern void appendImageNamed(uiImage *img, const char *name); From a37fdadbd76dbd90ce86598dad820d5364a0d8bb Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 8 Jul 2018 21:22:55 -0400 Subject: [PATCH 1254/1329] Implemented uiControlDestroy() for uiTable on OS X. --- darwin/table.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/darwin/table.m b/darwin/table.m index fc203514..1bdc7f8d 100644 --- a/darwin/table.m +++ b/darwin/table.m @@ -163,7 +163,9 @@ static void uiTableDestroy(uiControl *c) { uiTable *t = uiTable(c); - // TODO + [t->m->tables removeObject:t->tv]; + uiprivScrollViewFreeData(t->sv, t->d); + [t->tv release]; [t->sv release]; uiFreeControl(uiControl(t)); } From 6812cab625cf939634bb46d117a34f5ead654a1f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 8 Jul 2018 21:50:16 -0400 Subject: [PATCH 1255/1329] Implemented uiControlDestroy() for uiTable on GTK+. --- unix/table.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/unix/table.c b/unix/table.c index 844f5925..9e6fe01e 100644 --- a/unix/table.c +++ b/unix/table.c @@ -276,12 +276,12 @@ static void progressBarColumnDataFunc(GtkTreeViewColumn *c, GtkCellRenderer *r, } else { if (val != NULL) { g_hash_table_remove(p->t->indeterminatePositions, rc); - uiprivFree(rc); if (g_hash_table_size(p->t->indeterminatePositions) == 0) { g_source_remove(p->t->indeterminateTimer); p->t->indeterminateTimer = 0; } } + uiprivFree(rc); g_object_set(r, "pulse", -1, "value", pval, @@ -478,8 +478,14 @@ uiUnixControlAllDefaultsExceptDestroy(uiTable) static void uiTableDestroy(uiControl *c) { uiTable *t = uiTable(c); + guint i; - // TODO + for (i = 0; i < t->columnParams->len; i++) + uiprivFree(g_ptr_array_index(t->columnParams, i)); + g_ptr_array_free(t->columnParams, TRUE); + if (g_hash_table_size(t->indeterminatePositions) != 0) + g_source_remove(t->indeterminateTimer); + g_hash_table_destroy(t->indeterminatePositions); g_object_unref(t->widget); uiFreeControl(uiControl(t)); } From 3c44d332da2322acfd13c1fb17b5590a073c1669 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 22 Jul 2018 21:41:03 -0400 Subject: [PATCH 1256/1329] More notes. --- _notes/highDPI | 1 + 1 file changed, 1 insertion(+) create mode 100644 _notes/highDPI diff --git a/_notes/highDPI b/_notes/highDPI new file mode 100644 index 00000000..99e9af82 --- /dev/null +++ b/_notes/highDPI @@ -0,0 +1 @@ +High DPI Displays | Qt 5.5 http://doc.qt.io/qt-5/highdpi.html bottom of page(?) From b60953ed1c5725b23f255d17899765c84ea0c607 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 28 Jul 2018 13:57:20 -0400 Subject: [PATCH 1257/1329] More TODOs. --- windows/table.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/windows/table.cpp b/windows/table.cpp index b8690a73..85a51c1a 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -6,6 +6,7 @@ // - should clicking on some other column of the same row, even one that doesn't edit, cancel editing? // - implement keyboard accessibility // - implement accessibility in general (Dynamic Annotations maybe?) +// - if I didn't handle these already: "drawing focus rects here, subitem navigation and activation with the keyboard" uiTableModel *uiNewTableModel(uiTableModelHandler *mh) { From 4eaf01f8401ff76d84184becb32edc971d751446 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 29 Jul 2018 13:25:53 -0400 Subject: [PATCH 1258/1329] Fix double-free spotted by @mischnic in #402. --- darwin/image.m | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/darwin/image.m b/darwin/image.m index 8824f3c6..28881ca3 100644 --- a/darwin/image.m +++ b/darwin/image.m @@ -66,11 +66,12 @@ void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, in bytesPerRow:pixelStride bitsPerPixel:32]; repsRGB = [repCalibrated bitmapImageRepByRetaggingWithColorSpace:[NSColorSpace sRGBColorSpace]]; - [repCalibrated release]; [i->i addRepresentation:repsRGB]; [repsRGB setSize:i->size]; - [repsRGB release]; + // don't release repsRGB; it may be equivalent to repCalibrated + // do release repCalibrated though; NSImage has a ref to either it or to repsRGB + [repCalibrated release]; // we need to keep swizzled alive for NSBitmapImageRep [i->swizzled addObject:[NSValue valueWithPointer:swizzled]]; From e7305349f3aa41bcf5301788a8189a4de95dbbf1 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 5 Aug 2018 17:52:15 -0400 Subject: [PATCH 1259/1329] Documented uiImage functions. --- uitable.h | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/uitable.h b/uitable.h index 5302edfe..8d069eb5 100644 --- a/uitable.h +++ b/uitable.h @@ -1,11 +1,47 @@ // 20 june 2016 // kept in a separate file for now +// uiImage stores an image for display on screen. +// +// Images are built from one or more representations, each with the +// same aspect ratio but a different pixel size. libui automatically +// selects the most appropriate representation for drawing the image +// when it comes time to draw the image; what this means depends +// on the pixel density of the target context. Therefore, one can use +// uiImage to draw higher-detailed images on higher-density +// displays. The typical use cases are either: +// +// - have just a single representation, at which point all screens +// use the same image, and thus uiImage acts like a simple +// bitmap image, or +// - have two images, one at normal resolution and one at 2x +// resolution; this matches the current expectations of some +// desktop systems at the time of writing (mid-2018) +// +// uiImage is very simple: it only supports non-premultiplied 32-bit +// ARGB images, and libui does not provide any image file loading +// or image format conversion utilities on top of that. typedef struct uiImage uiImage; -// TODO use const void * for const correctness +// @role uiImage constructor +// uiNewImage creates a new uiImage with the given width and +// height. This width and height should be the size in points of the +// image in the device-independent case; typically this is the 1x size. +// TODO for all uiImage functions: use const void * for const correctness _UI_EXTERN uiImage *uiNewImage(double width, double height); + +// @role uiImage destructor +// uiFreeImage frees the given image and all associated resources. _UI_EXTERN void uiFreeImage(uiImage *i); + +// uiImageAppend adds a representation to the uiImage. +// pixels should point to a byte array of non-premultiplied pixels +// stored in [A R G B] order (so ((uint8_t *) pixels)[0] is the A of the +// first pixel and [3] is the B of the first pixel). pixelWidth and +// pixelHeight is the size *in pixels* of the image, and pixelStride is +// the number *of pixels* per row of the pixels array. Therefore, +// pixels itself must be at least 4 * pixelStride * pixelHeight bytes +// long. _UI_EXTERN void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int pixelStride); typedef struct uiTableValue uiTableValue; From e0ca00e55be81801827a5f3a8aef80c48a23202d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 5 Aug 2018 18:39:29 -0400 Subject: [PATCH 1260/1329] Resolved confusion about the terminology of strides in uiImageAppend(). Also prevents overallocation on some platforms. Thanks to @mischnic and @msink for spotting this. Update #402. --- darwin/image.m | 25 ++++++++++++------------- uitable.h | 9 ++++----- unix/image.c | 20 +++++++++++--------- unix/table.c | 2 ++ windows/image.cpp | 6 +++--- 5 files changed, 32 insertions(+), 30 deletions(-) diff --git a/darwin/image.m b/darwin/image.m index 28881ca3..9492cd07 100644 --- a/darwin/image.m +++ b/darwin/image.m @@ -30,28 +30,27 @@ void uiFreeImage(uiImage *i) uiprivFree(i); } -void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int pixelStride) +void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int byteStride) { NSBitmapImageRep *repCalibrated, *repsRGB; uint8_t *swizzled, *bp, *sp; - int x, y; + int n; unsigned char *pix[1]; // OS X demands that R and B are in the opposite order from what we expect // we must swizzle :( // LONGTERM test on a big-endian system - swizzled = (uint8_t *) uiprivAlloc((pixelStride * pixelHeight * 4) * sizeof (uint8_t), "uint8_t[]"); + swizzled = (uint8_t *) uiprivAlloc((byteStride * pixelHeight) * sizeof (uint8_t), "uint8_t[]"); bp = (uint8_t *) pixels; sp = swizzled; - for (y = 0; y < pixelHeight * pixelStride; y += pixelStride) - for (x = 0; x < pixelStride; x++) { - sp[0] = bp[2]; - sp[1] = bp[1]; - sp[2] = bp[0]; - sp[3] = bp[3]; - sp += 4; - bp += 4; - } + for (n = 0; n < byteStride * pixelHeight; n += 4) { + sp[0] = bp[2]; + sp[1] = bp[1]; + sp[2] = bp[0]; + sp[3] = bp[3]; + sp += 4; + bp += 4; + } pix[0] = (unsigned char *) swizzled; repCalibrated = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:pix @@ -63,7 +62,7 @@ void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, in isPlanar:NO colorSpaceName:NSCalibratedRGBColorSpace bitmapFormat:0 - bytesPerRow:pixelStride + bytesPerRow:byteStride bitsPerPixel:32]; repsRGB = [repCalibrated bitmapImageRepByRetaggingWithColorSpace:[NSColorSpace sRGBColorSpace]]; diff --git a/uitable.h b/uitable.h index 8d069eb5..15f33ebe 100644 --- a/uitable.h +++ b/uitable.h @@ -36,13 +36,12 @@ _UI_EXTERN void uiFreeImage(uiImage *i); // uiImageAppend adds a representation to the uiImage. // pixels should point to a byte array of non-premultiplied pixels -// stored in [A R G B] order (so ((uint8_t *) pixels)[0] is the A of the +// stored in [R G B A] order (so ((uint8_t *) pixels)[0] is the A of the // first pixel and [3] is the B of the first pixel). pixelWidth and // pixelHeight is the size *in pixels* of the image, and pixelStride is -// the number *of pixels* per row of the pixels array. Therefore, -// pixels itself must be at least 4 * pixelStride * pixelHeight bytes -// long. -_UI_EXTERN void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int pixelStride); +// the number *of bytes* per row of the pixels array. Therefore, +// pixels itself must be at least byteStride * pixelHeight bytes long. +_UI_EXTERN void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int byteStride); typedef struct uiTableValue uiTableValue; diff --git a/unix/image.c b/unix/image.c index 3b5db020..eecdec18 100644 --- a/unix/image.c +++ b/unix/image.c @@ -34,24 +34,26 @@ void uiFreeImage(uiImage *i) uiprivFree(i); } -void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int pixelStride) +void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int byteStride) { cairo_surface_t *cs; unsigned char *buf, *p; uint8_t *src = (uint8_t *) pixels; - int cstride; - int y; + int cByteStride; + int n; - cstride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, pixelWidth); - buf = (unsigned char *) uiprivAlloc((cstride * pixelHeight * 4) * sizeof (unsigned char), "unsigned char[]"); + // unfortunately for optimal performance cairo expects its own stride values and we will have to reconcile them if they differ + cByteStride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, pixelWidth); + buf = (unsigned char *) uiprivAlloc((cByteStride * pixelHeight) * sizeof (unsigned char), "unsigned char[]"); p = buf; - for (y = 0; y < pixelStride * pixelHeight; y += pixelStride) { - memmove(p, src + y, cstride); - p += cstride; + for (n = 0; n < byteStride * pixelHeight; n += byteStride) { + memmove(p, src + n, cByteStride); + p += cByteStride; } + // also note that stride here is also in bytes cs = cairo_image_surface_create_for_data(buf, CAIRO_FORMAT_ARGB32, pixelWidth, pixelHeight, - cstride); + cByteStride); if (cairo_surface_status(cs) != CAIRO_STATUS_SUCCESS) /* TODO */; cairo_surface_flush(cs); diff --git a/unix/table.c b/unix/table.c index 9e6fe01e..d1e6fe60 100644 --- a/unix/table.c +++ b/unix/table.c @@ -2,6 +2,8 @@ #include "uipriv_unix.h" #include "table.h" +// TODO with GDK_SCALE set to 2 the 32x32 images are scaled up to 64x64? + struct uiTable { uiUnixControl c; GtkWidget *widget; diff --git a/windows/image.cpp b/windows/image.cpp index 0058aa13..368c77fa 100644 --- a/windows/image.cpp +++ b/windows/image.cpp @@ -39,14 +39,14 @@ void uiFreeImage(uiImage *i) uiprivFree(i); } -void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int pixelStride) +void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int byteStride) { IWICBitmap *b; HRESULT hr; hr = uiprivWICFactory->CreateBitmapFromMemory(pixelWidth, pixelHeight, - GUID_WICPixelFormat32bppRGBA, pixelStride, - pixelStride * pixelHeight, (BYTE *) pixels, + GUID_WICPixelFormat32bppRGBA, byteStride, + byteStride * pixelHeight, (BYTE *) pixels, &b); if (hr != S_OK) logHRESULT(L"error calling CreateBitmapFromMemory() in uiImageAppend()", hr); From c0742d3de00325a618334a62b4c5c9c4799ddd6b Mon Sep 17 00:00:00 2001 From: Niklas Mischkulnig Date: Mon, 6 Aug 2018 09:06:02 +0200 Subject: [PATCH 1261/1329] Mac uiImageAppend: handle stride correctly --- darwin/image.m | 20 ++++++++++++-------- uitable.h | 6 +++--- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/darwin/image.m b/darwin/image.m index 9492cd07..cf2bdf13 100644 --- a/darwin/image.m +++ b/darwin/image.m @@ -34,7 +34,7 @@ void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, in { NSBitmapImageRep *repCalibrated, *repsRGB; uint8_t *swizzled, *bp, *sp; - int n; + int n, l; unsigned char *pix[1]; // OS X demands that R and B are in the opposite order from what we expect @@ -43,13 +43,17 @@ void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, in swizzled = (uint8_t *) uiprivAlloc((byteStride * pixelHeight) * sizeof (uint8_t), "uint8_t[]"); bp = (uint8_t *) pixels; sp = swizzled; - for (n = 0; n < byteStride * pixelHeight; n += 4) { - sp[0] = bp[2]; - sp[1] = bp[1]; - sp[2] = bp[0]; - sp[3] = bp[3]; - sp += 4; - bp += 4; + for (l = 0; l < pixelHeight; l++){ + for (n = 0; n < pixelWidth; n++) { + sp[0] = bp[2]; + sp[1] = bp[1]; + sp[2] = bp[0]; + sp[3] = bp[3]; + sp += 4; + bp += 4; + } + // jump over unused bytes at end of line + bp += byteStride - pixelWidth * 4; } pix[0] = (unsigned char *) swizzled; diff --git a/uitable.h b/uitable.h index 15f33ebe..17a7abe8 100644 --- a/uitable.h +++ b/uitable.h @@ -19,7 +19,7 @@ // desktop systems at the time of writing (mid-2018) // // uiImage is very simple: it only supports non-premultiplied 32-bit -// ARGB images, and libui does not provide any image file loading +// RGBA images, and libui does not provide any image file loading // or image format conversion utilities on top of that. typedef struct uiImage uiImage; @@ -36,8 +36,8 @@ _UI_EXTERN void uiFreeImage(uiImage *i); // uiImageAppend adds a representation to the uiImage. // pixels should point to a byte array of non-premultiplied pixels -// stored in [R G B A] order (so ((uint8_t *) pixels)[0] is the A of the -// first pixel and [3] is the B of the first pixel). pixelWidth and +// stored in [R G B A] order (so ((uint8_t *) pixels)[0] is the R of the +// first pixel and [3] is the A of the first pixel). pixelWidth and // pixelHeight is the size *in pixels* of the image, and pixelStride is // the number *of bytes* per row of the pixels array. Therefore, // pixels itself must be at least byteStride * pixelHeight bytes long. From 68dade3e8103c6159ed908ab9f81cc6b407094f6 Mon Sep 17 00:00:00 2001 From: Niklas Mischkulnig Date: Mon, 6 Aug 2018 10:54:19 +0200 Subject: [PATCH 1262/1329] Rename loop variable --- darwin/image.m | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/darwin/image.m b/darwin/image.m index cf2bdf13..a4b322c7 100644 --- a/darwin/image.m +++ b/darwin/image.m @@ -34,7 +34,7 @@ void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, in { NSBitmapImageRep *repCalibrated, *repsRGB; uint8_t *swizzled, *bp, *sp; - int n, l; + int x, y; unsigned char *pix[1]; // OS X demands that R and B are in the opposite order from what we expect @@ -43,8 +43,8 @@ void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, in swizzled = (uint8_t *) uiprivAlloc((byteStride * pixelHeight) * sizeof (uint8_t), "uint8_t[]"); bp = (uint8_t *) pixels; sp = swizzled; - for (l = 0; l < pixelHeight; l++){ - for (n = 0; n < pixelWidth; n++) { + for (y = 0; y < pixelHeight; y++){ + for (x = 0; x < pixelWidth; x++) { sp[0] = bp[2]; sp[1] = bp[1]; sp[2] = bp[0]; From cd38f774905d60c9c50e42fb71b72058bfe7c3d7 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 6 Aug 2018 08:52:51 -0400 Subject: [PATCH 1263/1329] More TODOs regarding strides. --- uitable.h | 1 + 1 file changed, 1 insertion(+) diff --git a/uitable.h b/uitable.h index 17a7abe8..23e7abab 100644 --- a/uitable.h +++ b/uitable.h @@ -41,6 +41,7 @@ _UI_EXTERN void uiFreeImage(uiImage *i); // pixelHeight is the size *in pixels* of the image, and pixelStride is // the number *of bytes* per row of the pixels array. Therefore, // pixels itself must be at least byteStride * pixelHeight bytes long. +// TODO see if we either need the stride or can provide a way to get the OS-preferred stride (in cairo we do) _UI_EXTERN void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int byteStride); typedef struct uiTableValue uiTableValue; From dee35216ac3ac826260a592765bd5a42db84a8a4 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 6 Aug 2018 20:03:57 -0400 Subject: [PATCH 1264/1329] Added documentation for uiTableValue. Also added a @role to uiFreeAttribute(). --- ui.h | 1 + uitable.h | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/ui.h b/ui.h index 228fa755..07f22371 100644 --- a/ui.h +++ b/ui.h @@ -512,6 +512,7 @@ _UI_EXTERN void uiDrawRestore(uiDrawContext *c); // contents as necessary. typedef struct uiAttribute uiAttribute; +// @role uiAttribute destructor // uiFreeAttribute() frees a uiAttribute. You generally do not need to // call this yourself, as uiAttributedString does this for you. In fact, // it is an error to call this function on a uiAttribute that has been diff --git a/uitable.h b/uitable.h index 23e7abab..77c11add 100644 --- a/uitable.h +++ b/uitable.h @@ -44,10 +44,31 @@ _UI_EXTERN void uiFreeImage(uiImage *i); // TODO see if we either need the stride or can provide a way to get the OS-preferred stride (in cairo we do) _UI_EXTERN void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int byteStride); +// uiTableValue stores a value to be passed along uiTable and +// uiTableModel. +// +// You do not create uiTableValues directly; instead, you create a +// uiTableValue of a given type using the specialized constructor +// functions. +// +// uiTableValues are immutable and the uiTableModel and uiTable +// take ownership of the uiTableValue object once returned, copying +// its contents as necessary. typedef struct uiTableValue uiTableValue; +// @role uiTableValue destructor +// uiFreeTableValue() frees a uiTableValue. You generally do not +// need to call this yourself, as uiTable and uiTableModel do this +// for you. In fact, it is an error to call this function on a uiTableValue +// that has been given to a uiTable or uiTableModel. You can call this, +// however, if you created a uiTableValue that you aren't going to +// use later, or if you called a uiTableModelHandler method directly +// and thus never transferred ownership of the uiTableValue. _UI_EXTERN void uiFreeTableValue(uiTableValue *v); +// uiTableValueType holds the possible uiTableValue types that may +// be returned by uiTableValueGetType(). Refer to the documentation +// for each type's constructor function for details on each type. // TODO actually validate these _UI_ENUM(uiTableValueType) { uiTableValueTypeString, @@ -56,19 +77,56 @@ _UI_ENUM(uiTableValueType) { uiTableValueTypeColor, }; +// uiTableValueGetType() returns the type of v. // TODO I don't like this name _UI_EXTERN uiTableValueType uiTableValueGetType(const uiTableValue *v); +// uiNewTableValueString() returns a new uiTableValue that contains +// str. str is copied; you do not need to keep it alive after +// uiNewTableValueString() returns. _UI_EXTERN uiTableValue *uiNewTableValueString(const char *str); + +// uiTableValueString() returns the string stored in v. The returned +// string is owned by v. It is an error to call this on a uiTableValue +// that does not hold a string. _UI_EXTERN const char *uiTableValueString(const uiTableValue *v); +// uiNewTableValueImage() returns a new uiTableValue that contains +// the given uiImage. +// +// Unlike other similar constructors, uiNewTableValueImage() does +// NOT copy the image. This is because images are comparatively +// larger than the other objects in question. Therefore, you MUST +// keep the image alive as long as the returned uiTableValue is alive. +// As a general rule, if libui calls a uiTableModelHandler method, the +// uiImage is safe to free once any of your code is once again +// executed. _UI_EXTERN uiTableValue *uiNewTableValueImage(uiImage *img); + +// uiTableValueImage() returns the uiImage stored in v. As these +// images are not owned by v, you should not assume anything +// about the lifetime of the image (unless you created the image, +// and thus control its lifetime). It is an error to call this on a +// uiTableValue that does not hold an image. _UI_EXTERN uiImage *uiTableValueImage(const uiTableValue *v); +// uiNewTableValueInt() returns a uiTableValue that stores the given +// int. This can be used both for boolean values (nonzero is true, as +// in C) or progresses (in which case the valid range is -1..100 +// inclusive). _UI_EXTERN uiTableValue *uiNewTableValueInt(int i); + +// uiTableValueInt() returns the int stored in v. It is an error to call +// this on a uiTableValue that does not store an int. _UI_EXTERN int uiTableValueInt(const uiTableValue *v); +// uiNewTableValueColor() returns a uiTableValue that stores the +// given color. _UI_EXTERN uiTableValue *uiNewTableValueColor(double r, double g, double b, double a); + +// uiTableValueColor() returns the color stored in v. It is an error to +// call this on a uiTableValue that does not store a color. +// TODO define whether all this, for both uiTableValue and uiAttribute, is undefined behavior or a caught error _UI_EXTERN void uiTableValueColor(const uiTableValue *v, double *r, double *g, double *b, double *a); typedef struct uiTableModel uiTableModel; From 9202aeee2aa5774d03745a89e1d0b8ebc1dbb427 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 7 Aug 2018 10:58:12 -0400 Subject: [PATCH 1265/1329] Documented uiTableModel. --- uitable.h | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/uitable.h b/uitable.h index 77c11add..36ff0ccc 100644 --- a/uitable.h +++ b/uitable.h @@ -129,22 +129,90 @@ _UI_EXTERN uiTableValue *uiNewTableValueColor(double r, double g, double b, doub // TODO define whether all this, for both uiTableValue and uiAttribute, is undefined behavior or a caught error _UI_EXTERN void uiTableValueColor(const uiTableValue *v, double *r, double *g, double *b, double *a); +// uiTableModel is an object that provides the data for a uiTable. +// This data is returned via methods you provide in the +// uiTableModelHandler struct. +// +// uiTableModel represents data using a table, but this table does +// not map directly to uiTable itself. Instead, you can have data +// columns which provide instructions for how to render a given +// uiTable's column — for instance, one model column can be used +// to give certain rows of a uiTable a different background color. +// +// Once created, the number and data types of columns of a +// uiTableModel cannot change. +// +// Row and column numbers start at 0. typedef struct uiTableModel uiTableModel; + +// uiTableModelHandler defines the methods that uiTableModel +// calls when it needs data. Once a uiTableModel is created, these +// methods cannot change. typedef struct uiTableModelHandler uiTableModelHandler; // TODO validate ranges; validate types on each getter/setter call (? table columns only?) struct uiTableModelHandler { + // NumColumns returns the number of model columns in the + // uiTableModel. This value must remain constant through the + // lifetime of the uiTableModel. This method is not guaranteed + // to be called depending on the system. + // TODO strongly check column numbers and types on all platforms so these clauses can go away int (*NumColumns)(uiTableModelHandler *, uiTableModel *); + // ColumnType returns the value type of the data stored in + // the given model column of the uiTableModel. The returned + // values must remain constant through the lifetime of the + // uiTableModel. This method is not guaranteed to be called + // depending on the system. uiTableValueType (*ColumnType)(uiTableModelHandler *, uiTableModel *, int); + // NumRows returns the number or rows in the uiTableModel. + // This value must be non-negative. int (*NumRows)(uiTableModelHandler *, uiTableModel *); - uiTableValue *(*CellValue)(uiTableModelHandler *, uiTableModel *, int, int); + // CellValue returns a uiTableValue corresponding to the model + // cell at (row, column). The type of the returned uiTableValue + // must match column's value type. Under some circumstances, + // NULL may be returned; refer to the various methods that add + // columns to uiTable for details. Once returned, the uiTable + // that calls CellValue will free the uiTableValue returned. + uiTableValue *(*CellValue)(uiTableModelHandler *mh, uiTableModel *m, int row, int column); + // SetCellValue changes the model cell value at (row, column) + // in the uiTableModel. Within this function, either do nothing + // to keep the current cell value or save the new cell value as + // appropriate. After SetCellValue is called, the uiTable will + // itself reload the table cell. Under certain conditions, the + // uiTableValue passed in can be NULL; refer to the various + // methods that add columns to uiTable for details. Once + // returned, the uiTable that called SetCellValue will free the + // uiTableValue passed in. void (*SetCellValue)(uiTableModelHandler *, uiTableModel *, int, int, const uiTableValue *); }; +// uiNewTableModel() creates a new uiTableModel with the given +// handler methods. _UI_EXTERN uiTableModel *uiNewTableModel(uiTableModelHandler *mh); + +// uiFreeTableModel() frees the given table model. It is an error to +// free table models currently associated with a uiTable. _UI_EXTERN void uiFreeTableModel(uiTableModel *m); + +// uiTableModelRowInserted() tells any uiTable associated with m +// that a new row has been added to m at index index. You call +// this function when the number of rows in your model has +// changed; after calling it, NumRows() should returm the new row +// count. _UI_EXTERN void uiTableModelRowInserted(uiTableModel *m, int newIndex); + +// uiTableModelRowChanged() tells any uiTable associated with m +// that the data in the row at index has changed. You do not need to +// call this in your SetCellValue() handlers, but you do need to call +// this if your data changes at some other point. _UI_EXTERN void uiTableModelRowChanged(uiTableModel *m, int index); + +// uiTableModelRowDeleted() tells any uiTable associated with m +// that the row at index index has been deleted. You call this +// function when the number of rows in your model has changed; +// after calling it, NumRows() should returm the new row +// count. +// TODO for this and Inserted: make sure the "after" part is right; clarify if it's after returning or after calling _UI_EXTERN void uiTableModelRowDeleted(uiTableModel *m, int oldIndex); // TODO reordering/moving From 1c3d7be2279b546dde9efa24a3c6e1fb35f56fcd Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 7 Aug 2018 11:18:11 -0400 Subject: [PATCH 1266/1329] More uiTableModel documentation. --- uitable.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/uitable.h b/uitable.h index 36ff0ccc..20054ace 100644 --- a/uitable.h +++ b/uitable.h @@ -138,11 +138,13 @@ _UI_EXTERN void uiTableValueColor(const uiTableValue *v, double *r, double *g, d // columns which provide instructions for how to render a given // uiTable's column — for instance, one model column can be used // to give certain rows of a uiTable a different background color. +// Row numbers DO match with uiTable row numbers. // // Once created, the number and data types of columns of a // uiTableModel cannot change. // -// Row and column numbers start at 0. +// Row and column numbers start at 0. A uiTableModel can be +// associated with more than one uiTable at a time. typedef struct uiTableModel uiTableModel; // uiTableModelHandler defines the methods that uiTableModel @@ -186,10 +188,12 @@ struct uiTableModelHandler { void (*SetCellValue)(uiTableModelHandler *, uiTableModel *, int, int, const uiTableValue *); }; +// @role uiTableModel constructor // uiNewTableModel() creates a new uiTableModel with the given // handler methods. _UI_EXTERN uiTableModel *uiNewTableModel(uiTableModelHandler *mh); +// @role uiTableModel destructor // uiFreeTableModel() frees the given table model. It is an error to // free table models currently associated with a uiTable. _UI_EXTERN void uiFreeTableModel(uiTableModel *m); From f47e1423cf95ad7b1001663f3381b5a819fc67b9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 8 Aug 2018 09:17:50 -0400 Subject: [PATCH 1267/1329] And finished documenting uitable.h. Yay, we can merge back in now! --- uitable.h | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/uitable.h b/uitable.h index 20054ace..715fcf04 100644 --- a/uitable.h +++ b/uitable.h @@ -220,41 +220,88 @@ _UI_EXTERN void uiTableModelRowChanged(uiTableModel *m, int index); _UI_EXTERN void uiTableModelRowDeleted(uiTableModel *m, int oldIndex); // TODO reordering/moving +// uiTableModelColumnNeverEditable and +// uiTableModelColumnAlwaysEditable are the value of an editable +// model column parameter to one of the uiTable create column +// functions; if used, that jparticular uiTable colum is not editable +// by the user and always editable by the user, respectively. #define uiTableModelColumnNeverEditable (-1) #define uiTableModelColumnAlwaysEditable (-2) +// uiTableTextColumnOptionalParams are the optional parameters +// that control the appearance of the text column of a uiTable. typedef struct uiTableTextColumnOptionalParams uiTableTextColumnOptionalParams; + +// uiTableParams defines the parameters passed to uiNewTable(). typedef struct uiTableParams uiTableParams; struct uiTableTextColumnOptionalParams { + // ColorModelColumn is the model column containing the + // text color of this uiTable column's text, or -1 to use the + // default color. + // + // If CellValue() for this column for any cell returns NULL, that + // cell will also use the default text color. int ColorModelColumn; }; struct uiTableParams { + // Model is the uiTableModel to use for this uiTable. + // This parameter cannot be NULL. uiTableModel *Model; + // RowBackgroundColorModelColumn is a model column + // number that defines the background color used for the + // entire row in the uiTable, or -1 to use the default color for + // all rows. + // + // If CellValue() for this column for any row returns NULL, that + // row will also use the default background color. int RowBackgroundColorModelColumn; }; +// uiTable is a uiControl that shows tabular data, allowing users to +// manipulate rows of such data at a time. typedef struct uiTable uiTable; #define uiTable(this) ((uiTable *) (this)) + +// uiTableAppendTextColumn() appends a text column to t. +// name is displayed in the table header. +// textModelColumn is where the text comes from. +// If a row is editable according to textEditableModelColumn, +// SetCellValue() is called with textModelColumn as the column. _UI_EXTERN void uiTableAppendTextColumn(uiTable *t, const char *name, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *textParams); + +// uiTableAppendImageColumn() appends an image column to t. +// Images are drawn at icon size, appropriate to the pixel density +// of the screen showing the uiTable. _UI_EXTERN void uiTableAppendImageColumn(uiTable *t, const char *name, int imageModelColumn); + +// uiTableAppendImageTextColumn() appends a column to t that +// shows both an image and text. _UI_EXTERN void uiTableAppendImageTextColumn(uiTable *t, const char *name, int imageModelColumn, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *textParams); + +// uiTableAppendCheckboxColumn appends a column to t that +// contains a checkbox that the user can interact with (assuming the +// checkbox is editable). SetCellValue() will be called with +// checkboxModelColumn as the column in this case. _UI_EXTERN void uiTableAppendCheckboxColumn(uiTable *t, const char *name, int checkboxModelColumn, int checkboxEditableModelColumn); + +// uiTableAppendCheckboxTextColumn() appends a column to t +// that contains both a checkbox and text. _UI_EXTERN void uiTableAppendCheckboxTextColumn(uiTable *t, const char *name, int checkboxModelColumn, @@ -262,11 +309,25 @@ _UI_EXTERN void uiTableAppendCheckboxTextColumn(uiTable *t, int textModelColumn, int textEditableModelColumn, uiTableTextColumnOptionalParams *textParams); + +// uiTableAppendProgressBarColumn() appends a column to t +// that displays a progress bar. These columns work like +// uiProgressBar: a cell value of 0..100 displays that percentage, and +// a cell value of -1 displays an indeterminate progress bar. _UI_EXTERN void uiTableAppendProgressBarColumn(uiTable *t, const char *name, int progressModelColumn); + +// uiTableAppendButtonColumn() appends a column to t +// that shows a button that the user can click on. When the user +// does click on the button, SetCellValue() is called with a NULL +// value and buttonModelColumn as the column. +// CellValue() on buttonModelColumn should return the text to show +// in the button. _UI_EXTERN void uiTableAppendButtonColumn(uiTable *t, const char *name, int buttonModelColumn, int buttonClickableModelColumn); + +// uiNewTable() creates a new uiTable with the specified parameters. _UI_EXTERN uiTable *uiNewTable(uiTableParams *params); From 1a8e5555da752a3ef8e5385f0c6d6f07a2b1a076 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 8 Aug 2018 09:24:22 -0400 Subject: [PATCH 1268/1329] Updated the README and merged uitable.h into ui.h. Now to merge! --- README.md | 3 + ui.h | 332 ++++++++++++++++++++++++++++++++++++++++++++++++++++- uitable.h | 333 ------------------------------------------------------ 3 files changed, 333 insertions(+), 335 deletions(-) delete mode 100644 uitable.h diff --git a/README.md b/README.md index e9bbd4e5..6fd69209 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,9 @@ But libui is not dead; I am working on it whenever I can, and I hope to get it t *Note that today's entry (Eastern Time) may be updated later today.* +* **8 August 2018** + * Finally introduced an API for loading images, `uiImage`, and a new control, `uiTable`, for displaying tabular data. These provide enough basic functionality for now, but will be improved over time. You can read the documentation for the new features as they are [here](https://github.com/andlabs/libui/blob/f47e1423cf95ad7b1001663f3381b5a819fc67b9/uitable.h). Thanks to everyone who helped get to this point, in particular @bcampbell for the initial Windows code, and to everyone else for their patience! + * **30 May 2018** * Merged the previous Announcements and Updates section of this README into a single News section, and merged the respective archive files into a single NEWS.md file. diff --git a/ui.h b/ui.h index 07f22371..253cd2f0 100644 --- a/ui.h +++ b/ui.h @@ -1124,8 +1124,336 @@ _UI_EXTERN int uiGridPadded(uiGrid *g); _UI_EXTERN void uiGridSetPadded(uiGrid *g, int padded); _UI_EXTERN uiGrid *uiNewGrid(void); -// TODO merge -#include "uitable.h" +// uiImage stores an image for display on screen. +// +// Images are built from one or more representations, each with the +// same aspect ratio but a different pixel size. libui automatically +// selects the most appropriate representation for drawing the image +// when it comes time to draw the image; what this means depends +// on the pixel density of the target context. Therefore, one can use +// uiImage to draw higher-detailed images on higher-density +// displays. The typical use cases are either: +// +// - have just a single representation, at which point all screens +// use the same image, and thus uiImage acts like a simple +// bitmap image, or +// - have two images, one at normal resolution and one at 2x +// resolution; this matches the current expectations of some +// desktop systems at the time of writing (mid-2018) +// +// uiImage is very simple: it only supports non-premultiplied 32-bit +// RGBA images, and libui does not provide any image file loading +// or image format conversion utilities on top of that. +typedef struct uiImage uiImage; + +// @role uiImage constructor +// uiNewImage creates a new uiImage with the given width and +// height. This width and height should be the size in points of the +// image in the device-independent case; typically this is the 1x size. +// TODO for all uiImage functions: use const void * for const correctness +_UI_EXTERN uiImage *uiNewImage(double width, double height); + +// @role uiImage destructor +// uiFreeImage frees the given image and all associated resources. +_UI_EXTERN void uiFreeImage(uiImage *i); + +// uiImageAppend adds a representation to the uiImage. +// pixels should point to a byte array of non-premultiplied pixels +// stored in [R G B A] order (so ((uint8_t *) pixels)[0] is the R of the +// first pixel and [3] is the A of the first pixel). pixelWidth and +// pixelHeight is the size *in pixels* of the image, and pixelStride is +// the number *of bytes* per row of the pixels array. Therefore, +// pixels itself must be at least byteStride * pixelHeight bytes long. +// TODO see if we either need the stride or can provide a way to get the OS-preferred stride (in cairo we do) +_UI_EXTERN void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int byteStride); + +// uiTableValue stores a value to be passed along uiTable and +// uiTableModel. +// +// You do not create uiTableValues directly; instead, you create a +// uiTableValue of a given type using the specialized constructor +// functions. +// +// uiTableValues are immutable and the uiTableModel and uiTable +// take ownership of the uiTableValue object once returned, copying +// its contents as necessary. +typedef struct uiTableValue uiTableValue; + +// @role uiTableValue destructor +// uiFreeTableValue() frees a uiTableValue. You generally do not +// need to call this yourself, as uiTable and uiTableModel do this +// for you. In fact, it is an error to call this function on a uiTableValue +// that has been given to a uiTable or uiTableModel. You can call this, +// however, if you created a uiTableValue that you aren't going to +// use later, or if you called a uiTableModelHandler method directly +// and thus never transferred ownership of the uiTableValue. +_UI_EXTERN void uiFreeTableValue(uiTableValue *v); + +// uiTableValueType holds the possible uiTableValue types that may +// be returned by uiTableValueGetType(). Refer to the documentation +// for each type's constructor function for details on each type. +// TODO actually validate these +_UI_ENUM(uiTableValueType) { + uiTableValueTypeString, + uiTableValueTypeImage, + uiTableValueTypeInt, + uiTableValueTypeColor, +}; + +// uiTableValueGetType() returns the type of v. +// TODO I don't like this name +_UI_EXTERN uiTableValueType uiTableValueGetType(const uiTableValue *v); + +// uiNewTableValueString() returns a new uiTableValue that contains +// str. str is copied; you do not need to keep it alive after +// uiNewTableValueString() returns. +_UI_EXTERN uiTableValue *uiNewTableValueString(const char *str); + +// uiTableValueString() returns the string stored in v. The returned +// string is owned by v. It is an error to call this on a uiTableValue +// that does not hold a string. +_UI_EXTERN const char *uiTableValueString(const uiTableValue *v); + +// uiNewTableValueImage() returns a new uiTableValue that contains +// the given uiImage. +// +// Unlike other similar constructors, uiNewTableValueImage() does +// NOT copy the image. This is because images are comparatively +// larger than the other objects in question. Therefore, you MUST +// keep the image alive as long as the returned uiTableValue is alive. +// As a general rule, if libui calls a uiTableModelHandler method, the +// uiImage is safe to free once any of your code is once again +// executed. +_UI_EXTERN uiTableValue *uiNewTableValueImage(uiImage *img); + +// uiTableValueImage() returns the uiImage stored in v. As these +// images are not owned by v, you should not assume anything +// about the lifetime of the image (unless you created the image, +// and thus control its lifetime). It is an error to call this on a +// uiTableValue that does not hold an image. +_UI_EXTERN uiImage *uiTableValueImage(const uiTableValue *v); + +// uiNewTableValueInt() returns a uiTableValue that stores the given +// int. This can be used both for boolean values (nonzero is true, as +// in C) or progresses (in which case the valid range is -1..100 +// inclusive). +_UI_EXTERN uiTableValue *uiNewTableValueInt(int i); + +// uiTableValueInt() returns the int stored in v. It is an error to call +// this on a uiTableValue that does not store an int. +_UI_EXTERN int uiTableValueInt(const uiTableValue *v); + +// uiNewTableValueColor() returns a uiTableValue that stores the +// given color. +_UI_EXTERN uiTableValue *uiNewTableValueColor(double r, double g, double b, double a); + +// uiTableValueColor() returns the color stored in v. It is an error to +// call this on a uiTableValue that does not store a color. +// TODO define whether all this, for both uiTableValue and uiAttribute, is undefined behavior or a caught error +_UI_EXTERN void uiTableValueColor(const uiTableValue *v, double *r, double *g, double *b, double *a); + +// uiTableModel is an object that provides the data for a uiTable. +// This data is returned via methods you provide in the +// uiTableModelHandler struct. +// +// uiTableModel represents data using a table, but this table does +// not map directly to uiTable itself. Instead, you can have data +// columns which provide instructions for how to render a given +// uiTable's column — for instance, one model column can be used +// to give certain rows of a uiTable a different background color. +// Row numbers DO match with uiTable row numbers. +// +// Once created, the number and data types of columns of a +// uiTableModel cannot change. +// +// Row and column numbers start at 0. A uiTableModel can be +// associated with more than one uiTable at a time. +typedef struct uiTableModel uiTableModel; + +// uiTableModelHandler defines the methods that uiTableModel +// calls when it needs data. Once a uiTableModel is created, these +// methods cannot change. +typedef struct uiTableModelHandler uiTableModelHandler; + +// TODO validate ranges; validate types on each getter/setter call (? table columns only?) +struct uiTableModelHandler { + // NumColumns returns the number of model columns in the + // uiTableModel. This value must remain constant through the + // lifetime of the uiTableModel. This method is not guaranteed + // to be called depending on the system. + // TODO strongly check column numbers and types on all platforms so these clauses can go away + int (*NumColumns)(uiTableModelHandler *, uiTableModel *); + // ColumnType returns the value type of the data stored in + // the given model column of the uiTableModel. The returned + // values must remain constant through the lifetime of the + // uiTableModel. This method is not guaranteed to be called + // depending on the system. + uiTableValueType (*ColumnType)(uiTableModelHandler *, uiTableModel *, int); + // NumRows returns the number or rows in the uiTableModel. + // This value must be non-negative. + int (*NumRows)(uiTableModelHandler *, uiTableModel *); + // CellValue returns a uiTableValue corresponding to the model + // cell at (row, column). The type of the returned uiTableValue + // must match column's value type. Under some circumstances, + // NULL may be returned; refer to the various methods that add + // columns to uiTable for details. Once returned, the uiTable + // that calls CellValue will free the uiTableValue returned. + uiTableValue *(*CellValue)(uiTableModelHandler *mh, uiTableModel *m, int row, int column); + // SetCellValue changes the model cell value at (row, column) + // in the uiTableModel. Within this function, either do nothing + // to keep the current cell value or save the new cell value as + // appropriate. After SetCellValue is called, the uiTable will + // itself reload the table cell. Under certain conditions, the + // uiTableValue passed in can be NULL; refer to the various + // methods that add columns to uiTable for details. Once + // returned, the uiTable that called SetCellValue will free the + // uiTableValue passed in. + void (*SetCellValue)(uiTableModelHandler *, uiTableModel *, int, int, const uiTableValue *); +}; + +// @role uiTableModel constructor +// uiNewTableModel() creates a new uiTableModel with the given +// handler methods. +_UI_EXTERN uiTableModel *uiNewTableModel(uiTableModelHandler *mh); + +// @role uiTableModel destructor +// uiFreeTableModel() frees the given table model. It is an error to +// free table models currently associated with a uiTable. +_UI_EXTERN void uiFreeTableModel(uiTableModel *m); + +// uiTableModelRowInserted() tells any uiTable associated with m +// that a new row has been added to m at index index. You call +// this function when the number of rows in your model has +// changed; after calling it, NumRows() should returm the new row +// count. +_UI_EXTERN void uiTableModelRowInserted(uiTableModel *m, int newIndex); + +// uiTableModelRowChanged() tells any uiTable associated with m +// that the data in the row at index has changed. You do not need to +// call this in your SetCellValue() handlers, but you do need to call +// this if your data changes at some other point. +_UI_EXTERN void uiTableModelRowChanged(uiTableModel *m, int index); + +// uiTableModelRowDeleted() tells any uiTable associated with m +// that the row at index index has been deleted. You call this +// function when the number of rows in your model has changed; +// after calling it, NumRows() should returm the new row +// count. +// TODO for this and Inserted: make sure the "after" part is right; clarify if it's after returning or after calling +_UI_EXTERN void uiTableModelRowDeleted(uiTableModel *m, int oldIndex); +// TODO reordering/moving + +// uiTableModelColumnNeverEditable and +// uiTableModelColumnAlwaysEditable are the value of an editable +// model column parameter to one of the uiTable create column +// functions; if used, that jparticular uiTable colum is not editable +// by the user and always editable by the user, respectively. +#define uiTableModelColumnNeverEditable (-1) +#define uiTableModelColumnAlwaysEditable (-2) + +// uiTableTextColumnOptionalParams are the optional parameters +// that control the appearance of the text column of a uiTable. +typedef struct uiTableTextColumnOptionalParams uiTableTextColumnOptionalParams; + +// uiTableParams defines the parameters passed to uiNewTable(). +typedef struct uiTableParams uiTableParams; + +struct uiTableTextColumnOptionalParams { + // ColorModelColumn is the model column containing the + // text color of this uiTable column's text, or -1 to use the + // default color. + // + // If CellValue() for this column for any cell returns NULL, that + // cell will also use the default text color. + int ColorModelColumn; +}; + +struct uiTableParams { + // Model is the uiTableModel to use for this uiTable. + // This parameter cannot be NULL. + uiTableModel *Model; + // RowBackgroundColorModelColumn is a model column + // number that defines the background color used for the + // entire row in the uiTable, or -1 to use the default color for + // all rows. + // + // If CellValue() for this column for any row returns NULL, that + // row will also use the default background color. + int RowBackgroundColorModelColumn; +}; + +// uiTable is a uiControl that shows tabular data, allowing users to +// manipulate rows of such data at a time. +typedef struct uiTable uiTable; +#define uiTable(this) ((uiTable *) (this)) + +// uiTableAppendTextColumn() appends a text column to t. +// name is displayed in the table header. +// textModelColumn is where the text comes from. +// If a row is editable according to textEditableModelColumn, +// SetCellValue() is called with textModelColumn as the column. +_UI_EXTERN void uiTableAppendTextColumn(uiTable *t, + const char *name, + int textModelColumn, + int textEditableModelColumn, + uiTableTextColumnOptionalParams *textParams); + +// uiTableAppendImageColumn() appends an image column to t. +// Images are drawn at icon size, appropriate to the pixel density +// of the screen showing the uiTable. +_UI_EXTERN void uiTableAppendImageColumn(uiTable *t, + const char *name, + int imageModelColumn); + +// uiTableAppendImageTextColumn() appends a column to t that +// shows both an image and text. +_UI_EXTERN void uiTableAppendImageTextColumn(uiTable *t, + const char *name, + int imageModelColumn, + int textModelColumn, + int textEditableModelColumn, + uiTableTextColumnOptionalParams *textParams); + +// uiTableAppendCheckboxColumn appends a column to t that +// contains a checkbox that the user can interact with (assuming the +// checkbox is editable). SetCellValue() will be called with +// checkboxModelColumn as the column in this case. +_UI_EXTERN void uiTableAppendCheckboxColumn(uiTable *t, + const char *name, + int checkboxModelColumn, + int checkboxEditableModelColumn); + +// uiTableAppendCheckboxTextColumn() appends a column to t +// that contains both a checkbox and text. +_UI_EXTERN void uiTableAppendCheckboxTextColumn(uiTable *t, + const char *name, + int checkboxModelColumn, + int checkboxEditableModelColumn, + int textModelColumn, + int textEditableModelColumn, + uiTableTextColumnOptionalParams *textParams); + +// uiTableAppendProgressBarColumn() appends a column to t +// that displays a progress bar. These columns work like +// uiProgressBar: a cell value of 0..100 displays that percentage, and +// a cell value of -1 displays an indeterminate progress bar. +_UI_EXTERN void uiTableAppendProgressBarColumn(uiTable *t, + const char *name, + int progressModelColumn); + +// uiTableAppendButtonColumn() appends a column to t +// that shows a button that the user can click on. When the user +// does click on the button, SetCellValue() is called with a NULL +// value and buttonModelColumn as the column. +// CellValue() on buttonModelColumn should return the text to show +// in the button. +_UI_EXTERN void uiTableAppendButtonColumn(uiTable *t, + const char *name, + int buttonModelColumn, + int buttonClickableModelColumn); + +// uiNewTable() creates a new uiTable with the specified parameters. +_UI_EXTERN uiTable *uiNewTable(uiTableParams *params); #ifdef __cplusplus } diff --git a/uitable.h b/uitable.h deleted file mode 100644 index 715fcf04..00000000 --- a/uitable.h +++ /dev/null @@ -1,333 +0,0 @@ -// 20 june 2016 -// kept in a separate file for now - -// uiImage stores an image for display on screen. -// -// Images are built from one or more representations, each with the -// same aspect ratio but a different pixel size. libui automatically -// selects the most appropriate representation for drawing the image -// when it comes time to draw the image; what this means depends -// on the pixel density of the target context. Therefore, one can use -// uiImage to draw higher-detailed images on higher-density -// displays. The typical use cases are either: -// -// - have just a single representation, at which point all screens -// use the same image, and thus uiImage acts like a simple -// bitmap image, or -// - have two images, one at normal resolution and one at 2x -// resolution; this matches the current expectations of some -// desktop systems at the time of writing (mid-2018) -// -// uiImage is very simple: it only supports non-premultiplied 32-bit -// RGBA images, and libui does not provide any image file loading -// or image format conversion utilities on top of that. -typedef struct uiImage uiImage; - -// @role uiImage constructor -// uiNewImage creates a new uiImage with the given width and -// height. This width and height should be the size in points of the -// image in the device-independent case; typically this is the 1x size. -// TODO for all uiImage functions: use const void * for const correctness -_UI_EXTERN uiImage *uiNewImage(double width, double height); - -// @role uiImage destructor -// uiFreeImage frees the given image and all associated resources. -_UI_EXTERN void uiFreeImage(uiImage *i); - -// uiImageAppend adds a representation to the uiImage. -// pixels should point to a byte array of non-premultiplied pixels -// stored in [R G B A] order (so ((uint8_t *) pixels)[0] is the R of the -// first pixel and [3] is the A of the first pixel). pixelWidth and -// pixelHeight is the size *in pixels* of the image, and pixelStride is -// the number *of bytes* per row of the pixels array. Therefore, -// pixels itself must be at least byteStride * pixelHeight bytes long. -// TODO see if we either need the stride or can provide a way to get the OS-preferred stride (in cairo we do) -_UI_EXTERN void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int byteStride); - -// uiTableValue stores a value to be passed along uiTable and -// uiTableModel. -// -// You do not create uiTableValues directly; instead, you create a -// uiTableValue of a given type using the specialized constructor -// functions. -// -// uiTableValues are immutable and the uiTableModel and uiTable -// take ownership of the uiTableValue object once returned, copying -// its contents as necessary. -typedef struct uiTableValue uiTableValue; - -// @role uiTableValue destructor -// uiFreeTableValue() frees a uiTableValue. You generally do not -// need to call this yourself, as uiTable and uiTableModel do this -// for you. In fact, it is an error to call this function on a uiTableValue -// that has been given to a uiTable or uiTableModel. You can call this, -// however, if you created a uiTableValue that you aren't going to -// use later, or if you called a uiTableModelHandler method directly -// and thus never transferred ownership of the uiTableValue. -_UI_EXTERN void uiFreeTableValue(uiTableValue *v); - -// uiTableValueType holds the possible uiTableValue types that may -// be returned by uiTableValueGetType(). Refer to the documentation -// for each type's constructor function for details on each type. -// TODO actually validate these -_UI_ENUM(uiTableValueType) { - uiTableValueTypeString, - uiTableValueTypeImage, - uiTableValueTypeInt, - uiTableValueTypeColor, -}; - -// uiTableValueGetType() returns the type of v. -// TODO I don't like this name -_UI_EXTERN uiTableValueType uiTableValueGetType(const uiTableValue *v); - -// uiNewTableValueString() returns a new uiTableValue that contains -// str. str is copied; you do not need to keep it alive after -// uiNewTableValueString() returns. -_UI_EXTERN uiTableValue *uiNewTableValueString(const char *str); - -// uiTableValueString() returns the string stored in v. The returned -// string is owned by v. It is an error to call this on a uiTableValue -// that does not hold a string. -_UI_EXTERN const char *uiTableValueString(const uiTableValue *v); - -// uiNewTableValueImage() returns a new uiTableValue that contains -// the given uiImage. -// -// Unlike other similar constructors, uiNewTableValueImage() does -// NOT copy the image. This is because images are comparatively -// larger than the other objects in question. Therefore, you MUST -// keep the image alive as long as the returned uiTableValue is alive. -// As a general rule, if libui calls a uiTableModelHandler method, the -// uiImage is safe to free once any of your code is once again -// executed. -_UI_EXTERN uiTableValue *uiNewTableValueImage(uiImage *img); - -// uiTableValueImage() returns the uiImage stored in v. As these -// images are not owned by v, you should not assume anything -// about the lifetime of the image (unless you created the image, -// and thus control its lifetime). It is an error to call this on a -// uiTableValue that does not hold an image. -_UI_EXTERN uiImage *uiTableValueImage(const uiTableValue *v); - -// uiNewTableValueInt() returns a uiTableValue that stores the given -// int. This can be used both for boolean values (nonzero is true, as -// in C) or progresses (in which case the valid range is -1..100 -// inclusive). -_UI_EXTERN uiTableValue *uiNewTableValueInt(int i); - -// uiTableValueInt() returns the int stored in v. It is an error to call -// this on a uiTableValue that does not store an int. -_UI_EXTERN int uiTableValueInt(const uiTableValue *v); - -// uiNewTableValueColor() returns a uiTableValue that stores the -// given color. -_UI_EXTERN uiTableValue *uiNewTableValueColor(double r, double g, double b, double a); - -// uiTableValueColor() returns the color stored in v. It is an error to -// call this on a uiTableValue that does not store a color. -// TODO define whether all this, for both uiTableValue and uiAttribute, is undefined behavior or a caught error -_UI_EXTERN void uiTableValueColor(const uiTableValue *v, double *r, double *g, double *b, double *a); - -// uiTableModel is an object that provides the data for a uiTable. -// This data is returned via methods you provide in the -// uiTableModelHandler struct. -// -// uiTableModel represents data using a table, but this table does -// not map directly to uiTable itself. Instead, you can have data -// columns which provide instructions for how to render a given -// uiTable's column — for instance, one model column can be used -// to give certain rows of a uiTable a different background color. -// Row numbers DO match with uiTable row numbers. -// -// Once created, the number and data types of columns of a -// uiTableModel cannot change. -// -// Row and column numbers start at 0. A uiTableModel can be -// associated with more than one uiTable at a time. -typedef struct uiTableModel uiTableModel; - -// uiTableModelHandler defines the methods that uiTableModel -// calls when it needs data. Once a uiTableModel is created, these -// methods cannot change. -typedef struct uiTableModelHandler uiTableModelHandler; - -// TODO validate ranges; validate types on each getter/setter call (? table columns only?) -struct uiTableModelHandler { - // NumColumns returns the number of model columns in the - // uiTableModel. This value must remain constant through the - // lifetime of the uiTableModel. This method is not guaranteed - // to be called depending on the system. - // TODO strongly check column numbers and types on all platforms so these clauses can go away - int (*NumColumns)(uiTableModelHandler *, uiTableModel *); - // ColumnType returns the value type of the data stored in - // the given model column of the uiTableModel. The returned - // values must remain constant through the lifetime of the - // uiTableModel. This method is not guaranteed to be called - // depending on the system. - uiTableValueType (*ColumnType)(uiTableModelHandler *, uiTableModel *, int); - // NumRows returns the number or rows in the uiTableModel. - // This value must be non-negative. - int (*NumRows)(uiTableModelHandler *, uiTableModel *); - // CellValue returns a uiTableValue corresponding to the model - // cell at (row, column). The type of the returned uiTableValue - // must match column's value type. Under some circumstances, - // NULL may be returned; refer to the various methods that add - // columns to uiTable for details. Once returned, the uiTable - // that calls CellValue will free the uiTableValue returned. - uiTableValue *(*CellValue)(uiTableModelHandler *mh, uiTableModel *m, int row, int column); - // SetCellValue changes the model cell value at (row, column) - // in the uiTableModel. Within this function, either do nothing - // to keep the current cell value or save the new cell value as - // appropriate. After SetCellValue is called, the uiTable will - // itself reload the table cell. Under certain conditions, the - // uiTableValue passed in can be NULL; refer to the various - // methods that add columns to uiTable for details. Once - // returned, the uiTable that called SetCellValue will free the - // uiTableValue passed in. - void (*SetCellValue)(uiTableModelHandler *, uiTableModel *, int, int, const uiTableValue *); -}; - -// @role uiTableModel constructor -// uiNewTableModel() creates a new uiTableModel with the given -// handler methods. -_UI_EXTERN uiTableModel *uiNewTableModel(uiTableModelHandler *mh); - -// @role uiTableModel destructor -// uiFreeTableModel() frees the given table model. It is an error to -// free table models currently associated with a uiTable. -_UI_EXTERN void uiFreeTableModel(uiTableModel *m); - -// uiTableModelRowInserted() tells any uiTable associated with m -// that a new row has been added to m at index index. You call -// this function when the number of rows in your model has -// changed; after calling it, NumRows() should returm the new row -// count. -_UI_EXTERN void uiTableModelRowInserted(uiTableModel *m, int newIndex); - -// uiTableModelRowChanged() tells any uiTable associated with m -// that the data in the row at index has changed. You do not need to -// call this in your SetCellValue() handlers, but you do need to call -// this if your data changes at some other point. -_UI_EXTERN void uiTableModelRowChanged(uiTableModel *m, int index); - -// uiTableModelRowDeleted() tells any uiTable associated with m -// that the row at index index has been deleted. You call this -// function when the number of rows in your model has changed; -// after calling it, NumRows() should returm the new row -// count. -// TODO for this and Inserted: make sure the "after" part is right; clarify if it's after returning or after calling -_UI_EXTERN void uiTableModelRowDeleted(uiTableModel *m, int oldIndex); -// TODO reordering/moving - -// uiTableModelColumnNeverEditable and -// uiTableModelColumnAlwaysEditable are the value of an editable -// model column parameter to one of the uiTable create column -// functions; if used, that jparticular uiTable colum is not editable -// by the user and always editable by the user, respectively. -#define uiTableModelColumnNeverEditable (-1) -#define uiTableModelColumnAlwaysEditable (-2) - -// uiTableTextColumnOptionalParams are the optional parameters -// that control the appearance of the text column of a uiTable. -typedef struct uiTableTextColumnOptionalParams uiTableTextColumnOptionalParams; - -// uiTableParams defines the parameters passed to uiNewTable(). -typedef struct uiTableParams uiTableParams; - -struct uiTableTextColumnOptionalParams { - // ColorModelColumn is the model column containing the - // text color of this uiTable column's text, or -1 to use the - // default color. - // - // If CellValue() for this column for any cell returns NULL, that - // cell will also use the default text color. - int ColorModelColumn; -}; - -struct uiTableParams { - // Model is the uiTableModel to use for this uiTable. - // This parameter cannot be NULL. - uiTableModel *Model; - // RowBackgroundColorModelColumn is a model column - // number that defines the background color used for the - // entire row in the uiTable, or -1 to use the default color for - // all rows. - // - // If CellValue() for this column for any row returns NULL, that - // row will also use the default background color. - int RowBackgroundColorModelColumn; -}; - -// uiTable is a uiControl that shows tabular data, allowing users to -// manipulate rows of such data at a time. -typedef struct uiTable uiTable; -#define uiTable(this) ((uiTable *) (this)) - -// uiTableAppendTextColumn() appends a text column to t. -// name is displayed in the table header. -// textModelColumn is where the text comes from. -// If a row is editable according to textEditableModelColumn, -// SetCellValue() is called with textModelColumn as the column. -_UI_EXTERN void uiTableAppendTextColumn(uiTable *t, - const char *name, - int textModelColumn, - int textEditableModelColumn, - uiTableTextColumnOptionalParams *textParams); - -// uiTableAppendImageColumn() appends an image column to t. -// Images are drawn at icon size, appropriate to the pixel density -// of the screen showing the uiTable. -_UI_EXTERN void uiTableAppendImageColumn(uiTable *t, - const char *name, - int imageModelColumn); - -// uiTableAppendImageTextColumn() appends a column to t that -// shows both an image and text. -_UI_EXTERN void uiTableAppendImageTextColumn(uiTable *t, - const char *name, - int imageModelColumn, - int textModelColumn, - int textEditableModelColumn, - uiTableTextColumnOptionalParams *textParams); - -// uiTableAppendCheckboxColumn appends a column to t that -// contains a checkbox that the user can interact with (assuming the -// checkbox is editable). SetCellValue() will be called with -// checkboxModelColumn as the column in this case. -_UI_EXTERN void uiTableAppendCheckboxColumn(uiTable *t, - const char *name, - int checkboxModelColumn, - int checkboxEditableModelColumn); - -// uiTableAppendCheckboxTextColumn() appends a column to t -// that contains both a checkbox and text. -_UI_EXTERN void uiTableAppendCheckboxTextColumn(uiTable *t, - const char *name, - int checkboxModelColumn, - int checkboxEditableModelColumn, - int textModelColumn, - int textEditableModelColumn, - uiTableTextColumnOptionalParams *textParams); - -// uiTableAppendProgressBarColumn() appends a column to t -// that displays a progress bar. These columns work like -// uiProgressBar: a cell value of 0..100 displays that percentage, and -// a cell value of -1 displays an indeterminate progress bar. -_UI_EXTERN void uiTableAppendProgressBarColumn(uiTable *t, - const char *name, - int progressModelColumn); - -// uiTableAppendButtonColumn() appends a column to t -// that shows a button that the user can click on. When the user -// does click on the button, SetCellValue() is called with a NULL -// value and buttonModelColumn as the column. -// CellValue() on buttonModelColumn should return the text to show -// in the button. -_UI_EXTERN void uiTableAppendButtonColumn(uiTable *t, - const char *name, - int buttonModelColumn, - int buttonClickableModelColumn); - -// uiNewTable() creates a new uiTable with the specified parameters. -_UI_EXTERN uiTable *uiNewTable(uiTableParams *params); From ad1641f9ab0f488f6391142f44bcc0ef3fd3e7df Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 9 Aug 2018 04:28:10 -0400 Subject: [PATCH 1269/1329] Cleaned up timers in uiUninit() on Windows. Update #395. --- windows/init.cpp | 1 + windows/main.cpp | 17 +++++++++++++++++ windows/uipriv_windows.hpp | 2 ++ windows/utilwin.cpp | 2 +- 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/windows/init.cpp b/windows/init.cpp index 2fb52c0f..6b6752da 100644 --- a/windows/init.cpp +++ b/windows/init.cpp @@ -140,6 +140,7 @@ const char *uiInit(uiInitOptions *o) void uiUninit(void) { + uiprivUninitTimers(); uiprivUninitImage(); uninitMenus(); unregisterD2DScratchClass(); diff --git a/windows/main.cpp b/windows/main.cpp index d171e339..485ef570 100644 --- a/windows/main.cpp +++ b/windows/main.cpp @@ -129,6 +129,8 @@ void uiQueueMain(void (*f)(void *data), void *data) logLastError(L"error queueing function to run on main thread"); } +static std::map timers; + void uiTimer(int milliseconds, int (*f)(void *data), void *data) { uiprivTimer *timer; @@ -139,4 +141,19 @@ void uiTimer(int milliseconds, int (*f)(void *data), void *data) // note that timer IDs are pointer sized precisely so we can use them as timer IDs; see https://blogs.msdn.microsoft.com/oldnewthing/20150924-00/?p=91521 if (SetTimer(utilWindow, (UINT_PTR) timer, milliseconds, NULL) == 0) logLastError(L"error calling SetTimer() in uiTimer()"); + timers[timer] = true; +} + +void uiprivFreeTimer(uiprivTimer *t) +{ + timers.erase(t); + uiprivFree(t); +} + +void uiprivUninitTimers(void) +{ + // TODO why doesn't auto t : timers work? + for (auto t = timers.begin(); t != timers.end(); t++) + uiprivFree(t->first); + timers.clear(); } diff --git a/windows/uipriv_windows.hpp b/windows/uipriv_windows.hpp index a1c2bc29..52d74c51 100644 --- a/windows/uipriv_windows.hpp +++ b/windows/uipriv_windows.hpp @@ -103,6 +103,8 @@ struct uiprivTimer { }; extern int registerMessageFilter(void); extern void unregisterMessageFilter(void); +extern void uiprivFreeTimer(uiprivTimer *t); +extern void uiprivUninitTimers(void); // parent.cpp extern void paintContainerBackground(HWND hwnd, HDC dc, RECT *paintRect); diff --git a/windows/utilwin.cpp b/windows/utilwin.cpp index fc2d15d4..08183753 100644 --- a/windows/utilwin.cpp +++ b/windows/utilwin.cpp @@ -42,7 +42,7 @@ static LRESULT CALLBACK utilWindowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, L if (!(*(timer->f))(timer->data)) { if (KillTimer(utilWindow, (UINT_PTR) timer) == 0) logLastError(L"error calling KillTimer() to end uiTimer() procedure"); - uiprivFree(timer); + uiprivFreeTimer(timer); } return 0; } From 24df7bc3f20f02a8ae6e23ba017b29bf6ac96577 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 9 Aug 2018 04:49:07 -0400 Subject: [PATCH 1270/1329] Cleaned up timers in uiUninit() on GTK+. Update #395. --- unix/main.c | 38 ++++++++++++++++++++++++++------------ windows/main.cpp | 1 + 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/unix/main.c b/unix/main.c index 8ebdf5f5..5d349d32 100644 --- a/unix/main.c +++ b/unix/main.c @@ -3,6 +3,8 @@ uiInitOptions uiprivOptions; +static GHashTable *timers; + const char *uiInit(uiInitOptions *o) { GError *err = NULL; @@ -16,11 +18,21 @@ const char *uiInit(uiInitOptions *o) } uiprivInitAlloc(); uiprivLoadFutures(); + timers = g_hash_table_new(g_direct_hash, g_direct_equal); return NULL; } +struct timer; // TODO get rid of forward declaration + +static void uninitTimer(gpointer key, gpointer value, gpointer data) +{ + uiprivFree((struct timer *) key); +} + void uiUninit(void) { + g_hash_table_foreach(timers, uninitTimer, NULL); + g_hash_table_destroy(timers); uiprivUninitMenus(); uiprivUninitAlloc(); } @@ -108,27 +120,29 @@ void uiQueueMain(void (*f)(void *data), void *data) } struct timer { - int (*f)(void *); - void *data; + int (*f)(void *); + void *data; }; static gboolean doTimer(gpointer data) { - struct timer *t = (struct timer *) data; + struct timer *t = (struct timer *) data; - if (!(*(t->f))(t->data)) { - uiprivFree(t); - return FALSE; - } + if (!(*(t->f))(t->data)) { + g_hash_table_remove(timers, t); + uiprivFree(t); + return FALSE; + } return TRUE; } void uiTimer(int milliseconds, int (*f)(void *data), void *data) { - struct timer *t; + struct timer *t; - t = uiprivNew(struct timer); - t->f = f; - t->data = data; - g_timeout_add(milliseconds, doTimer, t); + t = uiprivNew(struct timer); + t->f = f; + t->data = data; + g_timeout_add(milliseconds, doTimer, t); + g_hash_table_add(timers, t); } diff --git a/windows/main.cpp b/windows/main.cpp index 485ef570..004e784c 100644 --- a/windows/main.cpp +++ b/windows/main.cpp @@ -150,6 +150,7 @@ void uiprivFreeTimer(uiprivTimer *t) uiprivFree(t); } +// since timers use uiprivAlloc(), we have to clean them up in uiUninit(), or else we'll get dangling allocation errors void uiprivUninitTimers(void) { // TODO why doesn't auto t : timers work? From 52bd45bb09f311e6618ed76fba5f2a012a965a2c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 9 Aug 2018 04:54:49 -0400 Subject: [PATCH 1271/1329] More TODOs. Update #395 --- darwin/main.m | 3 +++ 1 file changed, 3 insertions(+) diff --git a/darwin/main.m b/darwin/main.m index f7790b01..0d02d642 100644 --- a/darwin/main.m +++ b/darwin/main.m @@ -283,3 +283,6 @@ void uiTimer(int milliseconds, int (*f)(void *data), void *data) repeats:YES]; [delegate release]; } + +// TODO figure out the best way to clean the above up in uiUninit(), if it's even necessary +// TODO that means figure out if timers can still fire without the main loop From 9120d6b93f9ecd7f5f9128d1c1db0e05dd51814d Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 9 Aug 2018 05:08:38 -0400 Subject: [PATCH 1272/1329] Continuation of previous commit. --- darwin/area.m | 1 - 1 file changed, 1 deletion(-) diff --git a/darwin/area.m b/darwin/area.m index 32ecb045..a184bc4a 100644 --- a/darwin/area.m +++ b/darwin/area.m @@ -312,7 +312,6 @@ mouseEvent(otherMouseUp) - (void)setScrollingSize:(NSSize)s { self->libui_ss = s; - [self invalidateIntrinsicContentSize]; [self setFrameSize:s]; } From 2872e65db03bfaabe835d60b108030dde3ecf231 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Aug 2018 19:45:04 -0400 Subject: [PATCH 1273/1329] Declare Alpha 4. It doesn't have everything I want, but some of what I want will require bigger changes than I feel comfortable making in such a long-awaited release. --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6fd69209..7be647c8 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ It has come to my attention that I have not been particularly clear about how us libui is currently **mid-alpha** software. Much of what is currently present runs stabily enough for the examples and perhaps some small programs to work, but the stability is still a work-in-progress, much of what is already there is not feature-complete, some of it will be buggy on certain platforms, and there's a lot of stuff missing. In short, here's a list of features that I would like to add to libui, but that aren't in yet: -- tables and trees (the former is currently WIP and may land in preliminary form soon) +- trees - clipboard support, including drag and drop - more and better dialogs - printing @@ -18,7 +18,7 @@ libui is currently **mid-alpha** software. Much of what is currently present run - document-based programs - tighter OS integration (especially for document-based programs), to allow programs to fully feel native, rather than merely look and act native - better support for standard dialogs and features (search bars, etc.) -- OpenGL support (this was already being worked on by someone else, but I don't know what happened to them...) +- OpenGL support In addition, [here](https://github.com/andlabs/libui/issues?utf8=%E2%9C%93&q=master+in%3Atitle+is%3Aissue+is%3Aopen) is a list of issues generalizing existing problems. @@ -30,6 +30,9 @@ But libui is not dead; I am working on it whenever I can, and I hope to get it t *Note that today's entry (Eastern Time) may be updated later today.* +* **10 August 2018** + * **Alpha 4 is finally here.** Everything from Alpha 3.5 and what's listed below is in this release; the two biggest changes are still the new text drawing API and new uiTable control. In between all that is a whole bunch of bugfixes, and hopefully more stability too. Thanks to everybody who helped contribute! + * **8 August 2018** * Finally introduced an API for loading images, `uiImage`, and a new control, `uiTable`, for displaying tabular data. These provide enough basic functionality for now, but will be improved over time. You can read the documentation for the new features as they are [here](https://github.com/andlabs/libui/blob/f47e1423cf95ad7b1001663f3381b5a819fc67b9/uitable.h). Thanks to everyone who helped get to this point, in particular @bcampbell for the initial Windows code, and to everyone else for their patience! From 6a513038f43b0b189daf1152fe35010788651e71 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 10 Aug 2018 20:11:15 -0400 Subject: [PATCH 1274/1329] Okay let's try this again, with deployment keys this time. --- .appveyor.yml | 5 ++--- .travis.yml | 4 ++-- README.md | 1 + 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 2da0ed7f..0d579851 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -72,8 +72,7 @@ artifacts: deploy: provider: GitHub artifact: libui, examples - # TODO https://www.appveyor.com/docs/deployment/github/ - #auth_token: - #secure: "2l/602m6FkqAB9TTIAkPX3Erfwg9jVTlf/Inmf2dWcbOrJJzK/WrCUIgY3B4ngly" + auth_token: + secure: li92W7mFAC8HbAVeZN6Ugmo5H1GzKSjr6DXlMniLcCRspKmi2Nz1nlslSa+9sLfo on: appveyor_repo_tag: true # deploy on tag push only diff --git a/.travis.yml b/.travis.yml index 2c02134a..1d969f33 100644 --- a/.travis.yml +++ b/.travis.yml @@ -88,8 +88,8 @@ after_success: deploy: provider: releases - #api_key: - #secure: TODO + api_key: + secure: "fmgC97mlXQY/ASWAL/GyTJfiJIo/hsVFf6bP3Zz8odv259PJUFGgnZ9kNIgJcFQ5961lXDFi7pBMMSetm1GZ2EBZxIXnUfe1kfIhw62ybJHIwB2+g2tc8A4zzfkWJVY4xVYpitOU3iMuu5Z8U2n+68RYWKpcxhbkVw5v8Zu2Rms=" file: build/out/*.tgz file_glob: true skip_cleanup: true diff --git a/README.md b/README.md index 7be647c8..6b4966bc 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ But libui is not dead; I am working on it whenever I can, and I hope to get it t * **10 August 2018** * **Alpha 4 is finally here.** Everything from Alpha 3.5 and what's listed below is in this release; the two biggest changes are still the new text drawing API and new uiTable control. In between all that is a whole bunch of bugfixes, and hopefully more stability too. Thanks to everybody who helped contribute! + * Alpha 4 should hopefully also include automated binary releases via CI. Thanks to those who helped set that up! * **8 August 2018** * Finally introduced an API for loading images, `uiImage`, and a new control, `uiTable`, for displaying tabular data. These provide enough basic functionality for now, but will be improved over time. You can read the documentation for the new features as they are [here](https://github.com/andlabs/libui/blob/f47e1423cf95ad7b1001663f3381b5a819fc67b9/uitable.h). Thanks to everyone who helped get to this point, in particular @bcampbell for the initial Windows code, and to everyone else for their patience! From 76edc665b722c1dc4be8f6690f5985911c002b1e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 11 Aug 2018 19:38:49 -0400 Subject: [PATCH 1275/1329] More TODOs. --- ui.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ui.h b/ui.h index 253cd2f0..6aaa54f2 100644 --- a/ui.h +++ b/ui.h @@ -252,6 +252,9 @@ struct tm; typedef struct uiDateTimePicker uiDateTimePicker; #define uiDateTimePicker(this) ((uiDateTimePicker *) (this)) // TODO document that tm_wday and tm_yday are undefined, and tm_isdst should be -1 +// TODO document that for both sides +// TODO document time zone conversions or lack thereof +// TODO for Time: define what values are returned when a part is missing _UI_EXTERN void uiDateTimePickerTime(uiDateTimePicker *d, struct tm *time); _UI_EXTERN void uiDateTimePickerSetTime(uiDateTimePicker *d, const struct tm *time); _UI_EXTERN void uiDateTimePickerOnChanged(uiDateTimePicker *d, void (*f)(uiDateTimePicker *, void *), void *data); From 1692d5d465f38fde48f866830be19cf43278c82c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 13 Aug 2018 20:11:20 -0400 Subject: [PATCH 1276/1329] Quick documentation fix. --- ui.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui.h b/ui.h index 6aaa54f2..88982be9 100644 --- a/ui.h +++ b/ui.h @@ -563,7 +563,7 @@ _UI_EXTERN uiAttribute *uiNewSizeAttribute(double size); _UI_EXTERN double uiAttributeSize(const uiAttribute *a); // uiTextWeight represents possible text weights. These roughly -// map to the OSx2 text weight field of TrueType and OpenType +// map to the OS/2 text weight field of TrueType and OpenType // fonts, or to CSS weight numbers. The named constants are // nominal values; the actual values may vary by font and by OS, // though this isn't particularly likely. Any value between From c552c4bcae2ce27bf79252660e5b6d4bfad4eaaf Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 14 Aug 2018 21:28:51 -0400 Subject: [PATCH 1277/1329] More typo fixes. --- ui.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui.h b/ui.h index 88982be9..8f6e10c4 100644 --- a/ui.h +++ b/ui.h @@ -567,8 +567,8 @@ _UI_EXTERN double uiAttributeSize(const uiAttribute *a); // fonts, or to CSS weight numbers. The named constants are // nominal values; the actual values may vary by font and by OS, // though this isn't particularly likely. Any value between -// uiTextWeightMinimum and uiDrawTextWeightMaximum, -// inclusive, is allowed. +// uiTextWeightMinimum and uiTextWeightMaximum, inclusive, +// is allowed. // // Note that due to restrictions in early versions of Windows, some // fonts have "special" weights be exposed in many programs as From dcf6a2c178c3f6f9498cd6262a14d3e7ea300a5f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 19 Aug 2018 04:38:14 -0400 Subject: [PATCH 1278/1329] More notes. --- _notes/dialogs | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 _notes/dialogs diff --git a/_notes/dialogs b/_notes/dialogs new file mode 100644 index 00000000..a6ce3397 --- /dev/null +++ b/_notes/dialogs @@ -0,0 +1,2 @@ +https://github.com/kusti8/proton-native/issues/47#issuecomment-373068947 +https://blogs.kde.org/2009/03/26/how-crash-almost-every-qtkde-application-and-how-fix-it-0 From 9f89071db312767c26f9707d06f945b9252733bb Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 19 Aug 2018 21:56:22 -0400 Subject: [PATCH 1279/1329] More notes. --- _notes/windowsUWPGlass | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/_notes/windowsUWPGlass b/_notes/windowsUWPGlass index debe47cd..8293e295 100644 --- a/_notes/windowsUWPGlass +++ b/_notes/windowsUWPGlass @@ -23,3 +23,24 @@ http://www.brandonfa.lk/win8/ https://github.com/gamozolabs/pdblister https://github.com/wbenny/pdbex https://github.com/wbenny/pdbex/releases + +https://stackoverflow.com/questions/44000217/mimicking-acrylic-in-a-win32-app +https://github.com/riverar/sample-win32-acrylicblur +https://github.com/riverar/sample-win10-aeroglass +https://guerra24blog.wordpress.com/2017/12/20/windows-10-use-acrylic-in-win32-apps/ +https://news.ycombinator.com/item?id=14432951 +https://gist.github.com/ethanhs/0e157e4003812e99bf5bc7cb6f73459f ACCENTPOLICY +https://github.com/TranslucentTB/Tools +https://withinrafael.com/2015/07/08/adding-the-aero-glass-blur-to-your-windows-10-apps/ +https://withinrafael.com/2018/02/01/adding-acrylic-blur-to-your-windows-10-apps-redstone-4-desktop-apps/ + +diversion: DwmEnableBlurBehindWindow { +https://docs.microsoft.com/en-us/windows/desktop/api/dwmapi/nf-dwmapi-dwmenableblurbehindwindow +https://docs.microsoft.com/en-us/windows/desktop/api/dwmapi/ns-dwmapi-_dwm_blurbehind +https://docs.microsoft.com/en-us/windows/desktop/api/dwmapi/nf-dwmapi-dwmenableblurbehindwindow +http://www.danielmoth.com/Blog/Vista-Glass-Answers-And-DwmEnableBlurBehindWindow.aspx +http://www.danielmoth.com/Blog/Glass-In-C-An-Alternative-Approach.aspx +http://www.danielmoth.com/Blog/Vista-Glass-In-C.aspx +http://www.danielmoth.com/Blog/Glass-In-C-An-Alternative-Approach.aspx +https://weblogs.asp.net/kennykerr/Windows-Vista-for-Developers-_1320_-Part-3-_1320_-The-Desktop-Window-Manager +} From abeb0144d4f44f45e16bff2e5280c15cf539f321 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 29 Aug 2018 19:58:35 -0400 Subject: [PATCH 1280/1329] Changed the image generator for the tester to print the premultiplied RGBA byte stream instead of uint32_ts; regenerated images.c. --- test/images.c | 808 ++++++++++++++++++++++++++++++++++++--------- test/images/gen.go | 12 +- 2 files changed, 648 insertions(+), 172 deletions(-) diff --git a/test/images.c b/test/images.c index df21b6eb..31107f77 100644 --- a/test/images.c +++ b/test/images.c @@ -1,176 +1,656 @@ // auto-generated by images/gen.go #include "test.h" -static const uint32_t dat0[] = { - 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, 0xFF8AC3FF, 0xFF8AC3FF, 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF89C57C, - 0xFF89C57C, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF89C57C, - 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, +static const uint8_t dat0[] = { + 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, + 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, + 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xE5, 0x60, 0xFC, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xE5, 0x60, 0xFC, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xE5, 0x60, 0xFC, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, + 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xE5, 0x60, 0xFC, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, + 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, + 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, + 0x8A, 0xC3, 0xFF, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0x8A, 0xC3, 0xFF, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0x8A, 0xC3, 0xFF, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0x8A, 0xC3, 0xFF, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, }; -static const uint32_t dat1[] = { - 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, - 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, - 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, - 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, - 0xFFFFFB43, 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFE560FC, - 0xFFE560FC, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFF4079, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, - 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFFFFFB43, 0xFF89C57C, - 0xFF89C57C, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF89C57C, - 0xFF89C57C, 0xFF8AC3FF, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, - 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFFFFFB43, 0xFF8AC3FF, 0xFF89C57C, - 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, - 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, 0xFF89C57C, +static const uint8_t dat1[] = { + 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, + 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, + 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, + 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, + 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xE5, 0x60, 0xFC, 0xFF, + 0xE5, 0x60, 0xFC, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xE5, 0x60, 0xFC, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xE5, 0x60, 0xFC, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xE5, 0x60, 0xFC, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xE5, 0x60, 0xFC, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, + 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, + 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xE5, 0x60, 0xFC, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xE5, 0x60, 0xFC, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0x40, 0x79, 0xFF, + 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, + 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xE5, 0x60, 0xFC, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xE5, 0x60, 0xFC, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xE5, 0x60, 0xFC, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xE5, 0x60, 0xFC, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xE5, 0x60, 0xFC, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xE5, 0x60, 0xFC, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xE5, 0x60, 0xFC, 0xFF, + 0xE5, 0x60, 0xFC, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, + 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, + 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, + 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, + 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0x40, 0x79, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, + 0x8A, 0xC3, 0xFF, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0x8A, 0xC3, 0xFF, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0x8A, 0xC3, 0xFF, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0x8A, 0xC3, 0xFF, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0x8A, 0xC3, 0xFF, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0x8A, 0xC3, 0xFF, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0x8A, 0xC3, 0xFF, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0x8A, 0xC3, 0xFF, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0x8A, 0xC3, 0xFF, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0x8A, 0xC3, 0xFF, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, + 0xFF, 0xFB, 0x43, 0xFF, 0xFF, 0xFB, 0x43, 0xFF, 0x8A, 0xC3, 0xFF, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, + 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, 0x89, 0xC5, 0x7C, 0xFF, }; -static const uint32_t dat2[] = { - 0xAC676767, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0x562B2B2B, 0x00000000, 0x00000000, - 0xFF818181, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, - 0xFF818181, 0xFFFFFFFF, 0xFFB7B7B7, 0xFFB7B7B7, 0xFFB7B7B8, 0xFF9E9E9F, 0xFFB8B8B8, 0xFFB8B8B8, 0xFF9F9F9F, 0xFFB8B8B9, 0xFFB8B8B9, 0xFF9F9F9F, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, - 0xFF818181, 0xFFFFFFFF, 0xFF9E9E9E, 0xFF9F9F9E, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFF9F9F9F, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, - 0xFF818181, 0xFFFFFFFF, 0xFFB7B8B7, 0xFFC3C3C3, 0xFFC3C3C3, 0xFF9F9F9F, 0xFFEEEEEE, 0xFFEEEEEE, 0xFFC2C2C2, 0xFFEEEFEE, 0xFFEFEEEF, 0xFFC2C2C2, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, - 0xFF818181, 0xFFFFFFFF, 0xFFB1B1B1, 0xFFB8B8B8, 0xFFB9B8B9, 0xFF9F9F9F, 0xFFE0E0E0, 0xFFE0E1E0, 0xFFC2C2C2, 0xFFE1E0E0, 0xFFE1E1E1, 0xFFC2C2C2, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, - 0xFF818181, 0xFFFFFFFF, 0xFFB9B8B8, 0xFFC4C4C4, 0xFFC3C4C4, 0xFF9F9F9F, 0xFFEEEEEF, 0xFFEFEEEF, 0xFFC2C2C2, 0xFFEFEFEF, 0xFFF0EFEF, 0xFFC2C2C2, 0xFFFFFFFF, 0xFF818181, 0x00000000, 0x00000000, - 0xFF818181, 0xFFFFFFFF, 0xFFB1B1B1, 0xFFB9B9B8, 0xFFB9B9B9, 0xFF9F9F9F, 0xFFC9A6A5, 0xFFAE3A36, 0xFFA50F0C, 0xFFA40201, 0xFFA40201, 0xFFA40D0A, 0xFFB03B37, 0xFF8D6969, 0x00000000, 0x00000000, - 0xFF818181, 0xFFFFFFFF, 0xFFB9B9B9, 0xFFC4C4C4, 0xFFC4C4C4, 0xFF9E403D, 0xFFA70A07, 0xFFC66453, 0xFFCB715C, 0xFFC9735D, 0xFFC5715B, 0xFFBF6C59, 0xFFC06E61, 0xFFAC201D, 0xC17E2927, 0x19191919, - 0xFF818181, 0xFFFFFFFF, 0xFFB2B2B2, 0xFFB9B9B9, 0xFFA65C5B, 0xFFAF2017, 0xFFCB725D, 0xFFC7654D, 0xFFBB5338, 0xFFB65136, 0xFFB04E35, 0xFFB35D47, 0xFFAE5B45, 0xFFA95743, 0xFFAA2F28, 0xA1641716, - 0xFF818181, 0xFFFFFFFF, 0xFFB9B9B9, 0xFFC4C4C4, 0xFFA51412, 0xFFCA6F5A, 0xFFBF5439, 0xFFBA5237, 0xFFB44F36, 0xFFAF4D34, 0xFF886556, 0xFF765F5A, 0xFF715B56, 0xFF6B5853, 0xFF77605B, 0xF7980C0B, - 0xFF818181, 0xFFFFFFFF, 0xFFB2B2B2, 0xFFB9B9B9, 0xFFA40201, 0xFFC76951, 0xFFB85137, 0xFFB24E36, 0xFFAD4C34, 0xFFAE653A, 0xFF95995D, 0xFF1D84A0, 0xFF177F99, 0xFF3D91A5, 0xFF3B8EA0, 0xFFA20101, - 0xFF818181, 0xFFFFFFFF, 0xFFB9B9B9, 0xFFC4C4C4, 0xFF9F1212, 0xFFCA7E6D, 0xFFB85F48, 0xFFAB4C33, 0xFFA64932, 0xFFB18B44, 0xFFB8BC50, 0xFF358086, 0xFF509CAD, 0xFF278397, 0xFF496E77, 0xF7920808, - 0xFF818181, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFBC7878, 0xFF9C1D1A, 0xFFBD7E6D, 0xFFBE7F70, 0xFFBD8374, 0xFFCDC787, 0xFFCBD089, 0xFFB4B48A, 0xFF5C93A0, 0xFF44656D, 0xFF7B1618, 0xA1661D1D, - 0xB4696969, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF818181, 0xFF934040, 0xFF9E0605, 0xFF99463D, 0xFF9E624D, 0xFFA6A14C, 0xFF9F9B4A, 0xFF948845, 0xFF4B3C43, 0xFF9A0505, 0xBA771F1F, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x55351616, 0xD37F1717, 0xF99C0B0B, 0xFFA30201, 0xFFA20101, 0xF99C0B0B, 0xD3881E1E, 0x55412222, 0x00000000, 0x00000000, +static const uint8_t dat2[] = { + 0x67, 0x67, 0x67, 0xAC, 0x81, 0x81, 0x81, 0xFF, 0x81, 0x81, 0x81, 0xFF, 0x81, 0x81, 0x81, 0xFF, + 0x81, 0x81, 0x81, 0xFF, 0x81, 0x81, 0x81, 0xFF, 0x81, 0x81, 0x81, 0xFF, 0x81, 0x81, 0x81, 0xFF, + 0x81, 0x81, 0x81, 0xFF, 0x81, 0x81, 0x81, 0xFF, 0x81, 0x81, 0x81, 0xFF, 0x81, 0x81, 0x81, 0xFF, + 0x81, 0x81, 0x81, 0xFF, 0x2B, 0x2B, 0x2B, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x81, 0x81, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0x81, 0x81, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x81, 0x81, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xB7, 0xB7, 0xB7, 0xFF, 0xB7, 0xB7, 0xB7, 0xFF, + 0xB7, 0xB7, 0xB8, 0xFF, 0x9E, 0x9E, 0x9F, 0xFF, 0xB8, 0xB8, 0xB8, 0xFF, 0xB8, 0xB8, 0xB8, 0xFF, + 0x9F, 0x9F, 0x9F, 0xFF, 0xB8, 0xB8, 0xB9, 0xFF, 0xB8, 0xB8, 0xB9, 0xFF, 0x9F, 0x9F, 0x9F, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0x81, 0x81, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x81, 0x81, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x9E, 0x9E, 0x9E, 0xFF, 0x9F, 0x9F, 0x9E, 0xFF, + 0x9F, 0x9F, 0x9F, 0xFF, 0x9F, 0x9F, 0x9F, 0xFF, 0x9F, 0x9F, 0x9F, 0xFF, 0x9F, 0x9F, 0x9F, 0xFF, + 0x9F, 0x9F, 0x9F, 0xFF, 0x9F, 0x9F, 0x9F, 0xFF, 0x9F, 0x9F, 0x9F, 0xFF, 0x9F, 0x9F, 0x9F, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0x81, 0x81, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x81, 0x81, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xB7, 0xB8, 0xB7, 0xFF, 0xC3, 0xC3, 0xC3, 0xFF, + 0xC3, 0xC3, 0xC3, 0xFF, 0x9F, 0x9F, 0x9F, 0xFF, 0xEE, 0xEE, 0xEE, 0xFF, 0xEE, 0xEE, 0xEE, 0xFF, + 0xC2, 0xC2, 0xC2, 0xFF, 0xEE, 0xEF, 0xEE, 0xFF, 0xEF, 0xEE, 0xEF, 0xFF, 0xC2, 0xC2, 0xC2, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0x81, 0x81, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x81, 0x81, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xB1, 0xB1, 0xB1, 0xFF, 0xB8, 0xB8, 0xB8, 0xFF, + 0xB9, 0xB8, 0xB9, 0xFF, 0x9F, 0x9F, 0x9F, 0xFF, 0xE0, 0xE0, 0xE0, 0xFF, 0xE0, 0xE1, 0xE0, 0xFF, + 0xC2, 0xC2, 0xC2, 0xFF, 0xE1, 0xE0, 0xE0, 0xFF, 0xE1, 0xE1, 0xE1, 0xFF, 0xC2, 0xC2, 0xC2, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0x81, 0x81, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x81, 0x81, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xB9, 0xB8, 0xB8, 0xFF, 0xC4, 0xC4, 0xC4, 0xFF, + 0xC3, 0xC4, 0xC4, 0xFF, 0x9F, 0x9F, 0x9F, 0xFF, 0xEE, 0xEE, 0xEF, 0xFF, 0xEF, 0xEE, 0xEF, 0xFF, + 0xC2, 0xC2, 0xC2, 0xFF, 0xEF, 0xEF, 0xEF, 0xFF, 0xF0, 0xEF, 0xEF, 0xFF, 0xC2, 0xC2, 0xC2, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x81, 0x81, 0x81, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x81, 0x81, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xB1, 0xB1, 0xB1, 0xFF, 0xB9, 0xB9, 0xB8, 0xFF, + 0xB9, 0xB9, 0xB9, 0xFF, 0x9F, 0x9F, 0x9F, 0xFF, 0xC9, 0xA6, 0xA5, 0xFF, 0xAE, 0x3A, 0x36, 0xFF, + 0xA5, 0x0F, 0x0C, 0xFF, 0xA4, 0x02, 0x01, 0xFF, 0xA4, 0x02, 0x01, 0xFF, 0xA4, 0x0D, 0x0A, 0xFF, + 0xB0, 0x3B, 0x37, 0xFF, 0x8D, 0x69, 0x69, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x81, 0x81, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xB9, 0xB9, 0xB9, 0xFF, 0xC4, 0xC4, 0xC4, 0xFF, + 0xC4, 0xC4, 0xC4, 0xFF, 0x9E, 0x40, 0x3D, 0xFF, 0xA7, 0x0A, 0x07, 0xFF, 0xC6, 0x64, 0x53, 0xFF, + 0xCB, 0x71, 0x5C, 0xFF, 0xC9, 0x73, 0x5D, 0xFF, 0xC5, 0x71, 0x5B, 0xFF, 0xBF, 0x6C, 0x59, 0xFF, + 0xC0, 0x6E, 0x61, 0xFF, 0xAC, 0x20, 0x1D, 0xFF, 0x7E, 0x29, 0x27, 0xC1, 0x19, 0x19, 0x19, 0x19, + 0x81, 0x81, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xB2, 0xB2, 0xB2, 0xFF, 0xB9, 0xB9, 0xB9, 0xFF, + 0xA6, 0x5C, 0x5B, 0xFF, 0xAF, 0x20, 0x17, 0xFF, 0xCB, 0x72, 0x5D, 0xFF, 0xC7, 0x65, 0x4D, 0xFF, + 0xBB, 0x53, 0x38, 0xFF, 0xB6, 0x51, 0x36, 0xFF, 0xB0, 0x4E, 0x35, 0xFF, 0xB3, 0x5D, 0x47, 0xFF, + 0xAE, 0x5B, 0x45, 0xFF, 0xA9, 0x57, 0x43, 0xFF, 0xAA, 0x2F, 0x28, 0xFF, 0x64, 0x17, 0x16, 0xA1, + 0x81, 0x81, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xB9, 0xB9, 0xB9, 0xFF, 0xC4, 0xC4, 0xC4, 0xFF, + 0xA5, 0x14, 0x12, 0xFF, 0xCA, 0x6F, 0x5A, 0xFF, 0xBF, 0x54, 0x39, 0xFF, 0xBA, 0x52, 0x37, 0xFF, + 0xB4, 0x4F, 0x36, 0xFF, 0xAF, 0x4D, 0x34, 0xFF, 0x88, 0x65, 0x56, 0xFF, 0x76, 0x5F, 0x5A, 0xFF, + 0x71, 0x5B, 0x56, 0xFF, 0x6B, 0x58, 0x53, 0xFF, 0x77, 0x60, 0x5B, 0xFF, 0x98, 0x0C, 0x0B, 0xF7, + 0x81, 0x81, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xB2, 0xB2, 0xB2, 0xFF, 0xB9, 0xB9, 0xB9, 0xFF, + 0xA4, 0x02, 0x01, 0xFF, 0xC7, 0x69, 0x51, 0xFF, 0xB8, 0x51, 0x37, 0xFF, 0xB2, 0x4E, 0x36, 0xFF, + 0xAD, 0x4C, 0x34, 0xFF, 0xAE, 0x65, 0x3A, 0xFF, 0x95, 0x99, 0x5D, 0xFF, 0x1D, 0x84, 0xA0, 0xFF, + 0x17, 0x7F, 0x99, 0xFF, 0x3D, 0x91, 0xA5, 0xFF, 0x3B, 0x8E, 0xA0, 0xFF, 0xA2, 0x01, 0x01, 0xFF, + 0x81, 0x81, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xB9, 0xB9, 0xB9, 0xFF, 0xC4, 0xC4, 0xC4, 0xFF, + 0x9F, 0x12, 0x12, 0xFF, 0xCA, 0x7E, 0x6D, 0xFF, 0xB8, 0x5F, 0x48, 0xFF, 0xAB, 0x4C, 0x33, 0xFF, + 0xA6, 0x49, 0x32, 0xFF, 0xB1, 0x8B, 0x44, 0xFF, 0xB8, 0xBC, 0x50, 0xFF, 0x35, 0x80, 0x86, 0xFF, + 0x50, 0x9C, 0xAD, 0xFF, 0x27, 0x83, 0x97, 0xFF, 0x49, 0x6E, 0x77, 0xFF, 0x92, 0x08, 0x08, 0xF7, + 0x81, 0x81, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xBC, 0x78, 0x78, 0xFF, 0x9C, 0x1D, 0x1A, 0xFF, 0xBD, 0x7E, 0x6D, 0xFF, 0xBE, 0x7F, 0x70, 0xFF, + 0xBD, 0x83, 0x74, 0xFF, 0xCD, 0xC7, 0x87, 0xFF, 0xCB, 0xD0, 0x89, 0xFF, 0xB4, 0xB4, 0x8A, 0xFF, + 0x5C, 0x93, 0xA0, 0xFF, 0x44, 0x65, 0x6D, 0xFF, 0x7B, 0x16, 0x18, 0xFF, 0x66, 0x1D, 0x1D, 0xA1, + 0x69, 0x69, 0x69, 0xB4, 0x81, 0x81, 0x81, 0xFF, 0x81, 0x81, 0x81, 0xFF, 0x81, 0x81, 0x81, 0xFF, + 0x81, 0x81, 0x81, 0xFF, 0x93, 0x40, 0x40, 0xFF, 0x9E, 0x06, 0x05, 0xFF, 0x99, 0x46, 0x3D, 0xFF, + 0x9E, 0x62, 0x4D, 0xFF, 0xA6, 0xA1, 0x4C, 0xFF, 0x9F, 0x9B, 0x4A, 0xFF, 0x94, 0x88, 0x45, 0xFF, + 0x4B, 0x3C, 0x43, 0xFF, 0x9A, 0x05, 0x05, 0xFF, 0x77, 0x1F, 0x1F, 0xBA, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x16, 0x16, 0x55, 0x7F, 0x17, 0x17, 0xD3, + 0x9C, 0x0B, 0x0B, 0xF9, 0xA3, 0x02, 0x01, 0xFF, 0xA2, 0x01, 0x01, 0xFF, 0x9C, 0x0B, 0x0B, 0xF9, + 0x88, 0x1E, 0x1E, 0xD3, 0x41, 0x22, 0x22, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; -static const uint32_t dat3[] = { - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x52303030, 0xF2919191, 0xFF999999, 0xFF9C9C9C, 0xFF9E9E9E, 0xFF9C9C9C, 0xFF999999, 0xFF969696, 0xFF939393, 0xFF8F8F8F, 0xFF8C8C8C, 0xFF888888, - 0xFF848484, 0xFF818181, 0xFF7D7D7D, 0xFF7A7A7A, 0xFF767676, 0xFF727272, 0xFF6F6F6F, 0xFF6B6B6B, 0xFF686868, 0xFF646464, 0xF25F5F5F, 0x521E1E1E, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xEF8D8D8D, 0xFFFDFDFD, 0xFFFDFDFD, 0xFFFEFEFE, 0xFFFEFEFE, 0xFFFEFEFE, 0xFFFDFDFD, 0xFFFDFDFD, 0xFFFDFDFD, 0xFFFDFDFD, 0xFFFCFCFC, 0xFFFCFCFC, - 0xFFFCFCFC, 0xFFFBFBFB, 0xFFFBFBFB, 0xFFFBFBFB, 0xFFFAFAFA, 0xFFFAFAFA, 0xFFFAFAFA, 0xFFF9F9F9, 0xFFF9F9F9, 0xFFF9F9F9, 0xFFF8F8F8, 0xEF5D5D5D, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF939393, 0xFFFDFDFD, 0xFFDCDCDC, 0xFFDDDDDD, 0xFFDEDEDE, 0xFFDEDEDE, 0xFFDFDFDF, 0xFFDFDFDF, 0xFFE0E0E0, 0xFFE1E1E1, 0xFFE1E1E1, 0xFFE1E1E1, - 0xFFE2E2E2, 0xFFE2E2E2, 0xFFE2E2E2, 0xFFE2E2E2, 0xFFE3E3E3, 0xFFE3E3E3, 0xFFE3E3E3, 0xFFE3E3E3, 0xFFE3E3E3, 0xFFE2E2E2, 0xFFF8F8F8, 0xFF5D5D5D, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF929292, 0xFFFEFEFE, 0xFFDDDDDD, 0xFF787878, 0xFF8D8D8D, 0xFF8E8E8E, 0xFF8F8F8F, 0xFF8F8F8F, 0xFF787878, 0xFF8F8F8F, 0xFF909090, 0xFF909090, - 0xFF7A7A7A, 0xFF919191, 0xFF919191, 0xFF919191, 0xFF7B7B7B, 0xFF919191, 0xFF919191, 0xFF919191, 0xFF7D7D7D, 0xFFE3E3E3, 0xFFF8F8F8, 0xFF5D5D5D, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF909090, 0xFFFEFEFE, 0xFFDEDEDE, 0xFF949494, 0xFFB0B0B0, 0xFFB1B1B1, 0xFFB1B1B1, 0xFFB1B1B1, 0xFF969696, 0xFFB2B2B2, 0xFFB3B3B3, 0xFFB3B3B3, - 0xFF989898, 0xFFB4B4B4, 0xFFB4B4B4, 0xFFB5B5B5, 0xFF999999, 0xFFB5B5B5, 0xFFB5B5B5, 0xFFB5B5B5, 0xFF999999, 0xFFE4E4E4, 0xFFF8F8F8, 0xFF5C5C5C, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF8E8E8E, 0xFFFDFDFD, 0xFFDFDFDF, 0xFF949494, 0xFFB1B1B1, 0xFFB1B1B1, 0xFFB2B2B2, 0xFFB2B2B2, 0xFF979797, 0xFFB3B3B3, 0xFFB4B4B4, 0xFFB4B4B4, - 0xFF999999, 0xFFB5B5B5, 0xFFB5B5B5, 0xFFB5B5B5, 0xFF999999, 0xFFB5B5B5, 0xFFB5B5B5, 0xFFB5B5B5, 0xFF999999, 0xFFE5E5E5, 0xFFF8F8F8, 0xFF5C5C5C, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF8B8B8B, 0xFFFDFDFD, 0xFFDFDFDF, 0xFF7A7A7A, 0xFF919191, 0xFF929292, 0xFF929292, 0xFF939393, 0xFF7D7D7D, 0xFF949494, 0xFF949494, 0xFF949494, - 0xFF7D7D7D, 0xFF949494, 0xFF949494, 0xFF959595, 0xFF7E7E7E, 0xFF959595, 0xFF959595, 0xFF959595, 0xFF7E7E7E, 0xFFE6E6E6, 0xFFF8F8F8, 0xFF5B5B5B, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF898989, 0xFFFDFDFD, 0xFFE0E0E0, 0xFF949494, 0xFFB0B0B0, 0xFFB0B0B0, 0xFFB1B1B1, 0xFFB2B2B2, 0xFF979797, 0xFFE2E2E2, 0xFFE3E3E3, 0xFFE3E3E3, - 0xFFC0C0C0, 0xFFE4E4E4, 0xFFE4E4E4, 0xFFE5E5E5, 0xFFC1C1C1, 0xFFE5E5E5, 0xFFE5E5E5, 0xFFE5E5E5, 0xFFC1C1C1, 0xFFE8E8E8, 0xFFF8F8F8, 0xFF5A5A5A, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF868686, 0xFFFDFDFD, 0xFFE1E1E1, 0xFF7B7B7B, 0xFF929292, 0xFF939393, 0xFF949494, 0xFF949494, 0xFF7D7D7D, 0xFFBDBDBD, 0xFFBDBDBD, 0xFFBDBDBD, - 0xFFA0A0A0, 0xFFBEBEBE, 0xFFBEBEBE, 0xFFBFBFBF, 0xFFA1A1A1, 0xFFBFBFBF, 0xFFBFBFBF, 0xFFBFBFBF, 0xFFA1A1A1, 0xFFE9E9E9, 0xFFF8F8F8, 0xFF595959, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF838383, 0xFFFDFDFD, 0xFFE1E1E1, 0xFF949494, 0xFFB1B1B1, 0xFFB2B2B2, 0xFFB3B3B3, 0xFFB3B3B3, 0xFF979797, 0xFFE4E4E4, 0xFFE5E5E5, 0xFFE5E5E5, - 0xFFC2C2C2, 0xFFE6E6E6, 0xFFE6E6E6, 0xFFE7E7E7, 0xFFC3C3C3, 0xFFE7E7E7, 0xFFE7E7E7, 0xFFE7E7E7, 0xFFC3C3C3, 0xFFEAEAEA, 0xFFF8F8F8, 0xFF585858, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF808080, 0xFFFCFCFC, 0xFFE2E2E2, 0xFF7C7C7C, 0xFF949494, 0xFF949494, 0xFF949494, 0xFF949494, 0xFF7E7E7E, 0xFFBEBEBE, 0xFFBEBEBE, 0xFFBFBFBF, - 0xFFA2A2A2, 0xFFC0C0C0, 0xFFC0C0C0, 0xFFC1C1C1, 0xFFA3A3A3, 0xFFC1C1C1, 0xFFC1C1C1, 0xFFC1C1C1, 0xFFA3A3A3, 0xFFEBEBEB, 0xFFF8F8F8, 0xFF575757, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF7D7D7D, 0xFFFCFCFC, 0xFFE3E3E3, 0xFF969696, 0xFFB3B3B3, 0xFFB3B3B3, 0xFFB3B3B3, 0xFFB4B4B4, 0xFF999999, 0xFFE6E6E6, 0xFFE6E6E6, 0xFFE7E7E7, - 0xFFC4C4C4, 0xFFE8E8E8, 0xFFE8E8E8, 0xFFE9E9E9, 0xFFC4C4C4, 0xFFE9E9E9, 0xFFE9E9E9, 0xFFE9E9E9, 0xFFC4C4C4, 0xFFECECEC, 0xFFF8F8F8, 0xFF555555, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF7A7A7A, 0xFFFCFCFC, 0xFFE3E3E3, 0xFF7D7D7D, 0xFF949494, 0xFF949494, 0xFF959595, 0xFF969696, 0xFF7F7F7F, 0xFFBFBFBF, 0xFFC0C0C0, 0xFFC1C1C1, - 0xFFA3A3A3, 0xFFC1C1C1, 0xFFC1C1C1, 0xFFC2C2C2, 0xFFA4A4A4, 0xFFC2C2C2, 0xFFC2C2C2, 0xFFC2C2C2, 0xFFA4A4A4, 0xFFEDEDED, 0xFFF8F8F8, 0xFF545454, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF777777, 0xFFFBFBFB, 0xFFE4E4E4, 0xFF979797, 0xFFB3B3B3, 0xFFB4B4B4, 0xFFB5B5B5, 0xFFB6B6B6, 0xFF999999, 0xFFE7E7E7, 0xFFE8E8E8, 0xFFE9E9E9, - 0xFFC4C4C4, 0xFFEAEAEA, 0xFFEAEAEA, 0xFFEBEBEB, 0xFFC6C6C6, 0xFFEBEBEB, 0xFFEBEBEB, 0xFFEBEBEB, 0xFFC6C6C6, 0xFFEEEEEE, 0xFFF8F8F8, 0xFF525252, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF737373, 0xFFFBFBFB, 0xFFE4E4E4, 0xFF7D7D7D, 0xFF949494, 0xFF959595, 0xFF969696, 0xFF979797, 0xFF7F7F7F, 0xFFC1C1C1, 0xFFC1C1C1, 0xFFC2C2C2, - 0xFFA3A2A2, 0xFFAB9191, 0xFF946060, 0xFF843D3D, 0xFF782525, 0xFF802727, 0xFF7E2B2B, 0xFF843D3D, 0xFF865252, 0xFFCCB2B2, 0xFFF6F5F5, 0xFF505050, 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF707070, 0xFFFBFBFB, 0xFFE5E5E5, 0xFF979797, 0xFFB4B4B4, 0xFFB5B5B5, 0xFFB6B6B6, 0xFFB6B6B6, 0xFF9A9A9A, 0xFFE9E9E9, 0xFFE6E4E4, 0xFFB18585, - 0xFF7A1B1B, 0xFFA62F2F, 0xFFD35050, 0xFFF26767, 0xFFFF7070, 0xFFFE6E6E, 0xFFFC6969, 0xFFED5B5B, 0xFFCD4343, 0xFFA22525, 0xFF7E1D1D, 0xFF582C2C, 0x06020000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF6D6D6D, 0xFFFBFBFB, 0xFFE5E5E5, 0xFF7D7D7D, 0xFF959595, 0xFF969696, 0xFF979797, 0xFF979797, 0xFF808080, 0xFFB7ABAB, 0xFF7B2828, 0xFFAC3434, - 0xFFF56A6A, 0xFFFF7070, 0xFFFF7070, 0xFFFE6F6F, 0xFFFC6A6A, 0xFFFA6565, 0xFFF86060, 0xFFF55C5C, 0xFFF35757, 0xFFF15252, 0xFFE64848, 0xFFA11F1F, 0xCB530000, 0x1D0C0000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF6A6A6A, 0xFFFAFAFA, 0xFFE6E6E6, 0xFF989898, 0xFFB5B5B5, 0xFFB6B6B6, 0xFFB6B6B6, 0xFFB7B7B7, 0xFF989292, 0xFF7E2626, 0xFFCB5050, 0xFFFF7474, - 0xFFFF7070, 0xFFFF7070, 0xFFFD6B6B, 0xFFFA6767, 0xFFF86262, 0xFFF65D5D, 0xFFF45858, 0xFFF15353, 0xFFEF4E4E, 0xFFED4949, 0xFFEB4444, 0xFFE93F3F, 0xFFB62424, 0xD7570000, 0x0F060000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF666666, 0xFFFAFAFA, 0xFFE6E6E6, 0xFF7E7E7E, 0xFF969696, 0xFF979797, 0xFF979797, 0xFF989898, 0xFF733D3D, 0xFFA63737, 0xFFFF7979, 0xFFFF7272, - 0xFFFD6D6D, 0xFFFB6868, 0xFFF96363, 0xFFF65E5E, 0xFFF45959, 0xFFF25454, 0xFFF04F4F, 0xFFEE4A4A, 0xFFEB4545, 0xFFE94141, 0xFFE73C3C, 0xFFE53737, 0xFFE23535, 0xFF911313, 0x86360000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF636363, 0xFFFAFAFA, 0xFFE6E6E6, 0xFF989898, 0xFFB5B5B5, 0xFFB6B6B6, 0xFFB7B7B7, 0xFFB8B8B8, 0xFF6E1515, 0xFFDF6D6D, 0xFFFE7575, 0xFFFB6969, - 0xFFF96464, 0xFFF75F5F, 0xFFF55A5A, 0xFFF35555, 0xFFF05050, 0xFFEE4C4C, 0xFFEC4747, 0xFFEA4242, 0xFFE73D3D, 0xFFE53838, 0xFFE33333, 0xFFE12E2E, 0xFFDF2D2D, 0xFFC22828, 0xDE590000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF606060, 0xFFF9F9F9, 0xFFE6E6E6, 0xFF7E7E7E, 0xFF979797, 0xFF979797, 0xFF989898, 0xFF989898, 0xFF670101, 0xFFED7B7B, 0xFFFA6A6A, 0xFFF86060, - 0xFFF55B5B, 0xFFF35656, 0xFFF15252, 0xFFEF4D4D, 0xFFEC4848, 0xFFCD8478, 0xFF777CAE, 0xFF777BAD, 0xFF7275A7, 0xFF6B6FA1, 0xFF67699B, 0xFF606396, 0xFF5E6091, 0xFF7E5E82, 0xFC660000, 0x02000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF5D5D5D, 0xFFF9F9F9, 0xFFE6E6E6, 0xFF999999, 0xFFB6B6B6, 0xFFB6B6B6, 0xFFB7B7B7, 0xFFB8B8B8, 0xFF670000, 0xFFD86262, 0xFFF76D6D, 0xFFF45858, - 0xFFF15353, 0xFFEF4E4E, 0xFFED4949, 0xFFEB4444, 0xFFE84341, 0xFFC5E18C, 0xFF8A939F, 0xFF5889C7, 0xFF5182C1, 0xFF4B7CBA, 0xFF4475B4, 0xFF3D6EAE, 0xFF4572AD, 0xFF6A6087, 0xFF670000, 0x0E000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF595959, 0xFFF9F9F9, 0xFFE7E7E7, 0xFF7E7E7E, 0xFF979797, 0xFF979797, 0xFF989898, 0xFF989898, 0xFF690D0D, 0xFFA32424, 0xFFEF7B7B, 0xFFF15A5A, - 0xFFEE4A4A, 0xFFEB4545, 0xFFE94040, 0xFFE73C3C, 0xFFD86B4D, 0xFFADE773, 0xFFABC35F, 0xFF5585C0, 0xFF4D7EBD, 0xFF4778B7, 0xFF4071B0, 0xFF4372AE, 0xFF6B7AA6, 0xFF544267, 0xE85C0000, 0x16000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF565656, 0xFFF8F8F8, 0xFFE7E7E7, 0xFF999999, 0xFFB6B6B6, 0xFFB6B6B6, 0xFFB7B7B7, 0xFFB8B8B8, 0xFF773939, 0xFF7A0909, 0xFFBB3232, 0xFFE87171, - 0xFFED6161, 0xFFE84242, 0xFFE53838, 0xFFE33333, 0xFFBA9F4E, 0xFF99E053, 0xFF8FDC43, 0xFF848A66, 0xFF497AB9, 0xFF4777B4, 0xFF5882B9, 0xFF797FA6, 0xFF455E90, 0xFF651723, 0xAC3E0000, 0x13000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF535353, 0xFFF8F8F8, 0xFFE6E6E6, 0xFF7E7E7E, 0xFF979797, 0xFF979797, 0xFF989898, 0xFF989898, 0xFF766B6B, 0xFF6D0E0E, 0xFF881111, 0xFFB02323, - 0xFFCA4545, 0xFFE26666, 0xFFE85D5D, 0xFFE24F4B, 0xFFA1D656, 0xFF91DC47, 0xFF8BD93C, 0xFF90C93A, 0xFF7494BB, 0xFF848EB4, 0xFF70688E, 0xFF3B5E92, 0xFF602942, 0xEF5F0000, 0x400B0000, 0x08000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFF4F4F4F, 0xFFF8F8F8, 0xFFE6E6E6, 0xFFE5E5E5, 0xFFE6E6E6, 0xFFE7E7E7, 0xFFE8E8E8, 0xFFE9E9E9, 0xFFE6E6E6, 0xFFBDA9A9, 0xFF711515, 0xFF7B0C0C, - 0xFFA21D1D, 0xFFAD1B1B, 0xFFB62727, 0xFFB24F33, 0xFF93A640, 0xFF96A840, 0xFF91A73C, 0xFF869F33, 0xFF6E675B, 0xFF375C93, 0xFF544D75, 0xFF6B1823, 0xEB5C0000, 0x59130000, 0x16000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x0D000000, 0xF14C4C4C, 0xFFF7F7F7, 0xFFF6F6F6, 0xFFF6F6F6, 0xFFF6F6F6, 0xFFF7F7F7, 0xFFF7F7F7, 0xFFF7F7F7, 0xFFF7F7F7, 0xFFF0F0F0, 0xFFD1C9C9, 0xFF905353, - 0xFF6A0505, 0xFF770A0A, 0xFF8E1414, 0xFF91331C, 0xFF728723, 0xFF709024, 0xFF6E8F23, 0xFF737C1F, 0xFF703C31, 0xFF6C141B, 0xFF6A0505, 0xFB531616, 0x52080000, 0x1D000000, 0x01000000, 0x00000000, - 0x00000000, 0x00000000, 0x0D000000, 0x22000000, 0x78191919, 0xF6535353, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF565656, 0xFF555555, 0xFF4F4F4F, - 0xFF4B4343, 0xFF522828, 0xFF5A1616, 0xFF610A0A, 0xFF650404, 0xFF670000, 0xFF650404, 0xFF600909, 0xFF591515, 0xFF502626, 0xF9453C3C, 0x93151515, 0x3A000000, 0x1B000000, 0x02000000, 0x00000000, - 0x00000000, 0x00000000, 0x06000000, 0x1D000000, 0x30000000, 0x40000000, 0x47000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, 0x4A000000, - 0x4C000000, 0x54000000, 0x5E000000, 0x65000000, 0x69000000, 0x6B000000, 0x6B000000, 0x69000000, 0x63000000, 0x52000000, 0x4B000000, 0x3B000000, 0x29000000, 0x0C000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x07000000, 0x0F000000, 0x15000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x16000000, - 0x16000000, 0x16000000, 0x16000000, 0x16000000, 0x17000000, 0x18000000, 0x17000000, 0x17000000, 0x16000000, 0x14000000, 0x0F000000, 0x08000000, 0x01000000, 0x00000000, 0x00000000, 0x00000000, +static const uint8_t dat3[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x30, 0x30, 0x30, 0x52, 0x91, 0x91, 0x91, 0xF2, 0x99, 0x99, 0x99, 0xFF, 0x9C, 0x9C, 0x9C, 0xFF, + 0x9E, 0x9E, 0x9E, 0xFF, 0x9C, 0x9C, 0x9C, 0xFF, 0x99, 0x99, 0x99, 0xFF, 0x96, 0x96, 0x96, 0xFF, + 0x93, 0x93, 0x93, 0xFF, 0x8F, 0x8F, 0x8F, 0xFF, 0x8C, 0x8C, 0x8C, 0xFF, 0x88, 0x88, 0x88, 0xFF, + 0x84, 0x84, 0x84, 0xFF, 0x81, 0x81, 0x81, 0xFF, 0x7D, 0x7D, 0x7D, 0xFF, 0x7A, 0x7A, 0x7A, 0xFF, + 0x76, 0x76, 0x76, 0xFF, 0x72, 0x72, 0x72, 0xFF, 0x6F, 0x6F, 0x6F, 0xFF, 0x6B, 0x6B, 0x6B, 0xFF, + 0x68, 0x68, 0x68, 0xFF, 0x64, 0x64, 0x64, 0xFF, 0x5F, 0x5F, 0x5F, 0xF2, 0x1E, 0x1E, 0x1E, 0x52, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x8D, 0x8D, 0x8D, 0xEF, 0xFD, 0xFD, 0xFD, 0xFF, 0xFD, 0xFD, 0xFD, 0xFF, 0xFE, 0xFE, 0xFE, 0xFF, + 0xFE, 0xFE, 0xFE, 0xFF, 0xFE, 0xFE, 0xFE, 0xFF, 0xFD, 0xFD, 0xFD, 0xFF, 0xFD, 0xFD, 0xFD, 0xFF, + 0xFD, 0xFD, 0xFD, 0xFF, 0xFD, 0xFD, 0xFD, 0xFF, 0xFC, 0xFC, 0xFC, 0xFF, 0xFC, 0xFC, 0xFC, 0xFF, + 0xFC, 0xFC, 0xFC, 0xFF, 0xFB, 0xFB, 0xFB, 0xFF, 0xFB, 0xFB, 0xFB, 0xFF, 0xFB, 0xFB, 0xFB, 0xFF, + 0xFA, 0xFA, 0xFA, 0xFF, 0xFA, 0xFA, 0xFA, 0xFF, 0xFA, 0xFA, 0xFA, 0xFF, 0xF9, 0xF9, 0xF9, 0xFF, + 0xF9, 0xF9, 0xF9, 0xFF, 0xF9, 0xF9, 0xF9, 0xFF, 0xF8, 0xF8, 0xF8, 0xFF, 0x5D, 0x5D, 0x5D, 0xEF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x93, 0x93, 0x93, 0xFF, 0xFD, 0xFD, 0xFD, 0xFF, 0xDC, 0xDC, 0xDC, 0xFF, 0xDD, 0xDD, 0xDD, 0xFF, + 0xDE, 0xDE, 0xDE, 0xFF, 0xDE, 0xDE, 0xDE, 0xFF, 0xDF, 0xDF, 0xDF, 0xFF, 0xDF, 0xDF, 0xDF, 0xFF, + 0xE0, 0xE0, 0xE0, 0xFF, 0xE1, 0xE1, 0xE1, 0xFF, 0xE1, 0xE1, 0xE1, 0xFF, 0xE1, 0xE1, 0xE1, 0xFF, + 0xE2, 0xE2, 0xE2, 0xFF, 0xE2, 0xE2, 0xE2, 0xFF, 0xE2, 0xE2, 0xE2, 0xFF, 0xE2, 0xE2, 0xE2, 0xFF, + 0xE3, 0xE3, 0xE3, 0xFF, 0xE3, 0xE3, 0xE3, 0xFF, 0xE3, 0xE3, 0xE3, 0xFF, 0xE3, 0xE3, 0xE3, 0xFF, + 0xE3, 0xE3, 0xE3, 0xFF, 0xE2, 0xE2, 0xE2, 0xFF, 0xF8, 0xF8, 0xF8, 0xFF, 0x5D, 0x5D, 0x5D, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x92, 0x92, 0x92, 0xFF, 0xFE, 0xFE, 0xFE, 0xFF, 0xDD, 0xDD, 0xDD, 0xFF, 0x78, 0x78, 0x78, 0xFF, + 0x8D, 0x8D, 0x8D, 0xFF, 0x8E, 0x8E, 0x8E, 0xFF, 0x8F, 0x8F, 0x8F, 0xFF, 0x8F, 0x8F, 0x8F, 0xFF, + 0x78, 0x78, 0x78, 0xFF, 0x8F, 0x8F, 0x8F, 0xFF, 0x90, 0x90, 0x90, 0xFF, 0x90, 0x90, 0x90, 0xFF, + 0x7A, 0x7A, 0x7A, 0xFF, 0x91, 0x91, 0x91, 0xFF, 0x91, 0x91, 0x91, 0xFF, 0x91, 0x91, 0x91, 0xFF, + 0x7B, 0x7B, 0x7B, 0xFF, 0x91, 0x91, 0x91, 0xFF, 0x91, 0x91, 0x91, 0xFF, 0x91, 0x91, 0x91, 0xFF, + 0x7D, 0x7D, 0x7D, 0xFF, 0xE3, 0xE3, 0xE3, 0xFF, 0xF8, 0xF8, 0xF8, 0xFF, 0x5D, 0x5D, 0x5D, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x90, 0x90, 0x90, 0xFF, 0xFE, 0xFE, 0xFE, 0xFF, 0xDE, 0xDE, 0xDE, 0xFF, 0x94, 0x94, 0x94, 0xFF, + 0xB0, 0xB0, 0xB0, 0xFF, 0xB1, 0xB1, 0xB1, 0xFF, 0xB1, 0xB1, 0xB1, 0xFF, 0xB1, 0xB1, 0xB1, 0xFF, + 0x96, 0x96, 0x96, 0xFF, 0xB2, 0xB2, 0xB2, 0xFF, 0xB3, 0xB3, 0xB3, 0xFF, 0xB3, 0xB3, 0xB3, 0xFF, + 0x98, 0x98, 0x98, 0xFF, 0xB4, 0xB4, 0xB4, 0xFF, 0xB4, 0xB4, 0xB4, 0xFF, 0xB5, 0xB5, 0xB5, 0xFF, + 0x99, 0x99, 0x99, 0xFF, 0xB5, 0xB5, 0xB5, 0xFF, 0xB5, 0xB5, 0xB5, 0xFF, 0xB5, 0xB5, 0xB5, 0xFF, + 0x99, 0x99, 0x99, 0xFF, 0xE4, 0xE4, 0xE4, 0xFF, 0xF8, 0xF8, 0xF8, 0xFF, 0x5C, 0x5C, 0x5C, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x8E, 0x8E, 0x8E, 0xFF, 0xFD, 0xFD, 0xFD, 0xFF, 0xDF, 0xDF, 0xDF, 0xFF, 0x94, 0x94, 0x94, 0xFF, + 0xB1, 0xB1, 0xB1, 0xFF, 0xB1, 0xB1, 0xB1, 0xFF, 0xB2, 0xB2, 0xB2, 0xFF, 0xB2, 0xB2, 0xB2, 0xFF, + 0x97, 0x97, 0x97, 0xFF, 0xB3, 0xB3, 0xB3, 0xFF, 0xB4, 0xB4, 0xB4, 0xFF, 0xB4, 0xB4, 0xB4, 0xFF, + 0x99, 0x99, 0x99, 0xFF, 0xB5, 0xB5, 0xB5, 0xFF, 0xB5, 0xB5, 0xB5, 0xFF, 0xB5, 0xB5, 0xB5, 0xFF, + 0x99, 0x99, 0x99, 0xFF, 0xB5, 0xB5, 0xB5, 0xFF, 0xB5, 0xB5, 0xB5, 0xFF, 0xB5, 0xB5, 0xB5, 0xFF, + 0x99, 0x99, 0x99, 0xFF, 0xE5, 0xE5, 0xE5, 0xFF, 0xF8, 0xF8, 0xF8, 0xFF, 0x5C, 0x5C, 0x5C, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x8B, 0x8B, 0x8B, 0xFF, 0xFD, 0xFD, 0xFD, 0xFF, 0xDF, 0xDF, 0xDF, 0xFF, 0x7A, 0x7A, 0x7A, 0xFF, + 0x91, 0x91, 0x91, 0xFF, 0x92, 0x92, 0x92, 0xFF, 0x92, 0x92, 0x92, 0xFF, 0x93, 0x93, 0x93, 0xFF, + 0x7D, 0x7D, 0x7D, 0xFF, 0x94, 0x94, 0x94, 0xFF, 0x94, 0x94, 0x94, 0xFF, 0x94, 0x94, 0x94, 0xFF, + 0x7D, 0x7D, 0x7D, 0xFF, 0x94, 0x94, 0x94, 0xFF, 0x94, 0x94, 0x94, 0xFF, 0x95, 0x95, 0x95, 0xFF, + 0x7E, 0x7E, 0x7E, 0xFF, 0x95, 0x95, 0x95, 0xFF, 0x95, 0x95, 0x95, 0xFF, 0x95, 0x95, 0x95, 0xFF, + 0x7E, 0x7E, 0x7E, 0xFF, 0xE6, 0xE6, 0xE6, 0xFF, 0xF8, 0xF8, 0xF8, 0xFF, 0x5B, 0x5B, 0x5B, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x89, 0x89, 0x89, 0xFF, 0xFD, 0xFD, 0xFD, 0xFF, 0xE0, 0xE0, 0xE0, 0xFF, 0x94, 0x94, 0x94, 0xFF, + 0xB0, 0xB0, 0xB0, 0xFF, 0xB0, 0xB0, 0xB0, 0xFF, 0xB1, 0xB1, 0xB1, 0xFF, 0xB2, 0xB2, 0xB2, 0xFF, + 0x97, 0x97, 0x97, 0xFF, 0xE2, 0xE2, 0xE2, 0xFF, 0xE3, 0xE3, 0xE3, 0xFF, 0xE3, 0xE3, 0xE3, 0xFF, + 0xC0, 0xC0, 0xC0, 0xFF, 0xE4, 0xE4, 0xE4, 0xFF, 0xE4, 0xE4, 0xE4, 0xFF, 0xE5, 0xE5, 0xE5, 0xFF, + 0xC1, 0xC1, 0xC1, 0xFF, 0xE5, 0xE5, 0xE5, 0xFF, 0xE5, 0xE5, 0xE5, 0xFF, 0xE5, 0xE5, 0xE5, 0xFF, + 0xC1, 0xC1, 0xC1, 0xFF, 0xE8, 0xE8, 0xE8, 0xFF, 0xF8, 0xF8, 0xF8, 0xFF, 0x5A, 0x5A, 0x5A, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x86, 0x86, 0x86, 0xFF, 0xFD, 0xFD, 0xFD, 0xFF, 0xE1, 0xE1, 0xE1, 0xFF, 0x7B, 0x7B, 0x7B, 0xFF, + 0x92, 0x92, 0x92, 0xFF, 0x93, 0x93, 0x93, 0xFF, 0x94, 0x94, 0x94, 0xFF, 0x94, 0x94, 0x94, 0xFF, + 0x7D, 0x7D, 0x7D, 0xFF, 0xBD, 0xBD, 0xBD, 0xFF, 0xBD, 0xBD, 0xBD, 0xFF, 0xBD, 0xBD, 0xBD, 0xFF, + 0xA0, 0xA0, 0xA0, 0xFF, 0xBE, 0xBE, 0xBE, 0xFF, 0xBE, 0xBE, 0xBE, 0xFF, 0xBF, 0xBF, 0xBF, 0xFF, + 0xA1, 0xA1, 0xA1, 0xFF, 0xBF, 0xBF, 0xBF, 0xFF, 0xBF, 0xBF, 0xBF, 0xFF, 0xBF, 0xBF, 0xBF, 0xFF, + 0xA1, 0xA1, 0xA1, 0xFF, 0xE9, 0xE9, 0xE9, 0xFF, 0xF8, 0xF8, 0xF8, 0xFF, 0x59, 0x59, 0x59, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x83, 0x83, 0x83, 0xFF, 0xFD, 0xFD, 0xFD, 0xFF, 0xE1, 0xE1, 0xE1, 0xFF, 0x94, 0x94, 0x94, 0xFF, + 0xB1, 0xB1, 0xB1, 0xFF, 0xB2, 0xB2, 0xB2, 0xFF, 0xB3, 0xB3, 0xB3, 0xFF, 0xB3, 0xB3, 0xB3, 0xFF, + 0x97, 0x97, 0x97, 0xFF, 0xE4, 0xE4, 0xE4, 0xFF, 0xE5, 0xE5, 0xE5, 0xFF, 0xE5, 0xE5, 0xE5, 0xFF, + 0xC2, 0xC2, 0xC2, 0xFF, 0xE6, 0xE6, 0xE6, 0xFF, 0xE6, 0xE6, 0xE6, 0xFF, 0xE7, 0xE7, 0xE7, 0xFF, + 0xC3, 0xC3, 0xC3, 0xFF, 0xE7, 0xE7, 0xE7, 0xFF, 0xE7, 0xE7, 0xE7, 0xFF, 0xE7, 0xE7, 0xE7, 0xFF, + 0xC3, 0xC3, 0xC3, 0xFF, 0xEA, 0xEA, 0xEA, 0xFF, 0xF8, 0xF8, 0xF8, 0xFF, 0x58, 0x58, 0x58, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x80, 0x80, 0xFF, 0xFC, 0xFC, 0xFC, 0xFF, 0xE2, 0xE2, 0xE2, 0xFF, 0x7C, 0x7C, 0x7C, 0xFF, + 0x94, 0x94, 0x94, 0xFF, 0x94, 0x94, 0x94, 0xFF, 0x94, 0x94, 0x94, 0xFF, 0x94, 0x94, 0x94, 0xFF, + 0x7E, 0x7E, 0x7E, 0xFF, 0xBE, 0xBE, 0xBE, 0xFF, 0xBE, 0xBE, 0xBE, 0xFF, 0xBF, 0xBF, 0xBF, 0xFF, + 0xA2, 0xA2, 0xA2, 0xFF, 0xC0, 0xC0, 0xC0, 0xFF, 0xC0, 0xC0, 0xC0, 0xFF, 0xC1, 0xC1, 0xC1, 0xFF, + 0xA3, 0xA3, 0xA3, 0xFF, 0xC1, 0xC1, 0xC1, 0xFF, 0xC1, 0xC1, 0xC1, 0xFF, 0xC1, 0xC1, 0xC1, 0xFF, + 0xA3, 0xA3, 0xA3, 0xFF, 0xEB, 0xEB, 0xEB, 0xFF, 0xF8, 0xF8, 0xF8, 0xFF, 0x57, 0x57, 0x57, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7D, 0x7D, 0x7D, 0xFF, 0xFC, 0xFC, 0xFC, 0xFF, 0xE3, 0xE3, 0xE3, 0xFF, 0x96, 0x96, 0x96, 0xFF, + 0xB3, 0xB3, 0xB3, 0xFF, 0xB3, 0xB3, 0xB3, 0xFF, 0xB3, 0xB3, 0xB3, 0xFF, 0xB4, 0xB4, 0xB4, 0xFF, + 0x99, 0x99, 0x99, 0xFF, 0xE6, 0xE6, 0xE6, 0xFF, 0xE6, 0xE6, 0xE6, 0xFF, 0xE7, 0xE7, 0xE7, 0xFF, + 0xC4, 0xC4, 0xC4, 0xFF, 0xE8, 0xE8, 0xE8, 0xFF, 0xE8, 0xE8, 0xE8, 0xFF, 0xE9, 0xE9, 0xE9, 0xFF, + 0xC4, 0xC4, 0xC4, 0xFF, 0xE9, 0xE9, 0xE9, 0xFF, 0xE9, 0xE9, 0xE9, 0xFF, 0xE9, 0xE9, 0xE9, 0xFF, + 0xC4, 0xC4, 0xC4, 0xFF, 0xEC, 0xEC, 0xEC, 0xFF, 0xF8, 0xF8, 0xF8, 0xFF, 0x55, 0x55, 0x55, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7A, 0x7A, 0x7A, 0xFF, 0xFC, 0xFC, 0xFC, 0xFF, 0xE3, 0xE3, 0xE3, 0xFF, 0x7D, 0x7D, 0x7D, 0xFF, + 0x94, 0x94, 0x94, 0xFF, 0x94, 0x94, 0x94, 0xFF, 0x95, 0x95, 0x95, 0xFF, 0x96, 0x96, 0x96, 0xFF, + 0x7F, 0x7F, 0x7F, 0xFF, 0xBF, 0xBF, 0xBF, 0xFF, 0xC0, 0xC0, 0xC0, 0xFF, 0xC1, 0xC1, 0xC1, 0xFF, + 0xA3, 0xA3, 0xA3, 0xFF, 0xC1, 0xC1, 0xC1, 0xFF, 0xC1, 0xC1, 0xC1, 0xFF, 0xC2, 0xC2, 0xC2, 0xFF, + 0xA4, 0xA4, 0xA4, 0xFF, 0xC2, 0xC2, 0xC2, 0xFF, 0xC2, 0xC2, 0xC2, 0xFF, 0xC2, 0xC2, 0xC2, 0xFF, + 0xA4, 0xA4, 0xA4, 0xFF, 0xED, 0xED, 0xED, 0xFF, 0xF8, 0xF8, 0xF8, 0xFF, 0x54, 0x54, 0x54, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x77, 0x77, 0x77, 0xFF, 0xFB, 0xFB, 0xFB, 0xFF, 0xE4, 0xE4, 0xE4, 0xFF, 0x97, 0x97, 0x97, 0xFF, + 0xB3, 0xB3, 0xB3, 0xFF, 0xB4, 0xB4, 0xB4, 0xFF, 0xB5, 0xB5, 0xB5, 0xFF, 0xB6, 0xB6, 0xB6, 0xFF, + 0x99, 0x99, 0x99, 0xFF, 0xE7, 0xE7, 0xE7, 0xFF, 0xE8, 0xE8, 0xE8, 0xFF, 0xE9, 0xE9, 0xE9, 0xFF, + 0xC4, 0xC4, 0xC4, 0xFF, 0xEA, 0xEA, 0xEA, 0xFF, 0xEA, 0xEA, 0xEA, 0xFF, 0xEB, 0xEB, 0xEB, 0xFF, + 0xC6, 0xC6, 0xC6, 0xFF, 0xEB, 0xEB, 0xEB, 0xFF, 0xEB, 0xEB, 0xEB, 0xFF, 0xEB, 0xEB, 0xEB, 0xFF, + 0xC6, 0xC6, 0xC6, 0xFF, 0xEE, 0xEE, 0xEE, 0xFF, 0xF8, 0xF8, 0xF8, 0xFF, 0x52, 0x52, 0x52, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x73, 0x73, 0x73, 0xFF, 0xFB, 0xFB, 0xFB, 0xFF, 0xE4, 0xE4, 0xE4, 0xFF, 0x7D, 0x7D, 0x7D, 0xFF, + 0x94, 0x94, 0x94, 0xFF, 0x95, 0x95, 0x95, 0xFF, 0x96, 0x96, 0x96, 0xFF, 0x97, 0x97, 0x97, 0xFF, + 0x7F, 0x7F, 0x7F, 0xFF, 0xC1, 0xC1, 0xC1, 0xFF, 0xC1, 0xC1, 0xC1, 0xFF, 0xC2, 0xC2, 0xC2, 0xFF, + 0xA3, 0xA2, 0xA2, 0xFF, 0xAB, 0x91, 0x91, 0xFF, 0x94, 0x60, 0x60, 0xFF, 0x84, 0x3D, 0x3D, 0xFF, + 0x78, 0x25, 0x25, 0xFF, 0x80, 0x27, 0x27, 0xFF, 0x7E, 0x2B, 0x2B, 0xFF, 0x84, 0x3D, 0x3D, 0xFF, + 0x86, 0x52, 0x52, 0xFF, 0xCC, 0xB2, 0xB2, 0xFF, 0xF6, 0xF5, 0xF5, 0xFF, 0x50, 0x50, 0x50, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x70, 0x70, 0x70, 0xFF, 0xFB, 0xFB, 0xFB, 0xFF, 0xE5, 0xE5, 0xE5, 0xFF, 0x97, 0x97, 0x97, 0xFF, + 0xB4, 0xB4, 0xB4, 0xFF, 0xB5, 0xB5, 0xB5, 0xFF, 0xB6, 0xB6, 0xB6, 0xFF, 0xB6, 0xB6, 0xB6, 0xFF, + 0x9A, 0x9A, 0x9A, 0xFF, 0xE9, 0xE9, 0xE9, 0xFF, 0xE6, 0xE4, 0xE4, 0xFF, 0xB1, 0x85, 0x85, 0xFF, + 0x7A, 0x1B, 0x1B, 0xFF, 0xA6, 0x2F, 0x2F, 0xFF, 0xD3, 0x50, 0x50, 0xFF, 0xF2, 0x67, 0x67, 0xFF, + 0xFF, 0x70, 0x70, 0xFF, 0xFE, 0x6E, 0x6E, 0xFF, 0xFC, 0x69, 0x69, 0xFF, 0xED, 0x5B, 0x5B, 0xFF, + 0xCD, 0x43, 0x43, 0xFF, 0xA2, 0x25, 0x25, 0xFF, 0x7E, 0x1D, 0x1D, 0xFF, 0x58, 0x2C, 0x2C, 0xFF, + 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6D, 0x6D, 0x6D, 0xFF, 0xFB, 0xFB, 0xFB, 0xFF, 0xE5, 0xE5, 0xE5, 0xFF, 0x7D, 0x7D, 0x7D, 0xFF, + 0x95, 0x95, 0x95, 0xFF, 0x96, 0x96, 0x96, 0xFF, 0x97, 0x97, 0x97, 0xFF, 0x97, 0x97, 0x97, 0xFF, + 0x80, 0x80, 0x80, 0xFF, 0xB7, 0xAB, 0xAB, 0xFF, 0x7B, 0x28, 0x28, 0xFF, 0xAC, 0x34, 0x34, 0xFF, + 0xF5, 0x6A, 0x6A, 0xFF, 0xFF, 0x70, 0x70, 0xFF, 0xFF, 0x70, 0x70, 0xFF, 0xFE, 0x6F, 0x6F, 0xFF, + 0xFC, 0x6A, 0x6A, 0xFF, 0xFA, 0x65, 0x65, 0xFF, 0xF8, 0x60, 0x60, 0xFF, 0xF5, 0x5C, 0x5C, 0xFF, + 0xF3, 0x57, 0x57, 0xFF, 0xF1, 0x52, 0x52, 0xFF, 0xE6, 0x48, 0x48, 0xFF, 0xA1, 0x1F, 0x1F, 0xFF, + 0x53, 0x00, 0x00, 0xCB, 0x0C, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6A, 0x6A, 0x6A, 0xFF, 0xFA, 0xFA, 0xFA, 0xFF, 0xE6, 0xE6, 0xE6, 0xFF, 0x98, 0x98, 0x98, 0xFF, + 0xB5, 0xB5, 0xB5, 0xFF, 0xB6, 0xB6, 0xB6, 0xFF, 0xB6, 0xB6, 0xB6, 0xFF, 0xB7, 0xB7, 0xB7, 0xFF, + 0x98, 0x92, 0x92, 0xFF, 0x7E, 0x26, 0x26, 0xFF, 0xCB, 0x50, 0x50, 0xFF, 0xFF, 0x74, 0x74, 0xFF, + 0xFF, 0x70, 0x70, 0xFF, 0xFF, 0x70, 0x70, 0xFF, 0xFD, 0x6B, 0x6B, 0xFF, 0xFA, 0x67, 0x67, 0xFF, + 0xF8, 0x62, 0x62, 0xFF, 0xF6, 0x5D, 0x5D, 0xFF, 0xF4, 0x58, 0x58, 0xFF, 0xF1, 0x53, 0x53, 0xFF, + 0xEF, 0x4E, 0x4E, 0xFF, 0xED, 0x49, 0x49, 0xFF, 0xEB, 0x44, 0x44, 0xFF, 0xE9, 0x3F, 0x3F, 0xFF, + 0xB6, 0x24, 0x24, 0xFF, 0x57, 0x00, 0x00, 0xD7, 0x06, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x66, 0x66, 0x66, 0xFF, 0xFA, 0xFA, 0xFA, 0xFF, 0xE6, 0xE6, 0xE6, 0xFF, 0x7E, 0x7E, 0x7E, 0xFF, + 0x96, 0x96, 0x96, 0xFF, 0x97, 0x97, 0x97, 0xFF, 0x97, 0x97, 0x97, 0xFF, 0x98, 0x98, 0x98, 0xFF, + 0x73, 0x3D, 0x3D, 0xFF, 0xA6, 0x37, 0x37, 0xFF, 0xFF, 0x79, 0x79, 0xFF, 0xFF, 0x72, 0x72, 0xFF, + 0xFD, 0x6D, 0x6D, 0xFF, 0xFB, 0x68, 0x68, 0xFF, 0xF9, 0x63, 0x63, 0xFF, 0xF6, 0x5E, 0x5E, 0xFF, + 0xF4, 0x59, 0x59, 0xFF, 0xF2, 0x54, 0x54, 0xFF, 0xF0, 0x4F, 0x4F, 0xFF, 0xEE, 0x4A, 0x4A, 0xFF, + 0xEB, 0x45, 0x45, 0xFF, 0xE9, 0x41, 0x41, 0xFF, 0xE7, 0x3C, 0x3C, 0xFF, 0xE5, 0x37, 0x37, 0xFF, + 0xE2, 0x35, 0x35, 0xFF, 0x91, 0x13, 0x13, 0xFF, 0x36, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x63, 0x63, 0x63, 0xFF, 0xFA, 0xFA, 0xFA, 0xFF, 0xE6, 0xE6, 0xE6, 0xFF, 0x98, 0x98, 0x98, 0xFF, + 0xB5, 0xB5, 0xB5, 0xFF, 0xB6, 0xB6, 0xB6, 0xFF, 0xB7, 0xB7, 0xB7, 0xFF, 0xB8, 0xB8, 0xB8, 0xFF, + 0x6E, 0x15, 0x15, 0xFF, 0xDF, 0x6D, 0x6D, 0xFF, 0xFE, 0x75, 0x75, 0xFF, 0xFB, 0x69, 0x69, 0xFF, + 0xF9, 0x64, 0x64, 0xFF, 0xF7, 0x5F, 0x5F, 0xFF, 0xF5, 0x5A, 0x5A, 0xFF, 0xF3, 0x55, 0x55, 0xFF, + 0xF0, 0x50, 0x50, 0xFF, 0xEE, 0x4C, 0x4C, 0xFF, 0xEC, 0x47, 0x47, 0xFF, 0xEA, 0x42, 0x42, 0xFF, + 0xE7, 0x3D, 0x3D, 0xFF, 0xE5, 0x38, 0x38, 0xFF, 0xE3, 0x33, 0x33, 0xFF, 0xE1, 0x2E, 0x2E, 0xFF, + 0xDF, 0x2D, 0x2D, 0xFF, 0xC2, 0x28, 0x28, 0xFF, 0x59, 0x00, 0x00, 0xDE, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x60, 0x60, 0x60, 0xFF, 0xF9, 0xF9, 0xF9, 0xFF, 0xE6, 0xE6, 0xE6, 0xFF, 0x7E, 0x7E, 0x7E, 0xFF, + 0x97, 0x97, 0x97, 0xFF, 0x97, 0x97, 0x97, 0xFF, 0x98, 0x98, 0x98, 0xFF, 0x98, 0x98, 0x98, 0xFF, + 0x67, 0x01, 0x01, 0xFF, 0xED, 0x7B, 0x7B, 0xFF, 0xFA, 0x6A, 0x6A, 0xFF, 0xF8, 0x60, 0x60, 0xFF, + 0xF5, 0x5B, 0x5B, 0xFF, 0xF3, 0x56, 0x56, 0xFF, 0xF1, 0x52, 0x52, 0xFF, 0xEF, 0x4D, 0x4D, 0xFF, + 0xEC, 0x48, 0x48, 0xFF, 0xCD, 0x84, 0x78, 0xFF, 0x77, 0x7C, 0xAE, 0xFF, 0x77, 0x7B, 0xAD, 0xFF, + 0x72, 0x75, 0xA7, 0xFF, 0x6B, 0x6F, 0xA1, 0xFF, 0x67, 0x69, 0x9B, 0xFF, 0x60, 0x63, 0x96, 0xFF, + 0x5E, 0x60, 0x91, 0xFF, 0x7E, 0x5E, 0x82, 0xFF, 0x66, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5D, 0x5D, 0x5D, 0xFF, 0xF9, 0xF9, 0xF9, 0xFF, 0xE6, 0xE6, 0xE6, 0xFF, 0x99, 0x99, 0x99, 0xFF, + 0xB6, 0xB6, 0xB6, 0xFF, 0xB6, 0xB6, 0xB6, 0xFF, 0xB7, 0xB7, 0xB7, 0xFF, 0xB8, 0xB8, 0xB8, 0xFF, + 0x67, 0x00, 0x00, 0xFF, 0xD8, 0x62, 0x62, 0xFF, 0xF7, 0x6D, 0x6D, 0xFF, 0xF4, 0x58, 0x58, 0xFF, + 0xF1, 0x53, 0x53, 0xFF, 0xEF, 0x4E, 0x4E, 0xFF, 0xED, 0x49, 0x49, 0xFF, 0xEB, 0x44, 0x44, 0xFF, + 0xE8, 0x43, 0x41, 0xFF, 0xC5, 0xE1, 0x8C, 0xFF, 0x8A, 0x93, 0x9F, 0xFF, 0x58, 0x89, 0xC7, 0xFF, + 0x51, 0x82, 0xC1, 0xFF, 0x4B, 0x7C, 0xBA, 0xFF, 0x44, 0x75, 0xB4, 0xFF, 0x3D, 0x6E, 0xAE, 0xFF, + 0x45, 0x72, 0xAD, 0xFF, 0x6A, 0x60, 0x87, 0xFF, 0x67, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x0E, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x59, 0x59, 0x59, 0xFF, 0xF9, 0xF9, 0xF9, 0xFF, 0xE7, 0xE7, 0xE7, 0xFF, 0x7E, 0x7E, 0x7E, 0xFF, + 0x97, 0x97, 0x97, 0xFF, 0x97, 0x97, 0x97, 0xFF, 0x98, 0x98, 0x98, 0xFF, 0x98, 0x98, 0x98, 0xFF, + 0x69, 0x0D, 0x0D, 0xFF, 0xA3, 0x24, 0x24, 0xFF, 0xEF, 0x7B, 0x7B, 0xFF, 0xF1, 0x5A, 0x5A, 0xFF, + 0xEE, 0x4A, 0x4A, 0xFF, 0xEB, 0x45, 0x45, 0xFF, 0xE9, 0x40, 0x40, 0xFF, 0xE7, 0x3C, 0x3C, 0xFF, + 0xD8, 0x6B, 0x4D, 0xFF, 0xAD, 0xE7, 0x73, 0xFF, 0xAB, 0xC3, 0x5F, 0xFF, 0x55, 0x85, 0xC0, 0xFF, + 0x4D, 0x7E, 0xBD, 0xFF, 0x47, 0x78, 0xB7, 0xFF, 0x40, 0x71, 0xB0, 0xFF, 0x43, 0x72, 0xAE, 0xFF, + 0x6B, 0x7A, 0xA6, 0xFF, 0x54, 0x42, 0x67, 0xFF, 0x5C, 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00, 0x16, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x56, 0x56, 0x56, 0xFF, 0xF8, 0xF8, 0xF8, 0xFF, 0xE7, 0xE7, 0xE7, 0xFF, 0x99, 0x99, 0x99, 0xFF, + 0xB6, 0xB6, 0xB6, 0xFF, 0xB6, 0xB6, 0xB6, 0xFF, 0xB7, 0xB7, 0xB7, 0xFF, 0xB8, 0xB8, 0xB8, 0xFF, + 0x77, 0x39, 0x39, 0xFF, 0x7A, 0x09, 0x09, 0xFF, 0xBB, 0x32, 0x32, 0xFF, 0xE8, 0x71, 0x71, 0xFF, + 0xED, 0x61, 0x61, 0xFF, 0xE8, 0x42, 0x42, 0xFF, 0xE5, 0x38, 0x38, 0xFF, 0xE3, 0x33, 0x33, 0xFF, + 0xBA, 0x9F, 0x4E, 0xFF, 0x99, 0xE0, 0x53, 0xFF, 0x8F, 0xDC, 0x43, 0xFF, 0x84, 0x8A, 0x66, 0xFF, + 0x49, 0x7A, 0xB9, 0xFF, 0x47, 0x77, 0xB4, 0xFF, 0x58, 0x82, 0xB9, 0xFF, 0x79, 0x7F, 0xA6, 0xFF, + 0x45, 0x5E, 0x90, 0xFF, 0x65, 0x17, 0x23, 0xFF, 0x3E, 0x00, 0x00, 0xAC, 0x00, 0x00, 0x00, 0x13, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x53, 0x53, 0x53, 0xFF, 0xF8, 0xF8, 0xF8, 0xFF, 0xE6, 0xE6, 0xE6, 0xFF, 0x7E, 0x7E, 0x7E, 0xFF, + 0x97, 0x97, 0x97, 0xFF, 0x97, 0x97, 0x97, 0xFF, 0x98, 0x98, 0x98, 0xFF, 0x98, 0x98, 0x98, 0xFF, + 0x76, 0x6B, 0x6B, 0xFF, 0x6D, 0x0E, 0x0E, 0xFF, 0x88, 0x11, 0x11, 0xFF, 0xB0, 0x23, 0x23, 0xFF, + 0xCA, 0x45, 0x45, 0xFF, 0xE2, 0x66, 0x66, 0xFF, 0xE8, 0x5D, 0x5D, 0xFF, 0xE2, 0x4F, 0x4B, 0xFF, + 0xA1, 0xD6, 0x56, 0xFF, 0x91, 0xDC, 0x47, 0xFF, 0x8B, 0xD9, 0x3C, 0xFF, 0x90, 0xC9, 0x3A, 0xFF, + 0x74, 0x94, 0xBB, 0xFF, 0x84, 0x8E, 0xB4, 0xFF, 0x70, 0x68, 0x8E, 0xFF, 0x3B, 0x5E, 0x92, 0xFF, + 0x60, 0x29, 0x42, 0xFF, 0x5F, 0x00, 0x00, 0xEF, 0x0B, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4F, 0x4F, 0x4F, 0xFF, 0xF8, 0xF8, 0xF8, 0xFF, 0xE6, 0xE6, 0xE6, 0xFF, 0xE5, 0xE5, 0xE5, 0xFF, + 0xE6, 0xE6, 0xE6, 0xFF, 0xE7, 0xE7, 0xE7, 0xFF, 0xE8, 0xE8, 0xE8, 0xFF, 0xE9, 0xE9, 0xE9, 0xFF, + 0xE6, 0xE6, 0xE6, 0xFF, 0xBD, 0xA9, 0xA9, 0xFF, 0x71, 0x15, 0x15, 0xFF, 0x7B, 0x0C, 0x0C, 0xFF, + 0xA2, 0x1D, 0x1D, 0xFF, 0xAD, 0x1B, 0x1B, 0xFF, 0xB6, 0x27, 0x27, 0xFF, 0xB2, 0x4F, 0x33, 0xFF, + 0x93, 0xA6, 0x40, 0xFF, 0x96, 0xA8, 0x40, 0xFF, 0x91, 0xA7, 0x3C, 0xFF, 0x86, 0x9F, 0x33, 0xFF, + 0x6E, 0x67, 0x5B, 0xFF, 0x37, 0x5C, 0x93, 0xFF, 0x54, 0x4D, 0x75, 0xFF, 0x6B, 0x18, 0x23, 0xFF, + 0x5C, 0x00, 0x00, 0xEB, 0x13, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, + 0x4C, 0x4C, 0x4C, 0xF1, 0xF7, 0xF7, 0xF7, 0xFF, 0xF6, 0xF6, 0xF6, 0xFF, 0xF6, 0xF6, 0xF6, 0xFF, + 0xF6, 0xF6, 0xF6, 0xFF, 0xF7, 0xF7, 0xF7, 0xFF, 0xF7, 0xF7, 0xF7, 0xFF, 0xF7, 0xF7, 0xF7, 0xFF, + 0xF7, 0xF7, 0xF7, 0xFF, 0xF0, 0xF0, 0xF0, 0xFF, 0xD1, 0xC9, 0xC9, 0xFF, 0x90, 0x53, 0x53, 0xFF, + 0x6A, 0x05, 0x05, 0xFF, 0x77, 0x0A, 0x0A, 0xFF, 0x8E, 0x14, 0x14, 0xFF, 0x91, 0x33, 0x1C, 0xFF, + 0x72, 0x87, 0x23, 0xFF, 0x70, 0x90, 0x24, 0xFF, 0x6E, 0x8F, 0x23, 0xFF, 0x73, 0x7C, 0x1F, 0xFF, + 0x70, 0x3C, 0x31, 0xFF, 0x6C, 0x14, 0x1B, 0xFF, 0x6A, 0x05, 0x05, 0xFF, 0x53, 0x16, 0x16, 0xFB, + 0x08, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x22, + 0x19, 0x19, 0x19, 0x78, 0x53, 0x53, 0x53, 0xF6, 0x56, 0x56, 0x56, 0xFF, 0x56, 0x56, 0x56, 0xFF, + 0x56, 0x56, 0x56, 0xFF, 0x56, 0x56, 0x56, 0xFF, 0x56, 0x56, 0x56, 0xFF, 0x56, 0x56, 0x56, 0xFF, + 0x56, 0x56, 0x56, 0xFF, 0x56, 0x56, 0x56, 0xFF, 0x55, 0x55, 0x55, 0xFF, 0x4F, 0x4F, 0x4F, 0xFF, + 0x4B, 0x43, 0x43, 0xFF, 0x52, 0x28, 0x28, 0xFF, 0x5A, 0x16, 0x16, 0xFF, 0x61, 0x0A, 0x0A, 0xFF, + 0x65, 0x04, 0x04, 0xFF, 0x67, 0x00, 0x00, 0xFF, 0x65, 0x04, 0x04, 0xFF, 0x60, 0x09, 0x09, 0xFF, + 0x59, 0x15, 0x15, 0xFF, 0x50, 0x26, 0x26, 0xFF, 0x45, 0x3C, 0x3C, 0xF9, 0x15, 0x15, 0x15, 0x93, + 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1D, + 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0x4A, + 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x4A, + 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x4A, + 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x65, + 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x69, + 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x3B, + 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x16, + 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x16, + 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x16, + 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x16, + 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x17, + 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; static const struct { diff --git a/test/images/gen.go b/test/images/gen.go index 910abc74..7ded83e6 100644 --- a/test/images/gen.go +++ b/test/images/gen.go @@ -51,18 +51,14 @@ func main() { fmt.Println("#include \"test.h\"") fmt.Println() for i, im := range images { - fmt.Printf("static const uint32_t dat%d[] = {", i) - for j := 0; j < len(im.data); j += 4 { - if (j % (16 * 4)) == 0 { + fmt.Printf("static const uint8_t dat%d[] = {", i) + for j := 0; j < len(im.data); j++ { + if (j % 16) == 0 { fmt.Printf("\n\t") } else { fmt.Printf(" ") } - d := uint32(im.data[j + 0]) << 16 - d |= uint32(im.data[j + 1]) << 8 - d |= uint32(im.data[j + 2]) - d |= uint32(im.data[j + 3]) << 24 - fmt.Printf("0x%08X,", d) + fmt.Printf("0x%02X,", im.data[j]) } fmt.Println("\n};") From f065abedd78939929be8c5affcb176cbcba7d28a Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 29 Aug 2018 20:32:12 -0400 Subject: [PATCH 1281/1329] Fixed the uiImage implemenation on OS X. Even though libui won't be running on a big-endian Mac any time soon, I still want to test that code on one to make sure it's correct. --- darwin/image.m | 68 ++++++++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 35 deletions(-) diff --git a/darwin/image.m b/darwin/image.m index a4b322c7..49e3677c 100644 --- a/darwin/image.m +++ b/darwin/image.m @@ -4,7 +4,6 @@ struct uiImage { NSImage *i; NSSize size; - NSMutableArray *swizzled; }; uiImage *uiNewImage(double width, double height) @@ -14,50 +13,23 @@ uiImage *uiNewImage(double width, double height) i = uiprivNew(uiImage); i->size = NSMakeSize(width, height); i->i = [[NSImage alloc] initWithSize:i->size]; - i->swizzled = [NSMutableArray new]; return i; } void uiFreeImage(uiImage *i) { - NSValue *v; - [i->i release]; - // to be safe, do this after releasing the image - for (v in i->swizzled) - uiprivFree([v pointerValue]); - [i->swizzled release]; uiprivFree(i); } void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int byteStride) { NSBitmapImageRep *repCalibrated, *repsRGB; - uint8_t *swizzled, *bp, *sp; int x, y; - unsigned char *pix[1]; + uint8_t *pix, *data; + NSInteger realStride; - // OS X demands that R and B are in the opposite order from what we expect - // we must swizzle :( - // LONGTERM test on a big-endian system - swizzled = (uint8_t *) uiprivAlloc((byteStride * pixelHeight) * sizeof (uint8_t), "uint8_t[]"); - bp = (uint8_t *) pixels; - sp = swizzled; - for (y = 0; y < pixelHeight; y++){ - for (x = 0; x < pixelWidth; x++) { - sp[0] = bp[2]; - sp[1] = bp[1]; - sp[2] = bp[0]; - sp[3] = bp[3]; - sp += 4; - bp += 4; - } - // jump over unused bytes at end of line - bp += byteStride - pixelWidth * 4; - } - - pix[0] = (unsigned char *) swizzled; - repCalibrated = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:pix + repCalibrated = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL pixelsWide:pixelWidth pixelsHigh:pixelHeight bitsPerSample:8 @@ -66,8 +38,37 @@ void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, in isPlanar:NO colorSpaceName:NSCalibratedRGBColorSpace bitmapFormat:0 - bytesPerRow:byteStride + bytesPerRow:0 bitsPerPixel:32]; + + // Apple doesn't explicitly document this, but we apparently need to use native system endian for the data :| + // TODO split this into a utility routine? + // TODO find proper documentation + // TODO test this on a big-endian system somehow + pix = (uint8_t *) pixels; + data = (uint8_t *) [repCalibrated bitmapData]; + realStride = [repCalibrated bytesPerRow]; + for (y = 0; y < pixelHeight; y++) { + for (x = 0; x < pixelWidth * 4; x += 4) { + union { + uint32_t v32; + uint8_t v8[4]; + } v; + + v.v32 = ((uint32_t) (pix[x + 3])) << 24; + v.v32 |= ((uint32_t) (pix[x + 2])) << 16; + v.v32 |= ((uint32_t) (pix[x + 1])) << 8; + v.v32 |= ((uint32_t) (pix[x])); + data[x] = v.v8[0]; + data[x + 1] = v.v8[1]; + data[x + 2] = v.v8[2]; + data[x + 3] = v.v8[3]; + } + pix += byteStride; + data += realStride; + } + + // we can't call the constructor with this, but we can retag (NOT convert) repsRGB = [repCalibrated bitmapImageRepByRetaggingWithColorSpace:[NSColorSpace sRGBColorSpace]]; [i->i addRepresentation:repsRGB]; @@ -75,9 +76,6 @@ void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, in // don't release repsRGB; it may be equivalent to repCalibrated // do release repCalibrated though; NSImage has a ref to either it or to repsRGB [repCalibrated release]; - - // we need to keep swizzled alive for NSBitmapImageRep - [i->swizzled addObject:[NSValue valueWithPointer:swizzled]]; } NSImage *uiprivImageNSImage(uiImage *i) From e098cb558576c0b88eecf6e06a3ab1c5460c6989 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 29 Aug 2018 20:36:18 -0400 Subject: [PATCH 1282/1329] More TODOs --- darwin/image.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/darwin/image.m b/darwin/image.m index 49e3677c..0b10cb09 100644 --- a/darwin/image.m +++ b/darwin/image.m @@ -44,7 +44,7 @@ void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, in // Apple doesn't explicitly document this, but we apparently need to use native system endian for the data :| // TODO split this into a utility routine? // TODO find proper documentation - // TODO test this on a big-endian system somehow + // TODO test this on a big-endian system somehow; I have a feeling the above comment is wrong about the diagnosis since the order we are specifying is now 0xAABBGGRR pix = (uint8_t *) pixels; data = (uint8_t *) [repCalibrated bitmapData]; realStride = [repCalibrated bytesPerRow]; From 869992010c213ffd7f26fd15823544bb8f52dbff Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 30 Aug 2018 11:07:59 -0400 Subject: [PATCH 1283/1329] Fixed uiImage on GTK+. Also changed it to have cairo manage memory and decide best parameters for us. I forgot to mention that the fix on OS X did this there too. Also made sure we use cairo surfaces properly this time. --- unix/image.c | 50 +++++++++++++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/unix/image.c b/unix/image.c index eecdec18..cf21900d 100644 --- a/unix/image.c +++ b/unix/image.c @@ -10,11 +10,8 @@ struct uiImage { static void freeImageRep(gpointer item) { cairo_surface_t *cs = (cairo_surface_t *) item; - unsigned char *buf; - buf = cairo_image_surface_get_data(cs); cairo_surface_destroy(cs); - uiprivFree(buf); } uiImage *uiNewImage(double width, double height) @@ -37,26 +34,41 @@ void uiFreeImage(uiImage *i) void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int byteStride) { cairo_surface_t *cs; - unsigned char *buf, *p; - uint8_t *src = (uint8_t *) pixels; - int cByteStride; - int n; + uint8_t *data, *pix; + int realStride; + int x, y; - // unfortunately for optimal performance cairo expects its own stride values and we will have to reconcile them if they differ - cByteStride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, pixelWidth); - buf = (unsigned char *) uiprivAlloc((cByteStride * pixelHeight) * sizeof (unsigned char), "unsigned char[]"); - p = buf; - for (n = 0; n < byteStride * pixelHeight; n += byteStride) { - memmove(p, src + n, cByteStride); - p += cByteStride; - } - // also note that stride here is also in bytes - cs = cairo_image_surface_create_for_data(buf, CAIRO_FORMAT_ARGB32, - pixelWidth, pixelHeight, - cByteStride); + // note that this is native-endian + cs = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, + pixelWidth, pixelHeight); if (cairo_surface_status(cs) != CAIRO_STATUS_SUCCESS) /* TODO */; cairo_surface_flush(cs); + + pix = (uint8_t *) pixels; + data = (uint8_t *) cairo_image_surface_get_data(cs); + realStride = cairo_image_surface_get_stride(cs); + for (y = 0; y < pixelHeight; y++) { + for (x = 0; x < pixelWidth * 4; x += 4) { + union { + uint32_t v32; + uint8_t v8[4]; + } v; + + v.v32 = ((uint32_t) (pix[x + 3])) << 24; + v.v32 |= ((uint32_t) (pix[x])) << 16; + v.v32 |= ((uint32_t) (pix[x + 1])) << 8; + v.v32 |= ((uint32_t) (pix[x + 2])); + data[x] = v.v8[0]; + data[x + 1] = v.v8[1]; + data[x + 2] = v.v8[2]; + data[x + 3] = v.v8[3]; + } + pix += byteStride; + data += realStride; + } + + cairo_surface_mark_dirty(cs); g_ptr_array_add(i->images, cs); } From db9977a4ee1677ff067c96c579c7a11a25d51eec Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 1 Sep 2018 19:02:23 -0400 Subject: [PATCH 1284/1329] Fixed uiImage on Windows. Phew. Also made it have Windows decide the best parameters, like on other platforms. --- windows/image.cpp | 66 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 6 deletions(-) diff --git a/windows/image.cpp b/windows/image.cpp index 368c77fa..b063a5ef 100644 --- a/windows/image.cpp +++ b/windows/image.cpp @@ -1,5 +1,8 @@ #include "uipriv_windows.hpp" +// TODO: +// - is the alpha channel ignored when drawing images in tables? + IWICImagingFactory *uiprivWICFactory = NULL; HRESULT uiprivInitImage(void) @@ -39,17 +42,68 @@ void uiFreeImage(uiImage *i) uiprivFree(i); } +// to make things easier, we store images in WIC in the same way we store them in GDI (as system-endian ARGB) and premultiplied (as that's what AlphaBlend() expects (TODO confirm this)) +// but what WIC format is system-endian ARGB? for a little-endian system, that's BGRA +// it turns out that the Windows 8 BMP encoder uses BGRA if told to (https://docs.microsoft.com/en-us/windows/desktop/wic/bmp-format-overview) +// it also turns out Direct2D requires PBGRA for drawing (https://docs.microsoft.com/en-us/windows/desktop/wic/-wic-bitmapsources-howto-drawusingd2d) +// so I guess we can assume PBGRA is correct...? (TODO) +#define formatForGDI GUID_WICPixelFormat32bppPBGRA + void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int byteStride) { IWICBitmap *b; + WICRect r; + IWICBitmapLock *l; + uint8_t *pix, *data; + WICInProcPointer dipp; + UINT size; + UINT realStride; + int x, y; HRESULT hr; - hr = uiprivWICFactory->CreateBitmapFromMemory(pixelWidth, pixelHeight, - GUID_WICPixelFormat32bppRGBA, byteStride, - byteStride * pixelHeight, (BYTE *) pixels, + hr = uiprivWICFactory->CreateBitmap(pixelWidth, pixelHeight, + formatForGDI, WICBitmapCacheOnDemand, &b); if (hr != S_OK) - logHRESULT(L"error calling CreateBitmapFromMemory() in uiImageAppend()", hr); + logHRESULT(L"error calling CreateBitmap() in uiImageAppend()", hr); + r.X = 0; + r.Y = 0; + r.Width = pixelWidth; + r.Height = pixelHeight; + hr = b->Lock(&r, WICBitmapLockWrite, &l); + if (hr != S_OK) + logHRESULT(L"error calling Lock() in uiImageAppend()", hr); + + pix = (uint8_t *) pixels; + // TODO can size be NULL? + hr = l->GetDataPointer(&size, &dipp); + if (hr != S_OK) + logHRESULT(L"error calling GetDataPointer() in uiImageAppend()", hr); + data = (uint8_t *) dipp; + hr = l->GetStride(&realStride); + if (hr != S_OK) + logHRESULT(L"error calling GetStride() in uiImageAppend()", hr); + for (y = 0; y < pixelHeight; y++) { + for (x = 0; x < pixelWidth * 4; x += 4) { + union { + uint32_t v32; + uint8_t v8[4]; + } v; + + v.v32 = ((uint32_t) (pix[x + 3])) << 24; + v.v32 |= ((uint32_t) (pix[x])) << 16; + v.v32 |= ((uint32_t) (pix[x + 1])) << 8; + v.v32 |= ((uint32_t) (pix[x + 2])); + data[x] = v.v8[0]; + data[x + 1] = v.v8[1]; + data[x + 2] = v.v8[2]; + data[x + 3] = v.v8[3]; + } + pix += byteStride; + data += realStride; + } + + l->Release(); i->bitmaps->push_back(b); } @@ -172,7 +226,7 @@ HRESULT uiprivWICToGDI(IWICBitmap *b, HDC dc, int width, int height, HBITMAP *hb scaler->Release(); return hr; } - if (IsEqualGUID(guid, GUID_WICPixelFormat32bppRGBA)) + if (IsEqualGUID(guid, formatForGDI)) src = scaler; else { hr = uiprivWICFactory->CreateFormatConverter(&conv); @@ -180,7 +234,7 @@ HRESULT uiprivWICToGDI(IWICBitmap *b, HDC dc, int width, int height, HBITMAP *hb scaler->Release(); return hr; } - hr = conv->Initialize(scaler, GUID_WICPixelFormat32bppRGBA, + hr = conv->Initialize(scaler, formatForGDI, // TODO is the dither type correct in all cases? WICBitmapDitherTypeNone, NULL, 0, WICBitmapPaletteTypeMedianCut); scaler->Release(); From 2606235a74664cafc04e4766df94cdbe3656b482 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 1 Sep 2018 19:14:30 -0400 Subject: [PATCH 1285/1329] And fixed the documentation and marked Alpha 4.1. --- README.md | 3 +++ ui.h | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6b4966bc..87cae68e 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,9 @@ But libui is not dead; I am working on it whenever I can, and I hope to get it t *Note that today's entry (Eastern Time) may be updated later today.* +* **1 September 2018** + * **Alpha 4.1 is here.** This is an emergency fix to Alpha 4 to fix `uiImageAppend()` not working as documented. It now works properly, with one important difference you'll need to care about: **it now requires image data to be alpha-premultiplied**. In addition, it also manages memory slightly better now, and has minor documentation typo fixes. + * **10 August 2018** * **Alpha 4 is finally here.** Everything from Alpha 3.5 and what's listed below is in this release; the two biggest changes are still the new text drawing API and new uiTable control. In between all that is a whole bunch of bugfixes, and hopefully more stability too. Thanks to everybody who helped contribute! * Alpha 4 should hopefully also include automated binary releases via CI. Thanks to those who helped set that up! diff --git a/ui.h b/ui.h index 8f6e10c4..40aea949 100644 --- a/ui.h +++ b/ui.h @@ -1144,7 +1144,7 @@ _UI_EXTERN uiGrid *uiNewGrid(void); // resolution; this matches the current expectations of some // desktop systems at the time of writing (mid-2018) // -// uiImage is very simple: it only supports non-premultiplied 32-bit +// uiImage is very simple: it only supports premultiplied 32-bit // RGBA images, and libui does not provide any image file loading // or image format conversion utilities on top of that. typedef struct uiImage uiImage; @@ -1161,7 +1161,7 @@ _UI_EXTERN uiImage *uiNewImage(double width, double height); _UI_EXTERN void uiFreeImage(uiImage *i); // uiImageAppend adds a representation to the uiImage. -// pixels should point to a byte array of non-premultiplied pixels +// pixels should point to a byte array of premultiplied pixels // stored in [R G B A] order (so ((uint8_t *) pixels)[0] is the R of the // first pixel and [3] is the A of the first pixel). pixelWidth and // pixelHeight is the size *in pixels* of the image, and pixelStride is From 7268f2c78f550caafc02d5ff45aabe4665406d7e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 1 Sep 2018 19:15:41 -0400 Subject: [PATCH 1286/1329] Oops, clarified the README I just wrote. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 87cae68e..bce51d87 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ But libui is not dead; I am working on it whenever I can, and I hope to get it t *Note that today's entry (Eastern Time) may be updated later today.* * **1 September 2018** - * **Alpha 4.1 is here.** This is an emergency fix to Alpha 4 to fix `uiImageAppend()` not working as documented. It now works properly, with one important difference you'll need to care about: **it now requires image data to be alpha-premultiplied**. In addition, it also manages memory slightly better now, and has minor documentation typo fixes. + * **Alpha 4.1 is here.** This is an emergency fix to Alpha 4 to fix `uiImageAppend()` not working as documented. It now works properly, with one important difference you'll need to care about: **it now requires image data to be alpha-premultiplied**. In addition, `uiImage` also manages memory slightly better now, and `ui.h` has minor documentation typo fixes. * **10 August 2018** * **Alpha 4 is finally here.** Everything from Alpha 3.5 and what's listed below is in this release; the two biggest changes are still the new text drawing API and new uiTable control. In between all that is a whole bunch of bugfixes, and hopefully more stability too. Thanks to everybody who helped contribute! From e2222e414d1d98cf04940d4a2b03a8e7d9e2aa47 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 1 Sep 2018 20:03:15 -0400 Subject: [PATCH 1287/1329] Sigh MinGW --- README.md | 2 +- windows/image.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bce51d87..189caa35 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ But libui is not dead; I am working on it whenever I can, and I hope to get it t *Note that today's entry (Eastern Time) may be updated later today.* * **1 September 2018** - * **Alpha 4.1 is here.** This is an emergency fix to Alpha 4 to fix `uiImageAppend()` not working as documented. It now works properly, with one important difference you'll need to care about: **it now requires image data to be alpha-premultiplied**. In addition, `uiImage` also manages memory slightly better now, and `ui.h` has minor documentation typo fixes. + * **Alpha 4.1 is here.** This is an emergency fix to Alpha 4 to fix `uiImageAppend()` not working as documented. It now works properly, with one important difference you'll need to care about: **it now requires image data to be alpha-premultiplied**. In addition, `uiImage` also is implemented slightly more nicely now, and `ui.h` has minor documentation typo fixes. * **10 August 2018** * **Alpha 4 is finally here.** Everything from Alpha 3.5 and what's listed below is in this release; the two biggest changes are still the new text drawing API and new uiTable control. In between all that is a whole bunch of bugfixes, and hopefully more stability too. Thanks to everybody who helped contribute! diff --git a/windows/image.cpp b/windows/image.cpp index b063a5ef..7d1a06ea 100644 --- a/windows/image.cpp +++ b/windows/image.cpp @@ -55,7 +55,8 @@ void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, in WICRect r; IWICBitmapLock *l; uint8_t *pix, *data; - WICInProcPointer dipp; + // TODO WICInProcPointer is not available in MinGW-w64 + BYTE *dipp; UINT size; UINT realStride; int x, y; From 6891017cbe07dc3d0826977a825022c45210dae9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 1 Sep 2018 20:37:05 -0400 Subject: [PATCH 1288/1329] Wait I thought I did PIC already wtf --- CMakeLists.txt | 11 ++++++----- README.md | 1 + 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 17c87683..c7193fbb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -205,6 +205,9 @@ if(BUILD_SHARED_LIBS) SOVERSION "${_VERSION}") endif() endif() +# TODO why is this not a default?! +set_property(TARGET libui PROPERTY + POSITION_INDEPENDENT_CODE True) macro(_add_exec _name) # TODOTODO re-add WIN32 when merging back @@ -214,11 +217,9 @@ macro(_add_exec _name) target_link_libraries(${_name} libui) _target_link_options_private(${_name} _COMMON_LDFLAGS) - # make shared-linked executables PIC too - if(BUILD_SHARED_LIBS) - set_property(TARGET ${_name} PROPERTY - POSITION_INDEPENDENT_CODE True) - endif() + # TODO does this propagate? + set_property(TARGET ${_name} PROPERTY + POSITION_INDEPENDENT_CODE True) # TODO see above about INTERFACE if(NOT BUILD_SHARED_LIBS) target_link_libraries(${_name} diff --git a/README.md b/README.md index 189caa35..837e54f4 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ But libui is not dead; I am working on it whenever I can, and I hope to get it t * **1 September 2018** * **Alpha 4.1 is here.** This is an emergency fix to Alpha 4 to fix `uiImageAppend()` not working as documented. It now works properly, with one important difference you'll need to care about: **it now requires image data to be alpha-premultiplied**. In addition, `uiImage` also is implemented slightly more nicely now, and `ui.h` has minor documentation typo fixes. + * Alpha 4.1 also tries to make everything properly PIC-enabled. * **10 August 2018** * **Alpha 4 is finally here.** Everything from Alpha 3.5 and what's listed below is in this release; the two biggest changes are still the new text drawing API and new uiTable control. In between all that is a whole bunch of bugfixes, and hopefully more stability too. Thanks to everybody who helped contribute! From 186a92f6e9232e12ab442f64462ad182a584c8fb Mon Sep 17 00:00:00 2001 From: Travis Gibson Date: Wed, 19 Sep 2018 20:48:52 -0700 Subject: [PATCH 1289/1329] Perl6 NativeCall bindings --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 837e54f4..0664e35a 100644 --- a/README.md +++ b/README.md @@ -150,6 +150,7 @@ Julia | [Libui.jl](https://github.com/joa-quim/Libui.jl) Kotlin | [kotlin-libui](https://github.com/msink/kotlin-libui) Lua | [libuilua](https://github.com/zevv/libuilua), [libui-lua](https://github.com/mdombroski/libui-lua), [lui](http://tset.de/lui/index.html), [lui](https://github.com/zhaozg/lui) Nim | [ui](https://github.com/nim-lang/ui) +Perl6 | [perl6-libui](https://github.com/Garland-g/perl6-libui) PHP | [ui](https://github.com/krakjoe/ui) Python | [pylibui](https://github.com/joaoventura/pylibui), [pylibui-cffi](https://github.com/Yardanico/pylibui-cffi) Ruby | [libui-ruby](https://github.com/jamescook/libui-ruby) From 30ee36f8d21ab194affe88bffe4d6294923bf23f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 9 Oct 2018 23:39:55 -0400 Subject: [PATCH 1290/1329] More test programs. This isn't done yet, and it's not working right... --- doc/misctests/winrebarexplorertheme.cpp | 178 ++++++++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 doc/misctests/winrebarexplorertheme.cpp diff --git a/doc/misctests/winrebarexplorertheme.cpp b/doc/misctests/winrebarexplorertheme.cpp new file mode 100644 index 00000000..132c9c00 --- /dev/null +++ b/doc/misctests/winrebarexplorertheme.cpp @@ -0,0 +1,178 @@ +// 9 october 2018 +#define UNICODE +#define _UNICODE +#define STRICT +#define STRICT_TYPED_ITEMIDS +#define WINVER 0x0600 /* from Microsoft's winnls.h */ +#define _WIN32_WINNT 0x0600 +#define _WIN32_WINDOWS 0x0600 /* from Microsoft's pdh.h */ +#define _WIN32_IE 0x0700 +#define NTDDI_VERSION 0x06000000 +#include +#include +#include +#include +#include +#include +#include + +void diele(const char *func) +{ + DWORD le; + + le = GetLastError(); + fprintf(stderr, "%s: %I32u\n", func, le); + exit(EXIT_FAILURE); +} + +void diehr(const char *func, HRESULT hr) +{ + fprintf(stderr, "%s: 0x%08I32X\n", func, hr); + exit(EXIT_FAILURE); +} + +HINSTANCE hInstance; +HWND rebar; +HWND leftbar; +HWND rightbar; + +static struct { + const WCHAR *text; + BOOL dropdown; +} leftbarButtons[] = { + { L"Organize", TRUE }, + { L"Include in library", TRUE }, + { L"Share with", TRUE }, + { L"Burn", FALSE }, + { L"New folder", FALSE }, +}; + +void onWM_CREATE(HWND hwnd) +{ + TBBUTTON tbb[5]; + REBARBANDINFOW rbi; + int i; + +#if 0 + rebar = CreateWindowExW(0, + REBARCLASSNAMEW, NULL, + WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CCS_NODIVIDER | CCS_TOP, + 0, 0, 0, 0, + hwnd, (HMENU) 100, hInstance, NULL); + if (rebar == NULL) + diele("CreateWindowExW(REBARCLASSNAMEW)"); +#endif + + leftbar = CreateWindowExW(0, + TOOLBARCLASSNAMEW, NULL, + WS_CHILD | /*CCS_NOPARENTALIGN | CCS_NORESIZE | */TBSTYLE_FLAT | TBSTYLE_LIST | TBSTYLE_TRANSPARENT, + 0, 0, 0, 0, + hwnd, (HMENU) 101, hInstance, NULL); + SendMessageW(leftbar, TB_BUTTONSTRUCTSIZE, sizeof (TBBUTTON), 0); + ZeroMemory(tbb, 5 * sizeof (TBBUTTON)); + for (i = 0; i < 5; i++) { + tbb[i].iBitmap = I_IMAGENONE; + tbb[i].idCommand = i; + tbb[i].fsState = TBSTATE_ENABLED; + tbb[i].fsStyle = BTNS_AUTOSIZE | BTNS_BUTTON | BTNS_NOPREFIX | BTNS_SHOWTEXT; + if (leftbarButtons[i].dropdown) + tbb[i].fsStyle |= BTNS_DROPDOWN | BTNS_WHOLEDROPDOWN; + tbb[i].iString = (INT_PTR) (leftbarButtons[i].text); + } + if (SendMessageW(leftbar, TB_ADDBUTTONS, 5, (LPARAM) tbb) == FALSE) + diele("TB_ADDBUTTONS"); + +#if 0 + ZeroMemory(&rbi, sizeof (REBARBANDINFOW)); + rbi.cbSize = sizeof (REBARBANDINFOW); + rbi.fMask = RBBIM_CHILD | RBBIM_STYLE; + rbi.fStyle = RBBS_NOGRIPPER | RBBS_USECHEVRON | RBBS_HIDETITLE; + rbi.hwndChild = leftbar; + if (SendMessageW(rebar, RB_INSERTBANDW, (WPARAM) (-1), (LPARAM) (&rbi)) == 0) + diele("RB_INSERTBANDW leftbar"); +#endif + SendMessageW(leftbar, TB_AUTOSIZE, 0, 0); + ShowWindow(leftbar, SW_SHOW); +} + +LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) { + case WM_CREATE: + onWM_CREATE(hwnd); + break; + case WM_CLOSE: + PostQuitMessage(0); + break; + } + return DefWindowProcW(hwnd, uMsg, wParam, lParam); +} + +EXTERN_C IMAGE_DOS_HEADER __ImageBase; + +int main(void) +{ + STARTUPINFOW si; + int nCmdShow; + INITCOMMONCONTROLSEX icc; + HICON hDefaultIcon; + HCURSOR hDefaultCursor; + WNDCLASSW wc; + HWND mainwin; + MSG msg; + + hInstance = (HINSTANCE) (&__ImageBase); + nCmdShow = SW_SHOWDEFAULT; + GetStartupInfoW(&si); + if ((si.dwFlags & STARTF_USESHOWWINDOW) != 0) + nCmdShow = si.wShowWindow; + + ZeroMemory(&icc, sizeof (INITCOMMONCONTROLSEX)); + icc.dwSize = sizeof (INITCOMMONCONTROLSEX); + icc.dwICC = ICC_STANDARD_CLASSES | ICC_BAR_CLASSES | ICC_COOL_CLASSES; + if (InitCommonControlsEx(&icc) == 0) + diele("InitCommonControlsEx()"); + + hDefaultIcon = LoadIconW(NULL, IDI_APPLICATION); + if (hDefaultIcon == NULL) + diele("LoadIconW(IDI_APPLICATION)"); + hDefaultCursor = LoadCursorW(NULL, IDC_ARROW); + if (hDefaultCursor == NULL) + diele("LoadCursorW(IDC_ARROW)"); + + ZeroMemory(&wc, sizeof (WNDCLASSW)); + wc.lpszClassName = L"mainwin"; + wc.lpfnWndProc = wndproc; + wc.hInstance = hInstance; + wc.hIcon = hDefaultIcon; + wc.hCursor = hDefaultCursor; + wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1); + if (RegisterClassW(&wc) == 0) + diele("RegisterClassW()"); + + mainwin = CreateWindowExW(0, + L"mainwin", L"Main Window", + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + NULL, NULL, hInstance, NULL); + if (mainwin == NULL) + diele("CreateWindowExW(L\"mainwin\")"); + + ShowWindow(mainwin, nCmdShow); + if (UpdateWindow(mainwin) == 0) + diele("UpdateWindow()"); + + for (;;) { + int res; + + res = GetMessageW(&msg, NULL, 0, 0); + if (res < 0) + diele("GetMessageW()"); + if (res == 0) + break; + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + return 0; +} From fa4f5d78cbcd001a50d7a9ce3b7431d22459b56e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 10 Oct 2018 22:13:05 -0400 Subject: [PATCH 1291/1329] More notes. --- _notes/rebarstuff | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 _notes/rebarstuff diff --git a/_notes/rebarstuff b/_notes/rebarstuff new file mode 100644 index 00000000..e2675b17 --- /dev/null +++ b/_notes/rebarstuff @@ -0,0 +1,9 @@ +https://docs.microsoft.com/en-us/windows/desktop/uxguide/cmd-toolbars +https://docs.microsoft.com/en-us/windows/desktop/controls/cc-faq-iemenubar +https://docs.microsoft.com/en-us/windows/desktop/controls/cc-faq-ietoolbar +https://docs.microsoft.com/en-us/windows/desktop/controls/create-rebar-controls +https://docs.microsoft.com/en-us/windows/desktop/controls/create-toolbars +https://docs.microsoft.com/en-us/windows/desktop/controls/handle-drop-down-buttons +https://docs.microsoft.com/en-us/windows/desktop/controls/tb-buttonstructsize + https://www.google.com/search?q=winapi+toolbar+dropdown+position&ie=utf-8&oe=utf-8&client=firefox-b-1 +https://www.google.com/search?q=winapi+toolbar+dropdown+arrow+position&ie=utf-8&oe=utf-8&client=firefox-b-1 From 97b11e027de1c14c6dfb2f82a06753b048486bf4 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 11 Oct 2018 04:10:36 -0400 Subject: [PATCH 1292/1329] Refined the rebar test some more. --- doc/misctests/winrebarexplorertheme.cpp | 36 +++++++++++++++++-------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/doc/misctests/winrebarexplorertheme.cpp b/doc/misctests/winrebarexplorertheme.cpp index 132c9c00..36ed7de2 100644 --- a/doc/misctests/winrebarexplorertheme.cpp +++ b/doc/misctests/winrebarexplorertheme.cpp @@ -50,10 +50,12 @@ static struct { void onWM_CREATE(HWND hwnd) { TBBUTTON tbb[5]; + RECT btnrect; + DWORD tbbtnsize; + LONG tbsizex, tbsizey; REBARBANDINFOW rbi; int i; -#if 0 rebar = CreateWindowExW(0, REBARCLASSNAMEW, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CCS_NODIVIDER | CCS_TOP, @@ -61,17 +63,20 @@ void onWM_CREATE(HWND hwnd) hwnd, (HMENU) 100, hInstance, NULL); if (rebar == NULL) diele("CreateWindowExW(REBARCLASSNAMEW)"); -#endif leftbar = CreateWindowExW(0, TOOLBARCLASSNAMEW, NULL, - WS_CHILD | /*CCS_NOPARENTALIGN | CCS_NORESIZE | */TBSTYLE_FLAT | TBSTYLE_LIST | TBSTYLE_TRANSPARENT, + WS_CHILD | CCS_NODIVIDER | CCS_NOPARENTALIGN | CCS_NORESIZE | TBSTYLE_FLAT | TBSTYLE_LIST | TBSTYLE_TRANSPARENT, 0, 0, 0, 0, hwnd, (HMENU) 101, hInstance, NULL); SendMessageW(leftbar, TB_BUTTONSTRUCTSIZE, sizeof (TBBUTTON), 0); + // I_IMAGENONE causes the button text to be left-aligned; don't use it + if (SendMessageW(leftbar, TB_SETBITMAPSIZE, 0, 0) == FALSE) + diele("TB_SETBITMAPSIZE"); + SendMessageW(leftbar, TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_DRAWDDARROWS | TBSTYLE_EX_HIDECLIPPEDBUTTONS | TBSTYLE_EX_MIXEDBUTTONS); ZeroMemory(tbb, 5 * sizeof (TBBUTTON)); for (i = 0; i < 5; i++) { - tbb[i].iBitmap = I_IMAGENONE; + tbb[i].iBitmap = 0; tbb[i].idCommand = i; tbb[i].fsState = TBSTATE_ENABLED; tbb[i].fsStyle = BTNS_AUTOSIZE | BTNS_BUTTON | BTNS_NOPREFIX | BTNS_SHOWTEXT; @@ -79,20 +84,29 @@ void onWM_CREATE(HWND hwnd) tbb[i].fsStyle |= BTNS_DROPDOWN | BTNS_WHOLEDROPDOWN; tbb[i].iString = (INT_PTR) (leftbarButtons[i].text); } - if (SendMessageW(leftbar, TB_ADDBUTTONS, 5, (LPARAM) tbb) == FALSE) - diele("TB_ADDBUTTONS"); + if (SendMessageW(leftbar, TB_ADDBUTTONSW, 5, (LPARAM) tbb) == FALSE) + diele("TB_ADDBUTTONSW"); + + tbsizex = 0; + for (i = 0; i < 5; i++) { + if (SendMessageW(leftbar, TB_GETITEMRECT, (WPARAM) i, (LPARAM) (&btnrect)) == FALSE) + diele("TB_GETITEMRECT"); + tbsizex += btnrect.right - btnrect.left; + } + tbbtnsize = (DWORD) SendMessageW(leftbar, TB_GETBUTTONSIZE, 0, 0); + tbsizey = HIWORD(tbbtnsize); -#if 0 ZeroMemory(&rbi, sizeof (REBARBANDINFOW)); rbi.cbSize = sizeof (REBARBANDINFOW); - rbi.fMask = RBBIM_CHILD | RBBIM_STYLE; + rbi.fMask = RBBIM_CHILD | RBBIM_STYLE | RBBIM_SIZE | RBBIM_CHILDSIZE; rbi.fStyle = RBBS_NOGRIPPER | RBBS_USECHEVRON | RBBS_HIDETITLE; rbi.hwndChild = leftbar; + rbi.cx = tbsizex; + rbi.cyChild = tbsizey; + rbi.cxMinChild = 0; + rbi.cyMinChild = tbsizey; if (SendMessageW(rebar, RB_INSERTBANDW, (WPARAM) (-1), (LPARAM) (&rbi)) == 0) diele("RB_INSERTBANDW leftbar"); -#endif - SendMessageW(leftbar, TB_AUTOSIZE, 0, 0); - ShowWindow(leftbar, SW_SHOW); } LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) From 91fe6e7c47856f81fd3b1c4abeb262ffa701dec3 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 11 Oct 2018 05:35:06 -0400 Subject: [PATCH 1293/1329] More rebar refinements. --- doc/misctests/winrebarexplorertheme.cpp | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/doc/misctests/winrebarexplorertheme.cpp b/doc/misctests/winrebarexplorertheme.cpp index 36ed7de2..d86ec1bd 100644 --- a/doc/misctests/winrebarexplorertheme.cpp +++ b/doc/misctests/winrebarexplorertheme.cpp @@ -74,6 +74,9 @@ void onWM_CREATE(HWND hwnd) if (SendMessageW(leftbar, TB_SETBITMAPSIZE, 0, 0) == FALSE) diele("TB_SETBITMAPSIZE"); SendMessageW(leftbar, TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_DRAWDDARROWS | TBSTYLE_EX_HIDECLIPPEDBUTTONS | TBSTYLE_EX_MIXEDBUTTONS); + // TODO this *should* be DIPs... + // TODO figure out where the *2 is documented + SendMessageW(leftbar, TB_SETPADDING, 0, MAKELONG(6 * 2, 5 * 2)); ZeroMemory(tbb, 5 * sizeof (TBBUTTON)); for (i = 0; i < 5; i++) { tbb[i].iBitmap = 0; @@ -98,17 +101,34 @@ void onWM_CREATE(HWND hwnd) ZeroMemory(&rbi, sizeof (REBARBANDINFOW)); rbi.cbSize = sizeof (REBARBANDINFOW); - rbi.fMask = RBBIM_CHILD | RBBIM_STYLE | RBBIM_SIZE | RBBIM_CHILDSIZE; + rbi.fMask = RBBIM_CHILD | RBBIM_STYLE | RBBIM_SIZE | RBBIM_CHILDSIZE | RBBIM_IDEALSIZE; rbi.fStyle = RBBS_NOGRIPPER | RBBS_USECHEVRON | RBBS_HIDETITLE; rbi.hwndChild = leftbar; rbi.cx = tbsizex; rbi.cyChild = tbsizey; rbi.cxMinChild = 0; rbi.cyMinChild = tbsizey; + rbi.cxIdeal = tbsizex; if (SendMessageW(rebar, RB_INSERTBANDW, (WPARAM) (-1), (LPARAM) (&rbi)) == 0) diele("RB_INSERTBANDW leftbar"); } +// TODO it seems like I shouldn't have to do this? +void repositionRebar(HWND hwnd) +{ + RECT win, rb; + + if (GetClientRect(hwnd, &win) == 0) + diele("GetClientRect()"); + if (GetWindowRect(rebar, &rb) == 0) + diele("GetWindowRect()"); + if (SetWindowPos(rebar, NULL, + 0, 0, win.right - win.left, rb.bottom - rb.top, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER) == 0) + diele("SetWindowPos()"); + SendMessageW(rebar, RB_SETBANDWIDTH, 0, win.right - win.left); +} + LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { @@ -118,6 +138,9 @@ LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) case WM_CLOSE: PostQuitMessage(0); break; + case WM_SIZE: + repositionRebar(hwnd); + break; } return DefWindowProcW(hwnd, uMsg, wParam, lParam); } From d082b4efa48254efdca7e87c3b4723e985fa0790 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 11 Oct 2018 21:03:34 -0400 Subject: [PATCH 1294/1329] More work on winrebarexplorertheme.cpp. It registers themes. --- doc/misctests/winrebarexplorertheme.cpp | 123 ++++++++++++++++++++++++ 1 file changed, 123 insertions(+) diff --git a/doc/misctests/winrebarexplorertheme.cpp b/doc/misctests/winrebarexplorertheme.cpp index d86ec1bd..d2e275ae 100644 --- a/doc/misctests/winrebarexplorertheme.cpp +++ b/doc/misctests/winrebarexplorertheme.cpp @@ -35,6 +35,7 @@ HINSTANCE hInstance; HWND rebar; HWND leftbar; HWND rightbar; +HWND rebarCombo; static struct { const WCHAR *text; @@ -47,6 +48,43 @@ static struct { { L"New folder", FALSE }, }; +static const WCHAR *buttons[] = { + L"SetWindowTheme()", + L"Custom Draw Vista", + L"Custom Draw 7", + NULL, +}; + +static const WCHAR *rebarThemes[] = { + L"NULL", + L"", + L"AlternateRebar", + L"BrowserTabBar", + L"Communications", + L"Default", + L"ExplorerBar", + L"Help", + L"InactiveNavbar", + L"InactiveNavbarComposited", + L"ITBarBase", + L"MaxInactiveNavbar", + L"MaxInactiveNavbarComposited", + L"MaxNavbar", + L"MaxNavbarComposited", + L"Media", + L"Navbar", + L"NavbarBase", + L"NavbarComposited", + L"NavbarNonTopmost", + L"Rebar", + L"RebarStyle", + L"TaskBar", + L"TaskBarComposited", + NULL, +}; + +// TODO toolbarThemes + void onWM_CREATE(HWND hwnd) { TBBUTTON tbb[5]; @@ -54,6 +92,9 @@ void onWM_CREATE(HWND hwnd) DWORD tbbtnsize; LONG tbsizex, tbsizey; REBARBANDINFOW rbi; + HWND button; + LONG buttonx, buttony; + LONG combox, comboy; int i; rebar = CreateWindowExW(0, @@ -111,6 +152,37 @@ void onWM_CREATE(HWND hwnd) rbi.cxIdeal = tbsizex; if (SendMessageW(rebar, RB_INSERTBANDW, (WPARAM) (-1), (LPARAM) (&rbi)) == 0) diele("RB_INSERTBANDW leftbar"); + + buttonx = 10; + buttony = 40; +#define buttonwid 200 +#define buttonht 25 + for (i = 0; buttons[i] != NULL; i++) { + button = CreateWindowExW(0, + L"BUTTON", buttons[i], + WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, + buttonx, buttony, + buttonwid, buttonht, + hwnd, (HMENU) (200 + i), hInstance, NULL); + if (button == NULL) + diele("CreateWIndowExW(L\"BUTTON\")"); + if (i == 0) { + combox = buttonx + buttonwid + 5; + comboy = buttony; + } + buttony += buttonht + 5; + } + rebarCombo = CreateWindowExW(WS_EX_CLIENTEDGE, + L"COMBOBOX", L"", + WS_CHILD | WS_VISIBLE | CBS_DROPDOWN, + combox, comboy, + buttonwid, buttonht, + hwnd, (HMENU) 300, hInstance, NULL); + if (rebarCombo == NULL) + diele("CreateWindowExW(L\"COMBOBOX\")"); + for (i = 0; rebarThemes[i] != NULL; i++) + // TODO check error + SendMessageW(rebarCombo, CB_ADDSTRING, 0, (LPARAM) (rebarThemes[i])); } // TODO it seems like I shouldn't have to do this? @@ -129,6 +201,54 @@ void repositionRebar(HWND hwnd) SendMessageW(rebar, RB_SETBANDWIDTH, 0, win.right - win.left); } +// TODO check errors +void handleEvents(HWND hwnd, WPARAM wParam) +{ + LRESULT n; + const WCHAR *selRebar = NULL, *selToolbar = NULL; + WCHAR *bufRebar = NULL, *bufToolbar = NULL; + BOOL changeRebar = FALSE, changeToolbar = FALSE; + + switch (wParam) { + case MAKEWPARAM(300, CBN_SELCHANGE): + n = SendMessageW(rebarCombo, CB_GETCURSEL, 0, 0); + selRebar = rebarThemes[n]; + changeRebar = TRUE; + break; + case MAKEWPARAM(200, BN_CLICKED): + n = SendMessageW(rebarCombo, WM_GETTEXTLENGTH, 0, 0); + bufRebar = new WCHAR[n + 1]; + GetWindowTextW(rebarCombo, bufRebar, n + 1); + selRebar = bufRebar; + selToolbar = bufRebar; + changeRebar = TRUE; + changeToolbar = TRUE; + break; + } + if (changeRebar) { + if (selRebar != NULL && wcscmp(selRebar, L"NULL") == 0) + selRebar = NULL; + if (selRebar != NULL && *selRebar != L'\0') + SendMessageW(rebar, RB_SETWINDOWTHEME, 0, (LPARAM) selRebar); + else + SetWindowTheme(rebar, selRebar, selRebar); + InvalidateRect(hwnd, NULL, TRUE); + } + if (changeToolbar) { + if (selToolbar != NULL && wcscmp(selToolbar, L"NULL") == 0) + selToolbar = NULL; + if (selToolbar != NULL && *selToolbar != L'\0') + SendMessageW(leftbar, TB_SETWINDOWTHEME, 0, (LPARAM) selToolbar); + else + SetWindowTheme(leftbar, selToolbar, selToolbar); + InvalidateRect(hwnd, NULL, TRUE); + } + if (bufRebar != NULL) + delete[] bufRebar; + if (bufToolbar != NULL) + delete[] bufToolbar; +} + LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { @@ -141,6 +261,9 @@ LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) case WM_SIZE: repositionRebar(hwnd); break; + case WM_COMMAND: + handleEvents(hwnd, wParam); + break; } return DefWindowProcW(hwnd, uMsg, wParam, lParam); } From 6a22c61cd40df3261d5aef8e0371f3c1b797abc4 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 11 Oct 2018 22:19:10 -0400 Subject: [PATCH 1295/1329] More work on winrebarexplorertheme.cpp, including the boilerplate for custom draw. More TODOs in general. --- doc/misctests/winrebarexplorertheme.cpp | 149 ++++++++++++++++++++++-- windows/editablecombo.cpp | 2 + 2 files changed, 140 insertions(+), 11 deletions(-) diff --git a/doc/misctests/winrebarexplorertheme.cpp b/doc/misctests/winrebarexplorertheme.cpp index d2e275ae..199e3e97 100644 --- a/doc/misctests/winrebarexplorertheme.cpp +++ b/doc/misctests/winrebarexplorertheme.cpp @@ -36,6 +36,10 @@ HWND rebar; HWND leftbar; HWND rightbar; HWND rebarCombo; +HWND toolbarCombo; +HWND toolbarTransparentCheckbox; + +#define toolbarStyles (WS_CHILD | CCS_NODIVIDER | CCS_NOPARENTALIGN | CCS_NORESIZE | TBSTYLE_FLAT | TBSTYLE_LIST) static struct { const WCHAR *text; @@ -48,13 +52,28 @@ static struct { { L"New folder", FALSE }, }; -static const WCHAR *buttons[] = { - L"SetWindowTheme()", - L"Custom Draw Vista", - L"Custom Draw 7", - NULL, +LRESULT customDrawVista(NMCUSTOMDRAW *nm) +{ + return CDRF_DODEFAULT; +} + +LRESULT customDraw7(NMCUSTOMDRAW *nm) +{ + return CDRF_DODEFAULT; +} + +static struct { + const WCHAR *text; + LRESULT (*handle)(NMCUSTOMDRAW *nm); +} drawmodes[] = { + { L"SetWindowTheme()", NULL }, + { L"Custom Draw Vista", customDrawVista }, + { L"Custom Draw 7", customDraw7 }, + { NULL, NULL }, }; +int drawmode = 0; + static const WCHAR *rebarThemes[] = { L"NULL", L"", @@ -83,6 +102,47 @@ static const WCHAR *rebarThemes[] = { NULL, }; +static WCHAR *toolbarThemes[] = { + L"NULL", + L"", + L"Alternate", + L"BB", + L"BBComposited", + L"Communications", + L"ExplorerMenu", + L"Go", + L"GoComposited", + L"InactiveBB", + L"InactiveBBComposited", + L"InactiveGo", + L"InactiveGoComposited", + L"InfoPaneToolbar", + L"LVPopup", + L"LVPopupBottom", + L"MaxBB", + L"MaxBBComposited", + L"MaxGo", + L"MaxGoComposited", + L"MaxInactiveBB", + L"MaxInactiveBBComposited", + L"MaxInactiveGo", + L"MaxInactiveGoComposited", + L"Media", + L"Placesbar", + L"SearchButton", + L"SearchButtonComposited", + L"StartMenu", + L"TaskBar", + L"TaskBarComposited", + L"TaskBarVert", + L"TaskBarVertComposited", + L"Toolbar", + L"ToolbarStyle", + L"TrayNotify", + L"TrayNotifyComposited", + NULL, +}; + // TODO toolbarThemes void onWM_CREATE(HWND hwnd) @@ -107,7 +167,7 @@ void onWM_CREATE(HWND hwnd) leftbar = CreateWindowExW(0, TOOLBARCLASSNAMEW, NULL, - WS_CHILD | CCS_NODIVIDER | CCS_NOPARENTALIGN | CCS_NORESIZE | TBSTYLE_FLAT | TBSTYLE_LIST | TBSTYLE_TRANSPARENT, + toolbarStyles | TBSTYLE_TRANSPARENT, 0, 0, 0, 0, hwnd, (HMENU) 101, hInstance, NULL); SendMessageW(leftbar, TB_BUTTONSTRUCTSIZE, sizeof (TBBUTTON), 0); @@ -157,9 +217,9 @@ void onWM_CREATE(HWND hwnd) buttony = 40; #define buttonwid 200 #define buttonht 25 - for (i = 0; buttons[i] != NULL; i++) { + for (i = 0; drawmodes[i].text != NULL; i++) { button = CreateWindowExW(0, - L"BUTTON", buttons[i], + L"BUTTON", drawmodes[i].text, WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, buttonx, buttony, buttonwid, buttonht, @@ -183,6 +243,28 @@ void onWM_CREATE(HWND hwnd) for (i = 0; rebarThemes[i] != NULL; i++) // TODO check error SendMessageW(rebarCombo, CB_ADDSTRING, 0, (LPARAM) (rebarThemes[i])); + comboy += buttonht + 5; + toolbarCombo = CreateWindowExW(WS_EX_CLIENTEDGE, + L"COMBOBOX", L"", + WS_CHILD | WS_VISIBLE | CBS_DROPDOWN, + combox, comboy, + buttonwid, buttonht, + hwnd, (HMENU) 301, hInstance, NULL); + if (toolbarCombo == NULL) + diele("CreateWindowExW(L\"COMBOBOX\")"); + for (i = 0; toolbarThemes[i] != NULL; i++) + // TODO check error + SendMessageW(toolbarCombo, CB_ADDSTRING, 0, (LPARAM) (toolbarThemes[i])); + comboy += buttonht + 5; + toolbarTransparentCheckbox = CreateWindowExW(0, + L"BUTTON", L"Transparent toolbar", + WS_CHILD | WS_VISIBLE | BS_CHECKBOX, + combox, comboy, + buttonwid, buttonht, + hwnd, (HMENU) 302, hInstance, NULL); + if (toolbarTransparentCheckbox == NULL) + diele("CreateWindowExW(L\"BUTTON\")"); + SendMessage(toolbarTransparentCheckbox, BM_SETCHECK, BST_CHECKED, 0); } // TODO it seems like I shouldn't have to do this? @@ -208,6 +290,8 @@ void handleEvents(HWND hwnd, WPARAM wParam) const WCHAR *selRebar = NULL, *selToolbar = NULL; WCHAR *bufRebar = NULL, *bufToolbar = NULL; BOOL changeRebar = FALSE, changeToolbar = FALSE; + BOOL invalidate = FALSE; + WPARAM check; switch (wParam) { case MAKEWPARAM(300, CBN_SELCHANGE): @@ -215,15 +299,44 @@ void handleEvents(HWND hwnd, WPARAM wParam) selRebar = rebarThemes[n]; changeRebar = TRUE; break; + case MAKEWPARAM(301, CBN_SELCHANGE): + n = SendMessageW(toolbarCombo, CB_GETCURSEL, 0, 0); + selToolbar = toolbarThemes[n]; + changeToolbar = TRUE; + break; case MAKEWPARAM(200, BN_CLICKED): + drawmode = 0; n = SendMessageW(rebarCombo, WM_GETTEXTLENGTH, 0, 0); bufRebar = new WCHAR[n + 1]; GetWindowTextW(rebarCombo, bufRebar, n + 1); + n = SendMessageW(toolbarCombo, WM_GETTEXTLENGTH, 0, 0); + bufToolbar = new WCHAR[n + 1]; + GetWindowTextW(toolbarCombo, bufToolbar, n + 1); selRebar = bufRebar; - selToolbar = bufRebar; + selToolbar = bufToolbar; changeRebar = TRUE; changeToolbar = TRUE; break; + case MAKEWPARAM(201, BN_CLICKED): + drawmode = 1; + invalidate = TRUE; + break; + case MAKEWPARAM(202, BN_CLICKED): + drawmode = 2; + invalidate = TRUE; + break; + case MAKEWPARAM(302, BN_CLICKED): + ShowWindow(leftbar, SW_HIDE); + check = BST_CHECKED; + if (SendMessage(toolbarTransparentCheckbox, BM_GETCHECK, 0, 0) == BST_CHECKED) + check = BST_UNCHECKED; + SendMessage(toolbarTransparentCheckbox, BM_SETCHECK, check, 0); + if (check == BST_CHECKED) + SendMessageW(leftbar, TB_SETSTYLE, 0, toolbarStyles | TBSTYLE_TRANSPARENT); + else + SendMessageW(leftbar, TB_SETSTYLE, 0, toolbarStyles); + ShowWindow(leftbar, SW_SHOW); + break; } if (changeRebar) { if (selRebar != NULL && wcscmp(selRebar, L"NULL") == 0) @@ -232,7 +345,7 @@ void handleEvents(HWND hwnd, WPARAM wParam) SendMessageW(rebar, RB_SETWINDOWTHEME, 0, (LPARAM) selRebar); else SetWindowTheme(rebar, selRebar, selRebar); - InvalidateRect(hwnd, NULL, TRUE); + invalidate = TRUE; } if (changeToolbar) { if (selToolbar != NULL && wcscmp(selToolbar, L"NULL") == 0) @@ -241,8 +354,10 @@ void handleEvents(HWND hwnd, WPARAM wParam) SendMessageW(leftbar, TB_SETWINDOWTHEME, 0, (LPARAM) selToolbar); else SetWindowTheme(leftbar, selToolbar, selToolbar); - InvalidateRect(hwnd, NULL, TRUE); + invalidate = TRUE; } + if (invalidate) + InvalidateRect(hwnd, NULL, TRUE); if (bufRebar != NULL) delete[] bufRebar; if (bufToolbar != NULL) @@ -251,6 +366,8 @@ void handleEvents(HWND hwnd, WPARAM wParam) LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + NMHDR *nm = (NMHDR *) lParam; + switch (uMsg) { case WM_CREATE: onWM_CREATE(hwnd); @@ -264,6 +381,16 @@ LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) case WM_COMMAND: handleEvents(hwnd, wParam); break; + case WM_NOTIFY: + switch (nm->code) { + case NM_CUSTOMDRAW: + if (nm->hwndFrom != rebar) + break; + if (drawmode == 0) + break; + return (*(drawmodes[drawmode].handle))((NMCUSTOMDRAW *) nm); + } + break; } return DefWindowProcW(hwnd, uMsg, wParam, lParam); } diff --git a/windows/editablecombo.cpp b/windows/editablecombo.cpp index 138618d7..f1831bb6 100644 --- a/windows/editablecombo.cpp +++ b/windows/editablecombo.cpp @@ -1,6 +1,8 @@ // 20 may 2015 #include "uipriv_windows.hpp" +// TODO no scrollbars? also not sure if true for combobox as well + // we as Common Controls 6 users don't need to worry about the height of comboboxes; see http://blogs.msdn.com/b/oldnewthing/archive/2006/03/10/548537.aspx struct uiEditableCombobox { From 87d3fc5064502d612080cc6ec6da7c842e56716e Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 11 Oct 2018 22:49:37 -0400 Subject: [PATCH 1296/1329] And started the custom draw code. --- doc/misctests/winrebarexplorertheme.cpp | 32 +++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/doc/misctests/winrebarexplorertheme.cpp b/doc/misctests/winrebarexplorertheme.cpp index 199e3e97..e978f964 100644 --- a/doc/misctests/winrebarexplorertheme.cpp +++ b/doc/misctests/winrebarexplorertheme.cpp @@ -52,9 +52,37 @@ static struct { { L"New folder", FALSE }, }; +// TODO check errors LRESULT customDrawVista(NMCUSTOMDRAW *nm) { - return CDRF_DODEFAULT; + static TRIVERTEX vertices[] = { + { 0, 0, 4 << 8, 80 << 8, 130 << 8, 255 << 8 }, + { 0, 0, 17 << 8, 101 << 8, 132 << 8, 255 << 8 }, + { 0, 0, 17 << 8, 101 << 8, 132 << 8, 255 << 8 }, + { 0, 0, 29 << 8, 121 << 8, 134 << 8, 255 << 8 }, + }; + static GRADIENT_RECT gr[2] = { + { 0, 1 }, + { 2, 3 }, + }; + RECT r; + HTHEME theme; + + if (nm->dwDrawStage != CDDS_PREPAINT) + return CDRF_DODEFAULT; + GetClientRect(nm->hdr.hwndFrom, &r); + vertices[1].x = r.right - r.left; + vertices[1].y = (r.bottom - r.top) / 2; + vertices[2].y = (r.bottom - r.top) / 2; + vertices[3].x = r.right - r.left; + vertices[3].y = r.bottom - r.top; + GradientFill(nm->hdc, vertices, 4, (PVOID) gr, 2, GRADIENT_FILL_RECT_V); + theme = OpenThemeData(nm->hdr.hwndFrom, L"CommandModule"); + DrawThemeBackground(theme, nm->hdc, + 1, 0, + &r, NULL); + CloseThemeData(theme); + return CDRF_NOTIFYITEMDRAW; } LRESULT customDraw7(NMCUSTOMDRAW *nm) @@ -384,7 +412,7 @@ LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) case WM_NOTIFY: switch (nm->code) { case NM_CUSTOMDRAW: - if (nm->hwndFrom != rebar) + if (nm->hwndFrom != leftbar) break; if (drawmode == 0) break; From a5aa4624df7e7f7c3fca01f729e837ff2a374fdc Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 12 Oct 2018 20:56:53 -0400 Subject: [PATCH 1297/1329] More work. We're definitely building up to something... --- doc/misctests/winrebarexplorertheme.cpp | 27 ++++++++++++++----------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/doc/misctests/winrebarexplorertheme.cpp b/doc/misctests/winrebarexplorertheme.cpp index e978f964..04169314 100644 --- a/doc/misctests/winrebarexplorertheme.cpp +++ b/doc/misctests/winrebarexplorertheme.cpp @@ -16,6 +16,8 @@ #include #include +// cl winrebarexplorertheme.cpp -MD -link user32.lib kernel32.lib gdi32.lib comctl32.lib uxtheme.lib msimg32.lib windows.res + void diele(const char *func) { DWORD le; @@ -53,7 +55,8 @@ static struct { }; // TODO check errors -LRESULT customDrawVista(NMCUSTOMDRAW *nm) +// TODO extract colors from the theme +LRESULT customDrawExplorer(NMCUSTOMDRAW *nm) { static TRIVERTEX vertices[] = { { 0, 0, 4 << 8, 80 << 8, 130 << 8, 255 << 8 }, @@ -69,19 +72,24 @@ LRESULT customDrawVista(NMCUSTOMDRAW *nm) HTHEME theme; if (nm->dwDrawStage != CDDS_PREPAINT) - return CDRF_DODEFAULT; + return CDRF_DODEFAULT | TBCDRF_NOOFFSET; + if (nm->hdr.hwndFrom == rebar) { GetClientRect(nm->hdr.hwndFrom, &r); - vertices[1].x = r.right - r.left; + vertices[0].x = nm->rc.left; + vertices[0].y = 0; + vertices[1].x = nm->rc.right; vertices[1].y = (r.bottom - r.top) / 2; + vertices[2].x = nm->rc.left; vertices[2].y = (r.bottom - r.top) / 2; - vertices[3].x = r.right - r.left; + vertices[3].x = nm->rc.right; vertices[3].y = r.bottom - r.top; GradientFill(nm->hdc, vertices, 4, (PVOID) gr, 2, GRADIENT_FILL_RECT_V); theme = OpenThemeData(nm->hdr.hwndFrom, L"CommandModule"); DrawThemeBackground(theme, nm->hdc, 1, 0, - &r, NULL); + &r, &(nm->rc)); CloseThemeData(theme); + } return CDRF_NOTIFYITEMDRAW; } @@ -95,8 +103,7 @@ static struct { LRESULT (*handle)(NMCUSTOMDRAW *nm); } drawmodes[] = { { L"SetWindowTheme()", NULL }, - { L"Custom Draw Vista", customDrawVista }, - { L"Custom Draw 7", customDraw7 }, + { L"Custom Draw Explorer", customDrawExplorer }, { NULL, NULL }, }; @@ -349,10 +356,6 @@ void handleEvents(HWND hwnd, WPARAM wParam) drawmode = 1; invalidate = TRUE; break; - case MAKEWPARAM(202, BN_CLICKED): - drawmode = 2; - invalidate = TRUE; - break; case MAKEWPARAM(302, BN_CLICKED): ShowWindow(leftbar, SW_HIDE); check = BST_CHECKED; @@ -412,7 +415,7 @@ LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) case WM_NOTIFY: switch (nm->code) { case NM_CUSTOMDRAW: - if (nm->hwndFrom != leftbar) + if (nm->hwndFrom != rebar) break; if (drawmode == 0) break; From fc2ef2c904351fb364284841ae1cd3ffe36aefc7 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 12 Oct 2018 22:45:37 -0400 Subject: [PATCH 1298/1329] More work. This is starting to get twisted. --- doc/misctests/winrebarexplorertheme.cpp | 53 ++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/doc/misctests/winrebarexplorertheme.cpp b/doc/misctests/winrebarexplorertheme.cpp index 04169314..90874354 100644 --- a/doc/misctests/winrebarexplorertheme.cpp +++ b/doc/misctests/winrebarexplorertheme.cpp @@ -41,6 +41,9 @@ HWND rebarCombo; HWND toolbarCombo; HWND toolbarTransparentCheckbox; +HICON helpIcon; +HIMAGELIST helpList; + #define toolbarStyles (WS_CHILD | CCS_NODIVIDER | CCS_NOPARENTALIGN | CCS_NORESIZE | TBSTYLE_FLAT | TBSTYLE_LIST) static struct { @@ -205,6 +208,8 @@ void onWM_CREATE(HWND hwnd) toolbarStyles | TBSTYLE_TRANSPARENT, 0, 0, 0, 0, hwnd, (HMENU) 101, hInstance, NULL); + if (leftbar == NULL) + diele("CreateWindowExW(TOOLBARCLASSNAMEW) leftbar"); SendMessageW(leftbar, TB_BUTTONSTRUCTSIZE, sizeof (TBBUTTON), 0); // I_IMAGENONE causes the button text to be left-aligned; don't use it if (SendMessageW(leftbar, TB_SETBITMAPSIZE, 0, 0) == FALSE) @@ -245,8 +250,43 @@ void onWM_CREATE(HWND hwnd) rbi.cxMinChild = 0; rbi.cyMinChild = tbsizey; rbi.cxIdeal = tbsizex; +// if (SendMessageW(rebar, RB_INSERTBANDW, (WPARAM) (-1), (LPARAM) (&rbi)) == 0) +// diele("RB_INSERTBANDW leftbar"); + + rightbar = CreateWindowExW(0, + TOOLBARCLASSNAMEW, NULL, + (toolbarStyles & ~TBSTYLE_LIST) | TBSTYLE_TRANSPARENT, + 0, 0, 0, 0, + hwnd, (HMENU) 102, hInstance, NULL); + if (rightbar == NULL) + diele("CreateWindowExW(TOOLBARCLASSNAMEW) rightbar"); + SendMessageW(rightbar, TB_BUTTONSTRUCTSIZE, sizeof (TBBUTTON), 0); + // TODO check error + SendMessageW(rightbar, TB_SETBITMAPSIZE, 0, MAKELPARAM(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON))); + SendMessageW(rightbar, TB_SETIMAGELIST, 0, (LPARAM) helpList); + ZeroMemory(tbb, 5 * sizeof (TBBUTTON)); + tbb[0].iBitmap = 0; + tbb[0].idCommand = 0; + tbb[0].fsState = TBSTATE_ENABLED; + tbb[0].fsStyle = BTNS_BUTTON; + if (SendMessageW(rightbar, TB_ADDBUTTONSW, 1, (LPARAM) tbb) == FALSE) + diele("TB_ADDBUTTONSW"); + + tbbtnsize = (DWORD) SendMessageW(rightbar, TB_GETBUTTONSIZE, 0, 0); + tbsizex = LOWORD(tbbtnsize); + tbsizey = HIWORD(tbbtnsize); + + ZeroMemory(&rbi, sizeof (REBARBANDINFOW)); + rbi.cbSize = sizeof (REBARBANDINFOW); + rbi.fMask = RBBIM_CHILD | RBBIM_STYLE | RBBIM_SIZE | RBBIM_CHILDSIZE; + rbi.fStyle = RBBS_NOGRIPPER | RBBS_HIDETITLE; + rbi.hwndChild = rightbar; + rbi.cx = tbsizex; + rbi.cyChild = tbsizey; + rbi.cxMinChild = tbsizex; + rbi.cyMinChild = tbsizey; if (SendMessageW(rebar, RB_INSERTBANDW, (WPARAM) (-1), (LPARAM) (&rbi)) == 0) - diele("RB_INSERTBANDW leftbar"); + diele("RB_INSERTBANDW rightbar"); buttonx = 10; buttony = 40; @@ -458,6 +498,17 @@ int main(void) if (hDefaultCursor == NULL) diele("LoadCursorW(IDC_ARROW)"); + helpIcon = (HICON) LoadImageW(NULL, IDI_QUESTION, IMAGE_ICON, + GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR | LR_SHARED); + if (helpIcon == NULL) + diele("LoadImageW(IDI_QUESTION)"); + helpList = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), + ILC_COLOR32, 0, 1); + if (helpList == NULL) + diele("ImageList_Create()"); + if (ImageList_ReplaceIcon(helpList, -1, helpIcon) == -1) + diele("ImageList_ReplaceIcon()"); + ZeroMemory(&wc, sizeof (WNDCLASSW)); wc.lpszClassName = L"mainwin"; wc.lpfnWndProc = wndproc; From 9d31dddd130455724fe07ab58259effd8e79f60f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 13 Oct 2018 14:17:17 -0400 Subject: [PATCH 1299/1329] Figured out what I was doing wrong to get rightbar not working; it was some code I forgot about =P --- doc/misctests/winrebarexplorertheme.cpp | 53 ++++++++++++++++--------- 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/doc/misctests/winrebarexplorertheme.cpp b/doc/misctests/winrebarexplorertheme.cpp index 90874354..82a4a916 100644 --- a/doc/misctests/winrebarexplorertheme.cpp +++ b/doc/misctests/winrebarexplorertheme.cpp @@ -13,10 +13,11 @@ #include #include #include +#include #include #include -// cl winrebarexplorertheme.cpp -MD -link user32.lib kernel32.lib gdi32.lib comctl32.lib uxtheme.lib msimg32.lib windows.res +// cl winrebarexplorertheme.cpp -MD -link user32.lib kernel32.lib gdi32.lib comctl32.lib uxtheme.lib msimg32.lib shell32.lib windows.res void diele(const char *func) { @@ -44,7 +45,7 @@ HWND toolbarTransparentCheckbox; HICON helpIcon; HIMAGELIST helpList; -#define toolbarStyles (WS_CHILD | CCS_NODIVIDER | CCS_NOPARENTALIGN | CCS_NORESIZE | TBSTYLE_FLAT | TBSTYLE_LIST) +#define toolbarStyles (WS_CHILD | CCS_NODIVIDER | CCS_NOPARENTALIGN | CCS_NORESIZE | TBSTYLE_FLAT) static struct { const WCHAR *text; @@ -197,7 +198,7 @@ void onWM_CREATE(HWND hwnd) rebar = CreateWindowExW(0, REBARCLASSNAMEW, NULL, - WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CCS_NODIVIDER | CCS_TOP, + WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CCS_NODIVIDER | CCS_TOP | RBS_FIXEDORDER, 0, 0, 0, 0, hwnd, (HMENU) 100, hInstance, NULL); if (rebar == NULL) @@ -205,7 +206,7 @@ void onWM_CREATE(HWND hwnd) leftbar = CreateWindowExW(0, TOOLBARCLASSNAMEW, NULL, - toolbarStyles | TBSTYLE_TRANSPARENT, + toolbarStyles | TBSTYLE_LIST | TBSTYLE_TRANSPARENT, 0, 0, 0, 0, hwnd, (HMENU) 101, hInstance, NULL); if (leftbar == NULL) @@ -217,7 +218,7 @@ void onWM_CREATE(HWND hwnd) SendMessageW(leftbar, TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_DRAWDDARROWS | TBSTYLE_EX_HIDECLIPPEDBUTTONS | TBSTYLE_EX_MIXEDBUTTONS); // TODO this *should* be DIPs... // TODO figure out where the *2 is documented - SendMessageW(leftbar, TB_SETPADDING, 0, MAKELONG(6 * 2, 5 * 2)); + SendMessageW(leftbar, TB_SETPADDING, 0, MAKELPARAM(6 * 2, 5 * 2)); ZeroMemory(tbb, 5 * sizeof (TBBUTTON)); for (i = 0; i < 5; i++) { tbb[i].iBitmap = 0; @@ -243,34 +244,38 @@ void onWM_CREATE(HWND hwnd) ZeroMemory(&rbi, sizeof (REBARBANDINFOW)); rbi.cbSize = sizeof (REBARBANDINFOW); rbi.fMask = RBBIM_CHILD | RBBIM_STYLE | RBBIM_SIZE | RBBIM_CHILDSIZE | RBBIM_IDEALSIZE; - rbi.fStyle = RBBS_NOGRIPPER | RBBS_USECHEVRON | RBBS_HIDETITLE; + rbi.fStyle = RBBS_NOGRIPPER | RBBS_CHILDEDGE | RBBS_USECHEVRON | RBBS_HIDETITLE; rbi.hwndChild = leftbar; rbi.cx = tbsizex; rbi.cyChild = tbsizey; rbi.cxMinChild = 0; rbi.cyMinChild = tbsizey; rbi.cxIdeal = tbsizex; -// if (SendMessageW(rebar, RB_INSERTBANDW, (WPARAM) (-1), (LPARAM) (&rbi)) == 0) -// diele("RB_INSERTBANDW leftbar"); + if (SendMessageW(rebar, RB_INSERTBANDW, (WPARAM) (-1), (LPARAM) (&rbi)) == 0) + diele("RB_INSERTBANDW leftbar"); rightbar = CreateWindowExW(0, TOOLBARCLASSNAMEW, NULL, - (toolbarStyles & ~TBSTYLE_LIST) | TBSTYLE_TRANSPARENT, + toolbarStyles | TBSTYLE_TRANSPARENT, 0, 0, 0, 0, hwnd, (HMENU) 102, hInstance, NULL); if (rightbar == NULL) diele("CreateWindowExW(TOOLBARCLASSNAMEW) rightbar"); SendMessageW(rightbar, TB_BUTTONSTRUCTSIZE, sizeof (TBBUTTON), 0); - // TODO check error - SendMessageW(rightbar, TB_SETBITMAPSIZE, 0, MAKELPARAM(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON))); SendMessageW(rightbar, TB_SETIMAGELIST, 0, (LPARAM) helpList); + // TODO this *should* be DIPs... + // TODO figure out where the *2 is documented + SendMessageW(rightbar, TB_SETPADDING, 0, MAKELPARAM(6 * 2, 5 * 2)); ZeroMemory(tbb, 5 * sizeof (TBBUTTON)); tbb[0].iBitmap = 0; tbb[0].idCommand = 0; tbb[0].fsState = TBSTATE_ENABLED; - tbb[0].fsStyle = BTNS_BUTTON; + tbb[0].fsStyle = BTNS_AUTOSIZE | BTNS_BUTTON; if (SendMessageW(rightbar, TB_ADDBUTTONSW, 1, (LPARAM) tbb) == FALSE) diele("TB_ADDBUTTONSW"); + // TODO check error + // TODO figure out why this works here but not elsewhere + SendMessageW(rightbar, TB_SETBUTTONSIZE, 0, 0); tbbtnsize = (DWORD) SendMessageW(rightbar, TB_GETBUTTONSIZE, 0, 0); tbsizex = LOWORD(tbbtnsize); @@ -355,7 +360,6 @@ void repositionRebar(HWND hwnd) 0, 0, win.right - win.left, rb.bottom - rb.top, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER) == 0) diele("SetWindowPos()"); - SendMessageW(rebar, RB_SETBANDWIDTH, 0, win.right - win.left); } // TODO check errors @@ -403,9 +407,9 @@ void handleEvents(HWND hwnd, WPARAM wParam) check = BST_UNCHECKED; SendMessage(toolbarTransparentCheckbox, BM_SETCHECK, check, 0); if (check == BST_CHECKED) - SendMessageW(leftbar, TB_SETSTYLE, 0, toolbarStyles | TBSTYLE_TRANSPARENT); + SendMessageW(leftbar, TB_SETSTYLE, 0, toolbarStyles | TBSTYLE_LIST | TBSTYLE_TRANSPARENT); else - SendMessageW(leftbar, TB_SETSTYLE, 0, toolbarStyles); + SendMessageW(leftbar, TB_SETSTYLE, 0, toolbarStyles | TBSTYLE_LIST); ShowWindow(leftbar, SW_SHOW); break; } @@ -475,9 +479,11 @@ int main(void) INITCOMMONCONTROLSEX icc; HICON hDefaultIcon; HCURSOR hDefaultCursor; + SHSTOCKICONINFO sii; WNDCLASSW wc; HWND mainwin; MSG msg; + HRESULT hr; hInstance = (HINSTANCE) (&__ImageBase); nCmdShow = SW_SHOWDEFAULT; @@ -498,10 +504,19 @@ int main(void) if (hDefaultCursor == NULL) diele("LoadCursorW(IDC_ARROW)"); - helpIcon = (HICON) LoadImageW(NULL, IDI_QUESTION, IMAGE_ICON, - GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR | LR_SHARED); - if (helpIcon == NULL) - diele("LoadImageW(IDI_QUESTION)"); + // notes: + // - https://www.google.com/search?client=firefox-b-1&ei=gGzBW63XLYeG5wL3l5WQBg&q=winapi+%22system+icon%22+image+list&oq=winapi+%22system+icon%22+image+list&gs_l=psy-ab.3...31221.33763..33899...0.0..0.130.1967.22j2......0....1..gws-wiz.......0j0i71j35i39j0i20i264j0i67j0i20i263j0i22i30j0i22i10i30j33i160j33i299j33i22i29i30.wAw65ObMuTg + // - https://stackoverflow.com/questions/36763562/how-to-load-imagelist-with-system-dialog-icons + // - https://blogs.msdn.microsoft.com/oldnewthing/20121005-00/?p=6393 + // - https://stackoverflow.com/questions/4285890/how-to-load-a-small-system-icon/4286601 + // - https://stackoverflow.com/questions/6613513/compliant-loading-of-small-oem-icon-with-loadimage + // - https://web.archive.org/web/20170514073649/http://www.catch22.net/tuts/system-image-list + ZeroMemory(&sii, sizeof (SHSTOCKICONINFO)); + sii.cbSize = sizeof (SHSTOCKICONINFO); + hr = SHGetStockIconInfo(SIID_HELP, SHGSI_ICON | SHGSI_SMALLICON, &sii); + if (hr != S_OK) + diehr("SHGetStockIconInfo(SIID_HELP)", hr); + helpIcon = sii.hIcon; helpList = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR32, 0, 1); if (helpList == NULL) From 1d94e95e8398e0b8bc35a45ccb8f2b2a7fbd4620 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 13 Oct 2018 17:56:44 -0400 Subject: [PATCH 1300/1329] More work. That chevron is going to be annoying... --- doc/misctests/winrebarexplorertheme.cpp | 156 +++++++++++++++++++----- 1 file changed, 124 insertions(+), 32 deletions(-) diff --git a/doc/misctests/winrebarexplorertheme.cpp b/doc/misctests/winrebarexplorertheme.cpp index 82a4a916..f71b15bd 100644 --- a/doc/misctests/winrebarexplorertheme.cpp +++ b/doc/misctests/winrebarexplorertheme.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -60,7 +61,7 @@ static struct { // TODO check errors // TODO extract colors from the theme -LRESULT customDrawExplorer(NMCUSTOMDRAW *nm) +void drawExplorerBackground(HTHEME theme, HDC dc, RECT *rcWindow, RECT *rcPaint) { static TRIVERTEX vertices[] = { { 0, 0, 4 << 8, 80 << 8, 130 << 8, 255 << 8 }, @@ -72,42 +73,115 @@ LRESULT customDrawExplorer(NMCUSTOMDRAW *nm) { 0, 1 }, { 2, 3 }, }; - RECT r; - HTHEME theme; - if (nm->dwDrawStage != CDDS_PREPAINT) - return CDRF_DODEFAULT | TBCDRF_NOOFFSET; - if (nm->hdr.hwndFrom == rebar) { - GetClientRect(nm->hdr.hwndFrom, &r); - vertices[0].x = nm->rc.left; + vertices[0].x = rcPaint->left; vertices[0].y = 0; - vertices[1].x = nm->rc.right; - vertices[1].y = (r.bottom - r.top) / 2; - vertices[2].x = nm->rc.left; - vertices[2].y = (r.bottom - r.top) / 2; - vertices[3].x = nm->rc.right; - vertices[3].y = r.bottom - r.top; - GradientFill(nm->hdc, vertices, 4, (PVOID) gr, 2, GRADIENT_FILL_RECT_V); - theme = OpenThemeData(nm->hdr.hwndFrom, L"CommandModule"); - DrawThemeBackground(theme, nm->hdc, + vertices[1].x = rcPaint->right; + vertices[1].y = (rcWindow->bottom - rcWindow->top) / 2; + vertices[2].x = rcPaint->left; + vertices[2].y = (rcWindow->bottom - rcWindow->top) / 2; + vertices[3].x = rcPaint->right; + vertices[3].y = rcWindow->bottom - rcWindow->top; + GradientFill(dc, vertices, 4, (PVOID) gr, 2, GRADIENT_FILL_RECT_V); + DrawThemeBackground(theme, dc, 1, 0, - &r, &(nm->rc)); - CloseThemeData(theme); - } - return CDRF_NOTIFYITEMDRAW; + rcWindow, rcPaint); } -LRESULT customDraw7(NMCUSTOMDRAW *nm) +// TODO check errors +void drawExplorerChevron(HTHEME theme, HDC dc, HWND rebar, WPARAM band, RECT *rcPaint) { + REBARBANDINFOW rbi; + RECT r; + int state; + + ZeroMemory(&rbi, sizeof (REBARBANDINFOW)); + rbi.cbSize = sizeof (REBARBANDINFOW); + rbi.fMask = RBBIM_CHILD | RBBIM_CHEVRONLOCATION | RBBIM_CHEVRONSTATE; + SendMessageW(rebar, RB_GETBANDINFOW, band, (LPARAM) (&rbi)); + if ((rbi.uChevronState & STATE_SYSTEM_INVISIBLE) != 0) + return; + state = 1; + // TODO check if this is correct + if ((rbi.uChevronState & STATE_SYSTEM_FOCUSED) != 0) + state = 4; + if ((rbi.uChevronState & STATE_SYSTEM_HOTTRACKED) != 0) + state = 2; + if ((rbi.uChevronState & STATE_SYSTEM_PRESSED) != 0) + state = 3; + r = rbi.rcChevronLocation; + // TODO commctrl.h says this should be correct for the chevron rect, but it's not? +// MapWindowRect(rbi.hwndChild, rebar, &r); + DrawThemeBackground(theme, dc, + 3, state, + &r, rcPaint); + DrawThemeBackground(theme, dc, + 7, 1, + &r, rcPaint); +} + +// TODO check errors +LRESULT customDrawExplorerRebar(NMCUSTOMDRAW *nm) +{ + HTHEME theme; + RECT r; + + if (nm->dwDrawStage != CDDS_PREPAINT) + return CDRF_DODEFAULT; + theme = OpenThemeData(nm->hdr.hwndFrom, L"CommandModule"); + GetClientRect(nm->hdr.hwndFrom, &r); + drawExplorerBackground(theme, nm->hdc, &r, &(nm->rc)); + // TODO dwItemSpec is often invalid?! + drawExplorerChevron(theme, nm->hdc, nm->hdr.hwndFrom, 0, &(nm->rc)); + CloseThemeData(theme); + return CDRF_SKIPDEFAULT; +} + +// TODO check errors +LRESULT customDrawExplorerToolbar(NMTBCUSTOMDRAW *nm) +{ + HWND toolbar, rebar; + HTHEME theme; + RECT r; + int state; + + toolbar = nm->nmcd.hdr.hwndFrom; + switch (nm->nmcd.dwDrawStage) { + case CDDS_PREPAINT: + theme = OpenThemeData(toolbar, L"CommandModule"); + rebar = GetParent(toolbar); + GetWindowRect(rebar, &r); + MapWindowRect(NULL, toolbar, &r); + drawExplorerBackground(theme, nm->nmcd.hdc, &r, &(nm->nmcd.rc)); + CloseThemeData(theme); + return CDRF_NOTIFYITEMDRAW; + case CDDS_ITEMPREPAINT: + theme = OpenThemeData(toolbar, L"CommandModule"); + state = 1; + // TODO this doesn't work; both keyboard and mouse are listed as HOT + if ((nm->nmcd.uItemState & CDIS_FOCUS) != 0) + state = 4; + if ((nm->nmcd.uItemState & CDIS_HOT) != 0) + state = 2; + if ((nm->nmcd.uItemState & CDIS_SELECTED) != 0) + state = 3; + SendMessageW(toolbar, TB_GETITEMRECT, SendMessageW(toolbar, TB_COMMANDTOINDEX, nm->nmcd.dwItemSpec, 0), (LPARAM) (&r)); + DrawThemeBackground(theme, nm->nmcd.hdc, + 3, state, + &r, &(nm->nmcd.rc)); + CloseThemeData(theme); + return TBCDRF_NOBACKGROUND; + } return CDRF_DODEFAULT; } static struct { const WCHAR *text; - LRESULT (*handle)(NMCUSTOMDRAW *nm); + LRESULT (*handleRebar)(NMCUSTOMDRAW *nm); + LRESULT (*handleToolbar)(NMTBCUSTOMDRAW *nm); } drawmodes[] = { - { L"SetWindowTheme()", NULL }, - { L"Custom Draw Explorer", customDrawExplorer }, + { L"SetWindowTheme()", NULL, NULL }, + { L"Custom Draw Explorer", customDrawExplorerRebar, customDrawExplorerToolbar }, { NULL, NULL }, }; @@ -243,7 +317,7 @@ void onWM_CREATE(HWND hwnd) ZeroMemory(&rbi, sizeof (REBARBANDINFOW)); rbi.cbSize = sizeof (REBARBANDINFOW); - rbi.fMask = RBBIM_CHILD | RBBIM_STYLE | RBBIM_SIZE | RBBIM_CHILDSIZE | RBBIM_IDEALSIZE; + rbi.fMask = RBBIM_CHILD | RBBIM_STYLE | RBBIM_SIZE | RBBIM_CHILDSIZE | RBBIM_IDEALSIZE | RBBIM_ID; rbi.fStyle = RBBS_NOGRIPPER | RBBS_CHILDEDGE | RBBS_USECHEVRON | RBBS_HIDETITLE; rbi.hwndChild = leftbar; rbi.cx = tbsizex; @@ -251,6 +325,7 @@ void onWM_CREATE(HWND hwnd) rbi.cxMinChild = 0; rbi.cyMinChild = tbsizey; rbi.cxIdeal = tbsizex; + rbi.wID = 0; if (SendMessageW(rebar, RB_INSERTBANDW, (WPARAM) (-1), (LPARAM) (&rbi)) == 0) diele("RB_INSERTBANDW leftbar"); @@ -283,13 +358,14 @@ void onWM_CREATE(HWND hwnd) ZeroMemory(&rbi, sizeof (REBARBANDINFOW)); rbi.cbSize = sizeof (REBARBANDINFOW); - rbi.fMask = RBBIM_CHILD | RBBIM_STYLE | RBBIM_SIZE | RBBIM_CHILDSIZE; + rbi.fMask = RBBIM_CHILD | RBBIM_STYLE | RBBIM_SIZE | RBBIM_CHILDSIZE | RBBIM_ID; rbi.fStyle = RBBS_NOGRIPPER | RBBS_HIDETITLE; rbi.hwndChild = rightbar; rbi.cx = tbsizex; rbi.cyChild = tbsizey; rbi.cxMinChild = tbsizex; rbi.cyMinChild = tbsizey; + rbi.wID = 1; if (SendMessageW(rebar, RB_INSERTBANDW, (WPARAM) (-1), (LPARAM) (&rbi)) == 0) diele("RB_INSERTBANDW rightbar"); @@ -312,6 +388,14 @@ void onWM_CREATE(HWND hwnd) } buttony += buttonht + 5; } + button = CreateWindowExW(0, + L"BUTTON", L"Give Toolbar Focus", + WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, + buttonx, buttony, + buttonwid, buttonht, + hwnd, (HMENU) (200 + i), hInstance, NULL); + if (button == NULL) + diele("CreateWIndowExW(L\"BUTTON\")"); rebarCombo = CreateWindowExW(WS_EX_CLIENTEDGE, L"COMBOBOX", L"", WS_CHILD | WS_VISIBLE | CBS_DROPDOWN, @@ -412,6 +496,9 @@ void handleEvents(HWND hwnd, WPARAM wParam) SendMessageW(leftbar, TB_SETSTYLE, 0, toolbarStyles | TBSTYLE_LIST); ShowWindow(leftbar, SW_SHOW); break; + case MAKEWPARAM(202, BN_CLICKED): + SetFocus(leftbar); + break; } if (changeRebar) { if (selRebar != NULL && wcscmp(selRebar, L"NULL") == 0) @@ -425,10 +512,13 @@ void handleEvents(HWND hwnd, WPARAM wParam) if (changeToolbar) { if (selToolbar != NULL && wcscmp(selToolbar, L"NULL") == 0) selToolbar = NULL; - if (selToolbar != NULL && *selToolbar != L'\0') + if (selToolbar != NULL && *selToolbar != L'\0') { SendMessageW(leftbar, TB_SETWINDOWTHEME, 0, (LPARAM) selToolbar); - else + SendMessageW(rightbar, TB_SETWINDOWTHEME, 0, (LPARAM) selToolbar); + } else { SetWindowTheme(leftbar, selToolbar, selToolbar); + SetWindowTheme(rightbar, selToolbar, selToolbar); + } invalidate = TRUE; } if (invalidate) @@ -459,11 +549,13 @@ LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) case WM_NOTIFY: switch (nm->code) { case NM_CUSTOMDRAW: - if (nm->hwndFrom != rebar) - break; if (drawmode == 0) break; - return (*(drawmodes[drawmode].handle))((NMCUSTOMDRAW *) nm); + if (nm->hwndFrom == rebar) + return (*(drawmodes[drawmode].handleRebar))((NMCUSTOMDRAW *) nm); + else if (nm->hwndFrom == leftbar || nm->hwndFrom == rightbar) + return (*(drawmodes[drawmode].handleToolbar))((NMTBCUSTOMDRAW *) nm); + break; } break; } From 79d1d58af22a49012dca033e6be06799902123da Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 13 Oct 2018 18:12:04 -0400 Subject: [PATCH 1301/1329] Oops --- doc/misctests/winrebarexplorertheme.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/misctests/winrebarexplorertheme.cpp b/doc/misctests/winrebarexplorertheme.cpp index f71b15bd..cd6d2eb3 100644 --- a/doc/misctests/winrebarexplorertheme.cpp +++ b/doc/misctests/winrebarexplorertheme.cpp @@ -18,7 +18,7 @@ #include #include -// cl winrebarexplorertheme.cpp -MD -link user32.lib kernel32.lib gdi32.lib comctl32.lib uxtheme.lib msimg32.lib shell32.lib windows.res +// cl winrebarexplorertheme.cpp -MT -link user32.lib kernel32.lib gdi32.lib comctl32.lib uxtheme.lib msimg32.lib shell32.lib windows.res void diele(const char *func) { From ddd6afbf36ea688ab1774d0666588258d260c952 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 14 Oct 2018 15:12:00 -0400 Subject: [PATCH 1302/1329] Refined icon loading and tried to expand the winrebarexplorertheme test a bit more elaborate. This is going badly again... --- doc/misctests/winrebarexplorertheme.cpp | 66 +++++++++++++++---------- 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/doc/misctests/winrebarexplorertheme.cpp b/doc/misctests/winrebarexplorertheme.cpp index cd6d2eb3..07fa1baf 100644 --- a/doc/misctests/winrebarexplorertheme.cpp +++ b/doc/misctests/winrebarexplorertheme.cpp @@ -13,12 +13,11 @@ #include #include #include -#include #include #include #include -// cl winrebarexplorertheme.cpp -MT -link user32.lib kernel32.lib gdi32.lib comctl32.lib uxtheme.lib msimg32.lib shell32.lib windows.res +// cl winrebarexplorertheme.cpp -MT -link user32.lib kernel32.lib gdi32.lib comctl32.lib uxtheme.lib msimg32.lib windows.res void diele(const char *func) { @@ -43,8 +42,10 @@ HWND rebarCombo; HWND toolbarCombo; HWND toolbarTransparentCheckbox; +HICON shieldIcon; +HICON applicationIcon; HICON helpIcon; -HIMAGELIST helpList; +HIMAGELIST rightList; #define toolbarStyles (WS_CHILD | CCS_NODIVIDER | CCS_NOPARENTALIGN | CCS_NORESIZE | TBSTYLE_FLAT) @@ -337,7 +338,8 @@ void onWM_CREATE(HWND hwnd) if (rightbar == NULL) diele("CreateWindowExW(TOOLBARCLASSNAMEW) rightbar"); SendMessageW(rightbar, TB_BUTTONSTRUCTSIZE, sizeof (TBBUTTON), 0); - SendMessageW(rightbar, TB_SETIMAGELIST, 0, (LPARAM) helpList); + SendMessageW(rightbar, TB_SETIMAGELIST, 0, (LPARAM) rightList); + SendMessageW(rightbar, TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_DRAWDDARROWS); // TODO this *should* be DIPs... // TODO figure out where the *2 is documented SendMessageW(rightbar, TB_SETPADDING, 0, MAKELPARAM(6 * 2, 5 * 2)); @@ -345,15 +347,28 @@ void onWM_CREATE(HWND hwnd) tbb[0].iBitmap = 0; tbb[0].idCommand = 0; tbb[0].fsState = TBSTATE_ENABLED; - tbb[0].fsStyle = BTNS_AUTOSIZE | BTNS_BUTTON; - if (SendMessageW(rightbar, TB_ADDBUTTONSW, 1, (LPARAM) tbb) == FALSE) + tbb[0].fsStyle = BTNS_AUTOSIZE | BTNS_BUTTON | BTNS_DROPDOWN; + tbb[1].iBitmap = 1; + tbb[1].idCommand = 1; + tbb[1].fsState = TBSTATE_ENABLED; + tbb[1].fsStyle = BTNS_AUTOSIZE | BTNS_BUTTON; + tbb[2].iBitmap = 2; + tbb[2].idCommand = 2; + tbb[2].fsState = TBSTATE_ENABLED; + tbb[2].fsStyle = BTNS_AUTOSIZE | BTNS_BUTTON; + if (SendMessageW(rightbar, TB_ADDBUTTONSW, 3, (LPARAM) tbb) == FALSE) diele("TB_ADDBUTTONSW"); // TODO check error // TODO figure out why this works here but not elsewhere - SendMessageW(rightbar, TB_SETBUTTONSIZE, 0, 0); +// SendMessageW(rightbar, TB_SETBUTTONSIZE, 0, 0); + tbsizex = 0; + for (i = 0; i < 3; i++) { + if (SendMessageW(rightbar, TB_GETITEMRECT, (WPARAM) i, (LPARAM) (&btnrect)) == FALSE) + diele("TB_GETITEMRECT"); + tbsizex += btnrect.right - btnrect.left; + } tbbtnsize = (DWORD) SendMessageW(rightbar, TB_GETBUTTONSIZE, 0, 0); - tbsizex = LOWORD(tbbtnsize); tbsizey = HIWORD(tbbtnsize); ZeroMemory(&rbi, sizeof (REBARBANDINFOW)); @@ -571,7 +586,6 @@ int main(void) INITCOMMONCONTROLSEX icc; HICON hDefaultIcon; HCURSOR hDefaultCursor; - SHSTOCKICONINFO sii; WNDCLASSW wc; HWND mainwin; MSG msg; @@ -596,25 +610,25 @@ int main(void) if (hDefaultCursor == NULL) diele("LoadCursorW(IDC_ARROW)"); - // notes: - // - https://www.google.com/search?client=firefox-b-1&ei=gGzBW63XLYeG5wL3l5WQBg&q=winapi+%22system+icon%22+image+list&oq=winapi+%22system+icon%22+image+list&gs_l=psy-ab.3...31221.33763..33899...0.0..0.130.1967.22j2......0....1..gws-wiz.......0j0i71j35i39j0i20i264j0i67j0i20i263j0i22i30j0i22i10i30j33i160j33i299j33i22i29i30.wAw65ObMuTg - // - https://stackoverflow.com/questions/36763562/how-to-load-imagelist-with-system-dialog-icons - // - https://blogs.msdn.microsoft.com/oldnewthing/20121005-00/?p=6393 - // - https://stackoverflow.com/questions/4285890/how-to-load-a-small-system-icon/4286601 - // - https://stackoverflow.com/questions/6613513/compliant-loading-of-small-oem-icon-with-loadimage - // - https://web.archive.org/web/20170514073649/http://www.catch22.net/tuts/system-image-list - ZeroMemory(&sii, sizeof (SHSTOCKICONINFO)); - sii.cbSize = sizeof (SHSTOCKICONINFO); - hr = SHGetStockIconInfo(SIID_HELP, SHGSI_ICON | SHGSI_SMALLICON, &sii); + hr = LoadIconMetric(NULL, IDI_SHIELD, LIM_SMALL, &shieldIcon); if (hr != S_OK) - diehr("SHGetStockIconInfo(SIID_HELP)", hr); - helpIcon = sii.hIcon; - helpList = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), - ILC_COLOR32, 0, 1); - if (helpList == NULL) + diehr("LoadIconMetric(IDI_SHIELD)", hr); + hr = LoadIconMetric(NULL, IDI_APPLICATION, LIM_SMALL, &applicationIcon); + if (hr != S_OK) + diehr("LoadIconMetric(IDI_APPLICATION)", hr); + hr = LoadIconMetric(NULL, IDI_QUESTION, LIM_SMALL, &helpIcon); + if (hr != S_OK) + diehr("LoadIconMetric(IDI_QUESTION)", hr); + rightList = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), + ILC_COLOR32, 0, 3); + if (rightList == NULL) diele("ImageList_Create()"); - if (ImageList_ReplaceIcon(helpList, -1, helpIcon) == -1) - diele("ImageList_ReplaceIcon()"); + if (ImageList_ReplaceIcon(rightList, -1, shieldIcon) == -1) + diele("ImageList_ReplaceIcon(IDI_SHIELD)"); + if (ImageList_ReplaceIcon(rightList, -1, applicationIcon) == -1) + diele("ImageList_ReplaceIcon(IDI_APPLICATION)"); + if (ImageList_ReplaceIcon(rightList, -1, helpIcon) == -1) + diele("ImageList_ReplaceIcon(IDI_QUESTION)"); ZeroMemory(&wc, sizeof (WNDCLASSW)); wc.lpszClassName = L"mainwin"; From c216d8953deaf623184e6f331112cbba3c4e9d7f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 14 Oct 2018 18:42:51 -0400 Subject: [PATCH 1303/1329] Undid all custom sizing stuff for now. Let's just make this work first. --- doc/misctests/winrebarexplorertheme.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/misctests/winrebarexplorertheme.cpp b/doc/misctests/winrebarexplorertheme.cpp index 07fa1baf..28472661 100644 --- a/doc/misctests/winrebarexplorertheme.cpp +++ b/doc/misctests/winrebarexplorertheme.cpp @@ -288,12 +288,12 @@ void onWM_CREATE(HWND hwnd) diele("CreateWindowExW(TOOLBARCLASSNAMEW) leftbar"); SendMessageW(leftbar, TB_BUTTONSTRUCTSIZE, sizeof (TBBUTTON), 0); // I_IMAGENONE causes the button text to be left-aligned; don't use it - if (SendMessageW(leftbar, TB_SETBITMAPSIZE, 0, 0) == FALSE) - diele("TB_SETBITMAPSIZE"); +// if (SendMessageW(leftbar, TB_SETBITMAPSIZE, 0, 0) == FALSE) +// diele("TB_SETBITMAPSIZE"); SendMessageW(leftbar, TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_DRAWDDARROWS | TBSTYLE_EX_HIDECLIPPEDBUTTONS | TBSTYLE_EX_MIXEDBUTTONS); // TODO this *should* be DIPs... // TODO figure out where the *2 is documented - SendMessageW(leftbar, TB_SETPADDING, 0, MAKELPARAM(6 * 2, 5 * 2)); +// SendMessageW(leftbar, TB_SETPADDING, 0, MAKELPARAM(6 * 2, 5 * 2)); ZeroMemory(tbb, 5 * sizeof (TBBUTTON)); for (i = 0; i < 5; i++) { tbb[i].iBitmap = 0; @@ -342,7 +342,7 @@ void onWM_CREATE(HWND hwnd) SendMessageW(rightbar, TB_SETEXTENDEDSTYLE, 0, TBSTYLE_EX_DRAWDDARROWS); // TODO this *should* be DIPs... // TODO figure out where the *2 is documented - SendMessageW(rightbar, TB_SETPADDING, 0, MAKELPARAM(6 * 2, 5 * 2)); +// SendMessageW(rightbar, TB_SETPADDING, 0, MAKELPARAM(6 * 2, 5 * 2)); ZeroMemory(tbb, 5 * sizeof (TBBUTTON)); tbb[0].iBitmap = 0; tbb[0].idCommand = 0; From 359c08aa11aa0c2705139ac9f0620d20f04b9b15 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 14 Oct 2018 19:08:02 -0400 Subject: [PATCH 1304/1329] Tried to custom-draw the split button properly. Not sure it's possible... --- doc/misctests/winrebarexplorertheme.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/doc/misctests/winrebarexplorertheme.cpp b/doc/misctests/winrebarexplorertheme.cpp index 28472661..ddb08192 100644 --- a/doc/misctests/winrebarexplorertheme.cpp +++ b/doc/misctests/winrebarexplorertheme.cpp @@ -142,9 +142,11 @@ LRESULT customDrawExplorerRebar(NMCUSTOMDRAW *nm) LRESULT customDrawExplorerToolbar(NMTBCUSTOMDRAW *nm) { HWND toolbar, rebar; + WPARAM itemIndex; + TBBUTTON tbb; HTHEME theme; RECT r; - int state; + int part, state; toolbar = nm->nmcd.hdr.hwndFrom; switch (nm->nmcd.dwDrawStage) { @@ -157,7 +159,13 @@ LRESULT customDrawExplorerToolbar(NMTBCUSTOMDRAW *nm) CloseThemeData(theme); return CDRF_NOTIFYITEMDRAW; case CDDS_ITEMPREPAINT: + itemIndex = (WPARAM) SendMessageW(toolbar, TB_COMMANDTOINDEX, nm->nmcd.dwItemSpec, 0); + ZeroMemory(&tbb, sizeof (TBBUTTON)); + SendMessageW(toolbar, TB_GETBUTTON, itemIndex, (LPARAM) (&tbb)); theme = OpenThemeData(toolbar, L"CommandModule"); + part = 3; + if ((tbb.fsStyle & BTNS_DROPDOWN) != 0) + part = 4; state = 1; // TODO this doesn't work; both keyboard and mouse are listed as HOT if ((nm->nmcd.uItemState & CDIS_FOCUS) != 0) @@ -166,7 +174,7 @@ LRESULT customDrawExplorerToolbar(NMTBCUSTOMDRAW *nm) state = 2; if ((nm->nmcd.uItemState & CDIS_SELECTED) != 0) state = 3; - SendMessageW(toolbar, TB_GETITEMRECT, SendMessageW(toolbar, TB_COMMANDTOINDEX, nm->nmcd.dwItemSpec, 0), (LPARAM) (&r)); + SendMessageW(toolbar, TB_GETITEMRECT, itemIndex, (LPARAM) (&r)); DrawThemeBackground(theme, nm->nmcd.hdc, 3, state, &r, &(nm->nmcd.rc)); From 760a7c09f51e412f67209239466dc7e58ecd71b0 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 14 Oct 2018 23:56:00 -0400 Subject: [PATCH 1305/1329] Started a new test program that works like the rebar one except with real button controls instead. --- doc/misctests/winbuttonexplorertheme.cpp | 502 +++++++++++++++++++++++ 1 file changed, 502 insertions(+) create mode 100644 doc/misctests/winbuttonexplorertheme.cpp diff --git a/doc/misctests/winbuttonexplorertheme.cpp b/doc/misctests/winbuttonexplorertheme.cpp new file mode 100644 index 00000000..53101212 --- /dev/null +++ b/doc/misctests/winbuttonexplorertheme.cpp @@ -0,0 +1,502 @@ +// 9 october 2018 +#define UNICODE +#define _UNICODE +#define STRICT +#define STRICT_TYPED_ITEMIDS +#define WINVER 0x0600 /* from Microsoft's winnls.h */ +#define _WIN32_WINNT 0x0600 +#define _WIN32_WINDOWS 0x0600 /* from Microsoft's pdh.h */ +#define _WIN32_IE 0x0700 +#define NTDDI_VERSION 0x06000000 +#include +#include +#include +#include +#include +#include +#include +#include + +// cl winbuttonexplorertheme.cpp -MT -link user32.lib kernel32.lib gdi32.lib comctl32.lib uxtheme.lib msimg32.lib windows.res + +void diele(const char *func) +{ + DWORD le; + + le = GetLastError(); + fprintf(stderr, "%s: %I32u\n", func, le); + exit(EXIT_FAILURE); +} + +void diehr(const char *func, HRESULT hr) +{ + fprintf(stderr, "%s: 0x%08I32X\n", func, hr); + exit(EXIT_FAILURE); +} + +HINSTANCE hInstance; +HWND leftButtons[5]; +HWND rightButtons[3]; + +HICON shieldIcon; +HICON applicationIcon; +HICON helpIcon; +HIMAGELIST rightList; + +HTHEME theme = NULL; +HIMAGELIST dropdownArrowList = NULL; +HFONT buttonFont = NULL; + +static struct { + const WCHAR *text; + BOOL dropdown; +} leftbarButtons[] = { + { L"Organize", TRUE }, + { L"Include in library", TRUE }, + { L"Share with", TRUE }, + { L"Burn", FALSE }, + { L"New folder", FALSE }, +}; + +// TODO check errors +// TODO extract colors from the theme +void drawExplorerBackground(HTHEME theme, HDC dc, RECT *rcWindow, RECT *rcPaint) +{ + static TRIVERTEX vertices[] = { + { 0, 0, 4 << 8, 80 << 8, 130 << 8, 255 << 8 }, + { 0, 0, 17 << 8, 101 << 8, 132 << 8, 255 << 8 }, + { 0, 0, 17 << 8, 101 << 8, 132 << 8, 255 << 8 }, + { 0, 0, 29 << 8, 121 << 8, 134 << 8, 255 << 8 }, + }; + static GRADIENT_RECT gr[2] = { + { 0, 1 }, + { 2, 3 }, + }; + + vertices[0].x = rcPaint->left; + vertices[0].y = 0; + vertices[1].x = rcPaint->right; + vertices[1].y = (rcWindow->bottom - rcWindow->top) / 2; + vertices[2].x = rcPaint->left; + vertices[2].y = (rcWindow->bottom - rcWindow->top) / 2; + vertices[3].x = rcPaint->right; + vertices[3].y = rcWindow->bottom - rcWindow->top; + GradientFill(dc, vertices, 4, (PVOID) gr, 2, GRADIENT_FILL_RECT_V); + DrawThemeBackground(theme, dc, + 1, 0, + rcWindow, rcPaint); +} + +// TODO check errors +void drawExplorerChevron(HTHEME theme, HDC dc, HWND rebar, WPARAM band, RECT *rcPaint) +{ + REBARBANDINFOW rbi; + RECT r; + int state; + + ZeroMemory(&rbi, sizeof (REBARBANDINFOW)); + rbi.cbSize = sizeof (REBARBANDINFOW); + rbi.fMask = RBBIM_CHILD | RBBIM_CHEVRONLOCATION | RBBIM_CHEVRONSTATE; + SendMessageW(rebar, RB_GETBANDINFOW, band, (LPARAM) (&rbi)); + if ((rbi.uChevronState & STATE_SYSTEM_INVISIBLE) != 0) + return; + state = 1; + // TODO check if this is correct + if ((rbi.uChevronState & STATE_SYSTEM_FOCUSED) != 0) + state = 4; + if ((rbi.uChevronState & STATE_SYSTEM_HOTTRACKED) != 0) + state = 2; + if ((rbi.uChevronState & STATE_SYSTEM_PRESSED) != 0) + state = 3; + r = rbi.rcChevronLocation; + // TODO commctrl.h says this should be correct for the chevron rect, but it's not? +// MapWindowRect(rbi.hwndChild, rebar, &r); + DrawThemeBackground(theme, dc, + 3, state, + &r, rcPaint); + DrawThemeBackground(theme, dc, + 7, 1, + &r, rcPaint); +} + +// TODO check errors +LRESULT customDrawExplorerRebar(NMCUSTOMDRAW *nm) +{ + HTHEME theme; + RECT r; + + if (nm->dwDrawStage != CDDS_PREPAINT) + return CDRF_DODEFAULT; + theme = OpenThemeData(nm->hdr.hwndFrom, L"CommandModule"); + GetClientRect(nm->hdr.hwndFrom, &r); + drawExplorerBackground(theme, nm->hdc, &r, &(nm->rc)); + // TODO dwItemSpec is often invalid?! + drawExplorerChevron(theme, nm->hdc, nm->hdr.hwndFrom, 0, &(nm->rc)); + CloseThemeData(theme); + return CDRF_SKIPDEFAULT; +} + +// TODO check errors +LRESULT customDrawExplorerToolbar(NMTBCUSTOMDRAW *nm) +{ + HWND toolbar, rebar; + WPARAM itemIndex; + TBBUTTON tbb; + HTHEME theme; + RECT r; + int part, state; + + toolbar = nm->nmcd.hdr.hwndFrom; + switch (nm->nmcd.dwDrawStage) { + case CDDS_PREPAINT: + theme = OpenThemeData(toolbar, L"CommandModule"); + rebar = GetParent(toolbar); + GetWindowRect(rebar, &r); + MapWindowRect(NULL, toolbar, &r); + drawExplorerBackground(theme, nm->nmcd.hdc, &r, &(nm->nmcd.rc)); + CloseThemeData(theme); + return CDRF_NOTIFYITEMDRAW; + case CDDS_ITEMPREPAINT: + itemIndex = (WPARAM) SendMessageW(toolbar, TB_COMMANDTOINDEX, nm->nmcd.dwItemSpec, 0); + ZeroMemory(&tbb, sizeof (TBBUTTON)); + SendMessageW(toolbar, TB_GETBUTTON, itemIndex, (LPARAM) (&tbb)); + theme = OpenThemeData(toolbar, L"CommandModule"); + part = 3; + if ((tbb.fsStyle & BTNS_DROPDOWN) != 0) + part = 4; + state = 1; + // TODO this doesn't work; both keyboard and mouse are listed as HOT + if ((nm->nmcd.uItemState & CDIS_FOCUS) != 0) + state = 4; + if ((nm->nmcd.uItemState & CDIS_HOT) != 0) + state = 2; + if ((nm->nmcd.uItemState & CDIS_SELECTED) != 0) + state = 3; + SendMessageW(toolbar, TB_GETITEMRECT, itemIndex, (LPARAM) (&r)); + DrawThemeBackground(theme, nm->nmcd.hdc, + 3, state, + &r, &(nm->nmcd.rc)); + CloseThemeData(theme); + return TBCDRF_NOBACKGROUND; + } + return CDRF_DODEFAULT; +} + +void onWM_CREATE(HWND hwnd) +{ + int buttonx, buttony; + int i; + + buttonx = 5; + buttony = 5; + for (i = 0; i < 5; i++) { + leftButtons[i] = CreateWindowExW(0, + L"BUTTON", leftbarButtons[i].text, + WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON, + buttonx, buttony, + 150, 30, + hwnd, (HMENU) (100 + i), hInstance, NULL); + if (leftButtons[i] == NULL) + diele("CreateWindowExW(L\"BUTTON\")"); + buttonx += 150; + } +} + +// TODO check errors +void updateTheme(HWND hwnd) +{ + BUTTON_IMAGELIST bim; + HDC dc; + SIZE size; + HBITMAP hb; + HTHEME fontTheme; + LOGFONTW lf; + int i; + + if (buttonFont != NULL) { + for (i = 0; i < 5; i++) + SendMessageW(leftButtons[i], WM_SETFONT, (WPARAM) NULL, TRUE); + DeleteObject(buttonFont); + buttonFont = NULL; + } + if (dropdownArrowList != NULL) { + ZeroMemory(&bim, sizeof (BUTTON_IMAGELIST)); + bim.himl = BCCL_NOGLYPH; + for (i = 0; i < 3; i++) + SendMessageW(leftButtons[i], BCM_SETIMAGELIST, 0, (LPARAM) (&bim)); + ImageList_Destroy(dropdownArrowList); + dropdownArrowList = NULL; + } + if (theme != NULL) { + CloseThemeData(theme); + theme = NULL; + } + + theme = OpenThemeData(hwnd, L"CommandModule"); + dc = GetDC(hwnd); + // TS_MIN returns 1x1 and TS_DRAW returns 0x0, so... + GetThemePartSize(theme, dc, + 6, 0, + NULL, TS_TRUE, &size); + ReleaseDC(hwnd, dc); + // TODO draw a bitmap properly + GetThemeBitmap(theme, + 6, 0, + 0, GBF_COPY, &hb); + dropdownArrowList = ImageList_Create(size.cx, size.cy, + ILC_COLOR32, 0, 1); + ImageList_Add(dropdownArrowList, hb, NULL); + DeleteObject(hb); + ZeroMemory(&bim, sizeof (BUTTON_IMAGELIST)); + bim.himl = dropdownArrowList; + // TODO should be DIPs + bim.margin.left = 1; + bim.uAlign = BUTTON_IMAGELIST_ALIGN_RIGHT; + for (i = 0; i < 3; i++) + SendMessage(leftButtons[i], BCM_SETIMAGELIST, 0, (LPARAM) (&bim)); +} + +void repositionButtons(HWND hwnd) +{ + HDWP dwp; + int buttonx, buttony; + SIZE size; + int i; + + dwp = BeginDeferWindowPos(5); + if (dwp == NULL) + diele("BeginDeferWindowPos()"); + buttonx = 5; + buttony = 5; + for (i = 0; i < 5; i++) { + ZeroMemory(&size, sizeof (SIZE)); + if (SendMessageW(leftButtons[i], BCM_GETIDEALSIZE, 0, (LPARAM) (&size)) == 0) + diele("BCM_GETIDEALSIZE"); + dwp = DeferWindowPos(dwp, leftButtons[i], NULL, + buttonx, buttony, size.cx, size.cy, + SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER); + if (dwp == NULL) + diele("DeferWindowPos()"); + buttonx += size.cx; + } + if (EndDeferWindowPos(dwp) == 0) + diele("EndDeferWindowPos()"); +} + +#if 0 +// TODO check errors +void handleEvents(HWND hwnd, WPARAM wParam) +{ + LRESULT n; + const WCHAR *selRebar = NULL, *selToolbar = NULL; + WCHAR *bufRebar = NULL, *bufToolbar = NULL; + BOOL changeRebar = FALSE, changeToolbar = FALSE; + BOOL invalidate = FALSE; + WPARAM check; + + switch (wParam) { + case MAKEWPARAM(300, CBN_SELCHANGE): + n = SendMessageW(rebarCombo, CB_GETCURSEL, 0, 0); + selRebar = rebarThemes[n]; + changeRebar = TRUE; + break; + case MAKEWPARAM(301, CBN_SELCHANGE): + n = SendMessageW(toolbarCombo, CB_GETCURSEL, 0, 0); + selToolbar = toolbarThemes[n]; + changeToolbar = TRUE; + break; + case MAKEWPARAM(200, BN_CLICKED): + drawmode = 0; + n = SendMessageW(rebarCombo, WM_GETTEXTLENGTH, 0, 0); + bufRebar = new WCHAR[n + 1]; + GetWindowTextW(rebarCombo, bufRebar, n + 1); + n = SendMessageW(toolbarCombo, WM_GETTEXTLENGTH, 0, 0); + bufToolbar = new WCHAR[n + 1]; + GetWindowTextW(toolbarCombo, bufToolbar, n + 1); + selRebar = bufRebar; + selToolbar = bufToolbar; + changeRebar = TRUE; + changeToolbar = TRUE; + break; + case MAKEWPARAM(201, BN_CLICKED): + drawmode = 1; + invalidate = TRUE; + break; + case MAKEWPARAM(302, BN_CLICKED): + ShowWindow(leftbar, SW_HIDE); + check = BST_CHECKED; + if (SendMessage(toolbarTransparentCheckbox, BM_GETCHECK, 0, 0) == BST_CHECKED) + check = BST_UNCHECKED; + SendMessage(toolbarTransparentCheckbox, BM_SETCHECK, check, 0); + if (check == BST_CHECKED) + SendMessageW(leftbar, TB_SETSTYLE, 0, toolbarStyles | TBSTYLE_LIST | TBSTYLE_TRANSPARENT); + else + SendMessageW(leftbar, TB_SETSTYLE, 0, toolbarStyles | TBSTYLE_LIST); + ShowWindow(leftbar, SW_SHOW); + break; + case MAKEWPARAM(202, BN_CLICKED): + SetFocus(leftbar); + break; + } + if (changeRebar) { + if (selRebar != NULL && wcscmp(selRebar, L"NULL") == 0) + selRebar = NULL; + if (selRebar != NULL && *selRebar != L'\0') + SendMessageW(rebar, RB_SETWINDOWTHEME, 0, (LPARAM) selRebar); + else + SetWindowTheme(rebar, selRebar, selRebar); + invalidate = TRUE; + } + if (changeToolbar) { + if (selToolbar != NULL && wcscmp(selToolbar, L"NULL") == 0) + selToolbar = NULL; + if (selToolbar != NULL && *selToolbar != L'\0') { + SendMessageW(leftbar, TB_SETWINDOWTHEME, 0, (LPARAM) selToolbar); + SendMessageW(rightbar, TB_SETWINDOWTHEME, 0, (LPARAM) selToolbar); + } else { + SetWindowTheme(leftbar, selToolbar, selToolbar); + SetWindowTheme(rightbar, selToolbar, selToolbar); + } + invalidate = TRUE; + } + if (invalidate) + InvalidateRect(hwnd, NULL, TRUE); + if (bufRebar != NULL) + delete[] bufRebar; + if (bufToolbar != NULL) + delete[] bufToolbar; +} +#endif + +LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + NMHDR *nm = (NMHDR *) lParam; + + switch (uMsg) { + case WM_CREATE: + onWM_CREATE(hwnd); + updateTheme(hwnd); + repositionButtons(hwnd); + break; + case WM_CLOSE: + PostQuitMessage(0); + break; + case WM_SIZE: + repositionButtons(hwnd); + break; + case WM_THEMECHANGED: + updateTheme(hwnd); + repositionButtons(hwnd); + break; +#if 0 + case WM_COMMAND: + handleEvents(hwnd, wParam); + break; +#endif + case WM_NOTIFY: + switch (nm->code) { + case NM_CUSTOMDRAW: +#if 0 + if (nm->hwndFrom == rebar) + return (*(drawmodes[drawmode].handleRebar))((NMCUSTOMDRAW *) nm); + else if (nm->hwndFrom == leftbar || nm->hwndFrom == rightbar) + return (*(drawmodes[drawmode].handleToolbar))((NMTBCUSTOMDRAW *) nm); +#endif + break; + } + break; + } + return DefWindowProcW(hwnd, uMsg, wParam, lParam); +} + +EXTERN_C IMAGE_DOS_HEADER __ImageBase; + +int main(void) +{ + STARTUPINFOW si; + int nCmdShow; + INITCOMMONCONTROLSEX icc; + HICON hDefaultIcon; + HCURSOR hDefaultCursor; + WNDCLASSW wc; + HWND mainwin; + MSG msg; + HRESULT hr; + + hInstance = (HINSTANCE) (&__ImageBase); + nCmdShow = SW_SHOWDEFAULT; + GetStartupInfoW(&si); + if ((si.dwFlags & STARTF_USESHOWWINDOW) != 0) + nCmdShow = si.wShowWindow; + + ZeroMemory(&icc, sizeof (INITCOMMONCONTROLSEX)); + icc.dwSize = sizeof (INITCOMMONCONTROLSEX); + icc.dwICC = ICC_STANDARD_CLASSES | ICC_BAR_CLASSES | ICC_COOL_CLASSES; + if (InitCommonControlsEx(&icc) == 0) + diele("InitCommonControlsEx()"); + + hDefaultIcon = LoadIconW(NULL, IDI_APPLICATION); + if (hDefaultIcon == NULL) + diele("LoadIconW(IDI_APPLICATION)"); + hDefaultCursor = LoadCursorW(NULL, IDC_ARROW); + if (hDefaultCursor == NULL) + diele("LoadCursorW(IDC_ARROW)"); + + hr = LoadIconMetric(NULL, IDI_SHIELD, LIM_SMALL, &shieldIcon); + if (hr != S_OK) + diehr("LoadIconMetric(IDI_SHIELD)", hr); + hr = LoadIconMetric(NULL, IDI_APPLICATION, LIM_SMALL, &applicationIcon); + if (hr != S_OK) + diehr("LoadIconMetric(IDI_APPLICATION)", hr); + hr = LoadIconMetric(NULL, IDI_QUESTION, LIM_SMALL, &helpIcon); + if (hr != S_OK) + diehr("LoadIconMetric(IDI_QUESTION)", hr); + rightList = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), + ILC_COLOR32, 0, 3); + if (rightList == NULL) + diele("ImageList_Create()"); + if (ImageList_ReplaceIcon(rightList, -1, shieldIcon) == -1) + diele("ImageList_ReplaceIcon(IDI_SHIELD)"); + if (ImageList_ReplaceIcon(rightList, -1, applicationIcon) == -1) + diele("ImageList_ReplaceIcon(IDI_APPLICATION)"); + if (ImageList_ReplaceIcon(rightList, -1, helpIcon) == -1) + diele("ImageList_ReplaceIcon(IDI_QUESTION)"); + + ZeroMemory(&wc, sizeof (WNDCLASSW)); + wc.lpszClassName = L"mainwin"; + wc.lpfnWndProc = wndproc; + wc.hInstance = hInstance; + wc.hIcon = hDefaultIcon; + wc.hCursor = hDefaultCursor; + wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1); + if (RegisterClassW(&wc) == 0) + diele("RegisterClassW()"); + + mainwin = CreateWindowExW(0, + L"mainwin", L"Main Window", + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + NULL, NULL, hInstance, NULL); + if (mainwin == NULL) + diele("CreateWindowExW(L\"mainwin\")"); + + ShowWindow(mainwin, nCmdShow); + if (UpdateWindow(mainwin) == 0) + diele("UpdateWindow()"); + + for (;;) { + int res; + + res = GetMessageW(&msg, NULL, 0, 0); + if (res < 0) + diele("GetMessageW()"); + if (res == 0) + break; + if (IsDialogMessageW(mainwin, &msg) == 0) { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + } + return 0; +} From ba9c42e5bbee70975795bb60104ebbc2d65a54b6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 15 Oct 2018 01:53:22 -0400 Subject: [PATCH 1306/1329] More theming work. We may actually have it this time! --- doc/misctests/winbuttonexplorertheme.cpp | 31 ++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/doc/misctests/winbuttonexplorertheme.cpp b/doc/misctests/winbuttonexplorertheme.cpp index 53101212..b536a4b9 100644 --- a/doc/misctests/winbuttonexplorertheme.cpp +++ b/doc/misctests/winbuttonexplorertheme.cpp @@ -209,8 +209,10 @@ void updateTheme(HWND hwnd) HDC dc; SIZE size; HBITMAP hb; - HTHEME fontTheme; + HTHEME fontTheme, buttonTheme; LOGFONTW lf; + MARGINS marginOffsets; + RECT margins; int i; if (buttonFont != NULL) { @@ -253,7 +255,32 @@ void updateTheme(HWND hwnd) bim.margin.left = 1; bim.uAlign = BUTTON_IMAGELIST_ALIGN_RIGHT; for (i = 0; i < 3; i++) - SendMessage(leftButtons[i], BCM_SETIMAGELIST, 0, (LPARAM) (&bim)); + SendMessageW(leftButtons[i], BCM_SETIMAGELIST, 0, (LPARAM) (&bim)); + + fontTheme = OpenThemeData(hwnd, L"TEXTSTYLE"); + // TODO find the right TMT constant + GetThemeFont(fontTheme, NULL, + 4, 0, + TMT_FONT, &lf); + buttonFont = CreateFontIndirectW(&lf); + CloseThemeData(fontTheme); + for (i = 0; i < 5; i++) + SendMessageW(leftButtons[i], WM_SETFONT, (WPARAM) buttonFont, TRUE); + + buttonTheme = OpenThemeData(hwnd, L"Button"); + ZeroMemory(&marginOffsets, sizeof (MARGINS)); + GetThemeMargins(buttonTheme, NULL, + BP_PUSHBUTTON, PBS_NORMAL, + TMT_CONTENTMARGINS, NULL, &marginOffsets); + CloseThemeData(buttonTheme); + // TODO the constants should be DIPs + margins.left = 13 - marginOffsets.cxLeftWidth; + margins.top = 5 - marginOffsets.cyTopHeight; + margins.right = 13 - marginOffsets.cxRightWidth; + margins.bottom = 5 - marginOffsets.cyBottomHeight; + for (i = 0; i < 5; i++) + if (SendMessageW(leftButtons[i], BCM_SETTEXTMARGIN, 0, (LPARAM) (&margins)) == FALSE) + diele("BCM_SETTEXTMARGIN"); } void repositionButtons(HWND hwnd) From 38723aeff975ff377df40e64cf39b588254afc46 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 15 Oct 2018 01:54:15 -0400 Subject: [PATCH 1307/1329] More TODOs. --- doc/misctests/winbuttonexplorertheme.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/misctests/winbuttonexplorertheme.cpp b/doc/misctests/winbuttonexplorertheme.cpp index b536a4b9..67a2fb6c 100644 --- a/doc/misctests/winbuttonexplorertheme.cpp +++ b/doc/misctests/winbuttonexplorertheme.cpp @@ -190,6 +190,7 @@ void onWM_CREATE(HWND hwnd) buttonx = 5; buttony = 5; for (i = 0; i < 5; i++) { + // TODO split buttons don't support arrow navigation? leftButtons[i] = CreateWindowExW(0, L"BUTTON", leftbarButtons[i].text, WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON, From fcec8693ca30f72e96f1d724a88fe504b2e8d146 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 15 Oct 2018 09:30:22 -0400 Subject: [PATCH 1308/1329] Started drawing the button properly. Focus rects are now in the way... --- doc/misctests/winbuttonexplorertheme.cpp | 72 +++++++----------------- 1 file changed, 19 insertions(+), 53 deletions(-) diff --git a/doc/misctests/winbuttonexplorertheme.cpp b/doc/misctests/winbuttonexplorertheme.cpp index 67a2fb6c..3c11ca47 100644 --- a/doc/misctests/winbuttonexplorertheme.cpp +++ b/doc/misctests/winbuttonexplorertheme.cpp @@ -120,64 +120,32 @@ void drawExplorerChevron(HTHEME theme, HDC dc, HWND rebar, WPARAM band, RECT *rc } // TODO check errors -LRESULT customDrawExplorerRebar(NMCUSTOMDRAW *nm) +LRESULT drawExplorerButton(NMCUSTOMDRAW *nm) { - HTHEME theme; - RECT r; - - if (nm->dwDrawStage != CDDS_PREPAINT) - return CDRF_DODEFAULT; - theme = OpenThemeData(nm->hdr.hwndFrom, L"CommandModule"); - GetClientRect(nm->hdr.hwndFrom, &r); - drawExplorerBackground(theme, nm->hdc, &r, &(nm->rc)); - // TODO dwItemSpec is often invalid?! - drawExplorerChevron(theme, nm->hdc, nm->hdr.hwndFrom, 0, &(nm->rc)); - CloseThemeData(theme); - return CDRF_SKIPDEFAULT; -} - -// TODO check errors -LRESULT customDrawExplorerToolbar(NMTBCUSTOMDRAW *nm) -{ - HWND toolbar, rebar; - WPARAM itemIndex; - TBBUTTON tbb; - HTHEME theme; + HWND button; RECT r; int part, state; - toolbar = nm->nmcd.hdr.hwndFrom; - switch (nm->nmcd.dwDrawStage) { + button = nm->hdr.hwndFrom; + switch (nm->dwDrawStage) { case CDDS_PREPAINT: - theme = OpenThemeData(toolbar, L"CommandModule"); - rebar = GetParent(toolbar); - GetWindowRect(rebar, &r); - MapWindowRect(NULL, toolbar, &r); - drawExplorerBackground(theme, nm->nmcd.hdc, &r, &(nm->nmcd.rc)); - CloseThemeData(theme); - return CDRF_NOTIFYITEMDRAW; - case CDDS_ITEMPREPAINT: - itemIndex = (WPARAM) SendMessageW(toolbar, TB_COMMANDTOINDEX, nm->nmcd.dwItemSpec, 0); - ZeroMemory(&tbb, sizeof (TBBUTTON)); - SendMessageW(toolbar, TB_GETBUTTON, itemIndex, (LPARAM) (&tbb)); - theme = OpenThemeData(toolbar, L"CommandModule"); + GetClientRect(button, &r); part = 3; - if ((tbb.fsStyle & BTNS_DROPDOWN) != 0) - part = 4; +//TODO if ((tbb.fsStyle & BTNS_DROPDOWN) != 0) +//TODO part = 4; state = 1; // TODO this doesn't work; both keyboard and mouse are listed as HOT - if ((nm->nmcd.uItemState & CDIS_FOCUS) != 0) + if ((nm->uItemState & CDIS_FOCUS) != 0) state = 4; - if ((nm->nmcd.uItemState & CDIS_HOT) != 0) + if ((nm->uItemState & CDIS_HOT) != 0) state = 2; - if ((nm->nmcd.uItemState & CDIS_SELECTED) != 0) + if ((nm->uItemState & CDIS_SELECTED) != 0) state = 3; - SendMessageW(toolbar, TB_GETITEMRECT, itemIndex, (LPARAM) (&r)); - DrawThemeBackground(theme, nm->nmcd.hdc, - 3, state, - &r, &(nm->nmcd.rc)); - CloseThemeData(theme); - return TBCDRF_NOBACKGROUND; + DrawThemeParentBackground(button, nm->hdc, &(nm->rc)); + DrawThemeBackground(theme, nm->hdc, + part, state, + &r, &(nm->rc)); + return CDRF_NEWFONT; } return CDRF_DODEFAULT; } @@ -399,6 +367,7 @@ void handleEvents(HWND hwnd, WPARAM wParam) LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { NMHDR *nm = (NMHDR *) lParam; + int i; switch (uMsg) { case WM_CREATE: @@ -424,12 +393,9 @@ LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) case WM_NOTIFY: switch (nm->code) { case NM_CUSTOMDRAW: -#if 0 - if (nm->hwndFrom == rebar) - return (*(drawmodes[drawmode].handleRebar))((NMCUSTOMDRAW *) nm); - else if (nm->hwndFrom == leftbar || nm->hwndFrom == rightbar) - return (*(drawmodes[drawmode].handleToolbar))((NMTBCUSTOMDRAW *) nm); -#endif + for (i = 0; i < 5; i++) + if (nm->hwndFrom == leftButtons[i]) + return drawExplorerButton((NMCUSTOMDRAW *) nm); break; } break; From 83ba0b1a4151ef14c0cf6835d9a100582004f862 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 16 Oct 2018 11:25:22 -0400 Subject: [PATCH 1309/1329] More experiments in properly sizing our themed buttons since I'll just be custom-drawing them in their entirety based on the UIFILE's specification of content alignment. --- doc/misctests/winbuttonexplorertheme.cpp | 68 +++++++++++++++++++++--- 1 file changed, 61 insertions(+), 7 deletions(-) diff --git a/doc/misctests/winbuttonexplorertheme.cpp b/doc/misctests/winbuttonexplorertheme.cpp index 3c11ca47..eba7fa21 100644 --- a/doc/misctests/winbuttonexplorertheme.cpp +++ b/doc/misctests/winbuttonexplorertheme.cpp @@ -44,6 +44,7 @@ HICON helpIcon; HIMAGELIST rightList; HTHEME theme = NULL; +HTHEME textstyleTheme = NULL; HIMAGELIST dropdownArrowList = NULL; HFONT buttonFont = NULL; @@ -178,7 +179,7 @@ void updateTheme(HWND hwnd) HDC dc; SIZE size; HBITMAP hb; - HTHEME fontTheme, buttonTheme; + HTHEME buttonTheme; LOGFONTW lf; MARGINS marginOffsets; RECT margins; @@ -198,12 +199,17 @@ void updateTheme(HWND hwnd) ImageList_Destroy(dropdownArrowList); dropdownArrowList = NULL; } + if (textstyleTheme != NULL) { + CloseThemeData(textstyleTheme); + textstyleTheme = NULL; + } if (theme != NULL) { CloseThemeData(theme); theme = NULL; } theme = OpenThemeData(hwnd, L"CommandModule"); + textstyleTheme = OpenThemeData(hwnd, L"TEXTSTYLE"); dc = GetDC(hwnd); // TS_MIN returns 1x1 and TS_DRAW returns 0x0, so... GetThemePartSize(theme, dc, @@ -226,13 +232,11 @@ void updateTheme(HWND hwnd) for (i = 0; i < 3; i++) SendMessageW(leftButtons[i], BCM_SETIMAGELIST, 0, (LPARAM) (&bim)); - fontTheme = OpenThemeData(hwnd, L"TEXTSTYLE"); // TODO find the right TMT constant - GetThemeFont(fontTheme, NULL, + GetThemeFont(textstyleTheme, NULL, 4, 0, TMT_FONT, &lf); buttonFont = CreateFontIndirectW(&lf); - CloseThemeData(fontTheme); for (i = 0; i < 5; i++) SendMessageW(leftButtons[i], WM_SETFONT, (WPARAM) buttonFont, TRUE); @@ -252,6 +256,58 @@ void updateTheme(HWND hwnd) diele("BCM_SETTEXTMARGIN"); } +// TODO check errors +SIZE buttonSize(HWND button) +{ + HDC dc; + LRESULT n; + WCHAR *buf; + RECT textRect; + RECT contentRect; + RECT extentRect; + SIZE ret; + + dc = GetDC(button); + + n = SendMessageW(button, WM_GETTEXTLENGTH, 0, 0); + buf = new WCHAR[n + 1]; + GetWindowTextW(button, buf, n + 1); + GetThemeTextExtent(textstyleTheme, dc, + 4, 0, + buf, n, DT_CENTER, + NULL, &textRect); + contentRect.left = 0; + contentRect.top = 0; + contentRect.right = textRect.right - textRect.left; + contentRect.bottom = textRect.bottom - textRect.top; + + if (button == leftButtons[0] || button == leftButtons[1] || button == leftButtons[2]) { + SIZE arrowSize; + + // TODO this should be in DIPs + contentRect.right += 1; + // TS_MIN returns 1x1 and TS_DRAW returns 0x0, so... + GetThemePartSize(theme, dc, + 6, 0, + NULL, TS_TRUE, &arrowSize); + contentRect.right += arrowSize.cx; + if (contentRect.bottom < arrowSize.cy) + contentRect.bottom = arrowSize.cy; + } + + // TODO these should be DIPs + contentRect.right += 13 * 2; + contentRect.bottom += 5 * 2; + GetThemeBackgroundExtent(theme, dc, + 3, 1, + &contentRect, &extentRect); + + ReleaseDC(button, dc); + ret.cx = extentRect.right - extentRect.left; + ret.cy = extentRect.bottom - extentRect.top; + return ret; +} + void repositionButtons(HWND hwnd) { HDWP dwp; @@ -265,9 +321,7 @@ void repositionButtons(HWND hwnd) buttonx = 5; buttony = 5; for (i = 0; i < 5; i++) { - ZeroMemory(&size, sizeof (SIZE)); - if (SendMessageW(leftButtons[i], BCM_GETIDEALSIZE, 0, (LPARAM) (&size)) == 0) - diele("BCM_GETIDEALSIZE"); + size = buttonSize(leftButtons[i]); dwp = DeferWindowPos(dwp, leftButtons[i], NULL, buttonx, buttony, size.cx, size.cy, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER); From 7e34fac79da621d89032027b6f9e29d2fb6b3f39 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 16 Oct 2018 23:19:23 -0400 Subject: [PATCH 1310/1329] More experimentation. DirectUI is doing something with the sizing that I'm not... --- doc/misctests/winbuttonexplorertheme.cpp | 65 ++++++++++++++---------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/doc/misctests/winbuttonexplorertheme.cpp b/doc/misctests/winbuttonexplorertheme.cpp index eba7fa21..be7be867 100644 --- a/doc/misctests/winbuttonexplorertheme.cpp +++ b/doc/misctests/winbuttonexplorertheme.cpp @@ -126,29 +126,38 @@ LRESULT drawExplorerButton(NMCUSTOMDRAW *nm) HWND button; RECT r; int part, state; + LRESULT n; + WCHAR *buf; + if (nm->dwDrawStage != CDDS_PREPAINT) + return CDRF_DODEFAULT; button = nm->hdr.hwndFrom; - switch (nm->dwDrawStage) { - case CDDS_PREPAINT: - GetClientRect(button, &r); - part = 3; -//TODO if ((tbb.fsStyle & BTNS_DROPDOWN) != 0) -//TODO part = 4; - state = 1; - // TODO this doesn't work; both keyboard and mouse are listed as HOT - if ((nm->uItemState & CDIS_FOCUS) != 0) - state = 4; - if ((nm->uItemState & CDIS_HOT) != 0) - state = 2; - if ((nm->uItemState & CDIS_SELECTED) != 0) - state = 3; - DrawThemeParentBackground(button, nm->hdc, &(nm->rc)); - DrawThemeBackground(theme, nm->hdc, - part, state, - &r, &(nm->rc)); - return CDRF_NEWFONT; - } - return CDRF_DODEFAULT; + GetClientRect(button, &r); + part = 3; +//TODO if ((tbb.fsStyle & BTNS_DROPDOWN) != 0) +//TODO part = 4; + state = 1; + // TODO this doesn't work; both keyboard and mouse are listed as HOT + if ((nm->uItemState & CDIS_FOCUS) != 0) + state = 4; + if ((nm->uItemState & CDIS_HOT) != 0) + state = 2; + if ((nm->uItemState & CDIS_SELECTED) != 0) + state = 3; + DrawThemeParentBackground(button, nm->hdc, &(nm->rc)); + DrawThemeBackground(theme, nm->hdc, + part, state, + &r, &(nm->rc)); + n = SendMessageW(button, WM_GETTEXTLENGTH, 0, 0); + buf = new WCHAR[n + 1]; + GetWindowTextW(button, buf, n + 1); + SetBkMode(nm->hdc, TRANSPARENT); + DrawThemeText(textstyleTheme, nm->hdc, + 4, 0, + buf, n, DT_CENTER | DT_VCENTER | DT_SINGLELINE, + 0, &r); + delete[] buf; + return CDRF_SKIPDEFAULT; } void onWM_CREATE(HWND hwnd) @@ -264,11 +273,14 @@ SIZE buttonSize(HWND button) WCHAR *buf; RECT textRect; RECT contentRect; - RECT extentRect; + MARGINS margins; SIZE ret; dc = GetDC(button); +printf("%08I32X ", GetThemePartSize(theme, dc, 3, 1, NULL, TS_TRUE, &ret)); +printf("%d %d\n", ret.cx, ret.cy); + n = SendMessageW(button, WM_GETTEXTLENGTH, 0, 0); buf = new WCHAR[n + 1]; GetWindowTextW(button, buf, n + 1); @@ -280,6 +292,8 @@ SIZE buttonSize(HWND button) contentRect.top = 0; contentRect.right = textRect.right - textRect.left; contentRect.bottom = textRect.bottom - textRect.top; + delete[] buf; +printf("%d %d\n", contentRect.right, contentRect.bottom); if (button == leftButtons[0] || button == leftButtons[1] || button == leftButtons[2]) { SIZE arrowSize; @@ -298,13 +312,10 @@ SIZE buttonSize(HWND button) // TODO these should be DIPs contentRect.right += 13 * 2; contentRect.bottom += 5 * 2; - GetThemeBackgroundExtent(theme, dc, - 3, 1, - &contentRect, &extentRect); ReleaseDC(button, dc); - ret.cx = extentRect.right - extentRect.left; - ret.cy = extentRect.bottom - extentRect.top; + ret.cx = contentRect.right - contentRect.left; + ret.cy = contentRect.bottom - contentRect.top; return ret; } From 24e8649c87531d3bb62020948427a5741337bcce Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 18 Oct 2018 22:32:53 -0400 Subject: [PATCH 1311/1329] More experiments. This is getting absurd. --- doc/misctests/winbuttonexplorertheme.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/doc/misctests/winbuttonexplorertheme.cpp b/doc/misctests/winbuttonexplorertheme.cpp index be7be867..61924642 100644 --- a/doc/misctests/winbuttonexplorertheme.cpp +++ b/doc/misctests/winbuttonexplorertheme.cpp @@ -260,9 +260,9 @@ void updateTheme(HWND hwnd) margins.top = 5 - marginOffsets.cyTopHeight; margins.right = 13 - marginOffsets.cxRightWidth; margins.bottom = 5 - marginOffsets.cyBottomHeight; - for (i = 0; i < 5; i++) - if (SendMessageW(leftButtons[i], BCM_SETTEXTMARGIN, 0, (LPARAM) (&margins)) == FALSE) - diele("BCM_SETTEXTMARGIN"); +// for (i = 0; i < 5; i++) +// if (SendMessageW(leftButtons[i], BCM_SETTEXTMARGIN, 0, (LPARAM) (&margins)) == FALSE) +// diele("BCM_SETTEXTMARGIN"); } // TODO check errors @@ -273,7 +273,7 @@ SIZE buttonSize(HWND button) WCHAR *buf; RECT textRect; RECT contentRect; - MARGINS margins; + SIZE minSize; SIZE ret; dc = GetDC(button); @@ -313,6 +313,15 @@ printf("%d %d\n", contentRect.right, contentRect.bottom); contentRect.right += 13 * 2; contentRect.bottom += 5 * 2; + // dui70.dll seems to do this, including the TS_TRUE part... + GetThemePartSize(theme, dc, + 3, 1, + NULL, TS_TRUE, &minSize); + if (contentRect.right < minSize.cx) + contentRect.right = minSize.cx; + if (contentRect.bottom < minSize.cy) + contentRect.bottom = minSize.cy; + ReleaseDC(button, dc); ret.cx = contentRect.right - contentRect.left; ret.cy = contentRect.bottom - contentRect.top; From 71ddf6ce91675cd27c956557b820afa70da0078c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 19 Oct 2018 22:22:45 -0400 Subject: [PATCH 1312/1329] Figured out more stuff about our button size woes. --- doc/misctests/winbuttonexplorertheme.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/misctests/winbuttonexplorertheme.cpp b/doc/misctests/winbuttonexplorertheme.cpp index 61924642..911d7116 100644 --- a/doc/misctests/winbuttonexplorertheme.cpp +++ b/doc/misctests/winbuttonexplorertheme.cpp @@ -266,6 +266,8 @@ void updateTheme(HWND hwnd) } // TODO check errors +// TODO the width is always off by 4 pixels somehow (according to UI Automation) +// TODO the height is correct (according to UI Automation) but they don't visually match? SIZE buttonSize(HWND button) { HDC dc; @@ -325,6 +327,7 @@ printf("%d %d\n", contentRect.right, contentRect.bottom); ReleaseDC(button, dc); ret.cx = contentRect.right - contentRect.left; ret.cy = contentRect.bottom - contentRect.top; +printf("FINAL %d %d\n", ret.cx, ret.cy); return ret; } From 836e0dc2c7eea724d2285128c3988b5db195617f Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 20 Oct 2018 19:22:02 -0400 Subject: [PATCH 1313/1329] Resolved sizing woes for now. --- doc/misctests/winbuttonexplorertheme.cpp | 37 ++++++++++++++---------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/doc/misctests/winbuttonexplorertheme.cpp b/doc/misctests/winbuttonexplorertheme.cpp index 911d7116..cdcf8bf8 100644 --- a/doc/misctests/winbuttonexplorertheme.cpp +++ b/doc/misctests/winbuttonexplorertheme.cpp @@ -266,8 +266,7 @@ void updateTheme(HWND hwnd) } // TODO check errors -// TODO the width is always off by 4 pixels somehow (according to UI Automation) -// TODO the height is correct (according to UI Automation) but they don't visually match? +// TODO the sizes are correct (according to UI Automation) but they don't visually match? SIZE buttonSize(HWND button) { HDC dc; @@ -275,14 +274,13 @@ SIZE buttonSize(HWND button) WCHAR *buf; RECT textRect; RECT contentRect; - SIZE minSize; + LOGFONTW lf; + TEXTMETRICW tm; + int minStdButtonHeight; SIZE ret; dc = GetDC(button); -printf("%08I32X ", GetThemePartSize(theme, dc, 3, 1, NULL, TS_TRUE, &ret)); -printf("%d %d\n", ret.cx, ret.cy); - n = SendMessageW(button, WM_GETTEXTLENGTH, 0, 0); buf = new WCHAR[n + 1]; GetWindowTextW(button, buf, n + 1); @@ -294,8 +292,15 @@ printf("%d %d\n", ret.cx, ret.cy); contentRect.top = 0; contentRect.right = textRect.right - textRect.left; contentRect.bottom = textRect.bottom - textRect.top; +//wprintf(L"%s ", buf); delete[] buf; -printf("%d %d\n", contentRect.right, contentRect.bottom); + // dui70.dll adds this to the width when "overhang" is enabled, and it seems to be enabled for our cases, but I can't tell what conditions it's enabled for... + // and yes, it seems to be using the raw lfHeight value here :/ + // TODO find the right TMT constant + GetThemeFont(textstyleTheme, dc, + 4, 0, + TMT_FONT, &lf); + contentRect.right += 2 * (abs(lf.lfHeight) / 6); if (button == leftButtons[0] || button == leftButtons[1] || button == leftButtons[2]) { SIZE arrowSize; @@ -307,6 +312,7 @@ printf("%d %d\n", contentRect.right, contentRect.bottom); 6, 0, NULL, TS_TRUE, &arrowSize); contentRect.right += arrowSize.cx; + // TODO I don't think dui70.dll takes this into consideration... if (contentRect.bottom < arrowSize.cy) contentRect.bottom = arrowSize.cy; } @@ -315,19 +321,18 @@ printf("%d %d\n", contentRect.right, contentRect.bottom); contentRect.right += 13 * 2; contentRect.bottom += 5 * 2; - // dui70.dll seems to do this, including the TS_TRUE part... - GetThemePartSize(theme, dc, - 3, 1, - NULL, TS_TRUE, &minSize); - if (contentRect.right < minSize.cx) - contentRect.right = minSize.cx; - if (contentRect.bottom < minSize.cy) - contentRect.bottom = minSize.cy; + // and dui70.dll seems to do a variant of this but for text buttons only... + GetThemeTextMetrics(textstyleTheme, dc, + 4, 0, + &tm); + minStdButtonHeight = MulDiv(14, tm.tmHeight, 8); + if (contentRect.bottom < minStdButtonHeight) + contentRect.bottom = minStdButtonHeight; ReleaseDC(button, dc); ret.cx = contentRect.right - contentRect.left; ret.cy = contentRect.bottom - contentRect.top; -printf("FINAL %d %d\n", ret.cx, ret.cy); +//printf("%d %d\n", ret.cx, ret.cy); return ret; } From d15dfa06a8a445b78b23d5d93b1dcd32fbcf70d4 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 21 Oct 2018 22:23:11 -0400 Subject: [PATCH 1314/1329] More text drawing work. I might need to make a generic button metrics function for this instead. --- doc/misctests/winbuttonexplorertheme.cpp | 53 +++++++++++++++++++++--- 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/doc/misctests/winbuttonexplorertheme.cpp b/doc/misctests/winbuttonexplorertheme.cpp index cdcf8bf8..80643486 100644 --- a/doc/misctests/winbuttonexplorertheme.cpp +++ b/doc/misctests/winbuttonexplorertheme.cpp @@ -120,6 +120,8 @@ void drawExplorerChevron(HTHEME theme, HDC dc, HWND rebar, WPARAM band, RECT *rc &r, rcPaint); } +#define hasNonsplitArrow(button) ((button) == leftButtons[0] || (button) == leftButtons[1] || (button) == leftButtons[2]) + // TODO check errors LRESULT drawExplorerButton(NMCUSTOMDRAW *nm) { @@ -128,6 +130,11 @@ LRESULT drawExplorerButton(NMCUSTOMDRAW *nm) int part, state; LRESULT n; WCHAR *buf; + int textState; + COLORREF textColor; + RECT textRect; + RECT arrowRect; + DTTOPTS dttopts; if (nm->dwDrawStage != CDDS_PREPAINT) return CDRF_DODEFAULT; @@ -148,15 +155,49 @@ LRESULT drawExplorerButton(NMCUSTOMDRAW *nm) DrawThemeBackground(theme, nm->hdc, part, state, &r, &(nm->rc)); + + // TODO these values are only for part==3 + textState = 1; + if ((nm->uItemState & CDIS_DISABLED) != 0) + textState = 6; + // TODO name the constant for the property ID + GetThemeColor(theme, + 3, textState, + 3803, &textColor); n = SendMessageW(button, WM_GETTEXTLENGTH, 0, 0); buf = new WCHAR[n + 1]; GetWindowTextW(button, buf, n + 1); - SetBkMode(nm->hdc, TRANSPARENT); - DrawThemeText(textstyleTheme, nm->hdc, + textRect = r; + if (hasNonsplitArrow(button)) { + SIZE arrowSize; + + // TS_MIN returns 1x1 and TS_DRAW returns 0x0, so... + GetThemePartSize(theme, nm->hdc, + 6, 0, + NULL, TS_TRUE, &arrowSize); + textRect.right -= arrowSize.cx; + arrowRect.left = textRect.right; + arrowRect.top = textRect.top + (textRect.bottom - textRect.top - arrowSize.cy) / 2; + arrowRect.right = arrowRect.left + arrowSize.cx; + arrowRect.bottom = arrowRect.top + arrowSize.cy; + // TODO this should be in DIPs + textRect.right -= 1; + } + ZeroMemory(&dttopts, sizeof (DTTOPTS)); + dttopts.dwSize = sizeof (DTTOPTS); + dttopts.dwFlags = DTT_TEXTCOLOR; + dttopts.crText = textColor; + DrawThemeTextEx(textstyleTheme, nm->hdc, 4, 0, buf, n, DT_CENTER | DT_VCENTER | DT_SINGLELINE, - 0, &r); + &textRect, &dttopts); delete[] buf; + + if (hasNonsplitArrow(button)) + DrawThemeBackground(theme, nm->hdc, + 6, 0, + &arrowRect, &(nm->rc)); + return CDRF_SKIPDEFAULT; } @@ -302,11 +343,9 @@ SIZE buttonSize(HWND button) TMT_FONT, &lf); contentRect.right += 2 * (abs(lf.lfHeight) / 6); - if (button == leftButtons[0] || button == leftButtons[1] || button == leftButtons[2]) { + if (hasNonsplitArrow(button)) { SIZE arrowSize; - // TODO this should be in DIPs - contentRect.right += 1; // TS_MIN returns 1x1 and TS_DRAW returns 0x0, so... GetThemePartSize(theme, dc, 6, 0, @@ -315,6 +354,8 @@ SIZE buttonSize(HWND button) // TODO I don't think dui70.dll takes this into consideration... if (contentRect.bottom < arrowSize.cy) contentRect.bottom = arrowSize.cy; + // TODO this should be in DIPs + contentRect.right += 1; } // TODO these should be DIPs From 9e6baf0a61cb0ad4461fef6e3296cf3af3fa68b2 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 26 Oct 2018 23:23:08 -0400 Subject: [PATCH 1315/1329] And cleaned up the button sizing, metrics, and drawing code. It seems to position the content correctly still! :D --- doc/misctests/winbuttonexplorertheme.cpp | 296 +++++++++++++---------- 1 file changed, 173 insertions(+), 123 deletions(-) diff --git a/doc/misctests/winbuttonexplorertheme.cpp b/doc/misctests/winbuttonexplorertheme.cpp index 80643486..badc9202 100644 --- a/doc/misctests/winbuttonexplorertheme.cpp +++ b/doc/misctests/winbuttonexplorertheme.cpp @@ -88,6 +88,141 @@ void drawExplorerBackground(HTHEME theme, HDC dc, RECT *rcWindow, RECT *rcPaint) rcWindow, rcPaint); } +#define hasNonsplitArrow(button) ((button) == leftButtons[0] || (button) == leftButtons[1] || (button) == leftButtons[2]) + +// all coordinates are in client space +struct buttonMetrics { + SIZE fittingSize; + int baseX; + int baseY; + int dpiX; + int dpiY; + BOOL hasText; + SIZE textSize; + BOOL hasArrow; + SIZE arrowSize; +}; + +#define dlgUnitsToX(dlg, baseX) MulDiv((dlg), (baseX), 4) +#define dlgUnitsToY(dlg, baseY) MulDiv((dlg), (baseY), 8) +// TODO verify the parameter order +#define dipsToX(dip, dpiX) MulDiv((dip), (dpiX), 96) +#define dipsToY(dip, dpiY) MulDiv((dip), (dpiY), 96) + +// TODO check errors +// TODO the sizes are correct (according to UI Automation) but they don't visually match? +void buttonMetrics(HWND button, HDC dc, struct buttonMetrics *m) +{ + BOOL releaseDC; + TEXTMETRICW tm; + RECT r; + int minStdButtonHeight; + + releaseDC = FALSE; + if (dc == NULL) { + dc = GetDC(button); + releaseDC = TRUE; + } + + ZeroMemory(&tm, sizeof (TEXTMETRICW)); + GetThemeTextMetrics(textstyleTheme, dc, + 4, 0, + &tm); + GetThemeTextExtent(textstyleTheme, dc, + 4, 0, + L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 52, 0, + NULL, &r); + m->baseX = (int) (((r.right - r.left) / 26 + 1) / 2); + m->baseY = (int) tm.tmHeight; + m->dpiX = GetDeviceCaps(dc, LOGPIXELSX); + m->dpiY = GetDeviceCaps(dc, LOGPIXELSY); + + m->fittingSize.cx = 0; + m->fittingSize.cy = 0; + + m->hasText = TRUE; + if (m->hasText) { + LRESULT n; + WCHAR *buf; + LOGFONTW lf; + + n = SendMessageW(button, WM_GETTEXTLENGTH, 0, 0); + buf = new WCHAR[n + 1]; + GetWindowTextW(button, buf, n + 1); + GetThemeTextExtent(textstyleTheme, dc, + 4, 0, + buf, n, DT_CENTER, + NULL, &r); + m->textSize.cx = r.right - r.left; + m->textSize.cy = r.bottom - r.top; + delete[] buf; + m->fittingSize.cx += m->textSize.cx; + m->fittingSize.cy += m->textSize.cy; + + // dui70.dll adds this to the width when "overhang" is enabled, and it seems to be enabled for our cases, but I can't tell what conditions it's enabled for... + // and yes, it seems to be using the raw lfHeight value here :/ + // TODO find the right TMT constant + GetThemeFont(textstyleTheme, dc, + 4, 0, + TMT_FONT, &lf); + m->fittingSize.cx += 2 * (abs(lf.lfHeight) / 6); + } + + m->hasArrow = hasNonsplitArrow(button); + if (m->hasArrow) { + // TS_MIN returns 1x1 and TS_DRAW returns 0x0, so... + GetThemePartSize(theme, dc, + 6, 0, + NULL, TS_TRUE, &(m->arrowSize)); + m->fittingSize.cx += m->arrowSize.cx; + // TODO I don't think dui70.dll takes this into consideration... + if (m->fittingSize.cy < m->arrowSize.cy) + m->fittingSize.cy = m->arrowSize.cy; + m->fittingSize.cx += dipsToX(1, m->dpiX); + } + + m->fittingSize.cx += dipsToX(13, m->dpiX) * 2; + m->fittingSize.cy += dipsToY(5, m->dpiY) * 2; + + // and dui70.dll seems to do a variant of this but for text buttons only... + minStdButtonHeight = dlgUnitsToY(14, m->baseY); + if (m->fittingSize.cy < minStdButtonHeight) + m->fittingSize.cy = minStdButtonHeight; + + if (releaseDC) + ReleaseDC(button, dc); +} + +struct buttonRects { + RECT clientRect; + RECT textRect; + RECT arrowRect; +}; + +// TODO check errors +void buttonRects(HWND button, struct buttonMetrics *m, struct buttonRects *r) +{ + GetClientRect(button, &(r->clientRect)); + + if (m->hasText) + r->textRect = r->clientRect; + + if (m->hasArrow) { + r->arrowRect = r->clientRect; + r->arrowRect.left = r->arrowRect.right; + r->arrowRect.left -= dipsToX(13, m->dpiX); + r->arrowRect.right = r->arrowRect.left; + r->arrowRect.left -= m->arrowSize.cx; + r->arrowRect.top += ((r->arrowRect.bottom - r->arrowRect.top) - m->arrowSize.cy) / 2; + r->arrowRect.bottom = r->arrowRect.top + m->arrowSize.cy; + + if (m->hasText) { + r->textRect.right = r->arrowRect.left - dipsToX(1, m->dpiX); + r->textRect.right += dipsToX(13, m->dpiX); + } + } +} + // TODO check errors void drawExplorerChevron(HTHEME theme, HDC dc, HWND rebar, WPARAM band, RECT *rcPaint) { @@ -120,26 +255,20 @@ void drawExplorerChevron(HTHEME theme, HDC dc, HWND rebar, WPARAM band, RECT *rc &r, rcPaint); } -#define hasNonsplitArrow(button) ((button) == leftButtons[0] || (button) == leftButtons[1] || (button) == leftButtons[2]) - // TODO check errors LRESULT drawExplorerButton(NMCUSTOMDRAW *nm) { HWND button; - RECT r; + struct buttonMetrics m; + struct buttonRects r; int part, state; - LRESULT n; - WCHAR *buf; - int textState; - COLORREF textColor; - RECT textRect; - RECT arrowRect; - DTTOPTS dttopts; if (nm->dwDrawStage != CDDS_PREPAINT) return CDRF_DODEFAULT; button = nm->hdr.hwndFrom; - GetClientRect(button, &r); + buttonMetrics(button, nm->hdc, &m); + buttonRects(button, &m, &r); + part = 3; //TODO if ((tbb.fsStyle & BTNS_DROPDOWN) != 0) //TODO part = 4; @@ -154,49 +283,41 @@ LRESULT drawExplorerButton(NMCUSTOMDRAW *nm) DrawThemeParentBackground(button, nm->hdc, &(nm->rc)); DrawThemeBackground(theme, nm->hdc, part, state, - &r, &(nm->rc)); + &(r.clientRect), &(nm->rc)); - // TODO these values are only for part==3 - textState = 1; - if ((nm->uItemState & CDIS_DISABLED) != 0) - textState = 6; - // TODO name the constant for the property ID - GetThemeColor(theme, - 3, textState, - 3803, &textColor); - n = SendMessageW(button, WM_GETTEXTLENGTH, 0, 0); - buf = new WCHAR[n + 1]; - GetWindowTextW(button, buf, n + 1); - textRect = r; - if (hasNonsplitArrow(button)) { - SIZE arrowSize; + if (m.hasText) { + int textState; + COLORREF textColor; + LRESULT n; + WCHAR *buf; + DTTOPTS dttopts; - // TS_MIN returns 1x1 and TS_DRAW returns 0x0, so... - GetThemePartSize(theme, nm->hdc, - 6, 0, - NULL, TS_TRUE, &arrowSize); - textRect.right -= arrowSize.cx; - arrowRect.left = textRect.right; - arrowRect.top = textRect.top + (textRect.bottom - textRect.top - arrowSize.cy) / 2; - arrowRect.right = arrowRect.left + arrowSize.cx; - arrowRect.bottom = arrowRect.top + arrowSize.cy; - // TODO this should be in DIPs - textRect.right -= 1; + // TODO these values are only for part==3 + textState = 1; + if ((nm->uItemState & CDIS_DISABLED) != 0) + textState = 6; + // TODO name the constant for the property ID + GetThemeColor(theme, + 3, textState, + 3803, &textColor); + n = SendMessageW(button, WM_GETTEXTLENGTH, 0, 0); + buf = new WCHAR[n + 1]; + GetWindowTextW(button, buf, n + 1); + ZeroMemory(&dttopts, sizeof (DTTOPTS)); + dttopts.dwSize = sizeof (DTTOPTS); + dttopts.dwFlags = DTT_TEXTCOLOR; + dttopts.crText = textColor; + DrawThemeTextEx(textstyleTheme, nm->hdc, + 4, 0, + buf, n, DT_CENTER | DT_VCENTER | DT_SINGLELINE, + &(r.textRect), &dttopts); + delete[] buf; } - ZeroMemory(&dttopts, sizeof (DTTOPTS)); - dttopts.dwSize = sizeof (DTTOPTS); - dttopts.dwFlags = DTT_TEXTCOLOR; - dttopts.crText = textColor; - DrawThemeTextEx(textstyleTheme, nm->hdc, - 4, 0, - buf, n, DT_CENTER | DT_VCENTER | DT_SINGLELINE, - &textRect, &dttopts); - delete[] buf; - if (hasNonsplitArrow(button)) + if (m.hasArrow) DrawThemeBackground(theme, nm->hdc, 6, 0, - &arrowRect, &(nm->rc)); + &(r.arrowRect), &(nm->rc)); return CDRF_SKIPDEFAULT; } @@ -306,82 +427,11 @@ void updateTheme(HWND hwnd) // diele("BCM_SETTEXTMARGIN"); } -// TODO check errors -// TODO the sizes are correct (according to UI Automation) but they don't visually match? -SIZE buttonSize(HWND button) -{ - HDC dc; - LRESULT n; - WCHAR *buf; - RECT textRect; - RECT contentRect; - LOGFONTW lf; - TEXTMETRICW tm; - int minStdButtonHeight; - SIZE ret; - - dc = GetDC(button); - - n = SendMessageW(button, WM_GETTEXTLENGTH, 0, 0); - buf = new WCHAR[n + 1]; - GetWindowTextW(button, buf, n + 1); - GetThemeTextExtent(textstyleTheme, dc, - 4, 0, - buf, n, DT_CENTER, - NULL, &textRect); - contentRect.left = 0; - contentRect.top = 0; - contentRect.right = textRect.right - textRect.left; - contentRect.bottom = textRect.bottom - textRect.top; -//wprintf(L"%s ", buf); - delete[] buf; - // dui70.dll adds this to the width when "overhang" is enabled, and it seems to be enabled for our cases, but I can't tell what conditions it's enabled for... - // and yes, it seems to be using the raw lfHeight value here :/ - // TODO find the right TMT constant - GetThemeFont(textstyleTheme, dc, - 4, 0, - TMT_FONT, &lf); - contentRect.right += 2 * (abs(lf.lfHeight) / 6); - - if (hasNonsplitArrow(button)) { - SIZE arrowSize; - - // TS_MIN returns 1x1 and TS_DRAW returns 0x0, so... - GetThemePartSize(theme, dc, - 6, 0, - NULL, TS_TRUE, &arrowSize); - contentRect.right += arrowSize.cx; - // TODO I don't think dui70.dll takes this into consideration... - if (contentRect.bottom < arrowSize.cy) - contentRect.bottom = arrowSize.cy; - // TODO this should be in DIPs - contentRect.right += 1; - } - - // TODO these should be DIPs - contentRect.right += 13 * 2; - contentRect.bottom += 5 * 2; - - // and dui70.dll seems to do a variant of this but for text buttons only... - GetThemeTextMetrics(textstyleTheme, dc, - 4, 0, - &tm); - minStdButtonHeight = MulDiv(14, tm.tmHeight, 8); - if (contentRect.bottom < minStdButtonHeight) - contentRect.bottom = minStdButtonHeight; - - ReleaseDC(button, dc); - ret.cx = contentRect.right - contentRect.left; - ret.cy = contentRect.bottom - contentRect.top; -//printf("%d %d\n", ret.cx, ret.cy); - return ret; -} - void repositionButtons(HWND hwnd) { HDWP dwp; int buttonx, buttony; - SIZE size; + struct buttonMetrics m; int i; dwp = BeginDeferWindowPos(5); @@ -390,13 +440,13 @@ void repositionButtons(HWND hwnd) buttonx = 5; buttony = 5; for (i = 0; i < 5; i++) { - size = buttonSize(leftButtons[i]); + buttonMetrics(leftButtons[i], NULL, &m); dwp = DeferWindowPos(dwp, leftButtons[i], NULL, - buttonx, buttony, size.cx, size.cy, + buttonx, buttony, m.fittingSize.cx, m.fittingSize.cy, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER); if (dwp == NULL) diele("DeferWindowPos()"); - buttonx += size.cx; + buttonx += m.fittingSize.cx; } if (EndDeferWindowPos(dwp) == 0) diele("EndDeferWindowPos()"); From b82d1b0a3b7a1c33c0bb88836c3290ddd355f358 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 28 Oct 2018 18:17:37 -0400 Subject: [PATCH 1316/1329] More work; we now have the background. Oh boy, this is gonna be fun, because Vista doesn't have things set up quite right compared to 7... --- doc/misctests/winbuttonexplorertheme.cpp | 70 ++++++++++++++++++------ 1 file changed, 54 insertions(+), 16 deletions(-) diff --git a/doc/misctests/winbuttonexplorertheme.cpp b/doc/misctests/winbuttonexplorertheme.cpp index badc9202..b752aae1 100644 --- a/doc/misctests/winbuttonexplorertheme.cpp +++ b/doc/misctests/winbuttonexplorertheme.cpp @@ -60,29 +60,50 @@ static struct { }; // TODO check errors -// TODO extract colors from the theme void drawExplorerBackground(HTHEME theme, HDC dc, RECT *rcWindow, RECT *rcPaint) { - static TRIVERTEX vertices[] = { - { 0, 0, 4 << 8, 80 << 8, 130 << 8, 255 << 8 }, - { 0, 0, 17 << 8, 101 << 8, 132 << 8, 255 << 8 }, - { 0, 0, 17 << 8, 101 << 8, 132 << 8, 255 << 8 }, - { 0, 0, 29 << 8, 121 << 8, 134 << 8, 255 << 8 }, - }; + COLORREF color; + TRIVERTEX vertices[4]; static GRADIENT_RECT gr[2] = { { 0, 1 }, { 2, 3 }, }; - vertices[0].x = rcPaint->left; - vertices[0].y = 0; - vertices[1].x = rcPaint->right; - vertices[1].y = (rcWindow->bottom - rcWindow->top) / 2; - vertices[2].x = rcPaint->left; - vertices[2].y = (rcWindow->bottom - rcWindow->top) / 2; - vertices[3].x = rcPaint->right; - vertices[3].y = rcWindow->bottom - rcWindow->top; - GradientFill(dc, vertices, 4, (PVOID) gr, 2, GRADIENT_FILL_RECT_V); + // TODO get constant names + GetThemeColor(theme, + 2, 0, + 3810, &color); + vertices[0].x = rcWindow->left; + vertices[0].y = rcWindow->top; + vertices[0].Red = ((COLOR16) GetRValue(color)) << 8; + vertices[0].Green = ((COLOR16) GetGValue(color)) << 8; + vertices[0].Blue = ((COLOR16) GetBValue(color)) << 8; + vertices[0].Alpha = ((COLOR16) LOBYTE(color >> 24)) << 8; + + GetThemeColor(theme, + 2, 0, + 3811, &color); + vertices[1].x = (rcWindow->right - rcWindow->left) / 2; + vertices[1].y = rcWindow->bottom; + vertices[1].Red = ((COLOR16) GetRValue(color)) << 8; + vertices[1].Green = ((COLOR16) GetGValue(color)) << 8; + vertices[1].Blue = ((COLOR16) GetBValue(color)) << 8; + vertices[1].Alpha = ((COLOR16) LOBYTE(color >> 24)) << 8; + + vertices[2] = vertices[1]; + vertices[2].y = rcWindow->top; + + GetThemeColor(theme, + 2, 0, + 3812, &color); + vertices[3].x = rcWindow->right; + vertices[3].y = rcWindow->bottom; + vertices[3].Red = ((COLOR16) GetRValue(color)) << 8; + vertices[3].Green = ((COLOR16) GetGValue(color)) << 8; + vertices[3].Blue = ((COLOR16) GetBValue(color)) << 8; + vertices[3].Alpha = ((COLOR16) LOBYTE(color >> 24)) << 8; + + GradientFill(dc, vertices, 4, (PVOID) gr, 2, GRADIENT_FILL_RECT_H); DrawThemeBackground(theme, dc, 1, 0, rcWindow, rcPaint); @@ -539,6 +560,8 @@ void handleEvents(HWND hwnd, WPARAM wParam) LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + HDC dc; + PAINTSTRUCT ps; NMHDR *nm = (NMHDR *) lParam; int i; @@ -553,11 +576,26 @@ LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) break; case WM_SIZE: repositionButtons(hwnd); + // TODO check errors + InvalidateRect(hwnd, NULL, TRUE); break; case WM_THEMECHANGED: updateTheme(hwnd); repositionButtons(hwnd); break; + case WM_PAINT: + // TODO check errors + dc = BeginPaint(hwnd, &ps); + {RECT w; + GetClientRect(hwnd,&w); + drawExplorerBackground(theme, dc, &w, &(ps.rcPaint));} + EndPaint(hwnd, &ps); + return 0; + case WM_PRINTCLIENT: + {RECT w; + GetClientRect(hwnd,&w); + drawExplorerBackground(theme, (HDC) wParam, &w, &w);} + return 0; #if 0 case WM_COMMAND: handleEvents(hwnd, wParam); From c49151a4dc90c4f1db4ab046a413daf46d336ff9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 28 Oct 2018 22:03:15 -0400 Subject: [PATCH 1317/1329] Background now draws correctly on Vista. Something weird is going on with the theme there; the parts (except for part 1) seem to be shifted down one... and the UIFILE there uses named constants directly... --- doc/misctests/winbuttonexplorertheme.cpp | 64 ++++++++++++++++-------- 1 file changed, 42 insertions(+), 22 deletions(-) diff --git a/doc/misctests/winbuttonexplorertheme.cpp b/doc/misctests/winbuttonexplorertheme.cpp index b752aae1..0ecf9fbd 100644 --- a/doc/misctests/winbuttonexplorertheme.cpp +++ b/doc/misctests/winbuttonexplorertheme.cpp @@ -68,40 +68,60 @@ void drawExplorerBackground(HTHEME theme, HDC dc, RECT *rcWindow, RECT *rcPaint) { 0, 1 }, { 2, 3 }, }; + HRESULT hr; - // TODO get constant names - GetThemeColor(theme, - 2, 0, - 3810, &color); + // yes, Vista doesn't seem to have the colors in the theme, so get them from the UIFILE instead vertices[0].x = rcWindow->left; vertices[0].y = rcWindow->top; - vertices[0].Red = ((COLOR16) GetRValue(color)) << 8; - vertices[0].Green = ((COLOR16) GetGValue(color)) << 8; - vertices[0].Blue = ((COLOR16) GetBValue(color)) << 8; - vertices[0].Alpha = ((COLOR16) LOBYTE(color >> 24)) << 8; - - GetThemeColor(theme, + vertices[0].Red = 4 << 8; + vertices[0].Green = 80 << 8; + vertices[0].Blue = 130 << 8; + vertices[0].Alpha = 255 << 8; + // TODO get constant names + hr = GetThemeColor(theme, 2, 0, - 3811, &color); + 3810, &color); + if (hr == S_OK) { + vertices[0].Red = ((COLOR16) GetRValue(color)) << 8; + vertices[0].Green = ((COLOR16) GetGValue(color)) << 8; + vertices[0].Blue = ((COLOR16) GetBValue(color)) << 8; + vertices[0].Alpha = ((COLOR16) LOBYTE(color >> 24)) << 8; + } + vertices[1].x = (rcWindow->right - rcWindow->left) / 2; vertices[1].y = rcWindow->bottom; - vertices[1].Red = ((COLOR16) GetRValue(color)) << 8; - vertices[1].Green = ((COLOR16) GetGValue(color)) << 8; - vertices[1].Blue = ((COLOR16) GetBValue(color)) << 8; - vertices[1].Alpha = ((COLOR16) LOBYTE(color >> 24)) << 8; + vertices[1].Red = 17 << 8; + vertices[1].Green = 101 << 8; + vertices[1].Blue = 132 << 8; + vertices[1].Alpha = 255 << 8; + hr = GetThemeColor(theme, + 2, 0, + 3811, &color); + if (hr == S_OK) { + vertices[1].Red = ((COLOR16) GetRValue(color)) << 8; + vertices[1].Green = ((COLOR16) GetGValue(color)) << 8; + vertices[1].Blue = ((COLOR16) GetBValue(color)) << 8; + vertices[1].Alpha = ((COLOR16) LOBYTE(color >> 24)) << 8; + } vertices[2] = vertices[1]; vertices[2].y = rcWindow->top; - GetThemeColor(theme, - 2, 0, - 3812, &color); vertices[3].x = rcWindow->right; vertices[3].y = rcWindow->bottom; - vertices[3].Red = ((COLOR16) GetRValue(color)) << 8; - vertices[3].Green = ((COLOR16) GetGValue(color)) << 8; - vertices[3].Blue = ((COLOR16) GetBValue(color)) << 8; - vertices[3].Alpha = ((COLOR16) LOBYTE(color >> 24)) << 8; + vertices[3].Red = 29 << 8; + vertices[3].Green = 121 << 8; + vertices[3].Blue = 134 << 8; + vertices[3].Alpha = 255 << 8; + hr = GetThemeColor(theme, + 2, 0, + 3812, &color); + if (hr == S_OK) { + vertices[3].Red = ((COLOR16) GetRValue(color)) << 8; + vertices[3].Green = ((COLOR16) GetGValue(color)) << 8; + vertices[3].Blue = ((COLOR16) GetBValue(color)) << 8; + vertices[3].Alpha = ((COLOR16) LOBYTE(color >> 24)) << 8; + } GradientFill(dc, vertices, 4, (PVOID) gr, 2, GRADIENT_FILL_RECT_H); DrawThemeBackground(theme, dc, From 447dc246514c3dc76fdc7904ea225c39e08632c8 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 29 Oct 2018 21:11:16 -0400 Subject: [PATCH 1318/1329] Start of refactoring of stuff into classes! It's spaghetti already~ --- doc/misctests/winbuttonexplorertheme.cpp | 372 ++++++++++++++--------- 1 file changed, 232 insertions(+), 140 deletions(-) diff --git a/doc/misctests/winbuttonexplorertheme.cpp b/doc/misctests/winbuttonexplorertheme.cpp index 0ecf9fbd..468bd6ff 100644 --- a/doc/misctests/winbuttonexplorertheme.cpp +++ b/doc/misctests/winbuttonexplorertheme.cpp @@ -34,6 +34,14 @@ void diehr(const char *func, HRESULT hr) exit(EXIT_FAILURE); } +// TODO if we merge this into libui proper, this will need to be deduplicated +static inline HRESULT lastErrorToHRESULT(DWORD lastError) +{ + if (lastError == 0) + return E_FAIL; + return HRESULT_FROM_WIN32(lastError); +} + HINSTANCE hInstance; HWND leftButtons[5]; HWND rightButtons[3]; @@ -59,77 +67,87 @@ static struct { { L"New folder", FALSE }, }; -// TODO check errors -void drawExplorerBackground(HTHEME theme, HDC dc, RECT *rcWindow, RECT *rcPaint) -{ - COLORREF color; - TRIVERTEX vertices[4]; - static GRADIENT_RECT gr[2] = { - { 0, 1 }, - { 2, 3 }, - }; - HRESULT hr; +class commandModuleStyleParams { +public: + virtual int partID_CMOD_MODULEBACKGROUND(void) const = 0; + virtual int partID_CMOD_TASKBUTTON(void) const = 0; + virtual int partID_CMOD_SPLITBUTTONLEFT(void) const = 0; + virtual int partID_CMOD_SPLITBUTTONRIGHT(void) const = 0; + virtual int partID_CMOD_MENUGLYPH(void) const = 0; + virtual int partID_CMOD_OVERFLOWGLYPH(void) const = 0; - // yes, Vista doesn't seem to have the colors in the theme, so get them from the UIFILE instead - vertices[0].x = rcWindow->left; - vertices[0].y = rcWindow->top; - vertices[0].Red = 4 << 8; - vertices[0].Green = 80 << 8; - vertices[0].Blue = 130 << 8; - vertices[0].Alpha = 255 << 8; - // TODO get constant names - hr = GetThemeColor(theme, - 2, 0, - 3810, &color); - if (hr == S_OK) { - vertices[0].Red = ((COLOR16) GetRValue(color)) << 8; - vertices[0].Green = ((COLOR16) GetGValue(color)) << 8; - vertices[0].Blue = ((COLOR16) GetBValue(color)) << 8; - vertices[0].Alpha = ((COLOR16) LOBYTE(color >> 24)) << 8; + virtual int stateID_CMODS_NORMAL(void) const = 0; + virtual int stateID_CMODS_HOT(void) const = 0; + virtual int stateID_CMODS_PRESSED(void) const = 0; + virtual int stateID_CMODS_KEYFOCUSED(void) const = 0; + virtual int stateID_CMODS_NEARHOT(void) const = 0; + + virtual HRESULT backgroundGradientColors(HTHEME theme, COLORREF *colors) const = 0; +}; + +class commandModuleStyleParamsVista : public commandModuleStyleParams { +public: + virtual int partID_CMOD_MODULEBACKGROUND(void) const { return 1; } + virtual int partID_CMOD_TASKBUTTON(void) const { return 2; } + virtual int partID_CMOD_SPLITBUTTONLEFT(void) const { return 3; } + virtual int partID_CMOD_SPLITBUTTONRIGHT(void) const { return 4; } + virtual int partID_CMOD_MENUGLYPH(void) const { return 5; } + virtual int partID_CMOD_OVERFLOWGLYPH(void) const { return 6; } + + virtual int stateID_CMODS_NORMAL(void) const { return 1; } + virtual int stateID_CMODS_HOT(void) const { return 2; } + virtual int stateID_CMODS_PRESSED(void) const { return 3; } + virtual int stateID_CMODS_KEYFOCUSED(void) const { return 4; } + virtual int stateID_CMODS_NEARHOT(void) const { return 5; } + + virtual HRESULT backgroundGradientColors(HTHEME theme, COLORREF *colors) const + { + if (colors == NULL) + return E_POINTER; +#define argb(a, r, g, b) ((((COLORREF) ((BYTE) (a))) << 24) | RGB(r, g, b)) + colors[0] = argb(255, 4, 80, 130); + colors[1] = argb(255, 17, 101, 132); + colors[2] = argb(255, 29, 121, 134); +#undef argb + return S_OK; } +}; - vertices[1].x = (rcWindow->right - rcWindow->left) / 2; - vertices[1].y = rcWindow->bottom; - vertices[1].Red = 17 << 8; - vertices[1].Green = 101 << 8; - vertices[1].Blue = 132 << 8; - vertices[1].Alpha = 255 << 8; - hr = GetThemeColor(theme, - 2, 0, - 3811, &color); - if (hr == S_OK) { - vertices[1].Red = ((COLOR16) GetRValue(color)) << 8; - vertices[1].Green = ((COLOR16) GetGValue(color)) << 8; - vertices[1].Blue = ((COLOR16) GetBValue(color)) << 8; - vertices[1].Alpha = ((COLOR16) LOBYTE(color >> 24)) << 8; +class commandModuleStyleParams7 : public commandModuleStyleParams { + virtual int partID_CMOD_MODULEBACKGROUND(void) const { return 1; } + virtual int partID_CMOD_TASKBUTTON(void) const { return 3; } + virtual int partID_CMOD_SPLITBUTTONLEFT(void) const { return 4; } + virtual int partID_CMOD_SPLITBUTTONRIGHT(void) const { return 5; } + virtual int partID_CMOD_MENUGLYPH(void) const { return 6; } + virtual int partID_CMOD_OVERFLOWGLYPH(void) const { return 7; } + + virtual int stateID_CMODS_NORMAL(void) const { return 1; } + virtual int stateID_CMODS_HOT(void) const { return 2; } + virtual int stateID_CMODS_PRESSED(void) const { return 3; } + virtual int stateID_CMODS_KEYFOCUSED(void) const { return 4; } + /*TODO verify this*/virtual int stateID_CMODS_NEARHOT(void) const { return 5; } + + virtual HRESULT backgroundGradientColors(HTHEME theme, COLORREF *colors) const + { + HRESULT hr; + + if (colors == NULL) + return E_POINTER; + hr = GetThemeColor(theme, + 2, 0, + TMT_GRADIENTCOLOR1. color + 0); + if (hr != S_OK) + return hr; + hr = GetThemeColor(theme, + 2, 0, + TMT_GRADIENTCOLOR2, color + 1); + if (hr != S_OK) + return hr; + return GetThemeColor(theme, + 2, 0, + TMT_GRADIENTCOLOR3, color + 2); } - - vertices[2] = vertices[1]; - vertices[2].y = rcWindow->top; - - vertices[3].x = rcWindow->right; - vertices[3].y = rcWindow->bottom; - vertices[3].Red = 29 << 8; - vertices[3].Green = 121 << 8; - vertices[3].Blue = 134 << 8; - vertices[3].Alpha = 255 << 8; - hr = GetThemeColor(theme, - 2, 0, - 3812, &color); - if (hr == S_OK) { - vertices[3].Red = ((COLOR16) GetRValue(color)) << 8; - vertices[3].Green = ((COLOR16) GetGValue(color)) << 8; - vertices[3].Blue = ((COLOR16) GetBValue(color)) << 8; - vertices[3].Alpha = ((COLOR16) LOBYTE(color >> 24)) << 8; - } - - GradientFill(dc, vertices, 4, (PVOID) gr, 2, GRADIENT_FILL_RECT_H); - DrawThemeBackground(theme, dc, - 1, 0, - rcWindow, rcPaint); -} - -#define hasNonsplitArrow(button) ((button) == leftButtons[0] || (button) == leftButtons[1] || (button) == leftButtons[2]) +}; // all coordinates are in client space struct buttonMetrics { @@ -144,95 +162,169 @@ struct buttonMetrics { SIZE arrowSize; }; +class commandModuleStyle { +public: + virtual HRESULT drawFolderBar(commandModuleStyleParams *p, HDC dc, RECT *rcWindow, RECT *rcPaint) const = 0; + virtual HRESULT buttonMetrics(commandModuleStyleParams *p, HWND button, HDC dc, struct buttonMetrics *m) const = 0; +}; + +class commandModuleStyleThemed : public commandModuleStyle { + HTHEME theme; +public: + commandModuleStyleThemed(theme) { this->theme = theme; } + + virtual HRESULT drawFolderBar(commandModuleStyleParams *p, HDC dc, RECT *rcWindow, RECT *rcPaint) const + { + COLORREF colors[3]; + TRIVERTEX vertices[4]; + static GRADIENT_RECT gr[2] = { + { 0, 1 }, + { 2, 3 }, + }; + HRESULT hr; + + hr = p->backgroundGradientColors(this->theme, colors); + if (hr != S_OK) + return hr; + + vertices[0].x = rcWindow->left; + vertices[0].y = rcWindow->top; + vertices[0].Red = ((COLOR16) GetRValue(colors[0])) << 8; + vertices[0].Green = ((COLOR16) GetGValue(colors[0])) << 8; + vertices[0].Blue = ((COLOR16) GetBValue(colors[0])) << 8; + vertices[0].Alpha = ((COLOR16) LOBYTE(colors[0] >> 24)) << 8; + + vertices[1].x = (rcWindow->right - rcWindow->left) / 2; + vertices[1].y = rcWindow->bottom; + vertices[1].Red = ((COLOR16) GetRValue(colors[1])) << 8; + vertices[1].Green = ((COLOR16) GetGValue(colors[1])) << 8; + vertices[1].Blue = ((COLOR16) GetBValue(colors[1])) << 8; + vertices[1].Alpha = ((COLOR16) LOBYTE(colors[1] >> 24)) << 8; + + vertices[2] = vertices[1]; + vertices[2].y = rcWindow->top; + + vertices[3].x = rcWindow->right; + vertices[3].y = rcWindow->bottom; + vertices[3].Red = ((COLOR16) GetRValue(colors[2])) << 8; + vertices[3].Green = ((COLOR16) GetGValue(colors[2])) << 8; + vertices[3].Blue = ((COLOR16) GetBValue(colors[2])) << 8; + vertices[3].Alpha = ((COLOR16) LOBYTE(colors[2] >> 24)) << 8; + + if (GradientFill(dc, vertices, 4, (PVOID) gr, 2, GRADIENT_FILL_RECT_H) == FALSE) + return lastErrorToHRESULT(GetLastError()); + return DrawThemeBackground(this->theme, dc, + p->partID_CMOD_MODULEBACKGROUND(), 0, + rcWindow, rcPaint); + } + +#define hasNonsplitArrow(button) ((button) == leftButtons[0] || (button) == leftButtons[1] || (button) == leftButtons[2]) + #define dlgUnitsToX(dlg, baseX) MulDiv((dlg), (baseX), 4) #define dlgUnitsToY(dlg, baseY) MulDiv((dlg), (baseY), 8) // TODO verify the parameter order #define dipsToX(dip, dpiX) MulDiv((dip), (dpiX), 96) #define dipsToY(dip, dpiY) MulDiv((dip), (dpiY), 96) -// TODO check errors -// TODO the sizes are correct (according to UI Automation) but they don't visually match? -void buttonMetrics(HWND button, HDC dc, struct buttonMetrics *m) -{ - BOOL releaseDC; - TEXTMETRICW tm; - RECT r; - int minStdButtonHeight; + // TODO check errors + // TODO for win7: the sizes are correct (according to UI Automation) but they don't visually match? + virtual HRESULT buttonMetrics(commandModuleStyleParams *p, HWND button, HDC dc, struct buttonMetrics *m) const + { + BOOL releaseDC; + TEXTMETRICW tm; + RECT r; + int minStdButtonHeight; + HRESULT hr; - releaseDC = FALSE; - if (dc == NULL) { - dc = GetDC(button); - releaseDC = TRUE; - } + releaseDC = FALSE; + if (dc == NULL) { + dc = GetDC(button); + if (dc == NULL) + return lastErrorToHRESULT(GetLastError()); + releaseDC = TRUE; + } - ZeroMemory(&tm, sizeof (TEXTMETRICW)); - GetThemeTextMetrics(textstyleTheme, dc, - 4, 0, - &tm); - GetThemeTextExtent(textstyleTheme, dc, - 4, 0, - L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 52, 0, - NULL, &r); - m->baseX = (int) (((r.right - r.left) / 26 + 1) / 2); - m->baseY = (int) tm.tmHeight; - m->dpiX = GetDeviceCaps(dc, LOGPIXELSX); - m->dpiY = GetDeviceCaps(dc, LOGPIXELSY); - - m->fittingSize.cx = 0; - m->fittingSize.cy = 0; - - m->hasText = TRUE; - if (m->hasText) { - LRESULT n; - WCHAR *buf; - LOGFONTW lf; - - n = SendMessageW(button, WM_GETTEXTLENGTH, 0, 0); - buf = new WCHAR[n + 1]; - GetWindowTextW(button, buf, n + 1); + ZeroMemory(&tm, sizeof (TEXTMETRICW)); + // TODO get constant names + // TODO make textstyleTheme a member + GetThemeTextMetrics(textstyleTheme, dc, + 4, 0, + &tm); GetThemeTextExtent(textstyleTheme, dc, 4, 0, - buf, n, DT_CENTER, + L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 52, 0, NULL, &r); - m->textSize.cx = r.right - r.left; - m->textSize.cy = r.bottom - r.top; - delete[] buf; - m->fittingSize.cx += m->textSize.cx; - m->fittingSize.cy += m->textSize.cy; + m->baseX = (int) (((r.right - r.left) / 26 + 1) / 2); + m->baseY = (int) tm.tmHeight; + m->dpiX = GetDeviceCaps(dc, LOGPIXELSX); + m->dpiY = GetDeviceCaps(dc, LOGPIXELSY); - // dui70.dll adds this to the width when "overhang" is enabled, and it seems to be enabled for our cases, but I can't tell what conditions it's enabled for... - // and yes, it seems to be using the raw lfHeight value here :/ - // TODO find the right TMT constant - GetThemeFont(textstyleTheme, dc, - 4, 0, - TMT_FONT, &lf); - m->fittingSize.cx += 2 * (abs(lf.lfHeight) / 6); - } + m->fittingSize.cx = 0; + m->fittingSize.cy = 0; - m->hasArrow = hasNonsplitArrow(button); - if (m->hasArrow) { - // TS_MIN returns 1x1 and TS_DRAW returns 0x0, so... - GetThemePartSize(theme, dc, - 6, 0, - NULL, TS_TRUE, &(m->arrowSize)); - m->fittingSize.cx += m->arrowSize.cx; - // TODO I don't think dui70.dll takes this into consideration... - if (m->fittingSize.cy < m->arrowSize.cy) - m->fittingSize.cy = m->arrowSize.cy; - m->fittingSize.cx += dipsToX(1, m->dpiX); - } + m->hasText = TRUE; + if (m->hasText) { + LRESULT n; + WCHAR *buf; + LOGFONTW lf; - m->fittingSize.cx += dipsToX(13, m->dpiX) * 2; - m->fittingSize.cy += dipsToY(5, m->dpiY) * 2; + n = SendMessageW(button, WM_GETTEXTLENGTH, 0, 0); + buf = new WCHAR[n + 1]; + GetWindowTextW(button, buf, n + 1); + hr = GetThemeTextExtent(textstyleTheme, dc, + 4, 0, + buf, n, DT_CENTER, + NULL, &r); + delete[] buf; + if (hr != S_OK) + goto fail; + m->textSize.cx = r.right - r.left; + m->textSize.cy = r.bottom - r.top; + m->fittingSize.cx += m->textSize.cx; + m->fittingSize.cy += m->textSize.cy; - // and dui70.dll seems to do a variant of this but for text buttons only... - minStdButtonHeight = dlgUnitsToY(14, m->baseY); - if (m->fittingSize.cy < minStdButtonHeight) - m->fittingSize.cy = minStdButtonHeight; + // dui70.dll adds this to the width when "overhang" is enabled, and it seems to be enabled for our cases, but I can't tell what conditions it's enabled for... + // and yes, it seems to be using the raw lfHeight value here :/ + // TODO find the right TMT constant + hr = GetThemeFont(textstyleTheme, dc, + 4, 0, + TMT_FONT, &lf); + if (hr != S_OK) + goto fail; + m->fittingSize.cx += 2 * (abs(lf.lfHeight) / 6); + } - if (releaseDC) - ReleaseDC(button, dc); -} + m->hasArrow = hasNonsplitArrow(button); + if (m->hasArrow) { + // TS_MIN returns 1x1 and TS_DRAW returns 0x0, so... + hr = GetThemePartSize(theme, dc, + 6, 0, + NULL, TS_TRUE, &(m->arrowSize)); + if (hr != S_OK) + goto fail; + m->fittingSize.cx += m->arrowSize.cx; + // TODO I don't think dui70.dll takes this into consideration... + if (m->fittingSize.cy < m->arrowSize.cy) + m->fittingSize.cy = m->arrowSize.cy; + m->fittingSize.cx += dipsToX(1, m->dpiX); + } + + m->fittingSize.cx += dipsToX(13, m->dpiX) * 2; + m->fittingSize.cy += dipsToY(5, m->dpiY) * 2; + + // and dui70.dll seems to do a variant of this but for text buttons only... + minStdButtonHeight = dlgUnitsToY(14, m->baseY); + if (m->fittingSize.cy < minStdButtonHeight) + m->fittingSize.cy = minStdButtonHeight; + + hr = S_OK; + fail: + if (releaseDC) + // TODO when migrating this to libui, this will need to be renamed to indicate we are intentionally ignoring errors + ReleaseDC(button, dc); + return hr; + }; +}; struct buttonRects { RECT clientRect; From d9a851169ebec2c955c8212d1d172cb3db3941a6 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Tue, 30 Oct 2018 23:34:06 -0400 Subject: [PATCH 1319/1329] More work. More platform-specific parameters, yay! --- doc/misctests/winbuttonexplorertheme.cpp | 48 ++++++++++++++++-------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/doc/misctests/winbuttonexplorertheme.cpp b/doc/misctests/winbuttonexplorertheme.cpp index 468bd6ff..bbcee975 100644 --- a/doc/misctests/winbuttonexplorertheme.cpp +++ b/doc/misctests/winbuttonexplorertheme.cpp @@ -83,6 +83,10 @@ public: virtual int stateID_CMODS_NEARHOT(void) const = 0; virtual HRESULT backgroundGradientColors(HTHEME theme, COLORREF *colors) const = 0; + + virtual int buttonMarginsXDIP(void) const = 0; + virtual int buttonMarginsYDIP(void) const = 0; + virtual int buttonTextArrowSeparationXDIP(void) const = 0; }; class commandModuleStyleParamsVista : public commandModuleStyleParams { @@ -111,6 +115,10 @@ public: #undef argb return S_OK; } + + virtual int buttonMarginsXDIP(void) const { return 6; } + virtual int buttonMarginsYDIP(void) const { return 5; } + virtual int buttonTextArrowSeparationXDIP(void) const { return 3; } }; class commandModuleStyleParams7 : public commandModuleStyleParams { @@ -147,6 +155,10 @@ class commandModuleStyleParams7 : public commandModuleStyleParams { 2, 0, TMT_GRADIENTCOLOR3, color + 2); } + + virtual int buttonMarginsXDIP(void) const { return 13; } + virtual int buttonMarginsYDIP(void) const { return 5; } + virtual int buttonTextArrowSeparationXDIP(void) const { return 1; } }; // all coordinates are in client space @@ -170,8 +182,13 @@ public: class commandModuleStyleThemed : public commandModuleStyle { HTHEME theme; + HTHEME textstyleTheme; public: - commandModuleStyleThemed(theme) { this->theme = theme; } + commandModuleStyleThemed(HTHEME theme, HTHEME textstyleTheme) + { + this->theme = theme; + this->textstyleTheme = textstyleTheme; + } virtual HRESULT drawFolderBar(commandModuleStyleParams *p, HDC dc, RECT *rcWindow, RECT *rcPaint) const { @@ -226,7 +243,6 @@ public: #define dipsToX(dip, dpiX) MulDiv((dip), (dpiX), 96) #define dipsToY(dip, dpiY) MulDiv((dip), (dpiY), 96) - // TODO check errors // TODO for win7: the sizes are correct (according to UI Automation) but they don't visually match? virtual HRESULT buttonMetrics(commandModuleStyleParams *p, HWND button, HDC dc, struct buttonMetrics *m) const { @@ -245,15 +261,17 @@ public: } ZeroMemory(&tm, sizeof (TEXTMETRICW)); - // TODO get constant names - // TODO make textstyleTheme a member - GetThemeTextMetrics(textstyleTheme, dc, - 4, 0, + hr = GetThemeTextMetrics(this->textstyleTheme, dc, + TEXT_BODYTEXT, 0, &tm); - GetThemeTextExtent(textstyleTheme, dc, - 4, 0, + if (hr != S_OK) + goto fail; + hr = GetThemeTextExtent(this->textstyleTheme, dc, + TEXT_BODYTEXT, 0, L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 52, 0, NULL, &r); + if (hr != S_OK) + goto fail; m->baseX = (int) (((r.right - r.left) / 26 + 1) / 2); m->baseY = (int) tm.tmHeight; m->dpiX = GetDeviceCaps(dc, LOGPIXELSX); @@ -271,8 +289,8 @@ public: n = SendMessageW(button, WM_GETTEXTLENGTH, 0, 0); buf = new WCHAR[n + 1]; GetWindowTextW(button, buf, n + 1); - hr = GetThemeTextExtent(textstyleTheme, dc, - 4, 0, + hr = GetThemeTextExtent(this->textstyleTheme, dc, + TEXT_BODYTEXT, 0, buf, n, DT_CENTER, NULL, &r); delete[] buf; @@ -286,7 +304,7 @@ public: // dui70.dll adds this to the width when "overhang" is enabled, and it seems to be enabled for our cases, but I can't tell what conditions it's enabled for... // and yes, it seems to be using the raw lfHeight value here :/ // TODO find the right TMT constant - hr = GetThemeFont(textstyleTheme, dc, + hr = GetThemeFont(this->textstyleTheme, dc, 4, 0, TMT_FONT, &lf); if (hr != S_OK) @@ -298,7 +316,7 @@ public: if (m->hasArrow) { // TS_MIN returns 1x1 and TS_DRAW returns 0x0, so... hr = GetThemePartSize(theme, dc, - 6, 0, + p->partID_CMOD_MENUGLYPH(), 0, NULL, TS_TRUE, &(m->arrowSize)); if (hr != S_OK) goto fail; @@ -306,11 +324,11 @@ public: // TODO I don't think dui70.dll takes this into consideration... if (m->fittingSize.cy < m->arrowSize.cy) m->fittingSize.cy = m->arrowSize.cy; - m->fittingSize.cx += dipsToX(1, m->dpiX); + m->fittingSize.cx += dipsToX(p->buttonTextArrowSeparationXDIP(), m->dpiX); } - m->fittingSize.cx += dipsToX(13, m->dpiX) * 2; - m->fittingSize.cy += dipsToY(5, m->dpiY) * 2; + m->fittingSize.cx += dipsToX(p->buttonMarginsXDIP(), m->dpiX) * 2; + m->fittingSize.cy += dipsToY(p->buttonMarginsYDIP(), m->dpiY) * 2; // and dui70.dll seems to do a variant of this but for text buttons only... minStdButtonHeight = dlgUnitsToY(14, m->baseY); From 3d4b959632d9f13a878e0a549ff9c123753cd9d9 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 1 Nov 2018 10:50:10 -0400 Subject: [PATCH 1320/1329] More work on generalizing the winbuttonexplorertheme program. --- doc/misctests/winbuttonexplorertheme.cpp | 98 ++++++++++++++++++------ 1 file changed, 73 insertions(+), 25 deletions(-) diff --git a/doc/misctests/winbuttonexplorertheme.cpp b/doc/misctests/winbuttonexplorertheme.cpp index bbcee975..db3dd8a2 100644 --- a/doc/misctests/winbuttonexplorertheme.cpp +++ b/doc/misctests/winbuttonexplorertheme.cpp @@ -83,6 +83,8 @@ public: virtual int stateID_CMODS_NEARHOT(void) const = 0; virtual HRESULT backgroundGradientColors(HTHEME theme, COLORREF *colors) const = 0; + virtual HRESULT buttonTextColor(HTHEME theme, UINT uItemState, COLORREF *color) const = 0; + virtual BOOL buttonTextShadowed(UINT uItemState) const = 0; virtual int buttonMarginsXDIP(void) const = 0; virtual int buttonMarginsYDIP(void) const = 0; @@ -112,10 +114,25 @@ public: colors[0] = argb(255, 4, 80, 130); colors[1] = argb(255, 17, 101, 132); colors[2] = argb(255, 29, 121, 134); + return S_OK; + } + + virtual HRESULT buttonTextColor(HTHEME theme, UINT uItemState, COLORREF *color) const + { + if (color == NULL) + return E_POINTER; + *color = GetSysColor(COLOR_WINDOW); + if ((uItemState & CDIS_DISABLED) != 0) + *color = argb(255, 161, 204, 210); #undef argb return S_OK; } + virtual BOOL buttonTextShadowed(UINT uItemState) const + { + return (uItemState & CDIS_DISABLED) == 0; + } + virtual int buttonMarginsXDIP(void) const { return 6; } virtual int buttonMarginsYDIP(void) const { return 5; } virtual int buttonTextArrowSeparationXDIP(void) const { return 3; } @@ -156,6 +173,26 @@ class commandModuleStyleParams7 : public commandModuleStyleParams { TMT_GRADIENTCOLOR3, color + 2); } + virtual HRESULT buttonTextColor(HTHEME theme, UINT uItemState, COLORREF *color) const + { + int state; + + if (color == NULL) + return E_POINTER; + state = 1; + if ((uItemState & CDIS_DISABLED) != 0) + state = 6; + // TODO check if 3 is the correct part ID for all button types + return GetThemeColor(theme, + 3, state, + TMT_TEXTCOLOR, color); + } + + virtual BOOL buttonTextShadowed(UINT uItemState) const + { + return FALSE; + } + virtual int buttonMarginsXDIP(void) const { return 13; } virtual int buttonMarginsYDIP(void) const { return 5; } virtual int buttonTextArrowSeparationXDIP(void) const { return 1; } @@ -174,10 +211,17 @@ struct buttonMetrics { SIZE arrowSize; }; +struct buttonRects { + RECT clientRect; + RECT textRect; + RECT arrowRect; +}; + class commandModuleStyle { public: virtual HRESULT drawFolderBar(commandModuleStyleParams *p, HDC dc, RECT *rcWindow, RECT *rcPaint) const = 0; virtual HRESULT buttonMetrics(commandModuleStyleParams *p, HWND button, HDC dc, struct buttonMetrics *m) const = 0; + virtual HRESULT buttonRects(commandModuleStyleParams *p, HWND button, struct buttonMetrics *m, struct buttonRects *r) const = 0; }; class commandModuleStyleThemed : public commandModuleStyle { @@ -252,6 +296,9 @@ public: int minStdButtonHeight; HRESULT hr; + if (m == NULL) + return E_POINTER; + releaseDC = FALSE; if (dc == NULL) { dc = GetDC(button); @@ -342,38 +389,38 @@ public: ReleaseDC(button, dc); return hr; }; -}; -struct buttonRects { - RECT clientRect; - RECT textRect; - RECT arrowRect; -}; + // TODO check errors + virtual HRESULT buttonRects(commandModuleStyleParams *p, HWND button, struct buttonMetrics *m, struct buttonRects *r) const + { + if (r == NULL) + return E_POINTER; -// TODO check errors -void buttonRects(HWND button, struct buttonMetrics *m, struct buttonRects *r) -{ - GetClientRect(button, &(r->clientRect)); + GetClientRect(button, &(r->clientRect)); - if (m->hasText) - r->textRect = r->clientRect; + if (m->hasText) + r->textRect = r->clientRect; - if (m->hasArrow) { - r->arrowRect = r->clientRect; - r->arrowRect.left = r->arrowRect.right; - r->arrowRect.left -= dipsToX(13, m->dpiX); - r->arrowRect.right = r->arrowRect.left; - r->arrowRect.left -= m->arrowSize.cx; - r->arrowRect.top += ((r->arrowRect.bottom - r->arrowRect.top) - m->arrowSize.cy) / 2; - r->arrowRect.bottom = r->arrowRect.top + m->arrowSize.cy; + if (m->hasArrow) { + r->arrowRect = r->clientRect; + r->arrowRect.left = r->arrowRect.right; + r->arrowRect.left -= dipsToX(p->buttonMarginsXDIP(), m->dpiX); + r->arrowRect.right = r->arrowRect.left; + r->arrowRect.left -= m->arrowSize.cx; + r->arrowRect.top += ((r->arrowRect.bottom - r->arrowRect.top) - m->arrowSize.cy) / 2; + r->arrowRect.bottom = r->arrowRect.top + m->arrowSize.cy; - if (m->hasText) { - r->textRect.right = r->arrowRect.left - dipsToX(1, m->dpiX); - r->textRect.right += dipsToX(13, m->dpiX); + if (m->hasText) { + r->textRect.right = r->arrowRect.left - dipsToX(p->buttonTextArrowSeparationXDIP(), m->dpiX); + r->textRect.right += dipsToX(p->buttonMarginsXDIP(), m->dpiX); + } } - } -} + return S_OK; + } +}; + +#if 0 // TODO check errors void drawExplorerChevron(HTHEME theme, HDC dc, HWND rebar, WPARAM band, RECT *rcPaint) { @@ -405,6 +452,7 @@ void drawExplorerChevron(HTHEME theme, HDC dc, HWND rebar, WPARAM band, RECT *rc 7, 1, &r, rcPaint); } +#endif // TODO check errors LRESULT drawExplorerButton(NMCUSTOMDRAW *nm) From 7138276ccfbde94873cb6e2db65642adcbd2ee19 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Fri, 2 Nov 2018 23:23:35 -0400 Subject: [PATCH 1321/1329] And completed the genericization for Vista. It works!!! --- doc/misctests/winbuttonexplorertheme.cpp | 272 ++++++++++------------- 1 file changed, 120 insertions(+), 152 deletions(-) diff --git a/doc/misctests/winbuttonexplorertheme.cpp b/doc/misctests/winbuttonexplorertheme.cpp index db3dd8a2..e9bdc4e8 100644 --- a/doc/misctests/winbuttonexplorertheme.cpp +++ b/doc/misctests/winbuttonexplorertheme.cpp @@ -46,27 +46,6 @@ HINSTANCE hInstance; HWND leftButtons[5]; HWND rightButtons[3]; -HICON shieldIcon; -HICON applicationIcon; -HICON helpIcon; -HIMAGELIST rightList; - -HTHEME theme = NULL; -HTHEME textstyleTheme = NULL; -HIMAGELIST dropdownArrowList = NULL; -HFONT buttonFont = NULL; - -static struct { - const WCHAR *text; - BOOL dropdown; -} leftbarButtons[] = { - { L"Organize", TRUE }, - { L"Include in library", TRUE }, - { L"Share with", TRUE }, - { L"Burn", FALSE }, - { L"New folder", FALSE }, -}; - class commandModuleStyleParams { public: virtual int partID_CMOD_MODULEBACKGROUND(void) const = 0; @@ -160,17 +139,17 @@ class commandModuleStyleParams7 : public commandModuleStyleParams { return E_POINTER; hr = GetThemeColor(theme, 2, 0, - TMT_GRADIENTCOLOR1. color + 0); + TMT_GRADIENTCOLOR1, colors + 0); if (hr != S_OK) return hr; hr = GetThemeColor(theme, 2, 0, - TMT_GRADIENTCOLOR2, color + 1); + TMT_GRADIENTCOLOR2, colors + 1); if (hr != S_OK) return hr; return GetThemeColor(theme, 2, 0, - TMT_GRADIENTCOLOR3, color + 2); + TMT_GRADIENTCOLOR3, colors + 2); } virtual HRESULT buttonTextColor(HTHEME theme, UINT uItemState, COLORREF *color) const @@ -222,6 +201,7 @@ public: virtual HRESULT drawFolderBar(commandModuleStyleParams *p, HDC dc, RECT *rcWindow, RECT *rcPaint) const = 0; virtual HRESULT buttonMetrics(commandModuleStyleParams *p, HWND button, HDC dc, struct buttonMetrics *m) const = 0; virtual HRESULT buttonRects(commandModuleStyleParams *p, HWND button, struct buttonMetrics *m, struct buttonRects *r) const = 0; + virtual HRESULT drawButton(commandModuleStyleParams *p, HWND button, HDC dc, UINT uItemState, RECT *rcPaint) const = 0; }; class commandModuleStyleThemed : public commandModuleStyle { @@ -418,6 +398,76 @@ public: return S_OK; } + + // TODO check errors fully + virtual HRESULT drawButton(commandModuleStyleParams *p, HWND button, HDC dc, UINT uItemState, RECT *rcPaint) const + { + struct buttonMetrics m; + struct buttonRects r; + int part, state; + HRESULT hr; + + hr = this->buttonMetrics(p, button, dc, &m); + if (hr != S_OK) + return hr; + hr = this->buttonRects(p, button, &m, &r); + if (hr != S_OK) + return hr; + + part = p->partID_CMOD_TASKBUTTON(); + state = p->stateID_CMODS_NORMAL(); + if ((uItemState & CDIS_FOCUS) != 0) + state = p->stateID_CMODS_KEYFOCUSED(); + if ((uItemState & CDIS_HOT) != 0) + state = p->stateID_CMODS_HOT(); + if ((uItemState & CDIS_SELECTED) != 0) + state = p->stateID_CMODS_PRESSED(); + hr = DrawThemeParentBackground(button, dc, rcPaint); + if (hr != S_OK) + return hr; + hr = DrawThemeBackground(this->theme, dc, + part, state, + &(r.clientRect), rcPaint); + if (hr != S_OK) + return hr; + + if (m.hasText) { + int textState; + COLORREF textColor; + LRESULT n; + WCHAR *buf; + DTTOPTS dttopts; + + hr = p->buttonTextColor(this->theme, uItemState, &textColor); + if (hr != S_OK) + return hr; + n = SendMessageW(button, WM_GETTEXTLENGTH, 0, 0); + buf = new WCHAR[n + 1]; + GetWindowTextW(button, buf, n + 1); + ZeroMemory(&dttopts, sizeof (DTTOPTS)); + dttopts.dwSize = sizeof (DTTOPTS); + dttopts.dwFlags = DTT_TEXTCOLOR; + dttopts.crText = textColor; + hr = DrawThemeTextEx(this->textstyleTheme, dc, + TEXT_BODYTEXT, 0, + buf, n, DT_CENTER | DT_VCENTER | DT_SINGLELINE, + &(r.textRect), &dttopts); + delete[] buf; + if (hr != S_OK) + return hr; + } + + if (m.hasArrow) { + // TODO verify the state ID on all platforms + hr = DrawThemeBackground(this->theme, dc, + p->partID_CMOD_MENUGLYPH(), 0, + &(r.arrowRect), rcPaint); + if (hr != S_OK) + return hr; + } + + return S_OK; + } }; #if 0 @@ -454,70 +504,40 @@ void drawExplorerChevron(HTHEME theme, HDC dc, HWND rebar, WPARAM band, RECT *rc } #endif +HICON shieldIcon; +HICON applicationIcon; +HICON helpIcon; +HIMAGELIST rightList; + +HTHEME theme = NULL; +HTHEME textstyleTheme = NULL; +const char *which = "7"; +commandModuleStyle *cms = NULL; +commandModuleStyleParams *cmsp = NULL; +HIMAGELIST dropdownArrowList = NULL; + +static struct { + const WCHAR *text; + BOOL dropdown; +} leftbarButtons[] = { + { L"Organize", TRUE }, + { L"Include in library", TRUE }, + { L"Share with", TRUE }, + { L"Burn", FALSE }, + { L"New folder", FALSE }, +}; + // TODO check errors LRESULT drawExplorerButton(NMCUSTOMDRAW *nm) { - HWND button; - struct buttonMetrics m; - struct buttonRects r; - int part, state; + HRESULT hr; if (nm->dwDrawStage != CDDS_PREPAINT) return CDRF_DODEFAULT; - button = nm->hdr.hwndFrom; - buttonMetrics(button, nm->hdc, &m); - buttonRects(button, &m, &r); - - part = 3; -//TODO if ((tbb.fsStyle & BTNS_DROPDOWN) != 0) -//TODO part = 4; - state = 1; - // TODO this doesn't work; both keyboard and mouse are listed as HOT - if ((nm->uItemState & CDIS_FOCUS) != 0) - state = 4; - if ((nm->uItemState & CDIS_HOT) != 0) - state = 2; - if ((nm->uItemState & CDIS_SELECTED) != 0) - state = 3; - DrawThemeParentBackground(button, nm->hdc, &(nm->rc)); - DrawThemeBackground(theme, nm->hdc, - part, state, - &(r.clientRect), &(nm->rc)); - - if (m.hasText) { - int textState; - COLORREF textColor; - LRESULT n; - WCHAR *buf; - DTTOPTS dttopts; - - // TODO these values are only for part==3 - textState = 1; - if ((nm->uItemState & CDIS_DISABLED) != 0) - textState = 6; - // TODO name the constant for the property ID - GetThemeColor(theme, - 3, textState, - 3803, &textColor); - n = SendMessageW(button, WM_GETTEXTLENGTH, 0, 0); - buf = new WCHAR[n + 1]; - GetWindowTextW(button, buf, n + 1); - ZeroMemory(&dttopts, sizeof (DTTOPTS)); - dttopts.dwSize = sizeof (DTTOPTS); - dttopts.dwFlags = DTT_TEXTCOLOR; - dttopts.crText = textColor; - DrawThemeTextEx(textstyleTheme, nm->hdc, - 4, 0, - buf, n, DT_CENTER | DT_VCENTER | DT_SINGLELINE, - &(r.textRect), &dttopts); - delete[] buf; - } - - if (m.hasArrow) - DrawThemeBackground(theme, nm->hdc, - 6, 0, - &(r.arrowRect), &(nm->rc)); - + hr = cms->drawButton(cmsp, nm->hdr.hwndFrom, + nm->hdc, nm->uItemState, &(nm->rc)); + if (hr != S_OK) + return CDRF_DODEFAULT; return CDRF_SKIPDEFAULT; } @@ -545,29 +565,13 @@ void onWM_CREATE(HWND hwnd) // TODO check errors void updateTheme(HWND hwnd) { - BUTTON_IMAGELIST bim; - HDC dc; - SIZE size; - HBITMAP hb; - HTHEME buttonTheme; - LOGFONTW lf; - MARGINS marginOffsets; - RECT margins; - int i; - - if (buttonFont != NULL) { - for (i = 0; i < 5; i++) - SendMessageW(leftButtons[i], WM_SETFONT, (WPARAM) NULL, TRUE); - DeleteObject(buttonFont); - buttonFont = NULL; + if (cmsp != NULL) { + delete cmsp; + cmsp = NULL; } - if (dropdownArrowList != NULL) { - ZeroMemory(&bim, sizeof (BUTTON_IMAGELIST)); - bim.himl = BCCL_NOGLYPH; - for (i = 0; i < 3; i++) - SendMessageW(leftButtons[i], BCM_SETIMAGELIST, 0, (LPARAM) (&bim)); - ImageList_Destroy(dropdownArrowList); - dropdownArrowList = NULL; + if (cms != NULL) { + delete cms; + cms = NULL; } if (textstyleTheme != NULL) { CloseThemeData(textstyleTheme); @@ -580,50 +584,11 @@ void updateTheme(HWND hwnd) theme = OpenThemeData(hwnd, L"CommandModule"); textstyleTheme = OpenThemeData(hwnd, L"TEXTSTYLE"); - dc = GetDC(hwnd); - // TS_MIN returns 1x1 and TS_DRAW returns 0x0, so... - GetThemePartSize(theme, dc, - 6, 0, - NULL, TS_TRUE, &size); - ReleaseDC(hwnd, dc); - // TODO draw a bitmap properly - GetThemeBitmap(theme, - 6, 0, - 0, GBF_COPY, &hb); - dropdownArrowList = ImageList_Create(size.cx, size.cy, - ILC_COLOR32, 0, 1); - ImageList_Add(dropdownArrowList, hb, NULL); - DeleteObject(hb); - ZeroMemory(&bim, sizeof (BUTTON_IMAGELIST)); - bim.himl = dropdownArrowList; - // TODO should be DIPs - bim.margin.left = 1; - bim.uAlign = BUTTON_IMAGELIST_ALIGN_RIGHT; - for (i = 0; i < 3; i++) - SendMessageW(leftButtons[i], BCM_SETIMAGELIST, 0, (LPARAM) (&bim)); - - // TODO find the right TMT constant - GetThemeFont(textstyleTheme, NULL, - 4, 0, - TMT_FONT, &lf); - buttonFont = CreateFontIndirectW(&lf); - for (i = 0; i < 5; i++) - SendMessageW(leftButtons[i], WM_SETFONT, (WPARAM) buttonFont, TRUE); - - buttonTheme = OpenThemeData(hwnd, L"Button"); - ZeroMemory(&marginOffsets, sizeof (MARGINS)); - GetThemeMargins(buttonTheme, NULL, - BP_PUSHBUTTON, PBS_NORMAL, - TMT_CONTENTMARGINS, NULL, &marginOffsets); - CloseThemeData(buttonTheme); - // TODO the constants should be DIPs - margins.left = 13 - marginOffsets.cxLeftWidth; - margins.top = 5 - marginOffsets.cyTopHeight; - margins.right = 13 - marginOffsets.cxRightWidth; - margins.bottom = 5 - marginOffsets.cyBottomHeight; -// for (i = 0; i < 5; i++) -// if (SendMessageW(leftButtons[i], BCM_SETTEXTMARGIN, 0, (LPARAM) (&margins)) == FALSE) -// diele("BCM_SETTEXTMARGIN"); + cms = new commandModuleStyleThemed(theme, textstyleTheme); + if (strcmp(which, "vista") == 0) + cmsp = new commandModuleStyleParamsVista; + else + cmsp = new commandModuleStyleParams7; } void repositionButtons(HWND hwnd) @@ -639,7 +604,7 @@ void repositionButtons(HWND hwnd) buttonx = 5; buttony = 5; for (i = 0; i < 5; i++) { - buttonMetrics(leftButtons[i], NULL, &m); + cms->buttonMetrics(cmsp, leftButtons[i], NULL, &m); dwp = DeferWindowPos(dwp, leftButtons[i], NULL, buttonx, buttony, m.fittingSize.cx, m.fittingSize.cy, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER); @@ -766,13 +731,13 @@ LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) dc = BeginPaint(hwnd, &ps); {RECT w; GetClientRect(hwnd,&w); - drawExplorerBackground(theme, dc, &w, &(ps.rcPaint));} + cms->drawFolderBar(cmsp, dc, &w, &(ps.rcPaint));} EndPaint(hwnd, &ps); return 0; case WM_PRINTCLIENT: {RECT w; GetClientRect(hwnd,&w); - drawExplorerBackground(theme, (HDC) wParam, &w, &w);} + cms->drawFolderBar(cmsp, (HDC) wParam, &w, &w);} return 0; #if 0 case WM_COMMAND: @@ -794,7 +759,7 @@ LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) EXTERN_C IMAGE_DOS_HEADER __ImageBase; -int main(void) +int main(int argc, char *argv[]) { STARTUPINFOW si; int nCmdShow; @@ -806,6 +771,9 @@ int main(void) MSG msg; HRESULT hr; + if (argc > 1) + which = argv[1]; + hInstance = (HINSTANCE) (&__ImageBase); nCmdShow = SW_SHOWDEFAULT; GetStartupInfoW(&si); From a5fb19855810f1c2a8042bc5f3d969b1659cf0fd Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 4 Nov 2018 22:19:44 -0500 Subject: [PATCH 1322/1329] Started work to properly size the explorer bars. --- doc/misctests/winbuttonexplorertheme.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/doc/misctests/winbuttonexplorertheme.cpp b/doc/misctests/winbuttonexplorertheme.cpp index e9bdc4e8..400bcb84 100644 --- a/doc/misctests/winbuttonexplorertheme.cpp +++ b/doc/misctests/winbuttonexplorertheme.cpp @@ -65,6 +65,11 @@ public: virtual HRESULT buttonTextColor(HTHEME theme, UINT uItemState, COLORREF *color) const = 0; virtual BOOL buttonTextShadowed(UINT uItemState) const = 0; + virtual int folderBarMarginsLeftDIP(void) const = 0; + virtual int folderBarMarginsTopDIP(void) const = 0; + virtual int folderBarMarginsRightDIP(void) const = 0; + virtual int folderBarMarginsBottomDIP(void) const = 0; + virtual int buttonMarginsXDIP(void) const = 0; virtual int buttonMarginsYDIP(void) const = 0; virtual int buttonTextArrowSeparationXDIP(void) const = 0; @@ -112,6 +117,11 @@ public: return (uItemState & CDIS_DISABLED) == 0; } + virtual int folderBarMarginsLeftDIP(void) const { return 3; } + virtual int folderBarMarginsTopDIP(void) const { return 2; } + virtual int folderBarMarginsRightDIP(void) const { return 3; } + virtual int folderBarMarginsBottomDIP(void) const { return 3; } + virtual int buttonMarginsXDIP(void) const { return 6; } virtual int buttonMarginsYDIP(void) const { return 5; } virtual int buttonTextArrowSeparationXDIP(void) const { return 3; } @@ -172,6 +182,11 @@ class commandModuleStyleParams7 : public commandModuleStyleParams { return FALSE; } + virtual int folderBarMarginsLeftDIP(void) const { return 3; } + virtual int folderBarMarginsTopDIP(void) const { return 2; } + virtual int folderBarMarginsRightDIP(void) const { return 9; } + virtual int folderBarMarginsBottomDIP(void) const { return 3; } + virtual int buttonMarginsXDIP(void) const { return 13; } virtual int buttonMarginsYDIP(void) const { return 5; } virtual int buttonTextArrowSeparationXDIP(void) const { return 1; } From c25831ec666fd3f9a3e883915825f46eae8b0749 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Mon, 5 Nov 2018 21:09:50 -0500 Subject: [PATCH 1323/1329] Properly sized the explorer bar now. --- doc/misctests/winbuttonexplorertheme.cpp | 49 +++++++++++++++++++++--- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/doc/misctests/winbuttonexplorertheme.cpp b/doc/misctests/winbuttonexplorertheme.cpp index 400bcb84..de70db66 100644 --- a/doc/misctests/winbuttonexplorertheme.cpp +++ b/doc/misctests/winbuttonexplorertheme.cpp @@ -610,14 +610,25 @@ void repositionButtons(HWND hwnd) { HDWP dwp; int buttonx, buttony; + HDC dc; + int dpiX; + int dpiY; struct buttonMetrics m; int i; + dc = GetDC(hwnd); + if (dc == NULL) + diele("GetDC()"); + dpiX = GetDeviceCaps(dc, LOGPIXELSX); + dpiY = GetDeviceCaps(dc, LOGPIXELSY); + // TODO check error + ReleaseDC(hwnd, dc); + dwp = BeginDeferWindowPos(5); if (dwp == NULL) diele("BeginDeferWindowPos()"); - buttonx = 5; - buttony = 5; + buttonx = dipsToX(cmsp->folderBarMarginsLeftDIP(), dpiX); + buttony = dipsToY(cmsp->folderBarMarginsTopDIP(), dpiY); for (i = 0; i < 5; i++) { cms->buttonMetrics(cmsp, leftButtons[i], NULL, &m); dwp = DeferWindowPos(dwp, leftButtons[i], NULL, @@ -631,6 +642,33 @@ void repositionButtons(HWND hwnd) diele("EndDeferWindowPos()"); } +// TODO check errors +void folderBarRect(HWND hwnd, HDC dc, RECT *r) +{ + int dpiX; + int dpiY; + RECT child; + int i; + + dpiX = GetDeviceCaps(dc, LOGPIXELSX); + dpiY = GetDeviceCaps(dc, LOGPIXELSY); + + GetClientRect(hwnd, r); + r->right -= r->left; + r->left = 0; + r->top = 0; + r->bottom = 0; + + for (i = 0; i < 5; i++) { + GetWindowRect(leftButtons[i], &child); + if (r->bottom < (child.bottom - child.top)) + r->bottom = (child.bottom - child.top); + } + + r->bottom += dipsToY(cmsp->folderBarMarginsTopDIP(), dpiY); + r->bottom += dipsToY(cmsp->folderBarMarginsBottomDIP(), dpiY); +} + #if 0 // TODO check errors void handleEvents(HWND hwnd, WPARAM wParam) @@ -745,13 +783,14 @@ LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) // TODO check errors dc = BeginPaint(hwnd, &ps); {RECT w; - GetClientRect(hwnd,&w); + folderBarRect(hwnd, dc, &w); cms->drawFolderBar(cmsp, dc, &w, &(ps.rcPaint));} EndPaint(hwnd, &ps); return 0; case WM_PRINTCLIENT: - {RECT w; - GetClientRect(hwnd,&w); + {RECT w, paint; + folderBarRect(hwnd, (HDC) wParam, &w); + GetClientRect(hwnd,&paint); cms->drawFolderBar(cmsp, (HDC) wParam, &w, &w);} return 0; #if 0 From df5d144dbd4d9eedff53a9d109744cfc0dc69a50 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 7 Nov 2018 10:11:51 -0500 Subject: [PATCH 1324/1329] More TODOs. --- _future/unittest/testing.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/_future/unittest/testing.h b/_future/unittest/testing.h index 550e5250..ce15b170 100644 --- a/_future/unittest/testing.h +++ b/_future/unittest/testing.h @@ -1,5 +1,8 @@ // 27 february 2018 +// TODO +// - https://blogs.msdn.microsoft.com/oldnewthing/20181107-00/?p=100155 + #ifndef testingprivIncludeGuard_testing_h #define testingprivIncludeGuard_testing_h From ae8a3939ae729fa97735f60240a734333a0608bf Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 8 Nov 2018 11:32:41 -0500 Subject: [PATCH 1325/1329] More TODOs. --- _future/unittest/testing.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_future/unittest/testing.h b/_future/unittest/testing.h index ce15b170..d7a566d1 100644 --- a/_future/unittest/testing.h +++ b/_future/unittest/testing.h @@ -1,7 +1,7 @@ // 27 february 2018 // TODO -// - https://blogs.msdn.microsoft.com/oldnewthing/20181107-00/?p=100155 +// - https://blogs.msdn.microsoft.com/oldnewthing/20181107-00/?p=100155 https://blogs.msdn.microsoft.com/oldnewthing/20181108-00/?p=100165 #ifndef testingprivIncludeGuard_testing_h #define testingprivIncludeGuard_testing_h From 6490cdadb70d7efc3193594e576bb5cd7976c53c Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 11 Nov 2018 21:25:45 -0500 Subject: [PATCH 1326/1329] More TODOs. --- _future/unittest/testing.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/_future/unittest/testing.h b/_future/unittest/testing.h index d7a566d1..e9aee7ee 100644 --- a/_future/unittest/testing.h +++ b/_future/unittest/testing.h @@ -1,7 +1,8 @@ // 27 february 2018 // TODO -// - https://blogs.msdn.microsoft.com/oldnewthing/20181107-00/?p=100155 https://blogs.msdn.microsoft.com/oldnewthing/20181108-00/?p=100165 +// - https://blogs.msdn.microsoft.com/oldnewthing/20181107-00/?p=100155 https://blogs.msdn.microsoft.com/oldnewthing/20181108-00/?p=100165 https://blogs.msdn.microsoft.com/oldnewthing/20181109-00/?p=100175 +// - also in the above: note the unspecified order of data in the sub-segments... #ifndef testingprivIncludeGuard_testing_h #define testingprivIncludeGuard_testing_h From 97609af771577e0d9c5777b36a087510d06125b4 Mon Sep 17 00:00:00 2001 From: Thomas Corwin Date: Sun, 30 Sep 2018 23:04:28 -0400 Subject: [PATCH 1327/1329] Update the C# binding LibUISharp's name and link. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0664e35a..30d7c9fb 100644 --- a/README.md +++ b/README.md @@ -137,7 +137,7 @@ Language | Bindings --- | --- C++ | [libui-cpp](https://github.com/billyquith/libui-cpp), [cpp-libui-qtlike](https://github.com/aoloe/cpp-libui-qtlike) C# / .NET Framework | [LibUI.Binding](https://github.com/NattyNarwhal/LibUI.Binding) -C# / .NET Core | [DevZH.UI](https://github.com/noliar/DevZH.UI), [SharpUI](https://github.com/benpye/sharpui/), [LibUISharp](https://github.com/tom-corwin/LibUISharp) +C# / .NET Core | [DevZH.UI](https://github.com/noliar/DevZH.UI), [SharpUI](https://github.com/benpye/sharpui/), [TCD.UI](https://github.com/tacdevel/tcdfx) CHICKEN Scheme | [wasamasa/libui](https://github.com/wasamasa/libui) Common Lisp | [jinwoo/cl-ui](https://github.com/jinwoo/cl-ui) Crystal | [libui.cr](https://github.com/Fusion/libui.cr), [hedron](https://github.com/Qwerp-Derp/hedron) From 38e5d23891f4db13878e68c89c91d030749f4d58 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sat, 24 Nov 2018 19:57:05 -0500 Subject: [PATCH 1328/1329] More notes. --- _notes/i18n | 1 + 1 file changed, 1 insertion(+) diff --git a/_notes/i18n b/_notes/i18n index 79fdfe9f..fb3b6b8f 100644 --- a/_notes/i18n +++ b/_notes/i18n @@ -13,3 +13,4 @@ https://www.microsoft.com/en-us/language/Terminology https://www.microsoft.com/en-us/language/LicenseAgreement https://www.microsoft.com/en-us/language/Translations http://www.ttt.org/oscarstandards/tbx/ +https://blogs.msdn.microsoft.com/oldnewthing/20181122-00/?p=100295 From 0d9e3c4b672234549ee828657c29a72792be316b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Wed, 26 Dec 2018 12:40:37 -0500 Subject: [PATCH 1329/1329] More notes. --- _notes/cplusplus | 1 + 1 file changed, 1 insertion(+) create mode 100644 _notes/cplusplus diff --git a/_notes/cplusplus b/_notes/cplusplus new file mode 100644 index 00000000..0481a948 --- /dev/null +++ b/_notes/cplusplus @@ -0,0 +1 @@ +https://blogs.msdn.microsoft.com/oldnewthing/20181226-00/?p=100565