First Commit
This commit is contained in:
commit
ae0fab9400
424 files changed
+71006
No files matched your search
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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"
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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"
|
||||
@@ -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)
|
||||
@@ -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)")
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
# 21 october 2017
|
||||
gawk '
|
||||
BEGIN { FS = "\t+" }
|
||||
!/float..as/ { next }
|
||||
{ i = 0; if ($1 == "") i++ }
|
||||
(NF-i) != 2 { next }
|
||||
{ print }
|
||||
' "$@"
|
||||
@@ -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]
|
||||
}
|
||||
' "$@"
|
||||
@@ -0,0 +1,3 @@
|
||||
# 21 october 2017
|
||||
sort -t$'\t' -k1,1 -k2,2 "$@" |
|
||||
column -t -s$'\t'
|
||||
@@ -0,0 +1,3 @@
|
||||
// 22 october 2017
|
||||
extern int realMain(void);
|
||||
int main(void) { return realMain(); }
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
hiding a control also hides its label
|
||||
@@ -0,0 +1 @@
|
||||
after uiQuit or if uiShouldQuit returns nonzero, uiQueueMain's effect is undefined
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
if min >= max then they are swapped
|
||||
@@ -0,0 +1 @@
|
||||
if min >= max then they are swapped
|
||||
@@ -0,0 +1,2 @@
|
||||
comctl6
|
||||
libui.res
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
libui uses resources starting at 29000
|
||||
Reference in new issue
Block a user