Compare commits

..

No commits in common. "witmaster" and "alpha3.5" have entirely different histories.

323 changed files with 3557 additions and 28035 deletions

View File

@ -1,92 +0,0 @@
version: 'build #{build}'
environment:
matrix:
- arch: 386
libtype: shared
libfiles: libui.dll libui.lib
compiler: msvc2013
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013
- arch: 386
libtype: static
libfiles: libui.lib
compiler: msvc2013
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013
- arch: amd64
libtype: shared
libfiles: libui.dll libui.lib
compiler: msvc2013
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013
- arch: amd64
libtype: static
libfiles: libui.lib
compiler: msvc2013
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013
- arch: 386
libtype: static
libfiles: libui.lib
compiler: mingw
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
- arch: amd64
libtype: static
libfiles: libui.lib
compiler: mingw
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
platform:
- x64
# Note: AppVeyor tries to be "helpful" and splits cmd.exe scripts into their constitutent lines to check their error codes. There is no way to switch this off; for true multi-line scripts we have to use PowerShell. But we need to use vcvarsall.bat, so that's out of the question.
install:
# Set Python Version
- set "PYTHON_ROOT=C:\python37-x64"
- if %arch%==386 ( set "PYTHON_ROOT=C:\python37" )
- set "PATH=%PYTHON_ROOT%;%PYTHON_ROOT%\Scripts;%PATH%"
# Install Latest Meson
- pip install meson
# Install Ninja
- powershell -Command "Invoke-WebRequest https://github.com/ninja-build/ninja/releases/download/v1.9.0/ninja-win.zip -OutFile C:\ninja-win.zip"
- mkdir C:\ninja
- powershell -Command "Expand-Archive -LiteralPath C:\ninja-win.zip -DestinationPath C:\ninja"
- set "PATH=C:\ninja;%PATH%"
# Parameters for the build_script phase, to reduce their noise.
- set "mingwPath=C:\msys64\mingw64\bin"
- set vcvarsallArch=x86
- if %arch%==386 ( set "mingwPath=C:\msys64\mingw32\bin" )
- if %arch%==386 ( set vcvarsallArch=amd64 )
build_script:
- if %compiler%==mingw ( set "PATH=%mingwPath%;%PATH%" )
- if not %compiler%==mingw ( call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" %vcvarsallArch% )
- meson setup build --buildtype=release --default-library=%libtype%
- ninja -C build
after_build:
- set "version=%APPVEYOR_REPO_BRANCH%"
- if %APPVEYOR_REPO_TAG%==true ( set "version=%APPVEYOR_REPO_TAG_NAME%" )
- set "artifact=%version%-windows-%arch%-%compiler%-%libtype%"
- cd build\meson-out
# TODO msvc only?
- if %libtype%==static ( ren libui.a libui.lib )
- copy ..\..\ui.h .
- copy ..\..\ui_windows.h .
# remove unnecessary files
# TODO should we do this on Azure too?
- del libui.exp
- 7z a "%APPVEYOR_BUILD_FOLDER%\libui-%artifact%.zip" %libfiles% ui.h ui_windows.h
- 7z a "%APPVEYOR_BUILD_FOLDER%\examples-%artifact%.zip" controlgallery.exe cpp-multithread.exe datetime.exe drawtext.exe histogram.exe tester.exe timer.exe
- del ui.h ui_windows.h
artifacts:
- path: libui-*.zip
name: libui
- path: examples-*.zip
name: examples
deploy:
provider: GitHub
artifact: libui, examples
auth_token:
secure: li92W7mFAC8HbAVeZN6Ugmo5H1GzKSjr6DXlMniLcCRspKmi2Nz1nlslSa+9sLfo
on:
appveyor_repo_tag: true # deploy on tag push only

23
.travis.yml Normal file
View File

@ -0,0 +1,23 @@
os:
- linux
- osx
# This makes us use Ubuntu 14 instead of 12
dist: trusty
# Notes:
# - Travis uses cmake 3.0.2 on OS X; we need 3.1 or newer (thanks tbodt)
language: c
script:
- if [ "$TRAVIS_OS_NAME" == "linux" ]; then sudo apt-get update; fi
- if [ "$TRAVIS_OS_NAME" == "linux" ]; then sudo apt-get install libgtk-3-dev -y || sudo apt-cache search libgtk3; fi
- if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew update; fi
- mkdir build
- cd build
- cmake --version
- cmake .. -G "Unix Makefiles"
- make tester examples
- rm -rf *
- cmake .. -G "Unix Makefiles" -DBUILD_SHARED_LIBS=OFF
- make tester examples

22
ANNOUNCE.md Normal file
View File

@ -0,0 +1,22 @@
# Old Announcements
* **29 May 2016**
* **Alpha 3 is here!** Get it [here](https://github.com/andlabs/libui/releases/tag/alpha3).
* The next packaged release will introduce:
* uiGrid, another way to lay out controls, a la GtkGrid
* uiOpenGLArea, a way to render OpenGL content in a libui uiArea
* uiTable, a data grid control that may or may not have tree facilities (if it does, it will be called uiTree instead)
* a complete, possibly rewritten, drawing and text rendering infrastructure
* **24 May 2016**
* You can now help choose [a potential new build system for libui](https://github.com/andlabs/libui/issues/62).
* Tomorrow I will decide if OS X 10.7 will also be dropped alongside GTK+ 3.4-3.8 this Saturday. Stay tuned.
* **22 May 2016**
* Two more open questions I'd like your feedback on are available [here](https://github.com/andlabs/libui/issues/48) and [here](https://github.com/andlabs/libui/issues/25).
* Sometime in the next 48 hours (before 23:59 EDT on 24 May 2016) I will split `uiCombobox` into two separate controls, `uiCombobox` and `uiEditableCombobox`, each with slightly different events and "selected item" mechanics. Prepare your existing code.
* **21 May 2016**
* I will now post announcements and updates here.
* Now that Ubuntu 16.04 LTS is here, no earlier than next Saturday, 28 May 2016 at noon EDT, **I will bump the minimum GTK+ version from 3.4 to 3.10**. This will add a lot of new features that I can now add to libui, such as search-oriented uiEntries, lists of arbitrary control layouts, and more. If you are still running a Linux distribution that doesn't come with 3.10, you will either need to upgrade or use jhbuild to set up a newer version of GTK+ in a private environment.
* You can decide if I should also drop OS X 10.7 [here](https://github.com/andlabs/libui/issues/46).

219
CMakeLists.txt Normal file
View File

@ -0,0 +1,219 @@
# 3 june 2016
# see https://cmake.org/gitweb?p=cmake.git;a=commit;h=95cdf132489c79e88a10fdf7a7566fa002c7680b (thanks ngladitz in irc.freenode.net/#cmake)
cmake_minimum_required(VERSION 3.1.0 FATAL_ERROR)
# TODOs
# - silence entering/leaving messages?
# - uname -s for more refined OS control
# - Haiku for haiku
# - debian DESTDIR? https://github.com/andlabs/libui/pull/10
# - libui-combined* needs to be deleted so that custom command can run every time
# - add notelemetry.obj to *ALL TARGETS* on VS2015 and up - https://www.infoq.com/news/2016/06/visual-cpp-telemetry
# - switch to 3.1.0 features
# the docs say we need to set this up prior to project()
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.8")
# we want to disable incremental linking
# see also:
# - https://github.com/bulletphysics/bullet3/blob/master/CMakeLists.txt#L43
# - https://cmake.org/pipermail/cmake/2010-February/035174.html
# this must also go before project()
set(MSVC_INCREMENTAL_DEFAULT ON)
# default to debug builds
# do this before project() just to be safe
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE DEBUG CACHE STRING "" FORCE)
endif()
project(libui)
option(BUILD_SHARED_LIBS "Whether to build libui as a shared library or a static library" ON)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/out")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/out")
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/out")
set(CMAKE_PDB_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/out")
if(APPLE)
set(_OSNAME darwin)
set(_HASVERSION TRUE)
set(_VERSION "A")
# always use our rpath
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
# the / is required by some older versions of OS X
set(CMAKE_INSTALL_RPATH "@executable_path/")
set(CMAKE_MACOSX_RPATH TRUE)
elseif(WIN32)
set(_OSNAME windows)
# and don't include the default libraries with ANY of the builds
# note the CACHE FORCE stuff is required here
set(CMAKE_C_STANDARD_LIBRARIES CACHE STRING "" FORCE)
set(CMAKE_CXX_STANDARD_LIBRARIES CACHE STRING "" FORCE)
else()
set(_OSNAME unix)
set(_HASVERSION TRUE)
set(_VERSION "0")
# always use our rpath
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
set(CMAKE_INSTALL_RPATH "\$ORIGIN")
endif()
# common flags
if(MSVC)
# TODO subsystem version
# TODO /Wall does too much
# TODO -Wno-switch equivalent
# TODO /sdl turns C4996 into an ERROR
# don't use /analyze; that requires us to write annotations everywhere
# TODO undecided flags from qo?
# /RTCc is not supplied because it's discouraged as of VS2015; see https://www.reddit.com/r/cpp/comments/46mhne/rtcc_rejects_conformant_code_with_visual_c_2015/d06auq5
# /EHsc is to shut the compiler up in some cases
# TODO make /EHsc C++-only
set(_COMMON_CFLAGS
/W4 /wd4100
/bigobj /nologo
/RTC1 /RTCs /RTCu
/EHsc
)
# note the /MANIFEST:NO (which must be / and uppercase); thanks FraGag (https://github.com/andlabs/libui/issues/93#issuecomment-223183436)
# TODO warnings on undefined symbols
set(_COMMON_LDFLAGS
/LARGEADDRESSAWARE
/NOLOGO
/INCREMENTAL:NO
/MANIFEST:NO
)
# TODO autogenerate a .def file?
# more incremental linking fixes
# TODO actually get rid of incremental linking here
else()
set(_COMMON_CFLAGS
-Wall -Wextra -pedantic
-Wno-unused-parameter
-Wno-switch
-fvisibility=hidden
)
# don't use C_VERSION or CXX_VERSION because they use GNU standards
# TODO we can actually do this; set both C_EXTENSIONS and CXX_EXTENSIONS to OFF
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --std=c99")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --std=c++11")
set(_COMMON_LDFLAGS
-fvisibility=hidden
)
# don't require shipping the MinGW-w64 DLLs
if(WIN32)
list(APPEND _COMMON_LDFLAGS
-static
-static-libgcc
-static-libstdc++
)
endif()
endif()
# problem:
# - target_link_libraries() only supports - for flags
# - but cmake only doesn't generate the manifest if the flag has a /
macro(_target_link_options_private _target)
foreach(_opt IN LISTS ${ARGN})
set_property(TARGET ${_target} APPEND_STRING PROPERTY
LINK_FLAGS " ${_opt}")
endforeach()
endmacro()
add_subdirectory("common")
add_subdirectory("${_OSNAME}")
add_library(${_LIBUINAME} ${_LIBUI_SOURCES})
target_include_directories(${_LIBUINAME}
PUBLIC .
PRIVATE ${_LIBUI_INCLUEDIRS})
target_compile_definitions(${_LIBUINAME}
PRIVATE ${_LIBUI_DEFS})
# cmake produces this for us by default but only for shared libraries
target_compile_definitions(${_LIBUINAME}
PRIVATE libui_EXPORTS)
target_compile_options(${_LIBUINAME}
PUBLIC ${_COMMON_CFLAGS}
PRIVATE ${_LIBUI_CFLAGS})
# TODO link directories?
if(BUILD_SHARED_LIBS)
target_link_libraries(${_LIBUINAME}
PRIVATE ${_LIBUI_LIBS})
endif()
# TODO INTERFACE libs don't inherit to grandhcildren?
# on Windows the linker for static libraries is different; don't give it the flags
if(BUILD_SHARED_LIBS)
_target_link_options_private(${_LIBUINAME}
_COMMON_LDFLAGS
_LIBUI_LDFLAGS)
endif()
if(NOT BUILD_SHARED_LIBS)
_handle_static()
# TODO figure out a way to tell libui that it's static
target_compile_definitions(${_LIBUINAME}
PUBLIC _UI_STATIC)
endif()
if(NOT MSVC)
# on non-MSVC compilers cmake adds an extra lib-
# note that we apply this to libui, not to any intermediates
set_target_properties(libui PROPERTIES
OUTPUT_NAME ui)
# flags for warning on undefined symbols
# TODO figure out why FreeBSD follows linked libraries here
# TODO figure out MSVC equivalents
if(BUILD_SHARED_LIBS)
if(NOT (${CMAKE_SYSTEM_NAME} STREQUAL FreeBSD))
# on OS X we don't need to do this; Apple's linker warns about undefined symbols in -shared builds!
if(NOT APPLE)
target_link_libraries(libui
PRIVATE -Wl,--no-undefined -Wl,--no-allow-shlib-undefined
)
endif()
endif()
endif()
endif()
if(BUILD_SHARED_LIBS)
if(_HASVERSION)
set_target_properties(${_LIBUINAME} PROPERTIES
SOVERSION "${_VERSION}")
endif()
endif()
macro(_add_exec _name)
add_executable(${_name}
WIN32 EXCLUDE_FROM_ALL
${ARGN})
target_link_libraries(${_name} libui ${_LIBUI_STATIC_RES})
_target_link_options_private(${_name}
_COMMON_LDFLAGS)
# make shared-linked executables PIC too
if(BUILD_SHARED_LIBS)
set_property(TARGET ${_name} PROPERTY
POSITION_INDEPENDENT_CODE True)
endif()
# TODO see above about INTERFACE
if(NOT BUILD_SHARED_LIBS)
target_link_libraries(${_name}
${_LIBUI_LIBS})
endif()
# TODOfor some reason these don't propagate
if(NOT WIN32)
target_include_directories(${_name}
PUBLIC .)
target_compile_options(${_name}
PUBLIC ${_COMMON_CFLAGS})
endif()
endmacro()
add_subdirectory("test")
add_subdirectory("examples")

View File

@ -1,131 +0,0 @@
# Contributing to libui
libui is an open source project that openly accepts contributions. I appreciate your help!
## Rules for contributing code
While libui is open to contributions, a number of recent, significantly large contributions and uncontributed forks have recently surfaced that do not present themselves in a form that makes it easy for libui to accept them. In order to give your contribution a high chance of being accepted into libui, please keep the following in mind as you prepare your contribution.
### Commit messages and pull request description
libui does not enforce rules about the length or detail that a commit message. I'm not looking for an essay. However, single-word descriptions of nontrivial changes are *not* acceptable. I should be able to get a glimpse of what a commit does from the commit message, even if it's just one sentence to describe a trivial change. (Yes, I know I haven't followed this rule strictly myself, but I try not to break it too.) And a commit message should encompass everything; typically, I make a number of incremental commits toward a feature, so the commit messages don't have to be too long to explain everything.
Your pull request description, on the other hand, must be a summary of the sum total of all the changes made to libui. Don't just drop a pull request on me with a one-line-long elevator pitch of what you added. Describe your proposed API changes, implementation requirements, and any important consequences of your work.
### Code formatting
libui uses K&R C formatting rules for overall code structure: spaces after keywords like `if`, `{` on the same line as a statement with a space, `{` on its own line after a function or method signature (even those inside the class body), no space after the name of a function, etc.
Use hard tabs, NOT spaces, for indentation. I use a proportional-width font and my text editor doesn't set tabs to a multiple of the space width, so I *will* be able to tell. If you use a fixed-width font, I suggest setting a tab width of 4 spaces per tab, but don't put diagrams in comments with hard tabs, because not everyone does this.
Expressions should have a space around binary operators, and use parentheses where it would help humans gather the meaning of an expression, regardless of whether a computer could tell what is correct.
When breaking expressions into multiple lines, always break *after* an operator, such as `,` or `&&`.
There should be a newline between a function's variables and a function's code. After that, you can place newlines to delimit different parts of a function, but don't go crazy.
In the event you are unsure of something, refer to existing libui code for examples. I may wind up fixing minor details later anyway, so don't fret about getting minor details right the first time.
### Naming
libui uses camel-case for naming, with a handful of very specific exceptions (namely GObject method names, where GObject itself enforces the naming convention).
All public API names should begin with `ui` and followed by a capital letter. All public struct field names should begin with a capital letter. This is identical to the visibiilty rules of Go, assuming a package name of `ui`.
Private API names — specifcally those used by more than one source file — should begin with `uipriv` and be followed by a capital letter. This avoids namespace collisions in static libraries.
Static functions and static objects do not have naming restrictions.
Acronyms should **NOT** be mixed-case. `http` for the first word in a camel-case name, `HTTP` for all else, but **NEVER** `Http`. This is possibly the only aspect of the controversial nature of code style that I consider indefensibly stupid.
### API documentation
(TODO I am writing an API documentation tool; once that becomes stable enough I can talk about documenting libui properly. You'll see vestiges of it throughout ui.h, though.)
### Other commenting
(TODO write this part)
### Compatibility
libui takes backward compatibility seriously. Your code should not break the current compatibility requirements. All platforms provide a series of macros, defined in the various `uipriv_*.h` files (or `winapi.hpp` on Windows), that specify the minimum required version. If you find yourself needing to remove these or ignore resultant warnings or errors, you're probably breaking compatibility.
Choosing to drop older versions of Windows, GTK+, and OS X that I could have easily continued to support was not done lightly. If you want to discuss dropping support for an older version of any of these for the benefit of libui, file an issue pleading your case (see below).
GTK+ versions are harder to drop because I am limited by Linux distribution packaging. In general, I will consider bumping GTK+ versions on a new Ubuntu LTS release, choosing the earliest version available on the major distributions at the time of the *previous* Ubuntu LTS release. As of writing, the next milestone will be *after* April 2018, and the target GTK+ version appears to be 3.18, judging by Ubuntu 16.04 LTS alone. This may be bumped back depending on other distros (or it may not be bumped at all), but you may wish to keep this in mind as you write.
(TODO talk about future.c/.cpp/.m files)
As for language compatibility, libui is written in C99. I have no intention of changing this.
As for build system compatibility, libui uses CMake 3.1.0. If you wish to bump the version, file an issue pleading your case (but see below).
**If you do plead your case**, keep in mind that "it's old" is not a sufficient reason to drop things. If you can prove that **virtually no one** uses the minimum version anymore, then that is stronger evidence. The best evidence, however, is that not upgrading will hold libui back in some significant way — but beware that there are some things I won't add to libui itself.
### Windows-specific notes
The Windows backend of libui is written in C++ using C++11.
Despite using C++, please refrain from using the following:
- using C++ in ui_windows.h (this file should still be C compatible)
- smart pointers
- namespaces
- `using namespace`
- ATL, MFC, WTL
The following are not recommended, for consistency with the rest of libui:
- variable declarations anywhere in a function (keep them all at the top)
- `for (int x...` (C++11 foreach syntax is fine, though)
- omitting the `struct` on type names for ordinary structs
The format of a class should be
```c++
class name : public ancestor {
int privateVariable;
// etc.
public:
// public stuff here
};
```
### GTK+-specific notes
Avoid GNU-specific language features. I build with strict C99 conformance.
### OS X-specific notes
Avoid GNU-specific/clang-specific language features. I build with strict C99 conformance.
libui is presently **not** ARC-compliant. Features that require ARC should be avoided for now. I may consider changing this in the future, but it will be a significant change.
To ensure maximum compiler output in the event of a coding error, there should not be any implicit method calls in Objective-C code. For instance, don't do
```objective-c
[[array objectAtIndex:i] method]
```
Instead, cast the result of `objectAtIndex:` to the appropriate type, and then call the method. (TODO learn about, then decide a policy on, soft-generics on things other than `id`)
The format of a class should be
```objective-c
@interface name : parent<protocols> {
// ivars
}
// properties
- (ret)method:(int)arg;
// more methods
@end
@implementation name
- (ret)method:(int)arg
{
// note the lack of semicolon
}
@end
```

33
Changelog.md Normal file
View File

@ -0,0 +1,33 @@
# Old Updates
* **29 May 2016**
* Thanks to @pcwalton, we can now statically link libui! Simply do `make STATIC=1` instead of just `make`.
* On Windows you must link both `libui.lib` and `libui.res` AND provide a Common Controls 6 manifest for output static binaries to work properly.
* **28 May 2016**
* As promised, **the minimum system requirements are now OS X 10.8 and GTK+ 3.10 for OS X and Unix, respectively**.
* **26 May 2016**
* Two OS X-specific functions have been added: `uiDarwinMarginAmount()` and `uiDarwinPaddingAmount()`. These return the amount of margins and padding, respectively, to give to a control, and are intended for container implementations. These are suitable for the constant of a NSLayoutConstraint. They both take a pointer parameter that is reserved for future use and should be `NULL`.
* **25 May 2016**
* uiDrawTextLayout attributes are now specified in units of *graphemes* on all platforms. This means characters as seen from a user's perspective, not Unicode codepoints or UTF-8 bytes. So a long string of combining marker codepoints after one codepoint would still count as one grapheme.
* **24 May 2016**
* As promised, `uiCombobox` is now split into `uiCombobox` for non-editable comboboxes and `uiEditableCombobox` for editable comboboxes. Mind the function changes as well :)
* There is a new function `uiMainStep()`, which runs one iteration of the main loop. It takes a single boolean argument, indicating whether to wait for an event to occur or not. It returns true if an event was processed (or if no event is available if you don't want to wait) and false if the event loop was told to stop (for instance, `uiQuit()` was called).
* **23 May 2016**
* Fixed surrogate pair drawing on OS X.
* **22 May 2016**
* Removed `uiControlVerifyDestroy()`; that is now part of `uiFreeControl()` itself.
* Added `uiPi`, a constant for π. This is provided for C and C++ programmers, where there is no standard named constant for π; bindings authors shouldn't need to worry about this.
* Fixed uiMultilineEntry not properly having line breaks on Windows.
* Added `uiNewNonWrappingMultilineEntry()`, which creates a uiMultilineEntry that scrolls horizontally instead of wrapping lines. (This is not documented as being changeable after the fact on Windows, hence it's a creation-time choice.)
* uiAreas on Windows and some internal Direct2D areas now respond to `WM_PRINTCLIENT` properly, which should hopefully increase the quality of screenshots.
* uiDateTimePicker on GTK+ works properly on RTL layouts and no longer disappears off the bottom of the screen if not enough room is available. It will also no longer be marked for localization of the time format (what the separator should be and whether to use 24-hour time), as that information is not provided by the locale system. :(
* Added `uiUserBugCannotSetParentOnToplevel()`, which should be used by implementations of toplevel controls in their `SetParent()` implementations. This will also be the beginning of consolidating common user bug messages into a single place, though this will be one of the only few exported user bug functions.
* uiSpinbox and uiSlider now merely swap their min and max if min ≥ max. They will no longer panic and do nothing, respectively.
* Matrix scaling will no longer leave the matrix in an invalid state on OS X and GTK+.
* `uiMultilineEntrySetText()` and `uiMutlilineEntryAppend()` on GTK+ no longer fire `OnChanged()` events.

View File

@ -1,7 +0,0 @@
all:
redomod:
rm -f go.*
GO111MODULE= go mod init
GO111MODULE= go mod tidy

113
NEWS.md
View File

@ -1,113 +0,0 @@
# Old News
* **27 November 2016**
* Decided to split the table stuff into its own branch. It will be developed independently of everything else, along with a few other features.
* **2 November 2016**
* Added two new functions to replace the deleted `uiWindowPosition()` and friends: `uiAreaBeginUserWindowMove()` and `uiAreaBeginUserWindowResize()`. When used in a `uiAreaHandler.Mouse()` event handler, these let you initiate a user-driven mouse move or mouse resize of the window at any point in a uiArea.
* **31 October 2016**
* @krakjoe noticed that I accidentally used thread-unsafe code in uiQueueMain() on Unix. Fixed.
* **24 October 2016**
* `uiWindowSetContentSize()` on Unix no longer needs to call up the GTK+ main loop. As a result, bugs related to strange behavior using that function (and the now-deleted `uiWindowSetPosition()` and `uiWindowCenter()`) should go away. I'll need to go through the bugs to verify as much, though.
* **22 October 2016**
* Due to being unable to guarantee they will work (especially as we move toward capability-driven window systems like Wayland), or being unable to work without hacking that breaks other things, the following functions have been removed: `uiWindowPosition()`, `uiWindowSetPosition()`, `uiWindowCenter()`, and `uiWindowOnPositionChanged()`. Centering may come back at some point in the future, albeit in a possibly restricted form. A function to initiate a user move when a part of a uiArea is clicked will be provided soon.
* **21 October 2016**
* `uiDrawTextWeightUltraBold` is now spelled correctly. Thanks to @krakjoe.
* **18 June 2016**
* Help decide [the design of tables and trees in libui](https://github.com/andlabs/libui/issues/159); the implementation starts within the next few days, if not tomorrow!
* **17 June 2016**
* **CMake 3.1.0 is now required.** This is due to CMake's rapid development pace in the past few years adding things libui needs to build on as many systems as possible. If your OS is supported by libui but its repositories ship with an older version of CMake, you will need to find an updated one somewhere.
* Please help [plan out a better menu API](https://github.com/andlabs/libui/issues/152).
* `uiMainSteps()` no longer takes any arguments and no longer needs to invoke a function to do the work. You still need to call it, but once you do, it will return immediately and you can then get right to your main loop.
* **CMake 3.1.0 is now required.** This is due to CMake's rapid development pace in the past few years adding things libui needs to build on as many systems as possible. If your OS is supported by libui but its repositories ship with an older version of CMake, you will need to find an updated one somewhere.
* Added `uiNewVerticalSeparator()` to complement `uiNewHorizontalSeparator()`.
* **16 June 2016**
* Added `uiWindowContentSize()`, `uiWindowSetContentSize()`, and `uiWindowOnContentSizeChanged()` methods for manipulating uiWindow content sizes. Note the use of "content size"; the size you work with does NOT include window decorations (titlebars, menus, etc.).
* Added `uiWindowFullscreen()` and `uiWindowSetFullscreen()` to allow making fullscreen uiWindows, taking advantage of OS facilities for fullscreen and without changing the screen resolution (!).
* Added `uiWindowBorderless()` and `uiWindowSetBorderless()` for allowing borderless uiWindows.
* Added `uiMainSteps()`. You call this instead of `uiMain()` if you want to run the main loop yourself. You pass in a function that will be called; within that function, you call `uiMainStep()` repeatedly until it returns 0, doing whatever you need to do in the meantime. (This was needed because just having `uiMainStep()` by itself only worked on some systems.)
* Added `uiProgressBarValue()` and allowed passing -1 to `uiProgressBarSetValue()` to make an indeterminate progress bar. Thanks to @emersion.
* **15 June 2016**
* Added `uiFormDelete()`; thanks to @emersion.
* Added `uiWindowPosition()`, `uiWindowSetPosition()`, `uiWindowCenter()`, and `uiWindowOnPositionChanged()`, methods for manipulating uiWindow position.
* **14 June 2016**
* uiDarwinControl now has a `ChildVisibilityChanged()` method and a corresponding `NotifyVisibilityChanged()` function that is called by the default show/hide handlers. This is used to make visibility changes work on OS X; uiBox, uiForm, and uiGrid all respect these now.
* The same has been done on the Windows side as well.
* Hiding and showing controls and padding calculations are now correct on Windows at long last.
* Hiding a control in a uiForm now hides its label on all platforms.
* **13 June 2016**
* `intmax_t` and `uintmax_t` are no longer used for libui API functions; now we use `int`. This should make things much easier for bindings. `int` should be at least 32 bits wide; this should be sufficient for all but the most extreme cases.
* **12 June 2016**
* Added `uiGrid`, a new container control that arranges controls in rows and columns, with stretchy ("expanding") rows, stretchy ("expanding") columns, cells that span rows and columns, and cells whose content is aligned in either direction rather than just filling. It's quite powerful, is it? =P
* **8 June 2016**
* Added `uiForm`, a new container control that arranges controls vertically, with properly aligned labels on each. Have fun!
* **6 June 2016**
* Added `uiRadioButtonsSelected()`, `uiRadioButtonsSetSelected()`, and `uiRadioButtonsOnSelected()` to control selection of a radio button and catch an event when such a thing happens.
* **5 June 2016**
* **Alpha 3.1 is here.** This was a much-needed update to Alpha 3 that changes a few things:
* **The build system is now cmake.** cmake 2.8.11 or higher is needed.
* Static linking is now fully possible.
* MinGW linking is back, but static only.
* Added `uiNewPasswordEntry()`, which creates a new `uiEntry` suitable for entering passwords.
* Added `uiNewSearchEntry()`, which creates a new `uiEntry` suitable for searching. On some systems, the `OnChanged()` event will be slightly delayed and/or combined, to produce a more natural feel when searching.
* **29 May 2016**
* **Alpha 3 is here!** Get it [here](https://github.com/andlabs/libui/releases/tag/alpha3).
* The next packaged release will introduce:
* uiGrid, another way to lay out controls, a la GtkGrid
* uiOpenGLArea, a way to render OpenGL content in a libui uiArea
* uiTable, a data grid control that may or may not have tree facilities (if it does, it will be called uiTree instead)
* a complete, possibly rewritten, drawing and text rendering infrastructure
* Thanks to @pcwalton, we can now statically link libui! Simply do `make STATIC=1` instead of just `make`.
* On Windows you must link both `libui.lib` and `libui.res` AND provide a Common Controls 6 manifest for output static binaries to work properly.
* **28 May 2016**
* As promised, **the minimum system requirements are now OS X 10.8 and GTK+ 3.10 for OS X and Unix, respectively**.
* **26 May 2016**
* Two OS X-specific functions have been added: `uiDarwinMarginAmount()` and `uiDarwinPaddingAmount()`. These return the amount of margins and padding, respectively, to give to a control, and are intended for container implementations. These are suitable for the constant of a NSLayoutConstraint. They both take a pointer parameter that is reserved for future use and should be `NULL`.
* **25 May 2016**
* uiDrawTextLayout attributes are now specified in units of *graphemes* on all platforms. This means characters as seen from a user's perspective, not Unicode codepoints or UTF-8 bytes. So a long string of combining marker codepoints after one codepoint would still count as one grapheme.
* **24 May 2016**
* You can now help choose [a potential new build system for libui](https://github.com/andlabs/libui/issues/62).
* Tomorrow I will decide if OS X 10.7 will also be dropped alongside GTK+ 3.4-3.8 this Saturday. Stay tuned.
* As promised, `uiCombobox` is now split into `uiCombobox` for non-editable comboboxes and `uiEditableCombobox` for editable comboboxes. Mind the function changes as well :)
* There is a new function `uiMainStep()`, which runs one iteration of the main loop. It takes a single boolean argument, indicating whether to wait for an event to occur or not. It returns true if an event was processed (or if no event is available if you don't want to wait) and false if the event loop was told to stop (for instance, `uiQuit()` was called).
* **23 May 2016**
* Fixed surrogate pair drawing on OS X.
* **22 May 2016**
* Two more open questions I'd like your feedback on are available [here](https://github.com/andlabs/libui/issues/48) and [here](https://github.com/andlabs/libui/issues/25).
* Sometime in the next 48 hours (before 23:59 EDT on 24 May 2016) I will split `uiCombobox` into two separate controls, `uiCombobox` and `uiEditableCombobox`, each with slightly different events and "selected item" mechanics. Prepare your existing code.
* Removed `uiControlVerifyDestroy()`; that is now part of `uiFreeControl()` itself.
* Added `uiPi`, a constant for π. This is provided for C and C++ programmers, where there is no standard named constant for π; bindings authors shouldn't need to worry about this.
* Fixed uiMultilineEntry not properly having line breaks on Windows.
* Added `uiNewNonWrappingMultilineEntry()`, which creates a uiMultilineEntry that scrolls horizontally instead of wrapping lines. (This is not documented as being changeable after the fact on Windows, hence it's a creation-time choice.)
* uiAreas on Windows and some internal Direct2D areas now respond to `WM_PRINTCLIENT` properly, which should hopefully increase the quality of screenshots.
* uiDateTimePicker on GTK+ works properly on RTL layouts and no longer disappears off the bottom of the screen if not enough room is available. It will also no longer be marked for localization of the time format (what the separator should be and whether to use 24-hour time), as that information is not provided by the locale system. :(
* Added `uiUserBugCannotSetParentOnToplevel()`, which should be used by implementations of toplevel controls in their `SetParent()` implementations. This will also be the beginning of consolidating common user bug messages into a single place, though this will be one of the only few exported user bug functions.
* uiSpinbox and uiSlider now merely swap their min and max if min ≥ max. They will no longer panic and do nothing, respectively.
* Matrix scaling will no longer leave the matrix in an invalid state on OS X and GTK+.
* `uiMultilineEntrySetText()` and `uiMutlilineEntryAppend()` on GTK+ no longer fire `OnChanged()` events.
* **21 May 2016**
* I will now post announcements and updates here.
* Now that Ubuntu 16.04 LTS is here, no earlier than next Saturday, 28 May 2016 at noon EDT, **I will bump the minimum GTK+ version from 3.4 to 3.10**. This will add a lot of new features that I can now add to libui, such as search-oriented uiEntries, lists of arbitrary control layouts, and more. If you are still running a Linux distribution that doesn't come with 3.10, you will either need to upgrade or use jhbuild to set up a newer version of GTK+ in a private environment.
* You can decide if I should also drop OS X 10.7 [here](https://github.com/andlabs/libui/issues/46).

212
README.md
View File

@ -1,81 +1,91 @@
# libui: a portable GUI library for C # libui: a portable GUI library for C
This README is being written.<br> This README is being written.<br>
[![Build Status, Azure Pipelines](https://dev.azure.com/andlabs/libui/_apis/build/status/andlabs.libui?branchName=master)](https://dev.azure.com/andlabs/libui/_build/latest?definitionId=1&branchName=master)<br> [![Build Status](https://travis-ci.org/andlabs/libui.svg)](https://travis-ci.org/andlabs/libui)
[![Build Status, AppVeyor](https://ci.appveyor.com/api/projects/status/ouyk78c52mmisa31/branch/master?svg=true)](https://ci.appveyor.com/project/andlabs/libui/branch/master)
## Status ## Announcements
It has come to my attention that I have not been particularly clear about how usable or feature-complete libui is, and that this has fooled many people into expecting more from libui right this moment than I have explicitly promised to make available. I apologize for not doing this sooner.
libui is currently **mid-alpha** software. Much of what is currently present runs stabily enough for the examples and perhaps some small programs to work, but the stability is still a work-in-progress, much of what is already there is not feature-complete, some of it will be buggy on certain platforms, and there's a lot of stuff missing. In short, here's a list of features that I would like to add to libui, but that aren't in yet:
- trees
- clipboard support, including drag and drop
- more and better dialogs
- printing
- accessibility for uiArea and custom controls
- document-based programs
- tighter OS integration (especially for document-based programs), to allow programs to fully feel native, rather than merely look and act native
- better support for standard dialogs and features (search bars, etc.)
- OpenGL support
In addition, [here](https://github.com/andlabs/libui/issues?utf8=%E2%9C%93&q=master+in%3Atitle+is%3Aissue+is%3Aopen) is a list of issues generalizing existing problems.
Furthermore, libui is not properly fully documented yet. This is mainly due to the fact that the API was initially unstable enough so as to result in rewriting documentation multiple times, in addition to me not being happy with really any existing C code documentation tool. That being said, I have started to pin down my ideal code documentation style in parts of `ui.h`, most notably in the uiAttributedString APIs. Over time, I plan on extending this to the rest of the headers. You can also use [the documentation for libui's Go bindings](https://godoc.org/github.com/andlabs/ui) as a reference, though it is somewhat stale and not optimally written.
But libui is not dead; I am working on it whenever I can, and I hope to get it to a point of real quality soon!
## News
*Note that today's entry (Eastern Time) may be updated later today.*
* **7 April 2019**
* **The build system has been switched to Meson.** See below for instructions. This change was made because the previous build system, CMake, caused countless headaches over trivial issues. Meson was chosen due to how unproblematic setting up libui's build just right was, as well as having design goals that are by coincidence closely aligned with what libui wants.
* Travis CI has been replaced with Azure Pipelines and much of the AppVeyor CI configuration was integrated into the Azure Pipelines configuration. This shouldn't affect most developers.
* **1 September 2018**
* **Alpha 4.1 is here.** This is an emergency fix to Alpha 4 to fix `uiImageAppend()` not working as documented. It now works properly, with one important difference you'll need to care about: **it now requires image data to be alpha-premultiplied**. In addition, `uiImage` also is implemented slightly more nicely now, and `ui.h` has minor documentation typo fixes.
* Alpha 4.1 also tries to make everything properly PIC-enabled.
* **10 August 2018**
* **Alpha 4 is finally here.** Everything from Alpha 3.5 and what's listed below is in this release; the two biggest changes are still the new text drawing API and new uiTable control. In between all that is a whole bunch of bugfixes, and hopefully more stability too. Thanks to everybody who helped contribute!
* Alpha 4 should hopefully also include automated binary releases via CI. Thanks to those who helped set that up!
* **8 August 2018**
* Finally introduced an API for loading images, `uiImage`, and a new control, `uiTable`, for displaying tabular data. These provide enough basic functionality for now, but will be improved over time. You can read the documentation for the new features as they are [here](https://github.com/andlabs/libui/blob/f47e1423cf95ad7b1001663f3381b5a819fc67b9/uitable.h). Thanks to everyone who helped get to this point, in particular @bcampbell for the initial Windows code, and to everyone else for their patience!
* **30 May 2018**
* Merged the previous Announcements and Updates section of this README into a single News section, and merged the respective archive files into a single NEWS.md file.
* **16 May 2018**
* Thanks to @parro-it and @msink, libui now has better CI, including AppVeyor for Windows CI, and automated creation of binary releases when I make a tagged release.
* **13 May 2018**
* Added new functions to work with uiDateTimePickers: `uiDateTimePickerTime()`, `uiDateTimePickerSetTime()`, and `uiDateTimePickerOnChanged()`. These operate on standard `<time.h>` `struct tm`s. Thanks @cody271!
* Release builds on Windows with MSVC should be fixed now; thanks @l0calh05t, @slahn, @mischnic, and @zentner-kyle.
* **12 May 2018**
* GTK+ and OS X now have a cleaner build process for static libraries which no longer has intermediate files and differing configurations. As a result, certain issues should no longer be present. New naming rules for internal symbols of libui have also started being drafted; runtime symbols and edge cases still need to be handled (and the rules applied to Windows) before this can become a regular thing.
* **2 May 2018**
* On Windows, you no longer need to carry around a `libui.res` file with static builds. You do need to link in the appropriate manifest file, such as the one in the `windows/` folder (I still need to figure out exactly what is needed apart from the Common Controls v6 dependency, or at least to create a complete-ish template), or at least include it alongside your executables. This also means you should no longer see random cmake errors when building the static libraries.
* **18 April 2018**
* Introduced a new `uiTimer()` function for running code on a timer on the main thread. (Thanks to @cody271.)
* Migrated all code in the `common/` directory to use `uipriv` prefixes for everything that isn't `static`. This is the first step toward fixing static library oddities within libui, allowing libui to truly be safely used as either a static library or a shared library.
* **18 March 2018**
* Introduced an all-new formatted text API that allows you to process formatted text in ways that the old API wouldn't allow. You can read on the whole API [here](https://github.com/andlabs/libui/blob/8944a3fc5528445b9027b1294b6c86bae03eeb89/ui_attrstr.h). There is also a new examples for it: `drawtext`, which shows the whole API at a glance. It doesn't yet support measuring or manipulating text, nor does it currently support functions that would be necessary for things like text editors; all of this will be added back later.
* libui also now uses my [utf library](https://github.com/andlabs/utf) for UTF-8 and UTF-16 processing, to allow consistent behavior across platforms. This usage is not completely propagated throughout libui, but the Windows port uses it in most places now, and eventually this will become what libui will use throughout.
* Also introduced a formal set of contribution guidelines, see `CONTRIBUTING.md` for details. They are still WIP.
* **17 February 2018** * **17 February 2018**
* The longstanding Enter+Escape crashes on Windows have finally been fixed (thanks to @lxn). * The longstanding Enter+Escape crashes on Windows have finally been fixed (thanks to @lxn).
* **Alpha 3.5 is now here.** This is a quickie release primiarly intended to deploy the above fix to package ui itself. **It is a partial binary release; sorry!** More new things will come in the next release, which will also introduce semver (so it will be called v0.4.0 instead). * **Alpha 3.5 is now here.** This is a quickie release primiarly intended to deploy the above fix to package ui itself. **It is a partial binary release; sorry!** More new things will come in the next release, which will also introduce semver (so it will be called v0.4.0 instead).
* Alpha 3.5 also includes a new control gallery example. The screenshots below have not been updated yet. * Alpha 3.5 also includes a new control gallery example. The screenshots below have not been updated yet.
*Old announcements can be found in the NEWS.md file.* * **27 November 2016**
* Decided to split the table stuff into its own branch. It will be developed independently of everything else, along with a few other features.
* **2 November 2016**
* Added two new functions to replace the deleted `uiWindowPosition()` and friends: `uiAreaBeginUserWindowMove()` and `uiAreaBeginUserWindowResize()`. When used in a `uiAreaHandler.Mouse()` event handler, these let you initiate a user-driven mouse move or mouse resize of the window at any point in a uiArea.
* **31 October 2016**
* @krakjoe noticed that I accidentally used thread-unsafe code in uiQueueMain() on Unix. Fixed.
* **24 October 2016**
* `uiWindowSetContentSize()` on Unix no longer needs to call up the GTK+ main loop. As a result, bugs related to strange behavior using that function (and the now-deleted `uiWindowSetPosition()` and `uiWindowCenter()`) should go away. I'll need to go through the bugs to verify as much, though.
* **22 October 2016**
* Due to being unable to guarantee they will work (especially as we move toward capability-driven window systems like Wayland), or being unable to work without hacking that breaks other things, the following functions have been removed: `uiWindowPosition()`, `uiWindowSetPosition()`, `uiWindowCenter()`, and `uiWindowOnPositionChanged()`. Centering may come back at some point in the future, albeit in a possibly restricted form. A function to initiate a user move when a part of a uiArea is clicked will be provided soon.
* **21 October 2016**
* `uiDrawTextWeightUltraBold` is now spelled correctly. Thanks to @krakjoe.
* **18 June 2016**
* Help decide [the design of tables and trees in libui](https://github.com/andlabs/libui/issues/159); the implementation starts within the next few days, if not tomorrow!
* **17 June 2016**
* **CMake 3.1.0 is now required.** This is due to CMake's rapid development pace in the past few years adding things libui needs to build on as many systems as possible. If your OS is supported by libui but its repositories ship with an older version of CMake, you will need to find an updated one somewhere.
* Please help [plan out a better menu API](https://github.com/andlabs/libui/issues/152).
* **5 June 2016**
* **Alpha 3.1 is here.** This was a much-needed update to Alpha 3 that changes a few things:
* **The build system is now cmake.** cmake 2.8.11 or higher is needed.
* Static linking is now fully possible.
* MinGW linking is back, but static only.
*Old announcements can be found in the ANNOUNCE.md file.*
## Updates
*Note that today's entry (Eastern Time) may be updated later today.*
* **17 June 2016**
* `uiMainSteps()` no longer takes any arguments and no longer needs to invoke a function to do the work. You still need to call it, but once you do, it will return immediately and you can then get right to your main loop.
* **CMake 3.1.0 is now required.** This is due to CMake's rapid development pace in the past few years adding things libui needs to build on as many systems as possible. If your OS is supported by libui but its repositories ship with an older version of CMake, you will need to find an updated one somewhere.
* Added `uiNewVerticalSeparator()` to complement `uiNewHorizontalSeparator()`.
* **16 June 2016**
* Added `uiWindowContentSize()`, `uiWindowSetContentSize()`, and `uiWindowOnContentSizeChanged()` methods for manipulating uiWindow content sizes. Note the use of "content size"; the size you work with does NOT include window decorations (titlebars, menus, etc.).
* Added `uiWindowFullscreen()` and `uiWindowSetFullscreen()` to allow making fullscreen uiWindows, taking advantage of OS facilities for fullscreen and without changing the screen resolution (!).
* Added `uiWindowBorderless()` and `uiWindowSetBorderless()` for allowing borderless uiWindows.
* Added `uiMainSteps()`. You call this instead of `uiMain()` if you want to run the main loop yourself. You pass in a function that will be called; within that function, you call `uiMainStep()` repeatedly until it returns 0, doing whatever you need to do in the meantime. (This was needed because just having `uiMainStep()` by itself only worked on some systems.)
* Added `uiProgressBarValue()` and allowed passing -1 to `uiProgressBarSetValue()` to make an indeterminate progress bar. Thanks to @emersion.
* **15 June 2016**
* Added `uiFormDelete()`; thanks to @emersion.
* Added `uiWindowPosition()`, `uiWindowSetPosition()`, `uiWindowCenter()`, and `uiWindowOnPositionChanged()`, methods for manipulating uiWindow position.
* **14 June 2016**
* uiDarwinControl now has a `ChildVisibilityChanged()` method and a corresponding `NotifyVisibilityChanged()` function that is called by the default show/hide handlers. This is used to make visibility changes work on OS X; uiBox, uiForm, and uiGrid all respect these now.
* The same has been done on the Windows side as well.
* Hiding and showing controls and padding calculations are now correct on Windows at long last.
* Hiding a control in a uiForm now hides its label on all platforms.
* **13 June 2016**
* `intmax_t` and `uintmax_t` are no longer used for libui API functions; now we use `int`. This should make things much easier for bindings. `int` should be at least 32 bits wide; this should be sufficient for all but the most extreme cases.
* **12 June 2016**
* Added `uiGrid`, a new container control that arranges controls in rows and columns, with stretchy ("expanding") rows, stretchy ("expanding") columns, cells that span rows and columns, and cells whose content is aligned in either direction rather than just filling. It's quite powerful, is it? =P
* **8 June 2016**
* Added `uiForm`, a new container control that arranges controls vertically, with properly aligned labels on each. Have fun!
* **6 June 2016**
* Added `uiRadioButtonsSelected()`, `uiRadioButtonsSetSelected()`, and `uiRadioButtonsOnSelected()` to control selection of a radio button and catch an event when such a thing happens.
* **5 June 2016**
* Added `uiNewPasswordEntry()`, which creates a new `uiEntry` suitable for entering passwords.
* Added `uiNewSearchEntry()`, which creates a new `uiEntry` suitable for searching. On some systems, the `OnChanged()` event will be slightly delayed and/or combined, to produce a more natural feel when searching.
*Old updates can be found in the Changelog.md file.*
## Runtime Requirements ## Runtime Requirements
@ -86,8 +96,7 @@ But libui is not dead; I am working on it whenever I can, and I hope to get it t
## Build Requirements ## Build Requirements
* All platforms: * All platforms:
* [Meson](https://mesonbuild.com/) 0.48.0 or newer * CMake 3.1.0 or newer
* Any of Meson's backends; this section assumes you are using [Ninja](https://ninja-build.org/), but there is no reason the other backends shouldn't work.
* Windows: either * Windows: either
* Microsoft Visual Studio 2013 or newer (2013 is needed for `va_copy()`) — you can build either a static or a shared library * Microsoft Visual Studio 2013 or newer (2013 is needed for `va_copy()`) — you can build either a static or a shared library
* MinGW-w64 (other flavors of MinGW may not work) — **you can only build a static library**; shared library support will be re-added once the following features come in: * MinGW-w64 (other flavors of MinGW may not work) — **you can only build a static library**; shared library support will be re-added once the following features come in:
@ -97,41 +106,33 @@ But libui is not dead; I am working on it whenever I can, and I hope to get it t
## Building ## Building
libui uses only [the standard Meson build options](https://mesonbuild.com/Builtin-options.html), so a libui build can be set up just like any other: Out-of-tree builds typical of cmake are preferred:
``` ```
$ # you must be in the top-level libui directory, otherwise this won't work $ # you must be in the top-level libui directory, otherwise this won't work
$ meson setup build [options] $ mkdir build
$ ninja -C build $ cd build
$ cmake ..
``` ```
Once this completes, everything will be under `build/meson-out/`. (Note that unlike the previous build processes, everything is built by default, including tests and examples.) Pass `-DBUILD_SHARED_LIBS=OFF` to `cmake` to build a static library. The standard cmake build configurations are provided; if none is specified, `Debug` is used.
The most important options are: If you use a makefile generator with cmake, then
* `--buildtype=(debug|release|...)` controls the type of build made; the default is `debug`. For a full list of valid values, consult [the Meson documentation](https://mesonbuild.com/Running-Meson.html). ```
* `--default-library=(shared|static)` controls whether libui is built as a shared library or a static library; the default is `shared`. You currently cannot specify `both`, as the build process changes depending on the target type (though I am willing to look into changing things if at all possible). $ make
* `-Db_sanitize=which` allows enabling the chosen [sanitizer](https://github.com/google/sanitizers) on a system that supports sanitizers. The list of supported values is in [the Meson documentation](https://mesonbuild.com/Builtin-options.html#base-options). $ make tester # for the test program
* `--backend=backend` allows using the specified `backend` for builds instead of `ninja` (the default). A list of supported values is in [the Meson documentation](https://mesonbuild.com/Builtin-options.html#universal-options). $ make examples # for examples
```
Most other built-in options will work, though keep in mind there are a handful of options that cannot be overridden because libui depends on them holding a specific value; if you do override these, though, libui will warn you when you run `meson`. and pass `VERBOSE=1` to see build commands. Build targets will be in the `build/out` folder.
The Meson website and documentation has more in-depth usage instructions. Project file generators should work, but are untested by me.
For the sake of completeness, I should note that the default value of `--layout` is `flat`, not the usual `mirror`. This is done both to make creating the release archives easier as well as to reduce the chance that shared library builds will fail to start on Windows because the DLL is in another directory. You can always specify this manually if you want. On Windows, I use the `Unix Makefiles` generator and GNU make (built using the `build_w32.bat` script included in the source and run in the Visual Studio command line). In this state, if MinGW-w64 (either 32-bit or 64-bit) is not in your `%PATH%`, cmake will use MSVC by default; otherwise, cmake will use with whatever MinGW-w64 is in your path. `set PATH=%PATH%;c:\msys2\mingw(32/64)\bin` should be enough to temporarily change to a MinGW-w64 build for the current command line session only if you installed MinGW-w64 through [MSYS2](https://msys2.github.io/); no need to change global environment variables constantly.
Backends other than `ninja` should work, but are untested by me.
## Installation ## Installation
Meson also supports installing from source; if you use Ninja, just do
```
$ ninja -C build install
```
When running `meson`, the `--prefix` option will set the installation prefix. [The Meson documentation](https://mesonbuild.com/Builtin-options.html#universal-options) has more information, and even lists more fine-grained options that you can use to control the installation.
#### Arch Linux #### Arch Linux
Can be built from AUR: https://aur.archlinux.org/packages/libui-git/ Can be built from AUR: https://aur.archlinux.org/packages/libui-git/
@ -148,27 +149,22 @@ Other people have made bindings to other languages:
Language | Bindings Language | Bindings
--- | --- --- | ---
C++ | [libui-cpp](https://github.com/billyquith/libui-cpp), [cpp-libui-qtlike](https://github.com/aoloe/cpp-libui-qtlike) C#/.net | [LibUI.Binding](https://github.com/NattyNarwhal/LibUI.Binding), [SharpUI](https://github.com/benpye/sharpui/)
C# / .NET Framework | [LibUI.Binding](https://github.com/NattyNarwhal/LibUI.Binding)
C# / .NET Core | [DevZH.UI](https://github.com/noliar/DevZH.UI), [SharpUI](https://github.com/benpye/sharpui/), [TCD.UI](https://github.com/tacdevel/tcdfx)
CHICKEN Scheme | [wasamasa/libui](https://github.com/wasamasa/libui) CHICKEN Scheme | [wasamasa/libui](https://github.com/wasamasa/libui)
Common Lisp | [jinwoo/cl-ui](https://github.com/jinwoo/cl-ui) Crystal | [libui.cr](https://github.com/Fusion/libui.cr)
Crystal | [libui.cr](https://github.com/Fusion/libui.cr), [hedron](https://github.com/Qwerp-Derp/hedron)
D | [DerelictLibui (flat API)](https://github.com/Extrawurst/DerelictLibui), [libuid (object-oriented)](https://github.com/mogud/libuid) D | [DerelictLibui (flat API)](https://github.com/Extrawurst/DerelictLibui), [libuid (object-oriented)](https://github.com/mogud/libuid)
Euphoria | [libui-euphoria](https://github.com/ghaberek/libui-euphoria) Euphoria | [libui-euphoria](https://github.com/ghaberek/libui-euphoria)
Harbour | [hbui](https://github.com/rjopek/hbui) Harbour | [HBUI](https://github.com/RJopek/HBUI)
Haskell | [haskell-libui](https://github.com/beijaflor-io/haskell-libui) Haskell | [libui-haskell](https://github.com/ajnsit/libui-haskell), [beijaflor-io/haskell-libui (complete FFI bindings, extensions and higher-level API)](https://github.com/beijaflor-io/haskell-libui)
JavaScript/Node.js | [libui-node](https://github.com/parro-it/libui-node), [libui.js (merged into libui-node?)](https://github.com/mavenave/libui.js), [proton-native](https://github.com/kusti8/proton-native), [vuido](https://github.com/mimecorg/vuido) JavaScript | [libui.js (merged into libui-node?)](https://github.com/mavenave/libui.js)
Julia | [Libui.jl](https://github.com/joa-quim/Libui.jl) Julia | [Libui.jl](https://github.com/joa-quim/Libui.jl)
Kotlin | [kotlin-libui](https://github.com/msink/kotlin-libui) Lua | [libuilua](https://github.com/zevv/libuilua), [libui-lua](https://github.com/mdombroski/libui-lua)
Lua | [libuilua](https://github.com/zevv/libuilua), [libui-lua](https://github.com/mdombroski/libui-lua), [lui](http://tset.de/lui/index.html), [lui](https://github.com/zhaozg/lui)
Nim | [ui](https://github.com/nim-lang/ui) Nim | [ui](https://github.com/nim-lang/ui)
Perl6 | [perl6-libui](https://github.com/Garland-g/perl6-libui) Node.js | [libui-node](https://github.com/parro-it/libui-node)
PHP | [ui](https://github.com/krakjoe/ui) PHP | [ui](https://github.com/krakjoe/ui)
Python | [pylibui](https://github.com/joaoventura/pylibui) Python | [pylibui](https://github.com/joaoventura/pylibui)
Ruby | [libui-ruby](https://github.com/jamescook/libui-ruby), [libui](https://github.com/kojix2/libui) Ruby | [libui-ruby](https://github.com/jamescook/libui-ruby)
Rust | [libui-rs](https://github.com/rust-native-ui/libui-rs) Rust | [libui-rs](https://github.com/pcwalton/libui-rs)
Scala | [scalaui](https://github.com/lolgab/scalaui)
Swift | [libui-swift](https://github.com/sclukey/libui-swift) Swift | [libui-swift](https://github.com/sclukey/libui-swift)
## Frequently Asked Questions ## Frequently Asked Questions
@ -180,10 +176,6 @@ When you run a binary directly from the Terminal, however, you are running it di
See also [this](https://github.com/andlabs/libui/pull/20#issuecomment-211381971) and [this](http://stackoverflow.com/questions/25318524/what-exactly-should-i-pass-to-nsapp-activateignoringotherapps-to-get-my-appl). See also [this](https://github.com/andlabs/libui/pull/20#issuecomment-211381971) and [this](http://stackoverflow.com/questions/25318524/what-exactly-should-i-pass-to-nsapp-activateignoringotherapps-to-get-my-appl).
## Contributing
See `CONTRIBUTING.md`.
## Screenshots ## Screenshots
From examples/controlgallery: From examples/controlgallery:

133
TODO.md
View File

@ -127,136 +127,3 @@ label shortcut keys
[02:15:00] <vrishab> 1.40.3 [02:15:00] <vrishab> 1.40.3
[02:20:46] <andlabs> I'll ahve to keep this in mind then, thanks [02:20:46] <andlabs> I'll ahve to keep this in mind then, thanks
[02:20:59] <andlabs> if only there was a cairo-specific attribute for alpha... [02:20:59] <andlabs> if only there was a cairo-specific attribute for alpha...
FONT LOADING
[00:10:08] <hergertme> andlabs: is there API yet to load from memory? last i checked i only found from file (which we use in builder). https://git.gnome.org/browse/gnome-builder/tree/libide/editor/ide-editor-map-bin.c#n115
[00:13:12] mrmcq2u_ (mrmcq2u@109.79.53.90) joined the channel
[00:14:59] mrmcq2u (mrmcq2u@109.79.73.102) left IRC (Ping timeout: 181 seconds)
[00:15:19] <andlabs> hergertme: no, which is why I was asking =P
[00:15:30] <andlabs> I would have dug down if I could ensure at least something about the backends a GTK+ 3 program uses
[00:15:39] <andlabs> on all platforms except windows and os x
[00:16:11] <hergertme> to the best of my (partially outdated, given pace of foss) knowledge there isn't an api to load from memory
[00:16:28] <hergertme> you can possibly make a tmpdir and put a temp file in there
[00:16:52] <hergertme> and load that as your font dir in your FcConfig, so any PangoFontDescription would point to that one font, no matter what
[00:17:18] <hergertme> (using the API layed out in that link)
[00:18:18] dsr1014__ (dsr1014@c-73-72-102-18.hsd1.il.comcast.net) joined the channel
[00:35:18] simukis_ (simukis@78-60-58-6.static.zebra.lt) left IRC (Quit: simukis_)
[00:35:48] dreamon_ (dreamon@ppp-188-174-49-41.dynamic.mnet-online.de) joined the channel
[00:40:09] samtoday_ (samtoday@114-198-116-132.dyn.iinet.net.au) joined the channel
[00:40:32] mjog (mjog@120.18.225.46) joined the channel
[00:40:38] <andlabs> hergertme: not necessarily fontconfig
[00:40:45] <andlabs> it can be with ft2 or xft I guess
[00:40:55] <andlabs> especially since I want the API NOT to make the font part of the font panel
[00:42:07] <hergertme> what sort of deprecated code are you trying to support?
[00:42:35] <hergertme> both of those are deprecated in pango fwiw
[00:43:06] <hergertme> on Linux im pretty sure we use FC everywhere these days
[00:44:46] <hergertme> (and gtk_widget_set_font_map() is how you get your custom font into a widget without affecting the global font lists, as layed out in that link)
[00:49:14] vasaikar (vasaikar@125.16.97.121) joined the channel
[00:50:14] karlt (karl@2400:e780:801:224:f121:e611:d139:e70e) left IRC (Client exited)
[00:50:49] karlt (karl@2400:e780:801:224:f121:e611:d139:e70e) joined the channel
[00:51:43] PioneerAxon (PioneerAxo@122.171.61.146) left IRC (Ping timeout: 180 seconds)
[00:57:47] PioneerAxon (PioneerAxo@106.201.37.181) joined the channel
[01:03:01] karlt (karl@2400:e780:801:224:f121:e611:d139:e70e) left IRC (Ping timeout: 181 seconds)
[01:05:49] muhannad (muhannad@95.218.26.152) left IRC (Quit: muhannad)
[01:07:51] <andlabs> hergertme: hm
[01:07:54] <andlabs> all right, thanks
[01:08:05] <andlabs> hergertme: fwiw right now my requirement is 3.10
[01:10:47] <hergertme> ah, well you'll probably be missing the neccesary font API on gtk_widget
[01:11:04] <hergertme> but pango should be fine even back as far as https://developer.gnome.org/pango/1.28/PangoFcFontMap.html
[01:11:56] <andlabs> good
[01:12:04] <andlabs> because this is for custom drawing into a DrawingArea
[01:14:12] <hergertme> presumably just create your PangoContext as normal, but call pango_context_set_font_map() with the map you've setup. now, the load a font from a file i dont think was added to FontConfig until later though (not sure what release)
[01:15:53] <hergertme> FcConfigAppFontAddFile() <-- that API
[01:16:30] <hergertme> great, and they don't say what version the API was added in teh docs
function: ide_editor_map_bin_add()
- Mouse ClickLock: do we need to do anything special? *should* we? https://msdn.microsoft.com/en-us/library/windows/desktop/ms724947(v=vs.85).aspx
- consider a uiAnticipateDoubleClick() or uiDoubleClickTime() (for a uiQueueTimer()) or something: https://blogs.msdn.microsoft.com/oldnewthing/20041015-00/?p=37553
- determine whether MSGF_USER is for and if it's correct for our uiArea message filter (if we have one)
- source file encoding and MSVC compiler itself? https://stackoverflow.com/questions/20518040/how-can-i-get-the-directwrite-padwrite-sample-to-work
- also need to worry about object file and output encoding...
- this also names the author of the padwrite sample
- OpenType features TODOs
- https://stackoverflow.com/questions/32545675/what-are-the-default-typography-settings-used-by-idwritetextlayout
- feature/shaping interaction rules for arabic: https://www.microsoft.com/typography/OpenTypeDev/arabic/intro.htm
- other stuff, mostly about UIs and what users expect to be able to set
- https://klim.co.nz/blog/towards-an-ideal-opentype-user-interface/
- https://libregraphicsmeeting.org/2016/designing-for-many-applications-opentype-features-ui/
- https://www.youtube.com/watch?v=wEyDhsH076Y
- https://twitter.com/peter_works
- http://ilovetypography.com/2014/10/22/better-ui-for-better-typography-adobe-petition/
- http://silgraphite.sourceforge.net/ui/studynote.html
- add NXCOMPAT (DEP awareness) to the Windows builds
- and ASLR too? or is that not a linker setting
OS X: embedding an Info.plist into a binary directly
https://www.objc.io/issues/6-build-tools/mach-o-executables/
TODO will this let Dictation work?
TODO investigate ad-hoc codesigning
https://blogs.msdn.microsoft.com/oldnewthing/20040112-00/?p=41083 def files for decoration (I forget if I said this earlier)
TODO ClipCursor() stuff; probably not useful for libui but still
https://blogs.msdn.microsoft.com/oldnewthing/20140102-00/?p=2183
https://blogs.msdn.microsoft.com/oldnewthing/20061117-03/?p=28973
https://msdn.microsoft.com/en-us/library/windows/desktop/ms648383(v=vs.85).aspx
https://cmake.org/Wiki/CMake_Useful_Variables
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined")
On Unix systems, this will make linker report any unresolved symbols from object files (which is quite typical when you compile many targets in CMake projects, but do not bother with linking target dependencies in proper order).
(I used to have something like this back when I used makefiles; did it convert in? I forget)
look into these for the os x port
https://developer.apple.com/documentation/appkit/view_management/nseditor?language=objc
https://developer.apple.com/documentation/appkit/view_management/nseditorregistration?language=objc
for future versions of the os x port
https://developer.apple.com/documentation/appkit/nslayoutguide?language=objc and anchors
https://developer.apple.com/documentation/appkit/nsuserinterfacecompression?language=objc https://developer.apple.com/documentation/appkit/nsuserinterfacecompressionoptions?language=objc
though at some point we'll be able to use NSStackView and NSGridView directly, so...
Cocoa PDFs
https://developer.apple.com/documentation/appkit/nspdfimagerep?language=objc
https://developer.apple.com/documentation/coregraphics?language=objc
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Printing/osxp_pagination/osxp_pagination.html#//apple_ref/doc/uid/20001051-119037
https://developer.apple.com/documentation/appkit/nsprintoperation/1529269-pdfoperationwithview?language=objc
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Printing/osxp_printapps/osxp_printapps.html#//apple_ref/doc/uid/20000861-BAJBFGED
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Printing/osxp_printingapi/osxp_printingapi.html#//apple_ref/doc/uid/10000083i-CH2-SW2
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Printing/osxp_printinfo/osxp_printinfo.html#//apple_ref/doc/uid/20000864-BAJBFGED
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Printing/osxp_printlayoutpanel/osxp_printlayoutpanel.html#//apple_ref/doc/uid/20000863-BAJBFGED
https://developer.apple.com/documentation/appkit/nspagelayout?language=objc
https://developer.apple.com/documentation/appkit/nsprintinfo?language=objc
https://developer.apple.com/documentation/applicationservices/core_printing?language=objc
https://developer.apple.com/documentation/applicationservices/1463247-pmcreatesession?language=objc
https://developer.apple.com/documentation/applicationservices/pmprintsession?language=objc
https://developer.apple.com/documentation/applicationservices/1460101-pmsessionbegincgdocumentnodialog?language=objc
https://developer.apple.com/documentation/applicationservices/1463416-pmsessionbeginpagenodialog?language=objc
https://developer.apple.com/documentation/applicationservices/1506831-anonymous/kpmdestinationprocesspdf?language=objc
https://developer.apple.com/documentation/applicationservices/1461960-pmcreategenericprinter?language=objc
https://developer.apple.com/documentation/applicationservices/1460101-pmsessionbegincgdocumentnodialog?language=objc
https://developer.apple.com/documentation/applicationservices/1464527-pmsessionenddocumentnodialog?language=objc
https://developer.apple.com/documentation/applicationservices/1461952-pmsessiongetcggraphicscontext?language=objc
https://developer.apple.com/library/content/technotes/tn2248/_index.html
https://developer.apple.com/library/content/samplecode/PMPrinterPrintWithFile/Introduction/Intro.html
https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Printing/osxp_aboutprinting/osxp_aboutprt.html
- run os x code with `OBJC_DEBUG_MISSING_POOLS=YES` and other `OBJC_HELP=YES` options
- turn off the autorelease pool to make sure we're not autoreleasing improperly
TODO investigate -Weverything in clang alongside -Wall in MSVC (and in gcc too maybe...)
mac os x accessibility
- https://developer.apple.com/documentation/appkit/nsworkspace/1524656-accessibilitydisplayshoulddiffer?language=objc
- https://developer.apple.com/documentation/appkit/nsworkspace/1526290-accessibilitydisplayshouldincrea?language=objc
- https://developer.apple.com/documentation/appkit/nsworkspace/1533006-accessibilitydisplayshouldreduce?language=objc
uiEntry disabling bugs http://www.cocoabuilder.com/archive/cocoa/215525-nstextfield-bug-can-be.html
uiMultilineEntry disabling https://developer.apple.com/library/content/qa/qa1461/_index.html
more TODOs:
- make no guarantee about buildability of feature branches

View File

@ -1,750 +0,0 @@
// 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
}

View File

@ -1,149 +0,0 @@
-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"

View File

@ -1,146 +0,0 @@
-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"

View File

@ -1,149 +0,0 @@
-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"

View File

@ -1,149 +0,0 @@
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"

View File

@ -1,247 +0,0 @@
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
}

View File

@ -1,160 +0,0 @@
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)
}

View File

@ -1,77 +0,0 @@
-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

View File

@ -1,73 +0,0 @@
-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"

View File

@ -1,112 +0,0 @@
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)

View File

@ -1,73 +0,0 @@
// 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)")

View File

@ -1,58 +0,0 @@
// 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))
}
}

View File

@ -1,8 +0,0 @@
# 21 october 2017
gawk '
BEGIN { FS = "\t+" }
!/float..as/ { next }
{ i = 0; if ($1 == "") i++ }
(NF-i) != 2 { next }
{ print }
' "$@"

View File

@ -1,10 +0,0 @@
# 21 october 2017
gawk '
{
gsub(/float..as\(/, "")
gsub(/,/, "", $(NF - 1))
gsub(/\)$/, "")
split($0, parts, /:/)
print $(NF - 1) "\t" $NF "\t" parts[1]
}
' "$@"

View File

@ -1,3 +0,0 @@
# 21 october 2017
sort -t$'\t' -k1,1 -k2,2 "$@" |
column -t -s$'\t'

View File

@ -1,3 +0,0 @@
// 22 october 2017
extern int realMain(void);
int main(void) { return realMain(); }

View File

@ -1,11 +0,0 @@
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)

View File

@ -1,11 +0,0 @@
-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

View File

@ -1,51 +0,0 @@
# 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

View File

@ -1,136 +0,0 @@
// 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;
}

View File

@ -1,907 +0,0 @@
// 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;
}

View File

@ -1,676 +0,0 @@
// 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;
}

View File

@ -1,41 +0,0 @@
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

View File

@ -1,190 +0,0 @@
_UI_ENUM(uiAttribute) {
uiAttributeFamily,
uiAttributeSize, // use Double
uiAttributeWeight,
uiAttributeItalic,
uiAttributeStretch,
uiAttributeColor, // use R, G, B, A
uiAttributeBackground, // use R, G, B, A
// TODO kerning amount
// OS X: kCTKernAttributeName
// > 0: farther (TODO from advance or standard kerning?)
// == 0: no kerning
// < 0: closer (TODO same)
// undefined: standard kerning
// Pango: pango_attr_letter_spacing_new()
// parameter meaning unspecified
// Windows: requires Platform Update, SetLetterSpacing()
// parameter meaning unspecified
uiAttributeUnderline, // enum uiDrawUnderlineStyle
// TODO what is the color in the case we don't specify it, black or the text color?
uiAttributeUnderlineColor, // enum uiDrawUnderlineColor
// TODO kCTSuperscriptAttributeName vs below
// all it does is set the below attribute so
// TODO kCTBaselineClassAttributeName, kCTBaselineInfoAttributeName, kCTBaselineReferenceInfoAttributeName
// TODO strikethroughs? (pango yes, directwrite yes, os x no)
// TODO baseline offsets? (pango yes)
// TODO size scales? (pango yes)
// TODO fallbacks (pango: enable or disable)
// TODO document that this will also enable language-specific font features (TODO on DirectWrite too?)
// TODO document that this should be strict BCP 47 form (A-Z, a-z, 0-9, and -) for maximum compatibility
uiAttributeLanguage, // BCP 47 string
// These attributes represent typographic features. Each feature
// is a separate attribute, to make composition easier. The
// availability of for each attribute are defined by the font; the
// default values are defined by the font and/or by the OS.
//
// A note about features whose parameter is an enumeration:
// OS X defines typographic features using the AAT specification
// and converts to OpenType internally when needed, whereas
// other platforms use OpenType directly. OpenType is less
// precise about what each enumeration value means than AAT
// is, so enumeration values do not necessarily represent what
// OS X expects with all fonts. In cases where they do, libui
// provides an enumeration type to use. Otherwise, the AAT
// enumeration values are provided in comments for
// documentation purposes.
// TODO kAllTypographicFeaturesType
// AAT calls these "common ligatures"
uiAttributeStandardLigatures, // 0 = off, 1 = on
uiAttributeRequiredLigatures, // 0 = off, 1 = on
// AAT calls these "rare ligatures"
uiAttributeDiscretionaryLigatures, // 0 = off, 1 = on
uiAttributeContextualLigatures, // 0 = off, 1 = on
uiAttributeHistoricalLigatures, // 0 = off, 1 = on
// TODO uiAttributeCursiveConnection, // 0 = none, 1 = some, 2 = all
uiAttributeUnicase, // 0 = off, 1 = on
// TODO uiAttributeLinguisticRearrangement, // 0 = off, 1 = on
// TODO rename this
uiAttributeNumberSpacings, // enum uiAttributeNumberSpacing
// TODO kSmartSwashType, falt and jalt
// TODO kDiacriticsType
uiAttributeSuperscripts, // enum uiAttributeSuperscript
uiAttributeFractionForms, // enum uiAttributeFractionForm
uiAttributeSlashedZero, // 0 = off, 1 = on
uiAttributeMathematicalGreek, // 0 = off, 1 = on
// AAT defines the following values:
// 0 = none
// 1 = dingbats
// 2 = pi characters
// 3 = fleurons
// 4 = decorative borders
// 5 = international symbols
// 6 = mathematical symbols
// OpenType says alphanumeric characters must(? TODO) have one form each and the bullet character U+2022 (•) can have many
uiAttributeOrnamentalForms, // an integer from 0 to a font-specified upper bound
// TODO provide a function to get the upper bound?
// AAT calls this "character alternatives" and defines the
// following values:
// 0 = none
// OpenType calls this "access all alternates".
// TODO doesn't OpenType do the same about 0?
uiAttributeSpecificCharacterForm, // an integer from 0 to a font-specified upper bound
// TODO provide a function to get the upper bound?
uiAttributeTitlingCapitalForms, // 0 = off, 1 = on
// AAT calls these "character shapes"
uiAttributeHanCharacterForms, // enum uiAttributeHanCharacterForm
// OpenType calls these "old-style"
uiAttributeLowercaseNumbers, // 0 = off, 1 = on
// TODO kTextSpacingType
// see kKanaSpacingType below
uiAttributeHanjaToHangul, // 0 = off, 1 = on
// AAT defines the following values:
// 0 = none
// 1 = box
// 2 = rounded box
// 3 = circle
// 4 = inverted circle
// 5 = parentheses
// 6 = period
// 7 = roman numeral
// 8 = diamond
// 9 = inverted box
// 10 = inverted rounded box
// TODO rename to AnnotatedForms?
uiAttributeAnnotatedGlyphForms, // an integer from 0 to a font-specified upper bound
// TODO provide a function to get the upper bound?
// TODO kKanaSpacingType
// TODO kIdeographicSpacingType
// can they be provided independently of kTextSpacingType? Core Text doesn't seem to
// TODO kUnicodeDecompositionType
uiAttributeRubyKanaForms, // 0 = off, 1 = on
// TODO kCJKVerticalRomanPlacementType
// this is 'valt' in OpenType but I don't know if I want to make it selectable or not
uiAttributeCJKRomansToItalics, // 0 = off, 1 = on
// AAT calls this "case-sensitive layout"
uiAttributeCaseSensitiveForms, // 0 = off, 1 = on
// AAT: this is called "case-sensitive spacing"
uiAttributeCapitalSpacing, // 0 = off, 1 = on
uiAttributeAlternateHorizontalKana, // 0 = off, 1 = on
uiAttributeAlternateVerticalKana, // 0 = off, 1 = on
// TODO "Alternate"? unify all this
// TODO document that these are guaranteed to be consecutive
uiAttributeStylisticAlternate1, // 0 = off, 1 = on
uiAttributeStylisticAlternate2, // 0 = off, 1 = on
uiAttributeStylisticAlternate3, // 0 = off, 1 = on
uiAttributeStylisticAlternate4, // 0 = off, 1 = on
uiAttributeStylisticAlternate5, // 0 = off, 1 = on
uiAttributeStylisticAlternate6, // 0 = off, 1 = on
uiAttributeStylisticAlternate7, // 0 = off, 1 = on
uiAttributeStylisticAlternate8, // 0 = off, 1 = on
uiAttributeStylisticAlternate9, // 0 = off, 1 = on
uiAttributeStylisticAlternate10, // 0 = off, 1 = on
uiAttributeStylisticAlternate11, // 0 = off, 1 = on
uiAttributeStylisticAlternate12, // 0 = off, 1 = on
uiAttributeStylisticAlternate13, // 0 = off, 1 = on
uiAttributeStylisticAlternate14, // 0 = off, 1 = on
uiAttributeStylisticAlternate15, // 0 = off, 1 = on
uiAttributeStylisticAlternate16, // 0 = off, 1 = on
uiAttributeStylisticAlternate17, // 0 = off, 1 = on
uiAttributeStylisticAlternate18, // 0 = off, 1 = on
uiAttributeStylisticAlternate19, // 0 = off, 1 = on
uiAttributeStylisticAlternate20, // 0 = off, 1 = on
uiAttributeContextualAlternates, // 0 = off, 1 = on
uiAttributeSwashes, // 0 = off, 1 = on
uiAttributeContextualSwashes, // 0 = off, 1 = on
uiAttributeLowercaseCapForms, // enum uiAttributeCapForm
uiAttributeUppercaseCapForms, // enum uiAttributeCapForm
// TODO kCJKRomanSpacingType
// TODO uiAttributeSystem, (this might not be doable with DirectWrite)
// TODO uiAttributeCustom,
};

View File

@ -1 +0,0 @@
Removed because proper support on OS X doesn't come until 10.9 unless we use a font with an ltag table; none of the fonts I have come with ltag tables (none of the fonts on OS X do, or at least don't come with a sr entry in their ltag table, and OpenType has replaced ltag with what appears to be custom sub-tables of the GPOS and GSUB tables.)

View File

@ -1,19 +0,0 @@
struct fontParams {
uiDrawFontDescriptor desc;
uint16_t featureTypes[maxFeatures];
uint16_t featureSelectors[maxFeatures];
size_t nFeatures;
const char *language;
};
// locale identifiers are specified as BCP 47: https://developer.apple.com/reference/corefoundation/cflocale?language=objc
case uiAttributeLanguage:
// LONGTERM FUTURE when we move to 10.9, switch to using kCTLanguageAttributeName
ensureFontInRange(p, start, end);
adjustFontInRange(p, start, end, ^(struct fontParams *fp) {
fp->language = (const char *) (spec->Value);
});
break;
desc = fontdescAppendFeatures(desc, fp->featureTypes, fp->featureSelectors, fp->nFeatures, fp->language);

View File

@ -1,9 +0,0 @@
PangoLanguage *lang;
// language strings are specified as BCP 47: https://developer.gnome.org/pango/1.30/pango-Scripts-and-Languages.html#pango-language-from-string https://www.ietf.org/rfc/rfc3066.txt
case uiAttributeLanguage:
lang = pango_language_from_string((const char *) (spec->Value));
addattr(p, start, end,
pango_attr_language_new(lang));
// lang *cannot* be freed
break;

View File

@ -1,10 +0,0 @@
WCHAR *localeName;
// locale names are specified as BCP 47: https://msdn.microsoft.com/en-us/library/windows/desktop/dd373814(v=vs.85).aspx https://www.ietf.org/rfc/rfc4646.txt
case uiAttributeLanguage:
localeName = toUTF16((char *) (spec->Value));
hr = p->layout->SetLocaleName(localeName, range);
if (hr != S_OK)
logHRESULT(L"error applying locale name attribute", hr);
uiFree(localeName);
break;

View File

@ -1,2 +0,0 @@
case uiAttributeLanguage:
return asciiStringsEqualCaseFold((char *) (attr->spec.Value), (char *) (spec->Value));

View File

@ -1,27 +0,0 @@
before "or any combination of the above"
// thanks to https://twitter.com/codeman38/status/831924064012886017
next = "\xD0\xB1\xD0\xB3\xD0\xB4\xD0\xBF\xD1\x82";
uiAttributedStringAppendUnattributed(attrstr, "multiple languages (compare ");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeItalic;
spec.Value = uiDrawTextItalicItalic;
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
spec.Type = uiAttributeLanguage;
spec.Value = (uintptr_t) "ru";
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, " to ");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeItalic;
spec.Value = uiDrawTextItalicItalic;
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
spec.Type = uiAttributeLanguage;
spec.Value = (uintptr_t) "sr";
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, " \xE2\x80\x94 may require changing the font)");
uiAttributedStringAppendUnattributed(attrstr, ", ");

View File

@ -1,112 +0,0 @@
// note: this doesn't work for languages; we have to parse the ltag table
// fortunately features that aren't supported are simply ignored, so we can copy them all in
// LONGTERM FUTURE when we switch to 10.9, the language parameter won't be needed anymore
// LONGTERM FUTURE and on 10.10 we can use OpenType tags directly!
CTFontDescriptorRef fontdescAppendFeatures(CTFontDescriptorRef desc, const uint16_t *types, const uint16_t *selectors, size_t n, const char *language)
{
CTFontDescriptorRef new;
CFMutableArrayRef outerArray;
CFDictionaryRef innerDict;
CFNumberRef numType, numSelector;
const void *keys[2], *values[2];
size_t i;
CFArrayRef languages;
CFIndex il, nl;
CFStringRef curlang;
char d[2];
outerArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
if (outerArray == NULL) {
// TODO
}
keys[0] = kCTFontFeatureTypeIdentifierKey;
keys[1] = kCTFontFeatureSelectorIdentifierKey;
for (i = 0; i < n; i++) {
numType = CFNumberCreate(NULL, kCFNumberSInt16Type,
(const SInt16 *) (types + i));
numSelector = CFNumberCreate(NULL, kCFNumberSInt16Type,
(const SInt16 *) (selectors + i));
values[0] = numType;
values[1] = numSelector;
innerDict = CFDictionaryCreate(NULL,
keys, values, 2,
// TODO are these correct?
&kCFCopyStringDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (innerDict == NULL) {
// TODO
}
CFArrayAppendValue(outerArray, innerDict);
CFRelease(innerDict);
CFRelease(numSelector);
CFRelease(numType);
}
// now we have to take care of the language
if (language != NULL) {
languages = CTFontDescriptorCopyAttribute(desc, kCTFontLanguagesAttribute);
if (languages != NULL) {
nl = CFArrayGetCount(languages);
d[0] = language[0];
if (d[0] >= 'A' && d[0] <= 'Z')
d[0] += 'a' - 'A';
d[1] = language[1];
if (d[1] >= 'A' && d[1] <= 'Z')
d[1] += 'a' - 'A';
for (il = 0; il < nl; il++) {
char c[2];
curlang = (CFStringRef) CFArrayGetValueAtIndex(languages, il);
// TODO check for failure
CFStringGetBytes(curlang, CFRangeMake(0, 2),
kCFStringEncodingUTF8, 0, false,
(UInt8 *) c, 2, NULL);
if (c[0] >= 'A' && c[0] <= 'Z')
c[0] += 'a' - 'A';
if (c[1] >= 'A' && c[1] <= 'Z')
c[1] += 'a' - 'A';
if (c[0] == d[0] && c[1] == d[1])
break;
}
if (il != nl) {
uint16_t typ;
typ = kLanguageTagType;
il++;
numType = CFNumberCreate(NULL, kCFNumberSInt16Type,
(const SInt16 *) (&typ));
numSelector = CFNumberCreate(NULL, kCFNumberCFIndexType,
&il);
values[0] = numType;
values[1] = numSelector;
innerDict = CFDictionaryCreate(NULL,
keys, values, 2,
// TODO are these correct?
&kCFCopyStringDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (innerDict == NULL) {
// TODO
}
CFArrayAppendValue(outerArray, innerDict);
CFRelease(innerDict);
CFRelease(numSelector);
CFRelease(numType);
}
CFRelease(languages);
}
}
keys[0] = kCTFontFeatureSettingsAttribute;
values[0] = outerArray;
innerDict = CFDictionaryCreate(NULL,
keys, values, 1,
// TODO are these correct?
&kCFCopyStringDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFRelease(outerArray);
new = CTFontDescriptorCreateCopyWithAttributes(desc, innerDict);
CFRelease(desc);
CFRelease(innerDict);
return new;
}

View File

@ -1,5 +0,0 @@
after UnderlineColor, before feature tags
// TODO document that this will also enable language-specific font features (TODO on DirectWrite too?)
// TODO document that this should be strict BCP 47 form (A-Z, a-z, 0-9, and -) for maximum compatibility
uiAttributeLanguage, // BCP 47 string

View File

@ -1,25 +0,0 @@
= attributed strings
attribute lengths are rounded to complete unicode codepoints
zero-length attributes are elided
consecutive attributes of the same type and value are merged
overlapping attributes of different types do not split each other
overlapping attributes of the same type but different values do split
empty string is allowed
empty string cannot have attributes
font family names are case-insensitive both in attributes and in descriptors
attributes are unique throughout a Unicode codepoint, not just to UTF-8 bytes
define what "it is an error" means in the case of uiFreeAttribute() and all uiAttributeValue() functions and constructors
does uiAttributeFamily() return a normalized string
should uiNewAttributeBackground() be renamed to uiNewAttributeBackgroundColor() and likewise for the type constant
should underline colors just ignore non-custom component arguments
should any color getter function accept a NULL pointer
what should uiAttributeUnderlineColor() do if the color type isn't Custom but the other pointers are non-NULL
should uiOpenTypeFeaturesGet() accept a NULL value pointer
what happens if uiOpenTypeFeaturesForEach() is given a NULl function pointer
should FeaturesAttribute be changed to OpenTypeFeaturesAttribute and likewise for the type enum
should uiNewFeaturesAttribute() accept NULL
should uiNewFamilyAttribute() accept NULL
it is an error in ForEach too
invalid values for uiDrawTextAlign
empty text layouts have one line
TODO figure out what to do if any field (particularly the font family name) in uiFontDescriptor is unset

View File

@ -1,115 +0,0 @@
// 27 february 2018
#ifndef TODO_TEST
#error TODO this is where libui itself goes
#endif
#include <inttypes.h>
#include "testing.h"
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
typedef struct uiOpenTypeFeatures uiOpenTypeFeatures;
typedef int uiForEach;
enum { uiForEachContinue, uiForEachStop };
typedef uiForEach (*uiOpenTypeFeaturesForEachFunc)(const uiOpenTypeFeatures *otf, char a, char b, char c, char d, uint32_t value, void *data);
#define uiprivNew(x) ((x *) malloc(sizeof (x)))
#define uiprivAlloc(x,y) malloc(x)
#define uiprivRealloc(x,y,z) realloc(x,y)
#define uiprivFree free
#include "opentype.c"
static void freeOpenType(void *otf)
{
uiFreeOpenTypeFeatures((uiOpenTypeFeatures *) otf);
}
testingTest(OpenTypeFeaturesAddGet)
{
uiOpenTypeFeatures *otf;
int got;
uint32_t value;
otf = uiNewOpenTypeFeatures();
testingTDefer(t, freeOpenType, otf);
uiOpenTypeFeaturesAdd(otf, 'a', 'b', 'c', 'd', 12345);
got = uiOpenTypeFeaturesGet(otf, 'a', 'b', 'c', 'd', &value);
if (!got)
testingTErrorf(t, "uiOpenTypeFeaturesGet() failed to get feature we added");
else if (value != 12345)
testingTErrorf(t, "feature abcd: got %" PRIu32 ", want 12345", value);
}
testingTest(OpenTypeFeaturesRemove)
{
uiOpenTypeFeatures *otf;
uint32_t value;
otf = uiNewOpenTypeFeatures();
testingTDefer(t, freeOpenType, otf);
uiOpenTypeFeaturesAdd(otf, 'a', 'b', 'c', 'd', 12345);
uiOpenTypeFeaturesRemove(otf, 'a', 'b', 'c', 'd');
if (uiOpenTypeFeaturesGet(otf, 'a', 'b', 'c', 'd', &value))
testingTErrorf(t, "uiOpenTypeFeaturesGet() succeeded in getting deleted feature; value %" PRIu32, value);
}
testingTest(OpenTypeFeaturesCloneAdd)
{
uiOpenTypeFeatures *otf, *otf2;
uint32_t value;
otf = uiNewOpenTypeFeatures();
testingTDefer(t, freeOpenType, otf);
uiOpenTypeFeaturesAdd(otf, 'a', 'b', 'c', 'd', 12345);
otf2 = uiOpenTypeFeaturesClone(otf);
testingTDefer(t, freeOpenType, otf2);
uiOpenTypeFeaturesAdd(otf2, 'q', 'w', 'e', 'r', 56789);
if (uiOpenTypeFeaturesGet(otf, 'q', 'w', 'e', 'r', &value))
testingTErrorf(t, "uiOpenTypeFeaturesGet() on original succeeded in getting feature added to clone; value %" PRIu32, value);
}
testingTest(OpenTypeFeaturesCloneModify)
{
uiOpenTypeFeatures *otf, *otf2;
uint32_t value;
otf = uiNewOpenTypeFeatures();
testingTDefer(t, freeOpenType, otf);
uiOpenTypeFeaturesAdd(otf, 'a', 'b', 'c', 'd', 12345);
otf2 = uiOpenTypeFeaturesClone(otf);
testingTDefer(t, freeOpenType, otf2);
uiOpenTypeFeaturesAdd(otf2, 'a', 'b', 'c', 'd', 56789);
uiOpenTypeFeaturesGet(otf, 'a', 'b', 'c', 'd', &value);
if (value != 12345)
testingTErrorf(t, "uiOpenTypeFeaturesGet() on original: got %" PRIu32 ", want 12345", value);
uiOpenTypeFeaturesGet(otf2, 'a', 'b', 'c', 'd', &value);
if (value != 56789)
testingTErrorf(t, "uiOpenTypeFeaturesGet() on clone: got %" PRIu32 ", want 56789", value);
}
testingTest(OpenTypeFeaturesCloneRemove)
{
uiOpenTypeFeatures *otf, *otf2;
uint32_t value;
otf = uiNewOpenTypeFeatures();
testingTDefer(t, freeOpenType, otf);
uiOpenTypeFeaturesAdd(otf, 'a', 'b', 'c', 'd', 12345);
otf2 = uiOpenTypeFeaturesClone(otf);
testingTDefer(t, freeOpenType, otf2);
uiOpenTypeFeaturesRemove(otf2, 'a', 'b', 'c', 'd');
if (uiOpenTypeFeaturesGet(otf2, 'a', 'b', 'c', 'd', &value))
testingTErrorf(t, "uiOpenTypeFeaturesGet() on clone succeeded in getting feature removed from clone; value %" PRIu32, value);
if (!uiOpenTypeFeaturesGet(otf, 'a', 'b', 'c', 'd', &value))
testingTErrorf(t, "uiOpenTypeFeaturesGet() on original failed to get feature removed from clone");
}
int main(void)
{
return testingMain();
}

View File

@ -1,137 +0,0 @@
// 27 february 2018
// TODO
// - https://blogs.msdn.microsoft.com/oldnewthing/20181107-00/?p=100155 https://blogs.msdn.microsoft.com/oldnewthing/20181108-00/?p=100165 https://blogs.msdn.microsoft.com/oldnewthing/20181109-00/?p=100175
// - also in the above: note the unspecified order of data in the sub-segments...
#ifndef testingprivIncludeGuard_testing_h
#define testingprivIncludeGuard_testing_h
#include <stdarg.h>
#undef testingprivBadLanguageVersion
#ifdef __cplusplus
// TODO https://stackoverflow.com/questions/2324658/how-to-determine-the-version-of-the-c-standard-used-by-the-compiler implies this won't do with C++0x-era compilers, and https://wiki.apache.org/stdcxx/C++0xCompilerSupport doesn't talk about va_copy() so a simple version check for the C99 preprocessor may be wrong...
// TODO what if __cplusplus is blank (maybe only in that case, since IIRC C++98 requires __cplusplus to have a value)?
#if __cplusplus < 201103L
#define testingprivBadLanguageVersion
#endif
#elif !defined(__STDC_VERSION__)
#define testingprivBadLanguageVersion
#elif __STDC_VERSION__ < 199901L
#define testingprivBadLanguageVersion
#endif
#ifdef testingprivBadLanguageVersion
#error sorry, TODO requires either C99 or C++11; cannot continue
#endif
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
#define testingprivMkScaffold(name) \
static inline void testingprivScaffold ## name(testingT *t) \
{ \
bool failedNow = false, skippedNow = false; \
try { name(t); } \
catch (testingprivFailNowException e) { failedNow = true; } \
catch (testingprivSkipNowException e) { skippedNow = true; } \
/* TODO see if we should catch other exceptions too */ \
/* don't call these in the catch blocks as they call longjmp() */ \
if (failedNow) testingprivTDoFailNow(t); \
if (skippedNow) testingprivTDoSkipNow(t); \
}
#else
#define testingprivMkScaffold(name) \
static inline void testingprivScaffold ## name(testingT *t) { name(t); }
#endif
// references:
// - https://gitlab.gnome.org/GNOME/glib/blob/master/glib/gconstructor.h
// - https://gitlab.gnome.org/GNOME/glib/blob/master/gio/glib-compile-resources.c
// - https://msdn.microsoft.com/en-us/library/bb918180.aspx
#if defined(__cplusplus)
#define testingprivMkCtor(name, reg) \
static reg ## Class testingprivCtor ## name(#name, testingprivScaffold ## name);
#elif defined(__GNUC__)
#define testingprivMkCtor(name, reg) \
__attribute__((constructor)) static void testingprivCtor ## name(void) { reg(#name, testingprivScaffold ## name); }
#elif defined(_MSC_VER)
#define testingprivMkCtorPrototype(name, reg) \
static int name(void) testingprivCtor ## name(void) { reg(#name, testingprivScaffold ## name); return 0; } \
__pragma(section(".CRT$XCU",read)) \
__declspec(allocate(".CRT$XCU")) static int (*testingprivCtorPtr ## name)(void) = testingprivCtor ## name;
#elif defined(__SUNPRO_C)
#define testingprivMkCtor(name, reg) \
_Pragma("init(testingprivCtor" #name ")") static void testingprivCtor ## name(void) { reg(#name, testingprivScaffold ## name); }
#else
#error unknown compiler for making constructors in C; cannot continue
#endif
#define testingTest(Name) \
void Test ## Name(testingT *t); \
testingprivMkScaffold(Test ## Name) \
testingprivMkCtor(Test ## Name, testingprivRegisterTest) \
void Test ## Name(testingT *t)
extern int testingMain(void);
typedef struct testingT testingT;
#define testingTLogf(t, ...) \
testingprivExpand(testingprivTLogfThen((void), t, __VA_ARGS__))
#define testingTLogvf(t, format, ap) \
testingprivTLogvfThen((void), t, format, ap)
#define testingTErrorf(t, ...) \
testingprivExpand(testingprivTLogfThen(testingTFail, t, __VA_ARGS__))
#define testingTErrorvf(t, format, ap) \
testingprivTLogvfThen(testingTFail, t, format, ap)
#define testingTFatalf(t, ...) \
testingprivExpand(testingprivTLogfThen(testingTFailNow, t, __VA_ARGS__))
#define testingTFatalvf(t, format, ap) \
testingprivTLogvfThen(testingTFailNow, t, format, ap)
#define testingTSkipf(t, ...) \
testingprivExpand(testingprivTLogfThen(testingTSkipNow, t, __VA_ARGS__))
#define testingTSkipvf(t, format, ap) \
testingprivTLogvfThen(testingTSkipNow, t, format, ap)
extern void testingTFail(testingT *t);
#ifdef __cplusplus
#define testingTFailNow(t) (throw testingprivFailNowException())
#define testingTSkipNow(t) (throw testingprivSkipNowException())
#else
#define testingTFailNow(t) (testingprivTDoFailNow(t))
#define testingTSkipNow(t) (testingprivTDoSkipNow(t))
#endif
// TODO should the defered function also have t passed to it?
extern void testingTDefer(testingT *t, void (*f)(void *data), void *data);
// TODO IEEE 754 helpers
// references:
// - https://www.sourceware.org/ml/libc-alpha/2009-04/msg00005.html
// - https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html
// - https://stackoverflow.com/questions/5085533/is-a-c-preprocessor-identical-to-a-c-preprocessor
// TODO should __LINE__ arguments use intmax_t or uintmax_t instead of int?
extern void testingprivRegisterTest(const char *, void (*)(testingT *));
// see https://stackoverflow.com/questions/32399191/va-args-expansion-using-msvc
#define testingprivExpand(x) x
#define testingprivTLogfThen(then, t, ...) ((testingprivTLogfFull(t, __FILE__, __LINE__, __VA_ARGS__)), (then(t)))
#define testingprivTLogvfThen(then, t, format, ap) ((testingprivTLogvfFull(t, __FILE__, __LINE__, format, ap)), (then(t)))
extern void testingprivTLogfFull(testingT *, const char *, int, const char *, ...);
extern void testingprivTLogvfFull(testingT *, const char *, int, const char *, va_list);
extern void testingprivTDoFailNow(testingT *);
extern void testingprivTDoSkipNow(testingT *);
#ifdef __cplusplus
}
namespace {
class testingprivFailNowException {};
class testingprivSkipNowException {};
class testingprivRegisterTestClass {
public:
testingprivRegisterTestClass(const char *name, void (*f)(testingT *)) { testingprivRegisterTest(name, f); }
};
}
#endif
#endif

View File

@ -1,144 +0,0 @@
// 27 february 2018
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include "testing.h"
#define testingprivNew(T) ((T *) malloc(sizeof (T)))
struct defer {
void (*f)(void *);
void *data;
struct defer *next;
};
struct testingT {
const char *name;
void (*f)(testingT *);
int failed;
int skipped;
jmp_buf returnNowBuf;
struct defer *defers;
int defersRun;
testingT *next;
};
static testingT *tests = NULL;
void testingprivRegisterTest(const char *name, void (*f)(testingT *))
{
testingT *t;
t = testingprivNew(testingT);
t->name = name;
t->f = f;
t->failed = 0;
t->skipped = 0;
t->defers = NULL;
t->defersRun = 0;
// TODO add in the order called
t->next = tests;
tests = t;
}
static void runDefers(testingT *t)
{
struct defer *d;
if (t->defersRun)
return;
t->defersRun = 1;
for (d = t->defers; d != NULL; d = d->next)
(*(d->f))(d->data);
}
int testingMain(void)
{
testingT *t;
int anyFailed;
const char *status;
// TODO see if this should run if all tests are skipped
if (tests == NULL) {
fprintf(stderr, "warning: no tests to run\n");
// imitate Go here (TODO confirm this)
return 0;
}
anyFailed = 0;
for (t = tests; t != NULL; t = t->next) {
printf("=== RUN %s\n", t->name);
if (setjmp(t->returnNowBuf) == 0)
(*(t->f))(t);
runDefers(t);
status = "PASS";
if (t->failed) {
status = "FAIL";
anyFailed = 1;
} else if (t->skipped)
// note that failed overrides skipped
status = "SKIP";
printf("--- %s: %s (%s)\n", status, t->name, "TODO");
}
if (anyFailed) {
printf("FAIL\n");
return 1;
}
printf("PASS\n");
return 0;
}
void testingprivTLogfFull(testingT *t, const char *file, int line, const char *format, ...)
{
va_list ap;
va_start(ap, format);
testingprivTLogvfFull(t, file, line, format, ap);
va_end(ap);
}
void testingprivTLogvfFull(testingT *t, const char *file, int line, const char *format, va_list ap)
{
// TODO extract filename from file
printf("\t%s:%d: ", file, line);
// TODO split into lines separated by \n\t\t and trimming trailing empty lines
vprintf(format, ap);
printf("\n");
}
void testingTFail(testingT *t)
{
t->failed = 1;
}
static void returnNow(testingT *t)
{
// run defers before calling longjmp() just to be safe
runDefers(t);
longjmp(t->returnNowBuf, 1);
}
void testingprivTDoFailNow(testingT *t)
{
testingTFail(t);
returnNow(t);
}
void testingprivTDoSkipNow(testingT *t)
{
t->skipped = 1;
returnNow(t);
}
void testingTDefer(testingT *t, void (*f)(void *data), void *data)
{
struct defer *d;
d = testingprivNew(struct defer);
d->f = f;
d->data = data;
// add to the head of the list so defers are run in reverse order of how they were added
d->next = t->defers;
t->defers = d;
}

View File

@ -1,19 +0,0 @@
Proper vertical text support in uiDrawTextLayout was removed because DirectWrite doesn't add this until Windows 8.1 (unless I drop IDWriteTextLayout and do the script analysis myself; TODO consider this possibility).
On OS X, setting the vertical forms attribute stacks non-vertical scripts in vertical text (rotates each individual glyph) with Core Text, whereas everything else — including Cocoa's text system — rotates entire non-vertical strings. Not sure what to do about this except manually detect which characters to apply the attribute to:
http://www.unicode.org/notes/tn22/RobustVerticalLayout.pdf
http://www.unicode.org/Public/vertical/revision-17/VerticalOrientation-17.txt
In addition, with Core Text, the vertical forms attribute vertically centers the vertical glyphs on the bhorizontal baseline, rather than flush with the text. Using the baseline class attribute doesn't seem to work.
TODO investigate kCJKVerticalRomanPlacementType
If readded, this will need to be a layout-wide setting, not a per-character setting. Pango works right this way; the current Pango code doesn't seem to work.
More links:
https://www.w3.org/TR/2012/NOTE-jlreq-20120403/#line-composition
https://www.w3.org/TR/REC-CSS2/notes.html
TODO indicate where in the attributes.c file that block of code should go (or drop it entirely for the reasons listed above)
TODO same for ui.h
TODO vertical carets

View File

@ -1,19 +0,0 @@
case uiAttributeVerticalForms:
if (spec->Value != 0) {
CFAttributedStringSetAttribute(p->mas, range, kCTVerticalFormsAttributeName, kCFBooleanTrue);
// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassRoman);
// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicCentered);
// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicLow);
// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicHigh);
// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassHanging);
// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassMath);
} else {
CFAttributedStringSetAttribute(p->mas, range, kCTVerticalFormsAttributeName, kCFBooleanFalse);
// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassRoman);
// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicCentered);
// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicLow);
// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassIdeographicHigh);
// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassHanging);
// CFAttributedStringSetAttribute(p->mas, range, kCTBaselineClassAttributeName, kCTBaselineClassMath);
}
break;

View File

@ -1,9 +0,0 @@
PangoGravity gravity;
case uiAttributeVerticalForms:
gravity = PANGO_GRAVITY_SOUTH;
if (spec->Value != 0)
gravity = PANGO_GRAVITY_EAST;
addattr(p, start, end,
pango_attr_gravity_new(gravity));
break;

View File

@ -1,15 +0,0 @@
uint32_t vertval;
case uiAttributeVerticalForms:
// LONGTERM 8 and/or 8.1 add other methods for vertical text
op.p = p;
op.start = start;
op.end = end;
vertval = 0;
if (spec->Value != 0)
vertval = 1;
doOpenType("vert", vertval, &op);
doOpenType("vrt2", vertval, &op);
doOpenType("vkrn", vertval, &op);
doOpenType("vrtr", vertval, &op);
break;

View File

@ -1,2 +0,0 @@
case uiAttributeVerticalForms:
return boolsEqual(attr, spec);

View File

@ -1,18 +0,0 @@
next = "vertical glyph forms";
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeVerticalForms;
spec.Value = 1;
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, " (which you can draw rotated for proper vertical text; for instance, ");
next = "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A";
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeVerticalForms;
spec.Value = 1;
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ")");
uiAttributedStringAppendUnattributed(attrstr, ", ");

View File

@ -1,2 +0,0 @@
// TODO rename to uiAttributeVertical?
uiAttributeVerticalForms, // 0 = off, 1 = on

View File

@ -1,25 +0,0 @@
https://twitter.com/OS2World/status/983822011389620224
Hi. I recommend you today to start with OS/2 Warp 4.52 or ArcaOS ( The tools are almost the same of Warp 3). EDM/2 is a good place to start: http://www.edm2.com
https://twitter.com/OS2World/status/983822594465034240
There is also an RPM with OS/2 Software (https://www.arcanoae.com/resources/downloadables/arca-noae-package-manager/ …), and you can get from it some parts of the OS/2 Toolkit. If you want to develop drivers you require the OS/2 Device Driver Kit. (that is more complex). You can also develop in Qt4 and compile things with gcc.
http://www.edm2.com/index.php/Main_Page
https://www.arcanoae.com/resources/downloadables/arca-noae-package-manager/
http://www.edm2.com/index.php/IBM_OS/2_Toolkit_Documentation
https://www.os2world.com/forum/index.php?topic=953.0
https://www.google.com/search?client=firefox-b-1&ei=QIPPWurPLs7OwAK65K24BA&q=%22OS%2F2+Toolkit%22+site%3Aamazon.com&oq=%22OS%2F2+Toolkit%22+site%3AAmazon.com&gs_l=psy-ab.3...160814.161293.0.161540.2.2.0.0.0.0.128.252.0j2.2.0....0...1c.1.64.psy-ab..0.0.0....0.itT9Og6hC5c
http://www.edm2.com/index.php/List_of_Presentation_Manager_Articles
https://www.ecsoft2.org/
http://www.edm2.com/index.php/Cairo
http://www.edm2.com/index.php/Doodle
http://www.edm2.com/index.php/Workplace_Shell_Toolkit
http://wpstk.netlabs.org/en/site/index.xml
https://en.wikipedia.org/wiki/Workplace_Shell
https://www.google.com/search?q=OS2+alphablending&ie=utf-8&oe=utf-8&client=firefox-b-1
http://www.edm2.com/index.php/List_of_Multimedia_Articles
alphablending:
http://www.osnews.com/story/369/Review-eComStation-OS2-1.0/page3/
http://halfos.ru/documentation/33-os2-api-documentation/67-opengl-os2-developer-reference-guide.html
http://www.altools.com/ALTools/ALSee/ALSee-Image-Viewer.aspx
http://www.os2voice.org/vnewsarc/bn2007122.html
http://www.mozillazine.org/talkback.html?article=194
https://books.google.com/books?id=9cpU5uYCzq4C&pg=PA202&lpg=PA202&dq=%22OS/2%22+alphablending&source=bl&ots=uatEop2jAL&sig=HAa_ofQSKsk6-8tBR6YZ6MRJG_0&hl=en&sa=X&ved=0ahUKEwiDq5HukLbaAhUk8IMKHR7aCw4Q6AEIWTAI#v=onepage&q=%22OS%2F2%22%20alphablending&f=false

View File

@ -1,76 +0,0 @@
multi-platform {
https://docs.microsoft.com/en-us/azure/devops/pipelines/get-started-multiplatform?view=azure-devops
}
microsoft-hosted agents {
https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/hosted?view=azure-devops&tabs=yaml#use-a-microsoft-hosted-agent
https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/v2-windows?view=azure-devops
https://github.com/Microsoft/azure-pipelines-image-generation/blob/master/images/win/Vs2017-Server2016-Readme.md
https://github.com/Microsoft/azure-pipelines-image-generation/blob/master/images/win/Vs2015-Server2012R2-Readme.md
}
maximum number of processors (for ninja -j) {
msbuild supports this with the -m option natively; meanwhile:
https://www.gnu.org/software/coreutils/manual/html_node/nproc-invocation.html
}
potentially useful tasks {
https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/utility/publish-pipeline-artifact?view=azure-devops
https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/build/visual-studio-build?view=azure-devops
https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/build/xcode?view=azure-devops
https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/utility/github-release?view=azure-devops
https://docs.microsoft.com/en-us/azure/devops/extend/develop/add-build-task?view=azure-devops
}
meson CI reference {
https://github.com/mesonbuild/meson/blob/master/docs/markdown/Continuous-Integration.md
}
job templates, which will clean up our pipelines {
https://docs.microsoft.com/en-us/azure/devops/pipelines/process/templates?view=azure-devops
}
migrating from travis {
https://docs.microsoft.com/en-us/azure/devops/pipelines/migrate/from-travis?view=azure-devops
}
32-bit {
https://github.com/numpy/numpy/issues/12856
https://github.com/numpy/numpy/pull/12863/files
https://github.com/numpy/numpy/blob/master/azure-pipelines.yml (also explains what the @s mean; thanks guys)
}
using visual studio tools {
https://docs.microsoft.com/en-us/dotnet/framework/tools/developer-command-prompt-for-vs
https://docs.microsoft.com/en-us/cpp/build/building-on-the-command-line?view=vs-2019#developer_command_file_locations
}
OLD using visual studio tools; also some docker {
https://github.com/mesonbuild/meson/blob/master/ci/azure-steps.yml
https://github.com/reactiveui/Akavache/blob/master/azure-pipelines.yml
https://github.com/vector-of-bool/CMakeCM/blob/master/azure-pipelines.yml
https://docs.microsoft.com/en-us/visualstudio/install/advanced-build-tools-container?view=vs-2019
https://docs.microsoft.com/en-us/visualstudio/install/build-tools-container?view=vs-2019
https://devblogs.microsoft.com/cppblog/using-msvc-in-a-docker-container-for-your-c-projects/
https://devblogs.microsoft.com/cppblog/finding-the-visual-c-compiler-tools-in-visual-studio-2017/
}
reference {
https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=azure-devops&tabs=schema#task
https://docs.microsoft.com/en-us/azure/devops/pipelines/apps/windows/cpp?view=azure-devops
https://docs.microsoft.com/en-us/azure/devops/pipelines/languages/python?view=azure-devops
https://docs.microsoft.com/en-us/azure/devops/pipelines/process/conditions?view=azure-devops&tabs=yaml
}
C++ binary compatibility and shared libraries (TODO split into its own notes file) {
https://docs.microsoft.com/en-us/cpp/porting/binary-compat-2015-2017?view=vs-2017
}
others I forgot why I had them open {
https://github.com/Microsoft/cpprestsdk/blob/master/azure-pipelines.yml (32-bit?)
https://docs.microsoft.com/en-us/azure/devops/release-notes/2018/sprint-142-update (I forget, maybe also 32-bit?)
}
others that might not be relevant {
https://github.com/Microsoft/azure-pipelines-agent/blob/master/docs/start/envubuntu.md has instructions on how to build the agent tool itself
}

View File

@ -1 +0,0 @@
UWP has this (TODO check its implementation to see if it matches ours) https://docs.microsoft.com/en-us/uwp/api/windows.ui.viewmanagement.uisettings#Windows_UI_ViewManagement_UISettings_CaretWidth

View File

@ -1 +0,0 @@
https://blogs.msdn.microsoft.com/oldnewthing/20181226-00/?p=100565

View File

@ -1,6 +0,0 @@
https://developer.apple.com/library/mac/documentation/AppKit/Reference/NSLayoutConstraint_Class/
https://developer.apple.com/documentation/uikit/nslayoutconstraint?language=objc
https://developer.apple.com/library/mac/documentation/UserExperience/Conceptual/AutolayoutPG/ProgrammaticallyCreatingConstraints.html#//apple_ref/doc/uid/TP40010853-CH16-SW1
https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/ProgrammaticallyCreatingConstraints.html#//apple_ref/doc/uid/TP40010853-CH16-SW1 (Listing 13-3)
https://developer.apple.com/library/mac/documentation/UserExperience/Conceptual/AutolayoutPG/WorkingwithScrollViews.html#//apple_ref/doc/uid/TP40010853-CH24-SW1
https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/AutolayoutPG/WorkingwithScrollViews.html#//apple_ref/doc/uid/TP40010853-CH24-SW1 ("IMPORTANT Your layout must fully define..." large box)

View File

@ -1,8 +0,0 @@
https://www.google.com/search?q=nsalert+error+icon&client=firefox-b&tbm=isch&source=iu&ictx=1&fir=2iRctS5fJByN0M%253A%252Cw324MTzjHa1bAM%252C_&usg=__x3wpwdNN1L8VI2kHtkKAXFMtpj4%3D&sa=X&ved=0ahUKEwjJzpjN2qDZAhVjw1kKHfOHDoQQ9QEIMTAB#imgrc=2iRctS5fJByN0M:
http://0xced.blogspot.com/2009/11/clalert-nsalert-done-right.html
https://gist.github.com/0xced/228140
http://editra.org/uploads/code/artmac.html
http://mirror.informatimago.com/next/developer.apple.com/documentation/Carbon/Reference/IconServices/index.html
http://www.cocoabuilder.com/archive/cocoa/15427-iconref-to-nsimage.html
https://github.com/lukakerr/Swift-NSUserNotificationPrivate
https://stackoverflow.com/questions/32943220/the-sidebar-icon-image-name-in-osx

View File

@ -1,2 +0,0 @@
https://github.com/kusti8/proton-native/issues/47#issuecomment-373068947
https://blogs.kde.org/2009/03/26/how-crash-almost-every-qtkde-application-and-how-fix-it-0

View File

@ -1 +0,0 @@
High DPI Displays | Qt 5.5 http://doc.qt.io/qt-5/highdpi.html bottom of page(?)

View File

@ -1,16 +0,0 @@
https://msdn.microsoft.com/en-us/library/windows/desktop/dd319079(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/dd318103(v=vs.85).aspx
https://stackoverflow.com/questions/4663855/is-there-a-repository-for-localized-common-text-in-winforms
https://stackoverflow.com/questions/2502375/find-localized-windows-strings
https://docs.microsoft.com/en-us/windows-hardware/customize/mobile/mcsf/create-a-resource-only-dll-for-localized-strings
https://msdn.microsoft.com/en-us/library/windows/desktop/ee845043(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/cc194807.aspx
https://www.codeproject.com/Articles/10542/Easily-Load-and-Format-Strings-from-the-String-Tab
https://www.codeproject.com/Tips/431045/The-inner-working-of-FindResource-and-LoadString-W
https://mihai-nita.net/2007/05/03/how-to-localize-an-rc-file/
https://www.microsoft.com/en-us/language
https://www.microsoft.com/en-us/language/Terminology
https://www.microsoft.com/en-us/language/LicenseAgreement
https://www.microsoft.com/en-us/language/Translations
http://www.ttt.org/oscarstandards/tbx/
https://blogs.msdn.microsoft.com/oldnewthing/20181122-00/?p=100295

View File

@ -1,23 +0,0 @@
https://developer.apple.com/documentation/corefoundation/1542764-cfurlcopyresourcepropertyforkey?language=objc
https://developer.apple.com/documentation/foundation/nsargumentdomain?language=objc
https://developer.apple.com/documentation/appkit/1428499-nsapplicationmain
https://webkit.googlesource.com/WebKit/+/9ae3deefb7df48bd85f01edcf8382ee300eafbd4%5E!/
https://stackoverflow.com/questions/11181324/how-to-change-retina-display-system-preferences-in-osx
https://web.archive.org/web/20150828053709/http://garethjenkins.com/2012/07/01/investigating-a-high-resolution-retina-utility-for-macbook-pro-1x-and-2x-modes/
https://github.com/avibrazil/RDM
https://www.google.com/search?client=firefox-b-1-d&q=IOFramebuffer+subclass
https://developer.apple.com/documentation/kernel/ioframebuffer?language=objc
https://stackoverflow.com/questions/51846999/how-to-write-macos-display-driver
https://twitter.com/kenkeiter/status/3631378994298882
http://en.ennowelbers.info/node/33
https://www.insanelymac.com/forum/topic/114528-how-to-compile-a-driver-from-source/
https://github.com/mkernel/EWProxyFramebuffer
https://stackoverflow.com/questions/46904493/ioframebuffer-cant-access-vram-framebuffer-in-macos-10-13
https://www.google.com/search?client=firefox-b-1-d&q=IOFramebuffer+10.13
https://github.com/codykrieger/gfxCardStatus/issues/296
https://plugable.com/2018/03/30/macos-10-13-4-disables-displaylink-duet-display-devices/
https://www.tekrevue.com/tip/hidpi-mode-os-x/
https://medium.com/@ivan.ha/how-to-mimic-a-2k-monitor-as-retina-display-in-macos-sierra-using-hidpi-f53d87630c48
https://www.google.com/search?client=firefox-b-1-d&q=IOFramebuffer+10.13
https://www.quora.com/What-is-the-underlying-reason-if-DisplayLink-is-no-longer-working-properly-on-Mac-OS
https://github.com/Siguza/iokit-utils

View File

@ -1,213 +0,0 @@
windows data types, "open specifications" on msdn https://msdn.microsoft.com/en-us/library/cc230321.aspx
(I usually use https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx for windows data types)
windows platform update for windows 7
https://msdn.microsoft.com/en-us/library/windows/desktop/jj863687(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/hh802478(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/hh802480(v=vs.85).aspx
DWM, header bars, toolbars
https://stackoverflow.com/questions/41106347/why-is-my-dwmextendframeintoclientaread-window-not-drawing-the-dwm-borders/41125616#41125616
https://developer.gnome.org/hig/stable/header-bars.html.en
https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/OSXHIGuidelines/WindowTitleBarToolbar.html#//apple_ref/doc/uid/20000957-CH39-SW1
https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/OSXHIGuidelines/ControlsAll.html#//apple_ref/doc/uid/20000957-CH46-SW2
https://msdn.microsoft.com/en-us/library/windows/desktop/bb787329(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/bb787334(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/bb787337(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/cc835034(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/bb787345(v=vs.85).aspx
rendering
https://www.youtube.com/watch?v=UUfXWzp0-DU
text input on windows
https://blogs.msdn.microsoft.com/oldnewthing/20121025-00/?p=6253
https://www.google.com/search?q=translatemessage+site%3Ahttp%3A%2F%2Farchives.miloush.net%2Fmichkap%2Farchive%2F&ie=utf-8&oe=utf-8
http://archives.miloush.net/michkap/archive/2008/04/22/8415843.html
http://archives.miloush.net/michkap/archive/2007/03/25/1948887.html
http://archives.miloush.net/michkap/archive/2006/09/10/748775.html
http://archives.miloush.net/michkap/archive/2004/11/27/270931.html
https://stackoverflow.com/questions/41334851/since-translatemessage-returns-nonzero-unconditionally-how-can-i-tell-either
http://stackoverflow.com/questions/41334851/since-translatemessage-returns-nonzero-unconditionally-how-can-i-tell-either?noredirect=1#comment70107257_41334851
text layouts
https://developer.apple.com/reference/coretext/2110184-ctframe?language=objc
https://msdn.microsoft.com/en-us/library/windows/desktop/dd368203(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/dd368205(v=vs.85).aspx
https://developer.gnome.org/pango/1.30/pango-Layout-Objects.html#pango-layout-set-font-description
https://developer.gnome.org/pango/1.30/pango-Fonts.html#PangoWeight-enum
https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c (code for small caps)
https://bugzilla.gnome.org/show_bug.cgi?id=766148
https://developer.apple.com/documentation/coretext/ctline?preferredLanguage=occ
https://developer.apple.com/reference/coretext/1508876-ctlinegetstringindexforposition?language=objc
https://developer.apple.com/library/content/documentation/StringsTextFonts/Conceptual/CoreText_Programming/Introduction/Introduction.html#//apple_ref/doc/uid/TP40005533-CH1-SW1
https://developer.apple.com/reference/coretext/2110184-ctframe?language=objc
https://developer.apple.com/reference/coretext/2168885-ctline?language=objc
https://developer.apple.com/library/content/documentation/StringsTextFonts/Conceptual/CoreText_Programming/LayoutOperations/LayoutOperations.html
http://asciiwwdc.com/2013/sessions/205
https://developer.apple.com/reference/coretext/1511332-ctlinegetboundswithoptions?language=objc
https://stackoverflow.com/questions/19709751/ctlinegetboundswithoptions-what-does-the-returned-frame-origin-y-value-mean?rq=1
https://imgur.com/a/lqhzC
https://imgur.com/a/hYeOL
https://www.google.com/search?q=ctline+%22paragraph+spacing%22&ie=utf-8&oe=utf-8
http://stackoverflow.com/questions/8010615/how-do-i-determine-the-of-a-ctline-following-a-ctline
https://imgur.com/a/dlpm2
https://stackoverflow.com/questions/41601845/am-i-missing-something-in-core-text-that-will-let-me-see-which-line-a-certain-po
https://www.google.com/search?q=%22core+text%22+%22combining+character%22&oq=%22core+text%22+%22combining+character%22&gs_l=serp.3...2526898.2528158.0.2528485.21.10.0.0.0.0.216.997.5j3j1.9.0....0...1c.1.64.serp..12.8.904...33i21k1j33i160k1.J69jsB9g0N0
https://github.com/macvim-dev/macvim/issues/400#issuecomment-274292877
https://bugs.webkit.org/show_bug.cgi?id=68287
https://trac.webkit.org/changeset/95391
https://trac.webkit.org/changeset/95391/trunk/Source/WebCore/platform/graphics/mac/ComplexTextControllerCoreText.cpp
http://stackoverflow.com/questions/41857213/is-there-any-way-i-can-get-precise-metrics-line-ascent-line-descent-etc-of
http://pawlan.com/monica/articles/texttutorial/int.html
enter in entry fields, possibly other text layout issues, possibly keyboard shortcuts
https://developer.gnome.org/gtk3/3.10/GtkEntry.html "activate" signal
https://gitlab.gnome.org/GNOME/gtk/blob/gtk-3-10/gtk/gtkentry.c (not sure where)
https://developer.gnome.org/gtk3/3.10/GtkTextView.html signals section of contents
https://gitlab.gnome.org/GNOME/gtk/blob/gtk-3-10/gtk/gtktextview.c (not sure where; closed before I could find out again though gitlab might wreck it anyway)
https://developer.gnome.org/gtk3/3.10/gtk3-Bindings.html
https://gitlab.gnome.org/GNOME/gtk/blob/gtk-3-10/gtk/gtkwidget.c (not sure where)
https://gitlab.gnome.org/GNOME/gtk/blob/gtk-3-10/gtk/gtkbindings.c (not sure where)
https://gitlab.gnome.org/GNOME/gnome-builder/tree/master/libide/keybindings/ide-keybindings.c 404; TODO find the original cgit link in the chat logs to see what hergertme wanted me to see
https://gitlab.gnome.org/GNOME/gnome-builder/tree/master/libide/sourceview/ide-source-view.c likewise
https://gitlab.gnome.org/GNOME/gnome-builder/tree/master/libide/sourceview/ide-source-view-mode.c#n323 likewise
rgb int->float conversion
https://stackoverflow.com/questions/41348339/how-to-convert-rgb-to-hexadecimal-using-gtk?noredirect=1#comment69903262_41348339
windows fonts, hi-dpi
https://stackoverflow.com/questions/41448320/dlgtemplateex-and-ds-shellfont-what-about-point-size
windows default fonts
https://stackoverflow.com/questions/41505151/how-to-draw-text-with-the-default-ui-font-in-directwrite#41505750
uwp stuff
https://docs.microsoft.com/en-us/windows/uwp/design/controls-and-patterns/inking-controls
https://msdn.microsoft.com/en-us/windows/uwp/controls-and-patterns/master-details
cmake stuff
https://public.kitware.com/pipermail/cmake/2010-January/034669.html
https://cmake.org/cmake/help/v3.0/command/string.html
https://cmake.org/pipermail/cmake/2003-April/003599.html
opentype features and locales
https://www.microsoft.com/typography/otspec/featurelist.htm
https://en.wikipedia.org/wiki/List_of_typographic_features
https://www.microsoft.com/typography/otspec160/scripttags.htm
https://msdn.microsoft.com/en-us/library/windows/desktop/dd371503(v=vs.85).aspx
gspell (TODO figure out what I wanted from this; possibly spelling checking)
https://git.gnome.org/browse/gspell/tree/gspell/gspell-inline-checker-text-buffer.c
https://git.gnome.org/browse/gtk+/tree/gtk/gtktextview.c?h=gtk-3-10
other assorted notes on text
https://mail.gnome.org/archives/gtk-devel-list/2016-March/msg00037.html
https://mail.gnome.org/archives/gtk-devel-list/2016-June/msg00007.html
https://blogs.msdn.microsoft.com/tsfaware/
https://stackoverflow.com/questions/40453186/what-is-the-correct-modern-way-to-handle-arbitrary-text-input-in-a-custom-contr
https://www.microsoft.com/typography/fonts/family.aspx
windows ribbon
https://twitter.com/_Ninji/status/814918189708668929 (C08rw9TWgAMpLkC.jpg:large)
https://developer.apple.com/library/content/samplecode/CocoaTipsAndTricks/Introduction/Intro.html#//apple_ref/doc/uid/DTS40010039 BlurryView and StaticObjcMethods in particular
printing https://developer.apple.com/library/content/samplecode/PMPrinterPrintWithFile/Introduction/Intro.html#//apple_ref/doc/uid/DTS10003958
auto layout https://developer.apple.com/library/content/releasenotes/UserExperience/RNAutomaticLayout/index.html#//apple_ref/doc/uid/TP40010631
other stuff, mostly custom draw on windows
https://github.com/pauldotknopf/WindowsSDK7-Samples/blob/master/multimedia/DirectWrite/ChooseFont/ChooseFont.cpp at ChooseFontDialog::OnFontSizeNameEdit() definition
https://developer.gnome.org/gtk3/3.10/GtkColorButton.html
https://msdn.microsoft.com/en-us/library/windows/desktop/bb775951(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/bb775951(v=vs.85).aspx between BS_LEFTTEXT and BS_RADIOBUTTON
https://msdn.microsoft.com/en-us/library/windows/desktop/bb775923(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/bb775923(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/bb775802(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/bb775802(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/bb761837(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/bb761837(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/bb761847(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/bb761847(v=vs.85).aspx description + parameters
https://msdn.microsoft.com/en-us/library/windows/desktop/bb775483(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/bb775483(v=vs.85).aspx dwItemSpec parameter
https://msdn.microsoft.com/en-us/library/windows/desktop/bb775951(v=vs.85).aspx#BS_NOTIFY
https://msdn.microsoft.com/en-us/library/windows/desktop/bb775951(v=vs.85).aspx#BS_NOTIFY BS_NOTIFY to BS_RIGHT
http://stackoverflow.com/questions/17678261/how-to-change-color-of-a-button-while-keeping-buttons-functions-in-win32
https://msdn.microsoft.com/en-us/library/windows/desktop/ff919569(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/ff919569(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/ff919573(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/ff919573(v=vs.85).aspx switch (pnm->hdr.code){
http://stackoverflow.com/questions/36756094/will-the-device-context-of-a-child-window-have-the-same-text-metrics-and-text-ex
actually I think most if not all of these were from when I was trying to implement uiColorButton on Windows, so I'm not sure if I still need these, but meh...
more miscellaneous
https://blogs.msdn.microsoft.com/oldnewthing/20160328-00/?p=93204 from "One is virtual memory" to "occured to them that their"
https://blogs.msdn.microsoft.com/oldnewthing/20160331-00/?p=93231
https://msdn.microsoft.com/en-us/library/windows/desktop/aa373631(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/ms648395(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/ms648395(v=vs.85).aspx
https://blogs.msdn.microsoft.com/oldnewthing/20101118-00/?p=12253#comment-875273 "MTA implies poor service and fare hikes."
from #gtk+
[18:06:48] <zorcon> if i am doing an animation that needs to be updated at every duration(say every 50ms), should i use get_frame_time() from the frameclock to do that? (from add_tick_callback())
[18:12:47] <~Company> zorcon: yes
windows stuff: mostly aero tricks, sdk version macro notes, ribbon stuff, scrollbar stuff too
https://tools.stefankueng.com/SendMessage.html
https://sourceforge.net/p/sendmessage/code/HEAD/tree/trunk/src/SendMessage.vcxproj
https://sourceforge.net/p/sendmessage/code/HEAD/tree/trunk/src/stdafx.h
https://sourceforge.net/p/sendmessage/code/HEAD/tree/trunk/src/MainDlg.h
https://sourceforge.net/u/steveking/profile/
https://github.com/stefankueng/BowPad/tree/master/src + sourceforge original
https://github.com/stefankueng/sktoolslib/blob/master/AeroControls.cpp + sourceforge original
https://tools.stefankueng.com/BowPad.html
https://www.codeproject.com/Articles/1084/Custom-Scrollbar-Library-version-1-1#_articleTop
https://www.google.com/search?client=firefox-b-1&biw=1080&bih=600&ei=gUHRWsrdMYayggf30bfoAw&q=%22aerocontrols.h%22&oq=%22aerocontrols.h%22&gs_l=psy-ab.3..35i39k1l6.1816.3367.0.3413.4.2.0.0.0.0.0.0..1.0....0...1c.1.64.psy-ab..3.1.211.6...211.0Ppw_OREAk0
https://searchcode.com/codesearch/view/7295148/
https://github.com/TortoiseGit/tortoisesvn/blob/master/src/Utils/MiscUI/AeroBaseDlg.h
https://github.com/TortoiseGit/tortoisesvn/blob/master/src/Utils/MiscUI/AeroControls.cpp
https://github.com/gavioto/stexbar/blob/master/Misc/FileTool/src/CleanVerifyDlg.h
windows ribbon colors and dwm customization
https://github.com/stefankueng/BowPad/blob/master/src/MainWindow.cpp
https://msdn.microsoft.com/en-us/library/windows/desktop/dd940487(v=vs.85).aspx
https://github.com/yohei-yoshihara/GameOfLife3D/blob/master/GameOfLife3DLib/gameoflife3d/Ribbon.cpp
https://msdn.microsoft.com/en-us/library/windows/desktop/dd940404(v=vs.85).aspx
https://github.com/stefankueng/sktoolslib/blob/master/AeroColors.cpp
https://gist.github.com/emoacht/bfa852ccc16bdb5465bd
https://stackoverflow.com/questions/4258295/aero-how-to-draw-solid-opaque-colors-on-glass
https://github.com/ComponentFactory/Krypton
https://www.codeproject.com/Articles/620045/Custom-Controls-in-Win-API-Visual-Styles
https://blogs.msdn.microsoft.com/wpf/2010/11/30/systemcolors-reference/
https://stackoverflow.com/questions/25639621/check-when-a-user-changes-windows-glass-brush-theme-color
https://msdn.microsoft.com/en-us/library/windows/desktop/aa969513(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/aa969527(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/dd388198(v=vs.85).aspx
I hope the MFC ribbon can have customizable colors too, otherwise I'll have to do some serious image processing...
windows debugging
https://msdn.microsoft.com/en-us/library/windows/desktop/hh780351(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/ff476881(v=vs.85).aspx#Debug
https://msdn.microsoft.com/en-us/library/windows/desktop/hh780343(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/dn457937(v=vs.85).aspx
more OS2 stuff
https://www.google.com/search?q=%22ibm+graphics+development+toolkit%22&ie=utf-8&oe=utf-8&client=firefox-b-1
https://www.google.com/search?q=%22os%2F2+graphics+development+toolkit%22&ie=utf-8&oe=utf-8&client=firefox-b-1
http://www.edm2.com/index.php/IBM_OS/2_Developer%27s_Packages
[17:48:52] <Chloe> andlabs: I got splitView and NSWindow size restoration working btw
[17:49:09] <Chloe> andlabs: see https://github.com/eintw1ck/mail/blob/master/Sources/mail/MainViewController.swift for splitView
old stuff
font1.gif (GIF Image, 424 × 475 pixels) http://www.functionx.com/win32/controls/dlgboxes/font1.gif
Inskcapes Hidden Little Feature: Mesh Gradients | OCS-Mag http://www.ocsmag.com/2016/02/27/inskcapes-hidden-little-feature-mesh-gradients/ (near "When youre done colouring in all the nodes, you may want to drag")
https://msdn.microsoft.com/en-us/library/windows/desktop/ms632615(v=vs.85).aspx we need to handle this alongisde WM_CAPTURECHANGED
https://msdn.microsoft.com/en-us/library/windows/desktop/dn424996%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
whatever this is seems to have no documentation... it appears to be related to UWP, if not only Store apps...
unreachable code optimizations
https://docs.microsoft.com/en-us/cpp/intrinsics/assume?view=vs-2017
https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html

View File

@ -1,9 +0,0 @@
https://docs.microsoft.com/en-us/windows/desktop/uxguide/cmd-toolbars
https://docs.microsoft.com/en-us/windows/desktop/controls/cc-faq-iemenubar
https://docs.microsoft.com/en-us/windows/desktop/controls/cc-faq-ietoolbar
https://docs.microsoft.com/en-us/windows/desktop/controls/create-rebar-controls
https://docs.microsoft.com/en-us/windows/desktop/controls/create-toolbars
https://docs.microsoft.com/en-us/windows/desktop/controls/handle-drop-down-buttons
https://docs.microsoft.com/en-us/windows/desktop/controls/tb-buttonstructsize
https://www.google.com/search?q=winapi+toolbar+dropdown+position&ie=utf-8&oe=utf-8&client=firefox-b-1
https://www.google.com/search?q=winapi+toolbar+dropdown+arrow+position&ie=utf-8&oe=utf-8&client=firefox-b-1

View File

@ -1,4 +0,0 @@
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Protocols/NSOutlineViewDataSource_Protocol/index.html#//apple_ref/occ/intfm/NSOutlineViewDataSource/outlineView:child:ofItem:
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/NSOutlineViewDelegate_Protocol/index.html#//apple_ref/occ/intfm/NSOutlineViewDelegate/outlineView:didAddRowView:forRow:
https://developer.apple.com/documentation/appkit/nsoutlineviewdelegate/1528320-outlineview?language=objc
https://github.com/mity/mctrl/blob/master/mctrl/treelist.c around treelist_set_subitem()

View File

@ -1,3 +0,0 @@
http://www.catch22.net/tuts/transparent-text
https://msdn.microsoft.com/en-us/library/windows/desktop/ms724371(v=vs.85).aspx
TODO which color did I want from this list

View File

@ -1,3 +0,0 @@
http://www.os2museum.com/wp/windows-10-arm64-on-qemu/
https://docs.microsoft.com/en-us/windows/uwp/porting/apps-on-arm
http://pete.akeo.ie/2017/05/compiling-desktop-arm-applications-with.html

View File

@ -1,21 +0,0 @@
https://msdn.microsoft.com/en-us/library/windows/desktop/dn469266(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/mt744321(v=vs.85).aspx
!!!! http://stackoverflow.com/questions/41917279/do-child-windows-have-the-same-dpi-as-their-parents-in-a-per-monitor-aware-appli
https://msdn.microsoft.com/en-us/library/windows/desktop/mt843498(v=vs.85).aspx
https://msdn.microsoft.com/library/windows/desktop/mt791579(v=vs.85).aspx
https://blogs.windows.com/buildingapps/2017/04/04/high-dpi-scaling-improvements-desktop-applications-windows-10-creators-update/
https://channel9.msdn.com/Events/Windows/Windows-Developer-Day-Creators-Update/High-DPI-Improvements-for-Desktop-Developers
https://social.msdn.microsoft.com/Forums/vstudio/en-US/31d2a89f-3518-403e-b1e4-bbe37ac1b0f0/per-monitor-high-dpi-awareness-wmdpichanged?forum=vcgeneral
https://stackoverflow.com/questions/36864894/scaling-the-non-client-area-title-bar-menu-bar-for-per-monitor-high-dpi-suppo/37624363
https://stackoverflow.com/questions/41448320/dlgtemplateex-and-ds-shellfont-what-about-point-size
http://stackoverflow.com/questions/41917279/do-child-windows-have-the-same-dpi-as-their-parents-in-a-per-monitor-aware-appli
https://msdn.microsoft.com/en-us/library/windows/desktop/dn469266(v=vs.85).aspx
https://msdn.microsoft.com/library/windows/desktop/mt843498(v=vs.85).aspx(d=robot)
https://msdn.microsoft.com/en-us/library/windows/desktop/dn469266(v=vs.85).aspx#appendix_c_common_high_dpi_issues
https://msdn.microsoft.com/library/windows/desktop/mt843498(v=vs.85).aspx(d=robot)#appendix_c_common_high_dpi_issues
https://msdn.microsoft.com/en-us/library/windows/desktop/dn469266(v=vs.85).aspx#addressing_high_dpi_issues
https://msdn.microsoft.com/library/windows/desktop/mt843498(v=vs.85).aspx(d=robot)#addressing_high_dpi_issues
https://msdn.microsoft.com/en-us/library/windows/desktop/dn302215(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/hh802769(v=vs.85).aspx

View File

@ -1,7 +0,0 @@
https://msdn.microsoft.com/en-us/library/windows/desktop/ff686805(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/dn495653(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/hh448422(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/dd316975(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/dd372919(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/ee264335(v=vs.85).aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/dd145198(v=vs.85).aspx

View File

@ -1,46 +0,0 @@
https://twitter.com/omgubuntu/status/962765197109841922
https://github.com/DominicMaas/SoundByte/blob/master/SoundByte.UWP/Resources/Brushes.xaml
https://docs.microsoft.com/en-us/windows/uwp/design/style/acrylic
https://www.google.com/search?q=uwp+acrylic+winapi&ie=utf-8&oe=utf-8&client=firefox-b-1
https://stackoverflow.com/questions/43931709/acrylic-material-in-win32-app
https://stackoverflow.com/questions/44000217/mimicking-acrylic-in-a-win32-app
https://stackoverflow.com/questions/43699256/how-to-use-acrylic-accent-in-windows-10-creators-update
https://stackoverflow.com/questions/39135834/how-to-call-uwp-api-from-converted-win32-app-desktop-app-converter
https://stackoverflow.com/questions/32724187/how-do-you-set-the-glass-blend-colour-on-windows-10
https://channel9.msdn.com/Events/Build/2017/B8034
https://www.reddit.com/r/Windows10/comments/7lzyzc/since_the_taskbar_is_still_win32_and_it_can_have/
https://thenextweb.com/microsoft/2017/05/15/microsoft-fluent-design-system-breaking-windows-10s-new-look/
https://github.com/bbougot/AcrylicWPF
http://vhanla.codigobit.info/2015/07/enable-windows-10-aero-glass-aka-blur.html
http://undoc.airesoft.co.uk/user32.dll/SetWindowCompositionAttribute.php
http://undoc.airesoft.co.uk/user32.dll/GetWindowCompositionAttribute.php
https://msdn.microsoft.com/en-us/library/windows/desktop/aa969530(v=vs.85).aspx
https://github.com/bbougot/AcrylicWPF/blob/master/MainWindow.xaml.cs
https://www.google.com/search?q=%22WCA_ACCENT_POLICY%22&client=firefox-b-1&source=lnt&tbs=cdr%3A1%2Ccd_min%3A1%2F1%2F2001%2Ccd_max%3A12%2F31%2F2013&tbm=
https://github.com/sgothel/jogl/blob/master/src/nativewindow/native/win32/WindowsDWM.c
http://www.brandonfa.lk/win8/win8_devrel_head_x86/uxtheme.h
http://www.brandonfa.lk/win8/
https://github.com/gamozolabs/pdblister
https://github.com/wbenny/pdbex
https://github.com/wbenny/pdbex/releases
https://stackoverflow.com/questions/44000217/mimicking-acrylic-in-a-win32-app
https://github.com/riverar/sample-win32-acrylicblur
https://github.com/riverar/sample-win10-aeroglass
https://guerra24blog.wordpress.com/2017/12/20/windows-10-use-acrylic-in-win32-apps/
https://news.ycombinator.com/item?id=14432951
https://gist.github.com/ethanhs/0e157e4003812e99bf5bc7cb6f73459f ACCENTPOLICY
https://github.com/TranslucentTB/Tools
https://withinrafael.com/2015/07/08/adding-the-aero-glass-blur-to-your-windows-10-apps/
https://withinrafael.com/2018/02/01/adding-acrylic-blur-to-your-windows-10-apps-redstone-4-desktop-apps/
diversion: DwmEnableBlurBehindWindow {
https://docs.microsoft.com/en-us/windows/desktop/api/dwmapi/nf-dwmapi-dwmenableblurbehindwindow
https://docs.microsoft.com/en-us/windows/desktop/api/dwmapi/ns-dwmapi-_dwm_blurbehind
https://docs.microsoft.com/en-us/windows/desktop/api/dwmapi/nf-dwmapi-dwmenableblurbehindwindow
http://www.danielmoth.com/Blog/Vista-Glass-Answers-And-DwmEnableBlurBehindWindow.aspx
http://www.danielmoth.com/Blog/Glass-In-C-An-Alternative-Approach.aspx
http://www.danielmoth.com/Blog/Vista-Glass-In-C.aspx
http://www.danielmoth.com/Blog/Glass-In-C-An-Alternative-Approach.aspx
https://weblogs.asp.net/kennykerr/Windows-Vista-for-Developers-_1320_-Part-3-_1320_-The-Desktop-Window-Manager
}

View File

@ -1,6 +0,0 @@
# TODO is there a -Wno-switch equivalent?
# TODO /sdl turns C4996 into an ERROR
# don't use /analyze; that requires us to write annotations everywhere
# TODO undecided flags from qo?
# the RTC flags are only supplied in debug builds because they are only supposed to be used by debug builds (see "This is because run-time error checks are not valid in a release (optimized) build." in https://docs.microsoft.com/cpp/build/reference/rtc-run-time-error-checks)
# /RTCc is not supplied because it's discouraged as of VS2015; see https://www.reddit.com/r/cpp/comments/46mhne/rtcc_rejects_conformant_code_with_visual_c_2015/d06auq5

View File

@ -1,92 +0,0 @@
// 10 february 2017
#include "../ui.h"
#include "uipriv.h"
// TODO this doesn't handle the case where nLines == 0
// TODO this should never happen even if there are no characters??
// TODO figure out how to make this work on GTK+
void uiDrawCaret(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t pos, int *line)
{
double xoff;
uiDrawTextLayoutLineMetrics m;
struct caretDrawParams cdp;
uiDrawPath *path;
uiDrawBrush brush;
if (*line < 0)
*line = 0;
if (*line > (uiDrawTextLayoutNumLines(layout) - 1))
*line = (uiDrawTextLayoutNumLines(layout) - 1);
// TODO cap pos
xoff = uiDrawTextLayoutByteLocationInLine(layout, pos, *line);
while (xoff < 0) {
size_t start, end;
uiDrawTextLayoutLineByteRange(layout, *line, &start, &end);
if (end < pos) // too far up
(*line)++;
else
(*line)--;
xoff = uiDrawTextLayoutByteLocationInLine(layout, pos, *line);
}
uiDrawTextLayoutLineGetMetrics(layout, *line, &m);
caretDrawParams(c, m.Height, &cdp);
uiDrawSave(c);
path = uiDrawNewPath(uiDrawFillModeWinding);
uiDrawPathAddRectangle(path,
// TODO add m.X?
x + xoff - cdp.xoff, y + m.Y,
cdp.width, m.Height);
uiDrawPathEnd(path);
brush.Type = uiDrawBrushTypeSolid;
brush.R = cdp.r;
brush.G = cdp.g;
brush.B = cdp.b;
brush.A = cdp.a;
uiDrawFill(c, path, &brush);
uiDrawFreePath(path);
uiDrawRestore(c);
}
void drawTextBackground(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t start, size_t end, uiDrawBrush *brush, int isSelection)
{
int line, nLines;
size_t lstart, lend;
double layoutwid, layoutht;
uiDrawTextLayoutExtents(layout, &layoutwid, &layoutht);
nLines = uiDrawTextLayoutNumLines(layout);
for (line = 0; line < nLines; line++) {
uiDrawTextLayoutLineByteRange(layout, line, &lstart, &lend);
if (start >= lstart && start < lend)
break;
}
while (end - start > 0) {
uiDrawTextLayoutLineMetrics m;
double startx, endx;
uiDrawPath *path;
uiDrawTextLayoutLineByteRange(layout, line, &lstart, &lend);
if (lend > end) // don't cross lines
lend = end;
startx = uiDrawTextLayoutByteLocationInLine(layout, start, line);
// TODO explain this; note the use of start with lend
endx = layoutwid;
if (!isSelection || end <= lend)
endx = uiDrawTextLayoutByteLocationInLine(layout, lend, line);
uiDrawTextLayoutLineGetMetrics(layout, line, &m);
path = uiDrawNewPath(uiDrawFillModeWinding);
uiDrawPathAddRectangle(path,
x + startx, y + m.Y,
endx - startx, m.Height);
uiDrawPathEnd(path);
uiDrawFill(c, path, brush);
uiDrawFreePath(path);
line++;
start = lend;
}
}

View File

@ -1,15 +0,0 @@
// TODO split these into a separate header file?
// drawtext.c
struct caretDrawParams {
double r;
double g;
double b;
double a;
double xoff;
double width;
};
extern void caretDrawParams(uiDrawContext *c, double height, struct caretDrawParams *p);
extern void drawTextBackground(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t start, size_t end, uiDrawBrush *brush, int isSelection);

View File

@ -1,347 +0,0 @@
// 2 january 2017
#import "uipriv_darwin.h"
#import "draw.h"
@interface lineInfo : NSObject
@property NSRange glyphRange;
@property NSRange characterRange;
@property NSRect lineRect;
@property NSRect lineUsedRect;
@property NSRect glyphBoundingRect;
@property CGFloat baselineOffset;
@property double ascent;
@property double descent;
@property double leading;
@end
@implementation lineInfo
@end
struct uiDrawTextLayout {
// NSTextStorage is subclassed from NSMutableAttributedString
NSTextStorage *attrstr;
NSTextContainer *container;
NSLayoutManager *layoutManager;
// the width as passed into uiDrawTextLayout constructors
double width;
#if 0 /* TODO */
// the *actual* size of the frame
// note: technically, metrics returned from frame are relative to CGPathGetPathBoundingBox(tl->path)
// however, from what I can gather, for a path created by CGPathCreateWithRect(), like we do (with a NULL transform), CGPathGetPathBoundingBox() seems to just return the standardized form of the rect used to create the path
// (this I confirmed through experimentation)
// so we can just use tl->size for adjustments
// we don't need to adjust coordinates by any origin since our rect origin is (0, 0)
CGSize size;
#endif
NSMutableArray<lineInfo *> *lineInfo;
// for converting CFAttributedString indices to byte offsets
size_t *u16tou8;
size_t nu16tou8; // TODO I don't like the casing of this name
};
static NSFont *fontdescToNSFont(uiDrawFontDescriptor *fd)
{
NSFontDescriptor *desc;
NSFont *font;
desc = fontdescToNSFontDescriptor(fd);
font = [NSFont fontWithDescriptor:desc size:fd->Size];
[desc release];
return font;
}
static NSTextStorage *attrstrToTextStorage(uiAttributedString *s, uiDrawFontDescriptor *defaultFont)
{
NSString *nsstr;
NSMutableDictionary *defaultAttrs;
NSTextStorage *attrstr;
nsstr = [[NSString alloc] initWithCharacters:attrstrUTF16(s)
length:attrstrUTF16Len(s)];
defaultAttrs = [NSMutableDictionary new];
[defaultAttrs setObject:fontdescToNSFont(defaultFont)
forKey:NSFontAttributeName];
attrstr = [[NSTextStorage alloc] initWithString:nsstr
attributes:defaultAttrs];
[defaultAttrs release];
[nsstr release];
[attrstr beginEditing];
// TODO copy in the attributes
[attrstr endEditing];
return attrstr;
}
// TODO fine-tune all the properties
uiDrawTextLayout *uiDrawNewTextLayout(uiAttributedString *s, uiDrawFontDescriptor *defaultFont, double width)
{
uiDrawTextLayout *tl;
CGFloat cgwidth;
// TODO correct type?
NSUInteger index;
tl = uiNew(uiDrawTextLayout);
tl->attrstr = attrstrToTextStorage(s, defaultFont);
tl->width = width;
// TODO the documentation on the size property implies this might not be necessary?
cgwidth = (CGFloat) width;
if (cgwidth < 0)
cgwidth = CGFLOAT_MAX;
// TODO rename to tl->textContainer
tl->container = [[NSTextContainer alloc] initWithSize:NSMakeSize(cgwidth, CGFLOAT_MAX)];
// TODO pull the reference for this
[tl->container setLineFragmentPadding:0];
tl->layoutManager = [[NSLayoutManager alloc] init];
[tl->layoutManager setTypesetterBehavior:NSTypesetterLatestBehavior];
[tl->layoutManager addTextContainer:tl->container];
[tl->attrstr addLayoutManager:tl->layoutManager];
// and force a re-layout (TODO get source
[tl->layoutManager glyphRangeForTextContainer:tl->container];
// TODO equivalent of CTFrameProgression for RTL/LTR?
// now collect line information; see https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/TextLayout/Tasks/CountLines.html
tl->lineInfo = [NSMutableArray<lineInfo *> new];
index = 0;
while (index < [tl->layoutManager numberOfGlyphs]) {
NSRange glyphRange;
__block lineInfo *li;
li = [lineInfo new];
li.lineRect = [tl->layoutManager lineFragmentRectForGlyphAtIndex:index effectiveRange:&glyphRange];
li.glyphRange = glyphRange;
li.characterRange = [tl->layoutManager characterRangeForGlyphRange:li.glyphRange actualGlyphRange:NULL];
li.lineUsedRect = [tl->layoutManager lineFragmentUsedRectForGlyphAtIndex:index effectiveRange:NULL];
li.glyphBoundingRect = [tl->layoutManager boundingRectForGlyphRange:li.glyphRange inTextContainer:tl->container];
// and this is from http://www.cocoabuilder.com/archive/cocoa/308568-how-to-get-baseline-info.html and http://www.cocoabuilder.com/archive/cocoa/199283-height-and-location-of-text-within-line-in-nslayoutmanager-ignoring-spacing.html
li.baselineOffset = [[tl->layoutManager typesetter] baselineOffsetInLayoutManager:tl->layoutManager glyphIndex:index];
li.ascent = 0;
li.descent = 0;
li.leading = 0;
// imitate what AppKit actually does (or seems to)
[tl->attrstr enumerateAttributesInRange:li.characterRange options:0 usingBlock:^(NSDictionary<NSString *,id> *attrs, NSRange range, BOOL *stop) {
NSFont *f;
NSRect boundingRect;
double v, realAscent, realDescent, realLeading;
BOOL skipAdjust, doAdjust;
f = (NSFont *) [attrs objectForKey:NSFontAttributeName];
if (f == nil) {
f = [NSFont fontWithName:@"Helvetica" size:12.0];
if (f == nil)
f = [NSFont systemFontOfSize:12.0];
}
boundingRect = [f boundingRectForFont];
realAscent = [f ascender];
realDescent = -[f descender];
realLeading = [f leading];
skipAdjust = NO;
doAdjust = NO;
if (NSMaxY(boundingRect) <= realAscent) {
// ascent entirely within bounding box
// don't do anything if there's leading; I'm guessing it's a combination of both of the reasons to skip below... (least sure of this one)
if (realLeading != 0)
skipAdjust = YES;
// does the descent slip outside the bounding box?
if (-realDescent <= NSMinY(boundingRect))
// yes I guess we should assume accents don't collide with the previous line's descent, though I'm not as sure of that as I am about the else clause below...
skipAdjust = YES;
} else {
// ascent outside bounding box ascent does not include accents
// only skip adjustment if there isn't leading (apparently some fonts use the previous line's leading for accents? :/ )
if (realLeading != 0)
skipAdjust = YES;
}
if (!skipAdjust) {
UniChar ch = 0xC0;
CGGlyph glyph;
// there does not seem to be an AppKit API for this...
if (CTFontGetGlyphsForCharacters((CTFontRef) f, &ch, &glyph, 1) != false) {
NSRect bbox;
bbox = [f boundingRectForGlyph:glyph];
if (NSMaxY(bbox) > realAscent)
doAdjust = YES;
if (-realDescent > NSMinY(bbox))
doAdjust = YES;
}
}
// TODO vertical
v = [f ascender];
// TODO get this one back out
if (doAdjust)
v += 0.2 * ([f ascender] + [f descender]);
//v = floor(v + 0.5);
if (li.ascent < v)
li.ascent = v;
v = -[f descender];// floor(-[f descender] + 0.5);
if (li.descent < v)
li.descent = v;
v = [f leading];//floor([f leading] + 0.5);
if (li.leading < v)
li.leading = v;
}];
li.ascent = floor(li.ascent + 0.5);
li.descent = floor(li.descent + 0.5);
li.leading = floor(li.leading + 0.5);
[tl->lineInfo addObject:li];
[li release];
index = glyphRange.location + glyphRange.length;
}
// and finally copy the UTF-16 to UTF-8 index conversion table
tl->u16tou8 = attrstrCopyUTF16ToUTF8(s, &(tl->nu16tou8));
return tl;
}
void uiDrawFreeTextLayout(uiDrawTextLayout *tl)
{
uiFree(tl->u16tou8);
[tl->lineInfo release];
[tl->layoutManager release];
[tl->container release];
[tl->attrstr release];
uiFree(tl);
}
// TODO document that (x,y) is the top-left corner of the *entire frame*
void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y)
{
NSGraphicsContext *gc;
CGContextFlush(c->c); // just to be safe
[NSGraphicsContext saveGraphicsState];
gc = [NSGraphicsContext graphicsContextWithGraphicsPort:c->c flipped:YES];
[NSGraphicsContext setCurrentContext:gc];
// TODO is this the right point?
// TODO does this draw with the proper default styles?
[tl->layoutManager drawGlyphsForGlyphRange:[tl->layoutManager glyphRangeForTextContainer:tl->container]
atPoint:NSMakePoint(x, y)];
[gc flushGraphics]; // just to be safe
[NSGraphicsContext restoreGraphicsState];
// TODO release gc?
}
// TODO update all of these {
// TODO document that the width and height of a layout is not necessarily the sum of the widths and heights of its constituent lines; this is definitely untrue on OS X, where lines are placed in such a way that the distance between baselines is always integral
// TODO width doesn't include trailing whitespace...
// TODO figure out how paragraph spacing should play into this
// }
void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height)
{
NSRect r;
// see https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/TextLayout/Tasks/StringHeight.html
r = [tl->layoutManager usedRectForTextContainer:tl->container];
*width = r.size.width;
*height = r.size.height;
}
int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl)
{
return [tl->lineInfo count];
}
void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end)
{
lineInfo *li;
li = (lineInfo *) [tl->lineInfo objectAtIndex:line];
*start = tl->u16tou8[li.characterRange.location];
*end = tl->u16tou8[li.characterRange.location + li.characterRange.length];
}
void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m)
{
lineInfo *li;
li = (lineInfo *) [tl->lineInfo objectAtIndex:line];
m->X = li.lineRect.origin.x;
m->Y = li.lineRect.origin.y;
// if we use li.lineRect here we get the whole line, not just the part with stuff in it
m->Width = li.lineUsedRect.size.width;
m->Height = li.lineRect.size.height;
// TODO is this correct?
m->BaselineY = (m->Y + m->Height) - li.baselineOffset;
m->Ascent = li.ascent;
m->Descent = li.descent;
m->Leading = li.leading;
// TODO
m->ParagraphSpacingBefore = 0;
m->LineHeightSpace = 0;
m->LineSpacing = 0;
m->ParagraphSpacing = 0;
}
void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, uiDrawTextLayoutHitTestResult *result)
{
#if 0 /* TODO */
CFIndex i;
CTLineRef line;
CFIndex pos;
if (y >= 0) {
for (i = 0; i < tl->nLines; i++) {
double ltop, lbottom;
ltop = tl->lineMetrics[i].Y;
lbottom = ltop + tl->lineMetrics[i].Height;
if (y >= ltop && y < lbottom)
break;
}
result->YPosition = uiDrawTextLayoutHitTestPositionInside;
if (i == tl->nLines) {
i--;
result->YPosition = uiDrawTextLayoutHitTestPositionAfter;
}
} else {
i = 0;
result->YPosition = uiDrawTextLayoutHitTestPositionBefore;
}
result->Line = i;
result->XPosition = uiDrawTextLayoutHitTestPositionInside;
if (x < tl->lineMetrics[i].X) {
result->XPosition = uiDrawTextLayoutHitTestPositionBefore;
// and forcibly return the first character
x = tl->lineMetrics[i].X;
} else if (x > (tl->lineMetrics[i].X + tl->lineMetrics[i].Width)) {
result->XPosition = uiDrawTextLayoutHitTestPositionAfter;
// and forcibly return the last character
x = tl->lineMetrics[i].X + tl->lineMetrics[i].Width;
}
line = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, i);
// TODO copy the part from the docs about this point
pos = CTLineGetStringIndexForPosition(line, CGPointMake(x, 0));
if (pos == kCFNotFound) {
// TODO
}
result->Pos = tl->u16tou8[pos];
#endif
}
void uiDrawTextLayoutByteRangeToRectangle(uiDrawTextLayout *tl, size_t start, size_t end, uiDrawTextLayoutByteRangeRectangle *r)
{
}

View File

@ -1,223 +0,0 @@
// 3 january 2017
#import "uipriv_darwin.h"
// Stupidity: Core Text requires an **exact match for the entire traits dictionary**, otherwise it will **drop ALL the traits**.
// This seems to be true for every function in Core Text that advertises that it performs font matching; I can confirm at the time of writing this is the case for
// - CTFontDescriptorCreateMatchingFontDescriptors() (though the conditions here seem odd)
// - CTFontCreateWithFontDescriptor()
// - CTFontCreateCopyWithAttributes()
// And as a bonus prize, this also applies to Cocoa's NSFontDescriptor methods as well!
// We have to implement the closest match ourselves.
// This needs to be done early in the matching process; in particular, we have to do this before adding any features (such as small caps), because the matching descriptors won't have those.
struct closeness {
NSUInteger index;
double weight;
double italic;
double stretch;
double distance;
};
static double doubleAttr(NSDictionary *traits, NSString *attr)
{
NSNumber *n;
n = (NSNumber *) [traits objectForKey:attr];
return [n doubleValue];
}
struct italicCloseness {
double normal;
double oblique;
double italic;
};
// remember that in closeness, 0 means exact
// in this case, since we define the range, we use 0.5 to mean "close enough" (oblique for italic and italic for oblique) and 1 to mean "not a match"
static const struct italicCloseness italicClosenesses[] = {
[uiDrawTextItalicNormal] = { 0, 1, 1 },
[uiDrawTextItalicOblique] = { 1, 0, 0.5 },
[uiDrawTextItalicItalic] = { 1, 0.5, 0 },
};
// Italics are hard because Core Text does NOT distinguish between italic and oblique.
// All Core Text provides is a slant value and the italic bit of the symbolic traits mask.
// However, Core Text does seem to guarantee (from experimentation; see below) that the slant will be nonzero if and only if the italic bit is set, so we don't need to use the slant value.
// Core Text also seems to guarantee that if a font lists itself as Italic or Oblique by name (font subfamily name, font style name, whatever), it will also have that bit set, so testing this bit does cover all fonts that name themselves as Italic and Oblique. (Again, this is from the below experimentation.)
// TODO there is still one catch that might matter from a user's POV: the reverse is not true the italic bit can be set even if the style of the font face/subfamily/style isn't named as Italic (for example, script typefaces like Adobe's Palace Script MT Std); I don't know what to do about this... I know how to start: find a script font that has an italic form (Adobe's Palace Script MT Std does not; only Regular and Semibold)
// TODO see if the above applies to Cocoa as well
static double italicCloseness(NSFontDescriptor *desc, NSDictionary *traits, uiDrawTextItalic italic)
{
const struct italicCloseness *ic = &(italicClosenesses[italic]);
NSNumber *num;
NSFontSymbolicTraits symbolic;
NSString *styleName;
NSRange range;
BOOL isOblique;
num = (NSNumber *) [traits objectForKey:NSFontSymbolicTrait];
// TODO this should really be a uint32_t-specific one
symbolic = (NSFontSymbolicTraits) [num unsignedIntegerValue];
if ((symbolic & NSFontItalicTrait) == 0)
return ic->normal;
// Okay, now we know it's either Italic or Oblique
// Pango's Core Text code just does a g_strrstr() (backwards case-sensitive search) for "Oblique" in the font's style name (see https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c); let's do that too I guess
// note that NSFontFaceAttribute is the Cocoa equivalent of the style name
isOblique = NO; // default value
styleName = (NSString *) [desc objectForKey:NSFontFaceAttribute];
// TODO is styleName guaranteed?
// TODO NSLiteralSearch?
range = [styleName rangeOfString:@"Oblique" options:NSCaseInsensitiveSearch];
if (range.location != NSNotFound)
return ic->oblique;
return ic->italic;
}
static NSFontDescriptor *matchTraits(NSFontDescriptor *against, double targetWeight, uiDrawTextItalic targetItalic, double targetStretch)
{
NSArray<NSFontDescriptor *> *matching;
NSUInteger i, n;
struct closeness *closeness;
NSFontDescriptor *current;
NSFontDescriptor *out;
matching = [against matchingFontDescriptorsWithMandatoryKeys:nil];
if (matching == nil)
// no matches; give the original back and hope for the best
return against;
n = [matching count];
if (n == 0) {
// likewise
//TODO [matching release];
return against;
}
closeness = (struct closeness *) uiAlloc(n * sizeof (struct closeness), "struct closeness[]");
for (i = 0; i < n; i++) {
NSDictionary *traits;
closeness[i].index = i;
current = (NSFontDescriptor *) [matching objectAtIndex:i];
traits = (NSDictionary *) [current objectForKey:NSFontTraitsAttribute];
if (traits == nil) {
// couldn't get traits; be safe by ranking it lowest
// LONGTERM figure out what the longest possible distances are
closeness[i].weight = 3;
closeness[i].italic = 2;
closeness[i].stretch = 3;
continue;
}
closeness[i].weight = doubleAttr(traits, NSFontWeightTrait) - targetWeight;
closeness[i].italic = italicCloseness(current, traits, targetItalic);
closeness[i].stretch = doubleAttr(traits, NSFontWidthTrait) - targetStretch;
// TODO release traits?
}
// now figure out the 3-space difference between the three and sort by that
// TODO merge this loop with the previous loop?
for (i = 0; i < n; i++) {
double weight, italic, stretch;
weight = closeness[i].weight;
weight *= weight;
italic = closeness[i].italic;
italic *= italic;
stretch = closeness[i].stretch;
stretch *= stretch;
closeness[i].distance = sqrt(weight + italic + stretch);
}
qsort_b(closeness, n, sizeof (struct closeness), ^(const void *aa, const void *bb) {
const struct closeness *a = (const struct closeness *) aa;
const struct closeness *b = (const struct closeness *) bb;
// via http://www.gnu.org/software/libc/manual/html_node/Comparison-Functions.html#Comparison-Functions
// LONGTERM is this really the best way? isn't it the same as if (*a < *b) return -1; if (*a > *b) return 1; return 0; ?
return (a->distance > b->distance) - (a->distance < b->distance);
});
// and the first element of the sorted array is what we want
out = (NSFontDescriptor *) [matching objectAtIndex:closeness[0].index];
// TODO is this correct?
[out retain]; // get rule
// release everything
uiFree(closeness);
//TODO [matching release];
// and release the original descriptor since we no longer need it
[against release];
return out;
}
// since uiDrawTextWeight effectively corresponds to OS/2 weights (which roughly correspond to GDI, Pango, and DirectWrite weights, and to a lesser(? TODO) degree, CSS weights), let's just do what Core Text does with OS/2 weights
// TODO this will not be correct for system fonts, which use cached values that have no relation to the OS/2 weights; we need to figure out how to reconcile these
// for more information, see https://bugzilla.gnome.org/show_bug.cgi?id=766148 and TODO_put_blog_post_here_once_I_write_it (TODO keep this line when resolving the above TODO)
static const double weightsToCTWeights[] = {
-1.0, // 0..99
-0.7, // 100..199
-0.5, // 200..299
-0.23, // 300..399
0.0, // 400..499
0.2, // 500..599
0.3, // 600..699
0.4, // 700..799
0.6, // 800..899
0.8, // 900..999
1.0, // 1000
};
static double weightToCTWeight(uiDrawTextWeight weight)
{
int weightClass;
double ctclass;
double rest, weightFloor, nextFloor;
if (weight <= 0)
return -1.0;
if (weight >= 1000)
return 1.0;
weightClass = weight / 100;
rest = (double) weight;
weightFloor = (double) (weightClass * 100);
nextFloor = (double) ((weightClass + 1) * 100);
rest = (rest - weightFloor) / (nextFloor - weightFloor);
ctclass = weightsToCTWeights[weightClass];
return fma(rest,
weightsToCTWeights[weightClass + 1] - ctclass,
ctclass);
}
// based on what Core Text says about actual fonts (system fonts, system fonts in another folder to avoid using cached values, Adobe Font Folio 11, Google Fonts archive, fonts in Windows 7/8.1/10)
static const double stretchesToCTWidths[] = {
[uiDrawTextStretchUltraCondensed] = -0.400000,
[uiDrawTextStretchExtraCondensed] = -0.300000,
[uiDrawTextStretchCondensed] = -0.200000,
[uiDrawTextStretchSemiCondensed] = -0.100000,
[uiDrawTextStretchNormal] = 0.000000,
[uiDrawTextStretchSemiExpanded] = 0.100000,
[uiDrawTextStretchExpanded] = 0.200000,
[uiDrawTextStretchExtraExpanded] = 0.300000,
// this one isn't present in any of the fonts I tested, but it follows naturally from the pattern of the rest, so... (TODO verify by checking the font files directly)
[uiDrawTextStretchUltraExpanded] = 0.400000,
};
NSFontDescriptor *fontdescToNSFontDescriptor(uiDrawFontDescriptor *fd)
{
NSMutableDictionary *attrs;
NSFontDescriptor *basedesc;
attrs = [NSMutableDictionary new];
[attrs setObject:[NSString stringWithUTF8String:fd->Family]
forKey:NSFontFamilyAttribute];
[attrs setObject:[NSNumber numberWithDouble:fd->Size]
forKey:NSFontSizeAttribute];
basedesc = [[NSFontDescriptor alloc] initWithFontAttributes:attrs];
[attrs release];
return matchTraits(basedesc,
weightToCTWeight(fd->Weight),
fd->Italic,
stretchesToCTWidths[fd->Stretch]);
}

View File

@ -1,268 +0,0 @@
// 6 september 2015
#import "uipriv_darwin.h"
// TODO double-check that we are properly handling allocation failures (or just toll free bridge from cocoa)
struct uiDrawFontFamilies {
CFArrayRef fonts;
};
uiDrawFontFamilies *uiDrawListFontFamilies(void)
{
uiDrawFontFamilies *ff;
ff = uiNew(uiDrawFontFamilies);
ff->fonts = CTFontManagerCopyAvailableFontFamilyNames();
if (ff->fonts == NULL)
implbug("error getting available font names (no reason specified) (TODO)");
return ff;
}
int uiDrawFontFamiliesNumFamilies(uiDrawFontFamilies *ff)
{
return CFArrayGetCount(ff->fonts);
}
char *uiDrawFontFamiliesFamily(uiDrawFontFamilies *ff, int n)
{
CFStringRef familystr;
char *family;
familystr = (CFStringRef) CFArrayGetValueAtIndex(ff->fonts, n);
// toll-free bridge
family = uiDarwinNSStringToText((NSString *) familystr);
// Get Rule means we do not free familystr
return family;
}
void uiDrawFreeFontFamilies(uiDrawFontFamilies *ff)
{
CFRelease(ff->fonts);
uiFree(ff);
}
struct uiDrawTextFont {
CTFontRef f;
};
uiDrawTextFont *mkTextFont(CTFontRef f, BOOL retain)
{
uiDrawTextFont *font;
font = uiNew(uiDrawTextFont);
font->f = f;
if (retain)
CFRetain(font->f);
return font;
}
uiDrawTextFont *mkTextFontFromNSFont(NSFont *f)
{
// toll-free bridging; we do retain, though
return mkTextFont((CTFontRef) f, YES);
}
static CFMutableDictionaryRef newAttrList(void)
{
CFMutableDictionaryRef attr;
attr = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (attr == NULL)
complain("error creating attribute dictionary in newAttrList()()");
return attr;
}
static void addFontFamilyAttr(CFMutableDictionaryRef attr, const char *family)
{
CFStringRef cfstr;
cfstr = CFStringCreateWithCString(NULL, family, kCFStringEncodingUTF8);
if (cfstr == NULL)
complain("error creating font family name CFStringRef in addFontFamilyAttr()");
CFDictionaryAddValue(attr, kCTFontFamilyNameAttribute, cfstr);
CFRelease(cfstr); // dictionary holds its own reference
}
static void addFontSizeAttr(CFMutableDictionaryRef attr, double size)
{
CFNumberRef n;
n = CFNumberCreate(NULL, kCFNumberDoubleType, &size);
CFDictionaryAddValue(attr, kCTFontSizeAttribute, n);
CFRelease(n);
}
#if 0
TODO
// See http://stackoverflow.com/questions/4810409/does-coretext-support-small-caps/4811371#4811371 and https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c for what these do
// And fortunately, unlike the traits (see below), unmatched features are simply ignored without affecting the other features :D
static void addFontSmallCapsAttr(CFMutableDictionaryRef attr)
{
CFMutableArrayRef outerArray;
CFMutableDictionaryRef innerDict;
CFNumberRef numType, numSelector;
int num;
outerArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
if (outerArray == NULL)
complain("error creating outer CFArray for adding small caps attributes in addFontSmallCapsAttr()");
// Apple's headers say these are deprecated, but a few fonts still rely on them
num = kLetterCaseType;
numType = CFNumberCreate(NULL, kCFNumberIntType, &num);
num = kSmallCapsSelector;
numSelector = CFNumberCreate(NULL, kCFNumberIntType, &num);
innerDict = newAttrList();
CFDictionaryAddValue(innerDict, kCTFontFeatureTypeIdentifierKey, numType);
CFRelease(numType);
CFDictionaryAddValue(innerDict, kCTFontFeatureSelectorIdentifierKey, numSelector);
CFRelease(numSelector);
CFArrayAppendValue(outerArray, innerDict);
CFRelease(innerDict); // and likewise for CFArray
// these are the non-deprecated versions of the above; some fonts have these instead
num = kLowerCaseType;
numType = CFNumberCreate(NULL, kCFNumberIntType, &num);
num = kLowerCaseSmallCapsSelector;
numSelector = CFNumberCreate(NULL, kCFNumberIntType, &num);
innerDict = newAttrList();
CFDictionaryAddValue(innerDict, kCTFontFeatureTypeIdentifierKey, numType);
CFRelease(numType);
CFDictionaryAddValue(innerDict, kCTFontFeatureSelectorIdentifierKey, numSelector);
CFRelease(numSelector);
CFArrayAppendValue(outerArray, innerDict);
CFRelease(innerDict); // and likewise for CFArray
CFDictionaryAddValue(attr, kCTFontFeatureSettingsAttribute, outerArray);
CFRelease(outerArray);
}
#endif
#if 0
// Named constants for these were NOT added until 10.11, and even then they were added as external symbols instead of macros, so we can't use them directly :(
// kode54 got these for me before I had access to El Capitan; thanks to him.
#define ourNSFontWeightUltraLight -0.800000
#define ourNSFontWeightThin -0.600000
#define ourNSFontWeightLight -0.400000
#define ourNSFontWeightRegular 0.000000
#define ourNSFontWeightMedium 0.230000
#define ourNSFontWeightSemibold 0.300000
#define ourNSFontWeightBold 0.400000
#define ourNSFontWeightHeavy 0.560000
#define ourNSFontWeightBlack 0.620000
#endif
// Now remember what I said earlier about having to add the small caps traits after calling the above? This gets a dictionary back so we can do so.
CFMutableDictionaryRef extractAttributes(CTFontDescriptorRef desc)
{
CFDictionaryRef dict;
CFMutableDictionaryRef mdict;
dict = CTFontDescriptorCopyAttributes(desc);
// this might not be mutable, so make a mutable copy
mdict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
CFRelease(dict);
return mdict;
}
uiDrawTextFont *uiDrawLoadClosestFont(const uiDrawTextFontDescriptor *desc)
{
CTFontRef f;
CFMutableDictionaryRef attr;
CTFontDescriptorRef cfdesc;
attr = newAttrList();
addFontFamilyAttr(attr, desc->Family);
addFontSizeAttr(attr, desc->Size);
// now we have to do the traits matching, so create a descriptor, match the traits, and then get the attributes back
cfdesc = CTFontDescriptorCreateWithAttributes(attr);
// TODO release attr?
cfdesc = matchTraits(cfdesc, desc->Weight, desc->Italic, desc->Stretch);
// specify the initial size again just to be safe
f = CTFontCreateWithFontDescriptor(cfdesc, desc->Size, NULL);
// TODO release cfdesc?
return mkTextFont(f, NO); // we hold the initial reference; no need to retain again
}
void uiDrawFreeTextFont(uiDrawTextFont *font)
{
CFRelease(font->f);
uiFree(font);
}
uintptr_t uiDrawTextFontHandle(uiDrawTextFont *font)
{
return (uintptr_t) (font->f);
}
void uiDrawTextFontDescribe(uiDrawTextFont *font, uiDrawTextFontDescriptor *desc)
{
// TODO
}
// text sizes and user space points are identical:
// - https://developer.apple.com/library/mac/documentation/TextFonts/Conceptual/CocoaTextArchitecture/TypoFeatures/TextSystemFeatures.html#//apple_ref/doc/uid/TP40009459-CH6-51627-BBCCHIFF text points are 72 per inch
// - https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CocoaDrawingGuide/Transforms/Transforms.html#//apple_ref/doc/uid/TP40003290-CH204-SW5 user space points are 72 per inch
void uiDrawTextFontGetMetrics(uiDrawTextFont *font, uiDrawTextFontMetrics *metrics)
{
metrics->Ascent = CTFontGetAscent(font->f);
metrics->Descent = CTFontGetDescent(font->f);
metrics->Leading = CTFontGetLeading(font->f);
metrics->UnderlinePos = CTFontGetUnderlinePosition(font->f);
metrics->UnderlineThickness = CTFontGetUnderlineThickness(font->f);
}
// LONGTERM allow line separation and leading to be factored into a wrapping text layout
// TODO reconcile differences in character wrapping on platforms
void uiDrawTextLayoutExtents(uiDrawTextLayout *layout, double *width, double *height)
{
struct framesetter fs;
mkFramesetter(layout, &fs);
*width = fs.extents.width;
*height = fs.extents.height;
freeFramesetter(&fs);
}
// LONGTERM provide an equivalent to CTLineGetTypographicBounds() on uiDrawTextLayout?
// LONGTERM keep this for later features and documentation purposes
#if 0
// LONGTERM provide a way to get the image bounds as a separate function later
bounds = CTLineGetImageBounds(line, c);
// though CTLineGetImageBounds() returns CGRectNull on error, it also returns CGRectNull on an empty string, so we can't reasonably check for error
// CGContextSetTextPosition() positions at the baseline in the case of CTLineDraw(); we need the top-left corner instead
CTLineGetTypographicBounds(line, &yoff, NULL, NULL);
// remember that we're flipped, so we subtract
y -= yoff;
CGContextSetTextPosition(c, x, y);
#endif
#if 0
void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, int startChar, int endChar, double r, double g, double b, double a)
{
CGColorSpaceRef colorspace;
CGFloat components[4];
CGColorRef color;
// for consistency with windows, use sRGB
colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
components[0] = r;
components[1] = g;
components[2] = b;
components[3] = a;
color = CGColorCreate(colorspace, components);
CGColorSpaceRelease(colorspace);
CFAttributedStringSetAttribute(layout->mas,
rangeToCFRange(),
kCTForegroundColorAttributeName,
color);
CGColorRelease(color); // TODO safe?
}
#endif

View File

@ -1,314 +0,0 @@
// 2 january 2017
#import "uipriv_darwin.h"
#import "draw.h"
// TODO on an empty string nLines == 0
// we must prevent this somehow
// TODO in general, every function could be more robust, but we cannot have a situation where there are zero lines
// TODO what happens to extents if only whitespace?
struct uiDrawTextLayout {
CFAttributedStringRef attrstr;
// the width as passed into uiDrawTextLayout constructors
double width;
CTFramesetterRef framesetter;
// the *actual* size of the frame
// note: technically, metrics returned from frame are relative to CGPathGetPathBoundingBox(tl->path)
// however, from what I can gather, for a path created by CGPathCreateWithRect(), like we do (with a NULL transform), CGPathGetPathBoundingBox() seems to just return the standardized form of the rect used to create the path
// (this I confirmed through experimentation)
// so we can just use tl->size for adjustments
// we don't need to adjust coordinates by any origin since our rect origin is (0, 0)
CGSize size;
CGPathRef path;
CTFrameRef frame;
CFArrayRef lines;
CFIndex nLines;
// we compute this once when first creating the layout
uiDrawTextLayoutLineMetrics *lineMetrics;
NSArray *backgroundBlocks;
// for converting CFAttributedString indices from/to byte offsets
size_t *u8tou16;
size_t nUTF8;
size_t *u16tou8;
size_t nUTF16;
};
// TODO document that lines may or may not overlap because ours do in the case of multiple combining characters
static uiDrawTextLayoutLineMetrics *computeLineMetrics(CTFrameRef frame, CGSize size)
{
uiDrawTextLayoutLineMetrics *metrics;
CFArrayRef lines;
CTLineRef line;
CFIndex i, n;
CGFloat ypos;
CGRect bounds, boundsNoLeading;
CGFloat ascent, descent, leading;
CGPoint *origins;
lines = CTFrameGetLines(frame);
n = CFArrayGetCount(lines);
metrics = (uiDrawTextLayoutLineMetrics *) uiAlloc(n * sizeof (uiDrawTextLayoutLineMetrics), "uiDrawTextLayoutLineMetrics[] (text layout)");
origins = (CGPoint *) uiAlloc(n * sizeof (CGPoint), "CGPoint[] (text layout)");
CTFrameGetLineOrigins(frame, CFRangeMake(0, n), origins);
ypos = size.height;
for (i = 0; i < n; i++) {
line = (CTLineRef) CFArrayGetValueAtIndex(lines, i);
bounds = CTLineGetBoundsWithOptions(line, 0);
boundsNoLeading = CTLineGetBoundsWithOptions(line, kCTLineBoundsExcludeTypographicLeading);
// this is equivalent to boundsNoLeading.size.height + boundsNoLeading.origin.y (manually verified)
ascent = bounds.size.height + bounds.origin.y;
descent = -boundsNoLeading.origin.y;
leading = -bounds.origin.y - descent;
// Core Text always rounds these up for paragraph style calculations; there is a flag to control it but it's inaccessible (and this behavior is turned off for old versions of iPhoto)
ascent = floor(ascent + 0.5);
descent = floor(descent + 0.5);
if (leading > 0)
leading = floor(leading + 0.5);
metrics[i].X = origins[i].x;
metrics[i].Y = origins[i].y - descent - leading;
metrics[i].Width = bounds.size.width;
metrics[i].Height = ascent + descent + leading;
metrics[i].BaselineY = origins[i].y;
metrics[i].Ascent = ascent;
metrics[i].Descent = descent;
metrics[i].Leading = leading;
// TODO
metrics[i].ParagraphSpacingBefore = 0;
metrics[i].LineHeightSpace = 0;
metrics[i].LineSpacing = 0;
metrics[i].ParagraphSpacing = 0;
// and finally advance to the next line
ypos += metrics[i].Height;
}
// okay, but now all these metrics are unflipped
// we need to flip them
for (i = 0; i < n; i++) {
metrics[i].Y = size.height - metrics[i].Y;
// go from bottom-left corner to top-left
metrics[i].Y -= metrics[i].Height;
metrics[i].BaselineY = size.height - metrics[i].BaselineY;
}
uiFree(origins);
return metrics;
}
uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p)
{
uiDrawTextLayout *tl;
CGFloat cgwidth;
CFRange range, unused;
CGRect rect;
tl = uiNew(uiDrawTextLayout);
tl->attrstr = attrstrToCoreFoundation(p, &(tl->backgroundBlocks));
range.location = 0;
range.length = CFAttributedStringGetLength(tl->attrstr);
tl->width = p->Width;
// TODO kCTParagraphStyleSpecifierMaximumLineSpacing, kCTParagraphStyleSpecifierMinimumLineSpacing, kCTParagraphStyleSpecifierLineSpacingAdjustment for line spacing
tl->framesetter = CTFramesetterCreateWithAttributedString(tl->attrstr);
if (tl->framesetter == NULL) {
// TODO
}
cgwidth = (CGFloat) (tl->width);
if (cgwidth < 0)
cgwidth = CGFLOAT_MAX;
tl->size = CTFramesetterSuggestFrameSizeWithConstraints(tl->framesetter,
range,
// TODO kCTFramePathWidthAttributeName?
NULL,
CGSizeMake(cgwidth, CGFLOAT_MAX),
&unused); // not documented as accepting NULL (TODO really?)
rect.origin = CGPointZero;
rect.size = tl->size;
tl->path = CGPathCreateWithRect(rect, NULL);
tl->frame = CTFramesetterCreateFrame(tl->framesetter,
range,
tl->path,
// TODO kCTFramePathWidthAttributeName?
NULL);
if (tl->frame == NULL) {
// TODO
}
tl->lines = CTFrameGetLines(tl->frame);
tl->nLines = CFArrayGetCount(tl->lines);
tl->lineMetrics = computeLineMetrics(tl->frame, tl->size);
// and finally copy the UTF-8/UTF-16 conversion tables
tl->u8tou16 = attrstrCopyUTF8ToUTF16(p->String, &(tl->nUTF8));
tl->u16tou8 = attrstrCopyUTF16ToUTF8(p->String, &(tl->nUTF16));
return tl;
}
void uiDrawFreeTextLayout(uiDrawTextLayout *tl)
{
uiFree(tl->u16tou8);
uiFree(tl->u8tou16);
[tl->backgroundBlocks release];
uiFree(tl->lineMetrics);
CFRelease(tl->frame);
CFRelease(tl->path);
CFRelease(tl->framesetter);
CFRelease(tl->attrstr);
uiFree(tl);
}
// TODO document that (x,y) is the top-left corner of the *entire frame*
void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y)
{
backgroundBlock b;
CGAffineTransform textMatrix;
CGContextSaveGState(c->c);
// save the text matrix because it's not part of the graphics state
textMatrix = CGContextGetTextMatrix(c->c);
for (b in tl->backgroundBlocks)
b(c, tl, x, y);
// Core Text doesn't draw onto a flipped view correctly; we have to pretend it was unflipped
// see the iOS bits of the first example at https://developer.apple.com/library/mac/documentation/StringsTextFonts/Conceptual/CoreText_Programming/LayoutOperations/LayoutOperations.html#//apple_ref/doc/uid/TP40005533-CH12-SW1 (iOS is naturally flipped)
// TODO how is this affected by a non-identity CTM?
CGContextTranslateCTM(c->c, 0, c->height);
CGContextScaleCTM(c->c, 1.0, -1.0);
CGContextSetTextMatrix(c->c, CGAffineTransformIdentity);
// wait, that's not enough; we need to offset y values to account for our new flipping
// TODO explain this calculation
y = c->height - tl->size.height - y;
// CTFrameDraw() draws in the path we specified when creating the frame
// this means that in our usage, CTFrameDraw() will draw at (0,0)
// so move the origin to be at (x,y) instead
// TODO are the signs correct?
CGContextTranslateCTM(c->c, x, y);
CTFrameDraw(tl->frame, c->c);
CGContextSetTextMatrix(c->c, textMatrix);
CGContextRestoreGState(c->c);
}
// TODO document that the width and height of a layout is not necessarily the sum of the widths and heights of its constituent lines
// TODO width doesn't include trailing whitespace...
// TODO figure out how paragraph spacing should play into this
void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height)
{
*width = tl->size.width;
*height = tl->size.height;
}
int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl)
{
return tl->nLines;
}
void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end)
{
CTLineRef lr;
CFRange range;
lr = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, line);
range = CTLineGetStringRange(lr);
*start = tl->u16tou8[range.location];
*end = tl->u16tou8[range.location + range.length];
}
void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m)
{
*m = tl->lineMetrics[line];
}
// in the case of overlapping lines, we read lines first to last and use their bottommost point (Y + Height) to determine where the next line should start for hit-testing
// TODO should we document this?
void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *pos, int *line)
{
int i;
CTLineRef ln;
CFIndex p;
for (i = 0; i < tl->nLines; i++) {
double ltop, lbottom;
ltop = tl->lineMetrics[i].Y;
lbottom = ltop + tl->lineMetrics[i].Height;
// y will already >= ltop at this point since the past lbottom should == (or at least >=, see above) ltop
if (y < lbottom)
break;
}
if (i == tl->nLines)
i--;
*line = i;
ln = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, i);
// note: according to the docs, we pass a y of 0 for this since the is the baseline of that line (the point is relative to the line)
// note: x is relative to the line origin
x -= tl->lineMetrics[*line].X;
p = CTLineGetStringIndexForPosition(ln, CGPointMake(x, 0));
if (p == kCFNotFound) {
// TODO
}
*pos = tl->u16tou8[p];
}
double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line)
{
CTLineRef lr;
CFRange range;
pos = tl->u8tou16[pos];
if (line < 0 || line >= tl->nLines)
return -1;
lr = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, line);
range = CTLineGetStringRange(lr);
// note: >, not >=, because the position at end is valid!
if (pos < range.location || pos > (range.location + range.length))
return -1;
// no point in checking the return; we already validated everything and 0 is a valid return for the first index :/
// note: the result is relative to the line origin (TODO find documentation to support this)
// TODO document that these functions do this
return CTLineGetOffsetForStringIndex(lr, pos, NULL) + tl->lineMetrics[line].X;
}
void caretDrawParams(uiDrawContext *c, double height, struct caretDrawParams *p)
{
NSColor *cc;
CGFloat cr, cg, cb, ca;
// Interface Builder sets this as the insertion point color for a NSTextView by default
cc = [NSColor controlTextColor];
// the given color may not be an RGBA color, which will cause the -getRed:green:blue:alpha: call to throw an exception
cc = [cc colorUsingColorSpace:[NSColorSpace sRGBColorSpace]];
[cc getRed:&cr green:&cg blue:&cb alpha:&ca];
p->r = cr;
p->g = cg;
p->b = cb;
p->a = ca;
// both cc and the controlTextColor it was made from will be autoreleased since they aren't new or init calls
// TODO disabled carets have some blending applied...
// okay there doesn't seem to be any defined metrics for these, argh...
p->width = 1;
p->xoff = 0;
}

View File

@ -1,60 +0,0 @@
darwin
int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl)
{
return CFArrayGetCount([tl->forLines lines]);
}
void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end)
{
CTLineRef lr;
CFRange range;
lr = (CTLineRef) CFArrayGetValueAtIndex([tl->forLines lines], line);
range = CTLineGetStringRange(lr);
*start = tl->u16tou8[range.location];
if (tl->empty)
*end = *start;
else
*end = tl->u16tou8[range.location + range.length];
}
unix
int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl)
{
return pango_layout_get_line_count(tl->layout);
}
void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end)
{
PangoLayoutLine *pll;
pll = pango_layout_get_line_readonly(tl->layout, line);
*start = pll->start_index;
*end = pll->start_index + pll->length;
// TODO unref pll?
}
windows
int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl)
{
return 0;
#if 0
TODO
return tl->nLines;
#endif
}
// DirectWrite doesn't provide a direct way to do this, so we have to do this manually
// TODO does that comment still apply here or to the code at the top of this file?
void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end)
{
#if 0
TODO
*start = tl->lineInfo[line].startPos;
*start = tl->u16tou8[*start];
*end = tl->lineInfo[line].endPos - tl->lineInfo[line].newlineCount;
*end = tl->u16tou8[*end];
#endif
}

View File

@ -1,72 +0,0 @@
typedef struct uiDrawTextLayoutLineMetrics uiDrawTextLayoutLineMetrics;
// Height will equal ParagraphSpacingBefore + LineHeightSpace + Ascent + Descent + Leading + LineSpacing + ParagraphSpacing.
// The above values are listed in vertical order, from top to bottom.
// Ascent + Descent + Leading will give you the typographic bounds
// of the text. BaselineY is the boundary between Ascent and Descent.
// X, Y, and BaselineY are all in the layout's coordinate system, so the
// start point of the baseline will be at (X, BaselineY). All values are
// nonnegative.
struct uiDrawTextLayoutLineMetrics {
// This describes the overall bounding box of the line.
double X;
double Y;
double Width;
double Height;
// This describes the typographic bounds of the line.
double BaselineY;
double Ascent;
double Descent;
double Leading;
// This describes any additional whitespace.
// TODO come up with better names for these.
double ParagraphSpacingBefore;
double LineHeightSpace;
double LineSpacing;
double ParagraphSpacing;
// TODO trailing whitespace?
};
// uiDrawTextLayoutNumLines() returns the number of lines in tl.
// This number will always be greater than or equal to 1; a text
// layout with no text only has one line.
_UI_EXTERN int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl);
// uiDrawTextLayoutLineByteRange() returns the byte indices of the
// text that falls into the given line of tl as [start, end).
_UI_EXTERN void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end);
_UI_EXTERN void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m);
// TODO rewrite this documentation
// uiDrawTextLayoutHitTest() returns the byte offset and line closest
// to the given point. The point is relative to the top-left of the layout.
// If the point is outside the layout itself, the closest point is chosen;
// this allows the function to be used for cursor positioning with the
// mouse. Do keep the returned line in mind if used in this way; the
// user might click on the end of a line, at which point the cursor
// might be at the trailing edge of the last grapheme on the line
// (subject to the operating system's APIs).
_UI_EXTERN void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *pos, int *line);
// uiDrawTextLayoutByteLocationInLine() returns the point offset
// into the given line that the given byte position stands. This is
// relative to the line's X position (as returned by
// uiDrawTextLayoutLineGetMetrics()), which in turn is relative to
// the top-left of the layout. This function can be used for cursor
// positioning: if start and end are the start and end of the line
// (as returned by uiDrawTextLayoutLineByteRange()), you will get
// the correct offset, even if pos is at the end of the line. If pos is not
// in the range [start, end], a negative value will be returned,
// indicating you need to move the cursor to another line.
// TODO make sure this works right for right-aligned and center-aligned lines and justified lines and RTL text
_UI_EXTERN double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line);
_UI_EXTERN void uiDrawCaret(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout, size_t pos, int *line);
// TODO allow blinking
// TODO allow secondary carets

View File

@ -1,171 +0,0 @@
diff --git a/darwin/attrstr.m b/darwin/attrstr.m
index fd45ec25..86039fad 100644
--- a/darwin/attrstr.m
+++ b/darwin/attrstr.m
@@ -403,8 +403,13 @@ static CTParagraphStyleRef mkParagraphStyle(uiDrawTextLayoutParams *p)
return ps;
}
-CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks)
+static const UniChar emptyChars[] = { 0x20, 0x0 };
+static const CFIndex emptyCharCount = 1;
+
+CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, BOOL *isEmpty, NSArray **backgroundBlocks)
{
+ const UniChar *chars;
+ CFIndex charCount;
CFStringRef cfstr;
CFMutableDictionaryRef defaultAttrs;
CTFontRef defaultCTFont;
@@ -413,7 +418,15 @@ CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray
CFMutableAttributedStringRef mas;
struct foreachParams fep;
- cfstr = CFStringCreateWithCharacters(NULL, attrstrUTF16(p->String), attrstrUTF16Len(p->String));
+ *isEmpty = NO;
+ chars = attrstrUTF16(p->String);
+ charCount = attrstrUTF16Len(p->String);
+ if (charCount == 0) {
+ *isEmpty = YES;
+ chars = emptyChars;
+ charCount = emptyCharCount;
+ }
+ cfstr = CFStringCreateWithCharacters(NULL, chars, charCount);
if (cfstr == NULL) {
// TODO
}
diff --git a/darwin/drawtext.m b/darwin/drawtext.m
index 1fa5920e..65912383 100644
--- a/darwin/drawtext.m
+++ b/darwin/drawtext.m
@@ -2,13 +2,16 @@
#import "uipriv_darwin.h"
#import "draw.h"
-// TODO on an empty string nLines == 0
-// we must prevent this somehow
-// TODO in general, every function could be more robust, but we cannot have a situation where there are zero lines
+// TODO in general, every function could be more robust
// TODO what happens to extents if only whitespace?
+// TODO for empty layouts:
+// - check if alignment correct compared to other OSs, or expected behavior at all
+// - double-check if uiAttributedString allows zero-length attributes; I forget if I did
struct uiDrawTextLayout {
CFAttributedStringRef attrstr;
+ // this is needed because Core Text will give us an empty line array on a frame made with an empty string
+ BOOL isEmpty;
// the width as passed into uiDrawTextLayout constructors
double width;
@@ -41,7 +44,7 @@
};
// TODO document that lines may or may not overlap because ours do in the case of multiple combining characters
-static uiDrawTextLayoutLineMetrics *computeLineMetrics(CTFrameRef frame, CGSize size)
+static uiDrawTextLayoutLineMetrics *computeLineMetrics(CTFrameRef frame, CGSize size, BOOL isEmpty)
{
uiDrawTextLayoutLineMetrics *metrics;
CFArrayRef lines;
@@ -79,6 +82,8 @@
metrics[i].X = origins[i].x;
metrics[i].Y = origins[i].y - descent - leading;
metrics[i].Width = bounds.size.width;
+ if (isEmpty)
+ metrics[i].Width = 0;
metrics[i].Height = ascent + descent + leading;
metrics[i].BaselineY = origins[i].y;
@@ -117,7 +122,7 @@
CGRect rect;
tl = uiNew(uiDrawTextLayout);
- tl->attrstr = attrstrToCoreFoundation(p, &(tl->backgroundBlocks));
+ tl->attrstr = attrstrToCoreFoundation(p, &(tl->isEmpty), &(tl->backgroundBlocks));
range.location = 0;
range.length = CFAttributedStringGetLength(tl->attrstr);
tl->width = p->Width;
@@ -152,7 +157,7 @@
tl->lines = CTFrameGetLines(tl->frame);
tl->nLines = CFArrayGetCount(tl->lines);
- tl->lineMetrics = computeLineMetrics(tl->frame, tl->size);
+ tl->lineMetrics = computeLineMetrics(tl->frame, tl->size, tl->isEmpty);
// and finally copy the UTF-8/UTF-16 conversion tables
tl->u8tou16 = attrstrCopyUTF8ToUTF16(p->String, &(tl->nUTF8));
@@ -180,6 +185,9 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y)
backgroundBlock b;
CGAffineTransform textMatrix;
+ if (tl->isEmpty)
+ return;
+
CGContextSaveGState(c->c);
// save the text matrix because it's not part of the graphics state
textMatrix = CGContextGetTextMatrix(c->c);
@@ -216,6 +224,8 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y)
void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height)
{
*width = tl->size.width;
+ if (tl->isEmpty)
+ *width = 0;
*height = tl->size.height;
}
@@ -233,6 +243,8 @@ void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start
range = CTLineGetStringRange(lr);
*start = tl->u16tou8[range.location];
*end = tl->u16tou8[range.location + range.length];
+ if (tl->isEmpty)
+ *start = *end;
}
void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m)
@@ -262,14 +274,17 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *p
*line = i;
ln = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, i);
- // note: according to the docs, we pass a y of 0 for this since the is the baseline of that line (the point is relative to the line)
- // note: x is relative to the line origin
x -= tl->lineMetrics[*line].X;
- p = CTLineGetStringIndexForPosition(ln, CGPointMake(x, 0));
- if (p == kCFNotFound) {
- // TODO
+ *pos = 0;
+ if (!tl->isEmpty) {
+ // note: according to the docs, we pass a y of 0 for this since the is the baseline of that line (the point is relative to the line)
+ // note: x is relative to the line origin
+ p = CTLineGetStringIndexForPosition(ln, CGPointMake(x, 0));
+ if (p == kCFNotFound) {
+ // TODO
+ }
+ *pos = tl->u16tou8[p];
}
- *pos = tl->u16tou8[p];
}
double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line)
@@ -282,6 +297,9 @@ void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *p
return -1;
lr = (CTLineRef) CFArrayGetValueAtIndex(tl->lines, line);
range = CTLineGetStringRange(lr);
+ // TODO is the behavior of this part correct?
+ if (tl->isEmpty)
+ range.length = 0;
// note: >, not >=, because the position at end is valid!
if (pos < range.location || pos > (range.location + range.length))
return -1;
diff --git a/darwin/uipriv_darwin.h b/darwin/uipriv_darwin.h
index 0303c32c..d44ee410 100644
--- a/darwin/uipriv_darwin.h
+++ b/darwin/uipriv_darwin.h
@@ -151,7 +151,7 @@ extern void fontdescFromCTFontDescriptor(CTFontDescriptorRef ctdesc, uiDrawFontD
extern void initUnderlineColors(void);
extern void uninitUnderlineColors(void);
typedef void (^backgroundBlock)(uiDrawContext *c, uiDrawTextLayout *layout, double x, double y);
-extern CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, NSArray **backgroundBlocks);
+extern CFAttributedStringRef attrstrToCoreFoundation(uiDrawTextLayoutParams *p, BOOL *isEmpty, NSArray **backgroundBlocks);
// aat.m
typedef void (^aatBlock)(uint16_t type, uint16_t selector);

View File

@ -1,217 +0,0 @@
// 6 september 2015
#include "uipriv_unix.h"
#include "draw.h"
struct uiDrawFontFamilies {
PangoFontFamily **f;
int n;
};
uiDrawFontFamilies *uiDrawListFontFamilies(void)
{
uiDrawFontFamilies *ff;
PangoFontMap *map;
ff = uiNew(uiDrawFontFamilies);
map = pango_cairo_font_map_get_default();
pango_font_map_list_families(map, &(ff->f), &(ff->n));
// do not free map; it's a shared resource
return ff;
}
int uiDrawFontFamiliesNumFamilies(uiDrawFontFamilies *ff)
{
return ff->n;
}
char *uiDrawFontFamiliesFamily(uiDrawFontFamilies *ff, int n)
{
PangoFontFamily *f;
f = ff->f[n];
return uiUnixStrdupText(pango_font_family_get_name(f));
}
void uiDrawFreeFontFamilies(uiDrawFontFamilies *ff)
{
g_free(ff->f);
uiFree(ff);
}
struct uiDrawTextFont {
PangoFont *f;
};
uiDrawTextFont *mkTextFont(PangoFont *f, gboolean ref)
{
uiDrawTextFont *font;
font = uiNew(uiDrawTextFont);
font->f = f;
if (ref)
g_object_ref(font->f);
return font;
}
PangoFont *pangoDescToPangoFont(PangoFontDescription *pdesc)
{
PangoFont *f;
PangoContext *context;
// in this case, the context is necessary for the metrics to be correct
context = mkGenericPangoCairoContext();
f = pango_font_map_load_font(pango_cairo_font_map_get_default(), context, pdesc);
if (f == NULL) {
// LONGTERM
g_error("[libui] no match in pangoDescToPangoFont(); report to andlabs");
}
g_object_unref(context);
return f;
}
uiDrawTextFont *uiDrawLoadClosestFont(const uiDrawTextFontDescriptor *desc)
{
PangoFont *f;
PangoFontDescription *pdesc;
pdesc = pango_font_description_new();
pango_font_description_set_family(pdesc,
desc->Family);
pango_font_description_set_size(pdesc,
(gint) (desc->Size * PANGO_SCALE));
pango_font_description_set_weight(pdesc,
pangoWeights[desc->Weight]);
pango_font_description_set_style(pdesc,
pangoItalics[desc->Italic]);
pango_font_description_set_stretch(pdesc,
pangoStretches[desc->Stretch]);
f = pangoDescToPangoFont(pdesc);
pango_font_description_free(pdesc);
return mkTextFont(f, FALSE); // we hold the initial reference; no need to ref
}
void uiDrawFreeTextFont(uiDrawTextFont *font)
{
g_object_unref(font->f);
uiFree(font);
}
uintptr_t uiDrawTextFontHandle(uiDrawTextFont *font)
{
return (uintptr_t) (font->f);
}
void uiDrawTextFontDescribe(uiDrawTextFont *font, uiDrawTextFontDescriptor *desc)
{
PangoFontDescription *pdesc;
// this creates a copy; we free it later
pdesc = pango_font_describe(font->f);
// TODO
pango_font_description_free(pdesc);
}
// See https://developer.gnome.org/pango/1.30/pango-Cairo-Rendering.html#pango-Cairo-Rendering.description
// Note that we convert to double before dividing to make sure the floating-point stuff is right
#define pangoToCairo(pango) (((double) (pango)) / PANGO_SCALE)
#define cairoToPango(cairo) ((gint) ((cairo) * PANGO_SCALE))
void uiDrawTextFontGetMetrics(uiDrawTextFont *font, uiDrawTextFontMetrics *metrics)
{
PangoFontMetrics *pm;
pm = pango_font_get_metrics(font->f, NULL);
metrics->Ascent = pangoToCairo(pango_font_metrics_get_ascent(pm));
metrics->Descent = pangoToCairo(pango_font_metrics_get_descent(pm));
// Pango doesn't seem to expose this :( Use 0 and hope for the best.
metrics->Leading = 0;
metrics->UnderlinePos = pangoToCairo(pango_font_metrics_get_underline_position(pm));
metrics->UnderlineThickness = pangoToCairo(pango_font_metrics_get_underline_thickness(pm));
pango_font_metrics_unref(pm);
}
// note: PangoCairoLayouts are tied to a given cairo_t, so we can't store one in this device-independent structure
struct uiDrawTextLayout {
char *s;
ptrdiff_t *graphemes;
PangoFont *defaultFont;
double width;
PangoAttrList *attrs;
};
uiDrawTextLayout *uiDrawNewTextLayout(const char *text, uiDrawTextFont *defaultFont, double width)
{
uiDrawTextLayout *layout;
PangoContext *context;
layout = uiNew(uiDrawTextLayout);
layout->s = g_strdup(text);
context = mkGenericPangoCairoContext();
layout->graphemes = graphemes(layout->s, context);
g_object_unref(context);
layout->defaultFont = defaultFont->f;
g_object_ref(layout->defaultFont); // retain a copy
uiDrawTextLayoutSetWidth(layout, width);
layout->attrs = pango_attr_list_new();
return layout;
}
void uiDrawFreeTextLayout(uiDrawTextLayout *layout)
{
pango_attr_list_unref(layout->attrs);
g_object_unref(layout->defaultFont);
uiFree(layout->graphemes);
g_free(layout->s);
uiFree(layout);
}
void uiDrawTextLayoutSetWidth(uiDrawTextLayout *layout, double width)
{
layout->width = width;
}
static void prepareLayout(uiDrawTextLayout *layout, PangoLayout *pl)
{
// again, this makes a copy
desc = pango_font_describe(layout->defaultFont);
pango_layout_set_attributes(pl, layout->attrs);
}
void uiDrawText(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout)
{
PangoLayout *pl;
pl = pango_cairo_create_layout(c->cr);
}
static void addAttr(uiDrawTextLayout *layout, PangoAttribute *attr, int startChar, int endChar)
{
attr->start_index = layout->graphemes[startChar];
attr->end_index = layout->graphemes[endChar];
pango_attr_list_insert(layout->attrs, attr);
// pango_attr_list_insert() takes attr; we don't free it
}
void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, int startChar, int endChar, double r, double g, double b, double a)
{
PangoAttribute *attr;
guint16 rr, gg, bb, aa;
rr = (guint16) (r * 65535);
gg = (guint16) (g * 65535);
bb = (guint16) (b * 65535);
aa = (guint16) (a * 65535);
attr = pango_attr_foreground_new(rr, gg, bb);
addAttr(layout, attr, startChar, endChar);
// TODO what if aa == 0?
attr = FUTURE_pango_attr_foreground_alpha_new(aa);
if (attr != NULL)
addAttr(layout, attr, startChar, endChar);
}

View File

@ -1,227 +0,0 @@
// 17 january 2017
#include "uipriv_unix.h"
#include "draw.h"
// TODO
// - if the RTL override is at the beginning of a line, the preceding space is included?
// - nLines == 0: mostly works, except the width is wrong if the paragraph alignment is center or right...
// - TODO check whitespace and line bounds
struct uiDrawTextLayout {
PangoLayout *layout;
GPtrArray *backgroundClosures;
uiDrawTextLayoutLineMetrics *lineMetrics;
// TODO change everything to use this
int nLines;
};
// TODO neither these nor the overall extents seem to include trailing whitespace... we need to figure that out too
static void computeLineMetrics(uiDrawTextLayout *tl)
{
PangoLayoutIter *iter;
PangoLayoutLine *pll;
PangoRectangle lineStartPos, lineExtents;
int i, n;
uiDrawTextLayoutLineMetrics *m;
n = tl->nLines; // TODO remove this variable
tl->lineMetrics = (uiDrawTextLayoutLineMetrics *) uiAlloc(n * sizeof (uiDrawTextLayoutLineMetrics), "uiDrawTextLayoutLineMetrics[] (text layout)");
iter = pango_layout_get_iter(tl->layout);
m = tl->lineMetrics;
for (i = 0; i < n; i++) {
int baselineY;
// TODO we use this instead of _get_yrange() because of the block of text in that function's description about how line spacing is distributed in Pango; we have to worry about this when we start adding line spacing...
baselineY = pango_layout_iter_get_baseline(iter);
pll = pango_layout_iter_get_line_readonly(iter);
pango_layout_index_to_pos(tl->layout, pll->start_index, &lineStartPos);
pango_layout_line_get_extents(pll, NULL, &lineExtents);
// TODO unref pll?
// TODO is this correct for RTL glyphs?
m->X = pangoToCairo(lineStartPos.x);
// TODO fix the whole combined not being updated shenanigans in the static build (here because ugh)
m->Y = pangoToCairo(baselineY - PANGO_ASCENT(lineExtents));
// TODO this does not include the last space if any
m->Width = pangoToCairo(lineExtents.width);
m->Height = pangoToCairo(lineExtents.height);
m->BaselineY = pangoToCairo(baselineY);
m->Ascent = pangoToCairo(PANGO_ASCENT(lineExtents));
m->Descent = pangoToCairo(PANGO_DESCENT(lineExtents));
m->Leading = 0; // TODO
m->ParagraphSpacingBefore = 0; // TODO
m->LineHeightSpace = 0; // TODO
m->LineSpacing = 0; // TODO
m->ParagraphSpacing = 0; // TODO
// don't worry about the return value; we're not using this after the last line
pango_layout_iter_next_line(iter);
m++;
}
pango_layout_iter_free(iter);
}
uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p)
{
uiDrawTextLayout *tl;
PangoContext *context;
PangoFontDescription *desc;
PangoAttrList *attrs;
int pangoWidth;
tl = uiNew(uiDrawTextLayout);
// in this case, the context is necessary to create the layout
// the layout takes a ref on the context so we can unref it afterward
context = mkGenericPangoCairoContext();
tl->layout = pango_layout_new(context);
g_object_unref(context);
// this is safe; pango_layout_set_text() copies the string
pango_layout_set_text(tl->layout, uiAttributedStringString(p->String), -1);
desc = pango_font_description_new();
pango_font_description_set_family(desc, p->DefaultFont->Family);
pango_font_description_set_style(desc, pangoItalics[p->DefaultFont->Italic]);
// for the most part, pango weights correlate to ours
// the differences:
// - Book — libui: 350, Pango: 380
// - Ultra Heavy — libui: 950, Pango: 1000
// TODO figure out what to do about this misalignment
pango_font_description_set_weight(desc, p->DefaultFont->Weight);
pango_font_description_set_stretch(desc, pangoStretches[p->DefaultFont->Stretch]);
// see https://developer.gnome.org/pango/1.30/pango-Fonts.html#pango-font-description-set-size and https://developer.gnome.org/pango/1.30/pango-Glyph-Storage.html#pango-units-from-double
pango_font_description_set_size(desc, pango_units_from_double(p->DefaultFont->Size));
pango_layout_set_font_description(tl->layout, desc);
// this is safe; the description is copied
pango_font_description_free(desc);
pangoWidth = cairoToPango(p->Width);
if (p->Width < 0)
pangoWidth = -1;
pango_layout_set_width(tl->layout, pangoWidth);
pango_layout_set_alignment(tl->layout, pangoAligns[p->Align]);
attrs = attrstrToPangoAttrList(p, &(tl->backgroundClosures));
pango_layout_set_attributes(tl->layout, attrs);
pango_attr_list_unref(attrs);
tl->nLines = pango_layout_get_line_count(tl->layout);
computeLineMetrics(tl);
return tl;
}
void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m)
{
*m = tl->lineMetrics[line];
}
// TODO
#if 0
{
PangoLayoutLine *pll;
pll = pango_layout_get_line_readonly(tl->layout, line);
// TODO unref?
}
#endif
// note: Pango will not let us place the cursor at the end of a line the same way other OSs do; see https://git.gnome.org/browse/pango/tree/pango/pango-layout.c?id=f4cbd27f4e5bf8490ea411190d41813e14f12165#n4204
// ideally there'd be a way to say "I don't need this hack; I'm well behaved" but GTK+ 2 and 3 AND Qt 4 and 5 all behave like this, with the behavior seeming to date back to TkTextView, so...
void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *pos, int *line)
{
int p, trailing;
int i;
// this is layout-global, so it takes line origins into account
pango_layout_xy_to_index(tl->layout,
cairoToPango(x), cairoToPango(y),
&p, &trailing);
// on a trailing hit, align to the nearest cluster
// fortunately Pango provides that info directly
if (trailing != 0)
p += trailing;
*pos = p;
for (i = 0; i < tl->nLines; i++) {
double ltop, lbottom;
ltop = tl->lineMetrics[i].Y;
lbottom = ltop + tl->lineMetrics[i].Height;
// y will already >= ltop at this point since the past lbottom should == ltop
if (y < lbottom)
break;
}
if (i == pango_layout_get_line_count(tl->layout))
i--;
*line = i;
}
double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line)
{
PangoLayoutLine *pll;
gboolean trailing;
int pangox;
if (line < 0 || line >= tl->nLines)
return -1;
pll = pango_layout_get_line_readonly(tl->layout, line);
// note: >, not >=, because the position at end is valid!
if (pos < pll->start_index || pos > (pll->start_index + pll->length))
return -1;
// this behavior seems correct
// there's also PadWrite's TextEditor::GetCaretRect() but that requires state...
// TODO where does this fail?
// TODO optimize everything to avoid calling strlen()
trailing = 0;
if (pos != 0 && pos != strlen(pango_layout_get_text(tl->layout)) && pos == (pll->start_index + pll->length)) {
pos--;
trailing = 1;
}
pango_layout_line_index_to_x(pll, pos, trailing, &pangox);
// TODO unref pll?
// this is relative to the beginning of the line
return pangoToCairo(pangox) + tl->lineMetrics[line].X;
}
// note: we can't use gtk_render_insertion_cursor() because that doesn't take information about what line to render on
// we'll just recreate what it does
void caretDrawParams(uiDrawContext *c, double height, struct caretDrawParams *p)
{
GdkColor *color;
GdkRGBA rgba;
gfloat aspectRatio;
gint width, xoff;
gtk_style_context_get_style(c->style,
"cursor-color", &color,
"cursor-aspect-ratio", &aspectRatio,
NULL);
if (color != NULL) {
p->r = ((double) (color->red)) / 65535.0;
p->g = ((double) (color->green)) / 65535.0;
p->b = ((double) (color->blue)) / 65535.0;
p->a = 1.0;
gdk_color_free(color);
} else {
gtk_style_context_get_color(c->style, GTK_STATE_FLAG_NORMAL, &rgba);
p->r = rgba.red;
p->g = rgba.green;
p->b = rgba.blue;
p->a = rgba.alpha;
}
// GTK+ itself uses integer arithmetic here; let's do the same
width = height * aspectRatio + 1;
// TODO this is for LTR
xoff = width / 2;
p->xoff = xoff;
p->width = width;
}

View File

@ -1,311 +0,0 @@
// 22 december 2015
#include "uipriv_windows.hpp"
#include "draw.hpp"
// TODO really migrate
// notes:
// only available in windows 8 and newer:
// - character spacing
// - kerning control
// - justficiation (how could I possibly be making this up?!)
// - vertical text (SERIOUSLY?! WHAT THE ACTUAL FUCK, MICROSOFT?!?!?!? DID YOU NOT THINK ABOUT THIS THE FIRST TIME, TRYING TO IMPROVE THE INTERNATIONALIZATION OF WINDOWS 7?!?!?! bonus: some parts of MSDN even say 8.1 and up only!)
struct uiDrawFontFamilies {
fontCollection *fc;
};
uiDrawFontFamilies *uiDrawListFontFamilies(void)
{
struct uiDrawFontFamilies *ff;
ff = uiNew(struct uiDrawFontFamilies);
ff->fc = loadFontCollection();
return ff;
}
int uiDrawFontFamiliesNumFamilies(uiDrawFontFamilies *ff)
{
return ff->fc->fonts->GetFontFamilyCount();
}
char *uiDrawFontFamiliesFamily(uiDrawFontFamilies *ff, int n)
{
IDWriteFontFamily *family;
WCHAR *wname;
char *name;
HRESULT hr;
hr = ff->fc->fonts->GetFontFamily(n, &family);
if (hr != S_OK)
logHRESULT(L"error getting font out of collection", hr);
wname = fontCollectionFamilyName(ff->fc, family);
name = toUTF8(wname);
uiFree(wname);
family->Release();
return name;
}
void uiDrawFreeFontFamilies(uiDrawFontFamilies *ff)
{
fontCollectionFree(ff->fc);
uiFree(ff);
}
struct uiDrawTextFont {
IDWriteFont *f;
WCHAR *family; // save for convenience in uiDrawNewTextLayout()
double size;
};
uiDrawTextFont *mkTextFont(IDWriteFont *df, BOOL addRef, WCHAR *family, BOOL copyFamily, double size)
{
uiDrawTextFont *font;
WCHAR *copy;
HRESULT hr;
font = uiNew(uiDrawTextFont);
font->f = df;
if (addRef)
font->f->AddRef();
if (copyFamily) {
copy = (WCHAR *) uiAlloc((wcslen(family) + 1) * sizeof (WCHAR), "WCHAR[]");
wcscpy(copy, family);
font->family = copy;
} else
font->family = family;
font->size = size;
return font;
}
// TODO MinGW-w64 is missing this one
#define DWRITE_FONT_WEIGHT_SEMI_LIGHT (DWRITE_FONT_WEIGHT(350))
uiDrawTextFont *uiDrawLoadClosestFont(const uiDrawTextFontDescriptor *desc)
{
uiDrawTextFont *font;
IDWriteFontCollection *collection;
UINT32 index;
BOOL exists;
struct dwriteAttr attr;
IDWriteFontFamily *family;
WCHAR *wfamily;
IDWriteFont *match;
HRESULT hr;
// always get the latest available font information
hr = dwfactory->GetSystemFontCollection(&collection, TRUE);
if (hr != S_OK)
logHRESULT(L"error getting system font collection", hr);
wfamily = toUTF16(desc->Family);
hr = collection->FindFamilyName(wfamily, &index, &exists);
if (hr != S_OK)
logHRESULT(L"error finding font family", hr);
if (!exists)
implbug("LONGTERM family not found in uiDrawLoadClosestFont()", hr);
hr = collection->GetFontFamily(index, &family);
if (hr != S_OK)
logHRESULT(L"error loading font family", hr);
attr.weight = desc->Weight;
attr.italic = desc->Italic;
attr.stretch = desc->Stretch;
attrToDWriteAttr(&attr);
hr = family->GetFirstMatchingFont(
attr.dweight,
attr.dstretch,
attr.ditalic,
&match);
if (hr != S_OK)
logHRESULT(L"error loading font", hr);
font = mkTextFont(match,
FALSE, // we own the initial reference; no need to add another one
wfamily, FALSE, // will be freed with font
desc->Size);
family->Release();
collection->Release();
return font;
}
void uiDrawFreeTextFont(uiDrawTextFont *font)
{
font->f->Release();
uiFree(font->family);
uiFree(font);
}
uintptr_t uiDrawTextFontHandle(uiDrawTextFont *font)
{
return (uintptr_t) (font->f);
}
void uiDrawTextFontDescribe(uiDrawTextFont *font, uiDrawTextFontDescriptor *desc)
{
// TODO
desc->Size = font->size;
// TODO
}
// text sizes are 1/72 of an inch
// points in Direct2D are 1/96 of an inch (https://msdn.microsoft.com/en-us/library/windows/desktop/ff684173%28v=vs.85%29.aspx, https://msdn.microsoft.com/en-us/library/windows/desktop/hh447022%28v=vs.85%29.aspx)
// As for the actual conversion from design units, see:
// - http://cboard.cprogramming.com/windows-programming/136733-directwrite-font-height-issues.html
// - https://sourceforge.net/p/vstgui/mailman/message/32483143/
// - http://xboxforums.create.msdn.com/forums/t/109445.aspx
// - https://msdn.microsoft.com/en-us/library/dd183564%28v=vs.85%29.aspx
// - http://www.fontbureau.com/blog/the-em/
// TODO make points here about how DIPs in DirectWrite == DIPs in Direct2D; if not, figure out what they really are? for the width and layout functions later
static double scaleUnits(double what, double designUnitsPerEm, double size)
{
return (what / designUnitsPerEm) * (size * (96.0 / 72.0));
}
void uiDrawTextFontGetMetrics(uiDrawTextFont *font, uiDrawTextFontMetrics *metrics)
{
DWRITE_FONT_METRICS dm;
font->f->GetMetrics(&dm);
metrics->Ascent = scaleUnits(dm.ascent, dm.designUnitsPerEm, font->size);
metrics->Descent = scaleUnits(dm.descent, dm.designUnitsPerEm, font->size);
// TODO what happens if dm.xxx is negative?
// TODO remember what this was for
metrics->Leading = scaleUnits(dm.lineGap, dm.designUnitsPerEm, font->size);
metrics->UnderlinePos = scaleUnits(dm.underlinePosition, dm.designUnitsPerEm, font->size);
metrics->UnderlineThickness = scaleUnits(dm.underlineThickness, dm.designUnitsPerEm, font->size);
}
// some attributes, such as foreground color, can't be applied until after we establish a Direct2D context :/ so we have to prepare all attributes in advance
// also since there's no way to clear the attributes from a layout en masse (apart from overwriting them all), we'll play it safe by creating a new layout each time
enum layoutAttrType {
layoutAttrColor,
};
struct layoutAttr {
enum layoutAttrType type;
int start;
int end;
double components[4];
};
struct uiDrawTextLayout {
WCHAR *text;
size_t textlen;
size_t *graphemes;
double width;
IDWriteTextFormat *format;
std::vector<struct layoutAttr> *attrs;
};
uiDrawTextLayout *uiDrawNewTextLayout(const char *text, uiDrawTextFont *defaultFont, double width)
{
uiDrawTextLayout *layout;
HRESULT hr;
layout = uiNew(uiDrawTextLayout);
hr = dwfactory->CreateTextFormat(defaultFont->family,
NULL,
defaultFont->f->GetWeight(),
defaultFont->f->GetStyle(),
defaultFont->f->GetStretch(),
if (hr != S_OK)
logHRESULT(L"error creating IDWriteTextFormat", hr);
layout->text = toUTF16(text);
layout->textlen = wcslen(layout->text);
layout->graphemes = graphemes(layout->text);
uiDrawTextLayoutSetWidth(layout, width);
layout->attrs = new std::vector<struct layoutAttr>;
return layout;
}
void uiDrawFreeTextLayout(uiDrawTextLayout *layout)
{
delete layout->attrs;
layout->format->Release();
uiFree(layout->graphemes);
uiFree(layout->text);
uiFree(layout);
}
IDWriteTextLayout *prepareLayout(uiDrawTextLayout *layout, ID2D1RenderTarget *rt)
{
IDWriteTextLayout *dl;
DWRITE_TEXT_RANGE range;
IUnknown *unkBrush;
HRESULT hr;
for (const struct layoutAttr &attr : *(layout->attrs)) {
range.startPosition = layout->graphemes[attr.start];
range.length = layout->graphemes[attr.end] - layout->graphemes[attr.start];
switch (attr.type) {
case layoutAttrColor:
if (rt == NULL) // determining extents, not drawing
break;
unkBrush = mkSolidBrush(rt,
attr.components[0],
attr.components[1],
attr.components[2],
attr.components[3]);
hr = dl->SetDrawingEffect(unkBrush, range);
unkBrush->Release(); // associated with dl
break;
default:
hr = E_FAIL;
logHRESULT(L"invalid text attribute type", hr);
}
if (hr != S_OK)
logHRESULT(L"error adding attribute to text layout", hr);
}
return dl;
}
void uiDrawText(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout)
{
IDWriteTextLayout *dl;
D2D1_POINT_2F pt;
ID2D1Brush *black;
HRESULT hr;
// TODO document that fully opaque black is the default text color; figure out whether this is upheld in various scenarios on other platforms
black = mkSolidBrush(c->rt, 0.0, 0.0, 0.0, 1.0);
dl = prepareLayout(layout, c->rt);
pt.x = x;
pt.y = y;
// TODO D2D1_DRAW_TEXT_OPTIONS_NO_SNAP?
// TODO D2D1_DRAW_TEXT_OPTIONS_CLIP?
// TODO when setting 8.1 as minimum, D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT?
c->rt->DrawTextLayout(pt, dl, black, D2D1_DRAW_TEXT_OPTIONS_NONE);
dl->Release();
black->Release();
}
void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, int startChar, int endChar, double r, double g, double b, double a)
{
struct layoutAttr attr;
attr.type = layoutAttrColor;
attr.start = startChar;
attr.end = endChar;
attr.components[0] = r;
attr.components[1] = g;
attr.components[2] = b;
attr.components[3] = a;
layout->attrs->push_back(attr);
}

View File

@ -1,667 +0,0 @@
// 17 january 2017
#include "uipriv_windows.hpp"
#include "draw.hpp"
// TODO
// - consider the warnings about antialiasing in the PadWrite sample
// - if that's not a problem, do we have overlapping rects in the hittest sample? I can't tell...
// - empty string: nLines == 1 and all checks out except extents has x == 0 when not left aligned
// - paragraph alignment is subject to RTL mirroring; see if it is on other platforms
// - add overhang info to metrics?
// TODO verify our renderer is correct, especially with regards to snapping
struct uiDrawTextLayout {
IDWriteTextFormat *format;
IDWriteTextLayout *layout;
std::vector<backgroundFunc> *backgroundFuncs;
UINT32 nLines;
struct lineInfo *lineInfo;
// for converting DirectWrite indices from/to byte offsets
size_t *u8tou16;
size_t nUTF8;
size_t *u16tou8;
size_t nUTF16;
};
// TODO copy notes about DirectWrite DIPs being equal to Direct2D DIPs here
// typographic points are 1/72 inch; this parameter is 1/96 inch
// fortunately Microsoft does this too, in https://msdn.microsoft.com/en-us/library/windows/desktop/dd371554%28v=vs.85%29.aspx
#define pointSizeToDWriteSize(size) (size * (96.0 / 72.0))
struct lineInfo {
size_t startPos; // in UTF-16 points
size_t endPos;
size_t newlineCount;
double x;
double y;
double width;
double height;
double baseline;
};
// this function is deeply indebted to the PadWrite sample: https://github.com/Microsoft/Windows-classic-samples/blob/master/Samples/Win7Samples/multimedia/DirectWrite/PadWrite/TextEditor.cpp
static void computeLineInfo(uiDrawTextLayout *tl)
{
DWRITE_LINE_METRICS *dlm;
size_t nextStart;
UINT32 i, j;
DWRITE_HIT_TEST_METRICS *htm;
UINT32 nFragments, unused;
HRESULT hr;
// TODO make sure this is legal; if not, switch to GetMetrics() and use its line count field instead
hr = tl->layout->GetLineMetrics(NULL, 0, &(tl->nLines));
// ugh, HRESULT_TO_WIN32() is an inline function and is not constexpr so we can't use switch here
if (hr == S_OK) {
// TODO what do we do here
} else if (hr != E_NOT_SUFFICIENT_BUFFER)
logHRESULT(L"error getting number of lines in IDWriteTextLayout", hr);
tl->lineInfo = (struct lineInfo *) uiAlloc(tl->nLines * sizeof (struct lineInfo), "struct lineInfo[] (text layout)");
dlm = new DWRITE_LINE_METRICS[tl->nLines];
// we can't pass NULL here; it outright crashes if we do
// TODO verify the numbers haven't changed
hr = tl->layout->GetLineMetrics(dlm, tl->nLines, &unused);
if (hr != S_OK)
logHRESULT(L"error getting IDWriteTextLayout line metrics", hr);
// assume the first line starts at position 0 and the string flow is incremental
nextStart = 0;
for (i = 0; i < tl->nLines; i++) {
tl->lineInfo[i].startPos = nextStart;
tl->lineInfo[i].endPos = nextStart + dlm[i].length;
tl->lineInfo[i].newlineCount = dlm[i].newlineLength;
nextStart = tl->lineInfo[i].endPos;
// a line can have multiple fragments; for example, if there's a bidirectional override in the middle of a line
hr = tl->layout->HitTestTextRange(tl->lineInfo[i].startPos, (tl->lineInfo[i].endPos - tl->lineInfo[i].newlineCount) - tl->lineInfo[i].startPos,
0, 0,
NULL, 0, &nFragments);
if (hr != S_OK && hr != E_NOT_SUFFICIENT_BUFFER)
logHRESULT(L"error getting IDWriteTextLayout line fragment count", hr);
htm = new DWRITE_HIT_TEST_METRICS[nFragments];
// TODO verify unused == nFragments?
hr = tl->layout->HitTestTextRange(tl->lineInfo[i].startPos, (tl->lineInfo[i].endPos - tl->lineInfo[i].newlineCount) - tl->lineInfo[i].startPos,
0, 0,
htm, nFragments, &unused);
// TODO can this return E_NOT_SUFFICIENT_BUFFER again?
if (hr != S_OK)
logHRESULT(L"error getting IDWriteTextLayout line fragment metrics", hr);
// TODO verify htm.textPosition and htm.length against dtm[i]/tl->lineInfo[i]?
tl->lineInfo[i].x = htm[0].left;
tl->lineInfo[i].y = htm[0].top;
// TODO does this not include trailing whitespace? I forget
tl->lineInfo[i].width = htm[0].width;
tl->lineInfo[i].height = htm[0].height;
for (j = 1; j < nFragments; j++) {
// this is correct even if the leftmost fragment on the line is RTL
if (tl->lineInfo[i].x > htm[j].left)
tl->lineInfo[i].x = htm[j].left;
tl->lineInfo[i].width += htm[j].width;
// TODO verify y and height haven't changed?
}
// TODO verify dlm[i].height == htm.height?
delete[] htm;
// TODO on Windows 8.1 and/or 10 we can use DWRITE_LINE_METRICS1 to get specific info about the ascent and descent; do we have an alternative?
// TODO and even on those platforms can we somehow split tyographic leading from spacing?
// TODO and on that note, can we have both line spacing proportionally above and uniformly below?
tl->lineInfo[i].baseline = dlm[i].baseline;
}
delete[] dlm;
}
// TODO should be const but then I can't operator[] on it; the real solution is to find a way to do designated array initializers in C++11 but I do not know enough C++ voodoo to make it work (it is possible but no one else has actually done it before)
static std::map<uiDrawTextAlign, DWRITE_TEXT_ALIGNMENT> dwriteAligns = {
{ uiDrawTextAlignLeft, DWRITE_TEXT_ALIGNMENT_LEADING },
{ uiDrawTextAlignCenter, DWRITE_TEXT_ALIGNMENT_CENTER },
{ uiDrawTextAlignRight, DWRITE_TEXT_ALIGNMENT_TRAILING },
};
uiDrawTextLayout *uiDrawNewTextLayout(uiDrawTextLayoutParams *p)
{
uiDrawTextLayout *tl;
WCHAR *wDefaultFamily;
DWRITE_WORD_WRAPPING wrap;
FLOAT maxWidth;
HRESULT hr;
tl = uiNew(uiDrawTextLayout);
wDefaultFamily = toUTF16(p->DefaultFont->Family);
hr = dwfactory->CreateTextFormat(
wDefaultFamily, NULL,
uiprivWeightToDWriteWeight(p->DefaultFont->Weight),
uiprivItalicToDWriteStyle(p->DefaultFont->Italic),
uiprivStretchToDWriteStretch(p->DefaultFont->Stretch),
pointSizeToDWriteSize(p->DefaultFont->Size),
// see http://stackoverflow.com/questions/28397971/idwritefactorycreatetextformat-failing and https://msdn.microsoft.com/en-us/library/windows/desktop/dd368203.aspx
// TODO use the current locale?
L"",
&(tl->format));
if (hr != S_OK)
logHRESULT(L"error creating IDWriteTextFormat", hr);
hr = tl->format->SetTextAlignment(dwriteAligns[p->Align]);
if (hr != S_OK)
logHRESULT(L"error applying text layout alignment", hr);
hr = dwfactory->CreateTextLayout(
(const WCHAR *) attrstrUTF16(p->String), attrstrUTF16Len(p->String),
tl->format,
// FLOAT is float, not double, so this should work... TODO
FLT_MAX, FLT_MAX,
&(tl->layout));
if (hr != S_OK)
logHRESULT(L"error creating IDWriteTextLayout", hr);
// and set the width
// this is the only wrapping mode (apart from "no wrap") available prior to Windows 8.1 (TODO verify this fact) (TODO this should be the default anyway)
wrap = DWRITE_WORD_WRAPPING_WRAP;
maxWidth = (FLOAT) (p->Width);
if (p->Width < 0) {
// TODO is this wrapping juggling even necessary?
wrap = DWRITE_WORD_WRAPPING_NO_WRAP;
// setting the max width in this case technically isn't needed since the wrap mode will simply ignore the max width, but let's do it just to be safe
maxWidth = FLT_MAX; // see TODO above
}
hr = tl->layout->SetWordWrapping(wrap);
if (hr != S_OK)
logHRESULT(L"error setting IDWriteTextLayout word wrapping mode", hr);
hr = tl->layout->SetMaxWidth(maxWidth);
if (hr != S_OK)
logHRESULT(L"error setting IDWriteTextLayout max layout width", hr);
attrstrToIDWriteTextLayoutAttrs(p, tl->layout, &(tl->backgroundFuncs));
computeLineInfo(tl);
// and finally copy the UTF-8/UTF-16 index conversion tables
tl->u8tou16 = attrstrCopyUTF8ToUTF16(p->String, &(tl->nUTF8));
tl->u16tou8 = attrstrCopyUTF16ToUTF8(p->String, &(tl->nUTF16));
// TODO can/should this be moved elsewhere?
uiFree(wDefaultFamily);
return tl;
}
void uiDrawFreeTextLayout(uiDrawTextLayout *tl)
{
uiFree(tl->u16tou8);
uiFree(tl->u8tou16);
uiFree(tl->lineInfo);
delete tl->backgroundFuncs;
tl->layout->Release();
tl->format->Release();
uiFree(tl);
}
static HRESULT mkSolidBrush(ID2D1RenderTarget *rt, double r, double g, double b, double a, ID2D1SolidColorBrush **brush)
{
D2D1_BRUSH_PROPERTIES props;
D2D1_COLOR_F color;
ZeroMemory(&props, sizeof (D2D1_BRUSH_PROPERTIES));
props.opacity = 1.0;
// identity matrix
props.transform._11 = 1;
props.transform._22 = 1;
color.r = r;
color.g = g;
color.b = b;
color.a = a;
return rt->CreateSolidColorBrush(
&color,
&props,
brush);
}
static ID2D1SolidColorBrush *mustMakeSolidBrush(ID2D1RenderTarget *rt, double r, double g, double b, double a)
{
ID2D1SolidColorBrush *brush;
HRESULT hr;
hr = mkSolidBrush(rt, r, g, b, a, &brush);
if (hr != S_OK)
logHRESULT(L"error creating solid brush", hr);
return brush;
}
// some of the stuff we want to do isn't possible with what DirectWrite provides itself; we need to do it ourselves
// this is based on http://www.charlespetzold.com/blog/2014/01/Character-Formatting-Extensions-with-DirectWrite.html
class textRenderer : public IDWriteTextRenderer {
ULONG refcount;
ID2D1RenderTarget *rt;
BOOL snap;
ID2D1SolidColorBrush *black;
public:
textRenderer(ID2D1RenderTarget *rt, BOOL snap, ID2D1SolidColorBrush *black)
{
this->refcount = 1;
this->rt = rt;
this->snap = snap;
this->black = black;
}
// IUnknown
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject)
{
if (ppvObject == NULL)
return E_POINTER;
if (riid == IID_IUnknown ||
riid == __uuidof (IDWritePixelSnapping) ||
riid == __uuidof (IDWriteTextRenderer)) {
this->AddRef();
*ppvObject = this;
return S_OK;
}
*ppvObject = NULL;
return E_NOINTERFACE;
}
virtual ULONG STDMETHODCALLTYPE AddRef(void)
{
this->refcount++;
return this->refcount;
}
virtual ULONG STDMETHODCALLTYPE Release(void)
{
this->refcount--;
if (this->refcount == 0) {
delete this;
return 0;
}
return this->refcount;
}
// IDWritePixelSnapping
virtual HRESULT STDMETHODCALLTYPE GetCurrentTransform(void *clientDrawingContext, DWRITE_MATRIX *transform)
{
D2D1_MATRIX_3X2_F d2dtf;
if (transform == NULL)
return E_POINTER;
this->rt->GetTransform(&d2dtf);
transform->m11 = d2dtf._11;
transform->m12 = d2dtf._12;
transform->m21 = d2dtf._21;
transform->m22 = d2dtf._22;
transform->dx = d2dtf._31;
transform->dy = d2dtf._32;
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE GetPixelsPerDip(void *clientDrawingContext, FLOAT *pixelsPerDip)
{
FLOAT dpix, dpiy;
if (pixelsPerDip == NULL)
return E_POINTER;
this->rt->GetDpi(&dpix, &dpiy);
*pixelsPerDip = dpix / 96;
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE IsPixelSnappingDisabled(void *clientDrawingContext, BOOL *isDisabled)
{
if (isDisabled == NULL)
return E_POINTER;
*isDisabled = !this->snap;
return S_OK;
}
// IDWriteTextRenderer
virtual HRESULT STDMETHODCALLTYPE DrawGlyphRun(void *clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, DWRITE_MEASURING_MODE measuringMode, const DWRITE_GLYPH_RUN *glyphRun, const DWRITE_GLYPH_RUN_DESCRIPTION *glyphRunDescription, IUnknown *clientDrawingEffect)
{
D2D1_POINT_2F baseline;
textDrawingEffect *t = (textDrawingEffect *) clientDrawingEffect;
ID2D1SolidColorBrush *brush;
baseline.x = baselineOriginX;
baseline.y = baselineOriginY;
brush = this->black;
if (t != NULL && t->hasColor) {
HRESULT hr;
hr = mkSolidBrush(this->rt, t->r, t->g, t->b, t->a, &brush);
if (hr != S_OK)
return hr;
}
this->rt->DrawGlyphRun(
baseline,
glyphRun,
brush,
measuringMode);
if (t != NULL && t->hasColor)
brush->Release();
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE DrawInlineObject(void *clientDrawingContext, FLOAT originX, FLOAT originY, IDWriteInlineObject *inlineObject, BOOL isSideways, BOOL isRightToLeft, IUnknown *clientDrawingEffect)
{
if (inlineObject == NULL)
return E_POINTER;
return inlineObject->Draw(clientDrawingContext, this,
originX, originY,
isSideways, isRightToLeft,
clientDrawingEffect);
}
virtual HRESULT STDMETHODCALLTYPE DrawStrikethrough(void *clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, const DWRITE_STRIKETHROUGH *strikethrough, IUnknown *clientDrawingEffect)
{
// we don't support strikethrough
return E_UNEXPECTED;
}
virtual HRESULT STDMETHODCALLTYPE DrawUnderline(void *clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, const DWRITE_UNDERLINE *underline, IUnknown *clientDrawingEffect)
{
textDrawingEffect *t = (textDrawingEffect *) clientDrawingEffect;
ID2D1SolidColorBrush *brush;
D2D1_RECT_F rect;
D2D1::Matrix3x2F pixeltf;
FLOAT dpix, dpiy;
D2D1_POINT_2F pt;
if (underline == NULL)
return E_POINTER;
if (t == NULL) // we can only get here through an underline
return E_UNEXPECTED;
brush = this->black;
if (t->hasUnderlineColor) {
HRESULT hr;
hr = mkSolidBrush(this->rt, t->ur, t->ug, t->ub, t->ua, &brush);
if (hr != S_OK)
return hr;
} else if (t->hasColor) {
// TODO formalize this rule
HRESULT hr;
hr = mkSolidBrush(this->rt, t->r, t->g, t->b, t->a, &brush);
if (hr != S_OK)
return hr;
}
rect.left = baselineOriginX;
rect.top = baselineOriginY + underline->offset;
rect.right = rect.left + underline->width;
rect.bottom = rect.top + underline->thickness;
switch (t->u) {
case uiDrawUnderlineStyleSingle:
this->rt->FillRectangle(&rect, brush);
break;
case uiDrawUnderlineStyleDouble:
// TODO do any of the matrix methods return errors?
// TODO standardize double-underline shape across platforms? wavy underline shape?
this->rt->GetTransform(&pixeltf);
this->rt->GetDpi(&dpix, &dpiy);
pixeltf = pixeltf * D2D1::Matrix3x2F::Scale(dpix / 96, dpiy / 96);
pt.x = 0;
pt.y = rect.top;
pt = pixeltf.TransformPoint(pt);
rect.top = (FLOAT) ((int) (pt.y + 0.5));
pixeltf.Invert();
pt = pixeltf.TransformPoint(pt);
rect.top = pt.y;
// first line
rect.top -= underline->thickness;
// and it seems we need to recompute this
rect.bottom = rect.top + underline->thickness;
this->rt->FillRectangle(&rect, brush);
// second line
rect.top += 2 * underline->thickness;
rect.bottom = rect.top + underline->thickness;
this->rt->FillRectangle(&rect, brush);
break;
case uiDrawUnderlineStyleSuggestion:
{ // TODO get rid of the extra block
// TODO properly clean resources on failure
// TODO use fully qualified C overloads for all methods
// TODO ensure all methods properly have errors handled
ID2D1PathGeometry *path;
ID2D1GeometrySink *sink;
double amplitude, period, xOffset, yOffset;
double t;
bool first = true;
HRESULT hr;
hr = d2dfactory->CreatePathGeometry(&path);
if (hr != S_OK)
return hr;
hr = path->Open(&sink);
if (hr != S_OK)
return hr;
amplitude = underline->thickness;
period = 5 * underline->thickness;
xOffset = baselineOriginX;
yOffset = baselineOriginY + underline->offset;
for (t = 0; t < underline->width; t++) {
double x, angle, y;
D2D1_POINT_2F pt;
x = t + xOffset;
angle = 2 * uiPi * fmod(x, period) / period;
y = amplitude * sin(angle) + yOffset;
pt.x = x;
pt.y = y;
if (first) {
sink->BeginFigure(pt, D2D1_FIGURE_BEGIN_HOLLOW);
first = false;
} else
sink->AddLine(pt);
}
sink->EndFigure(D2D1_FIGURE_END_OPEN);
hr = sink->Close();
if (hr != S_OK)
return hr;
sink->Release();
this->rt->DrawGeometry(path, brush, underline->thickness);
path->Release();
}
break;
}
if (t->hasUnderlineColor || t->hasColor)
brush->Release();
return S_OK;
}
};
// TODO this ignores clipping?
void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y)
{
D2D1_POINT_2F pt;
ID2D1SolidColorBrush *black;
textRenderer *renderer;
HRESULT hr;
for (const auto &f : *(tl->backgroundFuncs))
f(c, tl, x, y);
// TODO document that fully opaque black is the default text color; figure out whether this is upheld in various scenarios on other platforms
// TODO figure out if this needs to be cleaned out
black = mustMakeSolidBrush(c->rt, 0.0, 0.0, 0.0, 1.0);
#define renderD2D 0
#define renderOur 1
#if renderD2D
pt.x = x;
pt.y = y;
// TODO D2D1_DRAW_TEXT_OPTIONS_NO_SNAP?
// TODO D2D1_DRAW_TEXT_OPTIONS_CLIP?
// TODO LONGTERM when setting 8.1 as minimum (TODO verify), D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT?
// TODO what is our pixel snapping setting related to the OPTIONS enum values?
c->rt->DrawTextLayout(pt, tl->layout, black, D2D1_DRAW_TEXT_OPTIONS_NONE);
#endif
#if renderD2D && renderOur
// draw ours semitransparent so we can check
// TODO get the actual color Charles Petzold uses and use that
black->Release();
black = mustMakeSolidBrush(c->rt, 1.0, 0.0, 0.0, 0.75);
#endif
#if renderOur
renderer = new textRenderer(c->rt,
TRUE, // TODO FALSE for no-snap?
black);
hr = tl->layout->Draw(NULL,
renderer,
x, y);
if (hr != S_OK)
logHRESULT(L"error drawing IDWriteTextLayout", hr);
renderer->Release();
#endif
black->Release();
}
// TODO for a single line the height includes the leading; should it? TextEdit on OS X always includes the leading and/or paragraph spacing, otherwise Klee won't work...
// TODO width does not include trailing whitespace
void uiDrawTextLayoutExtents(uiDrawTextLayout *tl, double *width, double *height)
{
DWRITE_TEXT_METRICS metrics;
HRESULT hr;
hr = tl->layout->GetMetrics(&metrics);
if (hr != S_OK)
logHRESULT(L"error getting IDWriteTextLayout layout metrics", hr);
*width = metrics.width;
// TODO make sure the behavior of this on empty strings is the same on all platforms (ideally should be 0-width, line height-height; TODO note this in the docs too)
*height = metrics.height;
}
int uiDrawTextLayoutNumLines(uiDrawTextLayout *tl)
{
return tl->nLines;
}
// DirectWrite doesn't provide a direct way to do this, so we have to do this manually
// TODO does that comment still apply here or to the code at the top of this file?
void uiDrawTextLayoutLineByteRange(uiDrawTextLayout *tl, int line, size_t *start, size_t *end)
{
*start = tl->lineInfo[line].startPos;
*start = tl->u16tou8[*start];
*end = tl->lineInfo[line].endPos - tl->lineInfo[line].newlineCount;
*end = tl->u16tou8[*end];
}
void uiDrawTextLayoutLineGetMetrics(uiDrawTextLayout *tl, int line, uiDrawTextLayoutLineMetrics *m)
{
m->X = tl->lineInfo[line].x;
m->Y = tl->lineInfo[line].y;
m->Width = tl->lineInfo[line].width;
m->Height = tl->lineInfo[line].height;
// TODO rename tl->lineInfo[line].baseline to .baselineOffset or something of the sort to make its meaning more clear
m->BaselineY = tl->lineInfo[line].y + tl->lineInfo[line].baseline;
m->Ascent = tl->lineInfo[line].baseline;
m->Descent = tl->lineInfo[line].height - tl->lineInfo[line].baseline;
m->Leading = 0; // TODO
m->ParagraphSpacingBefore = 0; // TODO
m->LineHeightSpace = 0; // TODO
m->LineSpacing = 0; // TODO
m->ParagraphSpacing = 0; // TODO
}
// this algorithm comes from Microsoft's PadWrite sample, following TextEditor::SetSelectionFromPoint()
// TODO go back through all of these and make sure we convert coordinates properly
// TODO same for OS X
void uiDrawTextLayoutHitTest(uiDrawTextLayout *tl, double x, double y, size_t *pos, int *line)
{
DWRITE_HIT_TEST_METRICS m;
BOOL trailing, inside;
size_t p;
UINT32 i;
HRESULT hr;
hr = tl->layout->HitTestPoint(x, y,
&trailing, &inside,
&m);
if (hr != S_OK)
logHRESULT(L"error hit-testing IDWriteTextLayout", hr);
p = m.textPosition;
// on a trailing hit, align to the nearest cluster
if (trailing) {
DWRITE_HIT_TEST_METRICS m2;
FLOAT x, y; // crashes if I skip these :/
hr = tl->layout->HitTestTextPosition(m.textPosition, trailing,
&x, &y, &m2);
if (hr != S_OK)
logHRESULT(L"error aligning trailing hit to nearest cluster", hr);
p = m2.textPosition + m2.length;
}
*pos = tl->u16tou8[p];
for (i = 0; i < tl->nLines; i++) {
double ltop, lbottom;
ltop = tl->lineInfo[i].y;
lbottom = ltop + tl->lineInfo[i].height;
// y will already >= ltop at this point since the past lbottom should == ltop
if (y < lbottom)
break;
}
if (i == tl->nLines)
i--;
*line = i;
}
double uiDrawTextLayoutByteLocationInLine(uiDrawTextLayout *tl, size_t pos, int line)
{
BOOL trailing;
DWRITE_HIT_TEST_METRICS m;
FLOAT x, y;
HRESULT hr;
if (line < 0 || line >= tl->nLines)
return -1;
pos = tl->u8tou16[pos];
// note: >, not >=, because the position at endPos is valid!
if (pos < tl->lineInfo[line].startPos || pos > tl->lineInfo[line].endPos)
return -1;
// this behavior seems correct
// there's also PadWrite's TextEditor::GetCaretRect() but that requires state...
// TODO where does this fail?
trailing = FALSE;
if (pos != 0 && pos != tl->nUTF16 && pos == tl->lineInfo[line].endPos) {
pos--;
trailing = TRUE;
}
hr = tl->layout->HitTestTextPosition(pos, trailing,
&x, &y, &m);
if (hr != S_OK)
logHRESULT(L"error calling IDWriteTextLayout::HitTestTextPosition()", hr);
return x;
}
void caretDrawParams(uiDrawContext *c, double height, struct caretDrawParams *p)
{
DWORD caretWidth;
// there seems to be no defined caret color
// the best I can come up with is "inverts colors underneath" (according to https://msdn.microsoft.com/en-us/library/windows/desktop/ms648397(v=vs.85).aspx) which I have no idea how to do (TODO)
// just return black for now
p->r = 0.0;
p->g = 0.0;
p->b = 0.0;
p->a = 1.0;
if (SystemParametersInfoW(SPI_GETCARETWIDTH, 0, &caretWidth, 0) == 0)
// don't log the failure, fall back gracefully
// the instruction to use this comes from https://msdn.microsoft.com/en-us/library/windows/desktop/ms648399(v=vs.85).aspx
// and we have to assume GetSystemMetrics() always succeeds, so
caretWidth = GetSystemMetrics(SM_CXBORDER);
// TODO make this a function and split it out of areautil.cpp
{
FLOAT dpix, dpiy;
// TODO can we pass NULL for dpiy?
c->rt->GetDpi(&dpix, &dpiy);
// see https://msdn.microsoft.com/en-us/library/windows/desktop/dd756649%28v=vs.85%29.aspx (and others; search "direct2d mouse")
p->width = ((double) (caretWidth * 96)) / dpix;
}
// and there doesn't seem to be this either... (TODO check what PadWrite does?)
p->xoff = 0;
}

View File

@ -1,912 +0,0 @@
// 11 february 2017
#include "drawtext.h"
static uiAttributedString *attrstr;
#define nFeatures 256
static uiOpenTypeFeatures *features[nFeatures];
static int curFeature = 0;
static uiOpenTypeFeatures *addFeature(const char tag[4], uint32_t value)
{
uiOpenTypeFeatures *otf;
if (curFeature >= nFeatures) {
fprintf(stderr, "TODO (also TODO is there a panic function?)\n");
exit(EXIT_FAILURE);
}
otf = uiNewOpenTypeFeatures();
uiOpenTypeFeaturesAdd(otf, tag[0], tag[1], tag[2], tag[3], value);
features[curFeature] = otf;
curFeature++;
return otf;
}
// some of these examples come from Microsoft's and Apple's lists of typographic features and also https://www.fontfont.com/staticcontent/downloads/FF_OT_User_Guide.pdf
static void setupAttributedString(void)
{
uiAttributeSpec spec;
size_t start, end;
const char *next;
uiOpenTypeFeatures *otf;
int i;
attrstr = uiNewAttributedString("uiAttributedString isn't just for plain text! It supports ");
next = "multiple fonts";
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFamily;
spec.Family = "Courier New";
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ", ");
next = "multiple sizes";
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeSize;
spec.Double = 18;
uiAttributedStringSetAttribute(attrstr,
&spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ", ");
next = "multiple weights";
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeWeight;
spec.Value = (uintptr_t) uiDrawTextWeightBold;
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ", ");
next = "multiple italics";
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeItalic;
spec.Value = (uintptr_t) uiDrawTextItalicItalic;
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ", ");
next = "multiple stretches";
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeStretch;
spec.Value = (uintptr_t) uiDrawTextStretchCondensed;
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ", ");
next = "multiple colors";
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeColor;
// Direct2D "Crimson" (#DC143C)
spec.R = 0.8627450980392156;
spec.G = 0.0784313725490196;
spec.B = 0.2352941176470588;
spec.A = 0.75;
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ", ");
next = "multiple backgrounds";
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeBackground;
// Direct2D "Peach Puff" (#FFDAB9)
// TODO choose a darker color
spec.R = 1.0;
spec.G = 0.85490196078431372;
spec.B = 0.7254901960784313;
spec.A = 0.5;
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ", ");
next = "multiple";
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeUnderline;
spec.Value = uiDrawUnderlineStyleSingle;
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, " ");
next = "underlines";
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeUnderline;
spec.Value = uiDrawUnderlineStyleDouble;
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
spec.Type = uiAttributeUnderlineColor;
spec.Value = uiDrawUnderlineColorCustom;
spec.R = 0.5;
spec.G = 0.0;
spec.B = 1.0;
spec.A = 1.0;
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, " (");
next = "including underlines for spelling correction and the like";
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeUnderline;
spec.Value = uiDrawUnderlineStyleSuggestion;
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
spec.Type = uiAttributeUnderlineColor;
spec.Value = uiDrawUnderlineColorSpelling;
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ")");
uiAttributedStringAppendUnattributed(attrstr, ", ");
// TODO randomize these ranges better
// TODO make some overlap to test that
// TODO also change colors to light foreground dark background
next = "or any combination of the above";
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeWeight;
spec.Value = (uintptr_t) uiDrawTextWeightBold;
uiAttributedStringSetAttribute(attrstr, &spec, start, end - 8);
spec.Type = uiAttributeItalic;
spec.Value = (uintptr_t) uiDrawTextItalicItalic;
uiAttributedStringSetAttribute(attrstr, &spec, start + 3, end - 4);
spec.Type = uiAttributeColor;
spec.R = 0.8627450980392156;
spec.G = 0.0784313725490196;
spec.B = 0.2352941176470588;
spec.A = 0.75;
uiAttributedStringSetAttribute(attrstr, &spec, start + 12, end);
spec.Type = uiAttributeFamily;
spec.Family = "Helvetica";
uiAttributedStringSetAttribute(attrstr, &spec, start + 8, end - 1);
spec.Type = uiAttributeBackground;
spec.R = 1.0;
spec.G = 0.85490196078431372;
spec.B = 0.7254901960784313;
spec.A = 0.5;
uiAttributedStringSetAttribute(attrstr, &spec, start + 5, end - 7);
spec.Type = uiAttributeUnderline;
spec.Value = uiDrawUnderlineStyleSingle;
uiAttributedStringSetAttribute(attrstr, &spec, start + 9, end - 1);
// TODO rewrite this to talk about OpenTpe instead
// TODO also shorten this to something more useful and that covers the general gist of things (and combines features arbitrarily like the previous demo) when we add a general OpenType demo (see the last TODO in this function)
uiAttributedStringAppendUnattributed(attrstr, ". In addition, a variety of typographical features are available (depending on the chosen font) that can be switched on (or off, if the font enables them by default): ");
next = "fi";
uiAttributedStringAppendUnattributed(attrstr, "standard ligatures like f+i (");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("liga", 1);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ")");
uiAttributedStringAppendUnattributed(attrstr, ", ");
// note the use of LTR marks and RTL embeds to make sure the bidi algorithm doesn't kick in for our demonstration (it will produce incorrect results)
// see also: https://www.w3.org/International/articles/inline-bidi-markup/#nomarkup
next = "\xD9\x84\xD8\xA7";
uiAttributedStringAppendUnattributed(attrstr, "required ligatures like \xE2\x80\xAB\xD9\x84\xE2\x80\xAC+\xE2\x80\xAB\xD8\xA7\xE2\x80\xAC (\xE2\x80\x8E\xE2\x80\xAB");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("rlig", 1);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, "\xE2\x80\xAC)");
uiAttributedStringAppendUnattributed(attrstr, ", ");
next = "ct";
uiAttributedStringAppendUnattributed(attrstr, "discretionary/rare ligatures like c+t (");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("dlig", 1);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ")");
uiAttributedStringAppendUnattributed(attrstr, ", ");
next = "the";
uiAttributedStringAppendUnattributed(attrstr, "contextual ligatures like h+e in the (");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("clig", 1);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ")");
uiAttributedStringAppendUnattributed(attrstr, ", ");
otf = addFeature("hlig", 1);
// This technically isn't what is meant by "historical ligatures", but Core Text's internal AAT-to-OpenType mapping says to include it, so we include it too
uiOpenTypeFeaturesAdd(otf, 'h', 'i', 's', 't', 1);
next = "\xC3\x9F";
uiAttributedStringAppendUnattributed(attrstr, "historical ligatures like the decomposition of \xC3\x9F (");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = otf;
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ")");
uiAttributedStringAppendUnattributed(attrstr, ", ");
// TODO a different word than "writing"?
next = "UnICasE wRITInG";
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("unic", 1);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ", ");
next = "316";
uiAttributedStringAppendUnattributed(attrstr, "proportional (");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("pnum", 1);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ") and tabular/monospaced (");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("tnum", 1);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ") numbers");
uiAttributedStringAppendUnattributed(attrstr, ", ");
next = "123";
uiAttributedStringAppendUnattributed(attrstr, "superscipts (");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("sups", 1);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ")");
uiAttributedStringAppendUnattributed(attrstr, ", ");
next = "123";
uiAttributedStringAppendUnattributed(attrstr, "subscripts (");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("subs", 1);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ")");
uiAttributedStringAppendUnattributed(attrstr, ", ");
next = "1st";
uiAttributedStringAppendUnattributed(attrstr, "ordinals (");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("ordn", 1);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ")");
uiAttributedStringAppendUnattributed(attrstr, ", ");
next = "H2O";
uiAttributedStringAppendUnattributed(attrstr, "scientific inferiors (");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("sinf", 1);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ")");
uiAttributedStringAppendUnattributed(attrstr, ", ");
next = "2/3";
uiAttributedStringAppendUnattributed(attrstr, "fraction forms (");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
#if 0 /* TODO */
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFractionForms;
spec.Value = uiAttributeFractionFormNone;
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ", ");
#endif
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("afrc", 1);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ", ");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("frac", 1);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ")");
uiAttributedStringAppendUnattributed(attrstr, ", ");
next = "0";
uiAttributedStringAppendUnattributed(attrstr, "slashed zeroes (");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("zero", 0);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, " vs. ");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("zero", 1);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ")");
uiAttributedStringAppendUnattributed(attrstr, ", ");
next = "\xCE\xA0\xCE\xA3";
uiAttributedStringAppendUnattributed(attrstr, "mathematical greek (");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("mgrk", 0);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, " vs. ");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("mgrk", 1);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ")");
uiAttributedStringAppendUnattributed(attrstr, ", ");
next = "qwertyuiop\xE2\x80\xA2";
uiAttributedStringAppendUnattributed(attrstr, "ornamental forms (");
for (i = 1; i < 11; i++) {
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("ornm", i);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
next = "\xE2\x80\xA2";
}
uiAttributedStringAppendUnattributed(attrstr, ")");
uiAttributedStringAppendUnattributed(attrstr, ", ");
next = "g";
uiAttributedStringAppendUnattributed(attrstr, "specific forms/alternates (");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("aalt", 0);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, " vs. ");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("aalt", 1);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ")");
uiAttributedStringAppendUnattributed(attrstr, ", ");
next = "ABCDEFGQWERTY";
uiAttributedStringAppendUnattributed(attrstr, "titling capital forms (");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("titl", 0);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, " vs. ");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("titl", 1);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ")");
uiAttributedStringAppendUnattributed(attrstr, ", ");
next = "\xE7\x80\x86";
uiAttributedStringAppendUnattributed(attrstr, "alternate Han character forms (");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("jp78", 1);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, " vs. ");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("jp83", 1);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ")");
uiAttributedStringAppendUnattributed(attrstr, ", ");
otf = addFeature("onum", 0);
// Core Text's internal AAT-to-OpenType mapping says to include this, so we include it too
// TODO is it always set?
uiOpenTypeFeaturesAdd(otf, 'l', 'n', 'u', 'm', 0);
next = "0123456789";
uiAttributedStringAppendUnattributed(attrstr, "lowercase numbers (");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = otf;
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, " vs. ");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
otf = addFeature("onum", 1);
uiOpenTypeFeaturesAdd(otf, 'l', 'n', 'u', 'm', 1);
spec.Features = otf;
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ")");
uiAttributedStringAppendUnattributed(attrstr, ", ");
next = "\xE4\xBC\xBD";
uiAttributedStringAppendUnattributed(attrstr, "hanja to hangul translation (");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("hngl", 0);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, " vs. ");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("hngl", 1);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ")");
uiAttributedStringAppendUnattributed(attrstr, ", ");
next = "\xE3\x81\x82";
uiAttributedStringAppendUnattributed(attrstr, "annotated glyph forms (");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("nalt", 0);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, " vs. ");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("nalt", 1);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, " vs. ");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("nalt", 4); // AAT inverted circle
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ")");
uiAttributedStringAppendUnattributed(attrstr, ", ");
next = "\xE3\x81\x82";
uiAttributedStringAppendUnattributed(attrstr, "ruby forms of kana (");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("ruby", 0);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, " vs. ");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("ruby", 1);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ")");
uiAttributedStringAppendUnattributed(attrstr, ", ");
next = "now is the time";
uiAttributedStringAppendUnattributed(attrstr, "italic forms of Latin letters in CJK fonts (");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("ital", 0);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, " vs. ");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("ital", 1);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ")");
uiAttributedStringAppendUnattributed(attrstr, ", ");
next = "{I} > {J}";
uiAttributedStringAppendUnattributed(attrstr, "case-sensitive character forms, such as punctuation (");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("case", 0);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, " vs. ");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("case", 1);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ")");
uiAttributedStringAppendUnattributed(attrstr, ", ");
next = "ABC";
uiAttributedStringAppendUnattributed(attrstr, "specialized spacing between uppercase letters (");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("cpsp", 0);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, " vs. ");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("cpsp", 1);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ")");
uiAttributedStringAppendUnattributed(attrstr, ", ");
next = "\xE3\x82\xB9\xE3\x83\x98\xE3\x83\x88";
uiAttributedStringAppendUnattributed(attrstr, "alternate horizontal (");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("hkna", 0);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, " vs. ");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("hkna", 1);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ") and vertical (");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("vkna", 0);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, " vs. ");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("vkna", 1);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ") kana forms");
uiAttributedStringAppendUnattributed(attrstr, ", ");
next = "g";
uiAttributedStringAppendUnattributed(attrstr, "stylistic alternates (");
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
for (i = 1; i <= 20; i++) {
char tag[4];
tag[0] = 's';
tag[1] = 's';
tag[2] = '0';
if (i >= 10)
tag[2] = '1';
tag[3] = (i % 10) + '0'; // TODO see how I wrote this elsewhere
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Features = addFeature(tag, 1);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
}
uiAttributedStringAppendUnattributed(attrstr, ")");
uiAttributedStringAppendUnattributed(attrstr, ", ");
next = "first";
uiAttributedStringAppendUnattributed(attrstr, "contextual alternates (");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("calt", 0);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, " vs. ");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("calt", 1);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ")");
uiAttributedStringAppendUnattributed(attrstr, ", ");
next = "FONT";
uiAttributedStringAppendUnattributed(attrstr, "swashes (");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("swsh", 0);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, " vs. ");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("swsh", 1);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ")");
uiAttributedStringAppendUnattributed(attrstr, ", ");
next = "Font";
uiAttributedStringAppendUnattributed(attrstr, "contextual swashes (");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("cswh", 0);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, " vs. ");
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("cswh", 1);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ")");
uiAttributedStringAppendUnattributed(attrstr, ", ");
next = "Small Caps";
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("smcp", 1);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ", ");
next = "Petite Caps";
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("pcap", 1);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ", ");
next = "SMALL UPPERCASES";
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("c2sp", 1);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ", and ");
next = "PETITE UPPERCASES";
start = uiAttributedStringLen(attrstr);
end = start + strlen(next);
uiAttributedStringAppendUnattributed(attrstr, next);
spec.Type = uiAttributeFeatures;
spec.Features = addFeature("c2pc", 1);
uiAttributedStringSetAttribute(attrstr, &spec, start, end);
uiAttributedStringAppendUnattributed(attrstr, ".");
// TODO write a dedicated example for experimenting with typographic features like the one in gtk3-demo
}
static char fontFamily[] = "Times New Roman";
// TODO should be const; look at constructor function?
static uiDrawFontDescriptor defaultFont = {
.Family = fontFamily,
.Size = 12,
.Weight = uiDrawTextWeightNormal,
.Italic = uiDrawTextItalicNormal,
.Stretch = uiDrawTextStretchNormal,
};
static uiDrawTextLayoutParams params;
#define margins 10
static uiBox *panel;
static uiCheckbox *showLineBounds;
static uiFontButton *fontButton;
// TODO should be const?
static uiDrawBrush fillBrushes[4] = {
{
.Type = uiDrawBrushTypeSolid,
.R = 1.0,
.G = 0.0,
.B = 0.0,
.A = 0.5,
},
{
.Type = uiDrawBrushTypeSolid,
.R = 0.0,
.G = 1.0,
.B = 0.0,
.A = 0.5,
},
{
.Type = uiDrawBrushTypeSolid,
.R = 0.0,
.G = 0.0,
.B = 1.0,
.A = 0.5,
},
{
.Type = uiDrawBrushTypeSolid,
.R = 0.0,
.G = 1.0,
.B = 1.0,
.A = 0.5,
},
};
static void draw(uiAreaDrawParams *p)
{
uiDrawPath *path;
uiDrawTextLayout *layout;
uiDrawBrush b;
b.Type = uiDrawBrushTypeSolid;
// only clip the text, not the guides
uiDrawSave(p->Context);
path = uiDrawNewPath(uiDrawFillModeWinding);
uiDrawPathAddRectangle(path, margins, margins,
p->AreaWidth - 2 * margins,
p->AreaHeight - 2 * margins);
uiDrawPathEnd(path);
uiDrawClip(p->Context, path);
uiDrawFreePath(path);
params.Width = p->AreaWidth - 2 * margins;
layout = uiDrawNewTextLayout(&params);
uiDrawText(p->Context, layout, margins, margins);
uiDrawRestore(p->Context);
if (uiCheckboxChecked(showLineBounds)) {
uiDrawTextLayoutLineMetrics m;
int i, n;
int fill = 0;
n = uiDrawTextLayoutNumLines(layout);
for (i = 0; i < n; i++) {
uiDrawTextLayoutLineGetMetrics(layout, i, &m);
path = uiDrawNewPath(uiDrawFillModeWinding);
uiDrawPathAddRectangle(path, margins + m.X, margins + m.Y,
m.Width, m.Height);
uiDrawPathEnd(path);
uiDrawFill(p->Context, path, fillBrushes + fill);
uiDrawFreePath(path);
fill = (fill + 1) % 4;
}
}
uiDrawFreeTextLayout(layout);
}
static struct example attributesExample;
static void changeFont(uiFontButton *b, void *data)
{
if (defaultFont.Family != fontFamily)
uiFreeText(defaultFont.Family);
// TODO rename defaultFont
uiFontButtonFont(fontButton, &defaultFont);
redraw();
}
// TODO share?
static void checkboxChecked(uiCheckbox *c, void *data)
{
redraw();
}
static uiCheckbox *newCheckbox(const char *text)
{
uiCheckbox *c;
c = uiNewCheckbox(text);
uiCheckboxOnToggled(c, checkboxChecked, NULL);
uiBoxAppend(panel, uiControl(c), 0);
return c;
}
struct example *mkAttributesExample(void)
{
panel = uiNewVerticalBox();
showLineBounds = newCheckbox("Show Line Bounds");
fontButton = uiNewFontButton();
uiFontButtonOnChanged(fontButton, changeFont, NULL);
// TODO set the font button to the current defaultFont
uiBoxAppend(panel, uiControl(fontButton), 0);
attributesExample.name = "Attributed Text";
attributesExample.panel = uiControl(panel);
attributesExample.draw = draw;
attributesExample.mouse = NULL;
attributesExample.key = NULL;
setupAttributedString();
params.String = attrstr;
params.DefaultFont = &defaultFont;
params.Align = uiDrawTextAlignLeft;
return &attributesExample;
}

View File

@ -1,267 +0,0 @@
// 17 january 2017
#include "drawtext.h"
static const char *text =
"It is with a kind of fear that I begin to write the history of my life. "
"I have, as it were, a superstitious hesitation in lifting the veil that "
"clings about my childhood like a golden mist. The task of writing an "
"autobiography is a difficult one. When I try to classify my earliest "
"impressions, I find that fact and fancy look alike across the years that "
"link the past with the present. The woman paints the child's experiences "
"in her own fantasy. A few impressions stand out vividly from the first "
"years of my life; but \"the shadows of the prison-house are on the rest.\" "
"Besides, many of the joys and sorrows of childhood have lost their "
"poignancy; and many incidents of vital importance in my early education "
"have been forgotten in the excitement of great discoveries. In order, "
"therefore, not to be tedious I shall try to present in a series of "
"sketches only the episodes that seem to me to be the most interesting "
"and important."
"";
static char fontFamily[] = "Palatino";
// TODO should be const; look at constructor function?
static uiDrawFontDescriptor defaultFont = {
.Family = fontFamily,
.Size = 18,
.Weight = uiDrawTextWeightNormal,
.Italic = uiDrawTextItalicNormal,
.Stretch = uiDrawTextStretchNormal,
};
static uiAttributedString *attrstr;
static uiDrawTextLayoutParams params;
#define margins 10
static uiBox *panel;
static uiCheckbox *showExtents;
static uiCheckbox *showLineBounds;
static uiCheckbox *showLineGuides;
// TODO should this be const?
static double strokeDashes[] = { 5, 2 };
// TODO this should be const
static uiDrawStrokeParams strokeParams = {
.Cap = uiDrawLineCapFlat,
.Join = uiDrawLineJoinMiter,
.Thickness = 1,
.MiterLimit = uiDrawDefaultMiterLimit,
.Dashes = strokeDashes,
.NumDashes = 2,
.DashPhase = 0,
};
// TODO should be const?
static uiDrawBrush fillBrushes[4] = {
{
.Type = uiDrawBrushTypeSolid,
.R = 1.0,
.G = 0.0,
.B = 0.0,
.A = 0.5,
},
{
.Type = uiDrawBrushTypeSolid,
.R = 0.0,
.G = 1.0,
.B = 0.0,
.A = 0.5,
},
{
.Type = uiDrawBrushTypeSolid,
.R = 0.0,
.G = 0.0,
.B = 1.0,
.A = 0.5,
},
{
.Type = uiDrawBrushTypeSolid,
.R = 0.0,
.G = 1.0,
.B = 1.0,
.A = 0.5,
},
};
// TODO come up with better colors
static uiDrawBrush strokeBrushes[3] = {
// baseline
{
.Type = uiDrawBrushTypeSolid,
.R = 0.5,
.G = 0.5,
.B = 0.0,
.A = 0.75,
},
// ascent
{
.Type = uiDrawBrushTypeSolid,
.R = 1.0,
.G = 0.0,
.B = 1.0,
.A = 0.75,
},
// descent
{
.Type = uiDrawBrushTypeSolid,
.R = 0.5,
.G = 0.75,
.B = 1.0,
.A = 0.75,
},
};
static void draw(uiAreaDrawParams *p)
{
uiDrawPath *path;
uiDrawTextLayout *layout;
uiDrawBrush b;
b.Type = uiDrawBrushTypeSolid;
// only clip the text, not the guides
uiDrawSave(p->Context);
path = uiDrawNewPath(uiDrawFillModeWinding);
uiDrawPathAddRectangle(path, margins, margins,
p->AreaWidth - 2 * margins,
p->AreaHeight - 2 * margins);
uiDrawPathEnd(path);
uiDrawClip(p->Context, path);
uiDrawFreePath(path);
// TODO get rid of this later
#if 0
path = uiDrawNewPath(uiDrawFillModeWinding);
uiDrawPathAddRectangle(path, -100, -100,
p->AreaWidth * 2,
p->AreaHeight * 2);
uiDrawPathEnd(path);
b.R = 0.0;
b.G = 1.0;
b.B = 0.0;
b.A = 1.0;
uiDrawFill(p->Context, path, &b);
uiDrawFreePath(path);
#endif
params.Width = p->AreaWidth - 2 * margins;
layout = uiDrawNewTextLayout(&params);
uiDrawText(p->Context, layout, margins, margins);
uiDrawRestore(p->Context);
if (uiCheckboxChecked(showExtents)) {
double width, height;
uiDrawTextLayoutExtents(layout, &width, &height);
path = uiDrawNewPath(uiDrawFillModeWinding);
uiDrawPathAddRectangle(path, margins, margins,
width, height);
uiDrawPathEnd(path);
b.R = 1.0;
b.G = 0.0;
b.B = 1.0;
b.A = 0.5;
uiDrawStroke(p->Context, path, &b, &strokeParams);
uiDrawFreePath(path);
}
if (uiCheckboxChecked(showLineBounds) || uiCheckboxChecked(showLineGuides)) {
uiDrawTextLayoutLineMetrics m;
int i, n;
int fill = 0;
n = uiDrawTextLayoutNumLines(layout);
for (i = 0; i < n; i++) {
uiDrawTextLayoutLineGetMetrics(layout, i, &m);
if (uiCheckboxChecked(showLineBounds)) {
path = uiDrawNewPath(uiDrawFillModeWinding);
uiDrawPathAddRectangle(path, margins + m.X, margins + m.Y,
m.Width, m.Height);
uiDrawPathEnd(path);
uiDrawFill(p->Context, path, fillBrushes + fill);
uiDrawFreePath(path);
fill = (fill + 1) % 4;
}
if (uiCheckboxChecked(showLineGuides)) {
// baseline
path = uiDrawNewPath(uiDrawFillModeWinding);
uiDrawPathNewFigure(path,
margins + m.X,
margins + m.BaselineY);
uiDrawPathLineTo(path,
margins + m.X + m.Width,
margins + m.BaselineY);
uiDrawPathEnd(path);
uiDrawStroke(p->Context, path, &(strokeBrushes[0]), &strokeParams);
uiDrawFreePath(path);
// ascent line
path = uiDrawNewPath(uiDrawFillModeWinding);
uiDrawPathNewFigure(path,
margins + m.X,
margins + m.BaselineY - m.Ascent);
uiDrawPathLineTo(path,
margins + m.X + m.Width,
margins + m.BaselineY - m.Ascent);
uiDrawPathEnd(path);
uiDrawStroke(p->Context, path, &(strokeBrushes[1]), &strokeParams);
uiDrawFreePath(path);
// descent line
path = uiDrawNewPath(uiDrawFillModeWinding);
uiDrawPathNewFigure(path,
margins + m.X,
margins + m.BaselineY + m.Descent);
uiDrawPathLineTo(path,
margins + m.X + m.Width,
margins + m.BaselineY + m.Descent);
uiDrawPathEnd(path);
uiDrawStroke(p->Context, path, &(strokeBrushes[2]), &strokeParams);
uiDrawFreePath(path);
}
}
}
uiDrawFreeTextLayout(layout);
}
static struct example basicExample;
// TODO share?
static void checkboxChecked(uiCheckbox *c, void *data)
{
redraw();
}
static uiCheckbox *newCheckbox(const char *text)
{
uiCheckbox *c;
c = uiNewCheckbox(text);
uiCheckboxOnToggled(c, checkboxChecked, NULL);
uiBoxAppend(panel, uiControl(c), 0);
return c;
}
struct example *mkBasicExample(void)
{
panel = uiNewVerticalBox();
showExtents = newCheckbox("Show Layout Extents");
showLineBounds = newCheckbox("Show Line Bounds");
showLineGuides = newCheckbox("Show Line Guides");
basicExample.name = "Basic Paragraph of Text";
basicExample.panel = uiControl(panel);
basicExample.draw = draw;
basicExample.mouse = NULL;
basicExample.key = NULL;
attrstr = uiNewAttributedString(text);
params.String = attrstr;
params.DefaultFont = &defaultFont;
params.Align = uiDrawTextAlignLeft;
return &basicExample;
}
// TODO on GTK+ an area by itself in a window doesn't get expand properties set properly?

View File

@ -1,29 +0,0 @@
// 20 january 2017
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "../../ui.h"
struct example {
const char *name;
uiControl *panel;
void (*draw)(uiAreaDrawParams *p);
void (*mouse)(uiAreaMouseEvent *e);
int (*key)(uiAreaKeyEvent *e);
// TODO key?
};
// main.c
extern void redraw(void);
// basic.c
extern struct example *mkBasicExample(void);
// hittest.c
extern struct example *mkHitTestExample(void);
// attributes.c
extern struct example *mkAttributesExample(void);
// emptystr_hittest.c
extern struct example *mkEmptyStringExample(void);

View File

@ -1,254 +0,0 @@
// 20 january 2017
#include "drawtext.h"
// TODO FOR THIS FILE
// get rid of it once we rewrite this example (which requires having more fine-grained scrolling control)
// TODO double-check ligatures on all platforms to make sure we can place the cursor at the right place
// TODO the hiding and showing does not work properly on GTK+
// TODO using the arrow keys allows us to walk back to the end of the line on some platforms (TODO which?); IIRC arrow keys shouldn't do that
// TODO make sure to check the cursor positions of RTL on all platforms
static const char *text = "";
static char fontFamily[] = "Helvetica";
static uiDrawFontDescriptor defaultFont = {
.Family = fontFamily,
.Size = 14,
.Weight = uiDrawTextWeightNormal,
.Italic = uiDrawTextItalicNormal,
.Stretch = uiDrawTextStretchNormal,
};
static uiAttributedString *attrstr;
static uiDrawTextLayoutParams params;
#define margins 10
static uiBox *panel;
static uiBox *vbox;
static uiLabel *caretLabel;
static uiCheckbox *showLineBounds;
static uiFontButton *fontButton;
static uiCombobox *textAlign;
static int caretLine = -1;
static size_t caretPos;
// TODO should be const?
static uiDrawBrush fillBrushes[4] = {
{
.Type = uiDrawBrushTypeSolid,
.R = 1.0,
.G = 0.0,
.B = 0.0,
.A = 0.5,
},
{
.Type = uiDrawBrushTypeSolid,
.R = 0.0,
.G = 1.0,
.B = 0.0,
.A = 0.5,
},
{
.Type = uiDrawBrushTypeSolid,
.R = 0.0,
.G = 0.0,
.B = 1.0,
.A = 0.5,
},
{
.Type = uiDrawBrushTypeSolid,
.R = 0.0,
.G = 1.0,
.B = 1.0,
.A = 0.5,
},
};
static void draw(uiAreaDrawParams *p)
{
uiDrawPath *path;
uiDrawTextLayout *layout;
uiDrawTextLayoutLineMetrics m;
// only clip the text, not the guides
uiDrawSave(p->Context);
path = uiDrawNewPath(uiDrawFillModeWinding);
uiDrawPathAddRectangle(path, margins, margins,
p->AreaWidth - 2 * margins,
p->AreaHeight - 2 * margins);
uiDrawPathEnd(path);
uiDrawClip(p->Context, path);
uiDrawFreePath(path);
params.Width = p->AreaWidth - 2 * margins;
layout = uiDrawNewTextLayout(&params);
uiDrawText(p->Context, layout, margins, margins);
uiDrawRestore(p->Context);
if (caretLine == -1) {
caretLine = uiDrawTextLayoutNumLines(layout) - 1;
caretPos = uiAttributedStringLen(attrstr);
}
uiDrawCaret(p->Context, margins, margins,
layout, caretPos, &caretLine);
if (uiCheckboxChecked(showLineBounds)) {
int i, n;
int fill = 0;
n = uiDrawTextLayoutNumLines(layout);
for (i = 0; i < n; i++) {
uiDrawTextLayoutLineGetMetrics(layout, i, &m);
path = uiDrawNewPath(uiDrawFillModeWinding);
uiDrawPathAddRectangle(path, margins + m.X, margins + m.Y,
m.Width, m.Height);
uiDrawPathEnd(path);
uiDrawFill(p->Context, path, fillBrushes + fill);
uiDrawFreePath(path);
fill = (fill + 1) % 4;
}
}
uiDrawFreeTextLayout(layout);
}
static void mouse(uiAreaMouseEvent *e)
{
uiDrawTextLayout *layout;
char labelText[128];
if (e->Down != 1)
return;
params.Width = e->AreaWidth - 2 * margins;
layout = uiDrawNewTextLayout(&params);
uiDrawTextLayoutHitTest(layout,
e->X - margins, e->Y - margins,
&caretPos, &caretLine);
uiDrawFreeTextLayout(layout);
// TODO move this into the draw handler so it is reflected by keyboard-based position changes
// urgh %zd is not supported by MSVC with sprintf()
// TODO get that part in test/ about having no other option
sprintf(labelText, "pos %d line %d",
(int) caretPos, caretLine);
uiLabelSetText(caretLabel, labelText);
redraw();
}
static int key(uiAreaKeyEvent *e)
{
size_t grapheme;
if (e->Up)
return 0;
if (e->Key != 0)
return 0;
switch (e->ExtKey) {
case uiExtKeyUp:
// TODO
return 1;
case uiExtKeyDown:
// TODO
return 1;
case uiExtKeyLeft:
grapheme = uiAttributedStringByteIndexToGrapheme(attrstr, caretPos);
if (grapheme == 0)
return 0;
grapheme--;
caretPos = uiAttributedStringGraphemeToByteIndex(attrstr, grapheme);
redraw();
return 1;
case uiExtKeyRight:
grapheme = uiAttributedStringByteIndexToGrapheme(attrstr, caretPos);
if (grapheme == uiAttributedStringNumGraphemes(attrstr))
return 0;
grapheme++;
caretPos = uiAttributedStringGraphemeToByteIndex(attrstr, grapheme);
redraw();
return 1;
}
return 0;
}
static struct example hitTestExample;
// TODO share?
static void checkboxChecked(uiCheckbox *c, void *data)
{
redraw();
}
static void changeFont(uiFontButton *b, void *data)
{
if (defaultFont.Family != fontFamily)
uiFreeText(defaultFont.Family);
// TODO rename defaultFont
uiFontButtonFont(fontButton, &defaultFont);
printf("{\n\tfamily: %s\n\tsize: %g\n\tweight: %d\n\titalic: %d\n\tstretch: %d\n}\n",
defaultFont.Family,
defaultFont.Size,
(int) (defaultFont.Weight),
(int) (defaultFont.Italic),
(int) (defaultFont.Stretch));
redraw();
}
static void changeTextAlign(uiCombobox *c, void *data)
{
// note the order of the items added below
params.Align = (uiDrawTextAlign) uiComboboxSelected(textAlign);
redraw();
}
// TODO share?
static uiCheckbox *newCheckbox(uiBox *box, const char *text)
{
uiCheckbox *c;
c = uiNewCheckbox(text);
uiCheckboxOnToggled(c, checkboxChecked, NULL);
uiBoxAppend(box, uiControl(c), 0);
return c;
}
struct example *mkEmptyStringExample(void)
{
panel = uiNewHorizontalBox();
vbox = uiNewVerticalBox();
// TODO the second vbox causes this not to stretch at least on OS X
uiBoxAppend(panel, uiControl(vbox), 1);
caretLabel = uiNewLabel("Caret information is shown here");
uiBoxAppend(vbox, uiControl(caretLabel), 0);
showLineBounds = newCheckbox(vbox, "Show Line Bounds (for debugging metrics)");
vbox = uiNewVerticalBox();
uiBoxAppend(panel, uiControl(vbox), 0);
fontButton = uiNewFontButton();
uiFontButtonOnChanged(fontButton, changeFont, NULL);
// TODO set the font button to the current defaultFont
uiBoxAppend(vbox, uiControl(fontButton), 0);
textAlign = uiNewCombobox();
// note that these are in the order in the enum
uiComboboxAppend(textAlign, "Left");
uiComboboxAppend(textAlign, "Center");
uiComboboxAppend(textAlign, "Right");
uiComboboxOnSelected(textAlign, changeTextAlign, NULL);
uiBoxAppend(vbox, uiControl(textAlign), 0);
hitTestExample.name = "Empty String";
hitTestExample.panel = uiControl(panel);
hitTestExample.draw = draw;
hitTestExample.mouse = mouse;
hitTestExample.key = key;
attrstr = uiNewAttributedString(text);
params.String = attrstr;
params.DefaultFont = &defaultFont;
params.Align = uiDrawTextAlignLeft;
return &hitTestExample;
}

View File

@ -1,262 +0,0 @@
// 20 january 2017
#include "drawtext.h"
// TODO double-check ligatures on all platforms to make sure we can place the cursor at the right place
// TODO using the arrow keys allows us to walk back to the end of the line on some platforms (TODO which?); IIRC arrow keys shouldn't do that
// TODO make sure to check the cursor positions of RTL on all platforms
static const char *text =
"Each of the glyphs an end user interacts with are called graphemes. "
"If you enter a byte range in the text boxes below and click the button, you can see the blue box move to surround that byte range, as well as what the actual byte range necessary is. "
// TODO rephrase this; I don't think this code will use those grapheme functions...
"You'll also see the index of the first grapheme; uiAttributedString has facilities for converting between UTF-8 code points and grapheme indices. "
"Additionally, click on the string to move the caret. Watch the status text at the bottom change too. "
"That being said: "
"\xC3\x93O\xCC\x81 (combining accents) "
"A\xCC\xAA\xEF\xB8\xA0 (multiple combining characters) "
"\xE2\x80\xAE#\xE2\x80\xAC (RTL glyph) "
"\xF0\x9F\x92\xBB (non-BMP character) "
"\xF0\x9F\x92\xBB\xCC\x80 (combined non-BMP character; may render strangely) "
"";
static char fontFamily[] = "Helvetica";
static uiDrawFontDescriptor defaultFont = {
.Family = fontFamily,
.Size = 14,
.Weight = uiDrawTextWeightNormal,
.Italic = uiDrawTextItalicNormal,
.Stretch = uiDrawTextStretchNormal,
};
static uiAttributedString *attrstr;
static uiDrawTextLayoutParams params;
#define margins 10
static uiBox *panel;
static uiBox *vbox;
static uiLabel *caretLabel;
static uiCheckbox *showLineBounds;
static uiFontButton *fontButton;
static uiCombobox *textAlign;
static int caretLine = -1;
static size_t caretPos;
// TODO should be const?
static uiDrawBrush fillBrushes[4] = {
{
.Type = uiDrawBrushTypeSolid,
.R = 1.0,
.G = 0.0,
.B = 0.0,
.A = 0.5,
},
{
.Type = uiDrawBrushTypeSolid,
.R = 0.0,
.G = 1.0,
.B = 0.0,
.A = 0.5,
},
{
.Type = uiDrawBrushTypeSolid,
.R = 0.0,
.G = 0.0,
.B = 1.0,
.A = 0.5,
},
{
.Type = uiDrawBrushTypeSolid,
.R = 0.0,
.G = 1.0,
.B = 1.0,
.A = 0.5,
},
};
static void draw(uiAreaDrawParams *p)
{
uiDrawPath *path;
uiDrawTextLayout *layout;
uiDrawTextLayoutLineMetrics m;
// only clip the text, not the guides
uiDrawSave(p->Context);
path = uiDrawNewPath(uiDrawFillModeWinding);
uiDrawPathAddRectangle(path, margins, margins,
p->AreaWidth - 2 * margins,
p->AreaHeight - 2 * margins);
uiDrawPathEnd(path);
uiDrawClip(p->Context, path);
uiDrawFreePath(path);
params.Width = p->AreaWidth - 2 * margins;
layout = uiDrawNewTextLayout(&params);
uiDrawText(p->Context, layout, margins, margins);
uiDrawRestore(p->Context);
if (caretLine == -1) {
caretLine = uiDrawTextLayoutNumLines(layout) - 1;
caretPos = uiAttributedStringLen(attrstr);
}
uiDrawCaret(p->Context, margins, margins,
layout, caretPos, &caretLine);
if (uiCheckboxChecked(showLineBounds)) {
int i, n;
int fill = 0;
n = uiDrawTextLayoutNumLines(layout);
for (i = 0; i < n; i++) {
uiDrawTextLayoutLineGetMetrics(layout, i, &m);
path = uiDrawNewPath(uiDrawFillModeWinding);
uiDrawPathAddRectangle(path, margins + m.X, margins + m.Y,
m.Width, m.Height);
uiDrawPathEnd(path);
uiDrawFill(p->Context, path, fillBrushes + fill);
uiDrawFreePath(path);
fill = (fill + 1) % 4;
}
}
uiDrawFreeTextLayout(layout);
}
static void mouse(uiAreaMouseEvent *e)
{
uiDrawTextLayout *layout;
char labelText[128];
if (e->Down != 1)
return;
params.Width = e->AreaWidth - 2 * margins;
layout = uiDrawNewTextLayout(&params);
uiDrawTextLayoutHitTest(layout,
e->X - margins, e->Y - margins,
&caretPos, &caretLine);
uiDrawFreeTextLayout(layout);
// TODO move this into the draw handler so it is reflected by keyboard-based position changes
// urgh %zd is not supported by MSVC with sprintf()
// TODO get that part in test/ about having no other option
sprintf(labelText, "pos %d line %d",
(int) caretPos, caretLine);
uiLabelSetText(caretLabel, labelText);
redraw();
}
static int key(uiAreaKeyEvent *e)
{
size_t grapheme;
if (e->Up)
return 0;
if (e->Key != 0)
return 0;
switch (e->ExtKey) {
case uiExtKeyUp:
// TODO
return 1;
case uiExtKeyDown:
// TODO
return 1;
case uiExtKeyLeft:
grapheme = uiAttributedStringByteIndexToGrapheme(attrstr, caretPos);
if (grapheme == 0)
return 0;
grapheme--;
caretPos = uiAttributedStringGraphemeToByteIndex(attrstr, grapheme);
redraw();
return 1;
case uiExtKeyRight:
grapheme = uiAttributedStringByteIndexToGrapheme(attrstr, caretPos);
if (grapheme == uiAttributedStringNumGraphemes(attrstr))
return 0;
grapheme++;
caretPos = uiAttributedStringGraphemeToByteIndex(attrstr, grapheme);
redraw();
return 1;
}
return 0;
}
static struct example hitTestExample;
// TODO share?
static void checkboxChecked(uiCheckbox *c, void *data)
{
redraw();
}
static void changeFont(uiFontButton *b, void *data)
{
if (defaultFont.Family != fontFamily)
uiFreeText(defaultFont.Family);
// TODO rename defaultFont
uiFontButtonFont(fontButton, &defaultFont);
printf("{\n\tfamily: %s\n\tsize: %g\n\tweight: %d\n\titalic: %d\n\tstretch: %d\n}\n",
defaultFont.Family,
defaultFont.Size,
(int) (defaultFont.Weight),
(int) (defaultFont.Italic),
(int) (defaultFont.Stretch));
redraw();
}
static void changeTextAlign(uiCombobox *c, void *data)
{
// note the order of the items added below
params.Align = (uiDrawTextAlign) uiComboboxSelected(textAlign);
redraw();
}
// TODO share?
static uiCheckbox *newCheckbox(uiBox *box, const char *text)
{
uiCheckbox *c;
c = uiNewCheckbox(text);
uiCheckboxOnToggled(c, checkboxChecked, NULL);
uiBoxAppend(box, uiControl(c), 0);
return c;
}
struct example *mkHitTestExample(void)
{
panel = uiNewHorizontalBox();
vbox = uiNewVerticalBox();
// TODO the second vbox causes this not to stretch at least on OS X
uiBoxAppend(panel, uiControl(vbox), 1);
caretLabel = uiNewLabel("Caret information is shown here");
uiBoxAppend(vbox, uiControl(caretLabel), 0);
showLineBounds = newCheckbox(vbox, "Show Line Bounds (for debugging metrics)");
vbox = uiNewVerticalBox();
uiBoxAppend(panel, uiControl(vbox), 0);
fontButton = uiNewFontButton();
uiFontButtonOnChanged(fontButton, changeFont, NULL);
// TODO set the font button to the current defaultFont
uiBoxAppend(vbox, uiControl(fontButton), 0);
textAlign = uiNewCombobox();
// note that these are in the order in the enum
uiComboboxAppend(textAlign, "Left");
uiComboboxAppend(textAlign, "Center");
uiComboboxAppend(textAlign, "Right");
uiComboboxOnSelected(textAlign, changeTextAlign, NULL);
uiBoxAppend(vbox, uiControl(textAlign), 0);
hitTestExample.name = "Hit-Testing and Grapheme Boundaries";
hitTestExample.panel = uiControl(panel);
hitTestExample.draw = draw;
hitTestExample.mouse = mouse;
hitTestExample.key = key;
attrstr = uiNewAttributedString(text);
params.String = attrstr;
params.DefaultFont = &defaultFont;
params.Align = uiDrawTextAlignLeft;
return &hitTestExample;
}

Binary file not shown.

View File

@ -1,136 +0,0 @@
// 17 january 2017
#include "drawtext.h"
static uiWindow *mainwin;
static uiBox *box;
static uiCombobox *exampleList;
static uiArea *area;
static uiAreaHandler handler;
#define nExamples 20
static struct example *examples[nExamples];
static int curExample = 0;
static void onExampleChanged(uiCombobox *c, void *data)
{
uiControlHide(examples[curExample]->panel);
curExample = uiComboboxSelected(exampleList);
uiControlShow(examples[curExample]->panel);
redraw();
}
void redraw(void)
{
uiAreaQueueRedrawAll(area);
}
static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *p)
{
examples[curExample]->draw(p);
}
static void handlerMouseEvent(uiAreaHandler *a, uiArea *area, uiAreaMouseEvent *e)
{
if (examples[curExample]->mouse != NULL)
examples[curExample]->mouse(e);
}
static void handlerMouseCrossed(uiAreaHandler *ah, uiArea *a, int left)
{
// do nothing
}
static void handlerDragBroken(uiAreaHandler *ah, uiArea *a)
{
// do nothing
}
static int handlerKeyEvent(uiAreaHandler *ah, uiArea *a, uiAreaKeyEvent *e)
{
if (examples[curExample]->key != NULL)
return examples[curExample]->key(e);
return 0;
}
static int onClosing(uiWindow *w, void *data)
{
uiControlDestroy(uiControl(mainwin));
uiQuit();
return 0;
}
static int shouldQuit(void *data)
{
uiControlDestroy(uiControl(mainwin));
return 1;
}
int main(void)
{
uiInitOptions o;
const char *err;
int n;
handler.Draw = handlerDraw;
handler.MouseEvent = handlerMouseEvent;
handler.MouseCrossed = handlerMouseCrossed;
handler.DragBroken = handlerDragBroken;
handler.KeyEvent = handlerKeyEvent;
memset(&o, 0, sizeof (uiInitOptions));
err = uiInit(&o);
if (err != NULL) {
fprintf(stderr, "error initializing ui: %s\n", err);
uiFreeInitError(err);
return 1;
}
uiOnShouldQuit(shouldQuit, NULL);
mainwin = uiNewWindow("libui Text-Drawing Example", 640, 480, 1);
uiWindowOnClosing(mainwin, onClosing, NULL);
box = uiNewVerticalBox();
uiWindowSetChild(mainwin, uiControl(box));
exampleList = uiNewCombobox();
uiBoxAppend(box, uiControl(exampleList), 0);
area = uiNewArea(&handler);
uiBoxAppend(box, uiControl(area), 1);
n = 0;
examples[n] = mkBasicExample();
uiComboboxAppend(exampleList, examples[n]->name);
uiControlHide(examples[n]->panel);
uiBoxAppend(box, examples[n]->panel, 0);
n++;
examples[n] = mkHitTestExample();
uiComboboxAppend(exampleList, examples[n]->name);
uiControlHide(examples[n]->panel);
uiBoxAppend(box, examples[n]->panel, 0);
n++;
examples[n] = mkAttributesExample();
uiComboboxAppend(exampleList, examples[n]->name);
uiControlHide(examples[n]->panel);
uiBoxAppend(box, examples[n]->panel, 0);
n++;
examples[n] = mkEmptyStringExample();
uiComboboxAppend(exampleList, examples[n]->name);
uiControlHide(examples[n]->panel);
uiBoxAppend(box, examples[n]->panel, 0);
n++;
// and set things up for the initial state
uiComboboxSetSelected(exampleList, 0);
uiComboboxOnSelected(exampleList, onExampleChanged, NULL);
// and set up the first one
onExampleChanged(NULL, NULL);
uiControlShow(uiControl(mainwin));
uiMain();
// TODO free examples
uiUninit();
return 0;
}

View File

@ -1,19 +0,0 @@
# 3 june 2016
_add_example(drawtext
drawtext/attributes.c
drawtext/basic.c
drawtext/emptystr_hittest.c
drawtext/hittest.c
drawtext/main.c
${_EXAMPLE_RESOURCES_RC}
)
target_include_directories(drawtext
PRIVATE drawtext)
_add_example(opentype
opentype/main.c
${_EXAMPLE_RESOURCES_RC}
)
target_include_directories(opentype
PRIVATE opentype)

View File

@ -1,201 +0,0 @@
// 10 june 2017
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include "../../ui.h"
// TODO the grid simply flat out does not work on OS X
uiWindow *mainwin;
uiFontButton *fontButton;
uiEntry *textEntry;
uiCheckbox *nullFeatures;
uiArea *area;
uiAttributedString *attrstr = NULL;
static void remakeAttrStr(void)
{
char *text;
uiOpenTypeFeatures *otf;
uiAttributeSpec spec;
if (attrstr != NULL)
uiFreeAttributedString(attrstr);
text = uiEntryText(textEntry);
attrstr = uiNewAttributedString(text);
uiFreeText(text);
if (!uiCheckboxChecked(nullFeatures)) {
otf = uiNewOpenTypeFeatures();
// TODO
spec.Type = uiAttributeFeatures;
spec.Features = otf;
uiAttributedStringSetAttribute(attrstr, &spec,
0, uiAttributedStringLen(attrstr));
// and uiAttributedString copied otf
uiFreeOpenTypeFeatures(otf);
}
uiAreaQueueRedrawAll(area);
}
// TODO make a variable of main()? in all programs?
static uiAreaHandler handler;
static void handlerDraw(uiAreaHandler *a, uiArea *area, uiAreaDrawParams *p)
{
uiDrawTextLayout *layout;
uiDrawTextLayoutParams lp;
uiDrawFontDescriptor desc;
memset(&lp, 0, sizeof (uiDrawTextLayoutParams));
lp.String = attrstr;
uiFontButtonFont(fontButton, &desc);
lp.DefaultFont = &desc;
lp.Width = p->AreaWidth;
lp.Align = uiDrawTextAlignLeft;
layout = uiDrawNewTextLayout(&lp);
uiDrawText(p->Context, layout, 0, 0);
uiDrawFreeTextLayout(layout);
}
static void handlerMouseEvent(uiAreaHandler *a, uiArea *area, uiAreaMouseEvent *e)
{
// do nothing
}
static void handlerMouseCrossed(uiAreaHandler *ah, uiArea *a, int left)
{
// do nothing
}
static void handlerDragBroken(uiAreaHandler *ah, uiArea *a)
{
// do nothing
}
static int handlerKeyEvent(uiAreaHandler *ah, uiArea *a, uiAreaKeyEvent *e)
{
// reject all keys
return 0;
}
static void onFontChanged(uiFontButton *b, void *data)
{
remakeAttrStr();
}
static void onTextChanged(uiEntry *e, void *data)
{
remakeAttrStr();
}
static void onNULLToggled(uiCheckbox *c, void *data)
{
remakeAttrStr();
}
static int onClosing(uiWindow *w, void *data)
{
// TODO change the others to be like this? (the others destroy here rather than later)
// TODO move this below uiQuit()?
uiControlHide(uiControl(w));
uiQuit();
return 0;
}
static int shouldQuit(void *data)
{
uiControlDestroy(uiControl(mainwin));
return 1;
}
int main(void)
{
uiInitOptions o;
const char *err;
uiGrid *grid;
uiBox *vbox;
handler.Draw = handlerDraw;
handler.MouseEvent = handlerMouseEvent;
handler.MouseCrossed = handlerMouseCrossed;
handler.DragBroken = handlerDragBroken;
handler.KeyEvent = handlerKeyEvent;
memset(&o, 0, sizeof (uiInitOptions));
err = uiInit(&o);
if (err != NULL) {
fprintf(stderr, "error initializing ui: %s\n", err);
uiFreeInitError(err);
return 1;
}
uiOnShouldQuit(shouldQuit, NULL);
// TODO 800x600? the size of the GTK+ example?
mainwin = uiNewWindow("libui OpenType Features Example", 640, 480, 1);
uiWindowSetMargined(mainwin, 1);
uiWindowOnClosing(mainwin, onClosing, NULL);
grid = uiNewGrid();
uiGridSetPadded(grid, 1);
uiWindowSetChild(mainwin, uiControl(grid));
fontButton = uiNewFontButton();
uiFontButtonOnChanged(fontButton, onFontChanged, NULL);
uiGridAppend(grid, uiControl(fontButton),
0, 0, 1, 1,
// TODO are these Y values correct?
0, uiAlignFill, 0, uiAlignCenter);
textEntry = uiNewEntry();
uiEntrySetText(textEntry, "afford afire aflight");
uiEntryOnChanged(textEntry, onTextChanged, NULL);
uiGridAppend(grid, uiControl(textEntry),
1, 0, 1, 1,
// TODO are these Y values correct too?
// TODO add a baseline align? or a form align?
1, uiAlignFill, 0, uiAlignCenter);
vbox = uiNewVerticalBox();
uiBoxSetPadded(vbox, 1);
uiGridAppend(grid, uiControl(vbox),
0, 1, 1, 1,
0, uiAlignFill, 1, uiAlignFill);
nullFeatures = uiNewCheckbox("NULL uiOpenTypeFeatures");
uiCheckboxOnToggled(nullFeatures, onNULLToggled, NULL);
uiBoxAppend(vbox, uiControl(nullFeatures), 0);
// TODO separator (if other stuff isn't a tab)
// TODO needed for this to be testable on os x without rewriting everything again
{
int x;
for (x = 0; x < 10; x++)
uiBoxAppend(vbox, uiControl(uiNewEntry()), 0);
}
// TODO other stuff
area = uiNewArea(&handler);
uiGridAppend(grid, uiControl(area),
1, 1, 1, 1,
1, uiAlignFill, 1, uiAlignFill);
// and set up the initial draw
remakeAttrStr();
uiControlShow(uiControl(mainwin));
uiMain();
uiControlDestroy(uiControl(mainwin));
uiFreeAttributedString(attrstr);
uiUninit();
return 0;
}

View File

@ -1,378 +0,0 @@
# 31 march 2019
trigger:
branches:
include:
- '*'
tags:
include:
- '*'
variables:
releaseExamples: 'controlgallery cpp-multithread datetime drawtext histogram tester timer'
jobs:
# linux {
- job: linux_386_shared
displayName: 'Linux 386 Shared'
pool:
vmImage: 'ubuntu-16.04'
workspace:
clean: all
steps:
- template: azure-pipelines/setup-python3.yml
- template: azure-pipelines/install-latest-meson-ninja.yml
- template: azure-pipelines/linux-386-install-gtk-dev.yml
- template: azure-pipelines/configure.yml
parameters:
beforeConfigure: export CFLAGS=-m32 CXXFLAGS=-m32 PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig
defaultLibrary: shared
- template: azure-pipelines/build.yml
- template: azure-pipelines/artifacts.yml
parameters:
os: linux
arch: 386
libtype: shared
libfiles: libui.so.0
osHeader: ui_unix.h
- job: linux_386_static
displayName: 'Linux 386 Static'
pool:
vmImage: 'ubuntu-16.04'
workspace:
clean: all
steps:
- template: azure-pipelines/setup-python3.yml
- template: azure-pipelines/install-latest-meson-ninja.yml
- template: azure-pipelines/linux-386-install-gtk-dev.yml
- template: azure-pipelines/configure.yml
parameters:
beforeConfigure: export CFLAGS=-m32 CXXFLAGS=-m32 PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig
defaultLibrary: static
- template: azure-pipelines/build.yml
- template: azure-pipelines/artifacts.yml
parameters:
os: linux
arch: 386
libtype: static
libfiles: libui.a
osHeader: ui_unix.h
- job: linux_amd64_shared
displayName: 'Linux amd64 Shared'
pool:
vmImage: 'ubuntu-16.04'
workspace:
clean: all
steps:
- template: azure-pipelines/setup-python3.yml
- template: azure-pipelines/install-latest-meson-ninja.yml
- template: azure-pipelines/linux-install-gtk-dev.yml
- template: azure-pipelines/configure.yml
parameters:
defaultLibrary: shared
- template: azure-pipelines/build.yml
- template: azure-pipelines/artifacts.yml
parameters:
os: linux
arch: amd64
libtype: shared
libfiles: libui.so.0
osHeader: ui_unix.h
- job: linux_amd64_static
displayName: 'Linux amd64 Static'
pool:
vmImage: 'ubuntu-16.04'
workspace:
clean: all
steps:
- template: azure-pipelines/setup-python3.yml
- template: azure-pipelines/install-latest-meson-ninja.yml
- template: azure-pipelines/linux-install-gtk-dev.yml
- template: azure-pipelines/configure.yml
parameters:
defaultLibrary: static
- template: azure-pipelines/build.yml
- template: azure-pipelines/artifacts.yml
parameters:
os: linux
arch: amd64
libtype: static
libfiles: libui.a
osHeader: ui_unix.h
# }
# windows vs2015 {
- job: windows_386_msvc2015_shared
displayName: 'Windows 386 MSVC2015 Shared'
pool:
vmImage: 'vs2015-win2012r2'
workspace:
clean: all
steps:
- template: azure-pipelines/vs2015-install-python3.yml
- template: azure-pipelines/install-latest-meson-ninja.yml
- template: azure-pipelines/windows-install-ninja.yml
- template: azure-pipelines/configure.yml
parameters:
beforeConfigure: call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86
defaultLibrary: shared
- template: azure-pipelines/build.yml
parameters:
beforeBuild: call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86
- template: azure-pipelines/windows-artifacts.yml
parameters:
os: windows
arch: 386
toolchain: msvc2015
libtype: shared
libfiles: libui.dll libui.exp libui.lib
osHeader: ui_windows.h
- job: windows_386_msvc2015_static
displayName: 'Windows 386 MSVC2015 Static'
pool:
vmImage: 'vs2015-win2012r2'
workspace:
clean: all
steps:
- template: azure-pipelines/vs2015-install-python3.yml
- template: azure-pipelines/install-latest-meson-ninja.yml
- template: azure-pipelines/windows-install-ninja.yml
- template: azure-pipelines/configure.yml
parameters:
beforeConfigure: call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86
defaultLibrary: static
- template: azure-pipelines/build.yml
parameters:
beforeBuild: call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86
afterBuild: ren build\meson-out\libui.a libui.lib
- template: azure-pipelines/windows-artifacts.yml
parameters:
os: windows
arch: 386
toolchain: msvc2015
libtype: static
libfiles: libui.lib
osHeader: ui_windows.h
- job: windows_amd64_msvc2015_shared
displayName: 'Windows amd64 MSVC2015 Shared'
pool:
vmImage: 'vs2015-win2012r2'
workspace:
clean: all
steps:
- template: azure-pipelines/vs2015-install-python3.yml
- template: azure-pipelines/install-latest-meson-ninja.yml
- template: azure-pipelines/windows-install-ninja.yml
- template: azure-pipelines/configure.yml
parameters:
beforeConfigure: call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64
defaultLibrary: shared
- template: azure-pipelines/build.yml
parameters:
beforeBuild: call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64
- template: azure-pipelines/windows-artifacts.yml
parameters:
os: windows
arch: amd64
toolchain: msvc2015
libtype: shared
libfiles: libui.dll libui.exp libui.lib
osHeader: ui_windows.h
- job: windows_amd64_msvc2015_static
displayName: 'Windows amd64 MSVC2015 Static'
pool:
vmImage: 'vs2015-win2012r2'
workspace:
clean: all
steps:
- template: azure-pipelines/vs2015-install-python3.yml
- template: azure-pipelines/install-latest-meson-ninja.yml
- template: azure-pipelines/windows-install-ninja.yml
- template: azure-pipelines/configure.yml
parameters:
beforeConfigure: call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64
defaultLibrary: static
- template: azure-pipelines/build.yml
parameters:
beforeBuild: call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64
afterBuild: ren build\meson-out\libui.a libui.lib
- template: azure-pipelines/windows-artifacts.yml
parameters:
os: windows
arch: amd64
toolchain: msvc2015
libtype: static
libfiles: libui.lib
osHeader: ui_windows.h
# }
# windows vs2017 {
- job: windows_386_msvc2017_shared
displayName: 'Windows 386 MSVC2017 Shared'
pool:
vmImage: 'vs2017-win2016'
workspace:
clean: all
steps:
- template: azure-pipelines/setup-python3.yml
- template: azure-pipelines/install-latest-meson-ninja.yml
- template: azure-pipelines/windows-install-ninja.yml
- template: azure-pipelines/configure.yml
parameters:
beforeConfigure: call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x86
defaultLibrary: shared
- template: azure-pipelines/build.yml
parameters:
beforeBuild: call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x86
- template: azure-pipelines/windows-artifacts.yml
parameters:
os: windows
arch: 386
toolchain: msvc2017
libtype: shared
libfiles: libui.dll libui.exp libui.lib
osHeader: ui_windows.h
- job: windows_386_msvc2017_static
displayName: 'Windows 386 MSVC2017 Static'
pool:
vmImage: 'vs2017-win2016'
workspace:
clean: all
steps:
- template: azure-pipelines/setup-python3.yml
- template: azure-pipelines/install-latest-meson-ninja.yml
- template: azure-pipelines/windows-install-ninja.yml
- template: azure-pipelines/configure.yml
parameters:
beforeConfigure: call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x86
defaultLibrary: static
- template: azure-pipelines/build.yml
parameters:
beforeBuild: call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x86
afterBuild: ren build\meson-out\libui.a libui.lib
- template: azure-pipelines/windows-artifacts.yml
parameters:
os: windows
arch: 386
toolchain: msvc2017
libtype: static
libfiles: libui.lib
osHeader: ui_windows.h
- job: windows_amd64_msvc2017_shared
displayName: 'Windows amd64 MSVC2017 Shared'
pool:
vmImage: 'vs2017-win2016'
workspace:
clean: all
steps:
- template: azure-pipelines/setup-python3.yml
- template: azure-pipelines/install-latest-meson-ninja.yml
- template: azure-pipelines/windows-install-ninja.yml
- template: azure-pipelines/configure.yml
parameters:
beforeConfigure: call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" amd64
defaultLibrary: shared
- template: azure-pipelines/build.yml
parameters:
beforeBuild: call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" amd64
- template: azure-pipelines/windows-artifacts.yml
parameters:
os: windows
arch: amd64
toolchain: msvc2017
libtype: shared
libfiles: libui.dll libui.exp libui.lib
osHeader: ui_windows.h
- job: windows_amd64_msvc2017_static
displayName: 'Windows amd64 MSVC2017 Static'
pool:
vmImage: 'vs2017-win2016'
workspace:
clean: all
steps:
- template: azure-pipelines/setup-python3.yml
- template: azure-pipelines/install-latest-meson-ninja.yml
- template: azure-pipelines/windows-install-ninja.yml
- template: azure-pipelines/configure.yml
parameters:
beforeConfigure: call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" amd64
defaultLibrary: static
- template: azure-pipelines/build.yml
parameters:
beforeBuild: call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" amd64
afterBuild: ren build\meson-out\libui.a libui.lib
- template: azure-pipelines/windows-artifacts.yml
parameters:
os: windows
arch: amd64
toolchain: msvc2017
libtype: static
libfiles: libui.lib
osHeader: ui_windows.h
# }
# mac {
# TODO beforeConfigure/beforeBuild: export SDKROOT=$(xcodebuild -version -sdk macosx10.13 Path)?
- job: darwin_amd64_shared
displayName: 'Darwin amd64 Shared'
pool:
vmImage: 'macos-10.13'
workspace:
clean: all
steps:
- template: azure-pipelines/setup-python3.yml
- template: azure-pipelines/install-latest-meson-ninja.yml
- template: azure-pipelines/darwin-install-ninja.yml
- template: azure-pipelines/configure.yml
parameters:
defaultLibrary: shared
- template: azure-pipelines/build.yml
- template: azure-pipelines/artifacts.yml
parameters:
os: darwin
arch: amd64
libtype: shared
libfiles: libui.A.dylib
osHeader: ui_darwin.h
- job: darwin_amd64_static
displayName: 'Darwin amd64 Static'
pool:
vmImage: 'macos-10.13'
workspace:
clean: all
steps:
- template: azure-pipelines/setup-python3.yml
- template: azure-pipelines/install-latest-meson-ninja.yml
- template: azure-pipelines/darwin-install-ninja.yml
- template: azure-pipelines/configure.yml
parameters:
defaultLibrary: static
- template: azure-pipelines/build.yml
- template: azure-pipelines/artifacts.yml
parameters:
os: darwin
arch: amd64
libtype: static
libfiles: libui.a
osHeader: ui_darwin.h
# }

View File

@ -1,399 +0,0 @@
# 31 march 2019
variables:
releaseExamples: 'controlgallery cpp-multithread datetime drawtext histogram tester timer'
strategy:
matrix:
# targetname:
# os: 'fill this in'
# arch: 'fill this in'
# toolchain: 'fill this in'
# libtype: 'fill this in'
# vmImage: 'fill this in'
# python3Template: 'fill filename'
# depsAndNinjaTemplate: 'fill filename'
# beforeConfigure: 'fill this in'
# beforeBuild: 'fill this in'
# afterBuild: 'fill this in'
# artifactTemplate: 'fill filename'
# libfiles: 'fill this in'
# osHeader: 'fill this in'
linux_386_shared:
os: 'linux'
arch: '386'
libtype: 'shared'
vmImage: 'ubuntu-16.04'
python3Template: 'azure-pipelines/setup-python3.yml'
depsAndNinjaTemplate: 'azure-pipelines/linux-386-install-gtk-dev.yml'
beforeConfigure: 'export CFLAGS=-m32 CXXFLAGS=-m32 PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig'
artifactTemplate: 'azure-pipelines/artifacts.yml'
libfiles: 'libui.so.0'
osHeader: 'ui_unix.h'
pool:
vmImage: $(vmImage)
workspace:
clean: all
steps:
- template: $(variables.python3Template)
- template: azure-pipelines/install-latest-meson-ninja.yml
- template: $(variables.depsAndNinjaTemplate)
- template: azure-pipelines/configure.yml
parameters:
beforeConfigure: $(beforeConfigure)
defaultLibrary: $(libtype)
- template: azure-pipelines/build.yml
parameters:
beforeBuild: $(beforeBuild)
afterBuild: $(afterBuild)
- template: $(variables.artifactTemplate)
parameters:
os: $(os)
arch: $(arch)
toolchain: $(toolchain)
libtype: $(libtype)
libfiles: $(libfiles)
osHeader: $(osHeader)
## linux {
#- job: linux_386_static
# displayName: 'Linux 386 Static'
# pool:
# vmImage: 'ubuntu-16.04'
# workspace:
# clean: all
# steps:
# - template: azure-pipelines/setup-python3.yml
# - template: azure-pipelines/install-latest-meson-ninja.yml
# - template: azure-pipelines/linux-386-install-gtk-dev.yml
# - template: azure-pipelines/configure.yml
# parameters:
# beforeConfigure: export CFLAGS=-m32 CXXFLAGS=-m32 PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig
# defaultLibrary: static
# - template: azure-pipelines/build.yml
# - template: azure-pipelines/artifacts.yml
# parameters:
# os: linux
# arch: 386
# libtype: static
# libfiles: libui.a
# osHeader: ui_unix.h
#
#- job: linux_amd64_shared
# displayName: 'Linux amd64 Shared'
# pool:
# vmImage: 'ubuntu-16.04'
# workspace:
# clean: all
# steps:
# - template: azure-pipelines/setup-python3.yml
# - template: azure-pipelines/install-latest-meson-ninja.yml
# - template: azure-pipelines/linux-install-gtk-dev.yml
# - template: azure-pipelines/configure.yml
# parameters:
# defaultLibrary: shared
# - template: azure-pipelines/build.yml
# - template: azure-pipelines/artifacts.yml
# parameters:
# os: linux
# arch: amd64
# libtype: shared
# libfiles: libui.so.0
# osHeader: ui_unix.h
#
#- job: linux_amd64_static
# displayName: 'Linux amd64 Static'
# pool:
# vmImage: 'ubuntu-16.04'
# workspace:
# clean: all
# steps:
# - template: azure-pipelines/setup-python3.yml
# - template: azure-pipelines/install-latest-meson-ninja.yml
# - template: azure-pipelines/linux-install-gtk-dev.yml
# - template: azure-pipelines/configure.yml
# parameters:
# defaultLibrary: static
# - template: azure-pipelines/build.yml
# - template: azure-pipelines/artifacts.yml
# parameters:
# os: linux
# arch: amd64
# libtype: static
# libfiles: libui.a
# osHeader: ui_unix.h
#
## }
#
## windows vs2015 {
#
#- job: windows_386_msvc2015_shared
# displayName: 'Windows 386 MSVC2015 Shared'
# pool:
# vmImage: 'vs2015-win2012r2'
# workspace:
# clean: all
# steps:
# - template: azure-pipelines/vs2015-install-python3.yml
# - template: azure-pipelines/install-latest-meson-ninja.yml
# - template: azure-pipelines/windows-install-ninja.yml
# - template: azure-pipelines/configure.yml
# parameters:
# beforeConfigure: call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86
# defaultLibrary: shared
# - template: azure-pipelines/build.yml
# parameters:
# beforeBuild: call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86
# - template: azure-pipelines/windows-artifacts.yml
# parameters:
# os: windows
# arch: 386
# toolchain: msvc2015
# libtype: shared
# libfiles: libui.dll libui.exp libui.lib
# osHeader: ui_windows.h
#
#- job: windows_386_msvc2015_static
# displayName: 'Windows 386 MSVC2015 Static'
# pool:
# vmImage: 'vs2015-win2012r2'
# workspace:
# clean: all
# steps:
# - template: azure-pipelines/vs2015-install-python3.yml
# - template: azure-pipelines/install-latest-meson-ninja.yml
# - template: azure-pipelines/windows-install-ninja.yml
# - template: azure-pipelines/configure.yml
# parameters:
# beforeConfigure: call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86
# defaultLibrary: static
# - template: azure-pipelines/build.yml
# parameters:
# beforeBuild: call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86
# afterBuild: ren build\meson-out\libui.a libui.lib
# - template: azure-pipelines/windows-artifacts.yml
# parameters:
# os: windows
# arch: 386
# toolchain: msvc2015
# libtype: static
# libfiles: libui.lib
# osHeader: ui_windows.h
#
#- job: windows_amd64_msvc2015_shared
# displayName: 'Windows amd64 MSVC2015 Shared'
# pool:
# vmImage: 'vs2015-win2012r2'
# workspace:
# clean: all
# steps:
# - template: azure-pipelines/vs2015-install-python3.yml
# - template: azure-pipelines/install-latest-meson-ninja.yml
# - template: azure-pipelines/windows-install-ninja.yml
# - template: azure-pipelines/configure.yml
# parameters:
# beforeConfigure: call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64
# defaultLibrary: shared
# - template: azure-pipelines/build.yml
# parameters:
# beforeBuild: call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64
# - template: azure-pipelines/windows-artifacts.yml
# parameters:
# os: windows
# arch: amd64
# toolchain: msvc2015
# libtype: shared
# libfiles: libui.dll libui.exp libui.lib
# osHeader: ui_windows.h
#
#- job: windows_amd64_msvc2015_static
# displayName: 'Windows amd64 MSVC2015 Static'
# pool:
# vmImage: 'vs2015-win2012r2'
# workspace:
# clean: all
# steps:
# - template: azure-pipelines/vs2015-install-python3.yml
# - template: azure-pipelines/install-latest-meson-ninja.yml
# - template: azure-pipelines/windows-install-ninja.yml
# - template: azure-pipelines/configure.yml
# parameters:
# beforeConfigure: call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64
# defaultLibrary: static
# - template: azure-pipelines/build.yml
# parameters:
# beforeBuild: call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64
# afterBuild: ren build\meson-out\libui.a libui.lib
# - template: azure-pipelines/windows-artifacts.yml
# parameters:
# os: windows
# arch: amd64
# toolchain: msvc2015
# libtype: static
# libfiles: libui.lib
# osHeader: ui_windows.h
#
## }
#
## windows vs2017 {
#
#- job: windows_386_msvc2017_shared
# displayName: 'Windows 386 MSVC2017 Shared'
# pool:
# vmImage: 'vs2017-win2016'
# workspace:
# clean: all
# steps:
# - template: azure-pipelines/setup-python3.yml
# - template: azure-pipelines/install-latest-meson-ninja.yml
# - template: azure-pipelines/windows-install-ninja.yml
# - template: azure-pipelines/configure.yml
# parameters:
# beforeConfigure: call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x86
# defaultLibrary: shared
# - template: azure-pipelines/build.yml
# parameters:
# beforeBuild: call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x86
# - template: azure-pipelines/windows-artifacts.yml
# parameters:
# os: windows
# arch: 386
# toolchain: msvc2017
# libtype: shared
# libfiles: libui.dll libui.exp libui.lib
# osHeader: ui_windows.h
#
#- job: windows_386_msvc2017_static
# displayName: 'Windows 386 MSVC2017 Static'
# pool:
# vmImage: 'vs2017-win2016'
# workspace:
# clean: all
# steps:
# - template: azure-pipelines/setup-python3.yml
# - template: azure-pipelines/install-latest-meson-ninja.yml
# - template: azure-pipelines/windows-install-ninja.yml
# - template: azure-pipelines/configure.yml
# parameters:
# beforeConfigure: call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x86
# defaultLibrary: static
# - template: azure-pipelines/build.yml
# parameters:
# beforeBuild: call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x86
# afterBuild: ren build\meson-out\libui.a libui.lib
# - template: azure-pipelines/windows-artifacts.yml
# parameters:
# os: windows
# arch: 386
# toolchain: msvc2017
# libtype: static
# libfiles: libui.lib
# osHeader: ui_windows.h
#
#- job: windows_amd64_msvc2017_shared
# displayName: 'Windows amd64 MSVC2017 Shared'
# pool:
# vmImage: 'vs2017-win2016'
# workspace:
# clean: all
# steps:
# - template: azure-pipelines/setup-python3.yml
# - template: azure-pipelines/install-latest-meson-ninja.yml
# - template: azure-pipelines/windows-install-ninja.yml
# - template: azure-pipelines/configure.yml
# parameters:
# beforeConfigure: call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" amd64
# defaultLibrary: shared
# - template: azure-pipelines/build.yml
# parameters:
# beforeBuild: call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" amd64
# - template: azure-pipelines/windows-artifacts.yml
# parameters:
# os: windows
# arch: amd64
# toolchain: msvc2017
# libtype: shared
# libfiles: libui.dll libui.exp libui.lib
# osHeader: ui_windows.h
#
#- job: windows_amd64_msvc2017_static
# displayName: 'Windows amd64 MSVC2017 Static'
# pool:
# vmImage: 'vs2017-win2016'
# workspace:
# clean: all
# steps:
# - template: azure-pipelines/setup-python3.yml
# - template: azure-pipelines/install-latest-meson-ninja.yml
# - template: azure-pipelines/windows-install-ninja.yml
# - template: azure-pipelines/configure.yml
# parameters:
# beforeConfigure: call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" amd64
# defaultLibrary: static
# - template: azure-pipelines/build.yml
# parameters:
# beforeBuild: call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" amd64
# afterBuild: ren build\meson-out\libui.a libui.lib
# - template: azure-pipelines/windows-artifacts.yml
# parameters:
# os: windows
# arch: amd64
# toolchain: msvc2017
# libtype: static
# libfiles: libui.lib
# osHeader: ui_windows.h
#
## }
#
## mac {
#
## TODO beforeConfigure/beforeBuild: export SDKROOT=$(xcodebuild -version -sdk macosx10.13 Path)?
#
#- job: darwin_amd64_shared
# displayName: 'Darwin amd64 Shared'
# pool:
# vmImage: 'macos-10.13'
# workspace:
# clean: all
# steps:
# - template: azure-pipelines/setup-python3.yml
# - template: azure-pipelines/install-latest-meson-ninja.yml
# - template: azure-pipelines/darwin-install-ninja.yml
# - template: azure-pipelines/configure.yml
# parameters:
# defaultLibrary: shared
# - template: azure-pipelines/build.yml
# - template: azure-pipelines/artifacts.yml
# parameters:
# os: darwin
# arch: amd64
# libtype: shared
# libfiles: libui.A.dylib
# osHeader: ui_darwin.h
#
#- job: darwin_amd64_static
# displayName: 'Darwin amd64 Static'
# pool:
# vmImage: 'macos-10.13'
# workspace:
# clean: all
# steps:
# - template: azure-pipelines/setup-python3.yml
# - template: azure-pipelines/install-latest-meson-ninja.yml
# - template: azure-pipelines/darwin-install-ninja.yml
# - template: azure-pipelines/configure.yml
# parameters:
# defaultLibrary: static
# - template: azure-pipelines/build.yml
# - template: azure-pipelines/artifacts.yml
# parameters:
# os: darwin
# arch: amd64
# libtype: static
# libfiles: libui.a
# osHeader: ui_darwin.h
#
## }

View File

@ -1,28 +0,0 @@
# 6 april 2019
parameters:
os: ''
arch: ''
libtype: ''
libfiles: ''
osHeader: ''
steps:
- script: |
set -x
pushd build/meson-out
cp ../../ui.h ../../${{ parameters.osHeader }} .
tar czf $(Build.ArtifactStagingDirectory)/libui-$(Build.SourceBranchName)-${{ parameters.os }}-${{ parameters.arch }}-${{ parameters.libtype }}.tgz ${{ parameters.libfiles }} ui.h ${{ parameters.osHeader}}
tar czf $(Build.ArtifactStagingDirectory)/examples-$(Build.SourceBranchName)-${{ parameters.os }}-${{ parameters.arch }}-${{ parameters.libtype }}.tgz $(releaseExamples)
rm ui.h ${{ parameters.osHeader }}
popd
displayName: 'Create Artifacts'
- task: GitHubRelease@0
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/')
inputs:
gitHubConnection: andlabs
repositoryName: andlabs/libui
action: 'edit'
addChangelog: false
assets: '$(Build.ArtifactStagingDirectory)/*'
assetUploadMode: 'replace'

View File

@ -1,12 +0,0 @@
# 5 april 2019
parameters:
beforeBuild: ''
afterBuild: ''
steps:
- script: |
${{ parameters.beforeBuild }}
ninja -C build -v
${{ parameters.afterBuild }}
displayName: 'Build'

View File

@ -1,43 +0,0 @@
# 7 april 2019
BEGIN {
RS = ""
FS = "\n +- "
}
/^- job:/ {
for (i = 1; i <= NF; i++) {
if (!(i in nextindex)) {
# fast path for first occurrence
lines[i, 0] = $i
nextindex[i] = 1
if (maxIndex < i)
maxIndex = i
continue
}
found = 0
for (j = 0; j < nextindex[i]; j++)
if (lines[i, j] == $i) {
found = 1
break
}
if (!found) {
lines[i, nextindex[i]] = $i
nextindex[i]++
}
}
}
END {
for (i = 1; i <= maxIndex; i++) {
if (nextindex[i] == 1) {
# only one entry here, just print it
print "- " lines[i, 0]
continue
}
print "{"
for (j = 0; j < nextindex[i]; j++)
print "- " lines[i, j]
print "}"
}
}

View File

@ -1,298 +0,0 @@
{
- - job: linux_386_shared
displayName: 'Linux 386 Shared'
pool:
vmImage: 'ubuntu-16.04'
workspace:
clean: all
steps:
- - job: linux_386_static
displayName: 'Linux 386 Static'
pool:
vmImage: 'ubuntu-16.04'
workspace:
clean: all
steps:
- - job: linux_amd64_shared
displayName: 'Linux amd64 Shared'
pool:
vmImage: 'ubuntu-16.04'
workspace:
clean: all
steps:
- - job: linux_amd64_static
displayName: 'Linux amd64 Static'
pool:
vmImage: 'ubuntu-16.04'
workspace:
clean: all
steps:
- - job: windows_386_msvc2015_shared
displayName: 'Windows 386 MSVC2015 Shared'
pool:
vmImage: 'vs2015-win2012r2'
workspace:
clean: all
steps:
- - job: windows_386_msvc2015_static
displayName: 'Windows 386 MSVC2015 Static'
pool:
vmImage: 'vs2015-win2012r2'
workspace:
clean: all
steps:
- - job: windows_amd64_msvc2015_shared
displayName: 'Windows amd64 MSVC2015 Shared'
pool:
vmImage: 'vs2015-win2012r2'
workspace:
clean: all
steps:
- - job: windows_amd64_msvc2015_static
displayName: 'Windows amd64 MSVC2015 Static'
pool:
vmImage: 'vs2015-win2012r2'
workspace:
clean: all
steps:
- - job: windows_386_msvc2017_shared
displayName: 'Windows 386 MSVC2017 Shared'
pool:
vmImage: 'vs2017-win2016'
workspace:
clean: all
steps:
- - job: windows_386_msvc2017_static
displayName: 'Windows 386 MSVC2017 Static'
pool:
vmImage: 'vs2017-win2016'
workspace:
clean: all
steps:
- - job: windows_amd64_msvc2017_shared
displayName: 'Windows amd64 MSVC2017 Shared'
pool:
vmImage: 'vs2017-win2016'
workspace:
clean: all
steps:
- - job: windows_amd64_msvc2017_static
displayName: 'Windows amd64 MSVC2017 Static'
pool:
vmImage: 'vs2017-win2016'
workspace:
clean: all
steps:
- - job: darwin_amd64_shared
displayName: 'Darwin amd64 Shared'
pool:
vmImage: 'macos-10.13'
workspace:
clean: all
steps:
- - job: darwin_amd64_static
displayName: 'Darwin amd64 Static'
pool:
vmImage: 'macos-10.13'
workspace:
clean: all
steps:
}
{
- template: azure-pipelines/setup-python3.yml
- template: azure-pipelines/vs2015-install-python3.yml
}
- template: azure-pipelines/install-latest-meson-ninja.yml
{
- template: azure-pipelines/linux-386-install-gtk-dev.yml
- template: azure-pipelines/linux-install-gtk-dev.yml
- template: azure-pipelines/windows-install-ninja.yml
- template: azure-pipelines/darwin-install-ninja.yml
}
{
- template: azure-pipelines/configure.yml
parameters:
beforeConfigure: export CFLAGS=-m32 CXXFLAGS=-m32 PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig
defaultLibrary: shared
- template: azure-pipelines/configure.yml
parameters:
beforeConfigure: export CFLAGS=-m32 CXXFLAGS=-m32 PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig
defaultLibrary: static
- template: azure-pipelines/configure.yml
parameters:
defaultLibrary: shared
- template: azure-pipelines/configure.yml
parameters:
defaultLibrary: static
- template: azure-pipelines/configure.yml
parameters:
beforeConfigure: call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86
defaultLibrary: shared
- template: azure-pipelines/configure.yml
parameters:
beforeConfigure: call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86
defaultLibrary: static
- template: azure-pipelines/configure.yml
parameters:
beforeConfigure: call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64
defaultLibrary: shared
- template: azure-pipelines/configure.yml
parameters:
beforeConfigure: call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64
defaultLibrary: static
- template: azure-pipelines/configure.yml
parameters:
beforeConfigure: call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x86
defaultLibrary: shared
- template: azure-pipelines/configure.yml
parameters:
beforeConfigure: call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x86
defaultLibrary: static
- template: azure-pipelines/configure.yml
parameters:
beforeConfigure: call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" amd64
defaultLibrary: shared
- template: azure-pipelines/configure.yml
parameters:
beforeConfigure: call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" amd64
defaultLibrary: static
}
{
- template: azure-pipelines/build.yml
- template: azure-pipelines/build.yml
parameters:
beforeBuild: call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86
- template: azure-pipelines/build.yml
parameters:
beforeBuild: call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86
afterBuild: ren build\meson-out\libui.a libui.lib
- template: azure-pipelines/build.yml
parameters:
beforeBuild: call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64
- template: azure-pipelines/build.yml
parameters:
beforeBuild: call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64
afterBuild: ren build\meson-out\libui.a libui.lib
- template: azure-pipelines/build.yml
parameters:
beforeBuild: call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x86
- template: azure-pipelines/build.yml
parameters:
beforeBuild: call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x86
afterBuild: ren build\meson-out\libui.a libui.lib
- template: azure-pipelines/build.yml
parameters:
beforeBuild: call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" amd64
- template: azure-pipelines/build.yml
parameters:
beforeBuild: call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" amd64
afterBuild: ren build\meson-out\libui.a libui.lib
}
{
- template: azure-pipelines/artifacts.yml
parameters:
os: linux
arch: 386
libtype: shared
libfiles: libui.so.0
osHeader: ui_unix.h
- template: azure-pipelines/artifacts.yml
parameters:
os: linux
arch: 386
libtype: static
libfiles: libui.a
osHeader: ui_unix.h
- template: azure-pipelines/artifacts.yml
parameters:
os: linux
arch: amd64
libtype: shared
libfiles: libui.so.0
osHeader: ui_unix.h
- template: azure-pipelines/artifacts.yml
parameters:
os: linux
arch: amd64
libtype: static
libfiles: libui.a
osHeader: ui_unix.h
- template: azure-pipelines/windows-artifacts.yml
parameters:
os: windows
arch: 386
toolchain: msvc2015
libtype: shared
libfiles: libui.dll libui.exp libui.lib
osHeader: ui_windows.h
- template: azure-pipelines/windows-artifacts.yml
parameters:
os: windows
arch: 386
toolchain: msvc2015
libtype: static
libfiles: libui.lib
osHeader: ui_windows.h
- template: azure-pipelines/windows-artifacts.yml
parameters:
os: windows
arch: amd64
toolchain: msvc2015
libtype: shared
libfiles: libui.dll libui.exp libui.lib
osHeader: ui_windows.h
- template: azure-pipelines/windows-artifacts.yml
parameters:
os: windows
arch: amd64
toolchain: msvc2015
libtype: static
libfiles: libui.lib
osHeader: ui_windows.h
- template: azure-pipelines/windows-artifacts.yml
parameters:
os: windows
arch: 386
toolchain: msvc2017
libtype: shared
libfiles: libui.dll libui.exp libui.lib
osHeader: ui_windows.h
- template: azure-pipelines/windows-artifacts.yml
parameters:
os: windows
arch: 386
toolchain: msvc2017
libtype: static
libfiles: libui.lib
osHeader: ui_windows.h
- template: azure-pipelines/windows-artifacts.yml
parameters:
os: windows
arch: amd64
toolchain: msvc2017
libtype: shared
libfiles: libui.dll libui.exp libui.lib
osHeader: ui_windows.h
- template: azure-pipelines/windows-artifacts.yml
parameters:
os: windows
arch: amd64
toolchain: msvc2017
libtype: static
libfiles: libui.lib
osHeader: ui_windows.h
- template: azure-pipelines/artifacts.yml
parameters:
os: darwin
arch: amd64
libtype: shared
libfiles: libui.A.dylib
osHeader: ui_darwin.h
- template: azure-pipelines/artifacts.yml
parameters:
os: darwin
arch: amd64
libtype: static
libfiles: libui.a
osHeader: ui_darwin.h
}

Some files were not shown because too many files have changed in this diff Show More