First Commit

This commit is contained in:
dsutanto committed 2025-11-19 16:30:37 +07:00
commit ae0fab9400
424 files changed
+71006

No files matched your search

+53
View File
@@ -0,0 +1,53 @@
# uiArea
uiArea is a uiControl that provides a canvas you can draw on. It receives keyboard and mouse events, supports scrolling, is DPI aware, and has several other useful features. A uiArea consists of the drawing area itself and horizontal and vertical scrollbars.
## The Area Handler
A uiArea is driven by an *area handler*. An area handler is an object with several methods that uiArea calls to do certain tasks. To create an area handler, simply have a structure whose first member is of type `uiAreaHandler`:
```c
struct uiAreaHandler {
void (*Draw)(uiAreaHandler *h, uiArea *a, uiAreaDrawParams *p);
void (*HScrollConfig)(uiAreaHandler *h, uiArea *a, uiAreaScrollConfig *c);
void (*VScrollConfig)(uiAreaHandler *h, uiArea *a, uiAreaScrollConfig *c);
}
```
## Drawing
Unlike drawing canvas controls in other toolkits, uiArea does **not** have a fixed size. The coordinate (0, 0) is always the top-left corner of the drawing area, regardless of how big the uiArea is in the current window or where the scrollbars presently are. Instead, you simulate a size by setting the scrollbar bounds, and you are given the current scrolling positions to base your drawing with.
The visible drawing area is called the *content area* by the drawing machinery.
TODO have a diagram.
When a part of the uiArea needs to be redrawn, the area handler's `Draw()` method is called. It receives the area handler, the uiArea, and a structure of parameters necessary for drawing.
```c
struct uiAreaDrawParams {
uiDrawContext *context;
intmax_t contentWidth;
intmax_t contentHeight;
intmax_t hscrollpos;
intmax_t vscrollpos;
intmax_t clipX;
intmax_t cilpY;
intmax_t clipWidth;
intmax_t clipHeight;
TODO dpiX;
TODO dpiY;
};
```
`context` is the drawing context; see drawing.md for details.
`contentWidth` and `contentHeight` is the current width and height of the content area. `hscrollpos` and `vscrollpos` are the current horizontal and vertical positions of the scrollbars, in units defined by the scrollbar configurations; see below.
`clipX`, `clipY`, `clipWidth`, and `clipHeight` define a rectangle, in content area coordinates, that the OS has requested to be redrawn. You can use this to optimize your drawing by only drawing where drawing is needed; the OS may choose to drop any drawing done outside the clip rectangle.
`dpiX` and `dpiY` are the uiArea's current DPI in the X and Y directions, respectively. Do not save these values; they are not guaranteed to stay the same once `Draw()` returns.
+1
View File
@@ -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.
+42
View File
@@ -0,0 +1,42 @@
# The Drawing Model
> Note: This model is not exclusive to libui; it is also applicable to many 2D graphics libraries, such as Direct2D, cairo, and Core Graphics.
## The Coordinate System and Points
In the traditional way we think of drawing, we think of rendering onto a plane of pixels. The pixels have a fixed size, and coordinates refer to the entire space that a pixel occupies.
For instance, in the traditional model, the coordinate system looks like
TODO image
and when we say "draw a line from (0, 0) to (5, 5) exclusive", we mean "fill the spaces that are occupied by the pixels at (0, 0), (1, 1), (2, 2), (3, 3), and (4, 4)":
TODO image
Ugh. With pixels as big as the ones TODO.
But now let's pretend we're working in a coordinate system where the point at (x, y) corresponds strictly to the top-left corner of the area that a pixel occupies.
TODO image
In this model, when we say "draw a line from (0, 0) to (5, 5)", we mean "draw a straight line filling every pixel that we cross if we traced a line from the top-left corner of what we used to call the pixel at (0, 0 to the top-left corner of what we used to call the pixel at (5, 5)":
TODO image
TODO.
There are both technical and non-technical reasons for following this model. The technical reason is that implementing certain drawing operations, such as filling shapes, is much easier if we do things this way. The [cairo FAQ](http://www.cairographics.org/FAQ/#sharp_lines) explains in more detail. The non-technical reason has to do with DPI independence.
## DPI Independence vs. DPI Awareness
An upcoming trend in computing is the high-resolution display. These displays fit more dots in the same area that older screens could. The conventional term for the number of dots that fit in a given area is the "dots per inch", or DPI, measure.
A naive approach to writing programs for these new displays is to think "well, if I just take the DPI and only use it in calculations where I need to deal with real-world measurements such as inches, rendering pure pixels as I always have, I should be fine". This kind of design is centered around *DPI awareness*. I know, I used to believe this too. But here's a little secret: this is wrong! A common myth about high-resolution monitors among non-technical people is that it makes the stuff on screen smaller. The mindset I just described causes this: TODO
Instead, what we want out of a high-resolution display is *to show a more detailed view of the same image in the same space*. [The first image on Apple''s discussion of the topic](https://developer.apple.com/library/mac/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Art/backing_store_2x.png) is the perfect example. On the left, you see a low-resolution monitor. Notice how big chunks of the shapes go into the boxes. When the code that maps points to pixels runs, it can't have two colors in one square, so it has to decide what color to use. TODO
TODO
- talk about how this relates to the OpenGL unit cube
- talk about the various names (point, user space coordinate, device-independent pixel)
- talk about "scaling"
+13
View File
@@ -0,0 +1,13 @@
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
uiDrawFontHandle() may not return a unique handle per instance
+5
View File
@@ -0,0 +1,5 @@
font features are not provided by the collection and have to be added when asking for a font
it does preserve when going from CTFont to CTFontDescriptor
feature 17 has no flags in the header but can also hold small caps info
if a feature is present, it is ignored; other features will still show up
at least in the case of kLetterCaseTrait and kLowerCaseTrait
+750
View File
@@ -0,0 +1,750 @@
// pseudo-go
func (f *CTFont) IsRegistered() bool {
n := f.Attribute(kCTFontRegistrationScopeAttribute)
if n == nil {
return false
}
return n.(*CFNumber).Uint32Value() == kCTFontManagerScopeNone
}
// 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
}
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
const (
kCGFontNameKeyPostScriptName = 0x6
kCGFontNameKeyPreferredSubfamily = 0x11
kCGFontNameKeyFontSubfamily = 0x2
kCGFontNameKeyFullName = 0x4
kCGFontNameKeyPreferredFamily = 0x10
kCGFontNameKeyFontFamily = 0x1
)
func (f *CGFont) CopyName(key int) (string, bool) {
table := f.TableForTag('name')
b := table.Bytes()
n := table.Len()
// 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
}
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 {
PlatformID uint16
PlatformSpecificID uint16
LanguageID uint16
NameID uint16
Length uint16
Offset uint16
}
var nameList *x_list
addrStrings := offNameRecords + (12 * count)
if addrStrings != stringOffset {
goto hasLanguageTags
}
pos := offNameRecords
if count == 0 {
// 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 {
// 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:
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
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, 0x3e6b851f), 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&)
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
}
}
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
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
// 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)
}
// based on CoreText dylib's __ZL33CreateTraitsByStyleGlossaryStringPK10__CFString — CreateTraitsByStyleGlossaryString(__CFString const*)
func CoreText_WeightByStyleGlossaryString(str string) (weight float64, ok bool) {
str.Fold(kCFCompareCaseInsensitive, nil)
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
}
+149
View File
@@ -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 0x3e6b851f 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"
+146
View File
@@ -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"
+149
View File
@@ -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 0x3e6b851f 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"
+149
View File
@@ -0,0 +1,149 @@
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 0x3e6b851f 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 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"
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"
+247
View File
@@ -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, 0x3e6b851f)
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
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
'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)
"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
}
+160
View File
@@ -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)
}
+77
View File
@@ -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
@@ -0,0 +1,73 @@
-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"
+112
View File
@@ -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)
+73
View File
@@ -0,0 +1,73 @@
// 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 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("\(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)")
+58
View File
@@ -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))
}
}
+8
View File
@@ -0,0 +1,8 @@
# 21 october 2017
gawk '
BEGIN { FS = "\t+" }
!/float..as/ { next }
{ i = 0; if ($1 == "") i++ }
(NF-i) != 2 { next }
{ print }
' "$@"
+10
View File
@@ -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]
}
' "$@"
+3
View File
@@ -0,0 +1,3 @@
# 21 october 2017
sort -t$'\t' -k1,1 -k2,2 "$@" |
column -t -s$'\t'
+3
View File
@@ -0,0 +1,3 @@
// 22 october 2017
extern int realMain(void);
int main(void) { return realMain(); }
+11
View File
@@ -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)
@@ -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
+51
View File
@@ -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
+1
View File
@@ -0,0 +1 @@
hiding a control also hides its label
+1
View File
@@ -0,0 +1 @@
after uiQuit or if uiShouldQuit returns nonzero, uiQueueMain's effect is undefined
+1
View File
@@ -0,0 +1 @@
the function passed to mainsteps must not return until uiQuit itself has been called; otherwise the results are undefined
@@ -0,0 +1,136 @@
// 25 june 2018
#include <gtk/gtk.h>
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;
}
@@ -0,0 +1,907 @@
// 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 <windows.h>
#include <commctrl.h>
#include <uxtheme.h>
#include <vsstyle.h>
#include <vssym32.h>
#include <windowsx.h>
#include <stdio.h>
#include <stdlib.h>
// 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);
}
// 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];
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;
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;
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;
};
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);
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 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; }
};
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, colors + 0);
if (hr != S_OK)
return hr;
hr = GetThemeColor(theme,
2, 0,
TMT_GRADIENTCOLOR2, colors + 1);
if (hr != S_OK)
return hr;
return GetThemeColor(theme,
2, 0,
TMT_GRADIENTCOLOR3, colors + 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 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; }
};
// 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;
};
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;
virtual HRESULT drawButton(commandModuleStyleParams *p, HWND button, HDC dc, UINT uItemState, RECT *rcPaint) const = 0;
};
class commandModuleStyleThemed : public commandModuleStyle {
HTHEME theme;
HTHEME textstyleTheme;
public:
commandModuleStyleThemed(HTHEME theme, HTHEME textstyleTheme)
{
this->theme = theme;
this->textstyleTheme = textstyleTheme;
}
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 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;
if (m == NULL)
return E_POINTER;
releaseDC = FALSE;
if (dc == NULL) {
dc = GetDC(button);
if (dc == NULL)
return lastErrorToHRESULT(GetLastError());
releaseDC = TRUE;
}
ZeroMemory(&tm, sizeof (TEXTMETRICW));
hr = GetThemeTextMetrics(this->textstyleTheme, dc,
TEXT_BODYTEXT, 0,
&tm);
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);
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);
hr = GetThemeTextExtent(this->textstyleTheme, dc,
TEXT_BODYTEXT, 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;
// 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(this->textstyleTheme, dc,
4, 0,
TMT_FONT, &lf);
if (hr != S_OK)
goto fail;
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...
hr = GetThemePartSize(theme, dc,
p->partID_CMOD_MENUGLYPH(), 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(p->buttonTextArrowSeparationXDIP(), m->dpiX);
}
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);
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;
};
// TODO check errors
virtual HRESULT buttonRects(commandModuleStyleParams *p, HWND button, struct buttonMetrics *m, struct buttonRects *r) const
{
if (r == NULL)
return E_POINTER;
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(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(p->buttonTextArrowSeparationXDIP(), m->dpiX);
r->textRect.right += dipsToX(p->buttonMarginsXDIP(), m->dpiX);
}
}
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
// 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);
}
#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)
{
HRESULT hr;
if (nm->dwDrawStage != CDDS_PREPAINT)
return CDRF_DODEFAULT;
hr = cms->drawButton(cmsp, nm->hdr.hwndFrom,
nm->hdc, nm->uItemState, &(nm->rc));
if (hr != S_OK)
return CDRF_DODEFAULT;
return CDRF_SKIPDEFAULT;
}
void onWM_CREATE(HWND hwnd)
{
int buttonx, buttony;
int i;
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,
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)
{
if (cmsp != NULL) {
delete cmsp;
cmsp = NULL;
}
if (cms != NULL) {
delete cms;
cms = 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");
cms = new commandModuleStyleThemed(theme, textstyleTheme);
if (strcmp(which, "vista") == 0)
cmsp = new commandModuleStyleParamsVista;
else
cmsp = new commandModuleStyleParams7;
}
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 = 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,
buttonx, buttony, m.fittingSize.cx, m.fittingSize.cy,
SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER);
if (dwp == NULL)
diele("DeferWindowPos()");
buttonx += m.fittingSize.cx;
}
if (EndDeferWindowPos(dwp) == 0)
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)
{
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)
{
HDC dc;
PAINTSTRUCT ps;
NMHDR *nm = (NMHDR *) lParam;
int i;
switch (uMsg) {
case WM_CREATE:
onWM_CREATE(hwnd);
updateTheme(hwnd);
repositionButtons(hwnd);
break;
case WM_CLOSE:
PostQuitMessage(0);
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;
folderBarRect(hwnd, dc, &w);
cms->drawFolderBar(cmsp, dc, &w, &(ps.rcPaint));}
EndPaint(hwnd, &ps);
return 0;
case WM_PRINTCLIENT:
{RECT w, paint;
folderBarRect(hwnd, (HDC) wParam, &w);
GetClientRect(hwnd,&paint);
cms->drawFolderBar(cmsp, (HDC) wParam, &w, &w);}
return 0;
#if 0
case WM_COMMAND:
handleEvents(hwnd, wParam);
break;
#endif
case WM_NOTIFY:
switch (nm->code) {
case NM_CUSTOMDRAW:
for (i = 0; i < 5; i++)
if (nm->hwndFrom == leftButtons[i])
return drawExplorerButton((NMCUSTOMDRAW *) nm);
break;
}
break;
}
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
int main(int argc, char *argv[])
{
STARTUPINFOW si;
int nCmdShow;
INITCOMMONCONTROLSEX icc;
HICON hDefaultIcon;
HCURSOR hDefaultCursor;
WNDCLASSW wc;
HWND mainwin;
MSG msg;
HRESULT hr;
if (argc > 1)
which = argv[1];
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;
}
@@ -0,0 +1,676 @@
// 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 <windows.h>
#include <commctrl.h>
#include <uxtheme.h>
#include <vsstyle.h>
#include <vssym32.h>
#include <windowsx.h>
#include <stdio.h>
#include <stdlib.h>
// cl winrebarexplorertheme.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 rebar;
HWND leftbar;
HWND rightbar;
HWND rebarCombo;
HWND toolbarCombo;
HWND toolbarTransparentCheckbox;
HICON shieldIcon;
HICON applicationIcon;
HICON helpIcon;
HIMAGELIST rightList;
#define toolbarStyles (WS_CHILD | CCS_NODIVIDER | CCS_NOPARENTALIGN | CCS_NORESIZE | TBSTYLE_FLAT)
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;
}
static struct {
const WCHAR *text;
LRESULT (*handleRebar)(NMCUSTOMDRAW *nm);
LRESULT (*handleToolbar)(NMTBCUSTOMDRAW *nm);
} drawmodes[] = {
{ L"SetWindowTheme()", NULL, NULL },
{ L"Custom Draw Explorer", customDrawExplorerRebar, customDrawExplorerToolbar },
{ NULL, NULL },
};
int drawmode = 0;
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,
};
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)
{
TBBUTTON tbb[5];
RECT btnrect;
DWORD tbbtnsize;
LONG tbsizex, tbsizey;
REBARBANDINFOW rbi;
HWND button;
LONG buttonx, buttony;
LONG combox, comboy;
int i;
rebar = CreateWindowExW(0,
REBARCLASSNAMEW, NULL,
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)
diele("CreateWindowExW(REBARCLASSNAMEW)");
leftbar = CreateWindowExW(0,
TOOLBARCLASSNAMEW, NULL,
toolbarStyles | TBSTYLE_LIST | 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)
// 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));
ZeroMemory(tbb, 5 * sizeof (TBBUTTON));
for (i = 0; i < 5; i++) {
tbb[i].iBitmap = 0;
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_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);
ZeroMemory(&rbi, sizeof (REBARBANDINFOW));
rbi.cbSize = sizeof (REBARBANDINFOW);
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;
rbi.cyChild = tbsizey;
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");
rightbar = CreateWindowExW(0,
TOOLBARCLASSNAMEW, NULL,
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);
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));
ZeroMemory(tbb, 5 * sizeof (TBBUTTON));
tbb[0].iBitmap = 0;
tbb[0].idCommand = 0;
tbb[0].fsState = TBSTATE_ENABLED;
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);
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);
tbsizey = HIWORD(tbbtnsize);
ZeroMemory(&rbi, sizeof (REBARBANDINFOW));
rbi.cbSize = sizeof (REBARBANDINFOW);
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");
buttonx = 10;
buttony = 40;
#define buttonwid 200
#define buttonht 25
for (i = 0; drawmodes[i].text != NULL; i++) {
button = CreateWindowExW(0,
L"BUTTON", drawmodes[i].text,
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;
}
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,
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]));
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?
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()");
}
// 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;
}
LRESULT CALLBACK wndproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
NMHDR *nm = (NMHDR *) lParam;
switch (uMsg) {
case WM_CREATE:
onWM_CREATE(hwnd);
break;
case WM_CLOSE:
PostQuitMessage(0);
break;
case WM_SIZE:
repositionRebar(hwnd);
break;
case WM_COMMAND:
handleEvents(hwnd, wParam);
break;
case WM_NOTIFY:
switch (nm->code) {
case NM_CUSTOMDRAW:
if (drawmode == 0)
break;
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;
}
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;
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
return 0;
}
+41
View File
@@ -0,0 +1,41 @@
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.
# Reserved names; for users
All reserved names in libui are defined by a prefix followed by any uppercase letter in ASCII. The bullet lists before list those prefixes.
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:
- `ui`
- `uipriv`
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):
- `uipriv`
Objective-C method names:
- `initWithUipriv`
- `initWithFrame:uipriv` (TODO probably worth removing)
- `uipriv`
- `isUipriv` (for compatibility with KVO and `@property` statements)
- `setUipriv` (for compatibility with KVO and `@property` statements)
Objective-C ivar names:
- `uipriv`
- `_uipriv` (for compatibility with KVO and `@property` statements)
Objective-C property names:
- `uipriv`
TODO GObject macros (in libui's source code), properties, and signals
# Developer notes
TODO
+1
View File
@@ -0,0 +1 @@
if min >= max then they are swapped
+1
View File
@@ -0,0 +1 @@
if min >= max then they are swapped
+2
View File
@@ -0,0 +1,2 @@
comctl6
libui.res
+3
View File
@@ -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
+1
View File
@@ -0,0 +1 @@
libui uses resources starting at 29000