Merge branch 'master' into autolayout

For some reason the merge of this ages ago was forgotten. We're gonna make a dummy commit and properly --no-ff this.
This commit is contained in:
Pietro Gagliardi 2019-01-05 17:34:08 -05:00
commit c65865d5c2
382 changed files with 37635 additions and 4841 deletions

78
.appveyor.yml Normal file
View File

@ -0,0 +1,78 @@
version: 'build #{build}'
environment:
matrix:
- linking: shared
compiler: msvc2013
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013
- linking: static
compiler: msvc2013
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013
- linking: shared
compiler: msvc2015
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
- linking: static
compiler: msvc2015
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
- linking: shared
compiler: msvc2017
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
- linking: static
compiler: msvc2017
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
- linking: static
compiler: mingw
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
platform:
- Win32
- x64
before_build:
- if %compiler%==msvc2013 ( set "CMAKE_GENERATOR=Visual Studio 12 2013" )
else if %compiler%==msvc2015 ( set "CMAKE_GENERATOR=Visual Studio 14 2015" )
else if %compiler%==msvc2017 ( set "CMAKE_GENERATOR=Visual Studio 15 2017" )
else if %compiler%==mingw ( set "CMAKE_GENERATOR=MinGW Makefiles" )
- if %compiler%-%platform%==mingw-Win32 ( set "PATH=C:\msys64\mingw32\bin;%PATH%" )
else if %compiler%-%platform%==mingw-x64 ( set "PATH=C:\msys64\mingw64\bin;%PATH%" )
else if %platform%==x64 ( set "CMAKE_GENERATOR=%CMAKE_GENERATOR% Win64" )
- if %linking%==static ( set CMAKE_FLAGS=-DBUILD_SHARED_LIBS=OFF )
- if %compiler%==mingw ( set "outdir=build\out" ) else ( set "outdir=build\out\Release" )
- ren "C:\Program Files\Git\usr\bin\sh.exe" _sh.exe
- set "simultaneous=3"
build_script:
- md build && cd build
- if not %compiler%==mingw ( set "CFLAGS=/MP%simultaneous% %CFLAGS%" )
- if not %compiler%==mingw ( set "CPPFLAGS=/MP%simultaneous% %CPPFLAGS%" )
- if not %compiler%==mingw ( set "CXXFLAGS=/MP%simultaneous% %CXXFLAGS%" )
- cmake -G "%CMAKE_GENERATOR%" %CMAKE_FLAGS% ..
- if %compiler%==mingw ( mingw32-make -j%simultaneous% tester examples )
else ( msbuild libui.sln /t:Build /p:Configuration=Release /p:Platform=%platform% )
- cd %APPVEYOR_BUILD_FOLDER%
after_build:
- if %platform%==x64 ( set "arch=amd64" ) else ( set "arch=386" )
- if %APPVEYOR_REPO_TAG%==true ( set "version=%APPVEYOR_REPO_TAG_NAME%" )
else ( set "version=%APPVEYOR_REPO_BRANCH%" )
- if %linking%==shared ( set "artifact=shared" ) else ( set "artifact=%compiler%-static" )
- set "artifact=%version%-windows-%arch%-%artifact%"
- del .\%outdir%\libui.exp # remove unnecessary files
- 7z a libui-%artifact%.zip .\%outdir%\libui.* ui.h ui_windows.h
- 7z l libui-%artifact%.zip
- 7z a examples-%artifact%.zip .\%outdir%\*.exe
- 7z l examples-%artifact%.zip
artifacts:
- path: libui-*.zip
name: libui
- path: examples-*.zip
name: examples
deploy:
provider: GitHub
artifact: libui, examples
auth_token:
secure: li92W7mFAC8HbAVeZN6Ugmo5H1GzKSjr6DXlMniLcCRspKmi2Nz1nlslSa+9sLfo
on:
appveyor_repo_tag: true # deploy on tag push only

View File

@ -1,16 +1,97 @@
os:
- linux
# This makes us use Ubuntu 14 instead of 12
dist: trusty
language: c
script:
- sudo apt-get update
- sudo apt-get install libgtk-3-dev -y || sudo apt-cache search libgtk3
- make -f GNUmakefile
- make -f GNUmakefile test
- make -f GNUmakefile examples
# TODO osx
# need to figure out how to force language: objective-c and turn off the apt-get rules
include: &toolchain_linux_amd64
os: linux
dist: trusty
addons:
apt:
update: true
packages:
- libgtk-3-dev
include: &toolchain_linux_386
os: linux
dist: trusty
addons:
apt:
packages:
- gcc-multilib
- g++-multilib
- libgtk-3-dev:i386
# the rest fixes broken dependencies of libgtk:i386
- libgirepository-1.0-1:i386
- libglib2.0-dev:i386
- gir1.2-glib-2.0:i386
- gir1.2-atk-1.0:i386
- libatk1.0-dev:i386
- libfreetype6-dev:i386
- libfontconfig1-dev:i386
- libcairo2-dev:i386
- libgdk-pixbuf2.0-dev:i386
- libpango1.0-dev:i386
- libxft-dev:i386
- libpng12-dev:i386
include: &toolchain_osx_amd64
os: osx
osx_image: xcode8
# Travis CI build matrix.
# Each entry below will trigger an extra, parallel build on Travis.
matrix:
include:
- env: linking=shared arch=amd64
<<: *toolchain_linux_amd64
- env: linking=static arch=amd64
<<: *toolchain_linux_amd64
- env: linking=shared arch=386
<<: *toolchain_linux_386
- env: linking=static arch=386
<<: *toolchain_linux_386
- env: linking=shared arch=amd64
<<: *toolchain_osx_amd64
- env: linking=static arch=amd64
<<: *toolchain_osx_amd64
install:
- if [[ "${arch}" == "386" ]]; then
export CFLAGS=-m32;
export CXXFLAGS=-m32;
export PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig;
fi
- if [[ "${linking}" == "static" ]]; then
export CMAKE_FLAGS=-DBUILD_SHARED_LIBS=OFF;
fi
script:
- cmake --version
- mkdir build
- pushd build
- cmake -G "Unix Makefiles" ${CMAKE_FLAGS} ..
- make tester examples
- popd
after_success:
- ls -lR build/out
- file build/out/test
- export platform="$TRAVIS_OS_NAME"
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then cp ui.h ui_darwin.h build/out/; export platform=darwin; fi
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then cp ui.h ui_unix.h build/out/; fi
- if [[ "x${TRAVIS_TAG}" != "x" ]]; then export version=${TRAVIS_TAG}; else export version=${TRAVIS_BRANCH}; fi
- export artifact=${version}-${platform}-${arch}-${linking}
- echo ${artifact}
- pushd build/out
- # TODO do not include symlinks in the archive
- tar -czvf libui-${artifact}.tgz libui.* *.h
- tar -czvf examples-${artifact}.tgz `find . -type f ! -name "*.*"`
- popd
deploy:
provider: releases
api_key:
secure: "fmgC97mlXQY/ASWAL/GyTJfiJIo/hsVFf6bP3Zz8odv259PJUFGgnZ9kNIgJcFQ5961lXDFi7pBMMSetm1GZ2EBZxIXnUfe1kfIhw62ybJHIwB2+g2tc8A4zzfkWJVY4xVYpitOU3iMuu5Z8U2n+68RYWKpcxhbkVw5v8Zu2Rms="
file: build/out/*.tgz
file_glob: true
skip_cleanup: true
on:
tags: true

238
CMakeLists.txt Normal file
View File

@ -0,0 +1,238 @@
# 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()
# the docs don't say this, but the CACHE part is important; see https://stackoverflow.com/questions/34208360/cmake-seems-to-ignore-cmake-osx-deployment-target
# TODO figure out what other variables must be set with CACHE
# TODO figure out if FORCE is needed here
# TODO figure out whether STRING "" is best or if something else is better; also what FORCE does because I forget and later I say it's needed
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.8" CACHE STRING "" FORCE)
# we want to disable incremental linking
# see also:
# - 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
# either DEBUG or Debug will work; we use DEBUG as that's what cmake does internally (https://cmake.org/pipermail/cmake/2013-June/055177.html)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE DEBUG CACHE STRING "" FORCE)
endif()
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?
# the RTC flags are only supplied in debug builds because they are only supposed to be used by debug builds (see "This is because run-time error checks are not valid in a release (optimized) build." in https://docs.microsoft.com/cpp/build/reference/rtc-run-time-error-checks)
# /RTCc is not supplied because it's discouraged as of VS2015; see https://www.reddit.com/r/cpp/comments/46mhne/rtcc_rejects_conformant_code_with_visual_c_2015/d06auq5
# /EHsc is to shut the compiler up in some cases
# TODO make /EHsc C++-only
set(_COMMON_CFLAGS
/W4 /wd4100
/bigobj /nologo
$<$<CONFIG:Debug>:/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()
# TODO document this
if(ADDRESS_SANITIZER)
set(_COMMON_CFLAGS ${_COMMON_CFLAGS}
-fsanitize=address
-fno-omit-frame-pointer
)
set(_COMMON_LDFLAGS ${_COMMON_LDFLAGS}
-fsanitize=address
-fno-omit-frame-pointer
)
endif()
endif()
# problem:
# - 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(libui ${_LIBUI_SOURCES})
target_include_directories(libui
PUBLIC .
PRIVATE ${_LIBUI_INCLUEDIRS})
target_compile_definitions(libui
PRIVATE ${_LIBUI_DEFS})
# cmake produces this for us by default but only for shared libraries
target_compile_definitions(libui
PRIVATE libui_EXPORTS)
target_compile_options(libui
PUBLIC ${_COMMON_CFLAGS}
PRIVATE ${_LIBUI_CFLAGS})
# TODO link directories?
if(BUILD_SHARED_LIBS)
target_link_libraries(libui
PRIVATE ${_LIBUI_LIBS})
endif()
# TODO INTERFACE libs don't inherit to grandhcildren?
# on Windows the linker for static libraries is different; don't give it the flags
if(BUILD_SHARED_LIBS)
_target_link_options_private(libui
_COMMON_LDFLAGS
_LIBUI_LDFLAGS)
endif()
if(NOT BUILD_SHARED_LIBS)
# TODO figure out a way to tell libui that it's static
target_compile_definitions(libui
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(libui PROPERTIES
SOVERSION "${_VERSION}")
endif()
endif()
# TODO why is this not a default?!
set_property(TARGET libui PROPERTY
POSITION_INDEPENDENT_CODE True)
macro(_add_exec _name)
# TODOTODO re-add WIN32 when merging back
add_executable(${_name}
EXCLUDE_FROM_ALL
${ARGN})
target_link_libraries(${_name} libui)
_target_link_options_private(${_name}
_COMMON_LDFLAGS)
# TODO does this propagate?
set_property(TARGET ${_name} PROPERTY
POSITION_INDEPENDENT_CODE True)
# TODO see above about INTERFACE
if(NOT BUILD_SHARED_LIBS)
target_link_libraries(${_name}
${_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")

131
CONTRIBUTING.md Normal file
View File

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

141
Compatibility.md Normal file
View File

@ -0,0 +1,141 @@
# Useful things in newer versions
## Windows
### Windows 7
http://channel9.msdn.com/blogs/pdc2008/pc43
TODO look up PDC 2008 talk "new shell user interface"
- new animation and text engine
- ribbon control (didn't this have some additional license?)
- LVITEM.piColFmt
### Windows 8
### Windows 8.1
### Windows 10
## GTK+
TODO what ships with Ubuntu Quantal (12.10)?
### GTK+ 3.6
ships with: Ubuntu Raring (13.04)
- GtkEntry and GtkTextView have input purposes and input hints for external input methods but do not change input themselves
- according to Company, we connect to insert-text for that
- GtkLevelBar
- GtkMenuButton
- **GtkSearchEntry**
### GTK+ 3.8
ships with: Ubuntu Saucy (13.10)
Not many interesting new things to us here, unless you count widget-internal tickers and single-click instead of double-click to select list items (a la KDE)... and oh yeah, also widget opacity.
### GTK+ 3.10
ships with: **Ubuntu Trusty (14.04 LTS)**
<br>GLib version: 2.40
- tab character stops in GtkEntry
- GtkHeaderBar
- intended for titlebar overrides; GtkInfoBar is what I keep thinking GtkHeaderBar is
- **GtkListBox**
- GtkRevealer for smooth animations of disclosure triangles
- GtkSearchBar for custom search popups
- **GtkStack and GtkStackSwitcher**
- titlebar overrides (seems to be the hot new thing)
### GTK+ 3.12
ships with: Ubuntu Utopic (14.10)
<br>GLib version: 2.42
- GtkActionBar (basically like the bottom-of-the-window toolbars in Mac programs)
- gtk_get_locale_direction(), for internationalization
- more control over GtkHeaderBar
- **GtkPopover**
- GtkPopovers on GtkMenuButtons
- GtkStack signaling
- **gtk_tree_path_new_from_indicesv()** (for when we add Table if we have trees too)
### GTK+ 3.14
ships with: **Debian Jessie**, Ubuntu Vivid (15.04)
<br>GLib version: Debian: 2.42, Ubuntu: 2.44
- gestures
- better GtkListbox selection handling
- more style classes (TODO also prior?)
- delayed switch changes on GtkSwitch
### GTK+ 3.16
ships with: Ubuntu Wily (15.10)
<br>GLib version: 2.46
- gtk_clipboard_get_default() (???)
- **GtkGLArea**
- proper xalign and yalign for GtkLabel; should get rid of runtime deprecation warnings
- better control of GtkListBox model-based creation (probably not relevant but)
- GtkModelButton (for GActions; probably not relevant?)
- wide handles on GtkPaned
- GtkPopoverMenu
- IPP paper names in GtkPaperSize (TODO will this be important for printing?)
- multiple matches in GtkSearchEntry (TODO evaluate priority)
- **GtkStackSidebar**
- GTK_STYLE_CLASS_LABEL, GTK_STYLE_CLASS_MONOSPACE, GTK_STYLE_CLASS_STATUSBAR, GTK_STYLE_CLASS_TOUCH_SELECTION, GTK_STYLE_CLASS_WIDE (TODO figure out which of these are useful)
- GtkTextView: extend-selection
- GtkTextView: font fallbacks
### GTK+ 3.18
### GTK+ 3.20
## Cocoa
### Mac OS X 10.8
- Foundation ([full details](https://developer.apple.com/library/mac/releasenotes/Foundation/RN-FoundationOlderNotes/#//apple_ref/doc/uid/TP40008080-TRANSLATED_CHAPTER_965-TRANSLATED_DEST_999B))
- NSDateComponents supports leap months
- NSNumberFormatter and NSDateFormatter default to 10.4 behavior by default (need to explicitly do this on 10.7)
- **NSUserNotification and NSUserNotificationCenter for Growl-style notifications**
- better linguistic triggers for Spanish and Italian
- NSByteCountFormatter
- AppKit ([full details](https://developer.apple.com/library/mac/releasenotes/AppKit/RN-AppKitOlderNotes/#X10_8Notes))
- view-based NSTableView/NSOutlineView have expansion tooltips
- NSScrollView magnification
- Quick Look events; TODO see if they conflict with keyboard handling in Area
- NSPageController (maybe useful?)
- not useful for package UI, but may be useful for a new library (probably not by me): NSSharingService
- NSOpenPanel and NSSavePanel are now longer NSPanels or NSWindows in sandboxed applications; this may be an issue should anyone dare to enable sandboxing on a program that uses package ui
- NSTextAlternatives
- -[NSOpenGLContext setFullScreen] now ineffective
- +[NSColor underPageBackgroundColor]
### Mac OS X 10.9
- Foundation ([full details](https://developer.apple.com/library/mac/releasenotes/Foundation/RN-Foundation/))
- system-provided progress reporting/cancellation support
- NSURLComponents
- **NSCalendar, NSDateFormatter, and NSNumberFormatter are now thread-safe**
- various NSCalendar and NSDateComponents improvements
- AppKit ([full details](https://developer.apple.com/library/mac/releasenotes/AppKit/RN-AppKit/))
- sheet handling is now block-based, queued, and in NSWindow; the delegate-based NSApplication API will still exist, except without the queue
- similar changes to NSAlert
- **return value changes to NSAlert**
- window visibility APIs (occlusion)
- NSApplicationActivationPolicyAccessory
- fullscreen toolbar behavior changes
- status items for multiple menu bars
- better NSSharingService support
- a special accelerated scrolling mode, Responsive Scrolling; won't matter for us since I plan to support the scroll wheel and it won't
- NSScrollView live scrolling notifications
- NSScrollView floating (anchored/non-scrolling) subviews
- better multimonitor support
- better key-value observing for NSOpenPanel/NSSavePanel (might want to look this up to see if we can override some other juicy details... TODO)
- better accessory view key-view handling in NSOpenPanel/NSSavePanel
- NSAppearance
- **-[NSTableView moveRowAtIndex:toIndex:] bug regarding first responders fixed**
- view-specific RTL overrides
### Mac OS X 10.10
### Mac OS X 10.11
* **NSLayoutGuide**

View File

@ -1,72 +0,0 @@
# 16 october 2015
# TODO http://stackoverflow.com/questions/4122831/disable-make-builtin-rules-and-variables-from-inside-the-make-file
# silence entering/leaving messages
MAKEFLAGS += --no-print-directory
OUTDIR = out
OBJDIR = .obj
# MAME does this so :/
ifeq ($(OS),Windows_NT)
OS = windows
endif
ifndef OS
UNAME = $(shell uname -s)
ifeq ($(UNAME),Darwin)
OS = darwin
else ifeq ($(UNAME),Haiku)
OS = haiku
else
OS = unix
endif
endif
# default is to build with debug symbols
ifndef NODEBUG
NODEBUG = 0
endif
# parameters
export OS
# TODO CC, CXX, RC, LD
export CFLAGS
export CXXFLAGS
# TODO RCFLAGS
export LDFLAGS
export NODEBUG
export EXAMPLE
export PREFIX
# for Debian - see https://github.com/andlabs/libui/pull/10
export DESTDIR
# other important variables
export OBJDIR
export OUTDIR
libui:
@$(MAKE) -f build/GNUmakefile.libui inlibuibuild=1
clean:
rm -rf $(OBJDIR) $(OUTDIR)
test: libui
@$(MAKE) -f build/GNUmakefile.test inlibuibuild=1
# TODO provide a build option for the queuemaintest
example: libui
@$(MAKE) -f build/GNUmakefile.example inlibuibuild=1
examples:
@$(MAKE) -f GNUmakefile example EXAMPLE=controlgallery
@$(MAKE) -f GNUmakefile example EXAMPLE=histogram
@$(MAKE) -f GNUmakefile example EXAMPLE=cpp-multithread
.PHONY: examples
install:
@$(MAKE) -f build/GNUmakefile.libui install inlibuibuild=1

113
NEWS.md Normal file
View File

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

65
OLD_uitable.h Normal file
View File

@ -0,0 +1,65 @@
// 20 june 2016
// kept in a separate file for now
typedef struct uiImage uiImage;
// TODO use const void * for const correctness
_UI_EXTERN uiImage *uiNewImage(double width, double height);
_UI_EXTERN void uiFreeImage(uiImage *i);
_UI_EXTERN void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int pixelStride);
typedef struct uiTableModel uiTableModel;
typedef struct uiTableModelHandler uiTableModelHandler;
// TODO actually validate these
_UI_ENUM(uiTableModelColumnType) {
uiTableModelColumnString,
uiTableModelColumnImage,
uiTableModelColumnInt,
uiTableModelColumnColor,
};
// TODO validate ranges; validate types on each getter/setter call (? table columns only?)
struct uiTableModelHandler {
int (*NumColumns)(uiTableModelHandler *, uiTableModel *);
uiTableModelColumnType (*ColumnType)(uiTableModelHandler *, uiTableModel *, int);
int (*NumRows)(uiTableModelHandler *, uiTableModel *);
void *(*CellValue)(uiTableModelHandler *, uiTableModel *, int, int);
void (*SetCellValue)(uiTableModelHandler *, uiTableModel *, int, int, const void *);
};
_UI_EXTERN void *uiTableModelStrdup(const char *str);
// TODO rename the strdup one to this too
_UI_EXTERN void *uiTableModelGiveColor(double r, double g, double b, double a);
_UI_EXTERN void *uiTableModelGiveInt(int i);
// TODO TakeString
// TODO add const
_UI_EXTERN int uiTableModelTakeInt(void *v);
_UI_EXTERN uiTableModel *uiNewTableModel(uiTableModelHandler *mh);
_UI_EXTERN void uiFreeTableModel(uiTableModel *m);
_UI_EXTERN void uiTableModelRowInserted(uiTableModel *m, int newIndex);
_UI_EXTERN void uiTableModelRowChanged(uiTableModel *m, int index);
_UI_EXTERN void uiTableModelRowDeleted(uiTableModel *m, int oldIndex);
// TODO reordering/moving
typedef struct uiTableColumn uiTableColumn;
_UI_EXTERN void uiTableColumnAppendTextPart(uiTableColumn *c, int modelColumn, int expand);
// TODO images shouldn't expand...
_UI_EXTERN void uiTableColumnAppendImagePart(uiTableColumn *c, int modelColumn, int expand);
_UI_EXTERN void uiTableColumnAppendButtonPart(uiTableColumn *c, int modelColumn, int expand);
// TODO should these have labels?
_UI_EXTERN void uiTableColumnAppendCheckboxPart(uiTableColumn *c, int modelColumn, int expand);
_UI_EXTERN void uiTableColumnAppendProgressBarPart(uiTableColumn *c, int modelColumn, int expand);
// TODO Editable?
_UI_EXTERN void uiTableColumnPartSetEditable(uiTableColumn *c, int part, int editable);
_UI_EXTERN void uiTableColumnPartSetTextColor(uiTableColumn *c, int part, int modelColumn);
typedef struct uiTable uiTable;
#define uiTable(this) ((uiTable *) (this))
_UI_EXTERN uiTableColumn *uiTableAppendColumn(uiTable *t, const char *name);
_UI_EXTERN uiTableColumn *uiTableAppendTextColumn(uiTable *t, const char *name, int modelColumn);
// TODO getter?
_UI_EXTERN void uiTableSetRowBackgroundColorModelColumn(uiTable *t, int modelColumn);
_UI_EXTERN uiTable *uiNewTable(uiTableModel *model);

169
README.md
View File

@ -1,30 +1,175 @@
# libui: a portable GUI library for C
This README is being written.<br>
[![Build Status](https://travis-ci.org/andlabs/libui.png)](https://travis-ci.org/andlabs/libui)
[![Build Status, Linux and macOS](https://travis-ci.org/andlabs/libui.svg?branch=master)](https://travis-ci.org/andlabs/libui)<br>
[![Build Status, Windows](https://ci.appveyor.com/api/projects/status/ouyk78c52mmisa31?svg=true)](https://ci.appveyor.com/project/andlabs/libui)
## Status
It has come to my attention that I have not been particularly clear about how usable or feature-complete libui is, and that this has fooled many people into expecting more from libui right this moment than I have explicitly promised to make available. I apologize for not doing this sooner.
libui is currently **mid-alpha** software. Much of what is currently present runs stabily enough for the examples and perhaps some small programs to work, but the stability is still a work-in-progress, much of what is already there is not feature-complete, some of it will be buggy on certain platforms, and there's a lot of stuff missing. In short, here's a list of features that I would like to add to libui, but that aren't in yet:
- 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.*
* **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**
* The longstanding Enter+Escape crashes on Windows have finally been fixed (thanks to @lxn).
* **Alpha 3.5 is now here.** This is a quickie release primiarly intended to deploy the above fix to package ui itself. **It is a partial binary release; sorry!** More new things will come in the next release, which will also introduce semver (so it will be called v0.4.0 instead).
* Alpha 3.5 also includes a new control gallery example. The screenshots below have not been updated yet.
*Old announcements can be found in the NEWS.md file.*
## Runtime Requirements
* Windows: Windows Vista SP2 with Platform Update or newer
* Unix: GTK+ 3.4 or newer
* Mac OS X: OS X 10.7 or newer
* Unix: GTK+ 3.10 or newer
* Mac OS X: OS X 10.8 or newer
## Build Requirements
* All platforms:
* GNU make 3.81 or newer (Xcode comes with this; on Windows you will need to get it yourself)
* Windows: Microsoft Visual Studio 2013 or newer (2013 is needed for `va_copy()`)
* MinGW is currently unsupported. MinGW-w64 support will be re-added once the following features come in:
* [Isolation awareness](https://msdn.microsoft.com/en-us/library/aa375197%28v=vs.85%29.aspx)
* Linker symbols for some functions such as `TaskDialog()` (which I thought I submitted...)
* Unix: nothing specific
* Mac OS X: nothing specific, so long as you can build Cocoa programs
* CMake 3.1.0 or newer
* Windows: either
* Microsoft Visual Studio 2013 or newer (2013 is needed for `va_copy()`) — you can build either a static or a shared library
* MinGW-w64 (other flavors of MinGW may not work) — **you can only build a static library**; shared library support will be re-added once the following features come in:
* [Isolation awareness](https://msdn.microsoft.com/en-us/library/aa375197%28v=vs.85%29.aspx), which is how you get themed controls from a DLL without needing a manifest
* Unix: nothing else specific
* Mac OS X: nothing else specific, so long as you can build Cocoa programs
(TODO write some notes on make variables and cross-compiling)
## Building
Out-of-tree builds typical of cmake are preferred:
```
$ # you must be in the top-level libui directory, otherwise this won't work
$ mkdir build
$ cd build
$ cmake ..
```
Pass `-DBUILD_SHARED_LIBS=OFF` to `cmake` to build a static library. The standard cmake build configurations are provided; if none is specified, `Debug` is used.
If you use a makefile generator with cmake, then
```
$ make
$ make tester # for the test program
$ make examples # for examples
```
and pass `VERBOSE=1` to see build commands. Build targets will be in the `build/out` folder.
Project file generators should work, but are untested by me.
On Windows, I use the `Unix Makefiles` generator and GNU make (built using the `build_w32.bat` script included in the source and run in the Visual Studio command line). In this state, if MinGW-w64 (either 32-bit or 64-bit) is not in your `%PATH%`, cmake will use MSVC by default; otherwise, cmake will use with whatever MinGW-w64 is in your path. `set PATH=%PATH%;c:\msys2\mingw(32/64)\bin` should be enough to temporarily change to a MinGW-w64 build for the current command line session only if you installed MinGW-w64 through [MSYS2](https://msys2.github.io/); no need to change global environment variables constantly.
## Installation
#### Arch Linux
Can be built from AUR: https://aur.archlinux.org/packages/libui-git/
## Documentation
Needs to be written. Consult ui.h and the examples for details for now.
Needs to be written. Consult `ui.h` and the examples for details for now.
## Language Bindings
libui was originally written as part of my [package ui for Go](https://github.com/andlabs/ui). Now that libui is separate, package ui has become a binding to libui. As such, package ui is the only official binding.
Other people have made bindings to other languages:
Language | Bindings
--- | ---
C++ | [libui-cpp](https://github.com/billyquith/libui-cpp), [cpp-libui-qtlike](https://github.com/aoloe/cpp-libui-qtlike)
C# / .NET Framework | [LibUI.Binding](https://github.com/NattyNarwhal/LibUI.Binding)
C# / .NET Core | [DevZH.UI](https://github.com/noliar/DevZH.UI), [SharpUI](https://github.com/benpye/sharpui/), [TCD.UI](https://github.com/tacdevel/tcdfx)
CHICKEN Scheme | [wasamasa/libui](https://github.com/wasamasa/libui)
Common Lisp | [jinwoo/cl-ui](https://github.com/jinwoo/cl-ui)
Crystal | [libui.cr](https://github.com/Fusion/libui.cr), [hedron](https://github.com/Qwerp-Derp/hedron)
D | [DerelictLibui (flat API)](https://github.com/Extrawurst/DerelictLibui), [libuid (object-oriented)](https://github.com/mogud/libuid)
Euphoria | [libui-euphoria](https://github.com/ghaberek/libui-euphoria)
Harbour | [HBUI](https://github.com/RJopek/HBUI)
Haskell | [haskell-libui](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)
Julia | [Libui.jl](https://github.com/joa-quim/Libui.jl)
Kotlin | [kotlin-libui](https://github.com/msink/kotlin-libui)
Lua | [libuilua](https://github.com/zevv/libuilua), [libui-lua](https://github.com/mdombroski/libui-lua), [lui](http://tset.de/lui/index.html), [lui](https://github.com/zhaozg/lui)
Nim | [ui](https://github.com/nim-lang/ui)
Perl6 | [perl6-libui](https://github.com/Garland-g/perl6-libui)
PHP | [ui](https://github.com/krakjoe/ui)
Python | [pylibui](https://github.com/joaoventura/pylibui), [pylibui-cffi](https://github.com/Yardanico/pylibui-cffi)
Ruby | [libui-ruby](https://github.com/jamescook/libui-ruby)
Rust | [libui-rs](https://github.com/pcwalton/libui-rs), [arcturu/libui-rs](https://github.com/arcturu/libui-rs), [LeoTindall/libui-rs](https://github.com/LeoTindall/libui-rs)
Scala | [scalaui](https://github.com/lolgab/scalaui)
Swift | [libui-swift](https://github.com/sclukey/libui-swift)
## Frequently Asked Questions
### Why does my program start in the background on OS X if I run from the command line?
OS X normally does not start program executables directly; instead, it uses [Launch Services](https://developer.apple.com/reference/coreservices/1658613-launch_services?language=objc) to coordinate the launching of the program between the various parts of the system and the loading of info from an .app bundle. One of these coordination tasks is responsible for bringing a newly launched app into the foreground. This is called "activation".
When you run a binary directly from the Terminal, however, you are running it directly, not through Launch Services. Therefore, the program starts in the background, because no one told it to activate! Now, it turns out [there is an API](https://developer.apple.com/reference/appkit/nsapplication/1428468-activateignoringotherapps) that we can use to force our app to be activated. But if we use it, then we'd be trampling over Launch Services, which already knows whether it should activate or not. Therefore, libui does not step over Launch Services, at the cost of requiring an extra user step if running directly from the command line.
See also [this](https://github.com/andlabs/libui/pull/20#issuecomment-211381971) and [this](http://stackoverflow.com/questions/25318524/what-exactly-should-i-pass-to-nsapp-activateignoringotherapps-to-get-my-appl).
## Contributing
See `CONTRIBUTING.md`.
## Screenshots

190
TODO.md
View File

@ -1,3 +1,13 @@
- make sure the last line of text layouts include leading
- documentation notes:
- static binaries do not link system libraries, meaning apps still depend on shared GTK+, etc.
- ui*Buttons are NOT compatible with uiButton functions
- more robust layout handling
- uiFormTie() for ensuring multiple uiForms have the same label area widths
- uiSizeGroup for size groups (GtkSizeGroup on GTK+, auto layout constraints on OS X; consider adding after 10.8 is gone)
- windows: should the initial hwndInsertAfter be HWND_BOTTOM for what we want?
- windows: document the rules for controls and containers
@ -6,9 +16,9 @@
- provisions for controls that cannot be grown? especiailly for windows
- LC_VERSION_MIN_MACOSX has the 10.11 SDK; see if we can knock it down to 10.7 too; OS version is fine
- LC_VERSION_MIN_MACOSX has the 10.11 SDK; see if we can knock it down to 10.8 too; OS version is fine
- apply the OS version stuff to the test program and examples too
- what about micro versions (10.7.x)? force 10.7.0?
- what about micro versions (10.8.x)? force 10.8.0?
- go through ALL the objective-c objects we create and make sure we are using the proper ownership (alloc/init and new are owned by us, all class method constructors are autoreleased - thanks mikeash)
@ -19,14 +29,6 @@
- provide a way to get the currently selected uiTab page? set?
- add uiPi for portability; compare against:
- M_PI on all systems with different requirements
- _GNU_SOURCE on unix
- _USE_MATH_DEFINES on windows
- G_PI on GLib
- XM_PI from DirectX
- Go math.Pi
- make it so that the windows cntrols only register a resize if their new minimum size is larger than their current size to easen the effect of flicker
- it won't remove that outright, but it'll help
@ -40,7 +42,6 @@
- DPI awareness on windows
- consider calling setAppleMenu: for the application menu; it doesn't seem to make much of a difference but
- http://stackoverflow.com/questions/4543087/applicationwillterminate-and-the-dock-but-wanting-to-cancel-this-action
ultimately:
@ -72,6 +73,8 @@ notes to self
- test RTL
- automate RTL
- now that stock items are deprecated, I have to maintain translations of the Cancel, Open, and Save buttons on GTK+ myself (thanks baedert in irc.gimp.net/#gtk+)
- either that or keep using stock items
- http://blogs.msdn.com/b/oldnewthing/archive/2014/02/26/10503148.aspx
@ -89,6 +92,171 @@ notes to self
don't forget LONGTERMs as well
notes
- http://blogs.msdn.com/b/oldnewthing/archive/2004/03/29/101121.aspx on accelerators
- group and tab should act as if they have no child if the child is hidden
on windows
- a way to do recursive main loops
- how do we handle 0 returns from non-recursive uiMainStep() calls that aren't the main loop? (event handlers, for instance)
- should repeated calls to uiMainStep() after uiQuit() return 0 reliably? this will be needed for non-recursive loops
http://stackoverflow.com/questions/38338426/meaning-of-ampersand-in-rc-files/38338841?noredirect=1#comment64093084_38338841
label shortcut keys
- remove whining from source code
[01:41:47] <vrishab> Hi. does pango support "fgalpha". I see that foreground="112233xx" works ( alpha=xx ), but fgalpha is a no-op
[01:52:29] <vrishab> pango_attr_foreground_alpha_new (32767) seems to be called in either case, but only the "foreground" attr works
[01:56:09] lolek (lolek@ip-91-244-230-76.simant.pl) joined the channel
[01:57:48] <vrishab> ok. seems like "foreground" is mandatory attr, 1. "foreground-without-alpha" + "alpha" works 2. "foreground-with-alpha" works. 3. "alpha" alone doesn
[01:57:52] <vrishab> 't work
[01:58:29] <vrishab> Is there a way to just specify alpha on the current foreground color ?
[02:00:23] lolek (lolek@ip-91-244-230-76.simant.pl) left the channel
[02:07:41] mjog (mjog@uniwide-pat-pool-129-94-8-98.gw.unsw.edu.au) left IRC (Quit: mjog)
[02:08:10] seb128 (seb128@53542B83.cm-6-5a.dynamic.ziggo.nl) joined the channel
[02:12:37] <andlabs> huh
[02:12:41] <andlabs> what version of pango?
[02:13:05] <vrishab> the latest .
[02:15:00] <vrishab> 1.40.3
[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...
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

@ -0,0 +1,90 @@
struct uiWindow {
// constraints
void (*onPositionChanged)(uiWindow *, void *);
void *onPositionChangedData;
BOOL suppressPositionChanged;
// onContentSizeChanged
};
@interface windowDelegateClass : NSObject<NSWindowDelegate> {
// windowShouldClose:
- (void)windowDidMove:(NSNotification *)note;
// windowDidResize:
@end
@implementation windowDelegateClass
// - (BOOL)windowShouldClose:(id)sender
// TODO doesn't happen live
- (void)windowDidMove:(NSNotification *)note
{
uiWindow *w;
w = [self lookupWindow:((NSWindow *) [note object])];
if (!w->suppressPositionChanged)
(*(w->onPositionChanged))(w, w->onPositionChangedData);
}
// - (void)windowDidResize:(NSNotification *)note
// void uiWindowSetTitle(uiWindow *w, const char *title)
void uiWindowPosition(uiWindow *w, int *x, int *y)
{
NSScreen *screen;
NSRect r;
r = [w->window frame];
*x = r.origin.x;
// this is the right screen to use; thanks mikeash in irc.freenode.net/#macdev
// -mainScreen is useless for positioning (it's just the key window's screen)
// and we use -frame, not -visibleFrame, for dealing with absolute positions
screen = (NSScreen *) [[NSScreen screens] objectAtIndex:0];
*y = ([screen frame].size.height - r.origin.y) - r.size.height;
}
void uiWindowSetPosition(uiWindow *w, int x, int y)
{
// -[NSWindow setFrameTopLeftPoint:] is acting weird so...
NSRect r;
NSScreen *screen;
// this fires windowDidMove:
w->suppressPositionChanged = YES;
r = [w->window frame];
r.origin.x = x;
screen = (NSScreen *) [[NSScreen screens] objectAtIndex:0];
r.origin.y = [screen frame].size.height - (y + r.size.height);
[w->window setFrameOrigin:r.origin];
w->suppressPositionChanged = NO;
}
void uiWindowCenter(uiWindow *w)
{
w->suppressPositionChanged = YES;
[w->window center];
w->suppressPositionChanged = NO;
}
void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data)
{
w->onPositionChanged = f;
w->onPositionChangedData = data;
}
// void uiWindowContentSize(uiWindow *w, int *width, int *height)
// static int defaultOnClosing(uiWindow *w, void *data)
static void defaultOnPositionContentSizeChanged(uiWindow *w, void *data)
{
// do nothing
}
uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar)
{
// uiWindowOnClosing(w, defaultOnClosing, NULL);
uiWindowOnPositionChanged(w, defaultOnPositionContentSizeChanged, NULL);
// uiWindowOnContentSizeChanged(w, defaultOnPositionContentSizeChanged, NULL);
}

View File

@ -0,0 +1,65 @@
static uiSpinbox *x, *y;
static void moveX(uiSpinbox *s, void *data)
{
uiWindow *w = uiWindow(data);
int xp, yp;
uiWindowPosition(w, &xp, &yp);
xp = uiSpinboxValue(x);
uiWindowSetPosition(w, xp, yp);
}
static void moveY(uiSpinbox *s, void *data)
{
uiWindow *w = uiWindow(data);
int xp, yp;
uiWindowPosition(w, &xp, &yp);
yp = uiSpinboxValue(y);
uiWindowSetPosition(w, xp, yp);
}
static void updatepos(uiWindow *w)
{
int xp, yp;
uiWindowPosition(w, &xp, &yp);
uiSpinboxSetValue(x, xp);
uiSpinboxSetValue(y, yp);
}
static void center(uiButton *b, void *data)
{
uiWindow *w = uiWindow(data);
uiWindowCenter(w);
updatepos(w);
}
void onMove(uiWindow *w, void *data)
{
printf("move\n");
updatepos(w);
}
uiBox *makePage15(uiWindow *w)
{
hbox = newHorizontalBox();
// TODO if I make this 1 and not add anything else AND not call uiWindowOnPositionChanged(), on OS X the box won't be able to grow vertically
uiBoxAppend(page15, uiControl(hbox), 0);
uiBoxAppend(hbox, uiControl(uiNewLabel("Position")), 0);
x = uiNewSpinbox(INT_MIN, INT_MAX);
uiBoxAppend(hbox, uiControl(x), 1);
y = uiNewSpinbox(INT_MIN, INT_MAX);
uiBoxAppend(hbox, uiControl(y), 1);
button = uiNewButton("Center");
uiBoxAppend(hbox, uiControl(button), 0);
uiSpinboxOnChanged(x, moveX, w);
uiSpinboxOnChanged(y, moveY, w);
uiButtonOnClicked(button, center, w);
uiWindowOnPositionChanged(w, onMove, NULL);
updatepos(w);
}

6
_abort/windowevents/ui.h Normal file
View File

@ -0,0 +1,6 @@
// uiWindowSetTitle
_UI_EXTERN void uiWindowPosition(uiWindow *w, int *x, int *y);
_UI_EXTERN void uiWindowSetPosition(uiWindow *w, int x, int y);
_UI_EXTERN void uiWindowCenter(uiWindow *w);
_UI_EXTERN void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data);
// uiWindowContentSize

View File

@ -0,0 +1,97 @@
struct uiWindow {
// void *onClosingData;
void (*onPositionChanged)(uiWindow *, void *);
void *onPositionChangedData;
gboolean changingPosition;
// void (*onContentSizeChanged)(uiWindow *, void *);
};
// static gboolean onClosing(GtkWidget *win, GdkEvent *e, gpointer data)
static gboolean onConfigure(GtkWidget *win, GdkEvent *e, gpointer data)
{
uiWindow *w = uiWindow(data);
// there doesn't seem to be a way to determine if only moving or only resizing is happening :/
if (w->changingPosition)
w->changingPosition = FALSE;
else
(*(w->onPositionChanged))(w, w->onPositionChangedData);
// always continue handling
return FALSE;
}
// static void onSizeAllocate(GtkWidget *widget, GdkRectangle *allocation, gpointer data)
// static int defaultOnClosing(uiWindow *w, void *data)
static void defaultOnPositionContentSizeChanged(uiWindow *w, void *data)
{
// do nothing
}
// static void uiWindowDestroy(uiControl *c)
// void uiWindowSetTitle(uiWindow *w, const char *title)
// TODO allow specifying either as NULL on all platforms
void uiWindowPosition(uiWindow *w, int *x, int *y)
{
gint rx, ry;
gtk_window_get_position(w->window, &rx, &ry);
*x = rx;
*y = ry;
}
void uiWindowSetPosition(uiWindow *w, int x, int y)
{
w->changingPosition = TRUE;
gtk_window_move(w->window, x, y);
// gtk_window_move() is asynchronous
// we need to wait for a configure-event
// thanks to hergertme in irc.gimp.net/#gtk+
while (w->changingPosition)
if (!uiMainStep(1))
break; // stop early if uiQuit() called
}
void uiWindowCenter(uiWindow *w)
{
gint x, y;
GtkAllocation winalloc;
GdkWindow *gdkwin;
GdkScreen *screen;
GdkRectangle workarea;
gtk_widget_get_allocation(w->widget, &winalloc);
gdkwin = gtk_widget_get_window(w->widget);
screen = gdk_window_get_screen(gdkwin);
gdk_screen_get_monitor_workarea(screen,
gdk_screen_get_monitor_at_window(screen, gdkwin),
&workarea);
x = (workarea.width - winalloc.width) / 2;
y = (workarea.height - winalloc.height) / 2;
// TODO move up slightly? see what Mutter or GNOME Shell or GNOME Terminal do(es)?
uiWindowSetPosition(w, x, y);
}
// TODO this and size changed get set during uiWindowDestroy
void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data)
{
w->onPositionChanged = f;
w->onPositionChangedData = data;
}
// void uiWindowContentSize(uiWindow *w, int *width, int *height)
uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar)
{
// g_signal_connect(w->widget, "delete-event", G_CALLBACK(onClosing), w);
g_signal_connect(w->widget, "configure-event", G_CALLBACK(onConfigure), w);
// g_signal_connect(w->childHolderWidget, "size-allocate", G_CALLBACK(onSizeAllocate), w);
// uiWindowOnClosing(w, defaultOnClosing, NULL);
uiWindowOnPositionChanged(w, defaultOnPositionContentSizeChanged, NULL);
// uiWindowOnContentSizeChanged(w, defaultOnPositionContentSizeChanged, NULL);
}

View File

@ -0,0 +1,86 @@
struct uiWindow {
// BOOL hasMenubar;
void (*onPositionChanged)(uiWindow *, void *);
void *onPositionChangedData;
BOOL changingPosition; // to avoid triggering the above when programmatically doing this
// void (*onContentSizeChanged)(uiWindow *, void *);
};
static LRESULT CALLBACK windowWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
case WM_WINDOWPOSCHANGED:
if ((wp->flags & SWP_NOMOVE) == 0)
if (!w->changingPosition)
(*(w->onPositionChanged))(w, w->onPositionChangedData);
// and continue anyway
// if ((wp->flags & SWP_NOSIZE) != 0)
}
// static int defaultOnClosing(uiWindow *w, void *data)
static void defaultOnPositionContentSizeChanged(uiWindow *w, void *data)
{
// do nothing
}
// static std::map<uiWindow *, bool> windows;
// void uiWindowSetTitle(uiWindow *w, const char *title)
void uiWindowPosition(uiWindow *w, int *x, int *y)
{
RECT r;
uiWindowsEnsureGetWindowRect(w->hwnd, &r);
*x = r.left;
*y = r.top;
}
void uiWindowSetPosition(uiWindow *w, int x, int y)
{
w->changingPosition = TRUE;
if (SetWindowPos(w->hwnd, NULL, x, y, 0, 0, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOZORDER) == 0)
logLastError(L"error moving window");
w->changingPosition = FALSE;
}
// static void windowMonitorRect(HWND hwnd, RECT *r)
// TODO use the work rect instead?
void uiWindowCenter(uiWindow *w)
{
RECT wr, mr;
int x, y;
LONG wwid, mwid;
LONG wht, mht;
uiWindowsEnsureGetWindowRect(w->hwnd, &wr);
windowMonitorRect(w->hwnd, &mr);
wwid = wr.right - wr.left;
mwid = mr.right - mr.left;
x = (mwid - wwid) / 2;
wht = wr.bottom - wr.top;
mht = mr.bottom - mr.top;
y = (mht - wht) / 2;
// y is now evenly divided, however https://msdn.microsoft.com/en-us/library/windows/desktop/dn742502(v=vs.85).aspx says that 45% should go above and 55% should go below
// so just move 5% of the way up
// TODO should this be on the work area?
// TODO is this calculation correct?
y -= y / 20;
uiWindowSetPosition(w, x, y);
}
void uiWindowOnPositionChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data)
{
w->onPositionChanged = f;
w->onPositionChangedData = data;
}
// void uiWindowContentSize(uiWindow *w, int *width, int *height)
uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar)
{
// uiWindowOnClosing(w, defaultOnClosing, NULL);
uiWindowOnPositionChanged(w, defaultOnPositionContentSizeChanged, NULL);
// uiWindowOnContentSizeChanged(w, defaultOnPositionContentSizeChanged, NULL);
}

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

137
_future/unittest/testing.h Normal file
View File

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

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

25
_notes/OS2 Normal file
View File

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

1
_notes/caretWidths Normal file
View File

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

1
_notes/cplusplus Normal file
View File

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

6
_notes/darwinAutoLayout Normal file
View File

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

View File

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

2
_notes/dialogs Normal file
View File

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

1
_notes/highDPI Normal file
View File

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

16
_notes/i18n Normal file
View File

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

206
_notes/misc Normal file
View File

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

9
_notes/rebarstuff Normal file
View File

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

4
_notes/tableNotes Normal file
View File

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

3
_notes/textSelections Normal file
View File

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

3
_notes/winARM64 Normal file
View File

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

18
_notes/windowsHighDPI Normal file
View File

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

7
_notes/windowsPrinting Normal file
View File

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

46
_notes/windowsUWPGlass Normal file
View File

@ -0,0 +1,46 @@
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,9 +0,0 @@
NSFontWeightUltraLight -0.800000
NSFontWeightThin -0.600000
NSFontWeightLight -0.400000
NSFontWeightRegular 0.000000
NSFontWeightMedium 0.230000
NSFontWeightSemibold 0.300000
NSFontWeightBold 0.400000
NSFontWeightHeavy 0.560000
NSFontWeightBlack 0.620000

View File

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

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

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

@ -0,0 +1,223 @@
// 3 january 2017
#import "uipriv_darwin.h"
// Stupidity: Core Text requires an **exact match for the entire traits dictionary**, otherwise it will **drop ALL the traits**.
// This seems to be true for every function in Core Text that advertises that it performs font matching; I can confirm at the time of writing this is the case for
// - CTFontDescriptorCreateMatchingFontDescriptors() (though the conditions here seem odd)
// - CTFontCreateWithFontDescriptor()
// - CTFontCreateCopyWithAttributes()
// 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

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

View File

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

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

View File

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

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

View File

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

View File

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

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

@ -0,0 +1,667 @@
// 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,91 +0,0 @@
NSScrollView
backgroundColor NSCalibratedWhiteColorSpace 1 1
drawsBackground 1
borderType 2
documentCursor <NSCursor: 0x600000056440>
hasHorizontalScroller 0
hasVerticalScroller 1
autohidesScrollers 0
hasHorizontalRuler 0
hasVerticalRuler 0
rulersVisible 0
automaticallyAdjustsContentInsets 1
contentInsets left:0 top:0 right:0 bottom:0
scrollerInsets left:0 top:0 right:0 bottom:0
scrollerKnobStyle 0
scrollerStyle 1
lineScroll 10
horizontalLineScroll 10
verticalLineScroll 10
pageScroll 10
horizontalPageScroll 10
verticalPageScroll 10
scrollsDynamically 1
findBarPosition 1
usesPredominantAxisScrolling 0
horizontalScrollElasticity 0
verticalScrollElasticity 0
allowsMagnification 0
magnification 1
maxMagnification 4
minMagnification 0.25
NSTextView
textContainerInset {0, 0}
textContainerOrigin {0, 0}
backgroundColor NSCalibratedWhiteColorSpace 1 1
drawsBackground 1
allowsDocumentBackgroundColorChange 0
allowedInputSourceLocales (null)
allowsUndo 1
isEditable 1
isSelectable 1
isFieldEditor 0
isRichText 0
importsGraphics 0
defaultParagraphStyle (null)
allowsImageEditing 0
isAutomaticQuoteSubstitutionEnabled 1
isAutomaticLinkDetectionEnabled 0
displaysLinkToolTips 1
usesRuler 0
isRulerVisible 0
usesInspectorBar 0
selectionAffinity 1
selectionGranularity 0
insertionPointColor NSNamedColorSpace System controlTextColor
selectedTextAttributes {
NSBackgroundColor = "NSNamedColorSpace System selectedTextBackgroundColor";
NSColor = "NSNamedColorSpace System selectedTextColor";
}
markedTextAttributes {
NSBackgroundColor = "NSCalibratedRGBColorSpace 1 0.866667 0.333333 1";
}
linkTextAttributes {
NSColor = "NSCalibratedRGBColorSpace 0 0 1 1";
NSCursor = "<NSCursor: 0x6000000599e0>";
NSUnderline = 1;
}
typingAttributes (null)
smartInsertDeleteEnabled 0
isContinuousSpellCheckingEnabled 1
isGrammarCheckingEnabled 0
acceptsGlyphInfo 0
usesFontPanel 0
usesFindPanel 1
enabledTextCheckingTypes 963
isAutomaticDashSubstitutionEnabled 1
isAutomaticDataDetectionEnabled 0
isAutomaticSpellingCorrectionEnabled 1
isAutomaticTextReplacementEnabled 1
usesFindBar 0
isIncrementalSearchingEnabled 0
NSText:
font (null)
textColor (null)
baseWritingDirection -1
maxSize {463, 10000000}
minSize {238, 133}
isVerticallyResizable 1
isHorizontallyResizable 0

View File

@ -1,91 +0,0 @@
NSScrollView
backgroundColor NSCalibratedWhiteColorSpace 1 1
drawsBackground 1
borderType 2
documentCursor <NSCursor: 0x600000056380>
hasHorizontalScroller 0
hasVerticalScroller 1
autohidesScrollers 0
hasHorizontalRuler 0
hasVerticalRuler 0
rulersVisible 0
automaticallyAdjustsContentInsets 1
contentInsets left:0 top:0 right:0 bottom:0
scrollerInsets left:0 top:0 right:0 bottom:0
scrollerKnobStyle 0
scrollerStyle 1
lineScroll 10
horizontalLineScroll 10
verticalLineScroll 10
pageScroll 10
horizontalPageScroll 10
verticalPageScroll 10
scrollsDynamically 1
findBarPosition 1
usesPredominantAxisScrolling 0
horizontalScrollElasticity 0
verticalScrollElasticity 0
allowsMagnification 0
magnification 1
maxMagnification 4
minMagnification 0.25
NSTextView
textContainerInset {0, 0}
textContainerOrigin {0, 0}
backgroundColor NSCalibratedWhiteColorSpace 1 1
drawsBackground 1
allowsDocumentBackgroundColorChange 0
allowedInputSourceLocales (null)
allowsUndo 1
isEditable 1
isSelectable 1
isFieldEditor 0
isRichText 1
importsGraphics 0
defaultParagraphStyle (null)
allowsImageEditing 0
isAutomaticQuoteSubstitutionEnabled 1
isAutomaticLinkDetectionEnabled 0
displaysLinkToolTips 1
usesRuler 1
isRulerVisible 0
usesInspectorBar 0
selectionAffinity 0
selectionGranularity 0
insertionPointColor NSNamedColorSpace System controlTextColor
selectedTextAttributes {
NSBackgroundColor = "NSNamedColorSpace System selectedTextBackgroundColor";
NSColor = "NSNamedColorSpace System selectedTextColor";
}
markedTextAttributes {
NSBackgroundColor = "NSCalibratedRGBColorSpace 1 0.866667 0.333333 1";
}
linkTextAttributes {
NSColor = "NSCalibratedRGBColorSpace 0 0 1 1";
NSCursor = "<NSCursor: 0x600000059920>";
NSUnderline = 1;
}
typingAttributes (null)
smartInsertDeleteEnabled 1
isContinuousSpellCheckingEnabled 1
isGrammarCheckingEnabled 0
acceptsGlyphInfo 1
usesFontPanel 1
usesFindPanel 1
enabledTextCheckingTypes 963
isAutomaticDashSubstitutionEnabled 1
isAutomaticDataDetectionEnabled 0
isAutomaticSpellingCorrectionEnabled 1
isAutomaticTextReplacementEnabled 1
usesFindBar 0
isIncrementalSearchingEnabled 0
NSText:
font (null)
textColor (null)
baseWritingDirection -1
maxSize {463, 10000000}
minSize {238, 133}
isVerticallyResizable 1
isHorizontallyResizable 0

View File

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

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

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

@ -0,0 +1,254 @@
// 20 january 2017
#include "drawtext.h"
// TODO FOR THIS FILE
// get rid of it once we rewrite this example (which requires having more fine-grained scrolling control)
// TODO double-check ligatures on all platforms to make sure we can place the cursor at the right place
// TODO the hiding and showing does not work properly on GTK+
// TODO using the arrow keys allows us to walk back to the end of the line on some platforms (TODO which?); IIRC arrow keys shouldn't do that
// TODO make sure to check the cursor positions of RTL on all platforms
static const char *text = "";
static char fontFamily[] = "Helvetica";
static uiDrawFontDescriptor defaultFont = {
.Family = fontFamily,
.Size = 14,
.Weight = uiDrawTextWeightNormal,
.Italic = uiDrawTextItalicNormal,
.Stretch = uiDrawTextStretchNormal,
};
static uiAttributedString *attrstr;
static uiDrawTextLayoutParams params;
#define margins 10
static uiBox *panel;
static uiBox *vbox;
static uiLabel *caretLabel;
static uiCheckbox *showLineBounds;
static uiFontButton *fontButton;
static uiCombobox *textAlign;
static int caretLine = -1;
static size_t caretPos;
// TODO should be const?
static uiDrawBrush fillBrushes[4] = {
{
.Type = uiDrawBrushTypeSolid,
.R = 1.0,
.G = 0.0,
.B = 0.0,
.A = 0.5,
},
{
.Type = uiDrawBrushTypeSolid,
.R = 0.0,
.G = 1.0,
.B = 0.0,
.A = 0.5,
},
{
.Type = uiDrawBrushTypeSolid,
.R = 0.0,
.G = 0.0,
.B = 1.0,
.A = 0.5,
},
{
.Type = uiDrawBrushTypeSolid,
.R = 0.0,
.G = 1.0,
.B = 1.0,
.A = 0.5,
},
};
static void draw(uiAreaDrawParams *p)
{
uiDrawPath *path;
uiDrawTextLayout *layout;
uiDrawTextLayoutLineMetrics m;
// only clip the text, not the guides
uiDrawSave(p->Context);
path = uiDrawNewPath(uiDrawFillModeWinding);
uiDrawPathAddRectangle(path, margins, margins,
p->AreaWidth - 2 * margins,
p->AreaHeight - 2 * margins);
uiDrawPathEnd(path);
uiDrawClip(p->Context, path);
uiDrawFreePath(path);
params.Width = p->AreaWidth - 2 * margins;
layout = uiDrawNewTextLayout(&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

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

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

@ -0,0 +1,58 @@
# 3 june 2016
if(WIN32)
set(_EXAMPLE_RESOURCES_RC resources.rc)
endif()
macro(_add_example _name)
_add_exec(${_name} ${ARGN})
# because Microsoft's toolchain is dumb
if(MSVC)
set_property(TARGET ${_name} APPEND_STRING PROPERTY
LINK_FLAGS " /ENTRY:mainCRTStartup")
endif()
endmacro()
_add_example(controlgallery
controlgallery/main.c
${_EXAMPLE_RESOURCES_RC}
)
_add_example(histogram
histogram/main.c
${_EXAMPLE_RESOURCES_RC}
)
_add_example(cpp-multithread
cpp-multithread/main.cpp
${_EXAMPLE_RESOURCES_RC}
)
if(NOT WIN32)
target_link_libraries(cpp-multithread pthread)
endif()
_add_example(drawtext
drawtext/attributes.c
drawtext/basic.c
drawtext/emptystr_hittest.c
drawtext/hittest.c
drawtext/main.c
${_EXAMPLE_RESOURCES_RC}
)
target_include_directories(drawtext
PRIVATE drawtext)
_add_example(opentype
opentype/main.c
${_EXAMPLE_RESOURCES_RC}
)
target_include_directories(opentype
PRIVATE opentype)
add_custom_target(examples
DEPENDS
controlgallery
histogram
cpp-multithread
drawtext
opentype)

View File

@ -0,0 +1,201 @@
// 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,93 +0,0 @@
NSScrollView
backgroundColor NSNamedColorSpace System controlBackgroundColor
drawsBackground 1
borderType 0
documentCursor <NSCursor: 0x600000056560>
hasHorizontalScroller 0
hasVerticalScroller 0
autohidesScrollers 0
hasHorizontalRuler 0
hasVerticalRuler 0
rulersVisible 0
automaticallyAdjustsContentInsets 1
contentInsets left:0 top:0 right:0 bottom:0
scrollerInsets left:0 top:0 right:0 bottom:0
scrollerKnobStyle 0
scrollerStyle 1
lineScroll 10
horizontalLineScroll 10
verticalLineScroll 10
pageScroll 10
horizontalPageScroll 10
verticalPageScroll 10
scrollsDynamically 1
findBarPosition 1
usesPredominantAxisScrolling 1
horizontalScrollElasticity 0
verticalScrollElasticity 0
allowsMagnification 0
magnification 1
maxMagnification 4
minMagnification 0.25
NSTextView
textContainerInset {0, 0}
textContainerOrigin {0, 0}
backgroundColor NSCalibratedWhiteColorSpace 1 1
drawsBackground 1
allowsDocumentBackgroundColorChange 0
allowedInputSourceLocales (null)
allowsUndo 0
isEditable 1
isSelectable 1
isFieldEditor 0
isRichText 1
importsGraphics 0
defaultParagraphStyle (null)
allowsImageEditing 0
isAutomaticQuoteSubstitutionEnabled 1
isAutomaticLinkDetectionEnabled 0
displaysLinkToolTips 1
usesRuler 1
isRulerVisible 0
usesInspectorBar 0
selectionAffinity 0
selectionGranularity 0
insertionPointColor NSNamedColorSpace System controlTextColor
selectedTextAttributes {
NSBackgroundColor = "NSNamedColorSpace System selectedTextBackgroundColor";
NSColor = "NSNamedColorSpace System selectedTextColor";
}
markedTextAttributes {
NSBackgroundColor = "NSCalibratedRGBColorSpace 1 0.866667 0.333333 1";
}
linkTextAttributes {
NSColor = "NSCalibratedRGBColorSpace 0 0 1 1";
NSCursor = "<NSCursor: 0x6000000599e0>";
NSUnderline = 1;
}
typingAttributes {
NSFont = "\"Helvetica 12.00 pt. P [] (0x60000005f2f0) fobj=0x6000001e4f00, spc=3.33\"";
}
smartInsertDeleteEnabled 1
isContinuousSpellCheckingEnabled 0
isGrammarCheckingEnabled 0
acceptsGlyphInfo 1
usesFontPanel 1
usesFindPanel 0
enabledTextCheckingTypes 961
isAutomaticDashSubstitutionEnabled 1
isAutomaticDataDetectionEnabled 0
isAutomaticSpellingCorrectionEnabled 1
isAutomaticTextReplacementEnabled 1
usesFindBar 0
isIncrementalSearchingEnabled 0
NSText:
font "Helvetica 12.00 pt. P [] (0x60000005f2f0) fobj=0x6000001e4f00, spc=3.33"
textColor (null)
baseWritingDirection -1
maxSize {0, 10000000}
minSize {0, 0}
isVerticallyResizable 1
isHorizontallyResizable 0

25
_wip/sv/normal Normal file
View File

@ -0,0 +1,25 @@
2016-05-26 22:38:12.877 svtest[81790:544681] backgroundColor NSNamedColorSpace System controlColor
2016-05-26 22:38:12.877 svtest[81790:544681] drawsBackground 1
2016-05-26 22:38:12.877 svtest[81790:544681] borderType 2
2016-05-26 22:38:12.877 svtest[81790:544681] documentCursor (null)
2016-05-26 22:38:12.877 svtest[81790:544681] hasHorizontalScroller 1
2016-05-26 22:38:12.877 svtest[81790:544681] hasVerticalScroller 1
2016-05-26 22:38:12.877 svtest[81790:544681] autohidesScrollers 0
2016-05-26 22:38:12.877 svtest[81790:544681] hasHorizontalRuler 0
2016-05-26 22:38:12.878 svtest[81790:544681] hasVerticalRuler 0
2016-05-26 22:38:12.878 svtest[81790:544681] rulersVisible 0
2016-05-26 22:38:12.878 svtest[81790:544681] 10.10 autoAdjContentInsets 1
2016-05-26 22:38:12.878 svtest[81790:544681] scrollerKnobStyle 0
2016-05-26 22:38:12.878 svtest[81790:544681] scrollerStyle 1
2016-05-26 22:38:12.878 svtest[81790:544681] horizontalLineScroll 10
2016-05-26 22:38:12.878 svtest[81790:544681] verticalLineScroll 10
2016-05-26 22:38:12.886 svtest[81790:544681] horizontalPageScroll 10
2016-05-26 22:38:12.886 svtest[81790:544681] verticalPageScroll 10
2016-05-26 22:38:12.886 svtest[81790:544681] scrollsDynamically 1
2016-05-26 22:38:12.886 svtest[81790:544681] findBarPosition 1
2016-05-26 22:38:12.886 svtest[81790:544681] usesPredomAxisScroll 0
2016-05-26 22:38:12.886 svtest[81790:544681] horizontalElasticity 0
2016-05-26 22:38:12.886 svtest[81790:544681] verticalElasticity 0
2016-05-26 22:38:12.887 svtest[81790:544681] 10.8 allowsMagnification 0
2016-05-26 22:38:12.887 svtest[81790:544681] 10.8 maxMagnification 4
2016-05-26 22:38:12.887 svtest[81790:544681] 10.8 minMagnification 0.25

25
_wip/sv/normal.nots Normal file
View File

@ -0,0 +1,25 @@
backgroundColor NSNamedColorSpace System controlColor
drawsBackground 1
borderType 2
documentCursor (null)
hasHorizontalScroller 1
hasVerticalScroller 1
autohidesScrollers 0
hasHorizontalRuler 0
hasVerticalRuler 0
rulersVisible 0
10.10 autoAdjContentInsets 1
scrollerKnobStyle 0
scrollerStyle 1
horizontalLineScroll 10
verticalLineScroll 10
horizontalPageScroll 10
verticalPageScroll 10
scrollsDynamically 1
findBarPosition 1
usesPredomAxisScroll 0
horizontalElasticity 0
verticalElasticity 0
10.8 allowsMagnification 0
10.8 maxMagnification 4
10.8 minMagnification 0.25

25
_wip/sv/outlineview Normal file
View File

@ -0,0 +1,25 @@
2016-05-26 22:42:16.208 svtest[82103:547159] backgroundColor NSNamedColorSpace System controlBackgroundColor
2016-05-26 22:42:16.208 svtest[82103:547159] drawsBackground 1
2016-05-26 22:42:16.208 svtest[82103:547159] borderType 2
2016-05-26 22:42:16.208 svtest[82103:547159] documentCursor (null)
2016-05-26 22:42:16.209 svtest[82103:547159] hasHorizontalScroller 1
2016-05-26 22:42:16.209 svtest[82103:547159] hasVerticalScroller 1
2016-05-26 22:42:16.209 svtest[82103:547159] autohidesScrollers 1
2016-05-26 22:42:16.209 svtest[82103:547159] hasHorizontalRuler 0
2016-05-26 22:42:16.209 svtest[82103:547159] hasVerticalRuler 0
2016-05-26 22:42:16.209 svtest[82103:547159] rulersVisible 0
2016-05-26 22:42:16.209 svtest[82103:547159] 10.10 autoAdjContentInsets 1
2016-05-26 22:42:16.209 svtest[82103:547159] scrollerKnobStyle 0
2016-05-26 22:42:16.209 svtest[82103:547159] scrollerStyle 1
2016-05-26 22:42:16.209 svtest[82103:547159] horizontalLineScroll 19
2016-05-26 22:42:16.209 svtest[82103:547159] verticalLineScroll 19
2016-05-26 22:42:16.217 svtest[82103:547159] horizontalPageScroll 10
2016-05-26 22:42:16.218 svtest[82103:547159] verticalPageScroll 10
2016-05-26 22:42:16.218 svtest[82103:547159] scrollsDynamically 1
2016-05-26 22:42:16.218 svtest[82103:547159] findBarPosition 1
2016-05-26 22:42:16.218 svtest[82103:547159] usesPredomAxisScroll 0
2016-05-26 22:42:16.218 svtest[82103:547159] horizontalElasticity 0
2016-05-26 22:42:16.218 svtest[82103:547159] verticalElasticity 0
2016-05-26 22:42:16.218 svtest[82103:547159] 10.8 allowsMagnification 0
2016-05-26 22:42:16.218 svtest[82103:547159] 10.8 maxMagnification 4
2016-05-26 22:42:16.218 svtest[82103:547159] 10.8 minMagnification 0.25

25
_wip/sv/outlineview.nots Normal file
View File

@ -0,0 +1,25 @@
backgroundColor NSNamedColorSpace System controlBackgroundColor
drawsBackground 1
borderType 2
documentCursor (null)
hasHorizontalScroller 1
hasVerticalScroller 1
autohidesScrollers 1
hasHorizontalRuler 0
hasVerticalRuler 0
rulersVisible 0
10.10 autoAdjContentInsets 1
scrollerKnobStyle 0
scrollerStyle 1
horizontalLineScroll 19
verticalLineScroll 19
horizontalPageScroll 10
verticalPageScroll 10
scrollsDynamically 1
findBarPosition 1
usesPredomAxisScroll 0
horizontalElasticity 0
verticalElasticity 0
10.8 allowsMagnification 0
10.8 maxMagnification 4
10.8 minMagnification 0.25

25
_wip/sv/sourcelist Normal file
View File

@ -0,0 +1,25 @@
2016-05-26 22:43:58.600 svtest[82237:548359] backgroundColor (null)
2016-05-26 22:43:58.600 svtest[82237:548359] drawsBackground 0
2016-05-26 22:43:58.600 svtest[82237:548359] borderType 2
2016-05-26 22:43:58.600 svtest[82237:548359] documentCursor (null)
2016-05-26 22:43:58.600 svtest[82237:548359] hasHorizontalScroller 1
2016-05-26 22:43:58.600 svtest[82237:548359] hasVerticalScroller 1
2016-05-26 22:43:58.600 svtest[82237:548359] autohidesScrollers 1
2016-05-26 22:43:58.600 svtest[82237:548359] hasHorizontalRuler 0
2016-05-26 22:43:58.600 svtest[82237:548359] hasVerticalRuler 0
2016-05-26 22:43:58.600 svtest[82237:548359] rulersVisible 0
2016-05-26 22:43:58.601 svtest[82237:548359] 10.10 autoAdjContentInsets 1
2016-05-26 22:43:58.601 svtest[82237:548359] scrollerKnobStyle 0
2016-05-26 22:43:58.601 svtest[82237:548359] scrollerStyle 1
2016-05-26 22:43:58.601 svtest[82237:548359] horizontalLineScroll 19
2016-05-26 22:43:58.601 svtest[82237:548359] verticalLineScroll 19
2016-05-26 22:43:58.645 svtest[82237:548359] horizontalPageScroll 10
2016-05-26 22:43:58.645 svtest[82237:548359] verticalPageScroll 10
2016-05-26 22:43:58.645 svtest[82237:548359] scrollsDynamically 1
2016-05-26 22:43:58.645 svtest[82237:548359] findBarPosition 1
2016-05-26 22:43:58.645 svtest[82237:548359] usesPredomAxisScroll 0
2016-05-26 22:43:58.645 svtest[82237:548359] horizontalElasticity 0
2016-05-26 22:43:58.646 svtest[82237:548359] verticalElasticity 0
2016-05-26 22:43:58.646 svtest[82237:548359] 10.8 allowsMagnification 0
2016-05-26 22:43:58.646 svtest[82237:548359] 10.8 maxMagnification 4
2016-05-26 22:43:58.646 svtest[82237:548359] 10.8 minMagnification 0.25

25
_wip/sv/sourcelist.nots Normal file
View File

@ -0,0 +1,25 @@
backgroundColor (null)
drawsBackground 0
borderType 2
documentCursor (null)
hasHorizontalScroller 1
hasVerticalScroller 1
autohidesScrollers 1
hasHorizontalRuler 0
hasVerticalRuler 0
rulersVisible 0
10.10 autoAdjContentInsets 1
scrollerKnobStyle 0
scrollerStyle 1
horizontalLineScroll 19
verticalLineScroll 19
horizontalPageScroll 10
verticalPageScroll 10
scrollsDynamically 1
findBarPosition 1
usesPredomAxisScroll 0
horizontalElasticity 0
verticalElasticity 0
10.8 allowsMagnification 0
10.8 maxMagnification 4
10.8 minMagnification 0.25

25
_wip/sv/tableview Normal file
View File

@ -0,0 +1,25 @@
2016-05-26 22:41:26.514 svtest[82032:546554] backgroundColor NSNamedColorSpace System controlBackgroundColor
2016-05-26 22:41:26.514 svtest[82032:546554] drawsBackground 1
2016-05-26 22:41:26.514 svtest[82032:546554] borderType 2
2016-05-26 22:41:26.514 svtest[82032:546554] documentCursor (null)
2016-05-26 22:41:26.515 svtest[82032:546554] hasHorizontalScroller 1
2016-05-26 22:41:26.515 svtest[82032:546554] hasVerticalScroller 1
2016-05-26 22:41:26.515 svtest[82032:546554] autohidesScrollers 1
2016-05-26 22:41:26.515 svtest[82032:546554] hasHorizontalRuler 0
2016-05-26 22:41:26.516 svtest[82032:546554] hasVerticalRuler 0
2016-05-26 22:41:26.516 svtest[82032:546554] rulersVisible 0
2016-05-26 22:41:26.516 svtest[82032:546554] 10.10 autoAdjContentInsets 1
2016-05-26 22:41:26.516 svtest[82032:546554] scrollerKnobStyle 0
2016-05-26 22:41:26.516 svtest[82032:546554] scrollerStyle 1
2016-05-26 22:41:26.516 svtest[82032:546554] horizontalLineScroll 19
2016-05-26 22:41:26.516 svtest[82032:546554] verticalLineScroll 19
2016-05-26 22:41:26.528 svtest[82032:546554] horizontalPageScroll 10
2016-05-26 22:41:26.528 svtest[82032:546554] verticalPageScroll 10
2016-05-26 22:41:26.528 svtest[82032:546554] scrollsDynamically 1
2016-05-26 22:41:26.528 svtest[82032:546554] findBarPosition 1
2016-05-26 22:41:26.528 svtest[82032:546554] usesPredomAxisScroll 0
2016-05-26 22:41:26.528 svtest[82032:546554] horizontalElasticity 0
2016-05-26 22:41:26.528 svtest[82032:546554] verticalElasticity 0
2016-05-26 22:41:26.528 svtest[82032:546554] 10.8 allowsMagnification 0
2016-05-26 22:41:26.528 svtest[82032:546554] 10.8 maxMagnification 4
2016-05-26 22:41:26.528 svtest[82032:546554] 10.8 minMagnification 0.25

25
_wip/sv/tableview.nots Normal file
View File

@ -0,0 +1,25 @@
backgroundColor NSNamedColorSpace System controlBackgroundColor
drawsBackground 1
borderType 2
documentCursor (null)
hasHorizontalScroller 1
hasVerticalScroller 1
autohidesScrollers 1
hasHorizontalRuler 0
hasVerticalRuler 0
rulersVisible 0
10.10 autoAdjContentInsets 1
scrollerKnobStyle 0
scrollerStyle 1
horizontalLineScroll 19
verticalLineScroll 19
horizontalPageScroll 10
verticalPageScroll 10
scrollsDynamically 1
findBarPosition 1
usesPredomAxisScroll 0
horizontalElasticity 0
verticalElasticity 0
10.8 allowsMagnification 0
10.8 maxMagnification 4
10.8 minMagnification 0.25

25
_wip/sv/textview Normal file
View File

@ -0,0 +1,25 @@
2016-05-26 22:40:02.050 svtest[81927:545793] backgroundColor NSCalibratedWhiteColorSpace 1 1
2016-05-26 22:40:02.050 svtest[81927:545793] drawsBackground 1
2016-05-26 22:40:02.051 svtest[81927:545793] borderType 2
2016-05-26 22:40:02.052 svtest[81927:545793] documentCursor <NSCursor: 0x608000041f20>
2016-05-26 22:40:02.052 svtest[81927:545793] hasHorizontalScroller 0
2016-05-26 22:40:02.052 svtest[81927:545793] hasVerticalScroller 1
2016-05-26 22:40:02.052 svtest[81927:545793] autohidesScrollers 0
2016-05-26 22:40:02.052 svtest[81927:545793] hasHorizontalRuler 0
2016-05-26 22:40:02.052 svtest[81927:545793] hasVerticalRuler 0
2016-05-26 22:40:02.052 svtest[81927:545793] rulersVisible 0
2016-05-26 22:40:02.052 svtest[81927:545793] 10.10 autoAdjContentInsets 1
2016-05-26 22:40:02.052 svtest[81927:545793] scrollerKnobStyle 0
2016-05-26 22:40:02.052 svtest[81927:545793] scrollerStyle 1
2016-05-26 22:40:02.054 svtest[81927:545793] horizontalLineScroll 10
2016-05-26 22:40:02.055 svtest[81927:545793] verticalLineScroll 10
2016-05-26 22:40:02.062 svtest[81927:545793] horizontalPageScroll 10
2016-05-26 22:40:02.062 svtest[81927:545793] verticalPageScroll 10
2016-05-26 22:40:02.062 svtest[81927:545793] scrollsDynamically 1
2016-05-26 22:40:02.062 svtest[81927:545793] findBarPosition 1
2016-05-26 22:40:02.062 svtest[81927:545793] usesPredomAxisScroll 0
2016-05-26 22:40:02.062 svtest[81927:545793] horizontalElasticity 0
2016-05-26 22:40:02.062 svtest[81927:545793] verticalElasticity 0
2016-05-26 22:40:02.062 svtest[81927:545793] 10.8 allowsMagnification 0
2016-05-26 22:40:02.062 svtest[81927:545793] 10.8 maxMagnification 4
2016-05-26 22:40:02.063 svtest[81927:545793] 10.8 minMagnification 0.25

25
_wip/sv/textview.nots Normal file
View File

@ -0,0 +1,25 @@
backgroundColor NSCalibratedWhiteColorSpace 1 1
drawsBackground 1
borderType 2
documentCursor <NSCursor: 0x608000041f20>
hasHorizontalScroller 0
hasVerticalScroller 1
autohidesScrollers 0
hasHorizontalRuler 0
hasVerticalRuler 0
rulersVisible 0
10.10 autoAdjContentInsets 1
scrollerKnobStyle 0
scrollerStyle 1
horizontalLineScroll 10
verticalLineScroll 10
horizontalPageScroll 10
verticalPageScroll 10
scrollsDynamically 1
findBarPosition 1
usesPredomAxisScroll 0
horizontalElasticity 0
verticalElasticity 0
10.8 allowsMagnification 0
10.8 maxMagnification 4
10.8 minMagnification 0.25

View File

@ -1,92 +0,0 @@
NSScrollView
backgroundColor NSNamedColorSpace System controlBackgroundColor
drawsBackground 1
borderType 2
documentCursor <NSCursor: 0x7f886b44f6a0>
hasHorizontalScroller 0
hasVerticalScroller 1
autohidesScrollers 1
hasHorizontalRuler 0
hasVerticalRuler 0
rulersVisible 0
automaticallyAdjustsContentInsets 1
contentInsets left:0 top:0 right:0 bottom:0
scrollerInsets left:0 top:0 right:0 bottom:0
scrollerKnobStyle 0
scrollerStyle 1
lineScroll 10
horizontalLineScroll 10
verticalLineScroll 10
pageScroll 10
horizontalPageScroll 10
verticalPageScroll 10
scrollsDynamically 1
findBarPosition 1
usesPredominantAxisScrolling 1
horizontalScrollElasticity 0
verticalScrollElasticity 0
allowsMagnification 0
magnification 1
maxMagnification 4
minMagnification 0.25
NSTextView
textContainerInset {0, 0}
textContainerOrigin {0, 0}
backgroundColor NSNamedColorSpace System textBackgroundColor
drawsBackground 1
allowsDocumentBackgroundColorChange 0
allowedInputSourceLocales (null)
allowsUndo 1
isEditable 1
isSelectable 1
isFieldEditor 0
isRichText 0
importsGraphics 0
defaultParagraphStyle (null)
allowsImageEditing 0
isAutomaticQuoteSubstitutionEnabled 0
isAutomaticLinkDetectionEnabled 0
displaysLinkToolTips 1
usesRuler 0
isRulerVisible 0
usesInspectorBar 0
selectionAffinity 0
selectionGranularity 0
insertionPointColor NSNamedColorSpace System controlTextColor
selectedTextAttributes {
NSBackgroundColor = "NSNamedColorSpace System selectedTextBackgroundColor";
NSColor = "NSNamedColorSpace System selectedTextColor";
}
markedTextAttributes {
NSBackgroundColor = "NSCalibratedRGBColorSpace 1 0.866667 0.333333 1";
}
linkTextAttributes {
NSColor = "NSCalibratedRGBColorSpace 0 0 1 1";
NSCursor = "<NSCursor: 0x7f886b4c59d0>";
NSUnderline = 1;
}
typingAttributes {
NSFont = "\".HelveticaNeueDeskInterface-Regular 13.00 pt. P [] (0x7f886b4bc6a0) fobj=0x7f886b474d90, spc=3.94\"";
}
smartInsertDeleteEnabled 0
isContinuousSpellCheckingEnabled 0
isGrammarCheckingEnabled 0
acceptsGlyphInfo 0
usesFontPanel 0
usesFindPanel 0
enabledTextCheckingTypes 0
isAutomaticDashSubstitutionEnabled 0
isAutomaticDataDetectionEnabled 0
isAutomaticSpellingCorrectionEnabled 0
isAutomaticTextReplacementEnabled 0
usesFindBar 0
isIncrementalSearchingEnabled 0
NSText:
font ".HelveticaNeueDeskInterface-Regular 13.00 pt. P [] (0x7f886b4bc6a0) fobj=0x7f886b474d90, spc=3.94"
textColor (null)
baseWritingDirection -1
maxSize {1, 10000000}
minSize {1, 1}
isVerticallyResizable 1
isHorizontallyResizable 0

View File

@ -1,79 +0,0 @@
# 16 october 2015
# TODO the loader looks for the soname, not the base name, which is frustrating
# Global flags.
CFLAGS += \
-fPIC \
-Wall -Wextra -pedantic \
-Wno-unused-parameter \
-Wno-switch \
--std=c99
# C++11 is needed due to stupid rules involving commas at the end of enum lists that C++03 stupidly didn't follow
# This means sorry, no GCC 2 for Haiku builds :(
CXXFLAGS += \
-fPIC \
-Wall -Wextra -pedantic \
-Wno-unused-parameter \
-Wno-switch \
--std=c++11
LDFLAGS += \
-fPIC
ifneq ($(NODEBUG),1)
CFLAGS += -g
CXXFLAGS += -g
LDFLAGS += -g
endif
# Build rules.
OFILES = \
$(subst /,_,$(CFILES)) \
$(subst /,_,$(CXXFILES)) \
$(subst /,_,$(MFILES)) \
$(subst /,_,$(RCFILES))
OFILES := $(OFILES:%=$(OBJDIR)/%.o)
OUT = $(OUTDIR)/$(NAME)$(SUFFIX)
OUTNOSONAME = $(OUTDIR)/$(NAME)$(LIBSUFFIX)
# TODO allow using LD
# LD is defined by default so we need a way to override the default define without blocking a user define
ifeq ($(CXXFILES),)
reallinker = $(CC)
else
reallinker = $(CXX)
endif
$(OUT): $(OFILES) | $(OUTDIR)
@$(reallinker) -o $(OUT) $(OFILES) $(LDFLAGS)
ifeq ($(USESSONAME),1)
@ln -sf $(NAME)$(SUFFIX) $(OUTNOSONAME)
endif
@echo ====== Linked $(OUT)
.SECONDEXPANSION:
$(OBJDIR)/%.c.o: $$(subst _,/,%).c $(HFILES) | $(OBJDIR)
@$(CC) -o $@ -c $< $(CFLAGS)
@echo ====== Compiled $<
$(OBJDIR)/%.cpp.o: $$(subst _,/,%).cpp $(HFILES) | $(OBJDIR)
@$(CXX) -o $@ -c $< $(CXXFLAGS)
@echo ====== Compiled $<
$(OBJDIR)/%.m.o: $$(subst _,/,%).m $(HFILES) | $(OBJDIR)
@$(CC) -o $@ -c $< $(CFLAGS)
@echo ====== Compiled $<
$(OBJDIR)/%.rc.o: $$(subst _,/,%).rc $(HFILES) | $(OBJDIR)
@$(RC) $(RCFLAGS) $< $@
@echo ====== Compiled $<
$(OBJDIR) $(OUTDIR):
@mkdir -p $@

View File

@ -1,99 +0,0 @@
# 16 october 2015
# IMPORTANT
# Do NOT use / for command-line options here!
# This breaks on GNU makes that come with some versions of
# MinGW because they mangle things that start with /, thinking that
# those arguments are Unix paths that need to be converted to
# Windows paths. This cannot be turned off. -_-'
# MSDN says cl, rc, and link all accept - instead of /, so we're good.
# See also:
# - https://github.com/andlabs/libui/issues/16
# - http://www.mingw.org/wiki/Posix_path_conversion
# - http://www.mingw.org/wiki/FAQ
# - http://stackoverflow.com/questions/7250130/how-to-stop-mingw-and-msys-from-mangling-path-names-given-at-the-command-line
# - http://stackoverflow.com/questions/28533664/how-to-prevent-msys-to-convert-the-file-path-for-an-external-program
# TODO subsystem version
# TODO silence compiler non-diagnostics (/nologo is not enough)
# Global flags.
# TODO /Wall does too much
# TODO -Wno-switch equivalent
# TODO /sdl turns C4996 into an ERROR
# TODO loads of warnings in the system header files
# TODO /analyze requires us to write annotations everywhere
# TODO undecided flags from qo?
CFLAGS += \
-W4 \
-wd4100 \
-TC \
-bigobj -nologo \
-RTC1 -RTCc -RTCs -RTCu
# TODO prune these
# -EHsc is to shut the compiler up in some cases
CXXFLAGS += \
-W4 \
-wd4100 \
-TP \
-bigobj -nologo \
-RTC1 -RTCc -RTCs -RTCu \
-EHsc
# TODO warnings on undefined symbols
LDFLAGS += \
-largeaddressaware -nologo -incremental:no
ifneq ($(NODEBUG),1)
CFLAGS += -Zi
CXXFLAGS += -Zi
LDFLAGS += -debug
endif
# Build rules.
OFILES = \
$(subst /,_,$(CFILES)) \
$(subst /,_,$(CXXFILES)) \
$(subst /,_,$(MFILES)) \
$(subst /,_,$(RCFILES))
OFILES := $(OFILES:%=$(OBJDIR)/%.o)
OUT = $(OUTDIR)/$(NAME)$(SUFFIX)
# TODO use $(CC), $(CXX), $(LD), and s$(RC)
$(OUT): $(OFILES) | $(OUTDIR)
@link -out:$(OUT) $(OFILES) $(LDFLAGS)
@echo ====== Linked $(OUT)
.SECONDEXPANSION:
# TODO can we put /Fd$@.pdb in a variable?
$(OBJDIR)/%.c.o: $$(subst _,/,%).c $(HFILES) | $(OBJDIR)
ifeq ($(NODEBUG),1)
@cl -Fo:$@ -c $< $(CFLAGS)
else
@cl -Fo:$@ -c $< $(CFLAGS) -Fd$@.pdb
endif
@echo ====== Compiled $<
$(OBJDIR)/%.cpp.o: $$(subst _,/,%).cpp $(HFILES) | $(OBJDIR)
ifeq ($(NODEBUG),1)
@cl -Fo:$@ -c $< $(CXXFLAGS)
else
@cl -Fo:$@ -c $< $(CXXFLAGS) -Fd$@.pdb
endif
@echo ====== Compiled $<
# note: don't run cvtres directly; the linker does that for us
$(OBJDIR)/%.rc.o: $$(subst _,/,%).rc $(HFILES) | $(OBJDIR)
@rc -nologo -v -fo $@ $<
@echo ====== Compiled $<
$(OBJDIR) $(OUTDIR):
@mkdir $@

View File

@ -1,48 +0,0 @@
# 16 october 2015
ifndef inlibuibuild
$(error Do not run these makefiles directly.)
endif
ifndef EXAMPLE
$(error You must specify an example to build by adding EXAMPLE=name to the command line.)
endif
include $(OS)/GNUosspecific.mk
ifneq ($(findstring cpp-,$(EXAMPLE)),)
CXXFILES += \
examples/$(EXAMPLE)/main.cpp
else
CFILES += \
examples/$(EXAMPLE)/main.c
endif
HFILES += \
ui.h
ifeq ($(OS),windows)
RCFILES += \
examples/resources.rc
endif
NAME = $(EXAMPLE)
SUFFIX = $(EXESUFFIX)
# TODO merge with the one in build/GNUmakefile.test
ifeq ($(TOOLCHAIN),gcc)
LDFLAGS += -L$(OUTDIR) -lui
# see build/GNUmakefile.test
ifeq ($(OS),darwin)
LDFLAGS += -Wl,-rpath,@executable_path
else
LDFLAGS += -Wl,-rpath,'$$ORIGIN'
endif
else
# TODO is there an equivalent to -L?
LDFLAGS += $(OUTDIR)/libui.lib
endif
# executables are not shared libraries
USESSONAME = 0
include build/GNUbase$(TOOLCHAIN).mk

View File

@ -1,53 +0,0 @@
# 16 october 2015
ifndef inlibuibuild
$(error Do not run these makefiles directly.)
endif
# for GCC
SOVERSION0 = 0
SOVERSIONA = A
include $(OS)/GNUosspecific.mk
include common/GNUfiles.mk
include $(OS)/GNUfiles.mk
HFILES += \
ui.h \
ui_$(OS)$(OSHSUFFIX)
NAME = libui
SUFFIX = $(LIBSUFFIX)
ifeq ($(USESSONAME),1)
SUFFIX = $(SONAMEEXT)
endif
ifeq ($(TOOLCHAIN),gcc)
# make every symbol hidden by default except _UI_EXTERN ones
# thanks ebassi in irc.gimp.net/#gtk+
CFLAGS += \
-D_UI_EXTERN='__attribute__((visibility("default"))) extern' \
-fvisibility=hidden
CXXFLAGS += \
-D_UI_EXTERN='__attribute__((visibility("default"))) extern' \
-fvisibility=hidden
LDFLAGS += \
-fvisibility=hidden
else
# make every symbol hidden by default except _UI_EXTERN ones
# TODO autogenerate a .def file?
CFLAGS += \
-D "_UI_EXTERN=__declspec(dllexport) extern"
CXXFLAGS += \
-D "_UI_EXTERN=__declspec(dllexport) extern"
endif
ifeq ($(USESSONAME),1)
LDFLAGS += $(SONAMEFLAG)$(NAME)$(SUFFIX)
endif
include build/GNUbase$(TOOLCHAIN).mk
# install rule is OS specific
# TODO probably better off making it a toolchain-wide rule
include $(OS)/GNUinstall.mk

View File

@ -1,34 +0,0 @@
# 16 october 2015
ifndef inlibuibuild
$(error Do not run these makefiles directly.)
endif
include $(OS)/GNUosspecific.mk
include test/GNUfiles.mk
HFILES += \
ui.h
NAME = test
SUFFIX = $(EXESUFFIX)
ifeq ($(TOOLCHAIN),gcc)
LDFLAGS += -L$(OUTDIR) -lui
# tell the dynamic loader to search in the executable path for shared objects
# note: OS X's linker complains if we say -rpath= instead of -rpath,
# also note that OS X doesn't use $ORIGIN - see http://jorgen.tjer.no/post/2014/05/20/dt-rpath-ld-and-at-rpath-dyld/
ifeq ($(OS),darwin)
LDFLAGS += -Wl,-rpath,@executable_path
else
LDFLAGS += -Wl,-rpath,'$$ORIGIN'
endif
else
# TODO is there an equivalent to -L?
LDFLAGS += $(OUTDIR)/libui.lib
endif
# executables are not shared libraries
USESSONAME = 0
include build/GNUbase$(TOOLCHAIN).mk

View File

@ -1,42 +0,0 @@
HOW TO BUILD
Simply type
make [variables...]
The build-time settings are
OS=xxx
default operating system
supported OSs are
windows
unix
darwin
haiku
this is autodetected by default
CFLAGS=xxx
CXXFLAGS=xxx
LDFLAGS=xxx
compiler flags
this is where you can specify -m32 or -m64, for instance
Objective-C uses $(CFLAGS)
NODEBUG=xxx
set to 1 to disable debug symbols
must be 1, any other value means "include debug symbols"
PREFIX=xxx
TODO
DESTDIR=xxx
applied before PREFIX; used by Debian
To build the test program use
make [variables....] test
The variables are the same as above.
To build an example, use
make EXAMPLE=name [variables...] example
You must specify the example name.
TODO discuss the internals

23
common/CMakeLists.txt Normal file
View File

@ -0,0 +1,23 @@
# 3 june 2016
list(APPEND _LIBUI_SOURCES
common/attribute.c
common/attrlist.c
common/attrstr.c
common/areaevents.c
common/control.c
common/debug.c
common/matrix.c
common/opentype.c
common/shouldquit.c
common/tablemodel.c
common/tablevalue.c
common/userbugs.c
common/utf.c
)
set(_LIBUI_SOURCES ${_LIBUI_SOURCES} PARENT_SCOPE)
list(APPEND _LIBUI_INCLUDEDIRS
common
)
set(_LIBUI_INCLUDEDIRS ${_LIBUI_INCLUDEDIRS} PARENT_SCOPE)

View File

@ -1,11 +0,0 @@
# 16 october 2015
CFILES += \
common/areaevents.c \
common/control.c \
common/matrix.c \
common/shouldquit.c
HFILES += \
common/controlsigs.h \
common/uipriv.h

22
common/OLD_table.c Normal file
View File

@ -0,0 +1,22 @@
// 21 june 2016
#include "../ui.h"
#include "uipriv.h"
void *uiTableModelGiveInt(int i)
{
return (void *) ((intptr_t) i);
}
int uiTableModelTakeInt(void *v)
{
return (int) ((intptr_t) v);
}
uiTableColumn *uiTableAppendTextColumn(uiTable *t, const char *name, int modelColumn)
{
uiTableColumn *tc;
tc = uiTableAppendColumn(t, name);
uiTableColumnAppendTextPart(tc, modelColumn, 1);
return tc;
}

View File

@ -10,11 +10,13 @@ For GTK+, we pull the double-click time and double-click distance, which work th
On GTK+ this will also allow us to discard the GDK_BUTTON_2PRESS and GDK_BUTTON_3PRESS events, so the button press stream will be just like on other platforms.
Thanks to mclasen, garnacho_, halfline, and tristan in irc.gimp.net/#gtk+.
TODO note the bits about asymmetry and g_rcClick initial value not mattering in the oldnewthing article
*/
// x, y, xdist, ydist, and c.rect must have the same units
// so must time, maxTime, and c.prevTime
uintmax_t clickCounterClick(clickCounter *c, uintmax_t button, intmax_t x, intmax_t y, uintptr_t time, uintptr_t maxTime, intmax_t xdist, intmax_t ydist)
int uiprivClickCounterClick(uiprivClickCounter *c, int button, int x, int y, uintptr_t time, uintptr_t maxTime, int32_t xdist, int32_t ydist)
{
// different button than before? if so, don't count
if (button != c->curButton)
@ -48,7 +50,7 @@ uintmax_t clickCounterClick(clickCounter *c, uintmax_t button, intmax_t x, intma
return c->count;
}
void clickCounterReset(clickCounter *c)
void uiprivClickCounterReset(uiprivClickCounter *c)
{
c->curButton = 0;
c->rectX0 = 0;
@ -149,7 +151,7 @@ static const struct {
{ 0xFFFF, 0 },
};
int fromScancode(uintptr_t scancode, uiAreaKeyEvent *ke)
int uiprivFromScancode(uintptr_t scancode, uiAreaKeyEvent *ke)
{
int i;

266
common/attribute.c Normal file
View File

@ -0,0 +1,266 @@
// 19 february 2018
#include "../ui.h"
#include "uipriv.h"
#include "attrstr.h"
struct uiAttribute {
int ownedByUser;
size_t refcount;
uiAttributeType type;
union {
char *family;
double size;
uiTextWeight weight;
uiTextItalic italic;
uiTextStretch stretch;
struct {
double r;
double g;
double b;
double a;
// put this here so we can reuse this structure
uiUnderlineColor underlineColor;
} color;
uiUnderline underline;
uiOpenTypeFeatures *features;
} u;
};
static uiAttribute *newAttribute(uiAttributeType type)
{
uiAttribute *a;
a = uiprivNew(uiAttribute);
a->ownedByUser = 1;
a->refcount = 0;
a->type = type;
return a;
}
// returns a to allow expressions like b = uiprivAttributeRetain(a)
// TODO would this allow us to copy attributes between strings in a foreach func, and if so, should that be allowed?
uiAttribute *uiprivAttributeRetain(uiAttribute *a)
{
a->ownedByUser = 0;
a->refcount++;
return a;
}
static void destroy(uiAttribute *a)
{
switch (a->type) {
case uiAttributeTypeFamily:
uiprivFree(a->u.family);
break;
case uiAttributeTypeFeatures:
uiFreeOpenTypeFeatures(a->u.features);
break;
}
uiprivFree(a);
}
void uiprivAttributeRelease(uiAttribute *a)
{
if (a->ownedByUser)
/* TODO implementation bug: we can't release an attribute we don't own */;
a->refcount--;
if (a->refcount == 0)
destroy(a);
}
void uiFreeAttribute(uiAttribute *a)
{
if (!a->ownedByUser)
/* TODO user bug: you can't free an attribute you don't own */;
destroy(a);
}
uiAttributeType uiAttributeGetType(const uiAttribute *a)
{
return a->type;
}
uiAttribute *uiNewFamilyAttribute(const char *family)
{
uiAttribute *a;
a = newAttribute(uiAttributeTypeFamily);
a->u.family = (char *) uiprivAlloc((strlen(family) + 1) * sizeof (char), "char[] (uiAttribute)");
strcpy(a->u.family, family);
return a;
}
const char *uiAttributeFamily(const uiAttribute *a)
{
return a->u.family;
}
uiAttribute *uiNewSizeAttribute(double size)
{
uiAttribute *a;
a = newAttribute(uiAttributeTypeSize);
a->u.size = size;
return a;
}
double uiAttributeSize(const uiAttribute *a)
{
return a->u.size;
}
uiAttribute *uiNewWeightAttribute(uiTextWeight weight)
{
uiAttribute *a;
a = newAttribute(uiAttributeTypeWeight);
a->u.weight = weight;
return a;
}
uiTextWeight uiAttributeWeight(const uiAttribute *a)
{
return a->u.weight;
}
uiAttribute *uiNewItalicAttribute(uiTextItalic italic)
{
uiAttribute *a;
a = newAttribute(uiAttributeTypeItalic);
a->u.italic = italic;
return a;
}
uiTextItalic uiAttributeItalic(const uiAttribute *a)
{
return a->u.italic;
}
uiAttribute *uiNewStretchAttribute(uiTextStretch stretch)
{
uiAttribute *a;
a = newAttribute(uiAttributeTypeStretch);
a->u.stretch = stretch;
return a;
}
uiTextStretch uiAttributeStretch(const uiAttribute *a)
{
return a->u.stretch;
}
uiAttribute *uiNewColorAttribute(double r, double g, double b, double a)
{
uiAttribute *at;
at = newAttribute(uiAttributeTypeColor);
at->u.color.r = r;
at->u.color.g = g;
at->u.color.b = b;
at->u.color.a = a;
return at;
}
void uiAttributeColor(const uiAttribute *a, double *r, double *g, double *b, double *alpha)
{
*r = a->u.color.r;
*g = a->u.color.g;
*b = a->u.color.b;
*alpha = a->u.color.a;
}
uiAttribute *uiNewBackgroundAttribute(double r, double g, double b, double a)
{
uiAttribute *at;
at = newAttribute(uiAttributeTypeBackground);
at->u.color.r = r;
at->u.color.g = g;
at->u.color.b = b;
at->u.color.a = a;
return at;
}
uiAttribute *uiNewUnderlineAttribute(uiUnderline u)
{
uiAttribute *a;
a = newAttribute(uiAttributeTypeUnderline);
a->u.underline = u;
return a;
}
uiUnderline uiAttributeUnderline(const uiAttribute *a)
{
return a->u.underline;
}
uiAttribute *uiNewUnderlineColorAttribute(uiUnderlineColor u, double r, double g, double b, double a)
{
uiAttribute *at;
at = uiNewColorAttribute(r, g, b, a);
at->type = uiAttributeTypeUnderlineColor;
at->u.color.underlineColor = u;
return at;
}
void uiAttributeUnderlineColor(const uiAttribute *a, uiUnderlineColor *u, double *r, double *g, double *b, double *alpha)
{
*u = a->u.color.underlineColor;
uiAttributeColor(a, r, g, b, alpha);
}
uiAttribute *uiNewFeaturesAttribute(const uiOpenTypeFeatures *otf)
{
uiAttribute *a;
a = newAttribute(uiAttributeTypeFeatures);
a->u.features = uiOpenTypeFeaturesClone(otf);
return a;
}
const uiOpenTypeFeatures *uiAttributeFeatures(const uiAttribute *a)
{
return a->u.features;
}
int uiprivAttributeEqual(const uiAttribute *a, const uiAttribute *b)
{
if (a == b)
return 1;
if (a->type != b->type)
return 0;
switch (a->type) {
case uiAttributeTypeFamily:
return uiprivStricmp(a->u.family, b->u.family);
case uiAttributeTypeSize:
// TODO is the use of == correct?
return a->u.size == b->u.size;
case uiAttributeTypeWeight:
return a->u.weight == b->u.weight;
case uiAttributeTypeItalic:
return a->u.italic == b->u.italic;
case uiAttributeTypeStretch:
return a->u.stretch == b->u.stretch;
case uiAttributeTypeUnderline:
return a->u.underline == b->u.underline;
case uiAttributeTypeUnderlineColor:
if (a->u.color.underlineColor != b->u.color.underlineColor)
return 0;
// fall through
case uiAttributeTypeColor:
case uiAttributeTypeBackground:
// TODO is the use of == correct?
return (a->u.color.r == b->u.color.r) &&
(a->u.color.g == b->u.color.g) &&
(a->u.color.b == b->u.color.b) &&
(a->u.color.a == b->u.color.a);
case uiAttributeTypeFeatures:
return uiprivOpenTypeFeaturesEqual(a->u.features, b->u.features);
}
// TODO should not be reached
return 0;
}

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