mirror of
https://github.com/libretro/RetroArch.git
synced 2024-11-24 08:30:16 +00:00
commit
719cb86bc0
9
.gitignore
vendored
9
.gitignore
vendored
@ -68,6 +68,12 @@ menu/driverspzarch.c
|
||||
.pc
|
||||
/media/shaders_glsl/
|
||||
/obj-w32/
|
||||
.cproject
|
||||
.settings
|
||||
libretro-super
|
||||
run.sh
|
||||
convert_rumble.awk
|
||||
*~
|
||||
|
||||
# Wii U
|
||||
*.depend
|
||||
@ -114,6 +120,9 @@ wiiu/wut/elf2rpl/elf2rpl
|
||||
|
||||
pkg/apple/iOS/build/
|
||||
pkg/apple/iOS/modules/
|
||||
pkg/apple/build/
|
||||
ui/drivers/qt/moc_*
|
||||
ui/drivers/moc_*
|
||||
|
||||
obj-unix/
|
||||
.vagrant/
|
||||
|
16
.project
16
.project
@ -5,7 +5,23 @@
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
|
||||
<triggers>clean,full,incremental,</triggers>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
|
||||
<triggers>full,incremental,</triggers>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.cdt.core.cnature</nature>
|
||||
<nature>org.eclipse.cdt.core.ccnature</nature>
|
||||
<nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
|
||||
<nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
|
14
.travis.yml
14
.travis.yml
@ -34,6 +34,20 @@ matrix:
|
||||
osx_image: xcode7.3
|
||||
script:
|
||||
- xcodebuild -target RetroArch -configuration Release -project pkg/apple/RetroArch.xcodeproj
|
||||
- os: osx
|
||||
osx_image: xcode9.3
|
||||
script:
|
||||
- cd ~/
|
||||
- brew install --force-bottle qt5
|
||||
- git clone --depth=50 https://github.com/libretro/libretro-super
|
||||
- cd libretro-super/travis
|
||||
- ./build-retroarch-metal.sh
|
||||
deploy:
|
||||
skip_cleanup: true
|
||||
provider: script
|
||||
script: cd ../retroarch; bash travis_metal_deploy.sh
|
||||
on:
|
||||
branch: master
|
||||
|
||||
script:
|
||||
- ./configure
|
||||
|
273
AUTHORS.h
Normal file
273
AUTHORS.h
Normal file
@ -0,0 +1,273 @@
|
||||
static const char *retroarch_contributors_list = R"(
|
||||
RetroArch and the libretro team would like to
|
||||
acknowledge the following contributors:
|
||||
|
||||
1ch (L0sted)
|
||||
Aaahh Ahh (Aaahh)
|
||||
Aaron Oneal (aarononeal)
|
||||
Adam Mechtley (amechtley)
|
||||
Adolfo Ketzer (reztek)
|
||||
AdrianoML
|
||||
ajefr
|
||||
Alberto Simon (asimonf)
|
||||
Alcaro
|
||||
Alessandro Mangone (Vektor) (zevektor)
|
||||
Alex Eckhart (alex34567)
|
||||
Alex Folland (AlexFolland)
|
||||
Alexander Spady (alkaseltzerspadt)
|
||||
Alexandre Garcia (Alexandre-Garcia)
|
||||
Alfredo Monclus (alfrix)
|
||||
aliaspider
|
||||
alphanu1
|
||||
altiereslima
|
||||
Alwin Garside (Yogarine)
|
||||
Amiga1200Gamer (amigagamer)
|
||||
Andre Leiradella (leiradel)
|
||||
Andrés (fr500)
|
||||
Anthony J. Bentley (bentley)
|
||||
AridRayne
|
||||
Arto Vainiolehto (arakerlu)
|
||||
asako (asakous)
|
||||
Ash (QuarkTheAwesome)
|
||||
Ashura (CoalaJoe)
|
||||
Autechre64
|
||||
Avton1
|
||||
barbudreadmon
|
||||
bearoso
|
||||
blackgur
|
||||
Boris Timofeev (btimofeev)
|
||||
bparker06
|
||||
Brandon Johnson (Denu8thell)
|
||||
Brian Koropoff (bkoropoff)
|
||||
Brian S. Stephan (bsstephan)
|
||||
Brunnis
|
||||
bubuleur
|
||||
Casey Borders (CaseyB)
|
||||
ceb33
|
||||
celerizer
|
||||
Chris Merrett (chrisfu)
|
||||
chu (9chu)
|
||||
CidVonHighwind
|
||||
clienthax
|
||||
Conn O'Griofa (psyke83)
|
||||
cpsw
|
||||
Cray Elliott (MP2E)
|
||||
Cristian Sandu (crazyquark)
|
||||
ctult
|
||||
cubtekki
|
||||
cucholix
|
||||
cudencuden
|
||||
cxd4
|
||||
dalter
|
||||
Dan Weiss (Dwedit)
|
||||
danieljg
|
||||
Daniil Zhilin (GeneralFailer)
|
||||
dankcushions
|
||||
Danny Glover (DannyGlover)
|
||||
Dave (freakdave)
|
||||
Dave Leaver (danzel)
|
||||
David Demelier (markand)
|
||||
David Erickson (daviderickson)
|
||||
David Shah (daveshah1)
|
||||
David Walters (hiddenasbestos)
|
||||
DEX357
|
||||
diablodiab
|
||||
dibas
|
||||
Diego A. (SuperrSonic)
|
||||
Diego Viola (diegoviola)
|
||||
Diogo Barros (BlueKore)
|
||||
DogParty
|
||||
dor3k
|
||||
Dudu Akiva (duduke)
|
||||
duganchen
|
||||
duskgao
|
||||
ekipan
|
||||
ensra
|
||||
erbridge
|
||||
esoptron
|
||||
Ethan Lee (flibitijibibo)
|
||||
Ezio-PS
|
||||
Fabio (Oibaf66)
|
||||
Fabio Ritrovato (Sephiroth87)
|
||||
Fayne Aldan (FayneAldan)
|
||||
FIX94
|
||||
Flame Sage (chris062689)
|
||||
flyinghead
|
||||
Francisco José García García (frangarcj)
|
||||
GameDragon2k
|
||||
Garrett Brown (garbear)
|
||||
gblues
|
||||
Geoffrey Plitt (GeoffreyPlitt)
|
||||
Glenn Hevey (hevey)
|
||||
Googer
|
||||
gouchi
|
||||
Gregor Richards (GregorR)
|
||||
Gui Andrade (archshift)
|
||||
Gustavo Maciel Dias Vieira (gmdvieira)
|
||||
gvbr
|
||||
H. İbrahim Güngör (igungor)
|
||||
hadess
|
||||
Hans-Kristian Arntzen (Themaister)
|
||||
Henri Gomez (hgomez)
|
||||
Henri Gomez (hgomez-sonarsource)
|
||||
Higor Eurípedes (heuripedes)
|
||||
hizzlekizzle
|
||||
Hugo B. (777sha)
|
||||
hunterk
|
||||
Hyllian
|
||||
i30817
|
||||
iAmGhost
|
||||
Jack (jakcron)
|
||||
James Le Cuirot (chewi)
|
||||
James Sexton (Bezier89)
|
||||
Jan Holthuis (Holzhaus)
|
||||
Jay McCarthy (jeapostrophe)
|
||||
Jean-André Santoni (kivutar)
|
||||
Jean-Paul Mari (djipi)
|
||||
Jean-Sébastien Guay (Skylark13)
|
||||
Jesse Bryan (winneon)
|
||||
Joel (joel16)
|
||||
Joerg Sonnenberger (jsonn)
|
||||
Johannes Schickel (lordhoto)
|
||||
John Regan (jprjr)
|
||||
Jon Maddox (maddox)
|
||||
Jonathan Relf (jonathanrelf)
|
||||
Joohan Lee (losernator)
|
||||
Jools Wills (joolswills)
|
||||
Joseph Carter (iKarith)
|
||||
Joseph Conan Montgomery (Grotke)
|
||||
Josh Palmer (ShiftyAxel)
|
||||
JuanVCS
|
||||
Justin Jacobs (dorkster)
|
||||
Justin Weiss (justinweiss)
|
||||
Ken Rossato (rossato)
|
||||
kurumushi
|
||||
kwyxz
|
||||
l3iggs
|
||||
lasers
|
||||
Lawrence Kesteloot (lkesteloot)
|
||||
liffy (lifning)
|
||||
Lionel Flandrin (simias)
|
||||
littleguy77
|
||||
Logan (loganmc10)
|
||||
lordashram
|
||||
Lothar Serra Mari (lotharsm)
|
||||
Lubosz Sarnecki (lubosz)
|
||||
lucianposton
|
||||
lxerandrew
|
||||
Mahyar Koshkouei (deltabeard)
|
||||
Marcelo Munhoz Pélos (mpelos)
|
||||
markwkidd
|
||||
Maschell
|
||||
Mat M. (lioncash)
|
||||
Matt Sephton (gingerbeardman)
|
||||
Maxime Gauduin (alucryd)
|
||||
mcrisostomo
|
||||
meancoot
|
||||
meepingsnesroms
|
||||
meleu
|
||||
Michael K. (kuxii2016)
|
||||
Michał Durak (micechal)
|
||||
Mico Chopitea (micochops)
|
||||
miguelpinheiro
|
||||
Mike Swanson (chungy)
|
||||
miqlas (extrowerk)
|
||||
misson20000
|
||||
Monroe88
|
||||
mprobinson
|
||||
MrHuu
|
||||
muzuiget
|
||||
mwtremblay
|
||||
Nathan S. (natinusala)
|
||||
nattycleopatra
|
||||
netux79
|
||||
nfnty
|
||||
Nicolas Adenis-Lamarre (nadenislamarre)
|
||||
Nicolas Guillaumin (nguillaumin)
|
||||
Nicolas Lopez Fernandez (pyroesp)
|
||||
Nikita Vakhrushev (JesterIOS)
|
||||
Nikola Kocic (nikola-kocic)
|
||||
Niouby
|
||||
nosh01
|
||||
not6 (r-type)
|
||||
notaz
|
||||
nstCactus
|
||||
nurupo
|
||||
Oggom
|
||||
Oleg Shevchenko (olsh)
|
||||
opendata26
|
||||
orbea
|
||||
OV2
|
||||
pamapa
|
||||
Paolo Bonzini (bonzini)
|
||||
Paul McCarty (a-shark)
|
||||
Pedro Ribeiro (pedrib)
|
||||
pinumbernumber
|
||||
pponso1
|
||||
Ramiro Morales (ramiro)
|
||||
retro-wertz
|
||||
Richard Howell (rmaz)
|
||||
Rob Loach (RobLoach)
|
||||
Romain Gay (vikbez)
|
||||
Romain Graillot (notnotme)
|
||||
Romain TISSERAND (rtissera)
|
||||
Roman Kalashnikov (lunixoid)
|
||||
Royerson
|
||||
rrooij
|
||||
rsn8887
|
||||
Ryunam
|
||||
rz5
|
||||
Saggi Mizrahi (ficoos)
|
||||
SAKUJ0
|
||||
Sam Pagenkopf (44100hertz)
|
||||
Sam Stenvall (Jalle19)
|
||||
Scheiker
|
||||
Sebastien Ronsse (sronsse)
|
||||
Sergi Granell (xerpi)
|
||||
Sergio Padrino (sergiou87)
|
||||
sergio-br2 (sergiobenrocha2)
|
||||
Seth Kingsley (sethk)
|
||||
Shane Mouton (ismouton)
|
||||
Shizeeg Unadequatov (shizeeg)
|
||||
Skarsnik
|
||||
slotek
|
||||
sparklewind
|
||||
spec-chum
|
||||
Stefan (gizmo98)
|
||||
stefan-gr
|
||||
Steven M. Vascellaro (stevoisiak)
|
||||
Stuart Carnie (stuartcarnie)
|
||||
Sunguk Lee (d3m3vilurr)
|
||||
Supernature2k
|
||||
Sven (RetroSven)
|
||||
Swizzy
|
||||
Tatsuya79
|
||||
Terry Lewis (terry-au)
|
||||
The Last Cabra (vanfanel)
|
||||
thedax
|
||||
theheroGAC
|
||||
TheOfficialFloW
|
||||
thiolliere
|
||||
Toad King (ToadKing)
|
||||
Tobias Jakobi (tobiasjakobi)
|
||||
Tomáš Kelemen (ToKe79)
|
||||
Torsten Paul (t-paul)
|
||||
TotalCaesar659
|
||||
TroggleMonkey
|
||||
Twinaphex
|
||||
URBANsUNITED
|
||||
vgmoose
|
||||
Vicky C Lau (vickychenglau)
|
||||
Vladimir Panteleev (CyberShadow)
|
||||
Víctor "IlDucci" (IlDucci)
|
||||
Wang Vincent (susemm)
|
||||
webgeek1234
|
||||
Wiktor Strzębała (wiktorek140)
|
||||
xhp-creations
|
||||
Yarsan Hoessain (hyarsan)
|
||||
Yongwoon Cho (ssangkong)
|
||||
yoshisuga
|
||||
zeromus
|
||||
zlice
|
||||
Zoran Vuckovic (casdevel)
|
||||
)";
|
31
CHANGES.md
31
CHANGES.md
@ -1,8 +1,17 @@
|
||||
# 1.7.4 (future)
|
||||
# 1.7.5 (future)
|
||||
- COMMON: Support for "OEM-102" key (usually '\' on Euro keyboards).
|
||||
- MENU/QT/WIMP: Add option to filter extensions inside archives when adding to a playlist.
|
||||
- METAL: Add screenshot support.
|
||||
|
||||
# 1.7.4
|
||||
- ANDROID: Add sustained performance mode, can be turned on/off in Power Management settings menu.
|
||||
- ANDROID: Powerstate/battery level support.
|
||||
- CHEEVOS: Fix crash when scrolling Achievement List while Unofficial Achievements enabled (#6732).
|
||||
- CHEEVOS: Added hitcounts support for PauseIf/ResetIf (#6817).
|
||||
- COMMON: Automatically hide "Configuration Override options" in Quick Menu.
|
||||
- COMMON: Small Bugfix to not trigger savestate code when pressing Reset.
|
||||
- COMMON: Added libsixel video driver.
|
||||
- EMSCRIPTEN: Fix Game Focus Toggle.
|
||||
- HID/OSX: Fix to set hid device registration deterministic (#6497), to address issue #6640 re-adding dynamic device registration.
|
||||
- LOCALIZATION: Update Italian translation.
|
||||
- LOCALIZATION: Update Japanese translation.
|
||||
@ -12,22 +21,38 @@
|
||||
- LOCALIZATION: Update Spanish translation.
|
||||
- MIDI: Add MIDI support to the libretro API. Dosbox is the first proof of concept core implementing libretro MIDI.
|
||||
- MIDI: Add a Windows driver for MIDI, based on winmm.
|
||||
- MENU/QT/WIMP: QT QSlider styling for Dark Theme.
|
||||
- MENU/QT/WIMP: Qt QSlider styling for Dark Theme.
|
||||
- MENU/QT/WIMP: Remove button ghostly inside highlighting.
|
||||
- MENU/QT/WIMP: Initial grid view.
|
||||
- MENU/QT/WIMP: Drag&drop to add new playlist items, add option to add/edit/delete playlists.
|
||||
- MENU/QT/WIMP: Add menu option to update RetroArch (Windows only for now).
|
||||
- MENU/QT/WIMP: Add menu option to manage shaders.
|
||||
- MENU/QT/WIMP: Add menu option to manage core options.
|
||||
- MENU/XMB: Add new icons for the settings
|
||||
- MENU/XMB: Add an option to show the desktop ui
|
||||
- METAL: Initial work-in-progress video driver for Metal. macOS-only right now, and currently requires macOS 10.13.
|
||||
- METAL: Supports XMB/MaterialUI, has a menu display driver. Has a font rendering driver.
|
||||
- METAL/SLANG: Slang shaders should be compatible with Metal video driver.
|
||||
- NETWORK: Enable SSL/TLS support by default for desktop platforms.
|
||||
- QNX: Fix Game Focus Toggle.
|
||||
- PS3: Add audio mixer support for FLAC and MP3.
|
||||
- PSP: Use proper button labels, fix inverted R-Stick Y axis.
|
||||
- REMAPS: Fix the way offsets are calculated for keyboard remapping.
|
||||
- RUNAHEAD: Fix full-screen mode change breaking Secondary Core's environment variables.
|
||||
- VITA: Use proper button labels, fix inverted R-Stick Y axis.
|
||||
- VITA: Add imc0: mount.
|
||||
- VITA: Use sceCtrlIsMultiControllerSupported to detect.
|
||||
- VULKAN: Fix two validation errors.
|
||||
- VULKAN: Try to avoid creating swapchains redundantly. Should fix black screen and having to alt tab out of window again to get display working on Nvidia GPUs (Windows).
|
||||
- VULKAN/OSX: Initial MoltenVK support. Not enabled yet, several MoltenVK bugs should be fixed first before we can have it fully working.
|
||||
- WINDOWS/DINPUT: Add rumble support.
|
||||
- WINDOWS/DINPUT: Fix Game Focus Toggle.
|
||||
- WINDOWS/RAWINPUT: Fix Game Focus Toggle.
|
||||
- X11: Fix Game Focus Toggle.
|
||||
- WII: Change deflicker setting to work in 480p or higher, and always enables vfilter so that the user can easily change brightness.
|
||||
- WIIU: Fix out-of-bounds rendering bug
|
||||
- WIIU: Implement UDP broadcast network logging on Wii U.
|
||||
- WIIU: Audio should no longer clip.
|
||||
|
||||
# 1.7.3
|
||||
- AUDIO: Audio mixer supports FLAC/MP3 file types now!
|
||||
@ -75,7 +100,7 @@ video drivers that implement it (D3D8/9/10/11/12/GL)
|
||||
- MENU/RGUI: D3D8/D3D9: Hookup Menu Linear Filter
|
||||
- MENU/XMB: Disable XMB shadow icons by default for PowerPC and ARM for performance reasons.
|
||||
- MENU/XMB: Left/right thumbnails are now automatically scaled according to layout.
|
||||
- MENU/XMB: Add Left Thumbnails (additional to the right).
|
||||
- MENU/XMB: Add Left Thumbnails (additional to the right).
|
||||
- MENU/XMB: Fixed left/right tab regression.
|
||||
- MENU/XMB: Fix scaling of tall images that were cut on bottom previously.
|
||||
- MENU/XMB: Menu scale factor setting now changes texts length, image scaling and margins.
|
||||
|
24
Makefile.apple
Normal file
24
Makefile.apple
Normal file
@ -0,0 +1,24 @@
|
||||
include Makefile.common
|
||||
|
||||
# Qt MOC generation, required for QObject-derived classes
|
||||
ifneq ($(MOC_HEADERS),)
|
||||
# prefix moc_ to base filename of paths and change extension from h to cpp, so a/b/foo.h becomes a/b/moc_foo.cpp
|
||||
MOC_SRC := $(join $(addsuffix moc_,$(dir $(MOC_HEADERS))), $(notdir $(MOC_HEADERS:.h=.cpp)))
|
||||
endif
|
||||
|
||||
MOC ?= $(error missing moc path)
|
||||
|
||||
.PHONY: generate
|
||||
|
||||
$(MOC_SRC):
|
||||
@$(if $(Q), $(shell echo echo MOC $<),)
|
||||
$(eval MOC_TMP := $(patsubst %.h,%_moc.cpp,$@))
|
||||
$(MOC) -o $(MOC_TMP) $<
|
||||
|
||||
$(foreach x,$(join $(addsuffix :,$(MOC_SRC)),$(MOC_HEADERS)),$(eval $x))
|
||||
|
||||
generate: $(MOC_SRC)
|
||||
@echo $(MOC_SRC)
|
||||
|
||||
print-%:
|
||||
@echo '$*=$($*)'
|
332
Makefile.common
332
Makefile.common
@ -7,6 +7,10 @@ ifeq ($(HAVE_STACK_USAGE), 1)
|
||||
CFLAGS += -fstack-usage
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_HAKCHI), 1)
|
||||
CFLAGS += -DHAVE_HAKCHI
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_GL_CONTEXT),)
|
||||
HAVE_GL_CONTEXT=0
|
||||
|
||||
@ -328,20 +332,44 @@ endif
|
||||
|
||||
# Qt WIMP GUI
|
||||
|
||||
ifeq ($(HAVE_OPENSSL), 1)
|
||||
DEFINES += $(OPENSSL_CFLAGS)
|
||||
LIBS += $(OPENSSL_LIBS)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_QT), 1)
|
||||
OBJ += ui/drivers/ui_qt.o \
|
||||
ui/drivers/qt/ui_qt_application.o \
|
||||
ui/drivers/qt/ui_qt_window.o \
|
||||
ui/drivers/qt/ui_qt_browser_window.o \
|
||||
ui/drivers/qt/ui_qt_load_core_window.o \
|
||||
ui/drivers/qt/ui_qt_msg_window.o
|
||||
ui/drivers/qt/ui_qt_msg_window.o \
|
||||
ui/drivers/qt/flowlayout.o \
|
||||
ui/drivers/qt/shaderparamsdialog.o \
|
||||
ui/drivers/qt/coreoptionsdialog.o \
|
||||
ui/drivers/qt/filedropwidget.o \
|
||||
ui/drivers/qt/coreinfodialog.o \
|
||||
ui/drivers/qt/playlistentrydialog.o \
|
||||
ui/drivers/qt/viewoptionsdialog.o \
|
||||
ui/drivers/qt/playlist.o \
|
||||
ui/drivers/qt/updateretroarch.o \
|
||||
ui/drivers/qt/thumbnaildownload.o \
|
||||
ui/drivers/qt/thumbnailpackdownload.o \
|
||||
ui/drivers/qt/playlistthumbnaildownload.o
|
||||
|
||||
MOC_HEADERS += ui/drivers/ui_qt.h \
|
||||
ui/drivers/qt/ui_qt_load_core_window.h
|
||||
ui/drivers/qt/ui_qt_load_core_window.h \
|
||||
ui/drivers/qt/flowlayout.h \
|
||||
ui/drivers/qt/shaderparamsdialog.h \
|
||||
ui/drivers/qt/coreoptionsdialog.h \
|
||||
ui/drivers/qt/filedropwidget.h \
|
||||
ui/drivers/qt/coreinfodialog.h \
|
||||
ui/drivers/qt/playlistentrydialog.h \
|
||||
ui/drivers/qt/viewoptionsdialog.h
|
||||
|
||||
DEFINES += $(QT5CORE_CFLAGS) $(QT5GUI_CFLAGS) $(QT5WIDGETS_CFLAGS) -DHAVE_MAIN
|
||||
DEFINES += $(QT5CORE_CFLAGS) $(QT5GUI_CFLAGS) $(QT5WIDGETS_CFLAGS) $(QT5CONCURRENT_CFLAGS) $(QT5NETWORK_CFLAGS) -DHAVE_MAIN
|
||||
#DEFINES += $(QT5WEBENGINE_CFLAGS)
|
||||
LIBS += $(QT5CORE_LIBS) $(QT5GUI_LIBS) $(QT5WIDGETS_LIBS)
|
||||
LIBS += $(QT5CORE_LIBS) $(QT5GUI_LIBS) $(QT5WIDGETS_LIBS) $(QT5CONCURRENT_LIBS) $(QT5NETWORK_LIBS)
|
||||
#LIBS += $(QT5WEBENGINE_LIBS)
|
||||
NEED_CXX_LINKER = 1
|
||||
|
||||
@ -368,199 +396,94 @@ OBJ += libretro-db/bintree.o \
|
||||
endif
|
||||
|
||||
ifneq ($(C89_BUILD), 1)
|
||||
HAVE_LIBUI = 0
|
||||
HAVE_GTKPLUS = 0
|
||||
|
||||
ifeq ($(HAVE_SSL), 1)
|
||||
DEFINES += -DHAVE_SSL
|
||||
DEFINES += -DMBEDTLS_SSL_DEBUG_ALL
|
||||
|
||||
ifeq ($(DEBUG), 1)
|
||||
DEFINES += -DMBEDTLS_SSL_DEBUG_ALL
|
||||
endif
|
||||
|
||||
# MinGW requires this for some reason, even though the include paths are relative to the source
|
||||
INCLUDE_DIRS += -Ideps/mbedtls
|
||||
|
||||
OBJS_TLS_CRYPTO = deps/mbedtls/aes.o deps/mbedtls/aesni.o deps/mbedtls/arc4.o \
|
||||
deps/mbedtls/asn1parse.o deps/mbedtls/asn1write.o deps/mbedtls/base64.o \
|
||||
deps/mbedtls/bignum.o deps/mbedtls/blowfish.o deps/mbedtls/camellia.o \
|
||||
deps/mbedtls/ccm.o deps/mbedtls/cipher.o deps/mbedtls/cipher_wrap.o \
|
||||
deps/mbedtls/cmac.o deps/mbedtls/ctr_drbg.o deps/mbedtls/des.o \
|
||||
deps/mbedtls/dhm.o deps/mbedtls/ecdh.o deps/mbedtls/ecdsa.o \
|
||||
deps/mbedtls/ecjpake.o deps/mbedtls/ecp.o \
|
||||
deps/mbedtls/ecp_curves.o deps/mbedtls/entropy.o deps/mbedtls/entropy_poll.o \
|
||||
deps/mbedtls/error.o deps/mbedtls/gcm.o deps/mbedtls/havege.o \
|
||||
deps/mbedtls/hmac_drbg.o deps/mbedtls/md.o deps/mbedtls/md2.o \
|
||||
deps/mbedtls/md4.o deps/mbedtls/md5.o deps/mbedtls/md_wrap.o \
|
||||
deps/mbedtls/memory_buffer_alloc.o deps/mbedtls/oid.o \
|
||||
deps/mbedtls/padlock.o deps/mbedtls/pem.o deps/mbedtls/pk.o \
|
||||
deps/mbedtls/pk_wrap.o deps/mbedtls/pkcs12.o deps/mbedtls/pkcs5.o \
|
||||
deps/mbedtls/pkparse.o deps/mbedtls/pkwrite.o deps/mbedtls/platform.o \
|
||||
deps/mbedtls/ripemd160.o deps/mbedtls/rsa.o deps/mbedtls/sha1.o \
|
||||
deps/mbedtls/sha256.o deps/mbedtls/sha512.o deps/mbedtls/threading.o \
|
||||
deps/mbedtls/timing.o deps/mbedtls/version.o \
|
||||
deps/mbedtls/version_features.o deps/mbedtls/xtea.o
|
||||
OBJS_TLS_CRYPTO = deps/mbedtls/aes.o \
|
||||
deps/mbedtls/aesni.o \
|
||||
deps/mbedtls/arc4.o \
|
||||
deps/mbedtls/asn1parse.o \
|
||||
deps/mbedtls/asn1write.o \
|
||||
deps/mbedtls/base64.o \
|
||||
deps/mbedtls/bignum.o \
|
||||
deps/mbedtls/blowfish.o \
|
||||
deps/mbedtls/camellia.o \
|
||||
deps/mbedtls/ccm.o \
|
||||
deps/mbedtls/cipher.o \
|
||||
deps/mbedtls/cipher_wrap.o \
|
||||
deps/mbedtls/cmac.o \
|
||||
deps/mbedtls/ctr_drbg.o \
|
||||
deps/mbedtls/des.o \
|
||||
deps/mbedtls/dhm.o \
|
||||
deps/mbedtls/ecdh.o \
|
||||
deps/mbedtls/ecdsa.o \
|
||||
deps/mbedtls/ecjpake.o \
|
||||
deps/mbedtls/ecp.o \
|
||||
deps/mbedtls/ecp_curves.o \
|
||||
deps/mbedtls/entropy.o \
|
||||
deps/mbedtls/entropy_poll.o \
|
||||
deps/mbedtls/error.o \
|
||||
deps/mbedtls/gcm.o \
|
||||
deps/mbedtls/havege.o \
|
||||
deps/mbedtls/hmac_drbg.o \
|
||||
deps/mbedtls/md.o \
|
||||
deps/mbedtls/md2.o \
|
||||
deps/mbedtls/md4.o \
|
||||
deps/mbedtls/md5.o \
|
||||
deps/mbedtls/md_wrap.o \
|
||||
deps/mbedtls/memory_buffer_alloc.o \
|
||||
deps/mbedtls/oid.o \
|
||||
deps/mbedtls/padlock.o \
|
||||
deps/mbedtls/pem.o \
|
||||
deps/mbedtls/pk.o \
|
||||
deps/mbedtls/pk_wrap.o \
|
||||
deps/mbedtls/pkcs12.o \
|
||||
deps/mbedtls/pkcs5.o \
|
||||
deps/mbedtls/pkparse.o \
|
||||
deps/mbedtls/pkwrite.o \
|
||||
deps/mbedtls/platform.o \
|
||||
deps/mbedtls/ripemd160.o \
|
||||
deps/mbedtls/rsa.o \
|
||||
deps/mbedtls/sha1.o \
|
||||
deps/mbedtls/sha256.o \
|
||||
deps/mbedtls/sha512.o \
|
||||
deps/mbedtls/threading.o \
|
||||
deps/mbedtls/timing.o \
|
||||
deps/mbedtls/version.o \
|
||||
deps/mbedtls/version_features.o \
|
||||
deps/mbedtls/xtea.o
|
||||
|
||||
OBJS_TLS_X509 = deps/mbedtls/certs.o deps/mbedtls/pkcs11.o deps/mbedtls/x509.o \
|
||||
deps/mbedtls/x509_create.o deps/mbedtls/x509_crl.o deps/mbedtls/x509_crt.o \
|
||||
deps/mbedtls/x509_csr.o deps/mbedtls/x509write_crt.o deps/mbedtls/x509write_csr.o
|
||||
OBJS_TLS_X509 = deps/mbedtls/certs.o \
|
||||
deps/mbedtls/pkcs11.o \
|
||||
deps/mbedtls/x509.o \
|
||||
deps/mbedtls/x509_create.o \
|
||||
deps/mbedtls/x509_crl.o \
|
||||
deps/mbedtls/x509_crt.o \
|
||||
deps/mbedtls/x509_csr.o \
|
||||
deps/mbedtls/x509write_crt.o \
|
||||
deps/mbedtls/x509write_csr.o
|
||||
|
||||
OBJS_TLS = deps/mbedtls/debug.o deps/mbedtls/net_sockets.o \
|
||||
deps/mbedtls/ssl_cache.o deps/mbedtls/ssl_ciphersuites.o \
|
||||
deps/mbedtls/ssl_cli.o deps/mbedtls/ssl_cookie.o \
|
||||
deps/mbedtls/ssl_srv.o deps/mbedtls/ssl_ticket.o \
|
||||
OBJS_TLS = deps/mbedtls/debug.o \
|
||||
deps/mbedtls/net_sockets.o \
|
||||
deps/mbedtls/ssl_cache.o \
|
||||
deps/mbedtls/ssl_ciphersuites.o \
|
||||
deps/mbedtls/ssl_cli.o \
|
||||
deps/mbedtls/ssl_cookie.o \
|
||||
deps/mbedtls/ssl_srv.o \
|
||||
deps/mbedtls/ssl_ticket.o \
|
||||
deps/mbedtls/ssl_tls.o
|
||||
|
||||
OBJ += $(OBJS_TLS_CRYPTO) $(OBJS_TLS_X509) $(OBJS_TLS)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_LIBUI), 1)
|
||||
DEFINES += -DHAVE_LIBUI
|
||||
ifneq ($(findstring Win32,$(OS)),)
|
||||
OBJ += deps/libui/windows/alloc.o \
|
||||
deps/libui/windows/area.o \
|
||||
deps/libui/windows/areadraw.o \
|
||||
deps/libui/windows/areaevents.o \
|
||||
deps/libui/windows/areascroll.o \
|
||||
deps/libui/windows/areautil.o \
|
||||
deps/libui/windows/box.o \
|
||||
deps/libui/windows/button.o \
|
||||
deps/libui/windows/checkbox.o \
|
||||
deps/libui/windows/colorbutton.o \
|
||||
deps/libui/windows/colordialog.o \
|
||||
deps/libui/windows/combobox.o \
|
||||
deps/libui/windows/container.o \
|
||||
deps/libui/windows/control.o \
|
||||
deps/libui/windows/d2dscratch.o \
|
||||
deps/libui/windows/datetimepicker.o \
|
||||
deps/libui/windows/debug.o \
|
||||
deps/libui/windows/draw.o \
|
||||
deps/libui/windows/drawmatrix.o \
|
||||
deps/libui/windows/drawpath.o \
|
||||
deps/libui/windows/drawtext.o \
|
||||
deps/libui/windows/dwrite.o \
|
||||
deps/libui/windows/editablecombo.o \
|
||||
deps/libui/windows/entry.o \
|
||||
deps/libui/windows/events.o \
|
||||
deps/libui/windows/fontbutton.o \
|
||||
deps/libui/windows/fontdialog.o \
|
||||
deps/libui/windows/form.o \
|
||||
deps/libui/windows/graphemes.o \
|
||||
deps/libui/windows/grid.o \
|
||||
deps/libui/windows/group.o \
|
||||
deps/libui/windows/init.o \
|
||||
deps/libui/windows/label.o \
|
||||
deps/libui/windows/main.o \
|
||||
deps/libui/windows/menu.o \
|
||||
deps/libui/windows/multilineentry.o \
|
||||
deps/libui/windows/parent.o \
|
||||
deps/libui/windows/progressbar.o \
|
||||
deps/libui/windows/radiobuttons.o \
|
||||
deps/libui/windows/separator.o \
|
||||
deps/libui/windows/sizing.o \
|
||||
deps/libui/windows/slider.o \
|
||||
deps/libui/windows/spinbox.o \
|
||||
deps/libui/windows/stddialogs.o \
|
||||
deps/libui/windows/tab.o \
|
||||
deps/libui/windows/tabpage.o \
|
||||
deps/libui/windows/text.o \
|
||||
deps/libui/windows/utf16.o \
|
||||
deps/libui/windows/utilwin.o \
|
||||
deps/libui/windows/window.o \
|
||||
deps/libui/windows/winpublic.o \
|
||||
deps/libui/windows/winutil.o
|
||||
LIBS += -luxtheme -ld2d1 -ldwrite -lusp10
|
||||
else
|
||||
ifneq ($(findstring Darwin,$(OS)),)
|
||||
OBJ += deps/libui/darwin/alloc.o \
|
||||
deps/libui/darwin/area.o \
|
||||
deps/libui/darwin/areaevents.o \
|
||||
deps/libui/darwin/autolayout.o \
|
||||
deps/libui/darwin/box.o \
|
||||
deps/libui/darwin/button.o \
|
||||
deps/libui/darwin/checkbox.o \
|
||||
deps/libui/darwin/colorbutton.o \
|
||||
deps/libui/darwin/combobox.o \
|
||||
deps/libui/darwin/control.o \
|
||||
deps/libui/darwin/datetimepicker.o \
|
||||
deps/libui/darwin/debug.o \
|
||||
deps/libui/darwin/draw.o \
|
||||
deps/libui/darwin/drawtext.o \
|
||||
deps/libui/darwin/editablecombo.o \
|
||||
deps/libui/darwin/entry.o \
|
||||
deps/libui/darwin/fontbutton.o \
|
||||
deps/libui/darwin/form.o \
|
||||
deps/libui/darwin/grid.o \
|
||||
deps/libui/darwin/group.o \
|
||||
deps/libui/darwin/image.o \
|
||||
deps/libui/darwin/label.o \
|
||||
deps/libui/darwin/main.o \
|
||||
deps/libui/darwin/map.o \
|
||||
deps/libui/darwin/menu.o \
|
||||
deps/libui/darwin/multilineentry.o \
|
||||
deps/libui/darwin/progressbar.o \
|
||||
deps/libui/darwin/radiobuttons.o \
|
||||
deps/libui/darwin/scrollview.o \
|
||||
deps/libui/darwin/separator.o \
|
||||
deps/libui/darwin/slider.o \
|
||||
deps/libui/darwin/spinbox.o \
|
||||
deps/libui/darwin/stddialogs.o \
|
||||
deps/libui/darwin/tab.o \
|
||||
deps/libui/darwin/text.o \
|
||||
deps/libui/darwin/util.o \
|
||||
deps/libui/darwin/window.o \
|
||||
deps/libui/darwin/winmoveresize.o
|
||||
else
|
||||
CFLAGS += -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/gtk-3.0 -I/usr/include/pango-1.0 -I/usr/include/cairo -I/usr/include/gdk-pixbuf-2.0 -I/usr/include/atk-1.0
|
||||
LIBS += -lgtk-3 -lgdk-3 -lpangocairo-1.0 -lpango-1.0 -latk-1.0 -lcairo-gobject -lcairo -lgdk_pixbuf-2.0 -lgio-2.0 -lgobject-2.0 -lglib-2.0
|
||||
OBJ += deps/libui/unix/alloc.o \
|
||||
deps/libui/unix/area.o \
|
||||
deps/libui/unix/box.o \
|
||||
deps/libui/unix/button.o \
|
||||
deps/libui/unix/cellrendererbutton.o \
|
||||
deps/libui/unix/checkbox.o \
|
||||
deps/libui/unix/child.o \
|
||||
deps/libui/unix/colorbutton.o \
|
||||
deps/libui/unix/combobox.o \
|
||||
deps/libui/unix/control.o \
|
||||
deps/libui/unix/datetimepicker.o \
|
||||
deps/libui/unix/debug.o \
|
||||
deps/libui/unix/draw.o \
|
||||
deps/libui/unix/drawmatrix.o \
|
||||
deps/libui/unix/drawpath.o \
|
||||
deps/libui/unix/drawtext.o \
|
||||
deps/libui/unix/editablecombo.o \
|
||||
deps/libui/unix/entry.o \
|
||||
deps/libui/unix/fontbutton.o \
|
||||
deps/libui/unix/form.o \
|
||||
deps/libui/unix/future.o \
|
||||
deps/libui/unix/graphemes.o \
|
||||
deps/libui/unix/grid.o \
|
||||
deps/libui/unix/group.o \
|
||||
deps/libui/unix/image.o \
|
||||
deps/libui/unix/label.o \
|
||||
deps/libui/unix/main.o \
|
||||
deps/libui/unix/menu.o \
|
||||
deps/libui/unix/multilineentry.o \
|
||||
deps/libui/unix/progressbar.o \
|
||||
deps/libui/unix/radiobuttons.o \
|
||||
deps/libui/unix/separator.o \
|
||||
deps/libui/unix/slider.o \
|
||||
deps/libui/unix/spinbox.o \
|
||||
deps/libui/unix/stddialogs.o \
|
||||
deps/libui/unix/tab.o \
|
||||
deps/libui/unix/text.o \
|
||||
deps/libui/unix/util.o \
|
||||
deps/libui/unix/window.o
|
||||
endif
|
||||
endif
|
||||
|
||||
OBJ += deps/libui/common/areaevents.o \
|
||||
deps/libui/common/control.o \
|
||||
deps/libui/common/debug.o \
|
||||
deps/libui/common/matrix.o \
|
||||
deps/libui/common/shouldquit.o \
|
||||
deps/libui/common/userbugs.o
|
||||
|
||||
OBJ += deps/libui/libui_main.o
|
||||
endif
|
||||
endif
|
||||
|
||||
# Miscellaneous
|
||||
@ -741,11 +664,16 @@ ifeq ($(HW_CONTEXT_MENU_DRIVERS), 1)
|
||||
ifeq ($(HAVE_XMB),)
|
||||
HAVE_XMB = 1
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_STRIPES),)
|
||||
HAVE_STRIPES = 1
|
||||
endif
|
||||
else
|
||||
HAVE_ZARCH ?= 0
|
||||
HAVE_MATERIALUI ?= 0
|
||||
#HAVE_NUKLEAR ?= 0
|
||||
HAVE_XMB ?= 0
|
||||
HAVE_STRIPES ?= 0
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_RGUI), 1)
|
||||
@ -777,6 +705,12 @@ ifeq ($(HAVE_XMB), 1)
|
||||
HAVE_MENU_COMMON = 1
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_STRIPES), 1)
|
||||
OBJ += menu/drivers/stripes.o
|
||||
DEFINES += -DHAVE_STRIPES
|
||||
HAVE_MENU_COMMON = 1
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_LAKKA), 1)
|
||||
DEFINES += -DHAVE_LAKKA
|
||||
endif
|
||||
@ -896,6 +830,9 @@ endif
|
||||
ifeq ($(HAVE_WAYLAND), 1)
|
||||
OBJ += gfx/drivers_context/wayland_ctx.o \
|
||||
input/drivers/wayland_input.o
|
||||
ifeq ($(HAVE_EGL), 1)
|
||||
LIBS += $(EGL_LIBS)
|
||||
endif
|
||||
DEFINES += $(WAYLAND_CFLAGS) $(WAYLAND_CURSOR_CFLAGS)
|
||||
LIBS += $(WAYLAND_LIBS) $(WAYLAND_CURSOR_LIBS)
|
||||
endif
|
||||
@ -1044,6 +981,18 @@ ifeq ($(HAVE_CACA), 1)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_SIXEL), 1)
|
||||
DEFINES += -DHAVE_SIXEL
|
||||
CFLAGS += -I/usr/include/sixel
|
||||
OBJ += gfx/drivers/sixel_gfx.o gfx/drivers_font/sixel_font.o \
|
||||
gfx/drivers_context/sixel_ctx.o
|
||||
LIBS += -lsixel
|
||||
|
||||
ifeq ($(HAVE_MENU_COMMON), 1)
|
||||
OBJ += menu/drivers_display/menu_display_sixel.o
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_PLAIN_DRM), 1)
|
||||
OBJ += gfx/drivers/drm_gfx.o
|
||||
CFLAGS += -I/usr/include/libdrm
|
||||
@ -1105,6 +1054,20 @@ endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_METAL), 1)
|
||||
DEFINES += -DHAVE_METAL
|
||||
OBJ += gfx/common/metal/Context.o \
|
||||
gfx/common/metal/Filter.o \
|
||||
gfx/common/metal/RendererCommon.o \
|
||||
gfx/common/metal/View.o \
|
||||
gfx/common/metal/TexturedView.o \
|
||||
gfx/common/metal/MenuDisplay.o \
|
||||
gfx/common/metal_common.o \
|
||||
gfx/drivers/metal.o \
|
||||
menu/drivers_display/menu_display_metal.o \
|
||||
gfx/drivers_font/metal_raster_font.o
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_MPV), 1)
|
||||
OBJ += cores/libretro-mpv/mpv-libretro.o
|
||||
DEFINES += -I$(DEPS_DIR) -DHAVE_MPV
|
||||
@ -1188,7 +1151,7 @@ ifeq ($(HAVE_VULKAN), 1)
|
||||
CXXFLAGS += -fpermissive
|
||||
endif
|
||||
|
||||
CXXFLAGS += -Wno-switch -Wno-sign-compare -fno-strict-aliasing -Wno-maybe-uninitialized -Wno-reorder -Wno-parentheses
|
||||
CXXFLAGS += -Wno-switch -Wno-sign-compare -fno-strict-aliasing -Wno-reorder -Wno-parentheses
|
||||
|
||||
OBJ += gfx/drivers/vulkan.o \
|
||||
gfx/common/vulkan_common.o \
|
||||
@ -1642,6 +1605,7 @@ ifeq ($(HAVE_NETWORKING), 1)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_DISCORD), 1)
|
||||
NEED_CXX_LINKER = 1
|
||||
DEFINES += -DHAVE_DISCORD
|
||||
DEFINES += -Ideps/discord-rpc/include/ -Ideps/discord-rpc/thirdparty/rapidjson-1.1.0/include/
|
||||
OBJ += deps/discord-rpc/src/discord_rpc.o \
|
||||
|
62
Makefile.nintendoc
Normal file
62
Makefile.nintendoc
Normal file
@ -0,0 +1,62 @@
|
||||
# Hakchi version added to ease confusion amongst Hakchi community due to messy past...
|
||||
#
|
||||
# Make sure you have readelf installed (sudo apt-get install readelf) to patch the binary
|
||||
# Might not be needed for your build but it's a useful tool and for safety it should be
|
||||
# run anyway to ensure the SDL2 link isn't broken...
|
||||
|
||||
HAKCHI_VER := Hakchi_Retroarch_Neo_v1_7_3b
|
||||
HAKCHI_NAME := "Hakchi RetroArch 'Neo' v1.7.3b
|
||||
MOD_CREATOR := TheOtherGuys
|
||||
MOD_CATEGORY := RetroArch
|
||||
|
||||
HAKCHI_DIR := hakchi
|
||||
TARGET := retroarch
|
||||
GIT_COMMIT := $(shell echo "`git rev-parse --short HEAD``git diff-index --quiet HEAD -- || echo '-dirty'`")
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
retroarch:
|
||||
readelf -v #Check if you have readelf installed... (sudo apt-get install readelf)
|
||||
./configure --host=arm-linux-gnueabihf --disable-freetype --enable-opengles --enable-udev --enable-alsa --enable-neon --enable-floathard
|
||||
make -f Makefile -j HAVE_HAKCHI=1
|
||||
patchelf --replace-needed libSDL2-2.0.so.0 libSDL2.so retroarch #libSDL2-2.0.so.0 sym link doesn't exist on native build. Just patch the binary...
|
||||
#/usr/bin/arm-linux-gnueabihf-strip retroarch
|
||||
rm -f $(HAKCHI_DIR)/bin/retroarch
|
||||
cp retroarch $(HAKCHI_DIR)/bin/retroarch
|
||||
echo $$(echo "Built by: " $$USER @ $$(date) \\\\\\ Git Commit: $(GIT_COMMIT)) > $(HAKCHI_DIR)/etc/libretro/retroarch_version
|
||||
cp $(HAKCHI_DIR)/readme.md $(HAKCHI_DIR)/readme_COPY.md
|
||||
printf "%s\n" \
|
||||
"---" \
|
||||
"Name: $(HAKCHI_VER)" \
|
||||
"Creator: $(MOD_CREATOR)" \
|
||||
"Category: $(MOD_CATEGORY)" \
|
||||
"Version: $(HAKCHI_VER)" \
|
||||
"Built on: $(shell date)" \
|
||||
"Git commit: $(GIT_COMMIT)" \
|
||||
"---" > $(HAKCHI_DIR)/readme.md
|
||||
cat $(HAKCHI_DIR)/readme_COPY.md >> $(HAKCHI_DIR)/readme.md
|
||||
rm $(HAKCHI_DIR)/readme_COPY.md
|
||||
@echo "** BUILDING HAKCHI $(HAKCHI_VER) HMOD PACKAGE **"
|
||||
cd $(HAKCHI_DIR)/; tar -czvf "$(HAKCHI_VER).hmod" *
|
||||
mv $(HAKCHI_DIR)/$(HAKCHI_VER).hmod .
|
||||
@echo "** BUILT HAKCHI $(HAKCHI_VER) HMOD PACKAGE **"
|
||||
clean:
|
||||
rm -f *.o
|
||||
rm -f audio/*.o
|
||||
rm -f conf/*.o
|
||||
rm -f gfx/*.o
|
||||
rm -f gfx/drivers_font/*.o
|
||||
rm -f gfx/drivers_font_renderer/*.o
|
||||
rm -f gfx/drivers_context/*.o
|
||||
rm -f gfx/py_state/*.o
|
||||
rm -f compat/*.o
|
||||
rm -f record/*.o
|
||||
rm -f input/*.o
|
||||
rm -f tools/*.o
|
||||
rm -f $(BINDIR)/retroarch
|
||||
rm -f $(BINDIR)/retroarch-joyconfig
|
||||
rm -f $(PNDDIR)/readme.html
|
||||
rm -f retroarch
|
||||
rm -f $(HAKCHI_DIR)/bin/retroarch
|
||||
rm -f $(HAKCHI_VER).hmod
|
||||
rm -f $(HAKCHI_DIR)/etc/libretro/retroarch_version
|
@ -13,7 +13,7 @@ TARGET = retroarchpsp
|
||||
ifeq ($(DEBUG), 1)
|
||||
OPTIMIZE_LV := -O0 -g
|
||||
else
|
||||
OPTIMIZE_LV := -O2
|
||||
OPTIMIZE_LV := -O3
|
||||
endif
|
||||
|
||||
ifeq ($(WHOLE_ARCHIVE_LINK), 1)
|
||||
@ -22,7 +22,7 @@ ifeq ($(WHOLE_ARCHIVE_LINK), 1)
|
||||
endif
|
||||
|
||||
INCDIR = deps deps/stb deps/libz deps/7zip deps/pthreads deps/pthreads/platform/psp deps/pthreads/platform/helper libretro-common/include
|
||||
CFLAGS = $(OPTIMIZE_LV) -G0 -std=gnu99 -ffast-math
|
||||
CFLAGS = $(OPTIMIZE_LV) -G0 -std=gnu99 -ffast-math -fsingle-precision-constant
|
||||
ASFLAGS = $(CFLAGS)
|
||||
|
||||
RARCH_DEFINES = -DPSP -D_MIPS_ARCH_ALLEGREX -DHAVE_LANGEXTRA -DHAVE_ZLIB -DHAVE_RPNG -DHAVE_RJPEG -DWANT_ZLIB -DHAVE_GRIFFIN=1 -DRARCH_INTERNAL -DRARCH_CONSOLE -DHAVE_MENU -DHAVE_RGUI -DHAVE_FILTERS_BUILTIN -DHAVE_7ZIP -DHAVE_CC_RESAMPLER
|
||||
|
@ -888,7 +888,7 @@ void audio_driver_monitor_adjust_system_rates(void)
|
||||
timing_skew = fabs(1.0f - info->fps / video_refresh_rate);
|
||||
audio_driver_input = info->sample_rate;
|
||||
|
||||
if (timing_skew <= max_timing_skew)
|
||||
if (timing_skew <= max_timing_skew && !settings->bools.vrr_runloop_enable)
|
||||
audio_driver_input *= (video_refresh_rate / info->fps);
|
||||
|
||||
RARCH_LOG("[Audio]: Set audio input rate to: %.2f Hz.\n",
|
||||
|
@ -32,7 +32,9 @@
|
||||
#include <retro_inline.h>
|
||||
#include <retro_miscellaneous.h>
|
||||
#include <retro_timers.h>
|
||||
#ifdef HAVE_THREADS
|
||||
#include <rthreads/rthreads.h>
|
||||
#endif
|
||||
#include <queues/fifo_queue.h>
|
||||
|
||||
#include "../audio_driver.h"
|
||||
@ -59,7 +61,11 @@ typedef struct dsound
|
||||
CRITICAL_SECTION crit;
|
||||
|
||||
HANDLE event;
|
||||
#ifdef HAVE_THREADS
|
||||
sthread_t *thread;
|
||||
#else
|
||||
HANDLE thread;
|
||||
#endif
|
||||
|
||||
unsigned buffer_size;
|
||||
|
||||
@ -137,7 +143,11 @@ static INLINE void release_region(dsound_t *ds, const struct audio_lock *region)
|
||||
region->size1, region->chunk2, region->size2);
|
||||
}
|
||||
|
||||
#ifdef HAVE_THREADS
|
||||
static void dsound_thread(void *data)
|
||||
#else
|
||||
static DWORD CALLBACK dsound_thread(PVOID data)
|
||||
#endif
|
||||
{
|
||||
DWORD write_ptr;
|
||||
dsound_t *ds = (dsound_t*)data;
|
||||
@ -219,7 +229,13 @@ static void dsound_stop_thread(dsound_t *ds)
|
||||
|
||||
ds->thread_alive = false;
|
||||
|
||||
#ifdef HAVE_THREADS
|
||||
sthread_join(ds->thread);
|
||||
#else
|
||||
WaitForSingleObject(ds->thread, INFINITE);
|
||||
CloseHandle(ds->thread);
|
||||
#endif
|
||||
|
||||
ds->thread = NULL;
|
||||
}
|
||||
|
||||
@ -228,7 +244,12 @@ static bool dsound_start_thread(dsound_t *ds)
|
||||
if (!ds->thread)
|
||||
{
|
||||
ds->thread_alive = true;
|
||||
ds->thread = sthread_create(dsound_thread, ds);
|
||||
|
||||
#ifdef HAVE_THREADS
|
||||
ds->thread = sthread_create(dsound_thread, ds);
|
||||
#else
|
||||
ds->thread = CreateThread(NULL, 0, dsound_thread, ds, 0, NULL);
|
||||
#endif
|
||||
if (!ds->thread)
|
||||
return false;
|
||||
}
|
||||
@ -261,7 +282,12 @@ static void dsound_free(void *data)
|
||||
if (ds->thread)
|
||||
{
|
||||
ds->thread_alive = false;
|
||||
#ifdef HAVE_THREADS
|
||||
sthread_join(ds->thread);
|
||||
#else
|
||||
WaitForSingleObject(ds->thread, INFINITE);
|
||||
CloseHandle(ds->thread);
|
||||
#endif
|
||||
}
|
||||
|
||||
DeleteCriticalSection(&ds->crit);
|
||||
|
@ -84,7 +84,7 @@ static void* ax_audio_init(const char* device, unsigned rate, unsigned latency,
|
||||
u16 setup_buf[0x30] = {0};
|
||||
setup_buf[0x25] = 2; /* we request 2 channels */
|
||||
AXInitParams init = {AX_INIT_RENDERER_48KHZ, 0, 0};
|
||||
AXVoiceVeData ve = {0xF000, 0};
|
||||
AXVoiceVeData ve = {0x8000, 0};
|
||||
ax_audio_t* ax = (ax_audio_t*)calloc(1, sizeof(ax_audio_t));
|
||||
|
||||
if (!ax)
|
||||
|
@ -1674,7 +1674,7 @@ static void cheevos_test_cheevo_set(const cheevoset_t *set)
|
||||
shotname[sizeof(shotname) - 1] = '\0';
|
||||
|
||||
if (take_screenshot(shotname, true,
|
||||
video_driver_cached_frame_has_valid_framebuffer()))
|
||||
video_driver_cached_frame_has_valid_framebuffer(), false, true))
|
||||
CHEEVOS_LOG("[CHEEVOS]: got a screenshot for cheevo %u\n", cheevo->id);
|
||||
else
|
||||
CHEEVOS_LOG("[CHEEVOS]: failed to get screenshot for cheevo %u\n", cheevo->id);
|
||||
@ -2080,7 +2080,7 @@ void cheevos_populate_menu(void *data)
|
||||
cheevo_t *cheevo = cheevos_locals.core.cheevos;
|
||||
end = cheevo + cheevos_locals.core.count;
|
||||
|
||||
if(settings->bools.cheevos_enable && settings->bools.cheevos_hardcore_mode_enable
|
||||
if(settings->bools.cheevos_enable && settings->bools.cheevos_hardcore_mode_enable
|
||||
&& cheevos_loaded)
|
||||
{
|
||||
if (!cheevos_hardcore_paused)
|
||||
@ -3389,9 +3389,9 @@ found:
|
||||
|
||||
/* Save token to config and clear pass on success */
|
||||
*coro->settings->arrays.cheevos_password = '\0';
|
||||
strncpy(
|
||||
strlcpy(
|
||||
coro->settings->arrays.cheevos_token,
|
||||
cheevos_locals.token, sizeof(cheevos_locals.token)
|
||||
cheevos_locals.token, sizeof(coro->settings->arrays.cheevos_token)
|
||||
);
|
||||
CORO_RET();
|
||||
}
|
||||
|
163
command.c
163
command.c
@ -1,6 +1,6 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2011-2017 - Daniel De Matteis
|
||||
* Copyright (C) 2015-2017 - Andrés Suárez
|
||||
* Copyright (C) 2015-2017 - Andres Suarez
|
||||
* Copyright (C) 2016-2017 - Brad Parker
|
||||
*
|
||||
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
||||
@ -93,6 +93,7 @@
|
||||
#include "retroarch.h"
|
||||
#include "configuration.h"
|
||||
#include "input/input_remapping.h"
|
||||
#include "version.h"
|
||||
|
||||
#define DEFAULT_NETWORK_CMD_PORT 55355
|
||||
#define STDIN_BUF_SIZE 4096
|
||||
@ -130,6 +131,8 @@ struct command
|
||||
#endif
|
||||
};
|
||||
|
||||
static bool command_version(const char *arg);
|
||||
|
||||
#if defined(HAVE_COMMAND) && defined(HAVE_CHEEVOS)
|
||||
static bool command_read_ram(const char *arg);
|
||||
static bool command_write_ram(const char *arg);
|
||||
@ -137,6 +140,7 @@ static bool command_write_ram(const char *arg);
|
||||
|
||||
static const struct cmd_action_map action_map[] = {
|
||||
{ "SET_SHADER", command_set_shader, "<shader path>" },
|
||||
{ "VERSION", command_version, "No argument"},
|
||||
#if defined(HAVE_COMMAND) && defined(HAVE_CHEEVOS)
|
||||
{ "READ_CORE_RAM", command_read_ram, "<address> <number of bytes>" },
|
||||
{ "WRITE_CORE_RAM", command_write_ram, "<address> <byte1> <byte2> ..." },
|
||||
@ -194,8 +198,7 @@ static struct sockaddr_storage lastcmd_net_source;
|
||||
static socklen_t lastcmd_net_source_len;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CHEEVOS
|
||||
#if defined(HAVE_STDIN_CMD) || defined(HAVE_NETWORK_CMD) && defined(HAVE_NETWORKING)
|
||||
#if defined(HAVE_CHEEVOS) && (defined(HAVE_STDIN_CMD) || defined(HAVE_NETWORK_CMD) && defined(HAVE_NETWORKING))
|
||||
static bool command_reply(const char * data, size_t len)
|
||||
{
|
||||
switch (lastcmd_source)
|
||||
@ -222,15 +225,16 @@ static bool command_reply(const char * data, size_t len)
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
bool command_set_shader(const char *arg)
|
||||
{
|
||||
char msg[256];
|
||||
bool is_preset = false;
|
||||
struct video_shader *shader = menu_shader_get();
|
||||
enum rarch_shader_type type = video_shader_get_type_from_ext(
|
||||
path_get_extension(arg), &is_preset);
|
||||
#ifdef HAVE_MENU
|
||||
struct video_shader *shader = menu_shader_get();
|
||||
#endif
|
||||
|
||||
if (type == RARCH_SHADER_NONE)
|
||||
return false;
|
||||
@ -242,32 +246,54 @@ bool command_set_shader(const char *arg)
|
||||
arg);
|
||||
|
||||
retroarch_set_shader_preset(arg);
|
||||
#ifdef HAVE_MENU
|
||||
return menu_shader_manager_set_preset(shader, type, arg);
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool command_version(const char* arg)
|
||||
{
|
||||
char reply[256] = {0};
|
||||
|
||||
sprintf(reply, "%s\n", PACKAGE_VERSION);
|
||||
#if defined(HAVE_CHEEVOS) && (defined(HAVE_STDIN_CMD) || defined(HAVE_NETWORK_CMD) && defined(HAVE_NETWORKING))
|
||||
command_reply(reply, strlen(reply));
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(HAVE_COMMAND) && defined(HAVE_CHEEVOS)
|
||||
#define SMY_CMD_STR "READ_CORE_RAM"
|
||||
static bool command_read_ram(const char *arg)
|
||||
{
|
||||
cheevos_var_t var;
|
||||
unsigned i;
|
||||
char reply[256] = {0};
|
||||
const uint8_t * data = NULL;
|
||||
char *reply_at = NULL;
|
||||
char *reply = NULL;
|
||||
const uint8_t * data = NULL;
|
||||
char *reply_at = NULL;
|
||||
unsigned int nbytes = 0;
|
||||
unsigned int alloc_size = 0;
|
||||
int addr = -1;
|
||||
|
||||
reply[0] = '\0';
|
||||
if (sscanf(arg, "%x %d", &addr, &nbytes) != 2)
|
||||
return true;
|
||||
|
||||
strlcpy(reply, "READ_CORE_RAM ", sizeof(reply));
|
||||
reply_at = reply + strlen("READ_CORE_RAM ");
|
||||
strlcpy(reply_at, arg, sizeof(reply)-strlen(reply));
|
||||
alloc_size = 40 + nbytes * 3; /* We alloc more than needed, saving 20 bytes is not really relevant */
|
||||
reply = (char*) malloc(alloc_size);
|
||||
reply[0] = '\0';
|
||||
reply_at = reply + sprintf(reply, SMY_CMD_STR " %x", addr);
|
||||
|
||||
var.value = addr;
|
||||
|
||||
var.value = strtoul(reply_at, (char**)&reply_at, 16);
|
||||
cheevos_var_patch_addr(&var, cheevos_get_console());
|
||||
data = cheevos_var_get_memory(&var);
|
||||
|
||||
data = cheevos_var_get_memory(&var);
|
||||
|
||||
if (data)
|
||||
{
|
||||
unsigned nbytes = strtol(reply_at, NULL, 10);
|
||||
|
||||
for (i=0;i<nbytes;i++)
|
||||
sprintf(reply_at+3*i, " %.2X", data[i]);
|
||||
reply_at[3*nbytes] = '\n';
|
||||
@ -278,9 +304,11 @@ static bool command_read_ram(const char *arg)
|
||||
strlcpy(reply_at, " -1\n", sizeof(reply)-strlen(reply));
|
||||
command_reply(reply, reply_at+strlen(" -1\n") - reply);
|
||||
}
|
||||
free(reply);
|
||||
|
||||
return true;
|
||||
}
|
||||
#undef SMY_CMD_STR
|
||||
|
||||
static bool command_write_ram(const char *arg)
|
||||
{
|
||||
@ -307,6 +335,7 @@ static bool command_write_ram(const char *arg)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_COMMAND
|
||||
static bool command_get_arg(const char *tok,
|
||||
const char **arg, unsigned *index)
|
||||
{
|
||||
@ -332,7 +361,7 @@ static bool command_get_arg(const char *tok,
|
||||
if (str == tok)
|
||||
{
|
||||
const char *argument = str + strlen(action_map[i].str);
|
||||
if (*argument != ' ')
|
||||
if (*argument != ' ' && *argument != '\0')
|
||||
return false;
|
||||
|
||||
if (arg)
|
||||
@ -347,6 +376,7 @@ static bool command_get_arg(const char *tok,
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_NETWORKING) && defined(HAVE_NETWORK_CMD) && defined(HAVE_COMMAND)
|
||||
static bool command_network_init(command_t *handle, uint16_t port)
|
||||
@ -527,10 +557,10 @@ bool command_network_send(const char *cmd_)
|
||||
}
|
||||
free(command);
|
||||
|
||||
return ret;
|
||||
#else
|
||||
return false;
|
||||
if (ret)
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef HAVE_STDIN_CMD
|
||||
@ -1036,7 +1066,7 @@ static void command_event_init_controllers(void)
|
||||
break;
|
||||
}
|
||||
|
||||
if (set_controller && i < info->ports.size)
|
||||
if (set_controller && info && i < info->ports.size)
|
||||
{
|
||||
pad.device = device;
|
||||
pad.port = i;
|
||||
@ -1051,8 +1081,11 @@ static void command_event_deinit_core(bool reinit)
|
||||
cheevos_unload();
|
||||
#endif
|
||||
|
||||
RARCH_LOG("Unloading game..\n");
|
||||
core_unload_game();
|
||||
RARCH_LOG("Unloading core..\n");
|
||||
core_unload();
|
||||
RARCH_LOG("Unloading core symbols..\n");
|
||||
core_uninit_symbols();
|
||||
|
||||
if (reinit)
|
||||
@ -1065,7 +1098,9 @@ static void command_event_deinit_core(bool reinit)
|
||||
|
||||
static void command_event_init_cheats(void)
|
||||
{
|
||||
bool allow_cheats = true;
|
||||
settings_t *settings = config_get_ptr();
|
||||
bool allow_cheats = true;
|
||||
|
||||
#ifdef HAVE_NETWORKING
|
||||
allow_cheats &= !netplay_driver_ctl(
|
||||
RARCH_NETPLAY_CTL_IS_DATA_INITED, NULL);
|
||||
@ -1075,7 +1110,12 @@ static void command_event_init_cheats(void)
|
||||
if (!allow_cheats)
|
||||
return;
|
||||
|
||||
/* TODO/FIXME - add some stuff here. */
|
||||
cheat_manager_alloc_if_empty() ;
|
||||
cheat_manager_load_game_specific_cheats() ;
|
||||
|
||||
|
||||
if (settings != NULL && settings->bools.apply_cheats_after_load)
|
||||
cheat_manager_apply_cheats();
|
||||
}
|
||||
|
||||
static void command_event_load_auto_state(void)
|
||||
@ -1303,8 +1343,8 @@ static void command_event_restore_default_shader_preset(void)
|
||||
|
||||
static void command_event_restore_remaps(void)
|
||||
{
|
||||
if (rarch_ctl(RARCH_CTL_IS_REMAPS_CORE_ACTIVE, NULL) ||
|
||||
rarch_ctl(RARCH_CTL_IS_REMAPS_CONTENT_DIR_ACTIVE, NULL) ||
|
||||
if (rarch_ctl(RARCH_CTL_IS_REMAPS_CORE_ACTIVE, NULL) ||
|
||||
rarch_ctl(RARCH_CTL_IS_REMAPS_CONTENT_DIR_ACTIVE, NULL) ||
|
||||
rarch_ctl(RARCH_CTL_IS_REMAPS_GAME_ACTIVE, NULL))
|
||||
input_remapping_set_defaults(true);
|
||||
}
|
||||
@ -1395,7 +1435,6 @@ static bool command_event_save_config(
|
||||
static bool command_event_save_core_config(void)
|
||||
{
|
||||
char msg[128];
|
||||
bool ret = false;
|
||||
bool found_path = false;
|
||||
bool overrides_active = false;
|
||||
const char *core_path = NULL;
|
||||
@ -1502,7 +1541,7 @@ static bool command_event_save_core_config(void)
|
||||
free(config_dir);
|
||||
free(config_name);
|
||||
free(config_path);
|
||||
return ret;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1746,7 +1785,9 @@ void command_playlist_update_write(
|
||||
**/
|
||||
bool command_event(enum event_command cmd, void *data)
|
||||
{
|
||||
#ifdef HAVE_DISCORD
|
||||
static bool discord_inited = false;
|
||||
#endif
|
||||
bool boolean = false;
|
||||
|
||||
switch (cmd)
|
||||
@ -1835,14 +1876,21 @@ bool command_event(enum event_command cmd, void *data)
|
||||
if (cheevos_hardcore_active)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
return command_event_main_state(cmd);
|
||||
if (!command_event_main_state(cmd))
|
||||
return false;
|
||||
break;
|
||||
case CMD_EVENT_UNDO_LOAD_STATE:
|
||||
return command_event_main_state(cmd);
|
||||
if (!command_event_main_state(cmd))
|
||||
return false;
|
||||
break;
|
||||
case CMD_EVENT_UNDO_SAVE_STATE:
|
||||
return command_event_main_state(cmd);
|
||||
if (!command_event_main_state(cmd))
|
||||
return false;
|
||||
break;
|
||||
case CMD_EVENT_RESIZE_WINDOWED_SCALE:
|
||||
return command_event_resize_windowed_scale();
|
||||
if (!command_event_resize_windowed_scale())
|
||||
return false;
|
||||
break;
|
||||
case CMD_EVENT_MENU_TOGGLE:
|
||||
#ifdef HAVE_MENU
|
||||
if (menu_driver_is_alive())
|
||||
@ -1883,7 +1931,9 @@ bool command_event(enum event_command cmd, void *data)
|
||||
configuration_set_int(settings, settings->ints.state_slot, new_state_slot);
|
||||
}
|
||||
}
|
||||
return command_event_main_state(cmd);
|
||||
if (!command_event_main_state(cmd))
|
||||
return false;
|
||||
break;
|
||||
case CMD_EVENT_SAVE_STATE_DECREMENT:
|
||||
{
|
||||
settings_t *settings = config_get_ptr();
|
||||
@ -1904,7 +1954,7 @@ bool command_event(enum event_command cmd, void *data)
|
||||
break;
|
||||
case CMD_EVENT_TAKE_SCREENSHOT:
|
||||
if (!take_screenshot(path_get(RARCH_PATH_BASENAME), false,
|
||||
video_driver_cached_frame_has_valid_framebuffer()))
|
||||
video_driver_cached_frame_has_valid_framebuffer(), false, true))
|
||||
return false;
|
||||
break;
|
||||
case CMD_EVENT_UNLOAD_CORE:
|
||||
@ -1933,7 +1983,9 @@ bool command_event(enum event_command cmd, void *data)
|
||||
}
|
||||
break;
|
||||
case CMD_EVENT_QUIT:
|
||||
return retroarch_main_quit();
|
||||
if (!retroarch_main_quit())
|
||||
return false;
|
||||
break;
|
||||
case CMD_EVENT_CHEEVOS_HARDCORE_MODE_TOGGLE:
|
||||
#ifdef HAVE_CHEEVOS
|
||||
cheevos_toggle_hardcore_mode();
|
||||
@ -1997,7 +2049,7 @@ TODO: Add a setting for these tweaks */
|
||||
if (!netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL))
|
||||
#endif
|
||||
{
|
||||
state_manager_event_init((unsigned)settings->rewind_buffer_size);
|
||||
state_manager_event_init((unsigned)settings->sizes.rewind_buffer_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2043,9 +2095,13 @@ TODO: Add a setting for these tweaks */
|
||||
break;
|
||||
case CMD_EVENT_AUDIO_STOP:
|
||||
midi_driver_set_all_sounds_off();
|
||||
return audio_driver_stop();
|
||||
if (!audio_driver_stop())
|
||||
return false;
|
||||
break;
|
||||
case CMD_EVENT_AUDIO_START:
|
||||
return audio_driver_start(rarch_ctl(RARCH_CTL_IS_SHUTDOWN, NULL));
|
||||
if (!audio_driver_start(rarch_ctl(RARCH_CTL_IS_SHUTDOWN, NULL)))
|
||||
return false;
|
||||
break;
|
||||
case CMD_EVENT_AUDIO_MUTE_TOGGLE:
|
||||
{
|
||||
bool audio_mute_enable = *(audio_get_bool_ptr(AUDIO_ACTION_MUTE_ENABLE));
|
||||
@ -2205,6 +2261,7 @@ TODO: Add a setting for these tweaks */
|
||||
break;
|
||||
case CMD_EVENT_CORE_INFO_DEINIT:
|
||||
core_info_deinit_list();
|
||||
core_info_free_current_core();
|
||||
break;
|
||||
case CMD_EVENT_CORE_INFO_INIT:
|
||||
{
|
||||
@ -2391,10 +2448,14 @@ TODO: Add a setting for these tweaks */
|
||||
if (!command_event_save_core_config())
|
||||
return false;
|
||||
break;
|
||||
case CMD_EVENT_SHADER_PRESET_LOADED:
|
||||
ui_companion_event_command(cmd);
|
||||
break;
|
||||
case CMD_EVENT_SHADERS_APPLY_CHANGES:
|
||||
#ifdef HAVE_MENU
|
||||
menu_shader_manager_apply_changes();
|
||||
#endif
|
||||
ui_companion_event_command(cmd);
|
||||
break;
|
||||
case CMD_EVENT_PAUSE_CHECKS:
|
||||
{
|
||||
@ -2416,6 +2477,13 @@ TODO: Add a setting for these tweaks */
|
||||
|
||||
if (!is_idle)
|
||||
video_driver_cached_frame();
|
||||
|
||||
#ifdef HAVE_DISCORD
|
||||
discord_userdata_t userdata;
|
||||
userdata.status = DISCORD_PRESENCE_GAME_PAUSED;
|
||||
|
||||
command_event(CMD_EVENT_DISCORD_UPDATE, &userdata);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -2630,14 +2698,13 @@ TODO: Add a setting for these tweaks */
|
||||
command_event(CMD_EVENT_REMOTE_DEINIT, NULL);
|
||||
input_driver_init_remote();
|
||||
break;
|
||||
|
||||
case CMD_EVENT_MAPPER_DEINIT:
|
||||
input_driver_deinit_mapper();
|
||||
break;
|
||||
case CMD_EVENT_MAPPER_INIT:
|
||||
command_event(CMD_EVENT_MAPPER_DEINIT, NULL);
|
||||
input_driver_init_mapper();
|
||||
break;
|
||||
break;
|
||||
case CMD_EVENT_LOG_FILE_DEINIT:
|
||||
retro_main_log_file_deinit();
|
||||
break;
|
||||
@ -2646,8 +2713,10 @@ TODO: Add a setting for these tweaks */
|
||||
const char *path = (const char*)data;
|
||||
if (string_is_empty(path))
|
||||
return false;
|
||||
return command_event_disk_control_append_image(path);
|
||||
if (!command_event_disk_control_append_image(path))
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case CMD_EVENT_DISK_EJECT_TOGGLE:
|
||||
{
|
||||
rarch_system_info_t *info = runloop_get_system_info();
|
||||
@ -2754,10 +2823,8 @@ TODO: Add a setting for these tweaks */
|
||||
}
|
||||
break;
|
||||
case CMD_EVENT_UI_COMPANION_TOGGLE:
|
||||
{
|
||||
ui_companion_driver_toggle(true);
|
||||
break;
|
||||
}
|
||||
ui_companion_driver_toggle(true);
|
||||
break;
|
||||
case CMD_EVENT_GAME_FOCUS_TOGGLE:
|
||||
{
|
||||
static bool game_focus_state = false;
|
||||
@ -2826,12 +2893,6 @@ TODO: Add a setting for these tweaks */
|
||||
case CMD_EVENT_RESTORE_DEFAULT_SHADER_PRESET:
|
||||
command_event_restore_default_shader_preset();
|
||||
break;
|
||||
case CMD_EVENT_LIBUI_TEST:
|
||||
#if HAVE_LIBUI
|
||||
extern int libui_main(void);
|
||||
libui_main();
|
||||
#endif
|
||||
break;
|
||||
case CMD_EVENT_DISCORD_INIT:
|
||||
#ifdef HAVE_DISCORD
|
||||
{
|
||||
|
@ -158,6 +158,8 @@ enum event_command
|
||||
CMD_EVENT_MENU_REFRESH,
|
||||
/* Applies shader changes. */
|
||||
CMD_EVENT_SHADERS_APPLY_CHANGES,
|
||||
/* A new shader preset has been loaded */
|
||||
CMD_EVENT_SHADER_PRESET_LOADED,
|
||||
/* Initializes shader directory. */
|
||||
CMD_EVENT_SHADER_DIR_INIT,
|
||||
/* Deinitializes shader directory. */
|
||||
@ -233,8 +235,7 @@ enum event_command
|
||||
CMD_EVENT_RESTORE_DEFAULT_SHADER_PRESET,
|
||||
CMD_EVENT_DISCORD_INIT,
|
||||
CMD_EVENT_DISCORD_DEINIT,
|
||||
CMD_EVENT_DISCORD_UPDATE,
|
||||
CMD_EVENT_LIBUI_TEST
|
||||
CMD_EVENT_DISCORD_UPDATE
|
||||
};
|
||||
|
||||
bool command_set_shader(const char *arg);
|
||||
|
28
config.def.h
28
config.def.h
@ -248,6 +248,12 @@ static const bool overlay_hide_in_menu = true;
|
||||
|
||||
static const bool display_keyboard_overlay = false;
|
||||
|
||||
#ifdef HAKCHI
|
||||
static const float default_input_overlay_opacity = 0.5f;
|
||||
#else
|
||||
static const float default_input_overlay_opacity = 0.7f;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_MENU
|
||||
#include "menu/menu_driver.h"
|
||||
|
||||
@ -520,10 +526,19 @@ static const bool framecount_show = true;
|
||||
* depending on the save state buffer. */
|
||||
static const bool rewind_enable = false;
|
||||
|
||||
/* When set, any time a cheat is toggled it is immediately applied. */
|
||||
static const bool apply_cheats_after_toggle = false;
|
||||
|
||||
/* When set, all enabled cheats are auto-applied when a game is loaded. */
|
||||
static const bool apply_cheats_after_load = false;
|
||||
|
||||
/* The buffer size for the rewind buffer. This needs to be about
|
||||
* 15-20MB per minute. Very game dependant. */
|
||||
static const unsigned rewind_buffer_size = 20 << 20; /* 20MiB */
|
||||
|
||||
/* The amount of MB to increase/decrease the rewind_buffer_size when it is changed via the UI. */
|
||||
static const unsigned rewind_buffer_size_step = 10; /* 10MB */
|
||||
|
||||
/* How many frames to rewind at a time. */
|
||||
static const unsigned rewind_granularity = 1;
|
||||
|
||||
@ -598,6 +613,9 @@ static const float slowmotion_ratio = 3.0;
|
||||
/* Maximum fast forward ratio. */
|
||||
static const float fastforward_ratio = 0.0;
|
||||
|
||||
/* Enable runloop for variable refresh rate screens. Force x1 speed while handling fast forward too. */
|
||||
static const bool vrr_runloop_enable = false;
|
||||
|
||||
/* Run core logic one or more frames ahead then load the state back to reduce perceived input lag. */
|
||||
static const unsigned run_ahead_frames = 1;
|
||||
|
||||
@ -700,13 +718,19 @@ static const unsigned midi_volume = 100;
|
||||
/* Only applies to Android 7.0 (API 24) and up */
|
||||
static const bool sustained_performance_mode = false;
|
||||
|
||||
#if defined(ANDROID)
|
||||
#if defined(ANDROID_ARM)
|
||||
#if defined(HAKCHI)
|
||||
static char buildbot_server_url[] = "http://hakchicloud.com/Libretro_Cores/";
|
||||
#elif defined(ANDROID)
|
||||
#if defined(ANDROID_ARM_V7)
|
||||
static char buildbot_server_url[] = "http://buildbot.libretro.com/nightly/android/latest/armeabi-v7a/";
|
||||
#elif defined(ANDROID_ARM)
|
||||
static char buildbot_server_url[] = "http://buildbot.libretro.com/nightly/android/latest/armeabi/";
|
||||
#elif defined(ANDROID_AARCH64)
|
||||
static char buildbot_server_url[] = "http://buildbot.libretro.com/nightly/android/latest/arm64-v8a/";
|
||||
#elif defined(ANDROID_X86)
|
||||
static char buildbot_server_url[] = "http://buildbot.libretro.com/nightly/android/latest/x86/";
|
||||
#elif defined(ANDROID_X64)
|
||||
static char buildbot_server_url[] = "http://buildbot.libretro.com/nightly/android/latest/x86_64/";
|
||||
#else
|
||||
static char buildbot_server_url[] = "";
|
||||
#endif
|
||||
|
131
configuration.c
131
configuration.c
@ -92,6 +92,16 @@ struct config_uint_setting
|
||||
enum rarch_override_setting override;
|
||||
};
|
||||
|
||||
struct config_size_setting
|
||||
{
|
||||
const char *ident;
|
||||
size_t *ptr;
|
||||
bool def_enable;
|
||||
size_t def;
|
||||
bool handle;
|
||||
enum rarch_override_setting override;
|
||||
};
|
||||
|
||||
struct config_float_setting
|
||||
{
|
||||
const char *ident;
|
||||
@ -125,6 +135,7 @@ enum video_driver_enum
|
||||
{
|
||||
VIDEO_GL = 0,
|
||||
VIDEO_VULKAN,
|
||||
VIDEO_METAL,
|
||||
VIDEO_DRM,
|
||||
VIDEO_XVIDEO,
|
||||
VIDEO_SDL,
|
||||
@ -273,6 +284,7 @@ enum menu_driver_enum
|
||||
MENU_XUI,
|
||||
MENU_MATERIALUI,
|
||||
MENU_XMB,
|
||||
MENU_STRIPES,
|
||||
MENU_NUKLEAR,
|
||||
MENU_NULL
|
||||
};
|
||||
@ -291,6 +303,8 @@ enum midi_driver_enum
|
||||
|
||||
#if defined(HAVE_OPENGL) || defined(HAVE_OPENGLES) || defined(__CELLOS_LV2__)
|
||||
static enum video_driver_enum VIDEO_DEFAULT_DRIVER = VIDEO_GL;
|
||||
#elif defined(HAVE_METAL)
|
||||
static enum video_driver_enum VIDEO_DEFAULT_DRIVER = VIDEO_METAL;
|
||||
#elif defined(GEKKO)
|
||||
static enum video_driver_enum VIDEO_DEFAULT_DRIVER = VIDEO_WII;
|
||||
#elif defined(WIIU)
|
||||
@ -547,6 +561,9 @@ static enum menu_driver_enum MENU_DEFAULT_DRIVER = MENU_NULL;
|
||||
#define SETTING_UINT(key, configval, default_enable, default_setting, handle_setting) \
|
||||
GENERAL_SETTING(key, configval, default_enable, default_setting, struct config_uint_setting, handle_setting)
|
||||
|
||||
#define SETTING_SIZE(key, configval, default_enable, default_setting, handle_setting) \
|
||||
GENERAL_SETTING(key, configval, default_enable, default_setting, struct config_size_setting, handle_setting)
|
||||
|
||||
#define SETTING_PATH(key, configval, default_enable, default_setting, handle_setting) \
|
||||
GENERAL_SETTING(key, configval, default_enable, default_setting, struct config_path_setting, handle_setting)
|
||||
|
||||
@ -711,6 +728,8 @@ const char *config_get_default_video(void)
|
||||
return "gl";
|
||||
case VIDEO_VULKAN:
|
||||
return "vulkan";
|
||||
case VIDEO_METAL:
|
||||
return "metal";
|
||||
case VIDEO_DRM:
|
||||
return "drm";
|
||||
case VIDEO_WII:
|
||||
@ -1008,6 +1027,8 @@ const char *config_get_default_menu(void)
|
||||
return "glui";
|
||||
case MENU_XMB:
|
||||
return "xmb";
|
||||
case MENU_STRIPES:
|
||||
return "stripes";
|
||||
case MENU_NUKLEAR:
|
||||
return "nuklear";
|
||||
case MENU_NULL:
|
||||
@ -1048,7 +1069,7 @@ bool config_overlay_enable_default(void)
|
||||
static struct config_array_setting *populate_settings_array(settings_t *settings, int *size)
|
||||
{
|
||||
unsigned count = 0;
|
||||
struct config_array_setting *tmp = (struct config_array_setting*)malloc((*size + 1) * sizeof(struct config_array_setting));
|
||||
struct config_array_setting *tmp = (struct config_array_setting*)calloc(1, (*size + 1) * sizeof(struct config_array_setting));
|
||||
|
||||
/* Arrays */
|
||||
SETTING_ARRAY("playlist_names", settings->arrays.playlist_names, false, NULL, true);
|
||||
@ -1091,7 +1112,7 @@ static struct config_path_setting *populate_settings_path(settings_t *settings,
|
||||
{
|
||||
unsigned count = 0;
|
||||
global_t *global = global_get_ptr();
|
||||
struct config_path_setting *tmp = (struct config_path_setting*)malloc((*size + 1) * sizeof(struct config_path_setting));
|
||||
struct config_path_setting *tmp = (struct config_path_setting*)calloc(1, (*size + 1) * sizeof(struct config_path_setting));
|
||||
|
||||
/* Paths */
|
||||
#ifdef HAVE_XMB
|
||||
@ -1259,6 +1280,9 @@ static struct config_bool_setting *populate_settings_bool(settings_t *settings,
|
||||
SETTING_BOOL("ui_menubar_enable", &settings->bools.ui_menubar_enable, true, true, false);
|
||||
SETTING_BOOL("suspend_screensaver_enable", &settings->bools.ui_suspend_screensaver_enable, true, true, false);
|
||||
SETTING_BOOL("rewind_enable", &settings->bools.rewind_enable, true, rewind_enable, false);
|
||||
SETTING_BOOL("vrr_runloop_enable", &settings->bools.vrr_runloop_enable, true, vrr_runloop_enable, false);
|
||||
SETTING_BOOL("apply_cheats_after_toggle", &settings->bools.apply_cheats_after_toggle, true, apply_cheats_after_toggle, false);
|
||||
SETTING_BOOL("apply_cheats_after_load", &settings->bools.apply_cheats_after_load, true, apply_cheats_after_load, false);
|
||||
SETTING_BOOL("run_ahead_enabled", &settings->bools.run_ahead_enabled, true, false, false);
|
||||
SETTING_BOOL("run_ahead_secondary_instance", &settings->bools.run_ahead_secondary_instance, true, false, false);
|
||||
SETTING_BOOL("run_ahead_hide_warnings", &settings->bools.run_ahead_hide_warnings, true, false, false);
|
||||
@ -1284,7 +1308,7 @@ static struct config_bool_setting *populate_settings_bool(settings_t *settings,
|
||||
SETTING_BOOL("video_vsync", &settings->bools.video_vsync, true, vsync, false);
|
||||
SETTING_BOOL("video_hard_sync", &settings->bools.video_hard_sync, true, hard_sync, false);
|
||||
SETTING_BOOL("video_black_frame_insertion", &settings->bools.video_black_frame_insertion, true, black_frame_insertion, false);
|
||||
SETTING_BOOL("crt_switch_resolution", &settings->bools.crt_switch_resolution, true, crt_switch_resolution, false);
|
||||
SETTING_BOOL("crt_switch_resolution", &settings->bools.crt_switch_resolution, true, crt_switch_resolution, false);
|
||||
SETTING_BOOL("video_disable_composition", &settings->bools.video_disable_composition, true, disable_composition, false);
|
||||
SETTING_BOOL("pause_nonactive", &settings->bools.pause_nonactive, true, pause_nonactive, false);
|
||||
SETTING_BOOL("video_gpu_screenshot", &settings->bools.video_gpu_screenshot, true, gpu_screenshot, false);
|
||||
@ -1456,7 +1480,7 @@ static struct config_bool_setting *populate_settings_bool(settings_t *settings,
|
||||
static struct config_float_setting *populate_settings_float(settings_t *settings, int *size)
|
||||
{
|
||||
unsigned count = 0;
|
||||
struct config_float_setting *tmp = (struct config_float_setting*)malloc((*size + 1) * sizeof(struct config_float_setting));
|
||||
struct config_float_setting *tmp = (struct config_float_setting*)calloc(1, (*size + 1) * sizeof(struct config_float_setting));
|
||||
|
||||
SETTING_FLOAT("video_aspect_ratio", &settings->floats.video_aspect_ratio, true, aspect_ratio, false);
|
||||
SETTING_FLOAT("video_scale", &settings->floats.video_scale, false, 0.0f, false);
|
||||
@ -1466,7 +1490,7 @@ static struct config_float_setting *populate_settings_float(settings_t *settings
|
||||
SETTING_FLOAT("audio_volume", &settings->floats.audio_volume, true, audio_volume, false);
|
||||
SETTING_FLOAT("audio_mixer_volume", &settings->floats.audio_mixer_volume, true, audio_mixer_volume, false);
|
||||
#ifdef HAVE_OVERLAY
|
||||
SETTING_FLOAT("input_overlay_opacity", &settings->floats.input_overlay_opacity, true, 0.7f, false);
|
||||
SETTING_FLOAT("input_overlay_opacity", &settings->floats.input_overlay_opacity, true, default_input_overlay_opacity, false);
|
||||
SETTING_FLOAT("input_overlay_scale", &settings->floats.input_overlay_scale, true, 1.0f, false);
|
||||
#endif
|
||||
#ifdef HAVE_MENU
|
||||
@ -1503,6 +1527,7 @@ static struct config_uint_setting *populate_settings_uint(settings_t *settings,
|
||||
SETTING_UINT("audio_resampler_quality", &settings->uints.audio_resampler_quality, true, audio_resampler_quality_level, false);
|
||||
SETTING_UINT("audio_block_frames", &settings->uints.audio_block_frames, true, 0, false);
|
||||
SETTING_UINT("rewind_granularity", &settings->uints.rewind_granularity, true, rewind_granularity, false);
|
||||
SETTING_UINT("rewind_buffer_size_step", &settings->uints.rewind_buffer_size_step, true, rewind_buffer_size_step, false);
|
||||
SETTING_UINT("autosave_interval", &settings->uints.autosave_interval, true, autosave_interval, false);
|
||||
SETTING_UINT("libretro_log_level", &settings->uints.libretro_log_level, true, libretro_log_level, false);
|
||||
SETTING_UINT("keyboard_gamepad_mapping_type",&settings->uints.input_keyboard_gamepad_mapping_type, true, 1, false);
|
||||
@ -1579,6 +1604,18 @@ static struct config_uint_setting *populate_settings_uint(settings_t *settings,
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static struct config_size_setting *populate_settings_size(settings_t *settings, int *size)
|
||||
{
|
||||
unsigned count = 0;
|
||||
struct config_size_setting *tmp = (struct config_size_setting*)calloc((*size + 1), sizeof(struct config_size_setting));
|
||||
|
||||
SETTING_SIZE("rewind_buffer_size", &settings->sizes.rewind_buffer_size, true, rewind_buffer_size, false);
|
||||
|
||||
*size = count;
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static struct config_int_setting *populate_settings_int(settings_t *settings, int *size)
|
||||
{
|
||||
unsigned count = 0;
|
||||
@ -1614,6 +1651,7 @@ static void config_set_defaults(void)
|
||||
int float_settings_size = sizeof(settings->floats) / sizeof(settings->floats.placeholder);
|
||||
int int_settings_size = sizeof(settings->ints) / sizeof(settings->ints.placeholder);
|
||||
int uint_settings_size = sizeof(settings->uints) / sizeof(settings->uints.placeholder);
|
||||
int size_settings_size = sizeof(settings->sizes) / sizeof(settings->sizes.placeholder);
|
||||
const char *def_video = config_get_default_video();
|
||||
const char *def_audio = config_get_default_audio();
|
||||
const char *def_audio_resampler = config_get_default_audio_resampler();
|
||||
@ -1633,6 +1671,7 @@ static void config_set_defaults(void)
|
||||
struct config_bool_setting *bool_settings = populate_settings_bool (settings, &bool_settings_size);
|
||||
struct config_int_setting *int_settings = populate_settings_int (settings, &int_settings_size);
|
||||
struct config_uint_setting *uint_settings = populate_settings_uint (settings, &uint_settings_size);
|
||||
struct config_size_setting *size_settings = populate_settings_size (settings, &size_settings_size);
|
||||
|
||||
if (bool_settings && (bool_settings_size > 0))
|
||||
{
|
||||
@ -1667,6 +1706,17 @@ static void config_set_defaults(void)
|
||||
free(uint_settings);
|
||||
}
|
||||
|
||||
if (size_settings && (size_settings_size > 0))
|
||||
{
|
||||
for (i = 0; i < (unsigned)size_settings_size; i++)
|
||||
{
|
||||
if (size_settings[i].def_enable)
|
||||
*size_settings[i].ptr = size_settings[i].def;
|
||||
}
|
||||
|
||||
free(size_settings);
|
||||
}
|
||||
|
||||
if (float_settings && (float_settings_size > 0))
|
||||
{
|
||||
for (i = 0; i < (unsigned)float_settings_size; i++)
|
||||
@ -1758,8 +1808,6 @@ static void config_set_defaults(void)
|
||||
audio_set_float(AUDIO_ACTION_VOLUME_GAIN, settings->floats.audio_volume);
|
||||
audio_set_float(AUDIO_ACTION_MIXER_VOLUME_GAIN, settings->floats.audio_mixer_volume);
|
||||
|
||||
settings->rewind_buffer_size = rewind_buffer_size;
|
||||
|
||||
#ifdef HAVE_LAKKA
|
||||
settings->bools.ssh_enable = filestream_exists(LAKKA_SSH_PATH);
|
||||
settings->bools.samba_enable = filestream_exists(LAKKA_SAMBA_PATH);
|
||||
@ -2357,6 +2405,7 @@ static bool check_shader_compatibility(enum file_path_enum enum_idx)
|
||||
settings_t *settings = config_get_ptr();
|
||||
|
||||
if (string_is_equal(settings->arrays.video_driver, "vulkan") ||
|
||||
string_is_equal(settings->arrays.video_driver, "metal") ||
|
||||
string_is_equal(settings->arrays.video_driver, "d3d11") ||
|
||||
string_is_equal(settings->arrays.video_driver, "d3d12") ||
|
||||
string_is_equal(settings->arrays.video_driver, "gx2"))
|
||||
@ -2457,12 +2506,14 @@ static bool config_load_file(const char *path, bool set_defaults,
|
||||
int float_settings_size = sizeof(settings->floats) / sizeof(settings->floats.placeholder);
|
||||
int int_settings_size = sizeof(settings->ints) / sizeof(settings->ints.placeholder);
|
||||
int uint_settings_size = sizeof(settings->uints) / sizeof(settings->uints.placeholder);
|
||||
int size_settings_size = sizeof(settings->sizes) / sizeof(settings->sizes.placeholder);
|
||||
int array_settings_size = sizeof(settings->arrays) / sizeof(settings->arrays.placeholder);
|
||||
int path_settings_size = sizeof(settings->paths) / sizeof(settings->paths.placeholder);
|
||||
struct config_bool_setting *bool_settings = populate_settings_bool (settings, &bool_settings_size);
|
||||
struct config_float_setting *float_settings = populate_settings_float (settings, &float_settings_size);
|
||||
struct config_int_setting *int_settings = populate_settings_int (settings, &int_settings_size);
|
||||
struct config_uint_setting *uint_settings = populate_settings_uint (settings, &uint_settings_size);
|
||||
struct config_size_setting *size_settings = populate_settings_size (settings, &size_settings_size);
|
||||
struct config_array_setting *array_settings = populate_settings_array (settings, &array_settings_size);
|
||||
struct config_path_setting *path_settings = populate_settings_path (settings, &path_settings_size);
|
||||
|
||||
@ -2587,6 +2638,21 @@ static bool config_load_file(const char *path, bool set_defaults,
|
||||
*uint_settings[i].ptr = tmp;
|
||||
}
|
||||
|
||||
for (i = 0; i < (unsigned)size_settings_size; i++)
|
||||
{
|
||||
size_t tmp = 0;
|
||||
if (config_get_size_t(conf, size_settings[i].ident, &tmp))
|
||||
*size_settings[i].ptr = tmp ;
|
||||
/* Special case for rewind_buffer_size - need to convert low values to what they were
|
||||
* intended to be based on the default value in config.def.h
|
||||
* If the value is less than 10000 then multiple by 1MB because if the retroarch.cfg
|
||||
* file contains rewind_buffer_size = "100" then that ultimately gets interpreted as
|
||||
* 100MB, so ensure the internal values represent that.*/
|
||||
if (string_is_equal(size_settings[i].ident, "rewind_buffer_size"))
|
||||
if (*size_settings[i].ptr < 10000)
|
||||
*size_settings[i].ptr = *size_settings[i].ptr * 1024 * 1024;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_USERS; i++)
|
||||
{
|
||||
char buf[64];
|
||||
@ -2618,13 +2684,6 @@ static bool config_load_file(const char *path, bool set_defaults,
|
||||
CONFIG_GET_INT_BASE(conf, settings, uints.led_map[i], buf);
|
||||
}
|
||||
|
||||
{
|
||||
/* ugly hack around C89 not allowing mixing declarations and code */
|
||||
int buffer_size = 0;
|
||||
if (config_get_int(conf, "rewind_buffer_size", &buffer_size))
|
||||
settings->rewind_buffer_size = buffer_size * UINT64_C(1000000);
|
||||
}
|
||||
|
||||
|
||||
/* Hexadecimal settings */
|
||||
|
||||
@ -2968,6 +3027,8 @@ end:
|
||||
free(array_settings);
|
||||
if (path_settings)
|
||||
free(path_settings);
|
||||
if (size_settings)
|
||||
free(size_settings);
|
||||
free(tmp_str);
|
||||
return ret;
|
||||
}
|
||||
@ -3352,12 +3413,14 @@ bool config_load_remap(void)
|
||||
|
||||
new_conf = NULL;
|
||||
|
||||
free(content_path);
|
||||
free(remap_directory);
|
||||
free(core_path);
|
||||
free(game_path);
|
||||
return false;
|
||||
|
||||
success:
|
||||
free(content_path);
|
||||
free(remap_directory);
|
||||
free(core_path);
|
||||
free(game_path);
|
||||
@ -3948,6 +4011,7 @@ bool config_save_file(const char *path)
|
||||
struct config_bool_setting *bool_settings = NULL;
|
||||
struct config_int_setting *int_settings = NULL;
|
||||
struct config_uint_setting *uint_settings = NULL;
|
||||
struct config_size_setting *size_settings = NULL;
|
||||
struct config_float_setting *float_settings = NULL;
|
||||
struct config_array_setting *array_settings = NULL;
|
||||
struct config_path_setting *path_settings = NULL;
|
||||
@ -3957,6 +4021,7 @@ bool config_save_file(const char *path)
|
||||
int float_settings_size = sizeof(settings->floats)/ sizeof(settings->floats.placeholder);
|
||||
int int_settings_size = sizeof(settings->ints) / sizeof(settings->ints.placeholder);
|
||||
int uint_settings_size = sizeof(settings->uints) / sizeof(settings->uints.placeholder);
|
||||
int size_settings_size = sizeof(settings->sizes) / sizeof(settings->sizes.placeholder);
|
||||
int array_settings_size = sizeof(settings->arrays)/ sizeof(settings->arrays.placeholder);
|
||||
int path_settings_size = sizeof(settings->paths) / sizeof(settings->paths.placeholder);
|
||||
|
||||
@ -3973,6 +4038,7 @@ bool config_save_file(const char *path)
|
||||
bool_settings = populate_settings_bool (settings, &bool_settings_size);
|
||||
int_settings = populate_settings_int (settings, &int_settings_size);
|
||||
uint_settings = populate_settings_uint (settings, &uint_settings_size);
|
||||
size_settings = populate_settings_size (settings, &size_settings_size);
|
||||
float_settings = populate_settings_float (settings, &float_settings_size);
|
||||
array_settings = populate_settings_array (settings, &array_settings_size);
|
||||
path_settings = populate_settings_path (settings, &path_settings_size);
|
||||
@ -4050,6 +4116,18 @@ bool config_save_file(const char *path)
|
||||
free(uint_settings);
|
||||
}
|
||||
|
||||
if (size_settings && (size_settings_size > 0))
|
||||
{
|
||||
for (i = 0; i < (unsigned)size_settings_size; i++)
|
||||
if (!size_settings[i].override ||
|
||||
!retroarch_override_setting_is_set(size_settings[i].override, NULL))
|
||||
config_set_int(conf,
|
||||
size_settings[i].ident,
|
||||
(int)*size_settings[i].ptr);
|
||||
|
||||
free(size_settings);
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_USERS; i++)
|
||||
{
|
||||
char cfg[64];
|
||||
@ -4169,8 +4247,10 @@ bool config_save_overrides(int override_type)
|
||||
struct config_bool_setting *bool_overrides = NULL;
|
||||
struct config_int_setting *int_settings = NULL;
|
||||
struct config_uint_setting *uint_settings = NULL;
|
||||
struct config_size_setting *size_settings = NULL;
|
||||
struct config_int_setting *int_overrides = NULL;
|
||||
struct config_uint_setting *uint_overrides = NULL;
|
||||
struct config_size_setting *size_overrides = NULL;
|
||||
struct config_float_setting *float_settings = NULL;
|
||||
struct config_float_setting *float_overrides= NULL;
|
||||
struct config_array_setting *array_settings = NULL;
|
||||
@ -4187,6 +4267,7 @@ bool config_save_overrides(int override_type)
|
||||
int float_settings_size = sizeof(settings->floats) / sizeof(settings->floats.placeholder);
|
||||
int int_settings_size = sizeof(settings->ints) / sizeof(settings->ints.placeholder);
|
||||
int uint_settings_size = sizeof(settings->uints) / sizeof(settings->uints.placeholder);
|
||||
int size_settings_size = sizeof(settings->sizes) / sizeof(settings->sizes.placeholder);
|
||||
int array_settings_size = sizeof(settings->arrays) / sizeof(settings->arrays.placeholder);
|
||||
int path_settings_size = sizeof(settings->paths) / sizeof(settings->paths.placeholder);
|
||||
rarch_system_info_t *system = runloop_get_system_info();
|
||||
@ -4255,6 +4336,10 @@ bool config_save_overrides(int override_type)
|
||||
tmp_i = sizeof(settings->uints) / sizeof(settings->uints.placeholder);
|
||||
uint_overrides = populate_settings_uint (overrides, &tmp_i);
|
||||
|
||||
size_settings = populate_settings_size(settings, &size_settings_size);
|
||||
tmp_i = sizeof(settings->sizes) / sizeof(settings->sizes.placeholder);
|
||||
size_overrides = populate_settings_size (overrides, &tmp_i);
|
||||
|
||||
float_settings = populate_settings_float(settings, &float_settings_size);
|
||||
tmp_i = sizeof(settings->floats) / sizeof(settings->floats.placeholder);
|
||||
float_overrides = populate_settings_float(overrides, &tmp_i);
|
||||
@ -4307,6 +4392,18 @@ bool config_save_overrides(int override_type)
|
||||
(*uint_overrides[i].ptr));
|
||||
}
|
||||
}
|
||||
for (i = 0; i < (unsigned)size_settings_size; i++)
|
||||
{
|
||||
if ((*size_settings[i].ptr) != (*size_overrides[i].ptr))
|
||||
{
|
||||
RARCH_LOG(" original: %s=%d\n",
|
||||
size_settings[i].ident, (*size_settings[i].ptr));
|
||||
RARCH_LOG(" override: %s=%d\n",
|
||||
size_overrides[i].ident, (*size_overrides[i].ptr));
|
||||
config_set_int(conf, size_overrides[i].ident,
|
||||
(int)(*size_overrides[i].ptr));
|
||||
}
|
||||
}
|
||||
for (i = 0; i < (unsigned)float_settings_size; i++)
|
||||
{
|
||||
if ((*float_settings[i].ptr) != (*float_overrides[i].ptr))
|
||||
@ -4336,7 +4433,7 @@ bool config_save_overrides(int override_type)
|
||||
for (i = 0; i < (unsigned)path_settings_size; i++)
|
||||
{
|
||||
|
||||
/* blacklist video_shader, better handled by shader presets*/
|
||||
/* blacklist video_shader, better handled by shader presets*/
|
||||
/* to-do: add setting to control blacklisting */
|
||||
if (string_is_equal(path_settings[i].ident, "video_shader"))
|
||||
continue;
|
||||
@ -4423,6 +4520,8 @@ bool config_save_overrides(int override_type)
|
||||
free(int_settings);
|
||||
if (uint_settings)
|
||||
free(uint_settings);
|
||||
if (size_settings)
|
||||
free(size_settings);
|
||||
if (int_overrides)
|
||||
free(int_overrides);
|
||||
if (uint_overrides)
|
||||
@ -4439,6 +4538,8 @@ bool config_save_overrides(int override_type)
|
||||
free(path_settings);
|
||||
if (path_overrides)
|
||||
free(path_overrides);
|
||||
if (size_overrides)
|
||||
free(size_overrides);
|
||||
free(settings);
|
||||
free(config_directory);
|
||||
free(override_directory);
|
||||
|
@ -239,6 +239,9 @@ typedef struct settings
|
||||
bool playlist_entry_remove;
|
||||
bool playlist_entry_rename;
|
||||
bool rewind_enable;
|
||||
bool vrr_runloop_enable;
|
||||
bool apply_cheats_after_toggle;
|
||||
bool apply_cheats_after_load;
|
||||
bool run_ahead_enabled;
|
||||
bool run_ahead_secondary_instance;
|
||||
bool run_ahead_hide_warnings;
|
||||
@ -348,6 +351,7 @@ typedef struct settings
|
||||
unsigned content_history_size;
|
||||
unsigned libretro_log_level;
|
||||
unsigned rewind_granularity;
|
||||
unsigned rewind_buffer_size_step;
|
||||
unsigned autosave_interval;
|
||||
unsigned network_cmd_port;
|
||||
unsigned network_remote_base_port;
|
||||
@ -411,6 +415,12 @@ typedef struct settings
|
||||
unsigned midi_volume;
|
||||
} uints;
|
||||
|
||||
struct
|
||||
{
|
||||
size_t placeholder;
|
||||
size_t rewind_buffer_size;
|
||||
} sizes;
|
||||
|
||||
struct
|
||||
{
|
||||
char placeholder;
|
||||
@ -509,7 +519,6 @@ typedef struct settings
|
||||
|
||||
video_viewport_t video_viewport_custom;
|
||||
|
||||
size_t rewind_buffer_size;
|
||||
} settings_t;
|
||||
|
||||
/**
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include "verbosity.h"
|
||||
#include "gfx/video_driver.h"
|
||||
#include "audio/audio_driver.h"
|
||||
#include "tasks/tasks_internal.h"
|
||||
|
||||
#ifdef HAVE_RUNAHEAD
|
||||
#include "runahead/copy_load_info.h"
|
||||
@ -298,6 +299,7 @@ bool core_load_game(retro_ctx_load_content_info_t *load_info)
|
||||
#endif
|
||||
|
||||
content_get_status(&contentless, &is_inited);
|
||||
set_save_state_in_background(false);
|
||||
|
||||
if (load_info && load_info->special)
|
||||
current_core.game_loaded = current_core.retro_load_game_special(
|
||||
@ -402,7 +404,6 @@ bool core_unload(void)
|
||||
bool core_unload_game(void)
|
||||
{
|
||||
video_driver_free_hw_context();
|
||||
audio_driver_stop();
|
||||
|
||||
video_driver_set_cached_frame_ptr(NULL);
|
||||
|
||||
@ -410,6 +411,8 @@ bool core_unload_game(void)
|
||||
|
||||
current_core.game_loaded = false;
|
||||
|
||||
audio_driver_stop();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
12
core_info.c
12
core_info.c
@ -149,6 +149,7 @@ static void core_info_list_free(core_info_list_t *core_info_list)
|
||||
free(info->path);
|
||||
free(info->core_name);
|
||||
free(info->systemname);
|
||||
free(info->system_id);
|
||||
free(info->system_manufacturer);
|
||||
free(info->display_name);
|
||||
free(info->display_version);
|
||||
@ -302,6 +303,14 @@ static core_info_list_t *core_info_list_new(const char *path,
|
||||
tmp = NULL;
|
||||
}
|
||||
|
||||
if (config_get_string(conf, "systemid", &tmp)
|
||||
&& !string_is_empty(tmp))
|
||||
{
|
||||
core_info[i].system_id = strdup(tmp);
|
||||
free(tmp);
|
||||
tmp = NULL;
|
||||
}
|
||||
|
||||
if (config_get_string(conf, "manufacturer", &tmp)
|
||||
&& !string_is_empty(tmp))
|
||||
{
|
||||
@ -679,6 +688,9 @@ bool core_info_load(core_info_ctx_find_t *info)
|
||||
if (!info)
|
||||
return false;
|
||||
|
||||
if (!core_info_current)
|
||||
core_info_init_current_core();
|
||||
|
||||
core_info_get_current_core(&core_info);
|
||||
|
||||
if (!core_info_curr_list)
|
||||
|
@ -47,6 +47,7 @@ typedef struct
|
||||
char *core_name;
|
||||
char *system_manufacturer;
|
||||
char *systemname;
|
||||
char *system_id;
|
||||
char *supported_extensions;
|
||||
char *authors;
|
||||
char *permissions;
|
||||
|
@ -478,6 +478,36 @@ database_info_list_t *database_info_list_new(
|
||||
|
||||
if (!new_ptr)
|
||||
{
|
||||
if (db_info.bbfc_rating)
|
||||
free(db_info.bbfc_rating);
|
||||
if (db_info.cero_rating)
|
||||
free(db_info.cero_rating);
|
||||
if (db_info.description)
|
||||
free(db_info.description);
|
||||
if (db_info.edge_magazine_review)
|
||||
free(db_info.edge_magazine_review);
|
||||
if (db_info.elspa_rating)
|
||||
free(db_info.elspa_rating);
|
||||
if (db_info.enhancement_hw)
|
||||
free(db_info.enhancement_hw);
|
||||
if (db_info.esrb_rating)
|
||||
free(db_info.esrb_rating);
|
||||
if (db_info.franchise)
|
||||
free(db_info.franchise);
|
||||
if (db_info.genre)
|
||||
free(db_info.genre);
|
||||
if (db_info.name)
|
||||
free(db_info.name);
|
||||
if (db_info.origin)
|
||||
free(db_info.origin);
|
||||
if (db_info.pegi_rating)
|
||||
free(db_info.pegi_rating);
|
||||
if (db_info.publisher)
|
||||
free(db_info.publisher);
|
||||
if (db_info.rom_name)
|
||||
free(db_info.rom_name);
|
||||
if (db_info.serial)
|
||||
free(db_info.serial);
|
||||
database_info_list_free(database_info_list);
|
||||
free(database_info);
|
||||
free(database_info_list);
|
||||
|
2
deps/discord-rpc/src/discord_register_osx.m
vendored
2
deps/discord-rpc/src/discord_register_osx.m
vendored
@ -3,7 +3,7 @@
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
#include "discord_register.h"
|
||||
#include "../include/discord_register.h"
|
||||
|
||||
static void RegisterCommand(const char* applicationId, const char* command)
|
||||
{
|
||||
|
2
deps/libFLAC/md5.c
vendored
2
deps/libFLAC/md5.c
vendored
@ -513,7 +513,7 @@ FLAC__bool FLAC__MD5Accumulate(FLAC__MD5Context *ctx, const FLAC__int32 * const
|
||||
|
||||
format_input_(&ctx->internal_buf, signal, channels, samples, bytes_per_sample);
|
||||
|
||||
FLAC__MD5Update(ctx, ctx->internal_buf.p8, bytes_needed);
|
||||
FLAC__MD5Update(ctx, ctx->internal_buf.p8, (unsigned)bytes_needed);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
9
deps/libui/LICENSE
vendored
9
deps/libui/LICENSE
vendored
@ -1,9 +0,0 @@
|
||||
Copyright (c) 2014 Pietro Gagliardi
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
(this is called the MIT License or Expat License; see http://www.opensource.org/licenses/MIT)
|
16
deps/libui/common/CMakeLists.txt
vendored
16
deps/libui/common/CMakeLists.txt
vendored
@ -1,16 +0,0 @@
|
||||
# 3 june 2016
|
||||
|
||||
list(APPEND _LIBUI_SOURCES
|
||||
common/areaevents.c
|
||||
common/control.c
|
||||
common/debug.c
|
||||
common/matrix.c
|
||||
common/shouldquit.c
|
||||
common/userbugs.c
|
||||
)
|
||||
set(_LIBUI_SOURCES ${_LIBUI_SOURCES} PARENT_SCOPE)
|
||||
|
||||
list(APPEND _LIBUI_INCLUDEDIRS
|
||||
common
|
||||
)
|
||||
set(_LIBUI_INCLUDEDIRS ${_LIBUI_INCLUDEDIRS} PARENT_SCOPE)
|
167
deps/libui/common/areaevents.c
vendored
167
deps/libui/common/areaevents.c
vendored
@ -1,167 +0,0 @@
|
||||
// 29 march 2014
|
||||
#include "../ui.h"
|
||||
#include "uipriv.h"
|
||||
|
||||
/*
|
||||
Windows and GTK+ have a limit of 2 and 3 clicks, respectively, natively supported. Fortunately, we can simulate the double/triple-click behavior to build higher-order clicks. We can use the same algorithm Windows uses on both:
|
||||
http://blogs.msdn.com/b/oldnewthing/archive/2004/10/18/243925.aspx
|
||||
For GTK+, we pull the double-click time and double-click distance, which work the same as the equivalents on Windows (so the distance is in all directions), from the GtkSettings system.
|
||||
|
||||
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+.
|
||||
*/
|
||||
|
||||
// x, y, xdist, ydist, and c.rect must have the same units
|
||||
// so must time, maxTime, and c.prevTime
|
||||
int clickCounterClick(clickCounter *c, int button, int x, int y, uintptr_t time, uintptr_t maxTime, int32_t xdist, int32_t ydist)
|
||||
{
|
||||
// different button than before? if so, don't count
|
||||
if (button != c->curButton)
|
||||
c->count = 0;
|
||||
|
||||
// (x, y) in the allowed region for a double-click? if not, don't count
|
||||
if (x < c->rectX0)
|
||||
c->count = 0;
|
||||
if (y < c->rectY0)
|
||||
c->count = 0;
|
||||
if (x >= c->rectX1)
|
||||
c->count = 0;
|
||||
if (y >= c->rectY1)
|
||||
c->count = 0;
|
||||
|
||||
// too slow? if so, don't count
|
||||
// note the below expression; time > (c.prevTime + maxTime) can overflow!
|
||||
if ((time - c->prevTime) > maxTime) // too slow; don't count
|
||||
c->count = 0;
|
||||
|
||||
c->count++; // if either of the above ifs happened, this will make the click count 1; otherwise it will make the click count 2, 3, 4, 5, ...
|
||||
|
||||
// now we need to update the internal structures for the next test
|
||||
c->curButton = button;
|
||||
c->prevTime = time;
|
||||
c->rectX0 = x - xdist;
|
||||
c->rectY0 = y - ydist;
|
||||
c->rectX1 = x + xdist;
|
||||
c->rectY1 = y + ydist;
|
||||
|
||||
return c->count;
|
||||
}
|
||||
|
||||
void clickCounterReset(clickCounter *c)
|
||||
{
|
||||
c->curButton = 0;
|
||||
c->rectX0 = 0;
|
||||
c->rectY0 = 0;
|
||||
c->rectX1 = 0;
|
||||
c->rectY1 = 0;
|
||||
c->prevTime = 0;
|
||||
c->count = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
For position independence across international keyboard layouts, typewriter keys are read using scancodes (which are always set 1).
|
||||
Windows provides the scancodes directly in the LPARAM.
|
||||
GTK+ provides the scancodes directly from the underlying window system via GdkEventKey.hardware_keycode.
|
||||
On X11, this is scancode + 8 (because X11 keyboard codes have a range of [8,255]).
|
||||
Wayland is guaranteed to give the same result (thanks ebassi in irc.gimp.net/#gtk+).
|
||||
On Linux, where evdev is used instead of polling scancodes directly from the keyboard, evdev's typewriter section key code constants are the same as scancodes anyway, so the rules above apply.
|
||||
Typewriter section scancodes are the same across international keyboards with some exceptions that have been accounted for (see KeyEvent's documentation); see http://www.quadibloc.com/comp/scan.htm for details.
|
||||
Non-typewriter keys can be handled safely using constants provided by the respective backend API.
|
||||
|
||||
Because GTK+ keysyms may or may not obey Num Lock, we also handle the 0-9 and . keys on the numeric keypad with scancodes (they match too).
|
||||
*/
|
||||
|
||||
// use uintptr_t to be safe; the size of the scancode/hardware key code field on each platform is different
|
||||
static const struct {
|
||||
uintptr_t scancode;
|
||||
char equiv;
|
||||
} scancodeKeys[] = {
|
||||
{ 0x02, '1' },
|
||||
{ 0x03, '2' },
|
||||
{ 0x04, '3' },
|
||||
{ 0x05, '4' },
|
||||
{ 0x06, '5' },
|
||||
{ 0x07, '6' },
|
||||
{ 0x08, '7' },
|
||||
{ 0x09, '8' },
|
||||
{ 0x0A, '9' },
|
||||
{ 0x0B, '0' },
|
||||
{ 0x0C, '-' },
|
||||
{ 0x0D, '=' },
|
||||
{ 0x0E, '\b' },
|
||||
{ 0x0F, '\t' },
|
||||
{ 0x10, 'q' },
|
||||
{ 0x11, 'w' },
|
||||
{ 0x12, 'e' },
|
||||
{ 0x13, 'r' },
|
||||
{ 0x14, 't' },
|
||||
{ 0x15, 'y' },
|
||||
{ 0x16, 'u' },
|
||||
{ 0x17, 'i' },
|
||||
{ 0x18, 'o' },
|
||||
{ 0x19, 'p' },
|
||||
{ 0x1A, '[' },
|
||||
{ 0x1B, ']' },
|
||||
{ 0x1C, '\n' },
|
||||
{ 0x1E, 'a' },
|
||||
{ 0x1F, 's' },
|
||||
{ 0x20, 'd' },
|
||||
{ 0x21, 'f' },
|
||||
{ 0x22, 'g' },
|
||||
{ 0x23, 'h' },
|
||||
{ 0x24, 'j' },
|
||||
{ 0x25, 'k' },
|
||||
{ 0x26, 'l' },
|
||||
{ 0x27, ';' },
|
||||
{ 0x28, '\'' },
|
||||
{ 0x29, '`' },
|
||||
{ 0x2B, '\\' },
|
||||
{ 0x2C, 'z' },
|
||||
{ 0x2D, 'x' },
|
||||
{ 0x2E, 'c' },
|
||||
{ 0x2F, 'v' },
|
||||
{ 0x30, 'b' },
|
||||
{ 0x31, 'n' },
|
||||
{ 0x32, 'm' },
|
||||
{ 0x33, ',' },
|
||||
{ 0x34, '.' },
|
||||
{ 0x35, '/' },
|
||||
{ 0x39, ' ' },
|
||||
{ 0xFFFF, 0 },
|
||||
};
|
||||
|
||||
static const struct {
|
||||
uintptr_t scancode;
|
||||
uiExtKey equiv;
|
||||
} scancodeExtKeys[] = {
|
||||
{ 0x47, uiExtKeyN7 },
|
||||
{ 0x48, uiExtKeyN8 },
|
||||
{ 0x49, uiExtKeyN9 },
|
||||
{ 0x4B, uiExtKeyN4 },
|
||||
{ 0x4C, uiExtKeyN5 },
|
||||
{ 0x4D, uiExtKeyN6 },
|
||||
{ 0x4F, uiExtKeyN1 },
|
||||
{ 0x50, uiExtKeyN2 },
|
||||
{ 0x51, uiExtKeyN3 },
|
||||
{ 0x52, uiExtKeyN0 },
|
||||
{ 0x53, uiExtKeyNDot },
|
||||
{ 0xFFFF, 0 },
|
||||
};
|
||||
|
||||
int fromScancode(uintptr_t scancode, uiAreaKeyEvent *ke)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; scancodeKeys[i].scancode != 0xFFFF; i++)
|
||||
if (scancodeKeys[i].scancode == scancode) {
|
||||
ke->Key = scancodeKeys[i].equiv;
|
||||
return 1;
|
||||
}
|
||||
for (i = 0; scancodeExtKeys[i].scancode != 0xFFFF; i++)
|
||||
if (scancodeExtKeys[i].scancode == scancode) {
|
||||
ke->ExtKey = scancodeExtKeys[i].equiv;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
101
deps/libui/common/control.c
vendored
101
deps/libui/common/control.c
vendored
@ -1,101 +0,0 @@
|
||||
// 26 may 2015
|
||||
#include "../ui.h"
|
||||
#include "uipriv.h"
|
||||
|
||||
void uiControlDestroy(uiControl *c)
|
||||
{
|
||||
(*(c->Destroy))(c);
|
||||
}
|
||||
|
||||
uintptr_t uiControlHandle(uiControl *c)
|
||||
{
|
||||
return (*(c->Handle))(c);
|
||||
}
|
||||
|
||||
uiControl *uiControlParent(uiControl *c)
|
||||
{
|
||||
return (*(c->Parent))(c);
|
||||
}
|
||||
|
||||
void uiControlSetParent(uiControl *c, uiControl *parent)
|
||||
{
|
||||
(*(c->SetParent))(c, parent);
|
||||
}
|
||||
|
||||
int uiControlToplevel(uiControl *c)
|
||||
{
|
||||
return (*(c->Toplevel))(c);
|
||||
}
|
||||
|
||||
int uiControlVisible(uiControl *c)
|
||||
{
|
||||
return (*(c->Visible))(c);
|
||||
}
|
||||
|
||||
void uiControlShow(uiControl *c)
|
||||
{
|
||||
(*(c->Show))(c);
|
||||
}
|
||||
|
||||
void uiControlHide(uiControl *c)
|
||||
{
|
||||
(*(c->Hide))(c);
|
||||
}
|
||||
|
||||
int uiControlEnabled(uiControl *c)
|
||||
{
|
||||
return (*(c->Enabled))(c);
|
||||
}
|
||||
|
||||
void uiControlEnable(uiControl *c)
|
||||
{
|
||||
(*(c->Enable))(c);
|
||||
}
|
||||
|
||||
void uiControlDisable(uiControl *c)
|
||||
{
|
||||
(*(c->Disable))(c);
|
||||
}
|
||||
|
||||
#define uiControlSignature 0x7569436F
|
||||
|
||||
uiControl *uiAllocControl(size_t size, uint32_t OSsig, uint32_t typesig, const char *typenamestr)
|
||||
{
|
||||
uiControl *c;
|
||||
|
||||
c = (uiControl *) uiAlloc(size, typenamestr);
|
||||
c->Signature = uiControlSignature;
|
||||
c->OSSignature = OSsig;
|
||||
c->TypeSignature = typesig;
|
||||
return c;
|
||||
}
|
||||
|
||||
void uiFreeControl(uiControl *c)
|
||||
{
|
||||
if (uiControlParent(c) != NULL)
|
||||
userbug("You cannot destroy a uiControl while it still has a parent. (control: %p)", c);
|
||||
uiFree(c);
|
||||
}
|
||||
|
||||
void uiControlVerifySetParent(uiControl *c, uiControl *parent)
|
||||
{
|
||||
uiControl *curParent;
|
||||
|
||||
if (uiControlToplevel(c))
|
||||
userbug("You cannot give a toplevel uiControl a parent. (control: %p)", c);
|
||||
curParent = uiControlParent(c);
|
||||
if (parent != NULL && curParent != NULL)
|
||||
userbug("You cannot give a uiControl a parent while it already has one. (control: %p; current parent: %p; new parent: %p)", c, curParent, parent);
|
||||
if (parent == NULL && curParent == NULL)
|
||||
implbug("attempt to double unparent uiControl %p", c);
|
||||
}
|
||||
|
||||
int uiControlEnabledToUser(uiControl *c)
|
||||
{
|
||||
while (c != NULL) {
|
||||
if (!uiControlEnabled(c))
|
||||
return 0;
|
||||
c = uiControlParent(c);
|
||||
}
|
||||
return 1;
|
||||
}
|
25
deps/libui/common/controlsigs.h
vendored
25
deps/libui/common/controlsigs.h
vendored
@ -1,25 +0,0 @@
|
||||
// 24 april 2016
|
||||
|
||||
#define uiAreaSignature 0x41726561
|
||||
#define uiBoxSignature 0x426F784C
|
||||
#define uiButtonSignature 0x42746F6E
|
||||
#define uiCheckboxSignature 0x43686B62
|
||||
#define uiColorButtonSignature 0x436F6C42
|
||||
#define uiComboboxSignature 0x436F6D62
|
||||
#define uiDateTimePickerSignature 0x44545069
|
||||
#define uiEditableComboboxSignature 0x45644362
|
||||
#define uiEntrySignature 0x456E7472
|
||||
#define uiFontButtonSignature 0x466F6E42
|
||||
#define uiFormSignature 0x466F726D
|
||||
#define uiGridSignature 0x47726964
|
||||
#define uiGroupSignature 0x47727062
|
||||
#define uiLabelSignature 0x4C61626C
|
||||
#define uiMultilineEntrySignature 0x4D6C6E45
|
||||
#define uiProgressBarSignature 0x50426172
|
||||
#define uiRadioButtonsSignature 0x5264696F
|
||||
#define uiSeparatorSignature 0x53657061
|
||||
#define uiSliderSignature 0x536C6964
|
||||
#define uiSpinboxSignature 0x5370696E
|
||||
#define uiTabSignature 0x54616273
|
||||
#define uiTableSignature 0x5461626C
|
||||
#define uiWindowSignature 0x57696E64
|
21
deps/libui/common/debug.c
vendored
21
deps/libui/common/debug.c
vendored
@ -1,21 +0,0 @@
|
||||
// 13 may 2016
|
||||
#include "../ui.h"
|
||||
#include "uipriv.h"
|
||||
|
||||
void _implbug(const char *file, const char *line, const char *func, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
realbug(file, line, func, "POSSIBLE IMPLEMENTATION BUG; CONTACT ANDLABS:\n", format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void _userbug(const char *file, const char *line, const char *func, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
realbug(file, line, func, "You have a bug: ", format, ap);
|
||||
va_end(ap);
|
||||
}
|
50
deps/libui/common/matrix.c
vendored
50
deps/libui/common/matrix.c
vendored
@ -1,50 +0,0 @@
|
||||
// 11 october 2015
|
||||
#include <math.h>
|
||||
#include "../ui.h"
|
||||
#include "uipriv.h"
|
||||
|
||||
void uiDrawMatrixSetIdentity(uiDrawMatrix *m)
|
||||
{
|
||||
m->M11 = 1;
|
||||
m->M12 = 0;
|
||||
m->M21 = 0;
|
||||
m->M22 = 1;
|
||||
m->M31 = 0;
|
||||
m->M32 = 0;
|
||||
}
|
||||
|
||||
// The rest of this file provides basic utilities in case the platform doesn't provide any of its own for these tasks.
|
||||
// Keep these as minimal as possible. They should generally not call other fallbacks.
|
||||
|
||||
// see https://msdn.microsoft.com/en-us/library/windows/desktop/ff684171%28v=vs.85%29.aspx#skew_transform
|
||||
// TODO see if there's a way we can avoid the multiplication
|
||||
void fallbackSkew(uiDrawMatrix *m, double x, double y, double xamount, double yamount)
|
||||
{
|
||||
uiDrawMatrix n;
|
||||
|
||||
uiDrawMatrixSetIdentity(&n);
|
||||
// TODO explain this
|
||||
n.M12 = tan(yamount);
|
||||
n.M21 = tan(xamount);
|
||||
n.M31 = -y * tan(xamount);
|
||||
n.M32 = -x * tan(yamount);
|
||||
uiDrawMatrixMultiply(m, &n);
|
||||
}
|
||||
|
||||
void scaleCenter(double xCenter, double yCenter, double *x, double *y)
|
||||
{
|
||||
*x = xCenter - (*x * xCenter);
|
||||
*y = yCenter - (*y * yCenter);
|
||||
}
|
||||
|
||||
// the basic algorithm is from cairo
|
||||
// but it's the same algorithm as the transform point, just without M31 and M32 taken into account, so let's just do that instead
|
||||
void fallbackTransformSize(uiDrawMatrix *m, double *x, double *y)
|
||||
{
|
||||
uiDrawMatrix m2;
|
||||
|
||||
m2 = *m;
|
||||
m2.M31 = 0;
|
||||
m2.M32 = 0;
|
||||
uiDrawMatrixTransformPoint(&m2, x, y);
|
||||
}
|
22
deps/libui/common/shouldquit.c
vendored
22
deps/libui/common/shouldquit.c
vendored
@ -1,22 +0,0 @@
|
||||
// 9 may 2015
|
||||
#include "../ui.h"
|
||||
#include "uipriv.h"
|
||||
|
||||
static int defaultOnShouldQuit(void *data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int (*onShouldQuit)(void *) = defaultOnShouldQuit;
|
||||
static void *onShouldQuitData;
|
||||
|
||||
void uiOnShouldQuit(int (*f)(void *), void *data)
|
||||
{
|
||||
onShouldQuit = f;
|
||||
onShouldQuitData = data;
|
||||
}
|
||||
|
||||
int shouldQuit(void)
|
||||
{
|
||||
return (*onShouldQuit)(onShouldQuitData);
|
||||
}
|
58
deps/libui/common/uipriv.h
vendored
58
deps/libui/common/uipriv.h
vendored
@ -1,58 +0,0 @@
|
||||
// 6 april 2015
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdarg.h>
|
||||
#include "controlsigs.h"
|
||||
|
||||
extern uiInitOptions options;
|
||||
|
||||
extern void *uiAlloc(size_t, const char *);
|
||||
#define uiNew(T) ((T *) uiAlloc(sizeof (T), #T))
|
||||
extern void *uiRealloc(void *, size_t, const char *);
|
||||
extern void uiFree(void *);
|
||||
|
||||
// ugh, this was only introduced in MSVC 2015...
|
||||
#ifdef _MSC_VER
|
||||
#define __func__ __FUNCTION__
|
||||
#endif
|
||||
extern void realbug(const char *file, const char *line, const char *func, const char *prefix, const char *format, va_list ap);
|
||||
#define _ns2(s) #s
|
||||
#define _ns(s) _ns2(s)
|
||||
extern void _implbug(const char *file, const char *line, const char *func, const char *format, ...);
|
||||
#define implbug(...) _implbug(__FILE__, _ns(__LINE__), __func__, __VA_ARGS__)
|
||||
extern void _userbug(const char *file, const char *line, const char *func, const char *format, ...);
|
||||
#define userbug(...) _userbug(__FILE__, _ns(__LINE__), __func__, __VA_ARGS__)
|
||||
|
||||
// control.c
|
||||
extern uiControl *newControl(size_t size, uint32_t OSsig, uint32_t typesig, const char *typenamestr);
|
||||
|
||||
// shouldquit.c
|
||||
extern int shouldQuit(void);
|
||||
|
||||
// areaevents.c
|
||||
typedef struct clickCounter clickCounter;
|
||||
// you should call Reset() to zero-initialize a new instance
|
||||
// it doesn't matter that all the non-count fields are zero: the first click will fail the curButton test straightaway, so it'll return 1 and set the rest of the structure accordingly
|
||||
struct clickCounter {
|
||||
int curButton;
|
||||
int rectX0;
|
||||
int rectY0;
|
||||
int rectX1;
|
||||
int rectY1;
|
||||
uintptr_t prevTime;
|
||||
int count;
|
||||
};
|
||||
int clickCounterClick(clickCounter *c, int button, int x, int y, uintptr_t time, uintptr_t maxTime, int32_t xdist, int32_t ydist);
|
||||
extern void clickCounterReset(clickCounter *);
|
||||
extern int fromScancode(uintptr_t, uiAreaKeyEvent *);
|
||||
|
||||
// matrix.c
|
||||
extern void fallbackSkew(uiDrawMatrix *, double, double, double, double);
|
||||
extern void scaleCenter(double, double, double *, double *);
|
||||
extern void fallbackTransformSize(uiDrawMatrix *, double *, double *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
8
deps/libui/common/userbugs.c
vendored
8
deps/libui/common/userbugs.c
vendored
@ -1,8 +0,0 @@
|
||||
// 22 may 2016
|
||||
#include "../ui.h"
|
||||
#include "uipriv.h"
|
||||
|
||||
void uiUserBugCannotSetParentOnToplevel(const char *type)
|
||||
{
|
||||
userbug("You cannot make a %s a child of another uiControl,", type);
|
||||
}
|
79
deps/libui/darwin/CMakeLists.txt
vendored
79
deps/libui/darwin/CMakeLists.txt
vendored
@ -1,79 +0,0 @@
|
||||
# 3 june 2016
|
||||
|
||||
list(APPEND _LIBUI_SOURCES
|
||||
darwin/alloc.m
|
||||
darwin/area.m
|
||||
darwin/areaevents.m
|
||||
darwin/autolayout.m
|
||||
darwin/box.m
|
||||
darwin/button.m
|
||||
darwin/checkbox.m
|
||||
darwin/colorbutton.m
|
||||
darwin/combobox.m
|
||||
darwin/control.m
|
||||
darwin/datetimepicker.m
|
||||
darwin/debug.m
|
||||
darwin/draw.m
|
||||
darwin/drawtext.m
|
||||
darwin/editablecombo.m
|
||||
darwin/entry.m
|
||||
darwin/fontbutton.m
|
||||
darwin/form.m
|
||||
darwin/grid.m
|
||||
darwin/group.m
|
||||
darwin/image.m
|
||||
darwin/label.m
|
||||
darwin/main.m
|
||||
darwin/map.m
|
||||
darwin/menu.m
|
||||
darwin/multilineentry.m
|
||||
darwin/progressbar.m
|
||||
darwin/radiobuttons.m
|
||||
darwin/scrollview.m
|
||||
darwin/separator.m
|
||||
darwin/slider.m
|
||||
darwin/spinbox.m
|
||||
darwin/stddialogs.m
|
||||
darwin/tab.m
|
||||
darwin/text.m
|
||||
darwin/util.m
|
||||
darwin/window.m
|
||||
darwin/winmoveresize.m
|
||||
)
|
||||
set(_LIBUI_SOURCES ${_LIBUI_SOURCES} PARENT_SCOPE)
|
||||
|
||||
list(APPEND _LIBUI_INCLUDEDIRS
|
||||
darwin
|
||||
)
|
||||
set(_LIBUI_INCLUDEDIRS _LIBUI_INCLUDEDIRS PARENT_SCOPE)
|
||||
|
||||
set(_LIBUINAME libui PARENT_SCOPE)
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
set(_LIBUINAME libui-temporary PARENT_SCOPE)
|
||||
endif()
|
||||
# thanks to Mr-Hide in irc.freenode.net/#cmake
|
||||
macro(_handle_static)
|
||||
set_target_properties(${_LIBUINAME} PROPERTIES
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
|
||||
set(_aname $<TARGET_FILE:${_LIBUINAME}>)
|
||||
set(_lname libui-combined.list)
|
||||
set(_oname libui-combined.o)
|
||||
add_custom_command(
|
||||
OUTPUT ${_oname}
|
||||
COMMAND
|
||||
nm -m ${_aname} | sed -E -n "'s/^[0-9a-f]* \\([A-Z_]+,[a-z_]+\\) external //p'" > ${_lname}
|
||||
COMMAND
|
||||
ld -exported_symbols_list ${_lname} -r -all_load ${_aname} -o ${_oname}
|
||||
COMMENT "Removing hidden symbols")
|
||||
add_library(libui STATIC ${_oname})
|
||||
# otherwise cmake won't know which linker to use
|
||||
set_target_properties(libui PROPERTIES
|
||||
LINKER_LANGUAGE C)
|
||||
set(_aname)
|
||||
set(_lname)
|
||||
set(_oname)
|
||||
endmacro()
|
||||
|
||||
set(_LIBUI_LIBS
|
||||
objc "-framework Foundation" "-framework AppKit"
|
||||
PARENT_SCOPE)
|
89
deps/libui/darwin/alloc.m
vendored
89
deps/libui/darwin/alloc.m
vendored
@ -1,89 +0,0 @@
|
||||
// 4 december 2014
|
||||
#import <stdlib.h>
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
static NSMutableArray *allocations;
|
||||
NSMutableArray *delegates;
|
||||
|
||||
void initAlloc(void)
|
||||
{
|
||||
allocations = [NSMutableArray new];
|
||||
delegates = [NSMutableArray new];
|
||||
}
|
||||
|
||||
#define UINT8(p) ((uint8_t *) (p))
|
||||
#define PVOID(p) ((void *) (p))
|
||||
#define EXTRA (sizeof (size_t) + sizeof (const char **))
|
||||
#define DATA(p) PVOID(UINT8(p) + EXTRA)
|
||||
#define BASE(p) PVOID(UINT8(p) - EXTRA)
|
||||
#define SIZE(p) ((size_t *) (p))
|
||||
#define CCHAR(p) ((const char **) (p))
|
||||
#define TYPE(p) CCHAR(UINT8(p) + sizeof (size_t))
|
||||
|
||||
void uninitAlloc(void)
|
||||
{
|
||||
NSMutableString *str;
|
||||
NSValue *v;
|
||||
|
||||
[delegates release];
|
||||
if ([allocations count] == 0) {
|
||||
[allocations release];
|
||||
return;
|
||||
}
|
||||
str = [NSMutableString new];
|
||||
for (v in allocations) {
|
||||
void *ptr;
|
||||
|
||||
ptr = [v pointerValue];
|
||||
[str appendString:[NSString stringWithFormat:@"%p %s\n", ptr, *TYPE(ptr)]];
|
||||
}
|
||||
userbug("Some data was leaked; either you left a uiControl lying around or there's a bug in libui itself. Leaked data:\n%s", [str UTF8String]);
|
||||
[str release];
|
||||
}
|
||||
|
||||
void *uiAlloc(size_t size, const char *type)
|
||||
{
|
||||
void *out;
|
||||
|
||||
out = malloc(EXTRA + size);
|
||||
if (out == NULL) {
|
||||
fprintf(stderr, "memory exhausted in uiAlloc()\n");
|
||||
abort();
|
||||
}
|
||||
memset(DATA(out), 0, size);
|
||||
*SIZE(out) = size;
|
||||
*TYPE(out) = type;
|
||||
[allocations addObject:[NSValue valueWithPointer:out]];
|
||||
return DATA(out);
|
||||
}
|
||||
|
||||
void *uiRealloc(void *p, size_t new, const char *type)
|
||||
{
|
||||
void *out;
|
||||
size_t *s;
|
||||
|
||||
if (p == NULL)
|
||||
return uiAlloc(new, type);
|
||||
p = BASE(p);
|
||||
out = realloc(p, EXTRA + new);
|
||||
if (out == NULL) {
|
||||
fprintf(stderr, "memory exhausted in uiRealloc()\n");
|
||||
abort();
|
||||
}
|
||||
s = SIZE(out);
|
||||
if (new <= *s)
|
||||
memset(((uint8_t *) DATA(out)) + *s, 0, new - *s);
|
||||
*s = new;
|
||||
[allocations removeObject:[NSValue valueWithPointer:p]];
|
||||
[allocations addObject:[NSValue valueWithPointer:out]];
|
||||
return DATA(out);
|
||||
}
|
||||
|
||||
void uiFree(void *p)
|
||||
{
|
||||
if (p == NULL)
|
||||
implbug("attempt to uiFree(NULL)");
|
||||
p = BASE(p);
|
||||
free(p);
|
||||
[allocations removeObject:[NSValue valueWithPointer:p]];
|
||||
}
|
475
deps/libui/darwin/area.m
vendored
475
deps/libui/darwin/area.m
vendored
@ -1,475 +0,0 @@
|
||||
// 9 september 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// 10.8 fixups
|
||||
#define NSEventModifierFlags NSUInteger
|
||||
|
||||
@interface areaView : NSView {
|
||||
uiArea *libui_a;
|
||||
NSTrackingArea *libui_ta;
|
||||
NSSize libui_ss;
|
||||
BOOL libui_enabled;
|
||||
}
|
||||
- (id)initWithFrame:(NSRect)r area:(uiArea *)a;
|
||||
- (uiModifiers)parseModifiers:(NSEvent *)e;
|
||||
- (void)doMouseEvent:(NSEvent *)e;
|
||||
- (int)sendKeyEvent:(uiAreaKeyEvent *)ke;
|
||||
- (int)doKeyDownUp:(NSEvent *)e up:(int)up;
|
||||
- (int)doKeyDown:(NSEvent *)e;
|
||||
- (int)doKeyUp:(NSEvent *)e;
|
||||
- (int)doFlagsChanged:(NSEvent *)e;
|
||||
- (void)setupNewTrackingArea;
|
||||
- (void)setScrollingSize:(NSSize)s;
|
||||
- (BOOL)isEnabled;
|
||||
- (void)setEnabled:(BOOL)e;
|
||||
@end
|
||||
|
||||
struct uiArea {
|
||||
uiDarwinControl c;
|
||||
NSView *view; // either sv or area depending on whether it is scrolling
|
||||
NSScrollView *sv;
|
||||
areaView *area;
|
||||
struct scrollViewData *d;
|
||||
uiAreaHandler *ah;
|
||||
BOOL scrolling;
|
||||
NSEvent *dragevent;
|
||||
};
|
||||
|
||||
@implementation areaView
|
||||
|
||||
- (id)initWithFrame:(NSRect)r area:(uiArea *)a
|
||||
{
|
||||
self = [super initWithFrame:r];
|
||||
if (self) {
|
||||
self->libui_a = a;
|
||||
[self setupNewTrackingArea];
|
||||
self->libui_ss = r.size;
|
||||
self->libui_enabled = YES;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)drawRect:(NSRect)r
|
||||
{
|
||||
uiArea *a = self->libui_a;
|
||||
CGContextRef c;
|
||||
uiAreaDrawParams dp;
|
||||
|
||||
c = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort];
|
||||
// see draw.m under text for why we need the height
|
||||
dp.Context = newContext(c, [self bounds].size.height);
|
||||
|
||||
dp.AreaWidth = 0;
|
||||
dp.AreaHeight = 0;
|
||||
if (!a->scrolling) {
|
||||
dp.AreaWidth = [self frame].size.width;
|
||||
dp.AreaHeight = [self frame].size.height;
|
||||
}
|
||||
|
||||
dp.ClipX = r.origin.x;
|
||||
dp.ClipY = r.origin.y;
|
||||
dp.ClipWidth = r.size.width;
|
||||
dp.ClipHeight = r.size.height;
|
||||
|
||||
// no need to save or restore the graphics state to reset transformations; Cocoa creates a brand-new context each time
|
||||
(*(a->ah->Draw))(a->ah, a, &dp);
|
||||
|
||||
freeContext(dp.Context);
|
||||
}
|
||||
|
||||
- (BOOL)isFlipped
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)acceptsFirstResponder
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (uiModifiers)parseModifiers:(NSEvent *)e
|
||||
{
|
||||
NSEventModifierFlags mods;
|
||||
uiModifiers m;
|
||||
|
||||
m = 0;
|
||||
mods = [e modifierFlags];
|
||||
if ((mods & NSControlKeyMask) != 0)
|
||||
m |= uiModifierCtrl;
|
||||
if ((mods & NSAlternateKeyMask) != 0)
|
||||
m |= uiModifierAlt;
|
||||
if ((mods & NSShiftKeyMask) != 0)
|
||||
m |= uiModifierShift;
|
||||
if ((mods & NSCommandKeyMask) != 0)
|
||||
m |= uiModifierSuper;
|
||||
return m;
|
||||
}
|
||||
|
||||
- (void)setupNewTrackingArea
|
||||
{
|
||||
self->libui_ta = [[NSTrackingArea alloc] initWithRect:[self bounds]
|
||||
options:(NSTrackingMouseEnteredAndExited |
|
||||
NSTrackingMouseMoved |
|
||||
NSTrackingActiveAlways |
|
||||
NSTrackingInVisibleRect |
|
||||
NSTrackingEnabledDuringMouseDrag)
|
||||
owner:self
|
||||
userInfo:nil];
|
||||
[self addTrackingArea:self->libui_ta];
|
||||
}
|
||||
|
||||
- (void)updateTrackingAreas
|
||||
{
|
||||
[self removeTrackingArea:self->libui_ta];
|
||||
[self->libui_ta release];
|
||||
[self setupNewTrackingArea];
|
||||
}
|
||||
|
||||
// capture on drag is done automatically on OS X
|
||||
- (void)doMouseEvent:(NSEvent *)e
|
||||
{
|
||||
uiArea *a = self->libui_a;
|
||||
uiAreaMouseEvent me;
|
||||
NSPoint point;
|
||||
int buttonNumber;
|
||||
NSUInteger pmb;
|
||||
unsigned int i, max;
|
||||
|
||||
// this will convert point to drawing space
|
||||
// thanks swillits in irc.freenode.net/#macdev
|
||||
point = [self convertPoint:[e locationInWindow] fromView:nil];
|
||||
me.X = point.x;
|
||||
me.Y = point.y;
|
||||
|
||||
me.AreaWidth = 0;
|
||||
me.AreaHeight = 0;
|
||||
if (!a->scrolling) {
|
||||
me.AreaWidth = [self frame].size.width;
|
||||
me.AreaHeight = [self frame].size.height;
|
||||
}
|
||||
|
||||
buttonNumber = [e buttonNumber] + 1;
|
||||
// swap button numbers 2 and 3 (right and middle)
|
||||
if (buttonNumber == 2)
|
||||
buttonNumber = 3;
|
||||
else if (buttonNumber == 3)
|
||||
buttonNumber = 2;
|
||||
|
||||
me.Down = 0;
|
||||
me.Up = 0;
|
||||
me.Count = 0;
|
||||
switch ([e type]) {
|
||||
case NSLeftMouseDown:
|
||||
case NSRightMouseDown:
|
||||
case NSOtherMouseDown:
|
||||
me.Down = buttonNumber;
|
||||
me.Count = [e clickCount];
|
||||
break;
|
||||
case NSLeftMouseUp:
|
||||
case NSRightMouseUp:
|
||||
case NSOtherMouseUp:
|
||||
me.Up = buttonNumber;
|
||||
break;
|
||||
case NSLeftMouseDragged:
|
||||
case NSRightMouseDragged:
|
||||
case NSOtherMouseDragged:
|
||||
// we include the button that triggered the dragged event in the Held fields
|
||||
buttonNumber = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
me.Modifiers = [self parseModifiers:e];
|
||||
|
||||
pmb = [NSEvent pressedMouseButtons];
|
||||
me.Held1To64 = 0;
|
||||
if (buttonNumber != 1 && (pmb & 1) != 0)
|
||||
me.Held1To64 |= 1;
|
||||
if (buttonNumber != 2 && (pmb & 4) != 0)
|
||||
me.Held1To64 |= 2;
|
||||
if (buttonNumber != 3 && (pmb & 2) != 0)
|
||||
me.Held1To64 |= 4;
|
||||
// buttons 4..32
|
||||
// https://developer.apple.com/library/mac/documentation/Carbon/Reference/QuartzEventServicesRef/index.html#//apple_ref/c/tdef/CGMouseButton says Quartz only supports up to 32 buttons
|
||||
max = 32;
|
||||
for (i = 4; i <= max; i++) {
|
||||
uint64_t j;
|
||||
|
||||
if (buttonNumber == i)
|
||||
continue;
|
||||
j = 1 << (i - 1);
|
||||
if ((pmb & j) != 0)
|
||||
me.Held1To64 |= j;
|
||||
}
|
||||
|
||||
if (self->libui_enabled) {
|
||||
// and allow dragging here
|
||||
a->dragevent = e;
|
||||
(*(a->ah->MouseEvent))(a->ah, a, &me);
|
||||
a->dragevent = nil;
|
||||
}
|
||||
}
|
||||
|
||||
#define mouseEvent(name) \
|
||||
- (void)name:(NSEvent *)e \
|
||||
{ \
|
||||
[self doMouseEvent:e]; \
|
||||
}
|
||||
mouseEvent(mouseMoved)
|
||||
mouseEvent(mouseDragged)
|
||||
mouseEvent(rightMouseDragged)
|
||||
mouseEvent(otherMouseDragged)
|
||||
mouseEvent(mouseDown)
|
||||
mouseEvent(rightMouseDown)
|
||||
mouseEvent(otherMouseDown)
|
||||
mouseEvent(mouseUp)
|
||||
mouseEvent(rightMouseUp)
|
||||
mouseEvent(otherMouseUp)
|
||||
|
||||
- (void)mouseEntered:(NSEvent *)e
|
||||
{
|
||||
uiArea *a = self->libui_a;
|
||||
|
||||
if (self->libui_enabled)
|
||||
(*(a->ah->MouseCrossed))(a->ah, a, 0);
|
||||
}
|
||||
|
||||
- (void)mouseExited:(NSEvent *)e
|
||||
{
|
||||
uiArea *a = self->libui_a;
|
||||
|
||||
if (self->libui_enabled)
|
||||
(*(a->ah->MouseCrossed))(a->ah, a, 1);
|
||||
}
|
||||
|
||||
// note: there is no equivalent to WM_CAPTURECHANGED on Mac OS X; there literally is no way to break a grab like that
|
||||
// even if I invoke the task switcher and switch processes, the mouse grab will still be held until I let go of all buttons
|
||||
// therefore, no DragBroken()
|
||||
|
||||
- (int)sendKeyEvent:(uiAreaKeyEvent *)ke
|
||||
{
|
||||
uiArea *a = self->libui_a;
|
||||
|
||||
return (*(a->ah->KeyEvent))(a->ah, a, ke);
|
||||
}
|
||||
|
||||
- (int)doKeyDownUp:(NSEvent *)e up:(int)up
|
||||
{
|
||||
uiAreaKeyEvent ke;
|
||||
|
||||
ke.Key = 0;
|
||||
ke.ExtKey = 0;
|
||||
ke.Modifier = 0;
|
||||
|
||||
ke.Modifiers = [self parseModifiers:e];
|
||||
|
||||
ke.Up = up;
|
||||
|
||||
if (!fromKeycode([e keyCode], &ke))
|
||||
return 0;
|
||||
return [self sendKeyEvent:&ke];
|
||||
}
|
||||
|
||||
- (int)doKeyDown:(NSEvent *)e
|
||||
{
|
||||
return [self doKeyDownUp:e up:0];
|
||||
}
|
||||
|
||||
- (int)doKeyUp:(NSEvent *)e
|
||||
{
|
||||
return [self doKeyDownUp:e up:1];
|
||||
}
|
||||
|
||||
- (int)doFlagsChanged:(NSEvent *)e
|
||||
{
|
||||
uiAreaKeyEvent ke;
|
||||
uiModifiers whichmod;
|
||||
|
||||
ke.Key = 0;
|
||||
ke.ExtKey = 0;
|
||||
|
||||
// Mac OS X sends this event on both key up and key down.
|
||||
// Fortunately -[e keyCode] IS valid here, so we can simply map from key code to Modifiers, get the value of [e modifierFlags], and check if the respective bit is set or not — that will give us the up/down state
|
||||
if (!keycodeModifier([e keyCode], &whichmod))
|
||||
return 0;
|
||||
ke.Modifier = whichmod;
|
||||
ke.Modifiers = [self parseModifiers:e];
|
||||
ke.Up = (ke.Modifiers & ke.Modifier) == 0;
|
||||
// and then drop the current modifier from Modifiers
|
||||
ke.Modifiers &= ~ke.Modifier;
|
||||
return [self sendKeyEvent:&ke];
|
||||
}
|
||||
|
||||
- (void)setFrameSize:(NSSize)size
|
||||
{
|
||||
uiArea *a = self->libui_a;
|
||||
|
||||
[super setFrameSize:size];
|
||||
if (!a->scrolling)
|
||||
// we must redraw everything on resize because Windows requires it
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
// TODO does this update the frame?
|
||||
- (void)setScrollingSize:(NSSize)s
|
||||
{
|
||||
self->libui_ss = s;
|
||||
[self invalidateIntrinsicContentSize];
|
||||
}
|
||||
|
||||
- (NSSize)intrinsicContentSize
|
||||
{
|
||||
if (!self->libui_a->scrolling)
|
||||
return [super intrinsicContentSize];
|
||||
return self->libui_ss;
|
||||
}
|
||||
|
||||
- (BOOL)becomeFirstResponder
|
||||
{
|
||||
return [self isEnabled];
|
||||
}
|
||||
|
||||
- (BOOL)isEnabled
|
||||
{
|
||||
return self->libui_enabled;
|
||||
}
|
||||
|
||||
- (void)setEnabled:(BOOL)e
|
||||
{
|
||||
self->libui_enabled = e;
|
||||
if (!self->libui_enabled && [self window] != nil)
|
||||
if ([[self window] firstResponder] == self)
|
||||
[[self window] makeFirstResponder:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
uiDarwinControlAllDefaultsExceptDestroy(uiArea, view)
|
||||
|
||||
static void uiAreaDestroy(uiControl *c)
|
||||
{
|
||||
uiArea *a = uiArea(c);
|
||||
|
||||
if (a->scrolling)
|
||||
scrollViewFreeData(a->sv, a->d);
|
||||
[a->area release];
|
||||
if (a->scrolling)
|
||||
[a->sv release];
|
||||
uiFreeControl(uiControl(a));
|
||||
}
|
||||
|
||||
// called by subclasses of -[NSApplication sendEvent:]
|
||||
// by default, NSApplication eats some key events
|
||||
// this prevents that from happening with uiArea
|
||||
// see http://stackoverflow.com/questions/24099063/how-do-i-detect-keyup-in-my-nsview-with-the-command-key-held and http://lists.apple.com/archives/cocoa-dev/2003/Oct/msg00442.html
|
||||
int sendAreaEvents(NSEvent *e)
|
||||
{
|
||||
NSEventType type;
|
||||
id focused;
|
||||
areaView *view;
|
||||
|
||||
type = [e type];
|
||||
if (type != NSKeyDown && type != NSKeyUp && type != NSFlagsChanged)
|
||||
return 0;
|
||||
focused = [[e window] firstResponder];
|
||||
if (focused == nil)
|
||||
return 0;
|
||||
if (![focused isKindOfClass:[areaView class]])
|
||||
return 0;
|
||||
view = (areaView *) focused;
|
||||
switch (type) {
|
||||
case NSKeyDown:
|
||||
return [view doKeyDown:e];
|
||||
case NSKeyUp:
|
||||
return [view doKeyUp:e];
|
||||
case NSFlagsChanged:
|
||||
return [view doFlagsChanged:e];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void uiAreaSetSize(uiArea *a, int width, int height)
|
||||
{
|
||||
if (!a->scrolling)
|
||||
userbug("You cannot call uiAreaSetSize() on a non-scrolling uiArea. (area: %p)", a);
|
||||
[a->area setScrollingSize:NSMakeSize(width, height)];
|
||||
}
|
||||
|
||||
void uiAreaQueueRedrawAll(uiArea *a)
|
||||
{
|
||||
[a->area setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
void uiAreaScrollTo(uiArea *a, double x, double y, double width, double height)
|
||||
{
|
||||
if (!a->scrolling)
|
||||
userbug("You cannot call uiAreaScrollTo() on a non-scrolling uiArea. (area: %p)", a);
|
||||
[a->area scrollRectToVisible:NSMakeRect(x, y, width, height)];
|
||||
// don't worry about the return value; it just says whether scrolling was needed
|
||||
}
|
||||
|
||||
void uiAreaBeginUserWindowMove(uiArea *a)
|
||||
{
|
||||
libuiNSWindow *w;
|
||||
|
||||
w = (libuiNSWindow *) [a->area window];
|
||||
if (w == nil)
|
||||
return; // TODO
|
||||
if (a->dragevent == nil)
|
||||
return; // TODO
|
||||
[w libui_doMove:a->dragevent];
|
||||
}
|
||||
|
||||
void uiAreaBeginUserWindowResize(uiArea *a, uiWindowResizeEdge edge)
|
||||
{
|
||||
libuiNSWindow *w;
|
||||
|
||||
w = (libuiNSWindow *) [a->area window];
|
||||
if (w == nil)
|
||||
return; // TODO
|
||||
if (a->dragevent == nil)
|
||||
return; // TODO
|
||||
[w libui_doResize:a->dragevent on:edge];
|
||||
}
|
||||
|
||||
uiArea *uiNewArea(uiAreaHandler *ah)
|
||||
{
|
||||
uiArea *a;
|
||||
|
||||
uiDarwinNewControl(uiArea, a);
|
||||
|
||||
a->ah = ah;
|
||||
a->scrolling = NO;
|
||||
|
||||
a->area = [[areaView alloc] initWithFrame:NSZeroRect area:a];
|
||||
|
||||
a->view = a->area;
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
uiArea *uiNewScrollingArea(uiAreaHandler *ah, int width, int height)
|
||||
{
|
||||
uiArea *a;
|
||||
struct scrollViewCreateParams p;
|
||||
|
||||
uiDarwinNewControl(uiArea, a);
|
||||
|
||||
a->ah = ah;
|
||||
a->scrolling = YES;
|
||||
|
||||
a->area = [[areaView alloc] initWithFrame:NSMakeRect(0, 0, width, height)
|
||||
area:a];
|
||||
|
||||
memset(&p, 0, sizeof (struct scrollViewCreateParams));
|
||||
p.DocumentView = a->area;
|
||||
p.BackgroundColor = [NSColor controlColor];
|
||||
p.DrawsBackground = 1;
|
||||
p.Bordered = NO;
|
||||
p.HScroll = YES;
|
||||
p.VScroll = YES;
|
||||
a->sv = mkScrollView(&p, &(a->d));
|
||||
|
||||
a->view = a->sv;
|
||||
|
||||
return a;
|
||||
}
|
159
deps/libui/darwin/areaevents.m
vendored
159
deps/libui/darwin/areaevents.m
vendored
@ -1,159 +0,0 @@
|
||||
// 30 march 2014
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
/*
|
||||
Mac OS X uses its own set of hardware key codes that are different from PC keyboard scancodes, but are positional (like PC keyboard scancodes). These are defined in <HIToolbox/Events.h>, a Carbon header. As far as I can tell, there's no way to include this header without either using an absolute path or linking Carbon into the program, so the constant values are used here instead.
|
||||
|
||||
The Cocoa docs do guarantee that -[NSEvent keyCode] results in key codes that are the same as those returned by Carbon; that is, these codes.
|
||||
*/
|
||||
|
||||
// use uintptr_t to be safe
|
||||
static const struct {
|
||||
uintptr_t keycode;
|
||||
char equiv;
|
||||
} keycodeKeys[] = {
|
||||
{ 0x00, 'a' },
|
||||
{ 0x01, 's' },
|
||||
{ 0x02, 'd' },
|
||||
{ 0x03, 'f' },
|
||||
{ 0x04, 'h' },
|
||||
{ 0x05, 'g' },
|
||||
{ 0x06, 'z' },
|
||||
{ 0x07, 'x' },
|
||||
{ 0x08, 'c' },
|
||||
{ 0x09, 'v' },
|
||||
{ 0x0B, 'b' },
|
||||
{ 0x0C, 'q' },
|
||||
{ 0x0D, 'w' },
|
||||
{ 0x0E, 'e' },
|
||||
{ 0x0F, 'r' },
|
||||
{ 0x10, 'y' },
|
||||
{ 0x11, 't' },
|
||||
{ 0x12, '1' },
|
||||
{ 0x13, '2' },
|
||||
{ 0x14, '3' },
|
||||
{ 0x15, '4' },
|
||||
{ 0x16, '6' },
|
||||
{ 0x17, '5' },
|
||||
{ 0x18, '=' },
|
||||
{ 0x19, '9' },
|
||||
{ 0x1A, '7' },
|
||||
{ 0x1B, '-' },
|
||||
{ 0x1C, '8' },
|
||||
{ 0x1D, '0' },
|
||||
{ 0x1E, ']' },
|
||||
{ 0x1F, 'o' },
|
||||
{ 0x20, 'u' },
|
||||
{ 0x21, '[' },
|
||||
{ 0x22, 'i' },
|
||||
{ 0x23, 'p' },
|
||||
{ 0x25, 'l' },
|
||||
{ 0x26, 'j' },
|
||||
{ 0x27, '\'' },
|
||||
{ 0x28, 'k' },
|
||||
{ 0x29, ';' },
|
||||
{ 0x2A, '\\' },
|
||||
{ 0x2B, ',' },
|
||||
{ 0x2C, '/' },
|
||||
{ 0x2D, 'n' },
|
||||
{ 0x2E, 'm' },
|
||||
{ 0x2F, '.' },
|
||||
{ 0x32, '`' },
|
||||
{ 0x24, '\n' },
|
||||
{ 0x30, '\t' },
|
||||
{ 0x31, ' ' },
|
||||
{ 0x33, '\b' },
|
||||
{ 0xFFFF, 0 },
|
||||
};
|
||||
|
||||
static const struct {
|
||||
uintptr_t keycode;
|
||||
uiExtKey equiv;
|
||||
} keycodeExtKeys[] = {
|
||||
{ 0x41, uiExtKeyNDot },
|
||||
{ 0x43, uiExtKeyNMultiply },
|
||||
{ 0x45, uiExtKeyNAdd },
|
||||
{ 0x4B, uiExtKeyNDivide },
|
||||
{ 0x4C, uiExtKeyNEnter },
|
||||
{ 0x4E, uiExtKeyNSubtract },
|
||||
{ 0x52, uiExtKeyN0 },
|
||||
{ 0x53, uiExtKeyN1 },
|
||||
{ 0x54, uiExtKeyN2 },
|
||||
{ 0x55, uiExtKeyN3 },
|
||||
{ 0x56, uiExtKeyN4 },
|
||||
{ 0x57, uiExtKeyN5 },
|
||||
{ 0x58, uiExtKeyN6 },
|
||||
{ 0x59, uiExtKeyN7 },
|
||||
{ 0x5B, uiExtKeyN8 },
|
||||
{ 0x5C, uiExtKeyN9 },
|
||||
{ 0x35, uiExtKeyEscape },
|
||||
{ 0x60, uiExtKeyF5 },
|
||||
{ 0x61, uiExtKeyF6 },
|
||||
{ 0x62, uiExtKeyF7 },
|
||||
{ 0x63, uiExtKeyF3 },
|
||||
{ 0x64, uiExtKeyF8 },
|
||||
{ 0x65, uiExtKeyF9 },
|
||||
{ 0x67, uiExtKeyF11 },
|
||||
{ 0x6D, uiExtKeyF10 },
|
||||
{ 0x6F, uiExtKeyF12 },
|
||||
{ 0x72, uiExtKeyInsert }, // listed as the Help key but it's in the same position on an Apple keyboard as the Insert key on a Windows keyboard; thanks to SeanieB from irc.badnik.net and Psy in irc.freenode.net/#macdev for confirming they have the same code
|
||||
{ 0x73, uiExtKeyHome },
|
||||
{ 0x74, uiExtKeyPageUp },
|
||||
{ 0x75, uiExtKeyDelete },
|
||||
{ 0x76, uiExtKeyF4 },
|
||||
{ 0x77, uiExtKeyEnd },
|
||||
{ 0x78, uiExtKeyF2 },
|
||||
{ 0x79, uiExtKeyPageDown },
|
||||
{ 0x7A, uiExtKeyF1 },
|
||||
{ 0x7B, uiExtKeyLeft },
|
||||
{ 0x7C, uiExtKeyRight },
|
||||
{ 0x7D, uiExtKeyDown },
|
||||
{ 0x7E, uiExtKeyUp },
|
||||
{ 0xFFFF, 0 },
|
||||
};
|
||||
|
||||
static const struct {
|
||||
uintptr_t keycode;
|
||||
uiModifiers equiv;
|
||||
} keycodeModifiers[] = {
|
||||
{ 0x37, uiModifierSuper }, // left command
|
||||
{ 0x38, uiModifierShift }, // left shift
|
||||
{ 0x3A, uiModifierAlt }, // left option
|
||||
{ 0x3B, uiModifierCtrl }, // left control
|
||||
{ 0x3C, uiModifierShift }, // right shift
|
||||
{ 0x3D, uiModifierAlt }, // right alt
|
||||
{ 0x3E, uiModifierCtrl }, // right control
|
||||
// the following is not in Events.h for some reason
|
||||
// thanks to Nicole and jedivulcan from irc.badnik.net
|
||||
{ 0x36, uiModifierSuper }, // right command
|
||||
{ 0xFFFF, 0 },
|
||||
};
|
||||
|
||||
BOOL fromKeycode(unsigned short keycode, uiAreaKeyEvent *ke)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; keycodeKeys[i].keycode != 0xFFFF; i++)
|
||||
if (keycodeKeys[i].keycode == keycode) {
|
||||
ke->Key = keycodeKeys[i].equiv;
|
||||
return YES;
|
||||
}
|
||||
for (i = 0; keycodeExtKeys[i].keycode != 0xFFFF; i++)
|
||||
if (keycodeExtKeys[i].keycode == keycode) {
|
||||
ke->ExtKey = keycodeExtKeys[i].equiv;
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
BOOL keycodeModifier(unsigned short keycode, uiModifiers *mod)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; keycodeModifiers[i].keycode != 0xFFFF; i++)
|
||||
if (keycodeModifiers[i].keycode == keycode) {
|
||||
*mod = keycodeModifiers[i].equiv;
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
161
deps/libui/darwin/autolayout.m
vendored
161
deps/libui/darwin/autolayout.m
vendored
@ -1,161 +0,0 @@
|
||||
// 15 august 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
NSLayoutConstraint *mkConstraint(id view1, NSLayoutAttribute attr1, NSLayoutRelation relation, id view2, NSLayoutAttribute attr2, CGFloat multiplier, CGFloat c, NSString *desc)
|
||||
{
|
||||
NSLayoutConstraint *constraint;
|
||||
|
||||
constraint = [NSLayoutConstraint constraintWithItem:view1
|
||||
attribute:attr1
|
||||
relatedBy:relation
|
||||
toItem:view2
|
||||
attribute:attr2
|
||||
multiplier:multiplier
|
||||
constant:c];
|
||||
// apparently only added in 10.9
|
||||
if ([constraint respondsToSelector:@selector(setIdentifier:)])
|
||||
[((id) constraint) setIdentifier:desc];
|
||||
return constraint;
|
||||
}
|
||||
|
||||
CGFloat uiDarwinMarginAmount(void *reserved)
|
||||
{
|
||||
return 20.0;
|
||||
}
|
||||
|
||||
CGFloat uiDarwinPaddingAmount(void *reserved)
|
||||
{
|
||||
return 8.0;
|
||||
}
|
||||
|
||||
// this is needed for NSSplitView to work properly; see http://stackoverflow.com/questions/34574478/how-can-i-set-the-position-of-a-nssplitview-nowadays-setpositionofdivideratind (stal in irc.freenode.net/#macdev came up with the exact combination)
|
||||
// turns out it also works on NSTabView and NSBox too, possibly others!
|
||||
// and for bonus points, it even seems to fix unsatisfiable-constraint-autoresizing-mask issues with NSTabView and NSBox too!!! this is nuts
|
||||
void jiggleViewLayout(NSView *view)
|
||||
{
|
||||
[view setNeedsLayout:YES];
|
||||
[view layoutSubtreeIfNeeded];
|
||||
}
|
||||
|
||||
static CGFloat margins(int margined)
|
||||
{
|
||||
if (!margined)
|
||||
return 0.0;
|
||||
return uiDarwinMarginAmount(NULL);
|
||||
}
|
||||
|
||||
void singleChildConstraintsEstablish(struct singleChildConstraints *c, NSView *contentView, NSView *childView, BOOL hugsTrailing, BOOL hugsBottom, int margined, NSString *desc)
|
||||
{
|
||||
CGFloat margin;
|
||||
|
||||
margin = margins(margined);
|
||||
|
||||
c->leadingConstraint = mkConstraint(contentView, NSLayoutAttributeLeading,
|
||||
NSLayoutRelationEqual,
|
||||
childView, NSLayoutAttributeLeading,
|
||||
1, -margin,
|
||||
[desc stringByAppendingString:@" leading constraint"]);
|
||||
[contentView addConstraint:c->leadingConstraint];
|
||||
[c->leadingConstraint retain];
|
||||
|
||||
c->topConstraint = mkConstraint(contentView, NSLayoutAttributeTop,
|
||||
NSLayoutRelationEqual,
|
||||
childView, NSLayoutAttributeTop,
|
||||
1, -margin,
|
||||
[desc stringByAppendingString:@" top constraint"]);
|
||||
[contentView addConstraint:c->topConstraint];
|
||||
[c->topConstraint retain];
|
||||
|
||||
c->trailingConstraintGreater = mkConstraint(contentView, NSLayoutAttributeTrailing,
|
||||
NSLayoutRelationGreaterThanOrEqual,
|
||||
childView, NSLayoutAttributeTrailing,
|
||||
1, margin,
|
||||
[desc stringByAppendingString:@" trailing >= constraint"]);
|
||||
if (hugsTrailing)
|
||||
[c->trailingConstraintGreater setPriority:NSLayoutPriorityDefaultLow];
|
||||
[contentView addConstraint:c->trailingConstraintGreater];
|
||||
[c->trailingConstraintGreater retain];
|
||||
|
||||
c->trailingConstraintEqual = mkConstraint(contentView, NSLayoutAttributeTrailing,
|
||||
NSLayoutRelationEqual,
|
||||
childView, NSLayoutAttributeTrailing,
|
||||
1, margin,
|
||||
[desc stringByAppendingString:@" trailing == constraint"]);
|
||||
if (!hugsTrailing)
|
||||
[c->trailingConstraintEqual setPriority:NSLayoutPriorityDefaultLow];
|
||||
[contentView addConstraint:c->trailingConstraintEqual];
|
||||
[c->trailingConstraintEqual retain];
|
||||
|
||||
c->bottomConstraintGreater = mkConstraint(contentView, NSLayoutAttributeBottom,
|
||||
NSLayoutRelationGreaterThanOrEqual,
|
||||
childView, NSLayoutAttributeBottom,
|
||||
1, margin,
|
||||
[desc stringByAppendingString:@" bottom >= constraint"]);
|
||||
if (hugsBottom)
|
||||
[c->bottomConstraintGreater setPriority:NSLayoutPriorityDefaultLow];
|
||||
[contentView addConstraint:c->bottomConstraintGreater];
|
||||
[c->bottomConstraintGreater retain];
|
||||
|
||||
c->bottomConstraintEqual = mkConstraint(contentView, NSLayoutAttributeBottom,
|
||||
NSLayoutRelationEqual,
|
||||
childView, NSLayoutAttributeBottom,
|
||||
1, margin,
|
||||
[desc stringByAppendingString:@" bottom == constraint"]);
|
||||
if (!hugsBottom)
|
||||
[c->bottomConstraintEqual setPriority:NSLayoutPriorityDefaultLow];
|
||||
[contentView addConstraint:c->bottomConstraintEqual];
|
||||
[c->bottomConstraintEqual retain];
|
||||
}
|
||||
|
||||
void singleChildConstraintsRemove(struct singleChildConstraints *c, NSView *cv)
|
||||
{
|
||||
if (c->leadingConstraint != nil) {
|
||||
[cv removeConstraint:c->leadingConstraint];
|
||||
[c->leadingConstraint release];
|
||||
c->leadingConstraint = nil;
|
||||
}
|
||||
if (c->topConstraint != nil) {
|
||||
[cv removeConstraint:c->topConstraint];
|
||||
[c->topConstraint release];
|
||||
c->topConstraint = nil;
|
||||
}
|
||||
if (c->trailingConstraintGreater != nil) {
|
||||
[cv removeConstraint:c->trailingConstraintGreater];
|
||||
[c->trailingConstraintGreater release];
|
||||
c->trailingConstraintGreater = nil;
|
||||
}
|
||||
if (c->trailingConstraintEqual != nil) {
|
||||
[cv removeConstraint:c->trailingConstraintEqual];
|
||||
[c->trailingConstraintEqual release];
|
||||
c->trailingConstraintEqual = nil;
|
||||
}
|
||||
if (c->bottomConstraintGreater != nil) {
|
||||
[cv removeConstraint:c->bottomConstraintGreater];
|
||||
[c->bottomConstraintGreater release];
|
||||
c->bottomConstraintGreater = nil;
|
||||
}
|
||||
if (c->bottomConstraintEqual != nil) {
|
||||
[cv removeConstraint:c->bottomConstraintEqual];
|
||||
[c->bottomConstraintEqual release];
|
||||
c->bottomConstraintEqual = nil;
|
||||
}
|
||||
}
|
||||
|
||||
void singleChildConstraintsSetMargined(struct singleChildConstraints *c, int margined)
|
||||
{
|
||||
CGFloat margin;
|
||||
|
||||
margin = margins(margined);
|
||||
if (c->leadingConstraint != nil)
|
||||
[c->leadingConstraint setConstant:-margin];
|
||||
if (c->topConstraint != nil)
|
||||
[c->topConstraint setConstant:-margin];
|
||||
if (c->trailingConstraintGreater != nil)
|
||||
[c->trailingConstraintGreater setConstant:margin];
|
||||
if (c->trailingConstraintEqual != nil)
|
||||
[c->trailingConstraintEqual setConstant:margin];
|
||||
if (c->bottomConstraintGreater != nil)
|
||||
[c->bottomConstraintGreater setConstant:margin];
|
||||
if (c->bottomConstraintEqual != nil)
|
||||
[c->bottomConstraintEqual setConstant:margin];
|
||||
}
|
469
deps/libui/darwin/box.m
vendored
469
deps/libui/darwin/box.m
vendored
@ -1,469 +0,0 @@
|
||||
// 15 august 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// TODO hiding all stretchy controls still hugs trailing edge
|
||||
|
||||
@interface boxChild : NSObject
|
||||
@property uiControl *c;
|
||||
@property BOOL stretchy;
|
||||
@property NSLayoutPriority oldPrimaryHuggingPri;
|
||||
@property NSLayoutPriority oldSecondaryHuggingPri;
|
||||
- (NSView *)view;
|
||||
@end
|
||||
|
||||
@interface boxView : NSView {
|
||||
uiBox *b;
|
||||
NSMutableArray *children;
|
||||
BOOL vertical;
|
||||
int padded;
|
||||
|
||||
NSLayoutConstraint *first;
|
||||
NSMutableArray *inBetweens;
|
||||
NSLayoutConstraint *last;
|
||||
NSMutableArray *otherConstraints;
|
||||
|
||||
NSLayoutAttribute primaryStart;
|
||||
NSLayoutAttribute primaryEnd;
|
||||
NSLayoutAttribute secondaryStart;
|
||||
NSLayoutAttribute secondaryEnd;
|
||||
NSLayoutAttribute primarySize;
|
||||
NSLayoutConstraintOrientation primaryOrientation;
|
||||
NSLayoutConstraintOrientation secondaryOrientation;
|
||||
}
|
||||
- (id)initWithVertical:(BOOL)vert b:(uiBox *)bb;
|
||||
- (void)onDestroy;
|
||||
- (void)removeOurConstraints;
|
||||
- (void)syncEnableStates:(int)enabled;
|
||||
- (CGFloat)paddingAmount;
|
||||
- (void)establishOurConstraints;
|
||||
- (void)append:(uiControl *)c stretchy:(int)stretchy;
|
||||
- (void)delete:(int)n;
|
||||
- (int)isPadded;
|
||||
- (void)setPadded:(int)p;
|
||||
- (BOOL)hugsTrailing;
|
||||
- (BOOL)hugsBottom;
|
||||
- (int)nStretchy;
|
||||
@end
|
||||
|
||||
struct uiBox {
|
||||
uiDarwinControl c;
|
||||
boxView *view;
|
||||
};
|
||||
|
||||
@implementation boxChild
|
||||
|
||||
- (NSView *)view
|
||||
{
|
||||
return (NSView *) uiControlHandle(self.c);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation boxView
|
||||
|
||||
- (id)initWithVertical:(BOOL)vert b:(uiBox *)bb
|
||||
{
|
||||
self = [super initWithFrame:NSZeroRect];
|
||||
if (self != nil) {
|
||||
// the weird names vert and bb are to shut the compiler up about shadowing because implicit this/self is stupid
|
||||
self->b = bb;
|
||||
self->vertical = vert;
|
||||
self->padded = 0;
|
||||
self->children = [NSMutableArray new];
|
||||
|
||||
self->inBetweens = [NSMutableArray new];
|
||||
self->otherConstraints = [NSMutableArray new];
|
||||
|
||||
if (self->vertical) {
|
||||
self->primaryStart = NSLayoutAttributeTop;
|
||||
self->primaryEnd = NSLayoutAttributeBottom;
|
||||
self->secondaryStart = NSLayoutAttributeLeading;
|
||||
self->secondaryEnd = NSLayoutAttributeTrailing;
|
||||
self->primarySize = NSLayoutAttributeHeight;
|
||||
self->primaryOrientation = NSLayoutConstraintOrientationVertical;
|
||||
self->secondaryOrientation = NSLayoutConstraintOrientationHorizontal;
|
||||
} else {
|
||||
self->primaryStart = NSLayoutAttributeLeading;
|
||||
self->primaryEnd = NSLayoutAttributeTrailing;
|
||||
self->secondaryStart = NSLayoutAttributeTop;
|
||||
self->secondaryEnd = NSLayoutAttributeBottom;
|
||||
self->primarySize = NSLayoutAttributeWidth;
|
||||
self->primaryOrientation = NSLayoutConstraintOrientationHorizontal;
|
||||
self->secondaryOrientation = NSLayoutConstraintOrientationVertical;
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)onDestroy
|
||||
{
|
||||
boxChild *bc;
|
||||
|
||||
[self removeOurConstraints];
|
||||
[self->inBetweens release];
|
||||
[self->otherConstraints release];
|
||||
|
||||
for (bc in self->children) {
|
||||
uiControlSetParent(bc.c, NULL);
|
||||
uiDarwinControlSetSuperview(uiDarwinControl(bc.c), nil);
|
||||
uiControlDestroy(bc.c);
|
||||
}
|
||||
[self->children release];
|
||||
}
|
||||
|
||||
- (void)removeOurConstraints
|
||||
{
|
||||
if (self->first != nil) {
|
||||
[self removeConstraint:self->first];
|
||||
[self->first release];
|
||||
self->first = nil;
|
||||
}
|
||||
if ([self->inBetweens count] != 0) {
|
||||
[self removeConstraints:self->inBetweens];
|
||||
[self->inBetweens removeAllObjects];
|
||||
}
|
||||
if (self->last != nil) {
|
||||
[self removeConstraint:self->last];
|
||||
[self->last release];
|
||||
self->last = nil;
|
||||
}
|
||||
if ([self->otherConstraints count] != 0) {
|
||||
[self removeConstraints:self->otherConstraints];
|
||||
[self->otherConstraints removeAllObjects];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)syncEnableStates:(int)enabled
|
||||
{
|
||||
boxChild *bc;
|
||||
|
||||
for (bc in self->children)
|
||||
uiDarwinControlSyncEnableState(uiDarwinControl(bc.c), enabled);
|
||||
}
|
||||
|
||||
- (CGFloat)paddingAmount
|
||||
{
|
||||
if (!self->padded)
|
||||
return 0.0;
|
||||
return uiDarwinPaddingAmount(NULL);
|
||||
}
|
||||
|
||||
- (void)establishOurConstraints
|
||||
{
|
||||
boxChild *bc;
|
||||
CGFloat padding;
|
||||
NSView *prev;
|
||||
NSLayoutConstraint *c;
|
||||
BOOL (*hugsSecondary)(uiDarwinControl *);
|
||||
|
||||
[self removeOurConstraints];
|
||||
if ([self->children count] == 0)
|
||||
return;
|
||||
padding = [self paddingAmount];
|
||||
|
||||
// first arrange in the primary direction
|
||||
prev = nil;
|
||||
for (bc in self->children) {
|
||||
if (!uiControlVisible(bc.c))
|
||||
continue;
|
||||
if (prev == nil) { // first view
|
||||
self->first = mkConstraint(self, self->primaryStart,
|
||||
NSLayoutRelationEqual,
|
||||
[bc view], self->primaryStart,
|
||||
1, 0,
|
||||
@"uiBox first primary constraint");
|
||||
[self addConstraint:self->first];
|
||||
[self->first retain];
|
||||
prev = [bc view];
|
||||
continue;
|
||||
}
|
||||
// not the first; link it
|
||||
c = mkConstraint(prev, self->primaryEnd,
|
||||
NSLayoutRelationEqual,
|
||||
[bc view], self->primaryStart,
|
||||
1, -padding,
|
||||
@"uiBox in-between primary constraint");
|
||||
[self addConstraint:c];
|
||||
[self->inBetweens addObject:c];
|
||||
prev = [bc view];
|
||||
}
|
||||
if (prev == nil) // no control visible; act as if no controls
|
||||
return;
|
||||
self->last = mkConstraint(prev, self->primaryEnd,
|
||||
NSLayoutRelationEqual,
|
||||
self, self->primaryEnd,
|
||||
1, 0,
|
||||
@"uiBox last primary constraint");
|
||||
[self addConstraint:self->last];
|
||||
[self->last retain];
|
||||
|
||||
// then arrange in the secondary direction
|
||||
hugsSecondary = uiDarwinControlHugsTrailingEdge;
|
||||
if (!self->vertical)
|
||||
hugsSecondary = uiDarwinControlHugsBottom;
|
||||
for (bc in self->children) {
|
||||
if (!uiControlVisible(bc.c))
|
||||
continue;
|
||||
c = mkConstraint(self, self->secondaryStart,
|
||||
NSLayoutRelationEqual,
|
||||
[bc view], self->secondaryStart,
|
||||
1, 0,
|
||||
@"uiBox secondary start constraint");
|
||||
[self addConstraint:c];
|
||||
[self->otherConstraints addObject:c];
|
||||
c = mkConstraint([bc view], self->secondaryEnd,
|
||||
NSLayoutRelationLessThanOrEqual,
|
||||
self, self->secondaryEnd,
|
||||
1, 0,
|
||||
@"uiBox secondary end <= constraint");
|
||||
if ((*hugsSecondary)(uiDarwinControl(bc.c)))
|
||||
[c setPriority:NSLayoutPriorityDefaultLow];
|
||||
[self addConstraint:c];
|
||||
[self->otherConstraints addObject:c];
|
||||
c = mkConstraint([bc view], self->secondaryEnd,
|
||||
NSLayoutRelationEqual,
|
||||
self, self->secondaryEnd,
|
||||
1, 0,
|
||||
@"uiBox secondary end == constraint");
|
||||
if (!(*hugsSecondary)(uiDarwinControl(bc.c)))
|
||||
[c setPriority:NSLayoutPriorityDefaultLow];
|
||||
[self addConstraint:c];
|
||||
[self->otherConstraints addObject:c];
|
||||
}
|
||||
|
||||
// and make all stretchy controls the same size
|
||||
if ([self nStretchy] == 0)
|
||||
return;
|
||||
prev = nil; // first stretchy view
|
||||
for (bc in self->children) {
|
||||
if (!uiControlVisible(bc.c))
|
||||
continue;
|
||||
if (!bc.stretchy)
|
||||
continue;
|
||||
if (prev == nil) {
|
||||
prev = [bc view];
|
||||
continue;
|
||||
}
|
||||
c = mkConstraint(prev, self->primarySize,
|
||||
NSLayoutRelationEqual,
|
||||
[bc view], self->primarySize,
|
||||
1, 0,
|
||||
@"uiBox stretchy size constraint");
|
||||
[self addConstraint:c];
|
||||
[self->otherConstraints addObject:c];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)append:(uiControl *)c stretchy:(int)stretchy
|
||||
{
|
||||
boxChild *bc;
|
||||
NSLayoutPriority priority;
|
||||
int oldnStretchy;
|
||||
|
||||
bc = [boxChild new];
|
||||
bc.c = c;
|
||||
bc.stretchy = stretchy;
|
||||
bc.oldPrimaryHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(bc.c), self->primaryOrientation);
|
||||
bc.oldSecondaryHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(bc.c), self->secondaryOrientation);
|
||||
|
||||
uiControlSetParent(bc.c, uiControl(self->b));
|
||||
uiDarwinControlSetSuperview(uiDarwinControl(bc.c), self);
|
||||
uiDarwinControlSyncEnableState(uiDarwinControl(bc.c), uiControlEnabledToUser(uiControl(self->b)));
|
||||
|
||||
// if a control is stretchy, it should not hug in the primary direction
|
||||
// otherwise, it should *forcibly* hug
|
||||
if (bc.stretchy)
|
||||
priority = NSLayoutPriorityDefaultLow;
|
||||
else
|
||||
// LONGTERM will default high work?
|
||||
priority = NSLayoutPriorityRequired;
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(bc.c), priority, self->primaryOrientation);
|
||||
// make sure controls don't hug their secondary direction so they fill the width of the view
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(bc.c), NSLayoutPriorityDefaultLow, self->secondaryOrientation);
|
||||
|
||||
oldnStretchy = [self nStretchy];
|
||||
[self->children addObject:bc];
|
||||
|
||||
[self establishOurConstraints];
|
||||
if (bc.stretchy)
|
||||
if (oldnStretchy == 0)
|
||||
uiDarwinNotifyEdgeHuggingChanged(uiDarwinControl(self->b));
|
||||
|
||||
[bc release]; // we don't need the initial reference now
|
||||
}
|
||||
|
||||
- (void)delete:(int)n
|
||||
{
|
||||
boxChild *bc;
|
||||
int stretchy;
|
||||
|
||||
bc = (boxChild *) [self->children objectAtIndex:n];
|
||||
stretchy = bc.stretchy;
|
||||
|
||||
uiControlSetParent(bc.c, NULL);
|
||||
uiDarwinControlSetSuperview(uiDarwinControl(bc.c), nil);
|
||||
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(bc.c), bc.oldPrimaryHuggingPri, self->primaryOrientation);
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(bc.c), bc.oldSecondaryHuggingPri, self->secondaryOrientation);
|
||||
|
||||
[self->children removeObjectAtIndex:n];
|
||||
|
||||
[self establishOurConstraints];
|
||||
if (stretchy)
|
||||
if ([self nStretchy] == 0)
|
||||
uiDarwinNotifyEdgeHuggingChanged(uiDarwinControl(self->b));
|
||||
}
|
||||
|
||||
- (int)isPadded
|
||||
{
|
||||
return self->padded;
|
||||
}
|
||||
|
||||
- (void)setPadded:(int)p
|
||||
{
|
||||
CGFloat padding;
|
||||
NSLayoutConstraint *c;
|
||||
|
||||
self->padded = p;
|
||||
padding = [self paddingAmount];
|
||||
for (c in self->inBetweens)
|
||||
[c setConstant:-padding];
|
||||
}
|
||||
|
||||
- (BOOL)hugsTrailing
|
||||
{
|
||||
if (self->vertical) // always hug if vertical
|
||||
return YES;
|
||||
return [self nStretchy] != 0;
|
||||
}
|
||||
|
||||
- (BOOL)hugsBottom
|
||||
{
|
||||
if (!self->vertical) // always hug if horizontal
|
||||
return YES;
|
||||
return [self nStretchy] != 0;
|
||||
}
|
||||
|
||||
- (int)nStretchy
|
||||
{
|
||||
boxChild *bc;
|
||||
int n;
|
||||
|
||||
n = 0;
|
||||
for (bc in self->children) {
|
||||
if (!uiControlVisible(bc.c))
|
||||
continue;
|
||||
if (bc.stretchy)
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static void uiBoxDestroy(uiControl *c)
|
||||
{
|
||||
uiBox *b = uiBox(c);
|
||||
|
||||
[b->view onDestroy];
|
||||
[b->view release];
|
||||
uiFreeControl(uiControl(b));
|
||||
}
|
||||
|
||||
uiDarwinControlDefaultHandle(uiBox, view)
|
||||
uiDarwinControlDefaultParent(uiBox, view)
|
||||
uiDarwinControlDefaultSetParent(uiBox, view)
|
||||
uiDarwinControlDefaultToplevel(uiBox, view)
|
||||
uiDarwinControlDefaultVisible(uiBox, view)
|
||||
uiDarwinControlDefaultShow(uiBox, view)
|
||||
uiDarwinControlDefaultHide(uiBox, view)
|
||||
uiDarwinControlDefaultEnabled(uiBox, view)
|
||||
uiDarwinControlDefaultEnable(uiBox, view)
|
||||
uiDarwinControlDefaultDisable(uiBox, view)
|
||||
|
||||
static void uiBoxSyncEnableState(uiDarwinControl *c, int enabled)
|
||||
{
|
||||
uiBox *b = uiBox(c);
|
||||
|
||||
if (uiDarwinShouldStopSyncEnableState(uiDarwinControl(b), enabled))
|
||||
return;
|
||||
[b->view syncEnableStates:enabled];
|
||||
}
|
||||
|
||||
uiDarwinControlDefaultSetSuperview(uiBox, view)
|
||||
|
||||
static BOOL uiBoxHugsTrailingEdge(uiDarwinControl *c)
|
||||
{
|
||||
uiBox *b = uiBox(c);
|
||||
|
||||
return [b->view hugsTrailing];
|
||||
}
|
||||
|
||||
static BOOL uiBoxHugsBottom(uiDarwinControl *c)
|
||||
{
|
||||
uiBox *b = uiBox(c);
|
||||
|
||||
return [b->view hugsBottom];
|
||||
}
|
||||
|
||||
static void uiBoxChildEdgeHuggingChanged(uiDarwinControl *c)
|
||||
{
|
||||
uiBox *b = uiBox(c);
|
||||
|
||||
[b->view establishOurConstraints];
|
||||
}
|
||||
|
||||
uiDarwinControlDefaultHuggingPriority(uiBox, view)
|
||||
uiDarwinControlDefaultSetHuggingPriority(uiBox, view)
|
||||
|
||||
static void uiBoxChildVisibilityChanged(uiDarwinControl *c)
|
||||
{
|
||||
uiBox *b = uiBox(c);
|
||||
|
||||
[b->view establishOurConstraints];
|
||||
}
|
||||
|
||||
void uiBoxAppend(uiBox *b, uiControl *c, int stretchy)
|
||||
{
|
||||
// LONGTERM on other platforms
|
||||
// or at leat allow this and implicitly turn it into a spacer
|
||||
if (c == NULL)
|
||||
userbug("You cannot add NULL to a uiBox.");
|
||||
[b->view append:c stretchy:stretchy];
|
||||
}
|
||||
|
||||
void uiBoxDelete(uiBox *b, int n)
|
||||
{
|
||||
[b->view delete:n];
|
||||
}
|
||||
|
||||
int uiBoxPadded(uiBox *b)
|
||||
{
|
||||
return [b->view isPadded];
|
||||
}
|
||||
|
||||
void uiBoxSetPadded(uiBox *b, int padded)
|
||||
{
|
||||
[b->view setPadded:padded];
|
||||
}
|
||||
|
||||
static uiBox *finishNewBox(BOOL vertical)
|
||||
{
|
||||
uiBox *b;
|
||||
|
||||
uiDarwinNewControl(uiBox, b);
|
||||
|
||||
b->view = [[boxView alloc] initWithVertical:vertical b:b];
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
uiBox *uiNewHorizontalBox(void)
|
||||
{
|
||||
return finishNewBox(NO);
|
||||
}
|
||||
|
||||
uiBox *uiNewVerticalBox(void)
|
||||
{
|
||||
return finishNewBox(YES);
|
||||
}
|
113
deps/libui/darwin/button.m
vendored
113
deps/libui/darwin/button.m
vendored
@ -1,113 +0,0 @@
|
||||
// 13 august 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
struct uiButton {
|
||||
uiDarwinControl c;
|
||||
NSButton *button;
|
||||
void (*onClicked)(uiButton *, void *);
|
||||
void *onClickedData;
|
||||
};
|
||||
|
||||
@interface buttonDelegateClass : NSObject {
|
||||
struct mapTable *buttons;
|
||||
}
|
||||
- (IBAction)onClicked:(id)sender;
|
||||
- (void)registerButton:(uiButton *)b;
|
||||
- (void)unregisterButton:(uiButton *)b;
|
||||
@end
|
||||
|
||||
@implementation buttonDelegateClass
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
self->buttons = newMap();
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
mapDestroy(self->buttons);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (IBAction)onClicked:(id)sender
|
||||
{
|
||||
uiButton *b;
|
||||
|
||||
b = (uiButton *) mapGet(self->buttons, sender);
|
||||
(*(b->onClicked))(b, b->onClickedData);
|
||||
}
|
||||
|
||||
- (void)registerButton:(uiButton *)b
|
||||
{
|
||||
mapSet(self->buttons, b->button, b);
|
||||
[b->button setTarget:self];
|
||||
[b->button setAction:@selector(onClicked:)];
|
||||
}
|
||||
|
||||
- (void)unregisterButton:(uiButton *)b
|
||||
{
|
||||
[b->button setTarget:nil];
|
||||
mapDelete(self->buttons, b->button);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static buttonDelegateClass *buttonDelegate = nil;
|
||||
|
||||
uiDarwinControlAllDefaultsExceptDestroy(uiButton, button)
|
||||
|
||||
static void uiButtonDestroy(uiControl *c)
|
||||
{
|
||||
uiButton *b = uiButton(c);
|
||||
|
||||
[buttonDelegate unregisterButton:b];
|
||||
[b->button release];
|
||||
uiFreeControl(uiControl(b));
|
||||
}
|
||||
|
||||
char *uiButtonText(uiButton *b)
|
||||
{
|
||||
return uiDarwinNSStringToText([b->button title]);
|
||||
}
|
||||
|
||||
void uiButtonSetText(uiButton *b, const char *text)
|
||||
{
|
||||
[b->button setTitle:toNSString(text)];
|
||||
}
|
||||
|
||||
void uiButtonOnClicked(uiButton *b, void (*f)(uiButton *, void *), void *data)
|
||||
{
|
||||
b->onClicked = f;
|
||||
b->onClickedData = data;
|
||||
}
|
||||
|
||||
static void defaultOnClicked(uiButton *b, void *data)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
uiButton *uiNewButton(const char *text)
|
||||
{
|
||||
uiButton *b;
|
||||
|
||||
uiDarwinNewControl(uiButton, b);
|
||||
|
||||
b->button = [[NSButton alloc] initWithFrame:NSZeroRect];
|
||||
[b->button setTitle:toNSString(text)];
|
||||
[b->button setButtonType:NSMomentaryPushInButton];
|
||||
[b->button setBordered:YES];
|
||||
[b->button setBezelStyle:NSRoundedBezelStyle];
|
||||
uiDarwinSetControlFont(b->button, NSRegularControlSize);
|
||||
|
||||
if (buttonDelegate == nil) {
|
||||
buttonDelegate = [[buttonDelegateClass new] autorelease];
|
||||
[delegates addObject:buttonDelegate];
|
||||
}
|
||||
[buttonDelegate registerButton:b];
|
||||
uiButtonOnClicked(b, defaultOnClicked, NULL);
|
||||
|
||||
return b;
|
||||
}
|
129
deps/libui/darwin/checkbox.m
vendored
129
deps/libui/darwin/checkbox.m
vendored
@ -1,129 +0,0 @@
|
||||
// 14 august 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
struct uiCheckbox {
|
||||
uiDarwinControl c;
|
||||
NSButton *button;
|
||||
void (*onToggled)(uiCheckbox *, void *);
|
||||
void *onToggledData;
|
||||
};
|
||||
|
||||
@interface checkboxDelegateClass : NSObject {
|
||||
struct mapTable *buttons;
|
||||
}
|
||||
- (IBAction)onToggled:(id)sender;
|
||||
- (void)registerCheckbox:(uiCheckbox *)c;
|
||||
- (void)unregisterCheckbox:(uiCheckbox *)c;
|
||||
@end
|
||||
|
||||
@implementation checkboxDelegateClass
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
self->buttons = newMap();
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
mapDestroy(self->buttons);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (IBAction)onToggled:(id)sender
|
||||
{
|
||||
uiCheckbox *c;
|
||||
|
||||
c = (uiCheckbox *) mapGet(self->buttons, sender);
|
||||
(*(c->onToggled))(c, c->onToggledData);
|
||||
}
|
||||
|
||||
- (void)registerCheckbox:(uiCheckbox *)c
|
||||
{
|
||||
mapSet(self->buttons, c->button, c);
|
||||
[c->button setTarget:self];
|
||||
[c->button setAction:@selector(onToggled:)];
|
||||
}
|
||||
|
||||
- (void)unregisterCheckbox:(uiCheckbox *)c
|
||||
{
|
||||
[c->button setTarget:nil];
|
||||
mapDelete(self->buttons, c->button);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static checkboxDelegateClass *checkboxDelegate = nil;
|
||||
|
||||
uiDarwinControlAllDefaultsExceptDestroy(uiCheckbox, button)
|
||||
|
||||
static void uiCheckboxDestroy(uiControl *cc)
|
||||
{
|
||||
uiCheckbox *c = uiCheckbox(cc);
|
||||
|
||||
[checkboxDelegate unregisterCheckbox:c];
|
||||
[c->button release];
|
||||
uiFreeControl(uiControl(c));
|
||||
}
|
||||
|
||||
char *uiCheckboxText(uiCheckbox *c)
|
||||
{
|
||||
return uiDarwinNSStringToText([c->button title]);
|
||||
}
|
||||
|
||||
void uiCheckboxSetText(uiCheckbox *c, const char *text)
|
||||
{
|
||||
[c->button setTitle:toNSString(text)];
|
||||
}
|
||||
|
||||
void uiCheckboxOnToggled(uiCheckbox *c, void (*f)(uiCheckbox *, void *), void *data)
|
||||
{
|
||||
c->onToggled = f;
|
||||
c->onToggledData = data;
|
||||
}
|
||||
|
||||
int uiCheckboxChecked(uiCheckbox *c)
|
||||
{
|
||||
return [c->button state] == NSOnState;
|
||||
}
|
||||
|
||||
void uiCheckboxSetChecked(uiCheckbox *c, int checked)
|
||||
{
|
||||
NSInteger state;
|
||||
|
||||
state = NSOnState;
|
||||
if (!checked)
|
||||
state = NSOffState;
|
||||
[c->button setState:state];
|
||||
}
|
||||
|
||||
static void defaultOnToggled(uiCheckbox *c, void *data)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
uiCheckbox *uiNewCheckbox(const char *text)
|
||||
{
|
||||
uiCheckbox *c;
|
||||
|
||||
uiDarwinNewControl(uiCheckbox, c);
|
||||
|
||||
c->button = [[NSButton alloc] initWithFrame:NSZeroRect];
|
||||
[c->button setTitle:toNSString(text)];
|
||||
[c->button setButtonType:NSSwitchButton];
|
||||
// doesn't seem to have an associated bezel style
|
||||
[c->button setBordered:NO];
|
||||
[c->button setTransparent:NO];
|
||||
uiDarwinSetControlFont(c->button, NSRegularControlSize);
|
||||
|
||||
if (checkboxDelegate == nil) {
|
||||
checkboxDelegate = [[checkboxDelegateClass new] autorelease];
|
||||
[delegates addObject:checkboxDelegate];
|
||||
}
|
||||
[checkboxDelegate registerCheckbox:c];
|
||||
uiCheckboxOnToggled(c, defaultOnToggled, NULL);
|
||||
|
||||
return c;
|
||||
}
|
159
deps/libui/darwin/colorbutton.m
vendored
159
deps/libui/darwin/colorbutton.m
vendored
@ -1,159 +0,0 @@
|
||||
// 15 may 2016
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// TODO no intrinsic height?
|
||||
|
||||
@interface colorButton : NSColorWell {
|
||||
uiColorButton *libui_b;
|
||||
BOOL libui_changing;
|
||||
BOOL libui_setting;
|
||||
}
|
||||
- (id)initWithFrame:(NSRect)frame libuiColorButton:(uiColorButton *)b;
|
||||
- (void)deactivateOnClose:(NSNotification *)note;
|
||||
- (void)libuiColor:(double *)r g:(double *)g b:(double *)b a:(double *)a;
|
||||
- (void)libuiSetColor:(double)r g:(double)g b:(double)b a:(double)a;
|
||||
@end
|
||||
|
||||
// only one may be active at one time
|
||||
static colorButton *activeColorButton = nil;
|
||||
|
||||
struct uiColorButton {
|
||||
uiDarwinControl c;
|
||||
colorButton *button;
|
||||
void (*onChanged)(uiColorButton *, void *);
|
||||
void *onChangedData;
|
||||
};
|
||||
|
||||
@implementation colorButton
|
||||
|
||||
- (id)initWithFrame:(NSRect)frame libuiColorButton:(uiColorButton *)b
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
// the default color is white; set it to black first (see -setColor: below for why we do it first)
|
||||
[self libuiSetColor:0.0 g:0.0 b:0.0 a:1.0];
|
||||
|
||||
self->libui_b = b;
|
||||
self->libui_changing = NO;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)activate:(BOOL)exclusive
|
||||
{
|
||||
if (activeColorButton != nil)
|
||||
activeColorButton->libui_changing = YES;
|
||||
[NSColorPanel setPickerMask:NSColorPanelAllModesMask];
|
||||
[[NSColorPanel sharedColorPanel] setShowsAlpha:YES];
|
||||
[super activate:YES];
|
||||
activeColorButton = self;
|
||||
// see stddialogs.m for details
|
||||
[[NSColorPanel sharedColorPanel] setWorksWhenModal:NO];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(deactivateOnClose:)
|
||||
name:NSWindowWillCloseNotification
|
||||
object:[NSColorPanel sharedColorPanel]];
|
||||
}
|
||||
|
||||
- (void)deactivate
|
||||
{
|
||||
[super deactivate];
|
||||
activeColorButton = nil;
|
||||
if (!self->libui_changing)
|
||||
[[NSColorPanel sharedColorPanel] orderOut:nil];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self
|
||||
name:NSWindowWillCloseNotification
|
||||
object:[NSColorPanel sharedColorPanel]];
|
||||
self->libui_changing = NO;
|
||||
}
|
||||
|
||||
- (void)deactivateOnClose:(NSNotification *)note
|
||||
{
|
||||
[self deactivate];
|
||||
}
|
||||
|
||||
- (void)setColor:(NSColor *)color
|
||||
{
|
||||
uiColorButton *b = self->libui_b;
|
||||
|
||||
[super setColor:color];
|
||||
// this is called by NSColorWell's init, so we have to guard
|
||||
// also don't signal during a programmatic change
|
||||
if (b != nil && !self->libui_setting)
|
||||
(*(b->onChanged))(b, b->onChangedData);
|
||||
}
|
||||
|
||||
- (void)libuiColor:(double *)r g:(double *)g b:(double *)b a:(double *)a
|
||||
{
|
||||
NSColor *rgba;
|
||||
CGFloat cr, cg, cb, ca;
|
||||
|
||||
// the given color may not be an RGBA color, which will cause the -getRed:green:blue:alpha: call to throw an exception
|
||||
rgba = [[self color] colorUsingColorSpace:[NSColorSpace sRGBColorSpace]];
|
||||
[rgba getRed:&cr green:&cg blue:&cb alpha:&ca];
|
||||
*r = cr;
|
||||
*g = cg;
|
||||
*b = cb;
|
||||
*a = ca;
|
||||
// rgba will be autoreleased since it isn't a new or init call
|
||||
}
|
||||
|
||||
- (void)libuiSetColor:(double)r g:(double)g b:(double)b a:(double)a
|
||||
{
|
||||
self->libui_setting = YES;
|
||||
[self setColor:[NSColor colorWithSRGBRed:r green:g blue:b alpha:a]];
|
||||
self->libui_setting = NO;
|
||||
}
|
||||
|
||||
// NSColorWell has no intrinsic size by default; give it the default Interface Builder size.
|
||||
- (NSSize)intrinsicContentSize
|
||||
{
|
||||
return NSMakeSize(44, 23);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
uiDarwinControlAllDefaults(uiColorButton, button)
|
||||
|
||||
// we do not want color change events to be sent to any controls other than the color buttons
|
||||
// see main.m for more details
|
||||
BOOL colorButtonInhibitSendAction(SEL sel, id from, id to)
|
||||
{
|
||||
if (sel != @selector(changeColor:))
|
||||
return NO;
|
||||
return ![to isKindOfClass:[colorButton class]];
|
||||
}
|
||||
|
||||
static void defaultOnChanged(uiColorButton *b, void *data)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void uiColorButtonColor(uiColorButton *b, double *r, double *g, double *bl, double *a)
|
||||
{
|
||||
[b->button libuiColor:r g:g b:bl a:a];
|
||||
}
|
||||
|
||||
void uiColorButtonSetColor(uiColorButton *b, double r, double g, double bl, double a)
|
||||
{
|
||||
[b->button libuiSetColor:r g:g b:bl a:a];
|
||||
}
|
||||
|
||||
void uiColorButtonOnChanged(uiColorButton *b, void (*f)(uiColorButton *, void *), void *data)
|
||||
{
|
||||
b->onChanged = f;
|
||||
b->onChangedData = data;
|
||||
}
|
||||
|
||||
uiColorButton *uiNewColorButton(void)
|
||||
{
|
||||
uiColorButton *b;
|
||||
|
||||
uiDarwinNewControl(uiColorButton, b);
|
||||
|
||||
b->button = [[colorButton alloc] initWithFrame:NSZeroRect libuiColorButton:b];
|
||||
|
||||
uiColorButtonOnChanged(b, defaultOnChanged, NULL);
|
||||
|
||||
return b;
|
||||
}
|
145
deps/libui/darwin/combobox.m
vendored
145
deps/libui/darwin/combobox.m
vendored
@ -1,145 +0,0 @@
|
||||
// 14 august 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// NSComboBoxes have no intrinsic width; we'll use the default Interface Builder width for them.
|
||||
// NSPopUpButton is fine.
|
||||
#define comboboxWidth 96
|
||||
|
||||
struct uiCombobox {
|
||||
uiDarwinControl c;
|
||||
NSPopUpButton *pb;
|
||||
NSArrayController *pbac;
|
||||
void (*onSelected)(uiCombobox *, void *);
|
||||
void *onSelectedData;
|
||||
};
|
||||
|
||||
@interface comboboxDelegateClass : NSObject {
|
||||
struct mapTable *comboboxes;
|
||||
}
|
||||
- (IBAction)onSelected:(id)sender;
|
||||
- (void)registerCombobox:(uiCombobox *)c;
|
||||
- (void)unregisterCombobox:(uiCombobox *)c;
|
||||
@end
|
||||
|
||||
@implementation comboboxDelegateClass
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
self->comboboxes = newMap();
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
mapDestroy(self->comboboxes);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (IBAction)onSelected:(id)sender
|
||||
{
|
||||
uiCombobox *c;
|
||||
|
||||
c = uiCombobox(mapGet(self->comboboxes, sender));
|
||||
(*(c->onSelected))(c, c->onSelectedData);
|
||||
}
|
||||
|
||||
- (void)registerCombobox:(uiCombobox *)c
|
||||
{
|
||||
mapSet(self->comboboxes, c->pb, c);
|
||||
[c->pb setTarget:self];
|
||||
[c->pb setAction:@selector(onSelected:)];
|
||||
}
|
||||
|
||||
- (void)unregisterCombobox:(uiCombobox *)c
|
||||
{
|
||||
[c->pb setTarget:nil];
|
||||
mapDelete(self->comboboxes, c->pb);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static comboboxDelegateClass *comboboxDelegate = nil;
|
||||
|
||||
uiDarwinControlAllDefaultsExceptDestroy(uiCombobox, pb)
|
||||
|
||||
static void uiComboboxDestroy(uiControl *cc)
|
||||
{
|
||||
uiCombobox *c = uiCombobox(cc);
|
||||
|
||||
[comboboxDelegate unregisterCombobox:c];
|
||||
[c->pb unbind:@"contentObjects"];
|
||||
[c->pb unbind:@"selectedIndex"];
|
||||
[c->pbac release];
|
||||
[c->pb release];
|
||||
uiFreeControl(uiControl(c));
|
||||
}
|
||||
|
||||
void uiComboboxAppend(uiCombobox *c, const char *text)
|
||||
{
|
||||
[c->pbac addObject:toNSString(text)];
|
||||
}
|
||||
|
||||
int uiComboboxSelected(uiCombobox *c)
|
||||
{
|
||||
return [c->pb indexOfSelectedItem];
|
||||
}
|
||||
|
||||
void uiComboboxSetSelected(uiCombobox *c, int n)
|
||||
{
|
||||
[c->pb selectItemAtIndex:n];
|
||||
}
|
||||
|
||||
void uiComboboxOnSelected(uiCombobox *c, void (*f)(uiCombobox *c, void *data), void *data)
|
||||
{
|
||||
c->onSelected = f;
|
||||
c->onSelectedData = data;
|
||||
}
|
||||
|
||||
static void defaultOnSelected(uiCombobox *c, void *data)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
uiCombobox *uiNewCombobox(void)
|
||||
{
|
||||
uiCombobox *c;
|
||||
NSPopUpButtonCell *pbcell;
|
||||
|
||||
uiDarwinNewControl(uiCombobox, c);
|
||||
|
||||
c->pb = [[NSPopUpButton alloc] initWithFrame:NSZeroRect pullsDown:NO];
|
||||
[c->pb setPreferredEdge:NSMinYEdge];
|
||||
pbcell = (NSPopUpButtonCell *) [c->pb cell];
|
||||
[pbcell setArrowPosition:NSPopUpArrowAtBottom];
|
||||
// the font defined by Interface Builder is Menu 13, which is lol
|
||||
// just use the regular control size for consistency
|
||||
uiDarwinSetControlFont(c->pb, NSRegularControlSize);
|
||||
|
||||
// NSPopUpButton doesn't work like a combobox
|
||||
// - it automatically selects the first item
|
||||
// - it doesn't support duplicates
|
||||
// but we can use a NSArrayController and Cocoa bindings to bypass these restrictions
|
||||
c->pbac = [NSArrayController new];
|
||||
[c->pbac setAvoidsEmptySelection:NO];
|
||||
[c->pbac setSelectsInsertedObjects:NO];
|
||||
[c->pbac setAutomaticallyRearrangesObjects:NO];
|
||||
[c->pb bind:@"contentValues"
|
||||
toObject:c->pbac
|
||||
withKeyPath:@"arrangedObjects"
|
||||
options:nil];
|
||||
[c->pb bind:@"selectedIndex"
|
||||
toObject:c->pbac
|
||||
withKeyPath:@"selectionIndex"
|
||||
options:nil];
|
||||
|
||||
if (comboboxDelegate == nil) {
|
||||
comboboxDelegate = [[comboboxDelegateClass new] autorelease];
|
||||
[delegates addObject:comboboxDelegate];
|
||||
}
|
||||
[comboboxDelegate registerCombobox:c];
|
||||
uiComboboxOnSelected(c, defaultOnSelected, NULL);
|
||||
|
||||
return c;
|
||||
}
|
84
deps/libui/darwin/control.m
vendored
84
deps/libui/darwin/control.m
vendored
@ -1,84 +0,0 @@
|
||||
// 16 august 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
void uiDarwinControlSyncEnableState(uiDarwinControl *c, int state)
|
||||
{
|
||||
(*(c->SyncEnableState))(c, state);
|
||||
}
|
||||
|
||||
void uiDarwinControlSetSuperview(uiDarwinControl *c, NSView *superview)
|
||||
{
|
||||
(*(c->SetSuperview))(c, superview);
|
||||
}
|
||||
|
||||
BOOL uiDarwinControlHugsTrailingEdge(uiDarwinControl *c)
|
||||
{
|
||||
return (*(c->HugsTrailingEdge))(c);
|
||||
}
|
||||
|
||||
BOOL uiDarwinControlHugsBottom(uiDarwinControl *c)
|
||||
{
|
||||
return (*(c->HugsBottom))(c);
|
||||
}
|
||||
|
||||
void uiDarwinControlChildEdgeHuggingChanged(uiDarwinControl *c)
|
||||
{
|
||||
(*(c->ChildEdgeHuggingChanged))(c);
|
||||
}
|
||||
|
||||
NSLayoutPriority uiDarwinControlHuggingPriority(uiDarwinControl *c, NSLayoutConstraintOrientation orientation)
|
||||
{
|
||||
return (*(c->HuggingPriority))(c, orientation);
|
||||
}
|
||||
|
||||
void uiDarwinControlSetHuggingPriority(uiDarwinControl *c, NSLayoutPriority priority, NSLayoutConstraintOrientation orientation)
|
||||
{
|
||||
(*(c->SetHuggingPriority))(c, priority, orientation);
|
||||
}
|
||||
|
||||
void uiDarwinControlChildVisibilityChanged(uiDarwinControl *c)
|
||||
{
|
||||
(*(c->ChildVisibilityChanged))(c);
|
||||
}
|
||||
|
||||
void uiDarwinSetControlFont(NSControl *c, NSControlSize size)
|
||||
{
|
||||
[c setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:size]]];
|
||||
}
|
||||
|
||||
#define uiDarwinControlSignature 0x44617277
|
||||
|
||||
uiDarwinControl *uiDarwinAllocControl(size_t n, uint32_t typesig, const char *typenamestr)
|
||||
{
|
||||
return uiDarwinControl(uiAllocControl(n, uiDarwinControlSignature, typesig, typenamestr));
|
||||
}
|
||||
|
||||
BOOL uiDarwinShouldStopSyncEnableState(uiDarwinControl *c, BOOL enabled)
|
||||
{
|
||||
int ce;
|
||||
|
||||
ce = uiControlEnabled(uiControl(c));
|
||||
// only stop if we're going from disabled back to enabled; don't stop under any other condition
|
||||
// (if we stop when going from enabled to disabled then enabled children of a disabled control won't get disabled at the OS level)
|
||||
if (!ce && enabled)
|
||||
return YES;
|
||||
return NO;
|
||||
}
|
||||
|
||||
void uiDarwinNotifyEdgeHuggingChanged(uiDarwinControl *c)
|
||||
{
|
||||
uiControl *parent;
|
||||
|
||||
parent = uiControlParent(uiControl(c));
|
||||
if (parent != NULL)
|
||||
uiDarwinControlChildEdgeHuggingChanged(uiDarwinControl(parent));
|
||||
}
|
||||
|
||||
void uiDarwinNotifyVisibilityChanged(uiDarwinControl *c)
|
||||
{
|
||||
uiControl *parent;
|
||||
|
||||
parent = uiControlParent(uiControl(c));
|
||||
if (parent != NULL)
|
||||
uiDarwinControlChildVisibilityChanged(uiDarwinControl(parent));
|
||||
}
|
42
deps/libui/darwin/datetimepicker.m
vendored
42
deps/libui/darwin/datetimepicker.m
vendored
@ -1,42 +0,0 @@
|
||||
// 14 august 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
struct uiDateTimePicker {
|
||||
uiDarwinControl c;
|
||||
NSDatePicker *dp;
|
||||
};
|
||||
|
||||
uiDarwinControlAllDefaults(uiDateTimePicker, dp)
|
||||
|
||||
static uiDateTimePicker *finishNewDateTimePicker(NSDatePickerElementFlags elements)
|
||||
{
|
||||
uiDateTimePicker *d;
|
||||
|
||||
uiDarwinNewControl(uiDateTimePicker, d);
|
||||
|
||||
d->dp = [[NSDatePicker alloc] initWithFrame:NSZeroRect];
|
||||
[d->dp setBordered:NO];
|
||||
[d->dp setBezeled:YES];
|
||||
[d->dp setDrawsBackground:YES];
|
||||
[d->dp setDatePickerStyle:NSTextFieldAndStepperDatePickerStyle];
|
||||
[d->dp setDatePickerElements:elements];
|
||||
[d->dp setDatePickerMode:NSSingleDateMode];
|
||||
uiDarwinSetControlFont(d->dp, NSRegularControlSize);
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
uiDateTimePicker *uiNewDateTimePicker(void)
|
||||
{
|
||||
return finishNewDateTimePicker(NSYearMonthDayDatePickerElementFlag | NSHourMinuteSecondDatePickerElementFlag);
|
||||
}
|
||||
|
||||
uiDateTimePicker *uiNewDatePicker(void)
|
||||
{
|
||||
return finishNewDateTimePicker(NSYearMonthDayDatePickerElementFlag);
|
||||
}
|
||||
|
||||
uiDateTimePicker *uiNewTimePicker(void)
|
||||
{
|
||||
return finishNewDateTimePicker(NSHourMinuteSecondDatePickerElementFlag);
|
||||
}
|
19
deps/libui/darwin/debug.m
vendored
19
deps/libui/darwin/debug.m
vendored
@ -1,19 +0,0 @@
|
||||
// 13 may 2016
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// LONGTERM don't halt on release builds
|
||||
|
||||
void realbug(const char *file, const char *line, const char *func, const char *prefix, const char *format, va_list ap)
|
||||
{
|
||||
NSMutableString *str;
|
||||
NSString *formatted;
|
||||
|
||||
str = [NSMutableString new];
|
||||
[str appendString:[NSString stringWithFormat:@"[libui] %s:%s:%s() %s", file, line, func, prefix]];
|
||||
formatted = [[NSString alloc] initWithFormat:[NSString stringWithUTF8String:format] arguments:ap];
|
||||
[str appendString:formatted];
|
||||
[formatted release];
|
||||
NSLog(@"%@", str);
|
||||
[str release];
|
||||
__builtin_trap();
|
||||
}
|
454
deps/libui/darwin/draw.m
vendored
454
deps/libui/darwin/draw.m
vendored
@ -1,454 +0,0 @@
|
||||
// 6 september 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
struct uiDrawPath {
|
||||
CGMutablePathRef path;
|
||||
uiDrawFillMode fillMode;
|
||||
BOOL ended;
|
||||
};
|
||||
|
||||
uiDrawPath *uiDrawNewPath(uiDrawFillMode mode)
|
||||
{
|
||||
uiDrawPath *p;
|
||||
|
||||
p = uiNew(uiDrawPath);
|
||||
p->path = CGPathCreateMutable();
|
||||
p->fillMode = mode;
|
||||
return p;
|
||||
}
|
||||
|
||||
void uiDrawFreePath(uiDrawPath *p)
|
||||
{
|
||||
CGPathRelease((CGPathRef) (p->path));
|
||||
uiFree(p);
|
||||
}
|
||||
|
||||
void uiDrawPathNewFigure(uiDrawPath *p, double x, double y)
|
||||
{
|
||||
if (p->ended)
|
||||
userbug("You cannot call uiDrawPathNewFigure() on a uiDrawPath that has already been ended. (path; %p)", p);
|
||||
CGPathMoveToPoint(p->path, NULL, x, y);
|
||||
}
|
||||
|
||||
void uiDrawPathNewFigureWithArc(uiDrawPath *p, double xCenter, double yCenter, double radius, double startAngle, double sweep, int negative)
|
||||
{
|
||||
double sinStart, cosStart;
|
||||
double startx, starty;
|
||||
|
||||
if (p->ended)
|
||||
userbug("You cannot call uiDrawPathNewFigureWithArc() on a uiDrawPath that has already been ended. (path; %p)", p);
|
||||
sinStart = sin(startAngle);
|
||||
cosStart = cos(startAngle);
|
||||
startx = xCenter + radius * cosStart;
|
||||
starty = yCenter + radius * sinStart;
|
||||
CGPathMoveToPoint(p->path, NULL, startx, starty);
|
||||
uiDrawPathArcTo(p, xCenter, yCenter, radius, startAngle, sweep, negative);
|
||||
}
|
||||
|
||||
void uiDrawPathLineTo(uiDrawPath *p, double x, double y)
|
||||
{
|
||||
// TODO refine this to require being in a path
|
||||
if (p->ended)
|
||||
implbug("attempt to add line to ended path in uiDrawPathLineTo()");
|
||||
CGPathAddLineToPoint(p->path, NULL, x, y);
|
||||
}
|
||||
|
||||
void uiDrawPathArcTo(uiDrawPath *p, double xCenter, double yCenter, double radius, double startAngle, double sweep, int negative)
|
||||
{
|
||||
bool cw;
|
||||
|
||||
// TODO likewise
|
||||
if (p->ended)
|
||||
implbug("attempt to add arc to ended path in uiDrawPathArcTo()");
|
||||
if (sweep > 2 * uiPi)
|
||||
sweep = 2 * uiPi;
|
||||
cw = false;
|
||||
if (negative)
|
||||
cw = true;
|
||||
CGPathAddArc(p->path, NULL,
|
||||
xCenter, yCenter,
|
||||
radius,
|
||||
startAngle, startAngle + sweep,
|
||||
cw);
|
||||
}
|
||||
|
||||
void uiDrawPathBezierTo(uiDrawPath *p, double c1x, double c1y, double c2x, double c2y, double endX, double endY)
|
||||
{
|
||||
// TODO likewise
|
||||
if (p->ended)
|
||||
implbug("attempt to add bezier to ended path in uiDrawPathBezierTo()");
|
||||
CGPathAddCurveToPoint(p->path, NULL,
|
||||
c1x, c1y,
|
||||
c2x, c2y,
|
||||
endX, endY);
|
||||
}
|
||||
|
||||
void uiDrawPathCloseFigure(uiDrawPath *p)
|
||||
{
|
||||
// TODO likewise
|
||||
if (p->ended)
|
||||
implbug("attempt to close figure of ended path in uiDrawPathCloseFigure()");
|
||||
CGPathCloseSubpath(p->path);
|
||||
}
|
||||
|
||||
void uiDrawPathAddRectangle(uiDrawPath *p, double x, double y, double width, double height)
|
||||
{
|
||||
if (p->ended)
|
||||
userbug("You cannot call uiDrawPathAddRectangle() on a uiDrawPath that has already been ended. (path; %p)", p);
|
||||
CGPathAddRect(p->path, NULL, CGRectMake(x, y, width, height));
|
||||
}
|
||||
|
||||
void uiDrawPathEnd(uiDrawPath *p)
|
||||
{
|
||||
p->ended = TRUE;
|
||||
}
|
||||
|
||||
struct uiDrawContext {
|
||||
CGContextRef c;
|
||||
CGFloat height; // needed for text; see below
|
||||
};
|
||||
|
||||
uiDrawContext *newContext(CGContextRef ctxt, CGFloat height)
|
||||
{
|
||||
uiDrawContext *c;
|
||||
|
||||
c = uiNew(uiDrawContext);
|
||||
c->c = ctxt;
|
||||
c->height = height;
|
||||
return c;
|
||||
}
|
||||
|
||||
void freeContext(uiDrawContext *c)
|
||||
{
|
||||
uiFree(c);
|
||||
}
|
||||
|
||||
// a stroke is identical to a fill of a stroked path
|
||||
// we need to do this in order to stroke with a gradient; see http://stackoverflow.com/a/25034854/3408572
|
||||
// doing this for other brushes works too
|
||||
void uiDrawStroke(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b, uiDrawStrokeParams *p)
|
||||
{
|
||||
CGLineCap cap;
|
||||
CGLineJoin join;
|
||||
CGPathRef dashPath;
|
||||
CGFloat *dashes;
|
||||
size_t i;
|
||||
uiDrawPath p2;
|
||||
|
||||
if (!path->ended)
|
||||
userbug("You cannot call uiDrawStroke() on a uiDrawPath that has not been ended. (path: %p)", path);
|
||||
|
||||
switch (p->Cap) {
|
||||
case uiDrawLineCapFlat:
|
||||
cap = kCGLineCapButt;
|
||||
break;
|
||||
case uiDrawLineCapRound:
|
||||
cap = kCGLineCapRound;
|
||||
break;
|
||||
case uiDrawLineCapSquare:
|
||||
cap = kCGLineCapSquare;
|
||||
break;
|
||||
}
|
||||
switch (p->Join) {
|
||||
case uiDrawLineJoinMiter:
|
||||
join = kCGLineJoinMiter;
|
||||
break;
|
||||
case uiDrawLineJoinRound:
|
||||
join = kCGLineJoinRound;
|
||||
break;
|
||||
case uiDrawLineJoinBevel:
|
||||
join = kCGLineJoinBevel;
|
||||
break;
|
||||
}
|
||||
|
||||
// create a temporary path identical to the previous one
|
||||
dashPath = (CGPathRef) path->path;
|
||||
if (p->NumDashes != 0) {
|
||||
dashes = (CGFloat *) uiAlloc(p->NumDashes * sizeof (CGFloat), "CGFloat[]");
|
||||
for (i = 0; i < p->NumDashes; i++)
|
||||
dashes[i] = p->Dashes[i];
|
||||
dashPath = CGPathCreateCopyByDashingPath(path->path,
|
||||
NULL,
|
||||
p->DashPhase,
|
||||
dashes,
|
||||
p->NumDashes);
|
||||
uiFree(dashes);
|
||||
}
|
||||
// the documentation is wrong: this produces a path suitable for calling CGPathCreateCopyByStrokingPath(), not for filling directly
|
||||
// the cast is safe; we never modify the CGPathRef and always cast it back to a CGPathRef anyway
|
||||
p2.path = (CGMutablePathRef) CGPathCreateCopyByStrokingPath(dashPath,
|
||||
NULL,
|
||||
p->Thickness,
|
||||
cap,
|
||||
join,
|
||||
p->MiterLimit);
|
||||
if (p->NumDashes != 0)
|
||||
CGPathRelease(dashPath);
|
||||
|
||||
// always draw stroke fills using the winding rule
|
||||
// otherwise intersecting figures won't draw correctly
|
||||
p2.fillMode = uiDrawFillModeWinding;
|
||||
p2.ended = path->ended;
|
||||
uiDrawFill(c, &p2, b);
|
||||
// and clean up
|
||||
CGPathRelease((CGPathRef) (p2.path));
|
||||
}
|
||||
|
||||
// for a solid fill, we can merely have Core Graphics fill directly
|
||||
static void fillSolid(CGContextRef ctxt, uiDrawPath *p, uiDrawBrush *b)
|
||||
{
|
||||
// TODO this uses DeviceRGB; switch to sRGB
|
||||
CGContextSetRGBFillColor(ctxt, b->R, b->G, b->B, b->A);
|
||||
switch (p->fillMode) {
|
||||
case uiDrawFillModeWinding:
|
||||
CGContextFillPath(ctxt);
|
||||
break;
|
||||
case uiDrawFillModeAlternate:
|
||||
CGContextEOFillPath(ctxt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// for a gradient fill, we need to clip to the path and then draw the gradient
|
||||
// see http://stackoverflow.com/a/25034854/3408572
|
||||
static void fillGradient(CGContextRef ctxt, uiDrawPath *p, uiDrawBrush *b)
|
||||
{
|
||||
CGGradientRef gradient;
|
||||
CGColorSpaceRef colorspace;
|
||||
CGFloat *colors;
|
||||
CGFloat *locations;
|
||||
size_t i;
|
||||
|
||||
// gradients need a color space
|
||||
// for consistency with windows, use sRGB
|
||||
colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
|
||||
|
||||
// make the gradient
|
||||
colors = uiAlloc(b->NumStops * 4 * sizeof (CGFloat), "CGFloat[]");
|
||||
locations = uiAlloc(b->NumStops * sizeof (CGFloat), "CGFloat[]");
|
||||
for (i = 0; i < b->NumStops; i++) {
|
||||
colors[i * 4 + 0] = b->Stops[i].R;
|
||||
colors[i * 4 + 1] = b->Stops[i].G;
|
||||
colors[i * 4 + 2] = b->Stops[i].B;
|
||||
colors[i * 4 + 3] = b->Stops[i].A;
|
||||
locations[i] = b->Stops[i].Pos;
|
||||
}
|
||||
gradient = CGGradientCreateWithColorComponents(colorspace, colors, locations, b->NumStops);
|
||||
uiFree(locations);
|
||||
uiFree(colors);
|
||||
|
||||
// because we're mucking with clipping, we need to save the graphics state and restore it later
|
||||
CGContextSaveGState(ctxt);
|
||||
|
||||
// clip
|
||||
switch (p->fillMode) {
|
||||
case uiDrawFillModeWinding:
|
||||
CGContextClip(ctxt);
|
||||
break;
|
||||
case uiDrawFillModeAlternate:
|
||||
CGContextEOClip(ctxt);
|
||||
break;
|
||||
}
|
||||
|
||||
// draw the gradient
|
||||
switch (b->Type) {
|
||||
case uiDrawBrushTypeLinearGradient:
|
||||
CGContextDrawLinearGradient(ctxt,
|
||||
gradient,
|
||||
CGPointMake(b->X0, b->Y0),
|
||||
CGPointMake(b->X1, b->Y1),
|
||||
kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
|
||||
break;
|
||||
case uiDrawBrushTypeRadialGradient:
|
||||
CGContextDrawRadialGradient(ctxt,
|
||||
gradient,
|
||||
CGPointMake(b->X0, b->Y0),
|
||||
// make the start circle radius 0 to make it a point
|
||||
0,
|
||||
CGPointMake(b->X1, b->Y1),
|
||||
b->OuterRadius,
|
||||
kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
|
||||
break;
|
||||
}
|
||||
|
||||
// and clean up
|
||||
CGContextRestoreGState(ctxt);
|
||||
CGGradientRelease(gradient);
|
||||
CGColorSpaceRelease(colorspace);
|
||||
}
|
||||
|
||||
void uiDrawFill(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b)
|
||||
{
|
||||
if (!path->ended)
|
||||
userbug("You cannot call uiDrawStroke() on a uiDrawPath that has not been ended. (path: %p)", path);
|
||||
CGContextAddPath(c->c, (CGPathRef) (path->path));
|
||||
switch (b->Type) {
|
||||
case uiDrawBrushTypeSolid:
|
||||
fillSolid(c->c, path, b);
|
||||
return;
|
||||
case uiDrawBrushTypeLinearGradient:
|
||||
case uiDrawBrushTypeRadialGradient:
|
||||
fillGradient(c->c, path, b);
|
||||
return;
|
||||
// case uiDrawBrushTypeImage:
|
||||
// TODO
|
||||
return;
|
||||
}
|
||||
userbug("Unknown brush type %d passed to uiDrawFill().", b->Type);
|
||||
}
|
||||
|
||||
static void m2c(uiDrawMatrix *m, CGAffineTransform *c)
|
||||
{
|
||||
c->a = m->M11;
|
||||
c->b = m->M12;
|
||||
c->c = m->M21;
|
||||
c->d = m->M22;
|
||||
c->tx = m->M31;
|
||||
c->ty = m->M32;
|
||||
}
|
||||
|
||||
static void c2m(CGAffineTransform *c, uiDrawMatrix *m)
|
||||
{
|
||||
m->M11 = c->a;
|
||||
m->M12 = c->b;
|
||||
m->M21 = c->c;
|
||||
m->M22 = c->d;
|
||||
m->M31 = c->tx;
|
||||
m->M32 = c->ty;
|
||||
}
|
||||
|
||||
void uiDrawMatrixTranslate(uiDrawMatrix *m, double x, double y)
|
||||
{
|
||||
CGAffineTransform c;
|
||||
|
||||
m2c(m, &c);
|
||||
c = CGAffineTransformTranslate(c, x, y);
|
||||
c2m(&c, m);
|
||||
}
|
||||
|
||||
void uiDrawMatrixScale(uiDrawMatrix *m, double xCenter, double yCenter, double x, double y)
|
||||
{
|
||||
CGAffineTransform c;
|
||||
double xt, yt;
|
||||
|
||||
m2c(m, &c);
|
||||
xt = x;
|
||||
yt = y;
|
||||
scaleCenter(xCenter, yCenter, &xt, &yt);
|
||||
c = CGAffineTransformTranslate(c, xt, yt);
|
||||
c = CGAffineTransformScale(c, x, y);
|
||||
c = CGAffineTransformTranslate(c, -xt, -yt);
|
||||
c2m(&c, m);
|
||||
}
|
||||
|
||||
void uiDrawMatrixRotate(uiDrawMatrix *m, double x, double y, double amount)
|
||||
{
|
||||
CGAffineTransform c;
|
||||
|
||||
m2c(m, &c);
|
||||
c = CGAffineTransformTranslate(c, x, y);
|
||||
c = CGAffineTransformRotate(c, amount);
|
||||
c = CGAffineTransformTranslate(c, -x, -y);
|
||||
c2m(&c, m);
|
||||
}
|
||||
|
||||
void uiDrawMatrixSkew(uiDrawMatrix *m, double x, double y, double xamount, double yamount)
|
||||
{
|
||||
fallbackSkew(m, x, y, xamount, yamount);
|
||||
}
|
||||
|
||||
void uiDrawMatrixMultiply(uiDrawMatrix *dest, uiDrawMatrix *src)
|
||||
{
|
||||
CGAffineTransform c;
|
||||
CGAffineTransform d;
|
||||
|
||||
m2c(dest, &c);
|
||||
m2c(src, &d);
|
||||
c = CGAffineTransformConcat(c, d);
|
||||
c2m(&c, dest);
|
||||
}
|
||||
|
||||
// there is no test for invertibility; CGAffineTransformInvert() is merely documented as returning the matrix unchanged if it isn't invertible
|
||||
// therefore, special care must be taken to catch matrices who are their own inverses
|
||||
// TODO figure out which matrices these are and do so
|
||||
int uiDrawMatrixInvertible(uiDrawMatrix *m)
|
||||
{
|
||||
CGAffineTransform c, d;
|
||||
|
||||
m2c(m, &c);
|
||||
d = CGAffineTransformInvert(c);
|
||||
return CGAffineTransformEqualToTransform(c, d) == false;
|
||||
}
|
||||
|
||||
int uiDrawMatrixInvert(uiDrawMatrix *m)
|
||||
{
|
||||
CGAffineTransform c, d;
|
||||
|
||||
m2c(m, &c);
|
||||
d = CGAffineTransformInvert(c);
|
||||
if (CGAffineTransformEqualToTransform(c, d))
|
||||
return 0;
|
||||
c2m(&d, m);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void uiDrawMatrixTransformPoint(uiDrawMatrix *m, double *x, double *y)
|
||||
{
|
||||
CGAffineTransform c;
|
||||
CGPoint p;
|
||||
|
||||
m2c(m, &c);
|
||||
p = CGPointApplyAffineTransform(CGPointMake(*x, *y), c);
|
||||
*x = p.x;
|
||||
*y = p.y;
|
||||
}
|
||||
|
||||
void uiDrawMatrixTransformSize(uiDrawMatrix *m, double *x, double *y)
|
||||
{
|
||||
CGAffineTransform c;
|
||||
CGSize s;
|
||||
|
||||
m2c(m, &c);
|
||||
s = CGSizeApplyAffineTransform(CGSizeMake(*x, *y), c);
|
||||
*x = s.width;
|
||||
*y = s.height;
|
||||
}
|
||||
|
||||
void uiDrawTransform(uiDrawContext *c, uiDrawMatrix *m)
|
||||
{
|
||||
CGAffineTransform cm;
|
||||
|
||||
m2c(m, &cm);
|
||||
CGContextConcatCTM(c->c, cm);
|
||||
}
|
||||
|
||||
void uiDrawClip(uiDrawContext *c, uiDrawPath *path)
|
||||
{
|
||||
if (!path->ended)
|
||||
userbug("You cannot call uiDrawCilp() on a uiDrawPath that has not been ended. (path: %p)", path);
|
||||
CGContextAddPath(c->c, (CGPathRef) (path->path));
|
||||
switch (path->fillMode) {
|
||||
case uiDrawFillModeWinding:
|
||||
CGContextClip(c->c);
|
||||
break;
|
||||
case uiDrawFillModeAlternate:
|
||||
CGContextEOClip(c->c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO figure out what besides transforms these save/restore on all platforms
|
||||
void uiDrawSave(uiDrawContext *c)
|
||||
{
|
||||
CGContextSaveGState(c->c);
|
||||
}
|
||||
|
||||
void uiDrawRestore(uiDrawContext *c)
|
||||
{
|
||||
CGContextRestoreGState(c->c);
|
||||
}
|
||||
|
||||
void uiDrawText(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout)
|
||||
{
|
||||
doDrawText(c->c, c->height, x, y, layout);
|
||||
}
|
655
deps/libui/darwin/drawtext.m
vendored
655
deps/libui/darwin/drawtext.m
vendored
@ -1,655 +0,0 @@
|
||||
// 6 september 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// TODO
|
||||
#define complain(...) implbug(__VA_ARGS__)
|
||||
|
||||
// TODO double-check that we are properly handling allocation failures (or just toll free bridge from cocoa)
|
||||
struct uiDrawFontFamilies {
|
||||
CFArrayRef fonts;
|
||||
};
|
||||
|
||||
uiDrawFontFamilies *uiDrawListFontFamilies(void)
|
||||
{
|
||||
uiDrawFontFamilies *ff;
|
||||
|
||||
ff = uiNew(uiDrawFontFamilies);
|
||||
ff->fonts = CTFontManagerCopyAvailableFontFamilyNames();
|
||||
if (ff->fonts == NULL)
|
||||
implbug("error getting available font names (no reason specified) (TODO)");
|
||||
return ff;
|
||||
}
|
||||
|
||||
int uiDrawFontFamiliesNumFamilies(uiDrawFontFamilies *ff)
|
||||
{
|
||||
return CFArrayGetCount(ff->fonts);
|
||||
}
|
||||
|
||||
char *uiDrawFontFamiliesFamily(uiDrawFontFamilies *ff, int n)
|
||||
{
|
||||
CFStringRef familystr;
|
||||
char *family;
|
||||
|
||||
familystr = (CFStringRef) CFArrayGetValueAtIndex(ff->fonts, n);
|
||||
// toll-free bridge
|
||||
family = uiDarwinNSStringToText((NSString *) familystr);
|
||||
// Get Rule means we do not free familystr
|
||||
return family;
|
||||
}
|
||||
|
||||
void uiDrawFreeFontFamilies(uiDrawFontFamilies *ff)
|
||||
{
|
||||
CFRelease(ff->fonts);
|
||||
uiFree(ff);
|
||||
}
|
||||
|
||||
struct uiDrawTextFont {
|
||||
CTFontRef f;
|
||||
};
|
||||
|
||||
uiDrawTextFont *mkTextFont(CTFontRef f, BOOL retain)
|
||||
{
|
||||
uiDrawTextFont *font;
|
||||
|
||||
font = uiNew(uiDrawTextFont);
|
||||
font->f = f;
|
||||
if (retain)
|
||||
CFRetain(font->f);
|
||||
return font;
|
||||
}
|
||||
|
||||
uiDrawTextFont *mkTextFontFromNSFont(NSFont *f)
|
||||
{
|
||||
// toll-free bridging; we do retain, though
|
||||
return mkTextFont((CTFontRef) f, YES);
|
||||
}
|
||||
|
||||
static CFMutableDictionaryRef newAttrList(void)
|
||||
{
|
||||
CFMutableDictionaryRef attr;
|
||||
|
||||
attr = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||
if (attr == NULL)
|
||||
complain("error creating attribute dictionary in newAttrList()()");
|
||||
return attr;
|
||||
}
|
||||
|
||||
static void addFontFamilyAttr(CFMutableDictionaryRef attr, const char *family)
|
||||
{
|
||||
CFStringRef cfstr;
|
||||
|
||||
cfstr = CFStringCreateWithCString(NULL, family, kCFStringEncodingUTF8);
|
||||
if (cfstr == NULL)
|
||||
complain("error creating font family name CFStringRef in addFontFamilyAttr()");
|
||||
CFDictionaryAddValue(attr, kCTFontFamilyNameAttribute, cfstr);
|
||||
CFRelease(cfstr); // dictionary holds its own reference
|
||||
}
|
||||
|
||||
static void addFontSizeAttr(CFMutableDictionaryRef attr, double size)
|
||||
{
|
||||
CFNumberRef n;
|
||||
|
||||
n = CFNumberCreate(NULL, kCFNumberDoubleType, &size);
|
||||
CFDictionaryAddValue(attr, kCTFontSizeAttribute, n);
|
||||
CFRelease(n);
|
||||
}
|
||||
|
||||
#if 0
|
||||
TODO
|
||||
// See http://stackoverflow.com/questions/4810409/does-coretext-support-small-caps/4811371#4811371 and https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c for what these do
|
||||
// And fortunately, unlike the traits (see below), unmatched features are simply ignored without affecting the other features :D
|
||||
static void addFontSmallCapsAttr(CFMutableDictionaryRef attr)
|
||||
{
|
||||
CFMutableArrayRef outerArray;
|
||||
CFMutableDictionaryRef innerDict;
|
||||
CFNumberRef numType, numSelector;
|
||||
int num;
|
||||
|
||||
outerArray = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
|
||||
if (outerArray == NULL)
|
||||
complain("error creating outer CFArray for adding small caps attributes in addFontSmallCapsAttr()");
|
||||
|
||||
// Apple's headers say these are deprecated, but a few fonts still rely on them
|
||||
num = kLetterCaseType;
|
||||
numType = CFNumberCreate(NULL, kCFNumberIntType, &num);
|
||||
num = kSmallCapsSelector;
|
||||
numSelector = CFNumberCreate(NULL, kCFNumberIntType, &num);
|
||||
innerDict = newAttrList();
|
||||
CFDictionaryAddValue(innerDict, kCTFontFeatureTypeIdentifierKey, numType);
|
||||
CFRelease(numType);
|
||||
CFDictionaryAddValue(innerDict, kCTFontFeatureSelectorIdentifierKey, numSelector);
|
||||
CFRelease(numSelector);
|
||||
CFArrayAppendValue(outerArray, innerDict);
|
||||
CFRelease(innerDict); // and likewise for CFArray
|
||||
|
||||
// these are the non-deprecated versions of the above; some fonts have these instead
|
||||
num = kLowerCaseType;
|
||||
numType = CFNumberCreate(NULL, kCFNumberIntType, &num);
|
||||
num = kLowerCaseSmallCapsSelector;
|
||||
numSelector = CFNumberCreate(NULL, kCFNumberIntType, &num);
|
||||
innerDict = newAttrList();
|
||||
CFDictionaryAddValue(innerDict, kCTFontFeatureTypeIdentifierKey, numType);
|
||||
CFRelease(numType);
|
||||
CFDictionaryAddValue(innerDict, kCTFontFeatureSelectorIdentifierKey, numSelector);
|
||||
CFRelease(numSelector);
|
||||
CFArrayAppendValue(outerArray, innerDict);
|
||||
CFRelease(innerDict); // and likewise for CFArray
|
||||
|
||||
CFDictionaryAddValue(attr, kCTFontFeatureSettingsAttribute, outerArray);
|
||||
CFRelease(outerArray);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Named constants for these were NOT added until 10.11, and even then they were added as external symbols instead of macros, so we can't use them directly :(
|
||||
// kode54 got these for me before I had access to El Capitan; thanks to him.
|
||||
#define ourNSFontWeightUltraLight -0.800000
|
||||
#define ourNSFontWeightThin -0.600000
|
||||
#define ourNSFontWeightLight -0.400000
|
||||
#define ourNSFontWeightRegular 0.000000
|
||||
#define ourNSFontWeightMedium 0.230000
|
||||
#define ourNSFontWeightSemibold 0.300000
|
||||
#define ourNSFontWeightBold 0.400000
|
||||
#define ourNSFontWeightHeavy 0.560000
|
||||
#define ourNSFontWeightBlack 0.620000
|
||||
static const CGFloat ctWeights[] = {
|
||||
// yeah these two have their names swapped; blame Pango
|
||||
[uiDrawTextWeightThin] = ourNSFontWeightUltraLight,
|
||||
[uiDrawTextWeightUltraLight] = ourNSFontWeightThin,
|
||||
[uiDrawTextWeightLight] = ourNSFontWeightLight,
|
||||
// for this one let's go between Light and Regular
|
||||
// we're doing nearest so if there happens to be an exact value hopefully it's close enough
|
||||
[uiDrawTextWeightBook] = ourNSFontWeightLight + ((ourNSFontWeightRegular - ourNSFontWeightLight) / 2),
|
||||
[uiDrawTextWeightNormal] = ourNSFontWeightRegular,
|
||||
[uiDrawTextWeightMedium] = ourNSFontWeightMedium,
|
||||
[uiDrawTextWeightSemiBold] = ourNSFontWeightSemibold,
|
||||
[uiDrawTextWeightBold] = ourNSFontWeightBold,
|
||||
// for this one let's go between Bold and Heavy
|
||||
[uiDrawTextWeightUltraBold] = ourNSFontWeightBold + ((ourNSFontWeightHeavy - ourNSFontWeightBold) / 2),
|
||||
[uiDrawTextWeightHeavy] = ourNSFontWeightHeavy,
|
||||
[uiDrawTextWeightUltraHeavy] = ourNSFontWeightBlack,
|
||||
};
|
||||
|
||||
// Unfortunately there are still no named constants for these.
|
||||
// Let's just use normalized widths.
|
||||
// As far as I can tell (OS X only ships with condensed fonts, not expanded fonts; TODO), regardless of condensed or expanded, negative means condensed and positive means expanded.
|
||||
// TODO verify this is correct
|
||||
static const CGFloat ctStretches[] = {
|
||||
[uiDrawTextStretchUltraCondensed] = -1.0,
|
||||
[uiDrawTextStretchExtraCondensed] = -0.75,
|
||||
[uiDrawTextStretchCondensed] = -0.5,
|
||||
[uiDrawTextStretchSemiCondensed] = -0.25,
|
||||
[uiDrawTextStretchNormal] = 0.0,
|
||||
[uiDrawTextStretchSemiExpanded] = 0.25,
|
||||
[uiDrawTextStretchExpanded] = 0.5,
|
||||
[uiDrawTextStretchExtraExpanded] = 0.75,
|
||||
[uiDrawTextStretchUltraExpanded] = 1.0,
|
||||
};
|
||||
|
||||
struct closeness {
|
||||
CFIndex index;
|
||||
CGFloat weight;
|
||||
CGFloat italic;
|
||||
CGFloat stretch;
|
||||
CGFloat distance;
|
||||
};
|
||||
|
||||
// Stupidity: CTFont requires an **exact match for the entire traits dictionary**, otherwise it will **drop ALL the traits**.
|
||||
// We have to implement the closest match ourselves.
|
||||
// Also we have to do this before adding the small caps flags, because the matching descriptors won't have those.
|
||||
CTFontDescriptorRef matchTraits(CTFontDescriptorRef against, uiDrawTextWeight weight, uiDrawTextItalic italic, uiDrawTextStretch stretch)
|
||||
{
|
||||
CGFloat targetWeight;
|
||||
CGFloat italicCloseness, obliqueCloseness, normalCloseness;
|
||||
CGFloat targetStretch;
|
||||
CFArrayRef matching;
|
||||
CFIndex i, n;
|
||||
struct closeness *closeness;
|
||||
CTFontDescriptorRef current;
|
||||
CTFontDescriptorRef out;
|
||||
|
||||
targetWeight = ctWeights[weight];
|
||||
switch (italic) {
|
||||
case uiDrawTextItalicNormal:
|
||||
italicCloseness = 1;
|
||||
obliqueCloseness = 1;
|
||||
normalCloseness = 0;
|
||||
break;
|
||||
case uiDrawTextItalicOblique:
|
||||
italicCloseness = 0.5;
|
||||
obliqueCloseness = 0;
|
||||
normalCloseness = 1;
|
||||
break;
|
||||
case uiDrawTextItalicItalic:
|
||||
italicCloseness = 0;
|
||||
obliqueCloseness = 0.5;
|
||||
normalCloseness = 1;
|
||||
break;
|
||||
}
|
||||
targetStretch = ctStretches[stretch];
|
||||
|
||||
matching = CTFontDescriptorCreateMatchingFontDescriptors(against, NULL);
|
||||
if (matching == NULL)
|
||||
// no matches; give the original back and hope for the best
|
||||
return against;
|
||||
n = CFArrayGetCount(matching);
|
||||
if (n == 0) {
|
||||
// likewise
|
||||
CFRelease(matching);
|
||||
return against;
|
||||
}
|
||||
|
||||
closeness = (struct closeness *) uiAlloc(n * sizeof (struct closeness), "struct closeness[]");
|
||||
for (i = 0; i < n; i++) {
|
||||
CFDictionaryRef traits;
|
||||
CFNumberRef cfnum;
|
||||
CTFontSymbolicTraits symbolic;
|
||||
|
||||
closeness[i].index = i;
|
||||
|
||||
current = CFArrayGetValueAtIndex(matching, i);
|
||||
traits = CTFontDescriptorCopyAttribute(current, kCTFontTraitsAttribute);
|
||||
if (traits == NULL) {
|
||||
// couldn't get traits; be safe by ranking it lowest
|
||||
// LONGTERM figure out what the longest possible distances are
|
||||
closeness[i].weight = 3;
|
||||
closeness[i].italic = 2;
|
||||
closeness[i].stretch = 3;
|
||||
continue;
|
||||
}
|
||||
|
||||
symbolic = 0; // assume no symbolic traits if none are listed
|
||||
cfnum = CFDictionaryGetValue(traits, kCTFontSymbolicTrait);
|
||||
if (cfnum != NULL) {
|
||||
SInt32 s;
|
||||
|
||||
if (CFNumberGetValue(cfnum, kCFNumberSInt32Type, &s) == false)
|
||||
complain("error getting symbolic traits in matchTraits()");
|
||||
symbolic = (CTFontSymbolicTraits) s;
|
||||
// Get rule; do not release cfnum
|
||||
}
|
||||
|
||||
// now try weight
|
||||
cfnum = CFDictionaryGetValue(traits, kCTFontWeightTrait);
|
||||
if (cfnum != NULL) {
|
||||
CGFloat val;
|
||||
|
||||
// LONGTERM instead of complaining for this and width and possibly also symbolic traits above, should we just fall through to the default?
|
||||
if (CFNumberGetValue(cfnum, kCFNumberCGFloatType, &val) == false)
|
||||
complain("error getting weight value in matchTraits()");
|
||||
closeness[i].weight = val - targetWeight;
|
||||
} else
|
||||
// okay there's no weight key; let's try the literal meaning of the symbolic constant
|
||||
// LONGTERM is the weight key guaranteed?
|
||||
if ((symbolic & kCTFontBoldTrait) != 0)
|
||||
closeness[i].weight = ourNSFontWeightBold - targetWeight;
|
||||
else
|
||||
closeness[i].weight = ourNSFontWeightRegular - targetWeight;
|
||||
|
||||
// italics is a bit harder because Core Text doesn't expose a concept of obliqueness
|
||||
// Pango just does a g_strrstr() (backwards case-sensitive search) for "Oblique" in the font's style name (see https://git.gnome.org/browse/pango/tree/pango/pangocoretext-fontmap.c); let's do that too I guess
|
||||
if ((symbolic & kCTFontItalicTrait) != 0)
|
||||
closeness[i].italic = italicCloseness;
|
||||
else {
|
||||
CFStringRef styleName;
|
||||
BOOL isOblique;
|
||||
|
||||
isOblique = NO; // default value
|
||||
styleName = CTFontDescriptorCopyAttribute(current, kCTFontStyleNameAttribute);
|
||||
if (styleName != NULL) {
|
||||
CFRange range;
|
||||
|
||||
// note the use of the toll-free bridge for the string literal, since CFSTR() *can* return NULL
|
||||
range = CFStringFind(styleName, (CFStringRef) @"Oblique", kCFCompareBackwards);
|
||||
if (range.location != kCFNotFound)
|
||||
isOblique = YES;
|
||||
CFRelease(styleName);
|
||||
}
|
||||
if (isOblique)
|
||||
closeness[i].italic = obliqueCloseness;
|
||||
else
|
||||
closeness[i].italic = normalCloseness;
|
||||
}
|
||||
|
||||
// now try width
|
||||
// TODO this does not seem to be enough for Skia's extended variants; the width trait is 0 but the Expanded flag is on
|
||||
// TODO verify the rest of this matrix (what matrix?)
|
||||
cfnum = CFDictionaryGetValue(traits, kCTFontWidthTrait);
|
||||
if (cfnum != NULL) {
|
||||
CGFloat val;
|
||||
|
||||
if (CFNumberGetValue(cfnum, kCFNumberCGFloatType, &val) == false)
|
||||
complain("error getting width value in matchTraits()");
|
||||
closeness[i].stretch = val - targetStretch;
|
||||
} else
|
||||
// okay there's no width key; let's try the literal meaning of the symbolic constant
|
||||
// LONGTERM is the width key guaranteed?
|
||||
if ((symbolic & kCTFontExpandedTrait) != 0)
|
||||
closeness[i].stretch = 1.0 - targetStretch;
|
||||
else if ((symbolic & kCTFontCondensedTrait) != 0)
|
||||
closeness[i].stretch = -1.0 - targetStretch;
|
||||
else
|
||||
closeness[i].stretch = 0.0 - targetStretch;
|
||||
|
||||
CFRelease(traits);
|
||||
}
|
||||
|
||||
// now figure out the 3-space difference between the three and sort by that
|
||||
for (i = 0; i < n; i++) {
|
||||
CGFloat weight, italic, stretch;
|
||||
|
||||
weight = closeness[i].weight;
|
||||
weight *= weight;
|
||||
italic = closeness[i].italic;
|
||||
italic *= italic;
|
||||
stretch = closeness[i].stretch;
|
||||
stretch *= stretch;
|
||||
closeness[i].distance = sqrt(weight + italic + stretch);
|
||||
}
|
||||
qsort_b(closeness, n, sizeof (struct closeness), ^(const void *aa, const void *bb) {
|
||||
const struct closeness *a = (const struct closeness *) aa;
|
||||
const struct closeness *b = (const struct closeness *) bb;
|
||||
|
||||
// via http://www.gnu.org/software/libc/manual/html_node/Comparison-Functions.html#Comparison-Functions
|
||||
// LONGTERM is this really the best way? isn't it the same as if (*a < *b) return -1; if (*a > *b) return 1; return 0; ?
|
||||
return (a->distance > b->distance) - (a->distance < b->distance);
|
||||
});
|
||||
// and the first element of the sorted array is what we want
|
||||
out = CFArrayGetValueAtIndex(matching, closeness[0].index);
|
||||
CFRetain(out); // get rule
|
||||
|
||||
// release everything
|
||||
uiFree(closeness);
|
||||
CFRelease(matching);
|
||||
// and release the original descriptor since we no longer need it
|
||||
CFRelease(against);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
// Now remember what I said earlier about having to add the small caps traits after calling the above? This gets a dictionary back so we can do so.
|
||||
CFMutableDictionaryRef extractAttributes(CTFontDescriptorRef desc)
|
||||
{
|
||||
CFDictionaryRef dict;
|
||||
CFMutableDictionaryRef mdict;
|
||||
|
||||
dict = CTFontDescriptorCopyAttributes(desc);
|
||||
// this might not be mutable, so make a mutable copy
|
||||
mdict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
|
||||
CFRelease(dict);
|
||||
return mdict;
|
||||
}
|
||||
|
||||
uiDrawTextFont *uiDrawLoadClosestFont(const uiDrawTextFontDescriptor *desc)
|
||||
{
|
||||
CTFontRef f;
|
||||
CFMutableDictionaryRef attr;
|
||||
CTFontDescriptorRef cfdesc;
|
||||
|
||||
attr = newAttrList();
|
||||
addFontFamilyAttr(attr, desc->Family);
|
||||
addFontSizeAttr(attr, desc->Size);
|
||||
|
||||
// now we have to do the traits matching, so create a descriptor, match the traits, and then get the attributes back
|
||||
cfdesc = CTFontDescriptorCreateWithAttributes(attr);
|
||||
// TODO release attr?
|
||||
cfdesc = matchTraits(cfdesc, desc->Weight, desc->Italic, desc->Stretch);
|
||||
|
||||
// specify the initial size again just to be safe
|
||||
f = CTFontCreateWithFontDescriptor(cfdesc, desc->Size, NULL);
|
||||
// TODO release cfdesc?
|
||||
|
||||
return mkTextFont(f, NO); // we hold the initial reference; no need to retain again
|
||||
}
|
||||
|
||||
void uiDrawFreeTextFont(uiDrawTextFont *font)
|
||||
{
|
||||
CFRelease(font->f);
|
||||
uiFree(font);
|
||||
}
|
||||
|
||||
uintptr_t uiDrawTextFontHandle(uiDrawTextFont *font)
|
||||
{
|
||||
return (uintptr_t) (font->f);
|
||||
}
|
||||
|
||||
void uiDrawTextFontDescribe(uiDrawTextFont *font, uiDrawTextFontDescriptor *desc)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
// text sizes and user space points are identical:
|
||||
// - https://developer.apple.com/library/mac/documentation/TextFonts/Conceptual/CocoaTextArchitecture/TypoFeatures/TextSystemFeatures.html#//apple_ref/doc/uid/TP40009459-CH6-51627-BBCCHIFF text points are 72 per inch
|
||||
// - https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CocoaDrawingGuide/Transforms/Transforms.html#//apple_ref/doc/uid/TP40003290-CH204-SW5 user space points are 72 per inch
|
||||
void uiDrawTextFontGetMetrics(uiDrawTextFont *font, uiDrawTextFontMetrics *metrics)
|
||||
{
|
||||
metrics->Ascent = CTFontGetAscent(font->f);
|
||||
metrics->Descent = CTFontGetDescent(font->f);
|
||||
metrics->Leading = CTFontGetLeading(font->f);
|
||||
metrics->UnderlinePos = CTFontGetUnderlinePosition(font->f);
|
||||
metrics->UnderlineThickness = CTFontGetUnderlineThickness(font->f);
|
||||
}
|
||||
|
||||
struct uiDrawTextLayout {
|
||||
CFMutableAttributedStringRef mas;
|
||||
CFRange *charsToRanges;
|
||||
double width;
|
||||
};
|
||||
|
||||
uiDrawTextLayout *uiDrawNewTextLayout(const char *str, uiDrawTextFont *defaultFont, double width)
|
||||
{
|
||||
uiDrawTextLayout *layout;
|
||||
CFAttributedStringRef immutable;
|
||||
CFMutableDictionaryRef attr;
|
||||
CFStringRef backing;
|
||||
CFIndex i, j, n;
|
||||
|
||||
layout = uiNew(uiDrawTextLayout);
|
||||
|
||||
// TODO docs say we need to use a different set of key callbacks
|
||||
// TODO see if the font attribute key callbacks need to be the same
|
||||
attr = newAttrList();
|
||||
// this will retain defaultFont->f; no need to worry
|
||||
CFDictionaryAddValue(attr, kCTFontAttributeName, defaultFont->f);
|
||||
|
||||
immutable = CFAttributedStringCreate(NULL, (CFStringRef) [NSString stringWithUTF8String:str], attr);
|
||||
if (immutable == NULL)
|
||||
complain("error creating immutable attributed string in uiDrawNewTextLayout()");
|
||||
CFRelease(attr);
|
||||
|
||||
layout->mas = CFAttributedStringCreateMutableCopy(NULL, 0, immutable);
|
||||
if (layout->mas == NULL)
|
||||
complain("error creating attributed string in uiDrawNewTextLayout()");
|
||||
CFRelease(immutable);
|
||||
|
||||
uiDrawTextLayoutSetWidth(layout, width);
|
||||
|
||||
// unfortunately the CFRanges for attributes expect UTF-16 codepoints
|
||||
// we want graphemes
|
||||
// fortunately CFStringGetRangeOfComposedCharactersAtIndex() is here for us
|
||||
// https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Strings/Articles/stringsClusters.html says that this does work on all multi-codepoint graphemes (despite the name), and that this is the preferred function for this particular job anyway
|
||||
backing = CFAttributedStringGetString(layout->mas);
|
||||
n = CFStringGetLength(backing);
|
||||
// allocate one extra, just to be safe
|
||||
layout->charsToRanges = (CFRange *) uiAlloc((n + 1) * sizeof (CFRange), "CFRange[]");
|
||||
i = 0;
|
||||
j = 0;
|
||||
while (i < n) {
|
||||
CFRange range;
|
||||
|
||||
range = CFStringGetRangeOfComposedCharactersAtIndex(backing, i);
|
||||
i = range.location + range.length;
|
||||
layout->charsToRanges[j] = range;
|
||||
j++;
|
||||
}
|
||||
// and set the last one
|
||||
layout->charsToRanges[j].location = i;
|
||||
layout->charsToRanges[j].length = 0;
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
void uiDrawFreeTextLayout(uiDrawTextLayout *layout)
|
||||
{
|
||||
uiFree(layout->charsToRanges);
|
||||
CFRelease(layout->mas);
|
||||
uiFree(layout);
|
||||
}
|
||||
|
||||
void uiDrawTextLayoutSetWidth(uiDrawTextLayout *layout, double width)
|
||||
{
|
||||
layout->width = width;
|
||||
}
|
||||
|
||||
struct framesetter {
|
||||
CTFramesetterRef fs;
|
||||
CFMutableDictionaryRef frameAttrib;
|
||||
CGSize extents;
|
||||
};
|
||||
|
||||
// TODO CTFrameProgression for RTL/LTR
|
||||
// TODO kCTParagraphStyleSpecifierMaximumLineSpacing, kCTParagraphStyleSpecifierMinimumLineSpacing, kCTParagraphStyleSpecifierLineSpacingAdjustment for line spacing
|
||||
static void mkFramesetter(uiDrawTextLayout *layout, struct framesetter *fs)
|
||||
{
|
||||
CFRange fitRange;
|
||||
CGFloat width;
|
||||
|
||||
fs->fs = CTFramesetterCreateWithAttributedString(layout->mas);
|
||||
if (fs->fs == NULL)
|
||||
complain("error creating CTFramesetter object in mkFramesetter()");
|
||||
|
||||
// TODO kCTFramePathWidthAttributeName?
|
||||
fs->frameAttrib = NULL;
|
||||
|
||||
width = layout->width;
|
||||
if (layout->width < 0)
|
||||
width = CGFLOAT_MAX;
|
||||
// TODO these seem to be floor()'d or truncated?
|
||||
fs->extents = CTFramesetterSuggestFrameSizeWithConstraints(fs->fs,
|
||||
CFRangeMake(0, 0),
|
||||
fs->frameAttrib,
|
||||
CGSizeMake(width, CGFLOAT_MAX),
|
||||
&fitRange); // not documented as accepting NULL
|
||||
}
|
||||
|
||||
static void freeFramesetter(struct framesetter *fs)
|
||||
{
|
||||
if (fs->frameAttrib != NULL)
|
||||
CFRelease(fs->frameAttrib);
|
||||
CFRelease(fs->fs);
|
||||
}
|
||||
|
||||
// LONGTERM allow line separation and leading to be factored into a wrapping text layout
|
||||
|
||||
// TODO reconcile differences in character wrapping on platforms
|
||||
void uiDrawTextLayoutExtents(uiDrawTextLayout *layout, double *width, double *height)
|
||||
{
|
||||
struct framesetter fs;
|
||||
|
||||
mkFramesetter(layout, &fs);
|
||||
*width = fs.extents.width;
|
||||
*height = fs.extents.height;
|
||||
freeFramesetter(&fs);
|
||||
}
|
||||
|
||||
// Core Text doesn't draw onto a flipped view correctly; we have to do this
|
||||
// see the iOS bits of the first example at https://developer.apple.com/library/mac/documentation/StringsTextFonts/Conceptual/CoreText_Programming/LayoutOperations/LayoutOperations.html#//apple_ref/doc/uid/TP40005533-CH12-SW1 (iOS is naturally flipped)
|
||||
// TODO how is this affected by the CTM?
|
||||
static void prepareContextForText(CGContextRef c, CGFloat cheight, double *y)
|
||||
{
|
||||
CGContextSaveGState(c);
|
||||
CGContextTranslateCTM(c, 0, cheight);
|
||||
CGContextScaleCTM(c, 1.0, -1.0);
|
||||
CGContextSetTextMatrix(c, CGAffineTransformIdentity);
|
||||
|
||||
// wait, that's not enough; we need to offset y values to account for our new flipping
|
||||
*y = cheight - *y;
|
||||
}
|
||||
|
||||
// TODO placement is incorrect for Helvetica
|
||||
void doDrawText(CGContextRef c, CGFloat cheight, double x, double y, uiDrawTextLayout *layout)
|
||||
{
|
||||
struct framesetter fs;
|
||||
CGRect rect;
|
||||
CGPathRef path;
|
||||
CTFrameRef frame;
|
||||
|
||||
prepareContextForText(c, cheight, &y);
|
||||
mkFramesetter(layout, &fs);
|
||||
|
||||
// oh, and since we're flipped, y is the bottom-left coordinate of the rectangle, not the top-left
|
||||
// since we are flipped, we subtract
|
||||
y -= fs.extents.height;
|
||||
|
||||
rect.origin = CGPointMake(x, y);
|
||||
rect.size = fs.extents;
|
||||
path = CGPathCreateWithRect(rect, NULL);
|
||||
|
||||
frame = CTFramesetterCreateFrame(fs.fs,
|
||||
CFRangeMake(0, 0),
|
||||
path,
|
||||
fs.frameAttrib);
|
||||
if (frame == NULL)
|
||||
complain("error creating CTFrame object in doDrawText()");
|
||||
CTFrameDraw(frame, c);
|
||||
CFRelease(frame);
|
||||
|
||||
CFRelease(path);
|
||||
|
||||
freeFramesetter(&fs);
|
||||
CGContextRestoreGState(c);
|
||||
}
|
||||
|
||||
// LONGTERM provide an equivalent to CTLineGetTypographicBounds() on uiDrawTextLayout?
|
||||
|
||||
// LONGTERM keep this for later features and documentation purposes
|
||||
#if 0
|
||||
w = CTLineGetTypographicBounds(line, &ascent, &descent, NULL);
|
||||
// though CTLineGetTypographicBounds() returns 0 on error, it also returns 0 on an empty string, so we can't reasonably check for error
|
||||
CFRelease(line);
|
||||
|
||||
// LONGTERM provide a way to get the image bounds as a separate function later
|
||||
bounds = CTLineGetImageBounds(line, c);
|
||||
// though CTLineGetImageBounds() returns CGRectNull on error, it also returns CGRectNull on an empty string, so we can't reasonably check for error
|
||||
|
||||
// CGContextSetTextPosition() positions at the baseline in the case of CTLineDraw(); we need the top-left corner instead
|
||||
CTLineGetTypographicBounds(line, &yoff, NULL, NULL);
|
||||
// remember that we're flipped, so we subtract
|
||||
y -= yoff;
|
||||
CGContextSetTextPosition(c, x, y);
|
||||
#endif
|
||||
|
||||
static CFRange charsToRange(uiDrawTextLayout *layout, int startChar, int endChar)
|
||||
{
|
||||
CFRange start, end;
|
||||
CFRange out;
|
||||
|
||||
start = layout->charsToRanges[startChar];
|
||||
end = layout->charsToRanges[endChar];
|
||||
out.location = start.location;
|
||||
out.length = end.location - start.location;
|
||||
return out;
|
||||
}
|
||||
|
||||
#define rangeToCFRange() charsToRange(layout, startChar, endChar)
|
||||
|
||||
void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, int startChar, int endChar, double r, double g, double b, double a)
|
||||
{
|
||||
CGColorSpaceRef colorspace;
|
||||
CGFloat components[4];
|
||||
CGColorRef color;
|
||||
|
||||
// for consistency with windows, use sRGB
|
||||
colorspace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
|
||||
components[0] = r;
|
||||
components[1] = g;
|
||||
components[2] = b;
|
||||
components[3] = a;
|
||||
color = CGColorCreate(colorspace, components);
|
||||
CGColorSpaceRelease(colorspace);
|
||||
|
||||
CFAttributedStringSetAttribute(layout->mas,
|
||||
rangeToCFRange(),
|
||||
kCTForegroundColorAttributeName,
|
||||
color);
|
||||
CGColorRelease(color); // TODO safe?
|
||||
}
|
185
deps/libui/darwin/editablecombo.m
vendored
185
deps/libui/darwin/editablecombo.m
vendored
@ -1,185 +0,0 @@
|
||||
// 14 august 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// So why did I split uiCombobox into uiCombobox and uiEditableCombobox? Here's (90% of the; the other 10% is GTK+ events) answer:
|
||||
// When you type a value into a NSComboBox that just happens to be in the list, it will autoselect that item!
|
||||
// I can't seem to find a workaround.
|
||||
// Fortunately, there's other weird behaviors that made this split worth it.
|
||||
// And besides, selected items make little sense with editable comboboxes... you either separate or combine them with the text entry :V
|
||||
|
||||
// NSComboBoxes have no intrinsic width; we'll use the default Interface Builder width for them.
|
||||
#define comboboxWidth 96
|
||||
|
||||
@interface libui_intrinsicWidthNSComboBox : NSComboBox
|
||||
@end
|
||||
|
||||
@implementation libui_intrinsicWidthNSComboBox
|
||||
|
||||
- (NSSize)intrinsicContentSize
|
||||
{
|
||||
NSSize s;
|
||||
|
||||
s = [super intrinsicContentSize];
|
||||
s.width = comboboxWidth;
|
||||
return s;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
struct uiEditableCombobox {
|
||||
uiDarwinControl c;
|
||||
NSComboBox *cb;
|
||||
void (*onChanged)(uiEditableCombobox *, void *);
|
||||
void *onChangedData;
|
||||
};
|
||||
|
||||
@interface editableComboboxDelegateClass : NSObject<NSComboBoxDelegate> {
|
||||
struct mapTable *comboboxes;
|
||||
}
|
||||
- (void)controlTextDidChange:(NSNotification *)note;
|
||||
- (void)comboBoxSelectionDidChange:(NSNotification *)note;
|
||||
- (void)registerCombobox:(uiEditableCombobox *)c;
|
||||
- (void)unregisterCombobox:(uiEditableCombobox *)c;
|
||||
@end
|
||||
|
||||
@implementation editableComboboxDelegateClass
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
self->comboboxes = newMap();
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
mapDestroy(self->comboboxes);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void)controlTextDidChange:(NSNotification *)note
|
||||
{
|
||||
uiEditableCombobox *c;
|
||||
|
||||
c = uiEditableCombobox(mapGet(self->comboboxes, [note object]));
|
||||
(*(c->onChanged))(c, c->onChangedData);
|
||||
}
|
||||
|
||||
// the above doesn't handle when an item is selected; this will
|
||||
- (void)comboBoxSelectionDidChange:(NSNotification *)note
|
||||
{
|
||||
// except this is sent BEFORE the entry is changed, and that doesn't send the above, so
|
||||
// this is via http://stackoverflow.com/a/21059819/3408572 - it avoids the need to manage selected items
|
||||
// this still isn't perfect — I get residual changes to the same value while navigating the list — but it's good enough
|
||||
[self performSelector:@selector(controlTextDidChange:)
|
||||
withObject:note
|
||||
afterDelay:0];
|
||||
}
|
||||
|
||||
- (void)registerCombobox:(uiEditableCombobox *)c
|
||||
{
|
||||
mapSet(self->comboboxes, c->cb, c);
|
||||
[c->cb setDelegate:self];
|
||||
}
|
||||
|
||||
- (void)unregisterCombobox:(uiEditableCombobox *)c
|
||||
{
|
||||
[c->cb setDelegate:nil];
|
||||
mapDelete(self->comboboxes, c->cb);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static editableComboboxDelegateClass *comboboxDelegate = nil;
|
||||
|
||||
uiDarwinControlAllDefaultsExceptDestroy(uiEditableCombobox, cb)
|
||||
|
||||
static void uiEditableComboboxDestroy(uiControl *cc)
|
||||
{
|
||||
uiEditableCombobox *c = uiEditableCombobox(cc);
|
||||
|
||||
[comboboxDelegate unregisterCombobox:c];
|
||||
[c->cb release];
|
||||
uiFreeControl(uiControl(c));
|
||||
}
|
||||
|
||||
void uiEditableComboboxAppend(uiEditableCombobox *c, const char *text)
|
||||
{
|
||||
[c->cb addItemWithObjectValue:toNSString(text)];
|
||||
}
|
||||
|
||||
char *uiEditableComboboxText(uiEditableCombobox *c)
|
||||
{
|
||||
return uiDarwinNSStringToText([c->cb stringValue]);
|
||||
}
|
||||
|
||||
void uiEditableComboboxSetText(uiEditableCombobox *c, const char *text)
|
||||
{
|
||||
NSString *t;
|
||||
|
||||
t = toNSString(text);
|
||||
[c->cb setStringValue:t];
|
||||
// yes, let's imitate the behavior that caused uiEditableCombobox to be separate in the first place!
|
||||
// just to avoid confusion when users see an option in the list in the text field but not selected in the list
|
||||
[c->cb selectItemWithObjectValue:t];
|
||||
}
|
||||
|
||||
#if 0
|
||||
// LONGTERM
|
||||
void uiEditableComboboxSetSelected(uiEditableCombobox *c, int n)
|
||||
{
|
||||
if (c->editable) {
|
||||
// see https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ComboBox/Tasks/SettingComboBoxValue.html#//apple_ref/doc/uid/20000256
|
||||
id delegate;
|
||||
|
||||
// this triggers the delegate; turn it off for now
|
||||
delegate = [c->cb delegate];
|
||||
[c->cb setDelegate:nil];
|
||||
|
||||
// this seems to work fine for -1 too
|
||||
[c->cb selectItemAtIndex:n];
|
||||
if (n == -1)
|
||||
[c->cb setObjectValue:@""];
|
||||
else
|
||||
[c->cb setObjectValue:[c->cb objectValueOfSelectedItem]];
|
||||
|
||||
[c->cb setDelegate:delegate];
|
||||
return;
|
||||
}
|
||||
[c->pb selectItemAtIndex:n];
|
||||
}
|
||||
#endif
|
||||
|
||||
void uiEditableComboboxOnChanged(uiEditableCombobox *c, void (*f)(uiEditableCombobox *c, void *data), void *data)
|
||||
{
|
||||
c->onChanged = f;
|
||||
c->onChangedData = data;
|
||||
}
|
||||
|
||||
static void defaultOnChanged(uiEditableCombobox *c, void *data)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
uiEditableCombobox *uiNewEditableCombobox(void)
|
||||
{
|
||||
uiEditableCombobox *c;
|
||||
|
||||
uiDarwinNewControl(uiEditableCombobox, c);
|
||||
|
||||
c->cb = [[libui_intrinsicWidthNSComboBox alloc] initWithFrame:NSZeroRect];
|
||||
[c->cb setUsesDataSource:NO];
|
||||
[c->cb setButtonBordered:YES];
|
||||
[c->cb setCompletes:NO];
|
||||
uiDarwinSetControlFont(c->cb, NSRegularControlSize);
|
||||
|
||||
if (comboboxDelegate == nil) {
|
||||
comboboxDelegate = [[editableComboboxDelegateClass new] autorelease];
|
||||
[delegates addObject:comboboxDelegate];
|
||||
}
|
||||
[comboboxDelegate registerCombobox:c];
|
||||
uiEditableComboboxOnChanged(c, defaultOnChanged, NULL);
|
||||
|
||||
return c;
|
||||
}
|
251
deps/libui/darwin/entry.m
vendored
251
deps/libui/darwin/entry.m
vendored
@ -1,251 +0,0 @@
|
||||
// 14 august 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// Text fields for entering text have no intrinsic width; we'll use the default Interface Builder width for them.
|
||||
#define textfieldWidth 96
|
||||
|
||||
@interface libui_intrinsicWidthNSTextField : NSTextField
|
||||
@end
|
||||
|
||||
@implementation libui_intrinsicWidthNSTextField
|
||||
|
||||
- (NSSize)intrinsicContentSize
|
||||
{
|
||||
NSSize s;
|
||||
|
||||
s = [super intrinsicContentSize];
|
||||
s.width = textfieldWidth;
|
||||
return s;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
// TODO does this have one on its own?
|
||||
@interface libui_intrinsicWidthNSSecureTextField : NSSecureTextField
|
||||
@end
|
||||
|
||||
@implementation libui_intrinsicWidthNSSecureTextField
|
||||
|
||||
- (NSSize)intrinsicContentSize
|
||||
{
|
||||
NSSize s;
|
||||
|
||||
s = [super intrinsicContentSize];
|
||||
s.width = textfieldWidth;
|
||||
return s;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
// TODO does this have one on its own?
|
||||
@interface libui_intrinsicWidthNSSearchField : NSSearchField
|
||||
@end
|
||||
|
||||
@implementation libui_intrinsicWidthNSSearchField
|
||||
|
||||
- (NSSize)intrinsicContentSize
|
||||
{
|
||||
NSSize s;
|
||||
|
||||
s = [super intrinsicContentSize];
|
||||
s.width = textfieldWidth;
|
||||
return s;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
struct uiEntry {
|
||||
uiDarwinControl c;
|
||||
NSTextField *textfield;
|
||||
void (*onChanged)(uiEntry *, void *);
|
||||
void *onChangedData;
|
||||
};
|
||||
|
||||
static BOOL isSearchField(NSTextField *tf)
|
||||
{
|
||||
return [tf isKindOfClass:[NSSearchField class]];
|
||||
}
|
||||
|
||||
@interface entryDelegateClass : NSObject<NSTextFieldDelegate> {
|
||||
struct mapTable *entries;
|
||||
}
|
||||
- (void)controlTextDidChange:(NSNotification *)note;
|
||||
- (IBAction)onSearch:(id)sender;
|
||||
- (void)registerEntry:(uiEntry *)e;
|
||||
- (void)unregisterEntry:(uiEntry *)e;
|
||||
@end
|
||||
|
||||
@implementation entryDelegateClass
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
self->entries = newMap();
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
mapDestroy(self->entries);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void)controlTextDidChange:(NSNotification *)note
|
||||
{
|
||||
[self onSearch:[note object]];
|
||||
}
|
||||
|
||||
- (IBAction)onSearch:(id)sender
|
||||
{
|
||||
uiEntry *e;
|
||||
|
||||
e = (uiEntry *) mapGet(self->entries, sender);
|
||||
(*(e->onChanged))(e, e->onChangedData);
|
||||
}
|
||||
|
||||
- (void)registerEntry:(uiEntry *)e
|
||||
{
|
||||
mapSet(self->entries, e->textfield, e);
|
||||
if (isSearchField(e->textfield)) {
|
||||
[e->textfield setTarget:self];
|
||||
[e->textfield setAction:@selector(onSearch:)];
|
||||
} else
|
||||
[e->textfield setDelegate:self];
|
||||
}
|
||||
|
||||
- (void)unregisterEntry:(uiEntry *)e
|
||||
{
|
||||
if (isSearchField(e->textfield))
|
||||
[e->textfield setTarget:nil];
|
||||
else
|
||||
[e->textfield setDelegate:nil];
|
||||
mapDelete(self->entries, e->textfield);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static entryDelegateClass *entryDelegate = nil;
|
||||
|
||||
uiDarwinControlAllDefaultsExceptDestroy(uiEntry, textfield)
|
||||
|
||||
static void uiEntryDestroy(uiControl *c)
|
||||
{
|
||||
uiEntry *e = uiEntry(c);
|
||||
|
||||
[entryDelegate unregisterEntry:e];
|
||||
[e->textfield release];
|
||||
uiFreeControl(uiControl(e));
|
||||
}
|
||||
|
||||
char *uiEntryText(uiEntry *e)
|
||||
{
|
||||
return uiDarwinNSStringToText([e->textfield stringValue]);
|
||||
}
|
||||
|
||||
void uiEntrySetText(uiEntry *e, const char *text)
|
||||
{
|
||||
[e->textfield setStringValue:toNSString(text)];
|
||||
// don't queue the control for resize; entry sizes are independent of their contents
|
||||
}
|
||||
|
||||
void uiEntryOnChanged(uiEntry *e, void (*f)(uiEntry *, void *), void *data)
|
||||
{
|
||||
e->onChanged = f;
|
||||
e->onChangedData = data;
|
||||
}
|
||||
|
||||
int uiEntryReadOnly(uiEntry *e)
|
||||
{
|
||||
return [e->textfield isEditable] == NO;
|
||||
}
|
||||
|
||||
void uiEntrySetReadOnly(uiEntry *e, int readonly)
|
||||
{
|
||||
BOOL editable;
|
||||
|
||||
editable = YES;
|
||||
if (readonly)
|
||||
editable = NO;
|
||||
[e->textfield setEditable:editable];
|
||||
}
|
||||
|
||||
static void defaultOnChanged(uiEntry *e, void *data)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
// these are based on interface builder defaults; my comments in the old code weren't very good so I don't really know what talked about what, sorry :/
|
||||
void finishNewTextField(NSTextField *t, BOOL isEntry)
|
||||
{
|
||||
uiDarwinSetControlFont(t, NSRegularControlSize);
|
||||
|
||||
// THE ORDER OF THESE CALLS IS IMPORTANT; CHANGE IT AND THE BORDERS WILL DISAPPEAR
|
||||
[t setBordered:NO];
|
||||
[t setBezelStyle:NSTextFieldSquareBezel];
|
||||
[t setBezeled:isEntry];
|
||||
|
||||
// we don't need to worry about substitutions/autocorrect here; see window_darwin.m for details
|
||||
|
||||
[[t cell] setLineBreakMode:NSLineBreakByClipping];
|
||||
[[t cell] setScrollable:YES];
|
||||
}
|
||||
|
||||
static NSTextField *realNewEditableTextField(Class class)
|
||||
{
|
||||
NSTextField *tf;
|
||||
|
||||
tf = [[class alloc] initWithFrame:NSZeroRect];
|
||||
[tf setSelectable:YES]; // otherwise the setting is masked by the editable default of YES
|
||||
finishNewTextField(tf, YES);
|
||||
return tf;
|
||||
}
|
||||
|
||||
NSTextField *newEditableTextField(void)
|
||||
{
|
||||
return realNewEditableTextField([libui_intrinsicWidthNSTextField class]);
|
||||
}
|
||||
|
||||
static uiEntry *finishNewEntry(Class class)
|
||||
{
|
||||
uiEntry *e;
|
||||
|
||||
uiDarwinNewControl(uiEntry, e);
|
||||
|
||||
e->textfield = realNewEditableTextField(class);
|
||||
|
||||
if (entryDelegate == nil) {
|
||||
entryDelegate = [[entryDelegateClass new] autorelease];
|
||||
[delegates addObject:entryDelegate];
|
||||
}
|
||||
[entryDelegate registerEntry:e];
|
||||
uiEntryOnChanged(e, defaultOnChanged, NULL);
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
uiEntry *uiNewEntry(void)
|
||||
{
|
||||
return finishNewEntry([libui_intrinsicWidthNSTextField class]);
|
||||
}
|
||||
|
||||
uiEntry *uiNewPasswordEntry(void)
|
||||
{
|
||||
return finishNewEntry([libui_intrinsicWidthNSSecureTextField class]);
|
||||
}
|
||||
|
||||
uiEntry *uiNewSearchEntry(void)
|
||||
{
|
||||
uiEntry *e;
|
||||
NSSearchField *s;
|
||||
|
||||
e = finishNewEntry([libui_intrinsicWidthNSSearchField class]);
|
||||
s = (NSSearchField *) (e->textfield);
|
||||
// TODO these are only on 10.10
|
||||
// [s setSendsSearchStringImmediately:NO];
|
||||
// [s setSendsWholeSearchString:NO];
|
||||
[s setBordered:NO];
|
||||
[s setBezelStyle:NSTextFieldRoundedBezel];
|
||||
[s setBezeled:YES];
|
||||
return e;
|
||||
}
|
218
deps/libui/darwin/fontbutton.m
vendored
218
deps/libui/darwin/fontbutton.m
vendored
@ -1,218 +0,0 @@
|
||||
// 14 april 2016
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
@interface fontButton : NSButton {
|
||||
uiFontButton *libui_b;
|
||||
NSFont *libui_font;
|
||||
}
|
||||
- (id)initWithFrame:(NSRect)frame libuiFontButton:(uiFontButton *)b;
|
||||
- (void)updateFontButtonLabel;
|
||||
- (IBAction)fontButtonClicked:(id)sender;
|
||||
- (void)activateFontButton;
|
||||
- (void)deactivateFontButton:(BOOL)activatingAnother;
|
||||
- (void)deactivateOnClose:(NSNotification *)note;
|
||||
- (uiDrawTextFont *)libuiFont;
|
||||
@end
|
||||
|
||||
// only one may be active at one time
|
||||
static fontButton *activeFontButton = nil;
|
||||
|
||||
struct uiFontButton {
|
||||
uiDarwinControl c;
|
||||
fontButton *button;
|
||||
void (*onChanged)(uiFontButton *, void *);
|
||||
void *onChangedData;
|
||||
};
|
||||
|
||||
@implementation fontButton
|
||||
|
||||
- (id)initWithFrame:(NSRect)frame libuiFontButton:(uiFontButton *)b
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
if (self) {
|
||||
self->libui_b = b;
|
||||
|
||||
// imitate a NSColorWell in appearance
|
||||
[self setButtonType:NSPushOnPushOffButton];
|
||||
[self setBordered:YES];
|
||||
[self setBezelStyle:NSShadowlessSquareBezelStyle];
|
||||
|
||||
// default font values according to the CTFontDescriptor reference
|
||||
// this is autoreleased (thanks swillits in irc.freenode.net/#macdev)
|
||||
self->libui_font = [[NSFont fontWithName:@"Helvetica" size:12.0] retain];
|
||||
[self updateFontButtonLabel];
|
||||
|
||||
// for when clicked
|
||||
[self setTarget:self];
|
||||
[self setAction:@selector(fontButtonClicked:)];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
// clean up notifications
|
||||
if (activeFontButton == self)
|
||||
[self deactivateFontButton:NO];
|
||||
[self->libui_font release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void)updateFontButtonLabel
|
||||
{
|
||||
NSString *title;
|
||||
|
||||
title = [NSString stringWithFormat:@"%@ %g",
|
||||
[self->libui_font displayName],
|
||||
[self->libui_font pointSize]];
|
||||
[self setTitle:title];
|
||||
}
|
||||
|
||||
- (IBAction)fontButtonClicked:(id)sender
|
||||
{
|
||||
if ([self state] == NSOnState)
|
||||
[self activateFontButton];
|
||||
else
|
||||
[self deactivateFontButton:NO];
|
||||
}
|
||||
|
||||
- (void)activateFontButton
|
||||
{
|
||||
NSFontManager *sfm;
|
||||
|
||||
sfm = [NSFontManager sharedFontManager];
|
||||
if (activeFontButton != nil)
|
||||
[activeFontButton deactivateFontButton:YES];
|
||||
[sfm setTarget:self];
|
||||
[sfm setSelectedFont:self->libui_font isMultiple:NO];
|
||||
[sfm orderFrontFontPanel:self];
|
||||
activeFontButton = self;
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(deactivateOnClose:)
|
||||
name:NSWindowWillCloseNotification
|
||||
object:[NSFontPanel sharedFontPanel]];
|
||||
[self setState:NSOnState];
|
||||
}
|
||||
|
||||
- (void)deactivateFontButton:(BOOL)activatingAnother
|
||||
{
|
||||
NSFontManager *sfm;
|
||||
|
||||
sfm = [NSFontManager sharedFontManager];
|
||||
[sfm setTarget:nil];
|
||||
if (!activatingAnother)
|
||||
[[NSFontPanel sharedFontPanel] orderOut:self];
|
||||
activeFontButton = nil;
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self
|
||||
name:NSWindowWillCloseNotification
|
||||
object:[NSFontPanel sharedFontPanel]];
|
||||
[self setState:NSOffState];
|
||||
}
|
||||
|
||||
- (void)deactivateOnClose:(NSNotification *)note
|
||||
{
|
||||
[self deactivateFontButton:NO];
|
||||
}
|
||||
|
||||
- (void)changeFont:(id)sender
|
||||
{
|
||||
NSFontManager *fm;
|
||||
NSFont *old;
|
||||
uiFontButton *b = self->libui_b;
|
||||
|
||||
fm = (NSFontManager *) sender;
|
||||
old = self->libui_font;
|
||||
self->libui_font = [sender convertFont:self->libui_font];
|
||||
// do this even if it returns the same; we don't own anything that isn't from a new or alloc/init
|
||||
[self->libui_font retain];
|
||||
// do this second just in case
|
||||
[old release];
|
||||
[self updateFontButtonLabel];
|
||||
(*(b->onChanged))(b, b->onChangedData);
|
||||
}
|
||||
|
||||
- (NSUInteger)validModesForFontPanel:(NSFontPanel *)panel
|
||||
{
|
||||
return NSFontPanelFaceModeMask |
|
||||
NSFontPanelSizeModeMask |
|
||||
NSFontPanelCollectionModeMask;
|
||||
}
|
||||
|
||||
- (uiDrawTextFont *)libuiFont
|
||||
{
|
||||
return mkTextFontFromNSFont(self->libui_font);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
uiDarwinControlAllDefaults(uiFontButton, button)
|
||||
|
||||
// we do not want font change events to be sent to any controls other than the font buttons
|
||||
// see main.m for more details
|
||||
BOOL fontButtonInhibitSendAction(SEL sel, id from, id to)
|
||||
{
|
||||
if (sel != @selector(changeFont:))
|
||||
return NO;
|
||||
return ![to isKindOfClass:[fontButton class]];
|
||||
}
|
||||
|
||||
// we do not want NSFontPanelValidation messages to be sent to any controls other than the font buttons when a font button is active
|
||||
// see main.m for more details
|
||||
BOOL fontButtonOverrideTargetForAction(SEL sel, id from, id to, id *override)
|
||||
{
|
||||
if (activeFontButton == nil)
|
||||
return NO;
|
||||
if (sel != @selector(validModesForFontPanel:))
|
||||
return NO;
|
||||
*override = activeFontButton;
|
||||
return YES;
|
||||
}
|
||||
|
||||
// we also don't want the panel to be usable when there's a dialog running; see stddialogs.m for more details on that
|
||||
// unfortunately the panel seems to ignore -setWorksWhenModal: so we'll have to do things ourselves
|
||||
@interface nonModalFontPanel : NSFontPanel
|
||||
@end
|
||||
|
||||
@implementation nonModalFontPanel
|
||||
|
||||
- (BOOL)worksWhenModal
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
void setupFontPanel(void)
|
||||
{
|
||||
[NSFontManager setFontPanelFactory:[nonModalFontPanel class]];
|
||||
}
|
||||
|
||||
static void defaultOnChanged(uiFontButton *b, void *data)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
uiDrawTextFont *uiFontButtonFont(uiFontButton *b)
|
||||
{
|
||||
return [b->button libuiFont];
|
||||
}
|
||||
|
||||
void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data)
|
||||
{
|
||||
b->onChanged = f;
|
||||
b->onChangedData = data;
|
||||
}
|
||||
|
||||
uiFontButton *uiNewFontButton(void)
|
||||
{
|
||||
uiFontButton *b;
|
||||
|
||||
uiDarwinNewControl(uiFontButton, b);
|
||||
|
||||
b->button = [[fontButton alloc] initWithFrame:NSZeroRect libuiFontButton:b];
|
||||
uiDarwinSetControlFont(b->button, NSRegularControlSize);
|
||||
|
||||
uiFontButtonOnChanged(b, defaultOnChanged, NULL);
|
||||
|
||||
return b;
|
||||
}
|
561
deps/libui/darwin/form.m
vendored
561
deps/libui/darwin/form.m
vendored
@ -1,561 +0,0 @@
|
||||
// 7 june 2016
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// TODO in the test program, sometimes one of the radio buttons can disappear (try when spaced)
|
||||
|
||||
@interface formChild : NSView
|
||||
@property uiControl *c;
|
||||
@property (strong) NSTextField *label;
|
||||
@property BOOL stretchy;
|
||||
@property NSLayoutPriority oldHorzHuggingPri;
|
||||
@property NSLayoutPriority oldVertHuggingPri;
|
||||
@property (strong) NSLayoutConstraint *baseline;
|
||||
@property (strong) NSLayoutConstraint *leading;
|
||||
@property (strong) NSLayoutConstraint *top;
|
||||
@property (strong) NSLayoutConstraint *trailing;
|
||||
@property (strong) NSLayoutConstraint *bottom;
|
||||
- (id)initWithLabel:(NSTextField *)l;
|
||||
- (void)onDestroy;
|
||||
- (NSView *)view;
|
||||
@end
|
||||
|
||||
@interface formView : NSView {
|
||||
uiForm *f;
|
||||
NSMutableArray *children;
|
||||
int padded;
|
||||
|
||||
NSLayoutConstraint *first;
|
||||
NSMutableArray *inBetweens;
|
||||
NSLayoutConstraint *last;
|
||||
NSMutableArray *widths;
|
||||
NSMutableArray *leadings;
|
||||
NSMutableArray *middles;
|
||||
NSMutableArray *trailings;
|
||||
}
|
||||
- (id)initWithF:(uiForm *)ff;
|
||||
- (void)onDestroy;
|
||||
- (void)removeOurConstraints;
|
||||
- (void)syncEnableStates:(int)enabled;
|
||||
- (CGFloat)paddingAmount;
|
||||
- (void)establishOurConstraints;
|
||||
- (void)append:(NSString *)label c:(uiControl *)c stretchy:(int)stretchy;
|
||||
- (void)delete:(int)n;
|
||||
- (int)isPadded;
|
||||
- (void)setPadded:(int)p;
|
||||
- (BOOL)hugsTrailing;
|
||||
- (BOOL)hugsBottom;
|
||||
- (int)nStretchy;
|
||||
@end
|
||||
|
||||
struct uiForm {
|
||||
uiDarwinControl c;
|
||||
formView *view;
|
||||
};
|
||||
|
||||
@implementation formChild
|
||||
|
||||
- (id)initWithLabel:(NSTextField *)l
|
||||
{
|
||||
self = [super initWithFrame:NSZeroRect];
|
||||
if (self) {
|
||||
self.label = l;
|
||||
[self.label setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||
[self.label setContentHuggingPriority:NSLayoutPriorityRequired forOrientation:NSLayoutConstraintOrientationHorizontal];
|
||||
[self.label setContentHuggingPriority:NSLayoutPriorityRequired forOrientation:NSLayoutConstraintOrientationVertical];
|
||||
[self.label setContentCompressionResistancePriority:NSLayoutPriorityRequired forOrientation:NSLayoutConstraintOrientationHorizontal];
|
||||
[self.label setContentCompressionResistancePriority:NSLayoutPriorityRequired forOrientation:NSLayoutConstraintOrientationVertical];
|
||||
[self addSubview:self.label];
|
||||
|
||||
self.leading = mkConstraint(self.label, NSLayoutAttributeLeading,
|
||||
NSLayoutRelationGreaterThanOrEqual,
|
||||
self, NSLayoutAttributeLeading,
|
||||
1, 0,
|
||||
@"uiForm label leading");
|
||||
[self addConstraint:self.leading];
|
||||
self.top = mkConstraint(self.label, NSLayoutAttributeTop,
|
||||
NSLayoutRelationEqual,
|
||||
self, NSLayoutAttributeTop,
|
||||
1, 0,
|
||||
@"uiForm label top");
|
||||
[self addConstraint:self.top];
|
||||
self.trailing = mkConstraint(self.label, NSLayoutAttributeTrailing,
|
||||
NSLayoutRelationEqual,
|
||||
self, NSLayoutAttributeTrailing,
|
||||
1, 0,
|
||||
@"uiForm label trailing");
|
||||
[self addConstraint:self.trailing];
|
||||
self.bottom = mkConstraint(self.label, NSLayoutAttributeBottom,
|
||||
NSLayoutRelationEqual,
|
||||
self, NSLayoutAttributeBottom,
|
||||
1, 0,
|
||||
@"uiForm label bottom");
|
||||
[self addConstraint:self.bottom];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)onDestroy
|
||||
{
|
||||
[self removeConstraint:self.trailing];
|
||||
self.trailing = nil;
|
||||
[self removeConstraint:self.top];
|
||||
self.top = nil;
|
||||
[self removeConstraint:self.bottom];
|
||||
self.bottom = nil;
|
||||
|
||||
[self.label removeFromSuperview];
|
||||
self.label = nil;
|
||||
}
|
||||
|
||||
- (NSView *)view
|
||||
{
|
||||
return (NSView *) uiControlHandle(self.c);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation formView
|
||||
|
||||
- (id)initWithF:(uiForm *)ff
|
||||
{
|
||||
self = [super initWithFrame:NSZeroRect];
|
||||
if (self != nil) {
|
||||
self->f = ff;
|
||||
self->padded = 0;
|
||||
self->children = [NSMutableArray new];
|
||||
|
||||
self->inBetweens = [NSMutableArray new];
|
||||
self->widths = [NSMutableArray new];
|
||||
self->leadings = [NSMutableArray new];
|
||||
self->middles = [NSMutableArray new];
|
||||
self->trailings = [NSMutableArray new];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)onDestroy
|
||||
{
|
||||
formChild *fc;
|
||||
|
||||
[self removeOurConstraints];
|
||||
[self->inBetweens release];
|
||||
[self->widths release];
|
||||
[self->leadings release];
|
||||
[self->middles release];
|
||||
[self->trailings release];
|
||||
|
||||
for (fc in self->children) {
|
||||
[self removeConstraint:fc.baseline];
|
||||
fc.baseline = nil;
|
||||
uiControlSetParent(fc.c, NULL);
|
||||
uiDarwinControlSetSuperview(uiDarwinControl(fc.c), nil);
|
||||
uiControlDestroy(fc.c);
|
||||
[fc onDestroy];
|
||||
[fc removeFromSuperview];
|
||||
}
|
||||
[self->children release];
|
||||
}
|
||||
|
||||
- (void)removeOurConstraints
|
||||
{
|
||||
if (self->first != nil) {
|
||||
[self removeConstraint:self->first];
|
||||
[self->first release];
|
||||
self->first = nil;
|
||||
}
|
||||
if ([self->inBetweens count] != 0) {
|
||||
[self removeConstraints:self->inBetweens];
|
||||
[self->inBetweens removeAllObjects];
|
||||
}
|
||||
if (self->last != nil) {
|
||||
[self removeConstraint:self->last];
|
||||
[self->last release];
|
||||
self->last = nil;
|
||||
}
|
||||
if ([self->widths count] != 0) {
|
||||
[self removeConstraints:self->widths];
|
||||
[self->widths removeAllObjects];
|
||||
}
|
||||
if ([self->leadings count] != 0) {
|
||||
[self removeConstraints:self->leadings];
|
||||
[self->leadings removeAllObjects];
|
||||
}
|
||||
if ([self->middles count] != 0) {
|
||||
[self removeConstraints:self->middles];
|
||||
[self->middles removeAllObjects];
|
||||
}
|
||||
if ([self->trailings count] != 0) {
|
||||
[self removeConstraints:self->trailings];
|
||||
[self->trailings removeAllObjects];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)syncEnableStates:(int)enabled
|
||||
{
|
||||
formChild *fc;
|
||||
|
||||
for (fc in self->children)
|
||||
uiDarwinControlSyncEnableState(uiDarwinControl(fc.c), enabled);
|
||||
}
|
||||
|
||||
- (CGFloat)paddingAmount
|
||||
{
|
||||
if (!self->padded)
|
||||
return 0.0;
|
||||
return uiDarwinPaddingAmount(NULL);
|
||||
}
|
||||
|
||||
- (void)establishOurConstraints
|
||||
{
|
||||
formChild *fc;
|
||||
CGFloat padding;
|
||||
NSView *prev, *prevlabel;
|
||||
NSLayoutConstraint *c;
|
||||
|
||||
[self removeOurConstraints];
|
||||
if ([self->children count] == 0)
|
||||
return;
|
||||
padding = [self paddingAmount];
|
||||
|
||||
// first arrange the children vertically and make them the same width
|
||||
prev = nil;
|
||||
for (fc in self->children) {
|
||||
[fc setHidden:!uiControlVisible(fc.c)];
|
||||
if (!uiControlVisible(fc.c))
|
||||
continue;
|
||||
if (prev == nil) { // first view
|
||||
self->first = mkConstraint(self, NSLayoutAttributeTop,
|
||||
NSLayoutRelationEqual,
|
||||
[fc view], NSLayoutAttributeTop,
|
||||
1, 0,
|
||||
@"uiForm first vertical constraint");
|
||||
[self addConstraint:self->first];
|
||||
[self->first retain];
|
||||
prev = [fc view];
|
||||
prevlabel = fc;
|
||||
continue;
|
||||
}
|
||||
// not the first; link it
|
||||
c = mkConstraint(prev, NSLayoutAttributeBottom,
|
||||
NSLayoutRelationEqual,
|
||||
[fc view], NSLayoutAttributeTop,
|
||||
1, -padding,
|
||||
@"uiForm in-between vertical constraint");
|
||||
[self addConstraint:c];
|
||||
[self->inBetweens addObject:c];
|
||||
// and make the same width
|
||||
c = mkConstraint(prev, NSLayoutAttributeWidth,
|
||||
NSLayoutRelationEqual,
|
||||
[fc view], NSLayoutAttributeWidth,
|
||||
1, 0,
|
||||
@"uiForm control width constraint");
|
||||
[self addConstraint:c];
|
||||
[self->widths addObject:c];
|
||||
c = mkConstraint(prevlabel, NSLayoutAttributeWidth,
|
||||
NSLayoutRelationEqual,
|
||||
fc, NSLayoutAttributeWidth,
|
||||
1, 0,
|
||||
@"uiForm label lwidth constraint");
|
||||
[self addConstraint:c];
|
||||
[self->widths addObject:c];
|
||||
prev = [fc view];
|
||||
prevlabel = fc;
|
||||
}
|
||||
if (prev == nil) // all hidden; act as if nothing there
|
||||
return;
|
||||
self->last = mkConstraint(prev, NSLayoutAttributeBottom,
|
||||
NSLayoutRelationEqual,
|
||||
self, NSLayoutAttributeBottom,
|
||||
1, 0,
|
||||
@"uiForm last vertical constraint");
|
||||
[self addConstraint:self->last];
|
||||
[self->last retain];
|
||||
|
||||
// now arrange the controls horizontally
|
||||
for (fc in self->children) {
|
||||
if (!uiControlVisible(fc.c))
|
||||
continue;
|
||||
c = mkConstraint(self, NSLayoutAttributeLeading,
|
||||
NSLayoutRelationEqual,
|
||||
fc, NSLayoutAttributeLeading,
|
||||
1, 0,
|
||||
@"uiForm leading constraint");
|
||||
[self addConstraint:c];
|
||||
[self->leadings addObject:c];
|
||||
// coerce the control to be as wide as possible
|
||||
// see http://stackoverflow.com/questions/37710892/in-auto-layout-i-set-up-labels-that-shouldnt-grow-horizontally-and-controls-th
|
||||
c = mkConstraint(self, NSLayoutAttributeLeading,
|
||||
NSLayoutRelationEqual,
|
||||
[fc view], NSLayoutAttributeLeading,
|
||||
1, 0,
|
||||
@"uiForm leading constraint");
|
||||
[c setPriority:NSLayoutPriorityDefaultHigh];
|
||||
[self addConstraint:c];
|
||||
[self->leadings addObject:c];
|
||||
c = mkConstraint(fc, NSLayoutAttributeTrailing,
|
||||
NSLayoutRelationEqual,
|
||||
[fc view], NSLayoutAttributeLeading,
|
||||
1, -padding,
|
||||
@"uiForm middle constraint");
|
||||
[self addConstraint:c];
|
||||
[self->middles addObject:c];
|
||||
c = mkConstraint([fc view], NSLayoutAttributeTrailing,
|
||||
NSLayoutRelationEqual,
|
||||
self, NSLayoutAttributeTrailing,
|
||||
1, 0,
|
||||
@"uiForm trailing constraint");
|
||||
[self addConstraint:c];
|
||||
[self->trailings addObject:c];
|
||||
// TODO
|
||||
c = mkConstraint(fc, NSLayoutAttributeBottom,
|
||||
NSLayoutRelationLessThanOrEqual,
|
||||
self, NSLayoutAttributeBottom,
|
||||
1, 0,
|
||||
@"TODO");
|
||||
[self addConstraint:c];
|
||||
[self->trailings addObject:c];
|
||||
}
|
||||
|
||||
// and make all stretchy controls have the same height
|
||||
prev = nil;
|
||||
for (fc in self->children) {
|
||||
if (!uiControlVisible(fc.c))
|
||||
continue;
|
||||
if (!fc.stretchy)
|
||||
continue;
|
||||
if (prev == nil) {
|
||||
prev = [fc view];
|
||||
continue;
|
||||
}
|
||||
c = mkConstraint([fc view], NSLayoutAttributeHeight,
|
||||
NSLayoutRelationEqual,
|
||||
prev, NSLayoutAttributeHeight,
|
||||
1, 0,
|
||||
@"uiForm stretchy constraint");
|
||||
[self addConstraint:c];
|
||||
// TODO make a dedicated array for this
|
||||
[self->leadings addObject:c];
|
||||
}
|
||||
|
||||
// we don't arrange the labels vertically; that's done when we add the control since those constraints don't need to change (they just need to be at their baseline)
|
||||
}
|
||||
|
||||
- (void)append:(NSString *)label c:(uiControl *)c stretchy:(int)stretchy
|
||||
{
|
||||
formChild *fc;
|
||||
NSLayoutPriority priority;
|
||||
NSLayoutAttribute attribute;
|
||||
int oldnStretchy;
|
||||
|
||||
fc = [[formChild alloc] initWithLabel:newLabel(label)];
|
||||
fc.c = c;
|
||||
fc.stretchy = stretchy;
|
||||
fc.oldHorzHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(fc.c), NSLayoutConstraintOrientationHorizontal);
|
||||
fc.oldVertHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(fc.c), NSLayoutConstraintOrientationVertical);
|
||||
[fc setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||
[self addSubview:fc];
|
||||
|
||||
uiControlSetParent(fc.c, uiControl(self->f));
|
||||
uiDarwinControlSetSuperview(uiDarwinControl(fc.c), self);
|
||||
uiDarwinControlSyncEnableState(uiDarwinControl(fc.c), uiControlEnabledToUser(uiControl(self->f)));
|
||||
|
||||
// if a control is stretchy, it should not hug vertically
|
||||
// otherwise, it should *forcibly* hug
|
||||
if (fc.stretchy)
|
||||
priority = NSLayoutPriorityDefaultLow;
|
||||
else
|
||||
// LONGTERM will default high work?
|
||||
priority = NSLayoutPriorityRequired;
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(fc.c), priority, NSLayoutConstraintOrientationVertical);
|
||||
// make sure controls don't hug their horizontal direction so they fill the width of the view
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(fc.c), NSLayoutPriorityDefaultLow, NSLayoutConstraintOrientationHorizontal);
|
||||
|
||||
// and constrain the baselines to position the label vertically
|
||||
// if the view is a scroll view, align tops, not baselines
|
||||
// this is what Interface Builder does
|
||||
attribute = NSLayoutAttributeBaseline;
|
||||
if ([[fc view] isKindOfClass:[NSScrollView class]])
|
||||
attribute = NSLayoutAttributeTop;
|
||||
fc.baseline = mkConstraint(fc.label, attribute,
|
||||
NSLayoutRelationEqual,
|
||||
[fc view], attribute,
|
||||
1, 0,
|
||||
@"uiForm baseline constraint");
|
||||
[self addConstraint:fc.baseline];
|
||||
|
||||
oldnStretchy = [self nStretchy];
|
||||
[self->children addObject:fc];
|
||||
|
||||
[self establishOurConstraints];
|
||||
if (fc.stretchy)
|
||||
if (oldnStretchy == 0)
|
||||
uiDarwinNotifyEdgeHuggingChanged(uiDarwinControl(self->f));
|
||||
|
||||
[fc release]; // we don't need the initial reference now
|
||||
}
|
||||
|
||||
- (void)delete:(int)n
|
||||
{
|
||||
formChild *fc;
|
||||
int stretchy;
|
||||
|
||||
fc = (formChild *) [self->children objectAtIndex:n];
|
||||
stretchy = fc.stretchy;
|
||||
|
||||
uiControlSetParent(fc.c, NULL);
|
||||
uiDarwinControlSetSuperview(uiDarwinControl(fc.c), nil);
|
||||
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(fc.c), fc.oldHorzHuggingPri, NSLayoutConstraintOrientationHorizontal);
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(fc.c), fc.oldVertHuggingPri, NSLayoutConstraintOrientationVertical);
|
||||
|
||||
[fc onDestroy];
|
||||
[self->children removeObjectAtIndex:n];
|
||||
|
||||
[self establishOurConstraints];
|
||||
if (stretchy)
|
||||
if ([self nStretchy] == 0)
|
||||
uiDarwinNotifyEdgeHuggingChanged(uiDarwinControl(self->f));
|
||||
}
|
||||
|
||||
- (int)isPadded
|
||||
{
|
||||
return self->padded;
|
||||
}
|
||||
|
||||
- (void)setPadded:(int)p
|
||||
{
|
||||
CGFloat padding;
|
||||
NSLayoutConstraint *c;
|
||||
|
||||
self->padded = p;
|
||||
padding = [self paddingAmount];
|
||||
for (c in self->inBetweens)
|
||||
[c setConstant:-padding];
|
||||
for (c in self->middles)
|
||||
[c setConstant:-padding];
|
||||
}
|
||||
|
||||
- (BOOL)hugsTrailing
|
||||
{
|
||||
return YES; // always hug trailing
|
||||
}
|
||||
|
||||
- (BOOL)hugsBottom
|
||||
{
|
||||
// only hug if we have stretchy
|
||||
return [self nStretchy] != 0;
|
||||
}
|
||||
|
||||
- (int)nStretchy
|
||||
{
|
||||
formChild *fc;
|
||||
int n;
|
||||
|
||||
n = 0;
|
||||
for (fc in self->children) {
|
||||
if (!uiControlVisible(fc.c))
|
||||
continue;
|
||||
if (fc.stretchy)
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static void uiFormDestroy(uiControl *c)
|
||||
{
|
||||
uiForm *f = uiForm(c);
|
||||
|
||||
[f->view onDestroy];
|
||||
[f->view release];
|
||||
uiFreeControl(uiControl(f));
|
||||
}
|
||||
|
||||
uiDarwinControlDefaultHandle(uiForm, view)
|
||||
uiDarwinControlDefaultParent(uiForm, view)
|
||||
uiDarwinControlDefaultSetParent(uiForm, view)
|
||||
uiDarwinControlDefaultToplevel(uiForm, view)
|
||||
uiDarwinControlDefaultVisible(uiForm, view)
|
||||
uiDarwinControlDefaultShow(uiForm, view)
|
||||
uiDarwinControlDefaultHide(uiForm, view)
|
||||
uiDarwinControlDefaultEnabled(uiForm, view)
|
||||
uiDarwinControlDefaultEnable(uiForm, view)
|
||||
uiDarwinControlDefaultDisable(uiForm, view)
|
||||
|
||||
static void uiFormSyncEnableState(uiDarwinControl *c, int enabled)
|
||||
{
|
||||
uiForm *f = uiForm(c);
|
||||
|
||||
if (uiDarwinShouldStopSyncEnableState(uiDarwinControl(f), enabled))
|
||||
return;
|
||||
[f->view syncEnableStates:enabled];
|
||||
}
|
||||
|
||||
uiDarwinControlDefaultSetSuperview(uiForm, view)
|
||||
|
||||
static BOOL uiFormHugsTrailingEdge(uiDarwinControl *c)
|
||||
{
|
||||
uiForm *f = uiForm(c);
|
||||
|
||||
return [f->view hugsTrailing];
|
||||
}
|
||||
|
||||
static BOOL uiFormHugsBottom(uiDarwinControl *c)
|
||||
{
|
||||
uiForm *f = uiForm(c);
|
||||
|
||||
return [f->view hugsBottom];
|
||||
}
|
||||
|
||||
static void uiFormChildEdgeHuggingChanged(uiDarwinControl *c)
|
||||
{
|
||||
uiForm *f = uiForm(c);
|
||||
|
||||
[f->view establishOurConstraints];
|
||||
}
|
||||
|
||||
uiDarwinControlDefaultHuggingPriority(uiForm, view)
|
||||
uiDarwinControlDefaultSetHuggingPriority(uiForm, view)
|
||||
|
||||
static void uiFormChildVisibilityChanged(uiDarwinControl *c)
|
||||
{
|
||||
uiForm *f = uiForm(c);
|
||||
|
||||
[f->view establishOurConstraints];
|
||||
}
|
||||
|
||||
void uiFormAppend(uiForm *f, const char *label, uiControl *c, int stretchy)
|
||||
{
|
||||
// LONGTERM on other platforms
|
||||
// or at leat allow this and implicitly turn it into a spacer
|
||||
if (c == NULL)
|
||||
userbug("You cannot add NULL to a uiForm.");
|
||||
[f->view append:toNSString(label) c:c stretchy:stretchy];
|
||||
}
|
||||
|
||||
void uiFormDelete(uiForm *f, int n)
|
||||
{
|
||||
[f->view delete:n];
|
||||
}
|
||||
|
||||
int uiFormPadded(uiForm *f)
|
||||
{
|
||||
return [f->view isPadded];
|
||||
}
|
||||
|
||||
void uiFormSetPadded(uiForm *f, int padded)
|
||||
{
|
||||
[f->view setPadded:padded];
|
||||
}
|
||||
|
||||
uiForm *uiNewForm(void)
|
||||
{
|
||||
uiForm *f;
|
||||
|
||||
uiDarwinNewControl(uiForm, f);
|
||||
|
||||
f->view = [[formView alloc] initWithF:f];
|
||||
|
||||
return f;
|
||||
}
|
800
deps/libui/darwin/grid.m
vendored
800
deps/libui/darwin/grid.m
vendored
@ -1,800 +0,0 @@
|
||||
// 11 june 2016
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// TODO the assorted test doesn't work right at all
|
||||
|
||||
@interface gridChild : NSView
|
||||
@property uiControl *c;
|
||||
@property int left;
|
||||
@property int top;
|
||||
@property int xspan;
|
||||
@property int yspan;
|
||||
@property int hexpand;
|
||||
@property uiAlign halign;
|
||||
@property int vexpand;
|
||||
@property uiAlign valign;
|
||||
|
||||
@property (strong) NSLayoutConstraint *leadingc;
|
||||
@property (strong) NSLayoutConstraint *topc;
|
||||
@property (strong) NSLayoutConstraint *trailingc;
|
||||
@property (strong) NSLayoutConstraint *bottomc;
|
||||
@property (strong) NSLayoutConstraint *xcenterc;
|
||||
@property (strong) NSLayoutConstraint *ycenterc;
|
||||
|
||||
@property NSLayoutPriority oldHorzHuggingPri;
|
||||
@property NSLayoutPriority oldVertHuggingPri;
|
||||
- (void)setC:(uiControl *)c grid:(uiGrid *)g;
|
||||
- (void)onDestroy;
|
||||
- (NSView *)view;
|
||||
@end
|
||||
|
||||
@interface gridView : NSView {
|
||||
uiGrid *g;
|
||||
NSMutableArray *children;
|
||||
int padded;
|
||||
|
||||
NSMutableArray *edges;
|
||||
NSMutableArray *inBetweens;
|
||||
|
||||
NSMutableArray *emptyCellViews;
|
||||
}
|
||||
- (id)initWithG:(uiGrid *)gg;
|
||||
- (void)onDestroy;
|
||||
- (void)removeOurConstraints;
|
||||
- (void)syncEnableStates:(int)enabled;
|
||||
- (CGFloat)paddingAmount;
|
||||
- (void)establishOurConstraints;
|
||||
- (void)append:(gridChild *)gc;
|
||||
- (void)insert:(gridChild *)gc after:(uiControl *)c at:(uiAt)at;
|
||||
- (int)isPadded;
|
||||
- (void)setPadded:(int)p;
|
||||
- (BOOL)hugsTrailing;
|
||||
- (BOOL)hugsBottom;
|
||||
- (int)nhexpand;
|
||||
- (int)nvexpand;
|
||||
@end
|
||||
|
||||
struct uiGrid {
|
||||
uiDarwinControl c;
|
||||
gridView *view;
|
||||
};
|
||||
|
||||
@implementation gridChild
|
||||
|
||||
- (void)setC:(uiControl *)c grid:(uiGrid *)g
|
||||
{
|
||||
self.c = c;
|
||||
self.oldHorzHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(self.c), NSLayoutConstraintOrientationHorizontal);
|
||||
self.oldVertHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(self.c), NSLayoutConstraintOrientationVertical);
|
||||
|
||||
uiControlSetParent(self.c, uiControl(g));
|
||||
uiDarwinControlSetSuperview(uiDarwinControl(self.c), self);
|
||||
uiDarwinControlSyncEnableState(uiDarwinControl(self.c), uiControlEnabledToUser(uiControl(g)));
|
||||
|
||||
if (self.halign == uiAlignStart || self.halign == uiAlignFill) {
|
||||
self.leadingc = mkConstraint(self, NSLayoutAttributeLeading,
|
||||
NSLayoutRelationEqual,
|
||||
[self view], NSLayoutAttributeLeading,
|
||||
1, 0,
|
||||
@"uiGrid child horizontal alignment start constraint");
|
||||
[self addConstraint:self.leadingc];
|
||||
}
|
||||
if (self.halign == uiAlignCenter) {
|
||||
self.xcenterc = mkConstraint(self, NSLayoutAttributeCenterX,
|
||||
NSLayoutRelationEqual,
|
||||
[self view], NSLayoutAttributeCenterX,
|
||||
1, 0,
|
||||
@"uiGrid child horizontal alignment center constraint");
|
||||
[self addConstraint:self.xcenterc];
|
||||
}
|
||||
if (self.halign == uiAlignEnd || self.halign == uiAlignFill) {
|
||||
self.trailingc = mkConstraint(self, NSLayoutAttributeTrailing,
|
||||
NSLayoutRelationEqual,
|
||||
[self view], NSLayoutAttributeTrailing,
|
||||
1, 0,
|
||||
@"uiGrid child horizontal alignment end constraint");
|
||||
[self addConstraint:self.trailingc];
|
||||
}
|
||||
|
||||
if (self.valign == uiAlignStart || self.valign == uiAlignFill) {
|
||||
self.topc = mkConstraint(self, NSLayoutAttributeTop,
|
||||
NSLayoutRelationEqual,
|
||||
[self view], NSLayoutAttributeTop,
|
||||
1, 0,
|
||||
@"uiGrid child vertical alignment start constraint");
|
||||
[self addConstraint:self.topc];
|
||||
}
|
||||
if (self.valign == uiAlignCenter) {
|
||||
self.ycenterc = mkConstraint(self, NSLayoutAttributeCenterY,
|
||||
NSLayoutRelationEqual,
|
||||
[self view], NSLayoutAttributeCenterY,
|
||||
1, 0,
|
||||
@"uiGrid child vertical alignment center constraint");
|
||||
[self addConstraint:self.ycenterc];
|
||||
}
|
||||
if (self.valign == uiAlignEnd || self.valign == uiAlignFill) {
|
||||
self.bottomc = mkConstraint(self, NSLayoutAttributeBottom,
|
||||
NSLayoutRelationEqual,
|
||||
[self view], NSLayoutAttributeBottom,
|
||||
1, 0,
|
||||
@"uiGrid child vertical alignment end constraint");
|
||||
[self addConstraint:self.bottomc];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onDestroy
|
||||
{
|
||||
if (self.leadingc != nil) {
|
||||
[self removeConstraint:self.leadingc];
|
||||
self.leadingc = nil;
|
||||
}
|
||||
if (self.topc != nil) {
|
||||
[self removeConstraint:self.topc];
|
||||
self.topc = nil;
|
||||
}
|
||||
if (self.trailingc != nil) {
|
||||
[self removeConstraint:self.trailingc];
|
||||
self.trailingc = nil;
|
||||
}
|
||||
if (self.bottomc != nil) {
|
||||
[self removeConstraint:self.bottomc];
|
||||
self.bottomc = nil;
|
||||
}
|
||||
if (self.xcenterc != nil) {
|
||||
[self removeConstraint:self.xcenterc];
|
||||
self.xcenterc = nil;
|
||||
}
|
||||
if (self.ycenterc != nil) {
|
||||
[self removeConstraint:self.ycenterc];
|
||||
self.ycenterc = nil;
|
||||
}
|
||||
|
||||
uiControlSetParent(self.c, NULL);
|
||||
uiDarwinControlSetSuperview(uiDarwinControl(self.c), nil);
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(self.c), self.oldHorzHuggingPri, NSLayoutConstraintOrientationHorizontal);
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(self.c), self.oldVertHuggingPri, NSLayoutConstraintOrientationVertical);
|
||||
}
|
||||
|
||||
- (NSView *)view
|
||||
{
|
||||
return (NSView *) uiControlHandle(self.c);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation gridView
|
||||
|
||||
- (id)initWithG:(uiGrid *)gg
|
||||
{
|
||||
self = [super initWithFrame:NSZeroRect];
|
||||
if (self != nil) {
|
||||
self->g = gg;
|
||||
self->padded = 0;
|
||||
self->children = [NSMutableArray new];
|
||||
|
||||
self->edges = [NSMutableArray new];
|
||||
self->inBetweens = [NSMutableArray new];
|
||||
|
||||
self->emptyCellViews = [NSMutableArray new];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)onDestroy
|
||||
{
|
||||
gridChild *gc;
|
||||
|
||||
[self removeOurConstraints];
|
||||
[self->edges release];
|
||||
[self->inBetweens release];
|
||||
|
||||
[self->emptyCellViews release];
|
||||
|
||||
for (gc in self->children) {
|
||||
[gc onDestroy];
|
||||
uiControlDestroy(gc.c);
|
||||
[gc removeFromSuperview];
|
||||
}
|
||||
[self->children release];
|
||||
}
|
||||
|
||||
- (void)removeOurConstraints
|
||||
{
|
||||
NSView *v;
|
||||
|
||||
if ([self->edges count] != 0) {
|
||||
[self removeConstraints:self->edges];
|
||||
[self->edges removeAllObjects];
|
||||
}
|
||||
if ([self->inBetweens count] != 0) {
|
||||
[self removeConstraints:self->inBetweens];
|
||||
[self->inBetweens removeAllObjects];
|
||||
}
|
||||
|
||||
for (v in self->emptyCellViews)
|
||||
[v removeFromSuperview];
|
||||
[self->emptyCellViews removeAllObjects];
|
||||
}
|
||||
|
||||
- (void)syncEnableStates:(int)enabled
|
||||
{
|
||||
gridChild *gc;
|
||||
|
||||
for (gc in self->children)
|
||||
uiDarwinControlSyncEnableState(uiDarwinControl(gc.c), enabled);
|
||||
}
|
||||
|
||||
- (CGFloat)paddingAmount
|
||||
{
|
||||
if (!self->padded)
|
||||
return 0.0;
|
||||
return uiDarwinPaddingAmount(NULL);
|
||||
}
|
||||
|
||||
// LONGTERM stop early if all controls are hidden
|
||||
- (void)establishOurConstraints
|
||||
{
|
||||
gridChild *gc;
|
||||
CGFloat padding;
|
||||
int xmin, ymin;
|
||||
int xmax, ymax;
|
||||
int xcount, ycount;
|
||||
BOOL first;
|
||||
int **gg;
|
||||
NSView ***gv;
|
||||
BOOL **gspan;
|
||||
int x, y;
|
||||
int i;
|
||||
NSLayoutConstraint *c;
|
||||
int firstx, firsty;
|
||||
BOOL *hexpand, *vexpand;
|
||||
BOOL doit;
|
||||
BOOL onlyEmptyAndSpanning;
|
||||
|
||||
[self removeOurConstraints];
|
||||
if ([self->children count] == 0)
|
||||
return;
|
||||
padding = [self paddingAmount];
|
||||
|
||||
// first, figure out the minimum and maximum row and column numbers
|
||||
// ignore hidden controls
|
||||
first = YES;
|
||||
for (gc in self->children) {
|
||||
// this bit is important: it ensures row ymin and column xmin have at least one cell to draw, so the onlyEmptyAndSpanning logic below will never run on those rows
|
||||
if (!uiControlVisible(gc.c))
|
||||
continue;
|
||||
if (first) {
|
||||
xmin = gc.left;
|
||||
ymin = gc.top;
|
||||
xmax = gc.left + gc.xspan;
|
||||
ymax = gc.top + gc.yspan;
|
||||
first = NO;
|
||||
continue;
|
||||
}
|
||||
if (xmin > gc.left)
|
||||
xmin = gc.left;
|
||||
if (ymin > gc.top)
|
||||
ymin = gc.top;
|
||||
if (xmax < (gc.left + gc.xspan))
|
||||
xmax = gc.left + gc.xspan;
|
||||
if (ymax < (gc.top + gc.yspan))
|
||||
ymax = gc.top + gc.yspan;
|
||||
}
|
||||
if (first != NO) // the entire grid is hidden; do nothing
|
||||
return;
|
||||
xcount = xmax - xmin;
|
||||
ycount = ymax - ymin;
|
||||
|
||||
// now build a topological map of the grid gg[y][x]
|
||||
// also figure out which cells contain spanned views so they can be ignored later
|
||||
// treat hidden controls by keeping the indices -1
|
||||
gg = (int **) uiAlloc(ycount * sizeof (int *), "int[][]");
|
||||
gspan = (BOOL **) uiAlloc(ycount * sizeof (BOOL *), "BOOL[][]");
|
||||
for (y = 0; y < ycount; y++) {
|
||||
gg[y] = (int *) uiAlloc(xcount * sizeof (int), "int[]");
|
||||
gspan[y] = (BOOL *) uiAlloc(xcount * sizeof (BOOL), "BOOL[]");
|
||||
for (x = 0; x < xcount; x++)
|
||||
gg[y][x] = -1; // empty
|
||||
}
|
||||
for (i = 0; i < [self->children count]; i++) {
|
||||
gc = (gridChild *) [self->children objectAtIndex:i];
|
||||
if (!uiControlVisible(gc.c))
|
||||
continue;
|
||||
for (y = gc.top; y < gc.top + gc.yspan; y++)
|
||||
for (x = gc.left; x < gc.left + gc.xspan; x++) {
|
||||
gg[y - ymin][x - xmin] = i;
|
||||
if (x != gc.left || y != gc.top)
|
||||
gspan[y - ymin][x - xmin] = YES;
|
||||
}
|
||||
}
|
||||
|
||||
// if a row or column only contains emptys and spanning cells of a opposite-direction spannings, remove it by duplicating the previous row or column
|
||||
for (y = 0; y < ycount; y++) {
|
||||
onlyEmptyAndSpanning = YES;
|
||||
for (x = 0; x < xcount; x++)
|
||||
if (gg[y][x] != -1) {
|
||||
gc = (gridChild *) [self->children objectAtIndex:gg[y][x]];
|
||||
if (gc.yspan == 1 || gc.top - ymin == y) {
|
||||
onlyEmptyAndSpanning = NO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (onlyEmptyAndSpanning)
|
||||
for (x = 0; x < xcount; x++) {
|
||||
gg[y][x] = gg[y - 1][x];
|
||||
gspan[y][x] = YES;
|
||||
}
|
||||
}
|
||||
for (x = 0; x < xcount; x++) {
|
||||
onlyEmptyAndSpanning = YES;
|
||||
for (y = 0; y < ycount; y++)
|
||||
if (gg[y][x] != -1) {
|
||||
gc = (gridChild *) [self->children objectAtIndex:gg[y][x]];
|
||||
if (gc.xspan == 1 || gc.left - xmin == x) {
|
||||
onlyEmptyAndSpanning = NO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (onlyEmptyAndSpanning)
|
||||
for (y = 0; y < ycount; y++) {
|
||||
gg[y][x] = gg[y][x - 1];
|
||||
gspan[y][x] = YES;
|
||||
}
|
||||
}
|
||||
|
||||
// now build a topological map of the grid's views gv[y][x]
|
||||
// for any empty cell, create a dummy view
|
||||
gv = (NSView ***) uiAlloc(ycount * sizeof (NSView **), "NSView *[][]");
|
||||
for (y = 0; y < ycount; y++) {
|
||||
gv[y] = (NSView **) uiAlloc(xcount * sizeof (NSView *), "NSView *[]");
|
||||
for (x = 0; x < xcount; x++)
|
||||
if (gg[y][x] == -1) {
|
||||
gv[y][x] = [[NSView alloc] initWithFrame:NSZeroRect];
|
||||
[gv[y][x] setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||
[self addSubview:gv[y][x]];
|
||||
[self->emptyCellViews addObject:gv[y][x]];
|
||||
} else {
|
||||
gc = (gridChild *) [self->children objectAtIndex:gg[y][x]];
|
||||
gv[y][x] = gc;
|
||||
}
|
||||
}
|
||||
|
||||
// now figure out which rows and columns really expand
|
||||
hexpand = (BOOL *) uiAlloc(xcount * sizeof (BOOL), "BOOL[]");
|
||||
vexpand = (BOOL *) uiAlloc(ycount * sizeof (BOOL), "BOOL[]");
|
||||
// first, which don't span
|
||||
for (gc in self->children) {
|
||||
if (!uiControlVisible(gc.c))
|
||||
continue;
|
||||
if (gc.hexpand && gc.xspan == 1)
|
||||
hexpand[gc.left - xmin] = YES;
|
||||
if (gc.vexpand && gc.yspan == 1)
|
||||
vexpand[gc.top - ymin] = YES;
|
||||
}
|
||||
// second, which do span
|
||||
// the way we handle this is simple: if none of the spanned rows/columns expand, make all rows/columns expand
|
||||
for (gc in self->children) {
|
||||
if (!uiControlVisible(gc.c))
|
||||
continue;
|
||||
if (gc.hexpand && gc.xspan != 1) {
|
||||
doit = YES;
|
||||
for (x = gc.left; x < gc.left + gc.xspan; x++)
|
||||
if (hexpand[x - xmin]) {
|
||||
doit = NO;
|
||||
break;
|
||||
}
|
||||
if (doit)
|
||||
for (x = gc.left; x < gc.left + gc.xspan; x++)
|
||||
hexpand[x - xmin] = YES;
|
||||
}
|
||||
if (gc.vexpand && gc.yspan != 1) {
|
||||
doit = YES;
|
||||
for (y = gc.top; y < gc.top + gc.yspan; y++)
|
||||
if (vexpand[y - ymin]) {
|
||||
doit = NO;
|
||||
break;
|
||||
}
|
||||
if (doit)
|
||||
for (y = gc.top; y < gc.top + gc.yspan; y++)
|
||||
vexpand[y - ymin] = YES;
|
||||
}
|
||||
}
|
||||
|
||||
// now establish all the edge constraints
|
||||
// leading and trailing edges
|
||||
for (y = 0; y < ycount; y++) {
|
||||
c = mkConstraint(self, NSLayoutAttributeLeading,
|
||||
NSLayoutRelationEqual,
|
||||
gv[y][0], NSLayoutAttributeLeading,
|
||||
1, 0,
|
||||
@"uiGrid leading edge constraint");
|
||||
[self addConstraint:c];
|
||||
[self->edges addObject:c];
|
||||
c = mkConstraint(self, NSLayoutAttributeTrailing,
|
||||
NSLayoutRelationEqual,
|
||||
gv[y][xcount - 1], NSLayoutAttributeTrailing,
|
||||
1, 0,
|
||||
@"uiGrid trailing edge constraint");
|
||||
[self addConstraint:c];
|
||||
[self->edges addObject:c];
|
||||
}
|
||||
// top and bottom edges
|
||||
for (x = 0; x < xcount; x++) {
|
||||
c = mkConstraint(self, NSLayoutAttributeTop,
|
||||
NSLayoutRelationEqual,
|
||||
gv[0][x], NSLayoutAttributeTop,
|
||||
1, 0,
|
||||
@"uiGrid top edge constraint");
|
||||
[self addConstraint:c];
|
||||
[self->edges addObject:c];
|
||||
c = mkConstraint(self, NSLayoutAttributeBottom,
|
||||
NSLayoutRelationEqual,
|
||||
gv[ycount - 1][x], NSLayoutAttributeBottom,
|
||||
1, 0,
|
||||
@"uiGrid bottom edge constraint");
|
||||
[self addConstraint:c];
|
||||
[self->edges addObject:c];
|
||||
}
|
||||
|
||||
// now align leading and top edges
|
||||
// do NOT align spanning cells!
|
||||
for (x = 0; x < xcount; x++) {
|
||||
for (y = 0; y < ycount; y++)
|
||||
if (!gspan[y][x])
|
||||
break;
|
||||
firsty = y;
|
||||
for (y++; y < ycount; y++) {
|
||||
if (gspan[y][x])
|
||||
continue;
|
||||
c = mkConstraint(gv[firsty][x], NSLayoutAttributeLeading,
|
||||
NSLayoutRelationEqual,
|
||||
gv[y][x], NSLayoutAttributeLeading,
|
||||
1, 0,
|
||||
@"uiGrid column leading constraint");
|
||||
[self addConstraint:c];
|
||||
[self->edges addObject:c];
|
||||
}
|
||||
}
|
||||
for (y = 0; y < ycount; y++) {
|
||||
for (x = 0; x < xcount; x++)
|
||||
if (!gspan[y][x])
|
||||
break;
|
||||
firstx = x;
|
||||
for (x++; x < xcount; x++) {
|
||||
if (gspan[y][x])
|
||||
continue;
|
||||
c = mkConstraint(gv[y][firstx], NSLayoutAttributeTop,
|
||||
NSLayoutRelationEqual,
|
||||
gv[y][x], NSLayoutAttributeTop,
|
||||
1, 0,
|
||||
@"uiGrid row top constraint");
|
||||
[self addConstraint:c];
|
||||
[self->edges addObject:c];
|
||||
}
|
||||
}
|
||||
|
||||
// now string adjacent views together
|
||||
for (y = 0; y < ycount; y++)
|
||||
for (x = 1; x < xcount; x++)
|
||||
if (gv[y][x - 1] != gv[y][x]) {
|
||||
c = mkConstraint(gv[y][x - 1], NSLayoutAttributeTrailing,
|
||||
NSLayoutRelationEqual,
|
||||
gv[y][x], NSLayoutAttributeLeading,
|
||||
1, -padding,
|
||||
@"uiGrid internal horizontal constraint");
|
||||
[self addConstraint:c];
|
||||
[self->inBetweens addObject:c];
|
||||
}
|
||||
for (x = 0; x < xcount; x++)
|
||||
for (y = 1; y < ycount; y++)
|
||||
if (gv[y - 1][x] != gv[y][x]) {
|
||||
c = mkConstraint(gv[y - 1][x], NSLayoutAttributeBottom,
|
||||
NSLayoutRelationEqual,
|
||||
gv[y][x], NSLayoutAttributeTop,
|
||||
1, -padding,
|
||||
@"uiGrid internal vertical constraint");
|
||||
[self addConstraint:c];
|
||||
[self->inBetweens addObject:c];
|
||||
}
|
||||
|
||||
// now set priorities for all widgets that expand or not
|
||||
// if a cell is in an expanding row, OR If it spans, then it must be willing to stretch
|
||||
// otherwise, it tries not to
|
||||
// note we don't use NSLayoutPriorityRequired as that will cause things to squish when they shouldn't
|
||||
for (gc in self->children) {
|
||||
NSLayoutPriority priority;
|
||||
|
||||
if (!uiControlVisible(gc.c))
|
||||
continue;
|
||||
if (hexpand[gc.left - xmin] || gc.xspan != 1)
|
||||
priority = NSLayoutPriorityDefaultLow;
|
||||
else
|
||||
priority = NSLayoutPriorityDefaultHigh;
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(gc.c), priority, NSLayoutConstraintOrientationHorizontal);
|
||||
// same for vertical direction
|
||||
if (vexpand[gc.top - ymin] || gc.yspan != 1)
|
||||
priority = NSLayoutPriorityDefaultLow;
|
||||
else
|
||||
priority = NSLayoutPriorityDefaultHigh;
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(gc.c), priority, NSLayoutConstraintOrientationVertical);
|
||||
}
|
||||
|
||||
// TODO make all expanding rows/columns the same height/width
|
||||
|
||||
// and finally clean up
|
||||
uiFree(hexpand);
|
||||
uiFree(vexpand);
|
||||
for (y = 0; y < ycount; y++) {
|
||||
uiFree(gg[y]);
|
||||
uiFree(gv[y]);
|
||||
uiFree(gspan[y]);
|
||||
}
|
||||
uiFree(gg);
|
||||
uiFree(gv);
|
||||
uiFree(gspan);
|
||||
}
|
||||
|
||||
- (void)append:(gridChild *)gc
|
||||
{
|
||||
BOOL update;
|
||||
int oldnh, oldnv;
|
||||
|
||||
[gc setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||
[self addSubview:gc];
|
||||
|
||||
// no need to set priority here; that's done in establishOurConstraints
|
||||
|
||||
oldnh = [self nhexpand];
|
||||
oldnv = [self nvexpand];
|
||||
[self->children addObject:gc];
|
||||
|
||||
[self establishOurConstraints];
|
||||
update = NO;
|
||||
if (gc.hexpand)
|
||||
if (oldnh == 0)
|
||||
update = YES;
|
||||
if (gc.vexpand)
|
||||
if (oldnv == 0)
|
||||
update = YES;
|
||||
if (update)
|
||||
uiDarwinNotifyEdgeHuggingChanged(uiDarwinControl(self->g));
|
||||
|
||||
[gc release]; // we don't need the initial reference now
|
||||
}
|
||||
|
||||
- (void)insert:(gridChild *)gc after:(uiControl *)c at:(uiAt)at
|
||||
{
|
||||
gridChild *other;
|
||||
BOOL found;
|
||||
|
||||
found = NO;
|
||||
for (other in self->children)
|
||||
if (other.c == c) {
|
||||
found = YES;
|
||||
break;
|
||||
}
|
||||
if (!found)
|
||||
userbug("Existing control %p is not in grid %p; you cannot add other controls next to it", c, self->g);
|
||||
|
||||
switch (at) {
|
||||
case uiAtLeading:
|
||||
gc.left = other.left - gc.xspan;
|
||||
gc.top = other.top;
|
||||
break;
|
||||
case uiAtTop:
|
||||
gc.left = other.left;
|
||||
gc.top = other.top - gc.yspan;
|
||||
break;
|
||||
case uiAtTrailing:
|
||||
gc.left = other.left + other.xspan;
|
||||
gc.top = other.top;
|
||||
break;
|
||||
case uiAtBottom:
|
||||
gc.left = other.left;
|
||||
gc.top = other.top + other.yspan;
|
||||
break;
|
||||
// TODO add error checks to ALL enums
|
||||
}
|
||||
|
||||
[self append:gc];
|
||||
}
|
||||
|
||||
- (int)isPadded
|
||||
{
|
||||
return self->padded;
|
||||
}
|
||||
|
||||
- (void)setPadded:(int)p
|
||||
{
|
||||
CGFloat padding;
|
||||
NSLayoutConstraint *c;
|
||||
|
||||
#if 0 /* TODO */
|
||||
dispatch_after(
|
||||
dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC),
|
||||
dispatch_get_main_queue(),
|
||||
^{ [[self window] visualizeConstraints:[self constraints]]; }
|
||||
);
|
||||
#endif
|
||||
self->padded = p;
|
||||
padding = [self paddingAmount];
|
||||
for (c in self->inBetweens)
|
||||
switch ([c firstAttribute]) {
|
||||
case NSLayoutAttributeLeading:
|
||||
case NSLayoutAttributeTop:
|
||||
[c setConstant:padding];
|
||||
break;
|
||||
case NSLayoutAttributeTrailing:
|
||||
case NSLayoutAttributeBottom:
|
||||
[c setConstant:-padding];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)hugsTrailing
|
||||
{
|
||||
// only hug if we have horizontally expanding
|
||||
return [self nhexpand] != 0;
|
||||
}
|
||||
|
||||
- (BOOL)hugsBottom
|
||||
{
|
||||
// only hug if we have vertically expanding
|
||||
return [self nvexpand] != 0;
|
||||
}
|
||||
|
||||
- (int)nhexpand
|
||||
{
|
||||
gridChild *gc;
|
||||
int n;
|
||||
|
||||
n = 0;
|
||||
for (gc in self->children) {
|
||||
if (!uiControlVisible(gc.c))
|
||||
continue;
|
||||
if (gc.hexpand)
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
- (int)nvexpand
|
||||
{
|
||||
gridChild *gc;
|
||||
int n;
|
||||
|
||||
n = 0;
|
||||
for (gc in self->children) {
|
||||
if (!uiControlVisible(gc.c))
|
||||
continue;
|
||||
if (gc.vexpand)
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static void uiGridDestroy(uiControl *c)
|
||||
{
|
||||
uiGrid *g = uiGrid(c);
|
||||
|
||||
[g->view onDestroy];
|
||||
[g->view release];
|
||||
uiFreeControl(uiControl(g));
|
||||
}
|
||||
|
||||
uiDarwinControlDefaultHandle(uiGrid, view)
|
||||
uiDarwinControlDefaultParent(uiGrid, view)
|
||||
uiDarwinControlDefaultSetParent(uiGrid, view)
|
||||
uiDarwinControlDefaultToplevel(uiGrid, view)
|
||||
uiDarwinControlDefaultVisible(uiGrid, view)
|
||||
uiDarwinControlDefaultShow(uiGrid, view)
|
||||
uiDarwinControlDefaultHide(uiGrid, view)
|
||||
uiDarwinControlDefaultEnabled(uiGrid, view)
|
||||
uiDarwinControlDefaultEnable(uiGrid, view)
|
||||
uiDarwinControlDefaultDisable(uiGrid, view)
|
||||
|
||||
static void uiGridSyncEnableState(uiDarwinControl *c, int enabled)
|
||||
{
|
||||
uiGrid *g = uiGrid(c);
|
||||
|
||||
if (uiDarwinShouldStopSyncEnableState(uiDarwinControl(g), enabled))
|
||||
return;
|
||||
[g->view syncEnableStates:enabled];
|
||||
}
|
||||
|
||||
uiDarwinControlDefaultSetSuperview(uiGrid, view)
|
||||
|
||||
static BOOL uiGridHugsTrailingEdge(uiDarwinControl *c)
|
||||
{
|
||||
uiGrid *g = uiGrid(c);
|
||||
|
||||
return [g->view hugsTrailing];
|
||||
}
|
||||
|
||||
static BOOL uiGridHugsBottom(uiDarwinControl *c)
|
||||
{
|
||||
uiGrid *g = uiGrid(c);
|
||||
|
||||
return [g->view hugsBottom];
|
||||
}
|
||||
|
||||
static void uiGridChildEdgeHuggingChanged(uiDarwinControl *c)
|
||||
{
|
||||
uiGrid *g = uiGrid(c);
|
||||
|
||||
[g->view establishOurConstraints];
|
||||
}
|
||||
|
||||
uiDarwinControlDefaultHuggingPriority(uiGrid, view)
|
||||
uiDarwinControlDefaultSetHuggingPriority(uiGrid, view)
|
||||
|
||||
static void uiGridChildVisibilityChanged(uiDarwinControl *c)
|
||||
{
|
||||
uiGrid *g = uiGrid(c);
|
||||
|
||||
[g->view establishOurConstraints];
|
||||
}
|
||||
|
||||
static gridChild *toChild(uiControl *c, int xspan, int yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign, uiGrid *g)
|
||||
{
|
||||
gridChild *gc;
|
||||
|
||||
if (xspan < 0)
|
||||
userbug("You cannot have a negative xspan in a uiGrid cell.");
|
||||
if (yspan < 0)
|
||||
userbug("You cannot have a negative yspan in a uiGrid cell.");
|
||||
gc = [gridChild new];
|
||||
gc.xspan = xspan;
|
||||
gc.yspan = yspan;
|
||||
gc.hexpand = hexpand;
|
||||
gc.halign = halign;
|
||||
gc.vexpand = vexpand;
|
||||
gc.valign = valign;
|
||||
[gc setC:c grid:g];
|
||||
return gc;
|
||||
}
|
||||
|
||||
void uiGridAppend(uiGrid *g, uiControl *c, int left, int top, int xspan, int yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign)
|
||||
{
|
||||
gridChild *gc;
|
||||
|
||||
// LONGTERM on other platforms
|
||||
// or at leat allow this and implicitly turn it into a spacer
|
||||
if (c == NULL)
|
||||
userbug("You cannot add NULL to a uiGrid.");
|
||||
gc = toChild(c, xspan, yspan, hexpand, halign, vexpand, valign, g);
|
||||
gc.left = left;
|
||||
gc.top = top;
|
||||
[g->view append:gc];
|
||||
}
|
||||
|
||||
void uiGridInsertAt(uiGrid *g, uiControl *c, uiControl *existing, uiAt at, int xspan, int yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign)
|
||||
{
|
||||
gridChild *gc;
|
||||
|
||||
gc = toChild(c, xspan, yspan, hexpand, halign, vexpand, valign, g);
|
||||
[g->view insert:gc after:existing at:at];
|
||||
}
|
||||
|
||||
int uiGridPadded(uiGrid *g)
|
||||
{
|
||||
return [g->view isPadded];
|
||||
}
|
||||
|
||||
void uiGridSetPadded(uiGrid *g, int padded)
|
||||
{
|
||||
[g->view setPadded:padded];
|
||||
}
|
||||
|
||||
uiGrid *uiNewGrid(void)
|
||||
{
|
||||
uiGrid *g;
|
||||
|
||||
uiDarwinNewControl(uiGrid, g);
|
||||
|
||||
g->view = [[gridView alloc] initWithG:g];
|
||||
|
||||
return g;
|
||||
}
|
194
deps/libui/darwin/group.m
vendored
194
deps/libui/darwin/group.m
vendored
@ -1,194 +0,0 @@
|
||||
// 14 august 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
struct uiGroup {
|
||||
uiDarwinControl c;
|
||||
NSBox *box;
|
||||
uiControl *child;
|
||||
NSLayoutPriority oldHorzHuggingPri;
|
||||
NSLayoutPriority oldVertHuggingPri;
|
||||
int margined;
|
||||
struct singleChildConstraints constraints;
|
||||
NSLayoutPriority horzHuggingPri;
|
||||
NSLayoutPriority vertHuggingPri;
|
||||
};
|
||||
|
||||
static void removeConstraints(uiGroup *g)
|
||||
{
|
||||
// set to contentView instead of to the box itself, otherwise we get clipping underneath the label
|
||||
singleChildConstraintsRemove(&(g->constraints), [g->box contentView]);
|
||||
}
|
||||
|
||||
static void uiGroupDestroy(uiControl *c)
|
||||
{
|
||||
uiGroup *g = uiGroup(c);
|
||||
|
||||
removeConstraints(g);
|
||||
if (g->child != NULL) {
|
||||
uiControlSetParent(g->child, NULL);
|
||||
uiDarwinControlSetSuperview(uiDarwinControl(g->child), nil);
|
||||
uiControlDestroy(g->child);
|
||||
}
|
||||
[g->box release];
|
||||
uiFreeControl(uiControl(g));
|
||||
}
|
||||
|
||||
uiDarwinControlDefaultHandle(uiGroup, box)
|
||||
uiDarwinControlDefaultParent(uiGroup, box)
|
||||
uiDarwinControlDefaultSetParent(uiGroup, box)
|
||||
uiDarwinControlDefaultToplevel(uiGroup, box)
|
||||
uiDarwinControlDefaultVisible(uiGroup, box)
|
||||
uiDarwinControlDefaultShow(uiGroup, box)
|
||||
uiDarwinControlDefaultHide(uiGroup, box)
|
||||
uiDarwinControlDefaultEnabled(uiGroup, box)
|
||||
uiDarwinControlDefaultEnable(uiGroup, box)
|
||||
uiDarwinControlDefaultDisable(uiGroup, box)
|
||||
|
||||
static void uiGroupSyncEnableState(uiDarwinControl *c, int enabled)
|
||||
{
|
||||
uiGroup *g = uiGroup(c);
|
||||
|
||||
if (uiDarwinShouldStopSyncEnableState(uiDarwinControl(g), enabled))
|
||||
return;
|
||||
if (g->child != NULL)
|
||||
uiDarwinControlSyncEnableState(uiDarwinControl(g->child), enabled);
|
||||
}
|
||||
|
||||
uiDarwinControlDefaultSetSuperview(uiGroup, box)
|
||||
|
||||
static void groupRelayout(uiGroup *g)
|
||||
{
|
||||
NSView *childView;
|
||||
|
||||
removeConstraints(g);
|
||||
if (g->child == NULL)
|
||||
return;
|
||||
childView = (NSView *) uiControlHandle(g->child);
|
||||
singleChildConstraintsEstablish(&(g->constraints),
|
||||
[g->box contentView], childView,
|
||||
uiDarwinControlHugsTrailingEdge(uiDarwinControl(g->child)),
|
||||
uiDarwinControlHugsBottom(uiDarwinControl(g->child)),
|
||||
g->margined,
|
||||
@"uiGroup");
|
||||
// needed for some very rare drawing errors...
|
||||
jiggleViewLayout(g->box);
|
||||
}
|
||||
|
||||
// TODO rename these since I'm starting to get confused by what they mean by hugging
|
||||
BOOL uiGroupHugsTrailingEdge(uiDarwinControl *c)
|
||||
{
|
||||
uiGroup *g = uiGroup(c);
|
||||
|
||||
// TODO make a function?
|
||||
return g->horzHuggingPri < NSLayoutPriorityWindowSizeStayPut;
|
||||
}
|
||||
|
||||
BOOL uiGroupHugsBottom(uiDarwinControl *c)
|
||||
{
|
||||
uiGroup *g = uiGroup(c);
|
||||
|
||||
return g->vertHuggingPri < NSLayoutPriorityWindowSizeStayPut;
|
||||
}
|
||||
|
||||
static void uiGroupChildEdgeHuggingChanged(uiDarwinControl *c)
|
||||
{
|
||||
uiGroup *g = uiGroup(c);
|
||||
|
||||
groupRelayout(g);
|
||||
}
|
||||
|
||||
static NSLayoutPriority uiGroupHuggingPriority(uiDarwinControl *c, NSLayoutConstraintOrientation orientation)
|
||||
{
|
||||
uiGroup *g = uiGroup(c);
|
||||
|
||||
if (orientation == NSLayoutConstraintOrientationHorizontal)
|
||||
return g->horzHuggingPri;
|
||||
return g->vertHuggingPri;
|
||||
}
|
||||
|
||||
static void uiGroupSetHuggingPriority(uiDarwinControl *c, NSLayoutPriority priority, NSLayoutConstraintOrientation orientation)
|
||||
{
|
||||
uiGroup *g = uiGroup(c);
|
||||
|
||||
if (orientation == NSLayoutConstraintOrientationHorizontal)
|
||||
g->horzHuggingPri = priority;
|
||||
else
|
||||
g->vertHuggingPri = priority;
|
||||
uiDarwinNotifyEdgeHuggingChanged(uiDarwinControl(g));
|
||||
}
|
||||
|
||||
static void uiGroupChildVisibilityChanged(uiDarwinControl *c)
|
||||
{
|
||||
uiGroup *g = uiGroup(c);
|
||||
|
||||
groupRelayout(g);
|
||||
}
|
||||
|
||||
char *uiGroupTitle(uiGroup *g)
|
||||
{
|
||||
return uiDarwinNSStringToText([g->box title]);
|
||||
}
|
||||
|
||||
void uiGroupSetTitle(uiGroup *g, const char *title)
|
||||
{
|
||||
[g->box setTitle:toNSString(title)];
|
||||
}
|
||||
|
||||
void uiGroupSetChild(uiGroup *g, uiControl *child)
|
||||
{
|
||||
NSView *childView;
|
||||
|
||||
if (g->child != NULL) {
|
||||
removeConstraints(g);
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(g->child), g->oldHorzHuggingPri, NSLayoutConstraintOrientationHorizontal);
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(g->child), g->oldVertHuggingPri, NSLayoutConstraintOrientationVertical);
|
||||
uiControlSetParent(g->child, NULL);
|
||||
uiDarwinControlSetSuperview(uiDarwinControl(g->child), nil);
|
||||
}
|
||||
g->child = child;
|
||||
if (g->child != NULL) {
|
||||
childView = (NSView *) uiControlHandle(g->child);
|
||||
uiControlSetParent(g->child, uiControl(g));
|
||||
uiDarwinControlSetSuperview(uiDarwinControl(g->child), [g->box contentView]);
|
||||
uiDarwinControlSyncEnableState(uiDarwinControl(g->child), uiControlEnabledToUser(uiControl(g)));
|
||||
// don't hug, just in case we're a stretchy group
|
||||
g->oldHorzHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(g->child), NSLayoutConstraintOrientationHorizontal);
|
||||
g->oldVertHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(g->child), NSLayoutConstraintOrientationVertical);
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(g->child), NSLayoutPriorityDefaultLow, NSLayoutConstraintOrientationHorizontal);
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(g->child), NSLayoutPriorityDefaultLow, NSLayoutConstraintOrientationVertical);
|
||||
}
|
||||
groupRelayout(g);
|
||||
}
|
||||
|
||||
int uiGroupMargined(uiGroup *g)
|
||||
{
|
||||
return g->margined;
|
||||
}
|
||||
|
||||
void uiGroupSetMargined(uiGroup *g, int margined)
|
||||
{
|
||||
g->margined = margined;
|
||||
singleChildConstraintsSetMargined(&(g->constraints), g->margined);
|
||||
}
|
||||
|
||||
uiGroup *uiNewGroup(const char *title)
|
||||
{
|
||||
uiGroup *g;
|
||||
|
||||
uiDarwinNewControl(uiGroup, g);
|
||||
|
||||
g->box = [[NSBox alloc] initWithFrame:NSZeroRect];
|
||||
[g->box setTitle:toNSString(title)];
|
||||
[g->box setBoxType:NSBoxPrimary];
|
||||
[g->box setBorderType:NSLineBorder];
|
||||
[g->box setTransparent:NO];
|
||||
[g->box setTitlePosition:NSAtTop];
|
||||
// we can't use uiDarwinSetControlFont() because the selector is different
|
||||
[g->box setTitleFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]];
|
||||
|
||||
// default to low hugging to not hug edges
|
||||
g->horzHuggingPri = NSLayoutPriorityDefaultLow;
|
||||
g->vertHuggingPri = NSLayoutPriorityDefaultLow;
|
||||
|
||||
return g;
|
||||
}
|
82
deps/libui/darwin/image.m
vendored
82
deps/libui/darwin/image.m
vendored
@ -1,82 +0,0 @@
|
||||
// 25 june 2016
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
struct uiImage {
|
||||
NSImage *i;
|
||||
NSSize size;
|
||||
NSMutableArray *swizzled;
|
||||
};
|
||||
|
||||
uiImage *uiNewImage(double width, double height)
|
||||
{
|
||||
uiImage *i;
|
||||
|
||||
i = uiNew(uiImage);
|
||||
i->size = NSMakeSize(width, height);
|
||||
i->i = [[NSImage alloc] initWithSize:i->size];
|
||||
i->swizzled = [NSMutableArray new];
|
||||
return i;
|
||||
}
|
||||
|
||||
void uiFreeImage(uiImage *i)
|
||||
{
|
||||
NSValue *v;
|
||||
|
||||
[i->i release];
|
||||
// to be safe, do this after releasing the image
|
||||
for (v in i->swizzled)
|
||||
uiFree([v pointerValue]);
|
||||
[i->swizzled release];
|
||||
uiFree(i);
|
||||
}
|
||||
|
||||
void uiImageAppend(uiImage *i, void *pixels, int pixelWidth, int pixelHeight, int pixelStride)
|
||||
{
|
||||
NSBitmapImageRep *repCalibrated, *repsRGB;
|
||||
uint8_t *swizzled, *bp, *sp;
|
||||
int x, y;
|
||||
unsigned char *pix[1];
|
||||
|
||||
// OS X demands that R and B are in the opposite order from what we expect
|
||||
// we must swizzle :(
|
||||
// LONGTERM test on a big-endian system
|
||||
swizzled = (uint8_t *) uiAlloc((pixelStride * pixelHeight * 4) * sizeof (uint8_t), "uint8_t[]");
|
||||
bp = (uint8_t *) pixels;
|
||||
sp = swizzled;
|
||||
for (y = 0; y < pixelHeight * pixelStride; y += pixelStride)
|
||||
for (x = 0; x < pixelStride; x++) {
|
||||
sp[0] = bp[2];
|
||||
sp[1] = bp[1];
|
||||
sp[2] = bp[0];
|
||||
sp[3] = bp[3];
|
||||
sp += 4;
|
||||
bp += 4;
|
||||
}
|
||||
|
||||
pix[0] = (unsigned char *) swizzled;
|
||||
repCalibrated = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:pix
|
||||
pixelsWide:pixelWidth
|
||||
pixelsHigh:pixelHeight
|
||||
bitsPerSample:8
|
||||
samplesPerPixel:4
|
||||
hasAlpha:YES
|
||||
isPlanar:NO
|
||||
colorSpaceName:NSCalibratedRGBColorSpace
|
||||
bitmapFormat:0
|
||||
bytesPerRow:pixelStride
|
||||
bitsPerPixel:32];
|
||||
repsRGB = [repCalibrated bitmapImageRepByRetaggingWithColorSpace:[NSColorSpace sRGBColorSpace]];
|
||||
[repCalibrated release];
|
||||
|
||||
[i->i addRepresentation:repsRGB];
|
||||
[repsRGB setSize:i->size];
|
||||
[repsRGB release];
|
||||
|
||||
// we need to keep swizzled alive for NSBitmapImageRep
|
||||
[i->swizzled addObject:[NSValue valueWithPointer:swizzled]];
|
||||
}
|
||||
|
||||
NSImage *imageImage(uiImage *i)
|
||||
{
|
||||
return i->i;
|
||||
}
|
43
deps/libui/darwin/label.m
vendored
43
deps/libui/darwin/label.m
vendored
@ -1,43 +0,0 @@
|
||||
// 14 august 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
struct uiLabel {
|
||||
uiDarwinControl c;
|
||||
NSTextField *textfield;
|
||||
};
|
||||
|
||||
uiDarwinControlAllDefaults(uiLabel, textfield)
|
||||
|
||||
char *uiLabelText(uiLabel *l)
|
||||
{
|
||||
return uiDarwinNSStringToText([l->textfield stringValue]);
|
||||
}
|
||||
|
||||
void uiLabelSetText(uiLabel *l, const char *text)
|
||||
{
|
||||
[l->textfield setStringValue:toNSString(text)];
|
||||
}
|
||||
|
||||
NSTextField *newLabel(NSString *str)
|
||||
{
|
||||
NSTextField *tf;
|
||||
|
||||
tf = [[NSTextField alloc] initWithFrame:NSZeroRect];
|
||||
[tf setStringValue:str];
|
||||
[tf setEditable:NO];
|
||||
[tf setSelectable:NO];
|
||||
[tf setDrawsBackground:NO];
|
||||
finishNewTextField(tf, NO);
|
||||
return tf;
|
||||
}
|
||||
|
||||
uiLabel *uiNewLabel(const char *text)
|
||||
{
|
||||
uiLabel *l;
|
||||
|
||||
uiDarwinNewControl(uiLabel, l);
|
||||
|
||||
l->textfield = newLabel(toNSString(text));
|
||||
|
||||
return l;
|
||||
}
|
239
deps/libui/darwin/main.m
vendored
239
deps/libui/darwin/main.m
vendored
@ -1,239 +0,0 @@
|
||||
// 6 april 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
static BOOL canQuit = NO;
|
||||
static NSAutoreleasePool *globalPool;
|
||||
static applicationClass *app;
|
||||
static appDelegate *delegate;
|
||||
|
||||
static BOOL (^isRunning)(void);
|
||||
static BOOL stepsIsRunning;
|
||||
|
||||
@implementation applicationClass
|
||||
|
||||
- (void)sendEvent:(NSEvent *)e
|
||||
{
|
||||
if (sendAreaEvents(e) != 0)
|
||||
return;
|
||||
[super sendEvent:e];
|
||||
}
|
||||
|
||||
// NSColorPanel always sends changeColor: to the first responder regardless of whether there's a target set on it
|
||||
// we can override it here (see colorbutton.m)
|
||||
// thanks to mikeash in irc.freenode.net/#macdev for informing me this is how the first responder chain is initiated
|
||||
// it turns out NSFontManager also sends changeFont: through this; let's inhibit that here too (see fontbutton.m)
|
||||
- (BOOL)sendAction:(SEL)sel to:(id)to from:(id)from
|
||||
{
|
||||
if (colorButtonInhibitSendAction(sel, from, to))
|
||||
return NO;
|
||||
if (fontButtonInhibitSendAction(sel, from, to))
|
||||
return NO;
|
||||
return [super sendAction:sel to:to from:from];
|
||||
}
|
||||
|
||||
// likewise, NSFontManager also sends NSFontPanelValidation messages to the first responder, however it does NOT use sendAction:from:to:!
|
||||
// instead, it uses this one (thanks swillits in irc.freenode.net/#macdev)
|
||||
// we also need to override it (see fontbutton.m)
|
||||
- (id)targetForAction:(SEL)sel to:(id)to from:(id)from
|
||||
{
|
||||
id override;
|
||||
|
||||
if (fontButtonOverrideTargetForAction(sel, from, to, &override))
|
||||
return override;
|
||||
return [super targetForAction:sel to:to from:from];
|
||||
}
|
||||
|
||||
// hey look! we're overriding terminate:!
|
||||
// we're going to make sure we can go back to main() whether Cocoa likes it or not!
|
||||
// and just how are we going to do that, hm?
|
||||
// (note: this is called after applicationShouldTerminate:)
|
||||
- (void)terminate:(id)sender
|
||||
{
|
||||
// yes that's right folks: DO ABSOLUTELY NOTHING.
|
||||
// the magic is [NSApp run] will just... stop.
|
||||
|
||||
// well let's not do nothing; let's actually quit our graceful way
|
||||
NSEvent *e;
|
||||
|
||||
if (!canQuit)
|
||||
implbug("call to [NSApp terminate:] when not ready to terminate; definitely contact andlabs");
|
||||
|
||||
[realNSApp() stop:realNSApp()];
|
||||
// stop: won't register until another event has passed; let's synthesize one
|
||||
e = [NSEvent otherEventWithType:NSApplicationDefined
|
||||
location:NSZeroPoint
|
||||
modifierFlags:0
|
||||
timestamp:[[NSProcessInfo processInfo] systemUptime]
|
||||
windowNumber:0
|
||||
context:[NSGraphicsContext currentContext]
|
||||
subtype:0
|
||||
data1:0
|
||||
data2:0];
|
||||
[realNSApp() postEvent:e atStart:NO]; // let pending events take priority (this is what PostQuitMessage() on Windows does so we have to do it here too for parity; thanks to mikeash in irc.freenode.net/#macdev for confirming that this parameter should indeed be NO)
|
||||
|
||||
// and in case uiMainSteps() was called
|
||||
stepsIsRunning = NO;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation appDelegate
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
// Apple docs: "Don't Use Accessor Methods in Initializer Methods and dealloc"
|
||||
[_menuManager release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)app
|
||||
{
|
||||
// for debugging
|
||||
NSLog(@"in applicationShouldTerminate:");
|
||||
if (shouldQuit()) {
|
||||
canQuit = YES;
|
||||
// this will call terminate:, which is the same as uiQuit()
|
||||
return NSTerminateNow;
|
||||
}
|
||||
return NSTerminateCancel;
|
||||
}
|
||||
|
||||
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)app
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
uiInitOptions options;
|
||||
|
||||
const char *uiInit(uiInitOptions *o)
|
||||
{
|
||||
@autoreleasepool {
|
||||
options = *o;
|
||||
app = [[applicationClass sharedApplication] retain];
|
||||
// don't check for a NO return; something (launch services?) causes running from application bundles to always return NO when asking to change activation policy, even if the change is to the same activation policy!
|
||||
// see https://github.com/andlabs/ui/issues/6
|
||||
[realNSApp() setActivationPolicy:NSApplicationActivationPolicyRegular];
|
||||
delegate = [appDelegate new];
|
||||
[realNSApp() setDelegate:delegate];
|
||||
|
||||
initAlloc();
|
||||
|
||||
// always do this so we always have an application menu
|
||||
appDelegate().menuManager = [[menuManager new] autorelease];
|
||||
[realNSApp() setMainMenu:[appDelegate().menuManager makeMenubar]];
|
||||
|
||||
setupFontPanel();
|
||||
}
|
||||
|
||||
globalPool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void uiUninit(void)
|
||||
{
|
||||
if (!globalPool) {
|
||||
userbug("You must call uiInit() first!");
|
||||
}
|
||||
[globalPool release];
|
||||
|
||||
@autoreleasepool {
|
||||
[delegate release];
|
||||
[realNSApp() setDelegate:nil];
|
||||
[app release];
|
||||
uninitAlloc();
|
||||
}
|
||||
}
|
||||
|
||||
void uiFreeInitError(const char *err)
|
||||
{
|
||||
}
|
||||
|
||||
void uiMain(void)
|
||||
{
|
||||
isRunning = ^{
|
||||
return [realNSApp() isRunning];
|
||||
};
|
||||
[realNSApp() run];
|
||||
}
|
||||
|
||||
void uiMainSteps(void)
|
||||
{
|
||||
// SDL does this and it seems to be necessary for the menubar to work (see #182)
|
||||
[realNSApp() finishLaunching];
|
||||
isRunning = ^{
|
||||
return stepsIsRunning;
|
||||
};
|
||||
stepsIsRunning = YES;
|
||||
}
|
||||
|
||||
int uiMainStep(int wait)
|
||||
{
|
||||
struct nextEventArgs nea;
|
||||
|
||||
nea.mask = NSAnyEventMask;
|
||||
|
||||
// ProPuke did this in his original PR requesting this
|
||||
// I'm not sure if this will work, but I assume it will...
|
||||
nea.duration = [NSDate distantPast];
|
||||
if (wait) // but this is normal so it will work
|
||||
nea.duration = [NSDate distantFuture];
|
||||
|
||||
nea.mode = NSDefaultRunLoopMode;
|
||||
nea.dequeue = YES;
|
||||
|
||||
return mainStep(&nea, ^(NSEvent *e) {
|
||||
return NO;
|
||||
});
|
||||
}
|
||||
|
||||
// see also:
|
||||
// - http://www.cocoawithlove.com/2009/01/demystifying-nsapplication-by.html
|
||||
// - https://github.com/gnustep/gui/blob/master/Source/NSApplication.m
|
||||
int mainStep(struct nextEventArgs *nea, BOOL (^interceptEvent)(NSEvent *e))
|
||||
{
|
||||
NSDate *expire;
|
||||
NSEvent *e;
|
||||
NSEventType type;
|
||||
|
||||
@autoreleasepool {
|
||||
if (!isRunning())
|
||||
return 0;
|
||||
|
||||
e = [realNSApp() nextEventMatchingMask:nea->mask
|
||||
untilDate:nea->duration
|
||||
inMode:nea->mode
|
||||
dequeue:nea->dequeue];
|
||||
if (e == nil)
|
||||
return 1;
|
||||
|
||||
type = [e type];
|
||||
if (!interceptEvent(e))
|
||||
[realNSApp() sendEvent:e];
|
||||
[realNSApp() updateWindows];
|
||||
|
||||
// GNUstep does this
|
||||
// it also updates the Services menu but there doesn't seem to be a public API for that so
|
||||
if (type != NSPeriodic && type != NSMouseMoved)
|
||||
[[realNSApp() mainMenu] update];
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
void uiQuit(void)
|
||||
{
|
||||
canQuit = YES;
|
||||
[realNSApp() terminate:realNSApp()];
|
||||
}
|
||||
|
||||
// thanks to mikeash in irc.freenode.net/#macdev for suggesting the use of Grand Central Dispatch for this
|
||||
// LONGTERM will dispatch_get_main_queue() break after _CFRunLoopSetCurrent()?
|
||||
void uiQueueMain(void (*f)(void *data), void *data)
|
||||
{
|
||||
// dispatch_get_main_queue() is a serial queue so it will not execute multiple uiQueueMain() functions concurrently
|
||||
// the signature of f matches dispatch_function_t
|
||||
dispatch_async_f(dispatch_get_main_queue(), data, f);
|
||||
}
|
59
deps/libui/darwin/map.m
vendored
59
deps/libui/darwin/map.m
vendored
@ -1,59 +0,0 @@
|
||||
// 17 august 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// unfortunately NSMutableDictionary copies its keys, meaning we can't use it for pointers
|
||||
// hence, this file
|
||||
// we could expose a NSMapTable directly, but let's treat all pointers as opaque and hide the implementation, just to be safe and prevent even more rewrites later
|
||||
struct mapTable {
|
||||
NSMapTable *m;
|
||||
};
|
||||
|
||||
struct mapTable *newMap(void)
|
||||
{
|
||||
struct mapTable *m;
|
||||
|
||||
m = uiNew(struct mapTable);
|
||||
m->m = [[NSMapTable alloc] initWithKeyOptions:(NSPointerFunctionsOpaqueMemory | NSPointerFunctionsOpaquePersonality)
|
||||
valueOptions:(NSPointerFunctionsOpaqueMemory | NSPointerFunctionsOpaquePersonality)
|
||||
capacity:0];
|
||||
return m;
|
||||
}
|
||||
|
||||
void mapDestroy(struct mapTable *m)
|
||||
{
|
||||
if ([m->m count] != 0)
|
||||
implbug("attempt to destroy map with items inside");
|
||||
[m->m release];
|
||||
uiFree(m);
|
||||
}
|
||||
|
||||
void *mapGet(struct mapTable *m, void *key)
|
||||
{
|
||||
return NSMapGet(m->m, key);
|
||||
}
|
||||
|
||||
void mapSet(struct mapTable *m, void *key, void *value)
|
||||
{
|
||||
NSMapInsert(m->m, key, value);
|
||||
}
|
||||
|
||||
void mapDelete(struct mapTable *m, void *key)
|
||||
{
|
||||
NSMapRemove(m->m, key);
|
||||
}
|
||||
|
||||
void mapWalk(struct mapTable *m, void (*f)(void *key, void *value))
|
||||
{
|
||||
NSMapEnumerator e = NSEnumerateMapTable(m->m);
|
||||
void *k = NULL;
|
||||
void *v = NULL;
|
||||
while (NSNextMapEnumeratorPair(&e, &k, &v)) {
|
||||
f(k, v);
|
||||
}
|
||||
NSEndMapTableEnumeration(&e);
|
||||
}
|
||||
|
||||
void mapReset(struct mapTable *m)
|
||||
{
|
||||
NSResetMapTable(m->m);
|
||||
}
|
368
deps/libui/darwin/menu.m
vendored
368
deps/libui/darwin/menu.m
vendored
@ -1,368 +0,0 @@
|
||||
// 28 april 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
static NSMutableArray *menus = nil;
|
||||
static BOOL menusFinalized = NO;
|
||||
|
||||
struct uiMenu {
|
||||
NSMenu *menu;
|
||||
NSMenuItem *item;
|
||||
NSMutableArray *items;
|
||||
};
|
||||
|
||||
struct uiMenuItem {
|
||||
NSMenuItem *item;
|
||||
int type;
|
||||
BOOL disabled;
|
||||
void (*onClicked)(uiMenuItem *, uiWindow *, void *);
|
||||
void *onClickedData;
|
||||
};
|
||||
|
||||
enum {
|
||||
typeRegular,
|
||||
typeCheckbox,
|
||||
typeQuit,
|
||||
typePreferences,
|
||||
typeAbout,
|
||||
typeSeparator,
|
||||
};
|
||||
|
||||
static void mapItemReleaser(void *key, void *value)
|
||||
{
|
||||
uiMenuItem *item;
|
||||
|
||||
item = (uiMenuItem *)value;
|
||||
[item->item release];
|
||||
}
|
||||
|
||||
@implementation menuManager
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self->items = newMap();
|
||||
self->hasQuit = NO;
|
||||
self->hasPreferences = NO;
|
||||
self->hasAbout = NO;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
mapWalk(self->items, mapItemReleaser);
|
||||
mapReset(self->items);
|
||||
mapDestroy(self->items);
|
||||
uninitMenus();
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (IBAction)onClicked:(id)sender
|
||||
{
|
||||
uiMenuItem *item;
|
||||
|
||||
item = (uiMenuItem *) mapGet(self->items, sender);
|
||||
if (item->type == typeCheckbox)
|
||||
uiMenuItemSetChecked(item, !uiMenuItemChecked(item));
|
||||
// use the key window as the source of the menu event; it's the active window
|
||||
(*(item->onClicked))(item, windowFromNSWindow([realNSApp() keyWindow]), item->onClickedData);
|
||||
}
|
||||
|
||||
- (IBAction)onQuitClicked:(id)sender
|
||||
{
|
||||
if (shouldQuit())
|
||||
uiQuit();
|
||||
}
|
||||
|
||||
- (void)register:(NSMenuItem *)item to:(uiMenuItem *)smi
|
||||
{
|
||||
switch (smi->type) {
|
||||
case typeQuit:
|
||||
if (self->hasQuit)
|
||||
userbug("You can't have multiple Quit menu items in one program.");
|
||||
self->hasQuit = YES;
|
||||
break;
|
||||
case typePreferences:
|
||||
if (self->hasPreferences)
|
||||
userbug("You can't have multiple Preferences menu items in one program.");
|
||||
self->hasPreferences = YES;
|
||||
break;
|
||||
case typeAbout:
|
||||
if (self->hasAbout)
|
||||
userbug("You can't have multiple About menu items in one program.");
|
||||
self->hasAbout = YES;
|
||||
break;
|
||||
}
|
||||
mapSet(self->items, item, smi);
|
||||
}
|
||||
|
||||
// on OS X there are two ways to handle menu items being enabled or disabled: automatically and manually
|
||||
// unfortunately, the application menu requires automatic menu handling for the Hide, Hide Others, and Show All items to work correctly
|
||||
// therefore, we have to handle enabling of the other options ourselves
|
||||
- (BOOL)validateMenuItem:(NSMenuItem *)item
|
||||
{
|
||||
uiMenuItem *smi;
|
||||
|
||||
// disable the special items if they aren't present
|
||||
if (item == self.quitItem && !self->hasQuit)
|
||||
return NO;
|
||||
if (item == self.preferencesItem && !self->hasPreferences)
|
||||
return NO;
|
||||
if (item == self.aboutItem && !self->hasAbout)
|
||||
return NO;
|
||||
// then poll the item's enabled/disabled state
|
||||
smi = (uiMenuItem *) mapGet(self->items, item);
|
||||
return !smi->disabled;
|
||||
}
|
||||
|
||||
// Cocoa constructs the default application menu by hand for each program; that's what MainMenu.[nx]ib does
|
||||
- (void)buildApplicationMenu:(NSMenu *)menubar
|
||||
{
|
||||
NSString *appName;
|
||||
NSMenuItem *appMenuItem;
|
||||
NSMenu *appMenu;
|
||||
NSMenuItem *item;
|
||||
NSString *title;
|
||||
NSMenu *servicesMenu;
|
||||
|
||||
// note: no need to call setAppleMenu: on this anymore; see https://developer.apple.com/library/mac/releasenotes/AppKit/RN-AppKitOlderNotes/#X10_6Notes
|
||||
appName = [[NSProcessInfo processInfo] processName];
|
||||
appMenuItem = [[[NSMenuItem alloc] initWithTitle:appName action:NULL keyEquivalent:@""] autorelease];
|
||||
appMenu = [[[NSMenu alloc] initWithTitle:appName] autorelease];
|
||||
[appMenuItem setSubmenu:appMenu];
|
||||
[menubar addItem:appMenuItem];
|
||||
|
||||
// first is About
|
||||
title = [@"About " stringByAppendingString:appName];
|
||||
item = [[[NSMenuItem alloc] initWithTitle:title action:@selector(onClicked:) keyEquivalent:@""] autorelease];
|
||||
[item setTarget:self];
|
||||
[appMenu addItem:item];
|
||||
self.aboutItem = item;
|
||||
|
||||
[appMenu addItem:[NSMenuItem separatorItem]];
|
||||
|
||||
// next is Preferences
|
||||
item = [[[NSMenuItem alloc] initWithTitle:@"Preferences…" action:@selector(onClicked:) keyEquivalent:@","] autorelease];
|
||||
[item setTarget:self];
|
||||
[appMenu addItem:item];
|
||||
self.preferencesItem = item;
|
||||
|
||||
[appMenu addItem:[NSMenuItem separatorItem]];
|
||||
|
||||
// next is Services
|
||||
item = [[[NSMenuItem alloc] initWithTitle:@"Services" action:NULL keyEquivalent:@""] autorelease];
|
||||
servicesMenu = [[[NSMenu alloc] initWithTitle:@"Services"] autorelease];
|
||||
[item setSubmenu:servicesMenu];
|
||||
[realNSApp() setServicesMenu:servicesMenu];
|
||||
[appMenu addItem:item];
|
||||
|
||||
[appMenu addItem:[NSMenuItem separatorItem]];
|
||||
|
||||
// next are the three hiding options
|
||||
title = [@"Hide " stringByAppendingString:appName];
|
||||
item = [[[NSMenuItem alloc] initWithTitle:title action:@selector(hide:) keyEquivalent:@"h"] autorelease];
|
||||
// the .xib file says they go to -1 ("First Responder", which sounds wrong...)
|
||||
// to do that, we simply leave the target as nil
|
||||
[appMenu addItem:item];
|
||||
item = [[[NSMenuItem alloc] initWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"] autorelease];
|
||||
[item setKeyEquivalentModifierMask:(NSAlternateKeyMask | NSCommandKeyMask)];
|
||||
[appMenu addItem:item];
|
||||
item = [[[NSMenuItem alloc] initWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""] autorelease];
|
||||
[appMenu addItem:item];
|
||||
|
||||
[appMenu addItem:[NSMenuItem separatorItem]];
|
||||
|
||||
// and finally Quit
|
||||
// DON'T use @selector(terminate:) as the action; we handle termination ourselves
|
||||
title = [@"Quit " stringByAppendingString:appName];
|
||||
item = [[[NSMenuItem alloc] initWithTitle:title action:@selector(onQuitClicked:) keyEquivalent:@"q"] autorelease];
|
||||
[item setTarget:self];
|
||||
[appMenu addItem:item];
|
||||
self.quitItem = item;
|
||||
}
|
||||
|
||||
- (NSMenu *)makeMenubar
|
||||
{
|
||||
NSMenu *menubar;
|
||||
|
||||
menubar = [[[NSMenu alloc] initWithTitle:@""] autorelease];
|
||||
[self buildApplicationMenu:menubar];
|
||||
return menubar;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static void defaultOnClicked(uiMenuItem *item, uiWindow *w, void *data)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void uiMenuItemEnable(uiMenuItem *item)
|
||||
{
|
||||
item->disabled = NO;
|
||||
// we don't need to explicitly update the menus here; they'll be updated the next time they're opened (thanks mikeash in irc.freenode.net/#macdev)
|
||||
}
|
||||
|
||||
void uiMenuItemDisable(uiMenuItem *item)
|
||||
{
|
||||
item->disabled = YES;
|
||||
}
|
||||
|
||||
void uiMenuItemOnClicked(uiMenuItem *item, void (*f)(uiMenuItem *, uiWindow *, void *), void *data)
|
||||
{
|
||||
if (item->type == typeQuit)
|
||||
userbug("You can't call uiMenuItemOnClicked() on a Quit item; use uiOnShouldQuit() instead.");
|
||||
item->onClicked = f;
|
||||
item->onClickedData = data;
|
||||
}
|
||||
|
||||
int uiMenuItemChecked(uiMenuItem *item)
|
||||
{
|
||||
return [item->item state] != NSOffState;
|
||||
}
|
||||
|
||||
void uiMenuItemSetChecked(uiMenuItem *item, int checked)
|
||||
{
|
||||
NSInteger state;
|
||||
|
||||
state = NSOffState;
|
||||
if ([item->item state] == NSOffState)
|
||||
state = NSOnState;
|
||||
[item->item setState:state];
|
||||
}
|
||||
|
||||
static uiMenuItem *newItem(uiMenu *m, int type, const char *name)
|
||||
{
|
||||
@autoreleasepool {
|
||||
|
||||
uiMenuItem *item;
|
||||
|
||||
if (menusFinalized)
|
||||
userbug("You can't create a new menu item after menus have been finalized.");
|
||||
|
||||
item = uiNew(uiMenuItem);
|
||||
|
||||
item->type = type;
|
||||
switch (item->type) {
|
||||
case typeQuit:
|
||||
item->item = [appDelegate().menuManager.quitItem retain];
|
||||
break;
|
||||
case typePreferences:
|
||||
item->item = [appDelegate().menuManager.preferencesItem retain];
|
||||
break;
|
||||
case typeAbout:
|
||||
item->item = [appDelegate().menuManager.aboutItem retain];
|
||||
break;
|
||||
case typeSeparator:
|
||||
item->item = [[NSMenuItem separatorItem] retain];
|
||||
[m->menu addItem:item->item];
|
||||
break;
|
||||
default:
|
||||
item->item = [[NSMenuItem alloc] initWithTitle:toNSString(name) action:@selector(onClicked:) keyEquivalent:@""];
|
||||
[item->item setTarget:appDelegate().menuManager];
|
||||
[m->menu addItem:item->item];
|
||||
break;
|
||||
}
|
||||
|
||||
[appDelegate().menuManager register:item->item to:item];
|
||||
item->onClicked = defaultOnClicked;
|
||||
|
||||
[m->items addObject:[NSValue valueWithPointer:item]];
|
||||
|
||||
return item;
|
||||
|
||||
} // @autoreleasepool
|
||||
}
|
||||
|
||||
uiMenuItem *uiMenuAppendItem(uiMenu *m, const char *name)
|
||||
{
|
||||
return newItem(m, typeRegular, name);
|
||||
}
|
||||
|
||||
uiMenuItem *uiMenuAppendCheckItem(uiMenu *m, const char *name)
|
||||
{
|
||||
return newItem(m, typeCheckbox, name);
|
||||
}
|
||||
|
||||
uiMenuItem *uiMenuAppendQuitItem(uiMenu *m)
|
||||
{
|
||||
// duplicate check is in the register:to: selector
|
||||
return newItem(m, typeQuit, NULL);
|
||||
}
|
||||
|
||||
uiMenuItem *uiMenuAppendPreferencesItem(uiMenu *m)
|
||||
{
|
||||
// duplicate check is in the register:to: selector
|
||||
return newItem(m, typePreferences, NULL);
|
||||
}
|
||||
|
||||
uiMenuItem *uiMenuAppendAboutItem(uiMenu *m)
|
||||
{
|
||||
// duplicate check is in the register:to: selector
|
||||
return newItem(m, typeAbout, NULL);
|
||||
}
|
||||
|
||||
void uiMenuAppendSeparator(uiMenu *m)
|
||||
{
|
||||
newItem(m, typeSeparator, NULL);
|
||||
}
|
||||
|
||||
uiMenu *uiNewMenu(const char *name)
|
||||
{
|
||||
@autoreleasepool {
|
||||
|
||||
uiMenu *m;
|
||||
|
||||
if (menusFinalized)
|
||||
userbug("You can't create a new menu after menus have been finalized.");
|
||||
if (menus == nil)
|
||||
menus = [NSMutableArray new];
|
||||
|
||||
m = uiNew(uiMenu);
|
||||
|
||||
m->menu = [[NSMenu alloc] initWithTitle:toNSString(name)];
|
||||
// use automatic menu item enabling for all menus for consistency's sake
|
||||
|
||||
m->item = [[NSMenuItem alloc] initWithTitle:toNSString(name) action:NULL keyEquivalent:@""];
|
||||
[m->item setSubmenu:m->menu];
|
||||
|
||||
m->items = [NSMutableArray new];
|
||||
|
||||
[[realNSApp() mainMenu] addItem:m->item];
|
||||
|
||||
[menus addObject:[NSValue valueWithPointer:m]];
|
||||
|
||||
return m;
|
||||
|
||||
} // @autoreleasepool
|
||||
}
|
||||
|
||||
void finalizeMenus(void)
|
||||
{
|
||||
menusFinalized = YES;
|
||||
}
|
||||
|
||||
void uninitMenus(void)
|
||||
{
|
||||
if (menus == NULL)
|
||||
return;
|
||||
[menus enumerateObjectsUsingBlock:^(id obj, NSUInteger index, BOOL *stop) {
|
||||
NSValue *v;
|
||||
uiMenu *m;
|
||||
|
||||
v = (NSValue *) obj;
|
||||
m = (uiMenu *) [v pointerValue];
|
||||
[m->items enumerateObjectsUsingBlock:^(id obj, NSUInteger index, BOOL *stop) {
|
||||
NSValue *v;
|
||||
uiMenuItem *mi;
|
||||
|
||||
v = (NSValue *) obj;
|
||||
mi = (uiMenuItem *) [v pointerValue];
|
||||
uiFree(mi);
|
||||
}];
|
||||
[m->items release];
|
||||
uiFree(m);
|
||||
}];
|
||||
[menus release];
|
||||
}
|
233
deps/libui/darwin/multilineentry.m
vendored
233
deps/libui/darwin/multilineentry.m
vendored
@ -1,233 +0,0 @@
|
||||
// 8 december 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// NSTextView has no intrinsic content size by default, which wreaks havoc on a pure-Auto Layout system
|
||||
// we'll have to take over to get it to work
|
||||
// see also http://stackoverflow.com/questions/24210153/nstextview-not-properly-resizing-with-auto-layout and http://stackoverflow.com/questions/11237622/using-autolayout-with-expanding-nstextviews
|
||||
@interface intrinsicSizeTextView : NSTextView {
|
||||
uiMultilineEntry *libui_e;
|
||||
}
|
||||
- (id)initWithFrame:(NSRect)r e:(uiMultilineEntry *)e;
|
||||
@end
|
||||
|
||||
struct uiMultilineEntry {
|
||||
uiDarwinControl c;
|
||||
NSScrollView *sv;
|
||||
intrinsicSizeTextView *tv;
|
||||
struct scrollViewData *d;
|
||||
void (*onChanged)(uiMultilineEntry *, void *);
|
||||
void *onChangedData;
|
||||
BOOL changing;
|
||||
};
|
||||
|
||||
@implementation intrinsicSizeTextView
|
||||
|
||||
- (id)initWithFrame:(NSRect)r e:(uiMultilineEntry *)e
|
||||
{
|
||||
self = [super initWithFrame:r];
|
||||
if (self)
|
||||
self->libui_e = e;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSSize)intrinsicContentSize
|
||||
{
|
||||
NSTextContainer *textContainer;
|
||||
NSLayoutManager *layoutManager;
|
||||
NSRect rect;
|
||||
|
||||
textContainer = [self textContainer];
|
||||
layoutManager = [self layoutManager];
|
||||
[layoutManager ensureLayoutForTextContainer:textContainer];
|
||||
rect = [layoutManager usedRectForTextContainer:textContainer];
|
||||
return rect.size;
|
||||
}
|
||||
|
||||
- (void)didChangeText
|
||||
{
|
||||
[super didChangeText];
|
||||
[self invalidateIntrinsicContentSize];
|
||||
if (!self->libui_e->changing)
|
||||
(*(self->libui_e->onChanged))(self->libui_e, self->libui_e->onChangedData);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
uiDarwinControlAllDefaultsExceptDestroy(uiMultilineEntry, sv)
|
||||
|
||||
static void uiMultilineEntryDestroy(uiControl *c)
|
||||
{
|
||||
uiMultilineEntry *e = uiMultilineEntry(c);
|
||||
|
||||
scrollViewFreeData(e->sv, e->d);
|
||||
[e->tv release];
|
||||
[e->sv release];
|
||||
uiFreeControl(uiControl(e));
|
||||
}
|
||||
|
||||
static void defaultOnChanged(uiMultilineEntry *e, void *data)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
char *uiMultilineEntryText(uiMultilineEntry *e)
|
||||
{
|
||||
return uiDarwinNSStringToText([e->tv string]);
|
||||
}
|
||||
|
||||
void uiMultilineEntrySetText(uiMultilineEntry *e, const char *text)
|
||||
{
|
||||
[[e->tv textStorage] replaceCharactersInRange:NSMakeRange(0, [[e->tv string] length])
|
||||
withString:toNSString(text)];
|
||||
// must be called explicitly according to the documentation of shouldChangeTextInRange:replacementString:
|
||||
e->changing = YES;
|
||||
[e->tv didChangeText];
|
||||
e->changing = NO;
|
||||
}
|
||||
|
||||
// TODO scroll to end?
|
||||
void uiMultilineEntryAppend(uiMultilineEntry *e, const char *text)
|
||||
{
|
||||
[[e->tv textStorage] replaceCharactersInRange:NSMakeRange([[e->tv string] length], 0)
|
||||
withString:toNSString(text)];
|
||||
e->changing = YES;
|
||||
[e->tv didChangeText];
|
||||
e->changing = NO;
|
||||
}
|
||||
|
||||
void uiMultilineEntryOnChanged(uiMultilineEntry *e, void (*f)(uiMultilineEntry *e, void *data), void *data)
|
||||
{
|
||||
e->onChanged = f;
|
||||
e->onChangedData = data;
|
||||
}
|
||||
|
||||
int uiMultilineEntryReadOnly(uiMultilineEntry *e)
|
||||
{
|
||||
return [e->tv isEditable] == NO;
|
||||
}
|
||||
|
||||
void uiMultilineEntrySetReadOnly(uiMultilineEntry *e, int readonly)
|
||||
{
|
||||
BOOL editable;
|
||||
|
||||
editable = YES;
|
||||
if (readonly)
|
||||
editable = NO;
|
||||
[e->tv setEditable:editable];
|
||||
}
|
||||
|
||||
static uiMultilineEntry *finishMultilineEntry(BOOL hscroll)
|
||||
{
|
||||
uiMultilineEntry *e;
|
||||
NSFont *font;
|
||||
struct scrollViewCreateParams p;
|
||||
|
||||
uiDarwinNewControl(uiMultilineEntry, e);
|
||||
|
||||
e->tv = [[intrinsicSizeTextView alloc] initWithFrame:NSZeroRect e:e];
|
||||
|
||||
// verified against Interface Builder for a sufficiently customized text view
|
||||
|
||||
// NSText properties:
|
||||
// this is what Interface Builder sets the background color to
|
||||
[e->tv setBackgroundColor:[NSColor colorWithCalibratedWhite:1.0 alpha:1.0]];
|
||||
[e->tv setDrawsBackground:YES];
|
||||
[e->tv setEditable:YES];
|
||||
[e->tv setSelectable:YES];
|
||||
[e->tv setFieldEditor:NO];
|
||||
[e->tv setRichText:NO];
|
||||
[e->tv setImportsGraphics:NO];
|
||||
[e->tv setUsesFontPanel:NO];
|
||||
[e->tv setRulerVisible:NO];
|
||||
// we'll handle font last
|
||||
// while setAlignment: has been around since 10.0, the named constant "NSTextAlignmentNatural" seems to have only been introduced in 10.11
|
||||
#define ourNSTextAlignmentNatural 4
|
||||
[e->tv setAlignment:ourNSTextAlignmentNatural];
|
||||
// textColor is set to nil, just keep the dfault
|
||||
[e->tv setBaseWritingDirection:NSWritingDirectionNatural];
|
||||
[e->tv setHorizontallyResizable:NO];
|
||||
[e->tv setVerticallyResizable:YES];
|
||||
|
||||
// NSTextView properties:
|
||||
[e->tv setAllowsDocumentBackgroundColorChange:NO];
|
||||
[e->tv setAllowsUndo:YES];
|
||||
// default paragraph style is nil; keep default
|
||||
[e->tv setAllowsImageEditing:NO];
|
||||
[e->tv setAutomaticQuoteSubstitutionEnabled:NO];
|
||||
[e->tv setAutomaticLinkDetectionEnabled:NO];
|
||||
[e->tv setDisplaysLinkToolTips:YES];
|
||||
[e->tv setUsesRuler:NO];
|
||||
[e->tv setUsesInspectorBar:NO];
|
||||
[e->tv setSelectionGranularity:NSSelectByCharacter];
|
||||
// there is a dedicated named insertion point color but oh well
|
||||
[e->tv setInsertionPointColor:[NSColor controlTextColor]];
|
||||
// typing attributes is nil; keep default (we change it below for fonts though)
|
||||
[e->tv setSmartInsertDeleteEnabled:NO];
|
||||
[e->tv setContinuousSpellCheckingEnabled:NO];
|
||||
[e->tv setGrammarCheckingEnabled:NO];
|
||||
[e->tv setUsesFindPanel:YES];
|
||||
[e->tv setEnabledTextCheckingTypes:0];
|
||||
[e->tv setAutomaticDashSubstitutionEnabled:NO];
|
||||
[e->tv setAutomaticDataDetectionEnabled:NO];
|
||||
[e->tv setAutomaticSpellingCorrectionEnabled:NO];
|
||||
[e->tv setAutomaticTextReplacementEnabled:NO];
|
||||
[e->tv setUsesFindBar:NO];
|
||||
[e->tv setIncrementalSearchingEnabled:NO];
|
||||
|
||||
// NSTextContainer properties:
|
||||
[[e->tv textContainer] setWidthTracksTextView:YES];
|
||||
[[e->tv textContainer] setHeightTracksTextView:NO];
|
||||
|
||||
// NSLayoutManager properties:
|
||||
[[e->tv layoutManager] setAllowsNonContiguousLayout:YES];
|
||||
|
||||
// now just to be safe; this will do some of the above but whatever
|
||||
disableAutocorrect(e->tv);
|
||||
|
||||
// see https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/TextUILayer/Tasks/TextInScrollView.html
|
||||
// notice we don't use the Auto Layout code; see scrollview.m for more details
|
||||
[e->tv setMaxSize:NSMakeSize(CGFLOAT_MAX, CGFLOAT_MAX)];
|
||||
[e->tv setVerticallyResizable:YES];
|
||||
[e->tv setHorizontallyResizable:hscroll];
|
||||
if (hscroll) {
|
||||
[e->tv setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
|
||||
[[e->tv textContainer] setWidthTracksTextView:NO];
|
||||
} else {
|
||||
[e->tv setAutoresizingMask:NSViewWidthSizable];
|
||||
[[e->tv textContainer] setWidthTracksTextView:YES];
|
||||
}
|
||||
[[e->tv textContainer] setContainerSize:NSMakeSize(CGFLOAT_MAX, CGFLOAT_MAX)];
|
||||
|
||||
// don't use uiDarwinSetControlFont() directly; we have to do a little extra work to set the font
|
||||
font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]];
|
||||
[e->tv setTypingAttributes:[NSDictionary
|
||||
dictionaryWithObject:font
|
||||
forKey:NSFontAttributeName]];
|
||||
// e->tv font from Interface Builder is nil, but setFont:nil throws an exception
|
||||
// let's just set it to the standard control font anyway, just to be safe
|
||||
[e->tv setFont:font];
|
||||
|
||||
memset(&p, 0, sizeof (struct scrollViewCreateParams));
|
||||
p.DocumentView = e->tv;
|
||||
// this is what Interface Builder sets it to
|
||||
p.BackgroundColor = [NSColor colorWithCalibratedWhite:1.0 alpha:1.0];
|
||||
p.DrawsBackground = YES;
|
||||
p.Bordered = YES;
|
||||
p.HScroll = hscroll;
|
||||
p.VScroll = YES;
|
||||
e->sv = mkScrollView(&p, &(e->d));
|
||||
|
||||
uiMultilineEntryOnChanged(e, defaultOnChanged, NULL);
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
uiMultilineEntry *uiNewMultilineEntry(void)
|
||||
{
|
||||
return finishMultilineEntry(NO);
|
||||
}
|
||||
|
||||
uiMultilineEntry *uiNewNonWrappingMultilineEntry(void)
|
||||
{
|
||||
return finishMultilineEntry(YES);
|
||||
}
|
78
deps/libui/darwin/progressbar.m
vendored
78
deps/libui/darwin/progressbar.m
vendored
@ -1,78 +0,0 @@
|
||||
// 14 august 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// NSProgressIndicator has no intrinsic width by default; use the default width in Interface Builder
|
||||
#define progressIndicatorWidth 100
|
||||
|
||||
@interface intrinsicWidthNSProgressIndicator : NSProgressIndicator
|
||||
@end
|
||||
|
||||
@implementation intrinsicWidthNSProgressIndicator
|
||||
|
||||
- (NSSize)intrinsicContentSize
|
||||
{
|
||||
NSSize s;
|
||||
|
||||
s = [super intrinsicContentSize];
|
||||
s.width = progressIndicatorWidth;
|
||||
return s;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
struct uiProgressBar {
|
||||
uiDarwinControl c;
|
||||
NSProgressIndicator *pi;
|
||||
};
|
||||
|
||||
uiDarwinControlAllDefaults(uiProgressBar, pi)
|
||||
|
||||
int uiProgressBarValue(uiProgressBar *p)
|
||||
{
|
||||
if ([p->pi isIndeterminate])
|
||||
return -1;
|
||||
return [p->pi doubleValue];
|
||||
}
|
||||
|
||||
void uiProgressBarSetValue(uiProgressBar *p, int value)
|
||||
{
|
||||
if (value == -1) {
|
||||
[p->pi setIndeterminate:YES];
|
||||
[p->pi startAnimation:p->pi];
|
||||
return;
|
||||
}
|
||||
|
||||
if ([p->pi isIndeterminate]) {
|
||||
[p->pi setIndeterminate:NO];
|
||||
[p->pi stopAnimation:p->pi];
|
||||
}
|
||||
|
||||
if (value < 0 || value > 100)
|
||||
userbug("Value %d out of range for a uiProgressBar.", value);
|
||||
|
||||
// on 10.8 there's an animation when the progress bar increases, just like with Aero
|
||||
if (value == 100) {
|
||||
[p->pi setMaxValue:101];
|
||||
[p->pi setDoubleValue:101];
|
||||
[p->pi setDoubleValue:100];
|
||||
[p->pi setMaxValue:100];
|
||||
return;
|
||||
}
|
||||
[p->pi setDoubleValue:((double) (value + 1))];
|
||||
[p->pi setDoubleValue:((double) value)];
|
||||
}
|
||||
|
||||
uiProgressBar *uiNewProgressBar(void)
|
||||
{
|
||||
uiProgressBar *p;
|
||||
|
||||
uiDarwinNewControl(uiProgressBar, p);
|
||||
|
||||
p->pi = [[intrinsicWidthNSProgressIndicator alloc] initWithFrame:NSZeroRect];
|
||||
[p->pi setControlSize:NSRegularControlSize];
|
||||
[p->pi setBezeled:YES];
|
||||
[p->pi setStyle:NSProgressIndicatorBarStyle];
|
||||
[p->pi setIndeterminate:NO];
|
||||
|
||||
return p;
|
||||
}
|
207
deps/libui/darwin/radiobuttons.m
vendored
207
deps/libui/darwin/radiobuttons.m
vendored
@ -1,207 +0,0 @@
|
||||
// 14 august 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// TODO resizing the controlgallery vertically causes the third button to still resize :|
|
||||
|
||||
// In the old days you would use a NSMatrix for this; as of OS X 10.8 this was deprecated and now you need just a bunch of NSButtons with the same superview AND same action method.
|
||||
// This is documented on the NSMatrix page, but the rest of the OS X documentation says to still use NSMatrix.
|
||||
// NSMatrix has weird quirks anyway...
|
||||
|
||||
// LONGTERM 6 units of spacing between buttons, as suggested by Interface Builder?
|
||||
|
||||
@interface radioButtonsDelegate : NSObject {
|
||||
uiRadioButtons *libui_r;
|
||||
}
|
||||
- (id)initWithR:(uiRadioButtons *)r;
|
||||
- (IBAction)onClicked:(id)sender;
|
||||
@end
|
||||
|
||||
struct uiRadioButtons {
|
||||
uiDarwinControl c;
|
||||
NSView *view;
|
||||
NSMutableArray *buttons;
|
||||
NSMutableArray *constraints;
|
||||
NSLayoutConstraint *lastv;
|
||||
radioButtonsDelegate *delegate;
|
||||
void (*onSelected)(uiRadioButtons *, void *);
|
||||
void *onSelectedData;
|
||||
};
|
||||
|
||||
@implementation radioButtonsDelegate
|
||||
|
||||
- (id)initWithR:(uiRadioButtons *)r
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
self->libui_r = r;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (IBAction)onClicked:(id)sender
|
||||
{
|
||||
uiRadioButtons *r = self->libui_r;
|
||||
|
||||
(*(r->onSelected))(r, r->onSelectedData);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
uiDarwinControlAllDefaultsExceptDestroy(uiRadioButtons, view)
|
||||
|
||||
static void defaultOnSelected(uiRadioButtons *r, void *data)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
static void uiRadioButtonsDestroy(uiControl *c)
|
||||
{
|
||||
uiRadioButtons *r = uiRadioButtons(c);
|
||||
NSButton *b;
|
||||
|
||||
// drop the constraints
|
||||
[r->view removeConstraints:r->constraints];
|
||||
[r->constraints release];
|
||||
if (r->lastv != nil)
|
||||
[r->lastv release];
|
||||
// destroy the buttons
|
||||
for (b in r->buttons) {
|
||||
[b setTarget:nil];
|
||||
[b removeFromSuperview];
|
||||
}
|
||||
[r->buttons release];
|
||||
// destroy the delegate
|
||||
[r->delegate release];
|
||||
// and destroy ourselves
|
||||
[r->view release];
|
||||
uiFreeControl(uiControl(r));
|
||||
}
|
||||
|
||||
static NSButton *buttonAt(uiRadioButtons *r, int n)
|
||||
{
|
||||
return (NSButton *) [r->buttons objectAtIndex:n];
|
||||
}
|
||||
|
||||
void uiRadioButtonsAppend(uiRadioButtons *r, const char *text)
|
||||
{
|
||||
NSButton *b, *b2;
|
||||
NSLayoutConstraint *constraint;
|
||||
|
||||
b = [[NSButton alloc] initWithFrame:NSZeroRect];
|
||||
[b setTitle:toNSString(text)];
|
||||
[b setButtonType:NSRadioButton];
|
||||
// doesn't seem to have an associated bezel style
|
||||
[b setBordered:NO];
|
||||
[b setTransparent:NO];
|
||||
uiDarwinSetControlFont(b, NSRegularControlSize);
|
||||
[b setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||
|
||||
[b setTarget:r->delegate];
|
||||
[b setAction:@selector(onClicked:)];
|
||||
|
||||
[r->buttons addObject:b];
|
||||
[r->view addSubview:b];
|
||||
|
||||
// pin horizontally to the edges of the superview
|
||||
constraint = mkConstraint(b, NSLayoutAttributeLeading,
|
||||
NSLayoutRelationEqual,
|
||||
r->view, NSLayoutAttributeLeading,
|
||||
1, 0,
|
||||
@"uiRadioButtons button leading constraint");
|
||||
[r->view addConstraint:constraint];
|
||||
[r->constraints addObject:constraint];
|
||||
constraint = mkConstraint(b, NSLayoutAttributeTrailing,
|
||||
NSLayoutRelationEqual,
|
||||
r->view, NSLayoutAttributeTrailing,
|
||||
1, 0,
|
||||
@"uiRadioButtons button trailing constraint");
|
||||
[r->view addConstraint:constraint];
|
||||
[r->constraints addObject:constraint];
|
||||
|
||||
// if this is the first view, pin it to the top
|
||||
// otherwise pin to the bottom of the last
|
||||
if ([r->buttons count] == 1)
|
||||
constraint = mkConstraint(b, NSLayoutAttributeTop,
|
||||
NSLayoutRelationEqual,
|
||||
r->view, NSLayoutAttributeTop,
|
||||
1, 0,
|
||||
@"uiRadioButtons first button top constraint");
|
||||
else {
|
||||
b2 = buttonAt(r, [r->buttons count] - 2);
|
||||
constraint = mkConstraint(b, NSLayoutAttributeTop,
|
||||
NSLayoutRelationEqual,
|
||||
b2, NSLayoutAttributeBottom,
|
||||
1, 0,
|
||||
@"uiRadioButtons non-first button top constraint");
|
||||
}
|
||||
[r->view addConstraint:constraint];
|
||||
[r->constraints addObject:constraint];
|
||||
|
||||
// if there is a previous bottom constraint, remove it
|
||||
if (r->lastv != nil) {
|
||||
[r->view removeConstraint:r->lastv];
|
||||
[r->constraints removeObject:r->lastv];
|
||||
[r->lastv release];
|
||||
}
|
||||
|
||||
// and make the new bottom constraint
|
||||
r->lastv = mkConstraint(b, NSLayoutAttributeBottom,
|
||||
NSLayoutRelationEqual,
|
||||
r->view, NSLayoutAttributeBottom,
|
||||
1, 0,
|
||||
@"uiRadioButtons last button bottom constraint");
|
||||
[r->view addConstraint:r->lastv];
|
||||
[r->constraints addObject:r->lastv];
|
||||
[r->lastv retain];
|
||||
}
|
||||
|
||||
int uiRadioButtonsSelected(uiRadioButtons *r)
|
||||
{
|
||||
NSButton *b;
|
||||
NSUInteger i;
|
||||
|
||||
for (i = 0; i < [r->buttons count]; i++) {
|
||||
b = (NSButton *) [r->buttons objectAtIndex:i];
|
||||
if ([b state] == NSOnState)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void uiRadioButtonsSetSelected(uiRadioButtons *r, int n)
|
||||
{
|
||||
NSButton *b;
|
||||
NSInteger state;
|
||||
|
||||
state = NSOnState;
|
||||
if (n == -1) {
|
||||
n = uiRadioButtonsSelected(r);
|
||||
if (n == -1) // from nothing to nothing; do nothing
|
||||
return;
|
||||
state = NSOffState;
|
||||
}
|
||||
b = (NSButton *) [r->buttons objectAtIndex:n];
|
||||
[b setState:state];
|
||||
}
|
||||
|
||||
void uiRadioButtonsOnSelected(uiRadioButtons *r, void (*f)(uiRadioButtons *, void *), void *data)
|
||||
{
|
||||
r->onSelected = f;
|
||||
r->onSelectedData = data;
|
||||
}
|
||||
|
||||
uiRadioButtons *uiNewRadioButtons(void)
|
||||
{
|
||||
uiRadioButtons *r;
|
||||
|
||||
uiDarwinNewControl(uiRadioButtons, r);
|
||||
|
||||
r->view = [[NSView alloc] initWithFrame:NSZeroRect];
|
||||
r->buttons = [NSMutableArray new];
|
||||
r->constraints = [NSMutableArray new];
|
||||
|
||||
r->delegate = [[radioButtonsDelegate alloc] initWithR:r];
|
||||
|
||||
uiRadioButtonsOnSelected(r, defaultOnSelected, NULL);
|
||||
|
||||
return r;
|
||||
}
|
61
deps/libui/darwin/scrollview.m
vendored
61
deps/libui/darwin/scrollview.m
vendored
@ -1,61 +0,0 @@
|
||||
// 27 may 2016
|
||||
#include "uipriv_darwin.h"
|
||||
|
||||
// see http://stackoverflow.com/questions/37979445/how-do-i-properly-set-up-a-scrolling-nstableview-using-auto-layout-what-ive-tr for why we don't use auto layout
|
||||
// TODO do the same with uiGroup and uiTab?
|
||||
|
||||
struct scrollViewData {
|
||||
BOOL hscroll;
|
||||
BOOL vscroll;
|
||||
};
|
||||
|
||||
NSScrollView *mkScrollView(struct scrollViewCreateParams *p, struct scrollViewData **dout)
|
||||
{
|
||||
NSScrollView *sv;
|
||||
NSBorderType border;
|
||||
struct scrollViewData *d;
|
||||
|
||||
sv = [[NSScrollView alloc] initWithFrame:NSZeroRect];
|
||||
if (p->BackgroundColor != nil)
|
||||
[sv setBackgroundColor:p->BackgroundColor];
|
||||
[sv setDrawsBackground:p->DrawsBackground];
|
||||
border = NSNoBorder;
|
||||
if (p->Bordered)
|
||||
border = NSBezelBorder;
|
||||
// document view seems to set the cursor properly
|
||||
[sv setBorderType:border];
|
||||
[sv setAutohidesScrollers:YES];
|
||||
[sv setHasHorizontalRuler:NO];
|
||||
[sv setHasVerticalRuler:NO];
|
||||
[sv setRulersVisible:NO];
|
||||
[sv setScrollerKnobStyle:NSScrollerKnobStyleDefault];
|
||||
// the scroller style is documented as being set by default for us
|
||||
// LONGTERM verify line and page for programmatically created NSTableView
|
||||
[sv setScrollsDynamically:YES];
|
||||
[sv setFindBarPosition:NSScrollViewFindBarPositionAboveContent];
|
||||
[sv setUsesPredominantAxisScrolling:NO];
|
||||
[sv setHorizontalScrollElasticity:NSScrollElasticityAutomatic];
|
||||
[sv setVerticalScrollElasticity:NSScrollElasticityAutomatic];
|
||||
[sv setAllowsMagnification:NO];
|
||||
|
||||
[sv setDocumentView:p->DocumentView];
|
||||
d = uiNew(struct scrollViewData);
|
||||
scrollViewSetScrolling(sv, d, p->HScroll, p->VScroll);
|
||||
|
||||
*dout = d;
|
||||
return sv;
|
||||
}
|
||||
|
||||
// based on http://blog.bjhomer.com/2014/08/nsscrollview-and-autolayout.html because (as pointed out there) Apple's official guide is really only for iOS
|
||||
void scrollViewSetScrolling(NSScrollView *sv, struct scrollViewData *d, BOOL hscroll, BOOL vscroll)
|
||||
{
|
||||
d->hscroll = hscroll;
|
||||
[sv setHasHorizontalScroller:d->hscroll];
|
||||
d->vscroll = vscroll;
|
||||
[sv setHasVerticalScroller:d->vscroll];
|
||||
}
|
||||
|
||||
void scrollViewFreeData(NSScrollView *sv, struct scrollViewData *d)
|
||||
{
|
||||
uiFree(d);
|
||||
}
|
45
deps/libui/darwin/separator.m
vendored
45
deps/libui/darwin/separator.m
vendored
@ -1,45 +0,0 @@
|
||||
// 14 august 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// TODO make this intrinsic
|
||||
#define separatorWidth 96
|
||||
#define separatorHeight 96
|
||||
|
||||
struct uiSeparator {
|
||||
uiDarwinControl c;
|
||||
NSBox *box;
|
||||
};
|
||||
|
||||
uiDarwinControlAllDefaults(uiSeparator, box)
|
||||
|
||||
uiSeparator *uiNewHorizontalSeparator(void)
|
||||
{
|
||||
uiSeparator *s;
|
||||
|
||||
uiDarwinNewControl(uiSeparator, s);
|
||||
|
||||
// make the initial width >= initial height to force horizontal
|
||||
s->box = [[NSBox alloc] initWithFrame:NSMakeRect(0, 0, 100, 1)];
|
||||
[s->box setBoxType:NSBoxSeparator];
|
||||
[s->box setBorderType:NSGrooveBorder];
|
||||
[s->box setTransparent:NO];
|
||||
[s->box setTitlePosition:NSNoTitle];
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
uiSeparator *uiNewVerticalSeparator(void)
|
||||
{
|
||||
uiSeparator *s;
|
||||
|
||||
uiDarwinNewControl(uiSeparator, s);
|
||||
|
||||
// make the initial height >= initial width to force vertical
|
||||
s->box = [[NSBox alloc] initWithFrame:NSMakeRect(0, 0, 1, 100)];
|
||||
[s->box setBoxType:NSBoxSeparator];
|
||||
[s->box setBorderType:NSGrooveBorder];
|
||||
[s->box setTransparent:NO];
|
||||
[s->box setTitlePosition:NSNoTitle];
|
||||
|
||||
return s;
|
||||
}
|
147
deps/libui/darwin/slider.m
vendored
147
deps/libui/darwin/slider.m
vendored
@ -1,147 +0,0 @@
|
||||
// 14 august 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// Horizontal sliders have no intrinsic width; we'll use the default Interface Builder width for them.
|
||||
// This will also be used for the initial frame size, to ensure the slider is always horizontal (see below).
|
||||
#define sliderWidth 92
|
||||
|
||||
@interface libui_intrinsicWidthNSSlider : NSSlider
|
||||
@end
|
||||
|
||||
@implementation libui_intrinsicWidthNSSlider
|
||||
|
||||
- (NSSize)intrinsicContentSize
|
||||
{
|
||||
NSSize s;
|
||||
|
||||
s = [super intrinsicContentSize];
|
||||
s.width = sliderWidth;
|
||||
return s;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
struct uiSlider {
|
||||
uiDarwinControl c;
|
||||
NSSlider *slider;
|
||||
void (*onChanged)(uiSlider *, void *);
|
||||
void *onChangedData;
|
||||
};
|
||||
|
||||
@interface sliderDelegateClass : NSObject {
|
||||
struct mapTable *sliders;
|
||||
}
|
||||
- (IBAction)onChanged:(id)sender;
|
||||
- (void)registerSlider:(uiSlider *)b;
|
||||
- (void)unregisterSlider:(uiSlider *)b;
|
||||
@end
|
||||
|
||||
@implementation sliderDelegateClass
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
self->sliders = newMap();
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
mapDestroy(self->sliders);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (IBAction)onChanged:(id)sender
|
||||
{
|
||||
uiSlider *s;
|
||||
|
||||
s = (uiSlider *) mapGet(self->sliders, sender);
|
||||
(*(s->onChanged))(s, s->onChangedData);
|
||||
}
|
||||
|
||||
- (void)registerSlider:(uiSlider *)s
|
||||
{
|
||||
mapSet(self->sliders, s->slider, s);
|
||||
[s->slider setTarget:self];
|
||||
[s->slider setAction:@selector(onChanged:)];
|
||||
}
|
||||
|
||||
- (void)unregisterSlider:(uiSlider *)s
|
||||
{
|
||||
[s->slider setTarget:nil];
|
||||
mapDelete(self->sliders, s->slider);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static sliderDelegateClass *sliderDelegate = nil;
|
||||
|
||||
uiDarwinControlAllDefaultsExceptDestroy(uiSlider, slider)
|
||||
|
||||
static void uiSliderDestroy(uiControl *c)
|
||||
{
|
||||
uiSlider *s = uiSlider(c);
|
||||
|
||||
[sliderDelegate unregisterSlider:s];
|
||||
[s->slider release];
|
||||
uiFreeControl(uiControl(s));
|
||||
}
|
||||
|
||||
int uiSliderValue(uiSlider *s)
|
||||
{
|
||||
return [s->slider integerValue];
|
||||
}
|
||||
|
||||
void uiSliderSetValue(uiSlider *s, int value)
|
||||
{
|
||||
[s->slider setIntegerValue:value];
|
||||
}
|
||||
|
||||
void uiSliderOnChanged(uiSlider *s, void (*f)(uiSlider *, void *), void *data)
|
||||
{
|
||||
s->onChanged = f;
|
||||
s->onChangedData = data;
|
||||
}
|
||||
|
||||
static void defaultOnChanged(uiSlider *s, void *data)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
uiSlider *uiNewSlider(int min, int max)
|
||||
{
|
||||
uiSlider *s;
|
||||
NSSliderCell *cell;
|
||||
int temp;
|
||||
|
||||
if (min >= max) {
|
||||
temp = min;
|
||||
min = max;
|
||||
max = temp;
|
||||
}
|
||||
|
||||
uiDarwinNewControl(uiSlider, s);
|
||||
|
||||
// a horizontal slider is defined as one where the width > height, not by a flag
|
||||
// to be safe, don't use NSZeroRect, but make it horizontal from the get-go
|
||||
s->slider = [[libui_intrinsicWidthNSSlider alloc]
|
||||
initWithFrame:NSMakeRect(0, 0, sliderWidth, 2)];
|
||||
[s->slider setMinValue:min];
|
||||
[s->slider setMaxValue:max];
|
||||
[s->slider setAllowsTickMarkValuesOnly:NO];
|
||||
[s->slider setNumberOfTickMarks:0];
|
||||
[s->slider setTickMarkPosition:NSTickMarkAbove];
|
||||
|
||||
cell = (NSSliderCell *) [s->slider cell];
|
||||
[cell setSliderType:NSLinearSlider];
|
||||
|
||||
if (sliderDelegate == nil) {
|
||||
sliderDelegate = [[sliderDelegateClass new] autorelease];
|
||||
[delegates addObject:sliderDelegate];
|
||||
}
|
||||
[sliderDelegate registerSlider:s];
|
||||
uiSliderOnChanged(s, defaultOnChanged, NULL);
|
||||
|
||||
return s;
|
||||
}
|
214
deps/libui/darwin/spinbox.m
vendored
214
deps/libui/darwin/spinbox.m
vendored
@ -1,214 +0,0 @@
|
||||
// 14 august 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
@interface libui_spinbox : NSView<NSTextFieldDelegate> {
|
||||
NSTextField *tf;
|
||||
NSNumberFormatter *formatter;
|
||||
NSStepper *stepper;
|
||||
|
||||
NSInteger value;
|
||||
NSInteger minimum;
|
||||
NSInteger maximum;
|
||||
|
||||
uiSpinbox *spinbox;
|
||||
}
|
||||
- (id)initWithFrame:(NSRect)r spinbox:(uiSpinbox *)sb;
|
||||
// see https://github.com/andlabs/ui/issues/82
|
||||
- (NSInteger)libui_value;
|
||||
- (void)libui_setValue:(NSInteger)val;
|
||||
- (void)setMinimum:(NSInteger)min;
|
||||
- (void)setMaximum:(NSInteger)max;
|
||||
- (IBAction)stepperClicked:(id)sender;
|
||||
- (void)controlTextDidChange:(NSNotification *)note;
|
||||
@end
|
||||
|
||||
struct uiSpinbox {
|
||||
uiDarwinControl c;
|
||||
libui_spinbox *spinbox;
|
||||
void (*onChanged)(uiSpinbox *, void *);
|
||||
void *onChangedData;
|
||||
};
|
||||
|
||||
// yes folks, this varies by operating system! woo!
|
||||
// 10.10 started drawing the NSStepper one point too low, so we have to fix it up conditionally
|
||||
// TODO test this; we'll probably have to substitute 10_9
|
||||
static CGFloat stepperYDelta(void)
|
||||
{
|
||||
// via https://developer.apple.com/library/mac/releasenotes/AppKit/RN-AppKit/
|
||||
if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_9)
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
@implementation libui_spinbox
|
||||
|
||||
- (id)initWithFrame:(NSRect)r spinbox:(uiSpinbox *)sb
|
||||
{
|
||||
self = [super initWithFrame:r];
|
||||
if (self) {
|
||||
self->tf = newEditableTextField();
|
||||
[self->tf setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||
|
||||
self->formatter = [NSNumberFormatter new];
|
||||
[self->formatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
|
||||
[self->formatter setLocalizesFormat:NO];
|
||||
[self->formatter setUsesGroupingSeparator:NO];
|
||||
[self->formatter setHasThousandSeparators:NO];
|
||||
[self->formatter setAllowsFloats:NO];
|
||||
[self->tf setFormatter:self->formatter];
|
||||
|
||||
self->stepper = [[NSStepper alloc] initWithFrame:NSZeroRect];
|
||||
[self->stepper setIncrement:1];
|
||||
[self->stepper setValueWraps:NO];
|
||||
[self->stepper setAutorepeat:YES]; // hold mouse button to step repeatedly
|
||||
[self->stepper setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||
|
||||
[self->tf setDelegate:self];
|
||||
[self->stepper setTarget:self];
|
||||
[self->stepper setAction:@selector(stepperClicked:)];
|
||||
|
||||
[self addSubview:self->tf];
|
||||
[self addSubview:self->stepper];
|
||||
|
||||
[self addConstraint:mkConstraint(self->tf, NSLayoutAttributeLeading,
|
||||
NSLayoutRelationEqual,
|
||||
self, NSLayoutAttributeLeading,
|
||||
1, 0,
|
||||
@"uiSpinbox left edge")];
|
||||
[self addConstraint:mkConstraint(self->stepper, NSLayoutAttributeTrailing,
|
||||
NSLayoutRelationEqual,
|
||||
self, NSLayoutAttributeTrailing,
|
||||
1, 0,
|
||||
@"uiSpinbox right edge")];
|
||||
[self addConstraint:mkConstraint(self->tf, NSLayoutAttributeTop,
|
||||
NSLayoutRelationEqual,
|
||||
self, NSLayoutAttributeTop,
|
||||
1, 0,
|
||||
@"uiSpinbox top edge text field")];
|
||||
[self addConstraint:mkConstraint(self->tf, NSLayoutAttributeBottom,
|
||||
NSLayoutRelationEqual,
|
||||
self, NSLayoutAttributeBottom,
|
||||
1, 0,
|
||||
@"uiSpinbox bottom edge text field")];
|
||||
[self addConstraint:mkConstraint(self->stepper, NSLayoutAttributeTop,
|
||||
NSLayoutRelationEqual,
|
||||
self, NSLayoutAttributeTop,
|
||||
1, stepperYDelta(),
|
||||
@"uiSpinbox top edge stepper")];
|
||||
[self addConstraint:mkConstraint(self->stepper, NSLayoutAttributeBottom,
|
||||
NSLayoutRelationEqual,
|
||||
self, NSLayoutAttributeBottom,
|
||||
1, stepperYDelta(),
|
||||
@"uiSpinbox bottom edge stepper")];
|
||||
[self addConstraint:mkConstraint(self->tf, NSLayoutAttributeTrailing,
|
||||
NSLayoutRelationEqual,
|
||||
self->stepper, NSLayoutAttributeLeading,
|
||||
1, -3, // arbitrary amount; good enough visually (and it seems to match NSDatePicker too, at least on 10.11, which is even better)
|
||||
@"uiSpinbox space between text field and stepper")];
|
||||
|
||||
self->spinbox = sb;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[self->tf setDelegate:nil];
|
||||
[self->tf removeFromSuperview];
|
||||
[self->tf release];
|
||||
[self->formatter release];
|
||||
[self->stepper setTarget:nil];
|
||||
[self->stepper removeFromSuperview];
|
||||
[self->stepper release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (NSInteger)libui_value
|
||||
{
|
||||
return self->value;
|
||||
}
|
||||
|
||||
- (void)libui_setValue:(NSInteger)val
|
||||
{
|
||||
self->value = val;
|
||||
if (self->value < self->minimum)
|
||||
self->value = self->minimum;
|
||||
if (self->value > self->maximum)
|
||||
self->value = self->maximum;
|
||||
[self->tf setIntegerValue:self->value];
|
||||
[self->stepper setIntegerValue:self->value];
|
||||
}
|
||||
|
||||
- (void)setMinimum:(NSInteger)min
|
||||
{
|
||||
self->minimum = min;
|
||||
[self->formatter setMinimum:[NSNumber numberWithInteger:self->minimum]];
|
||||
[self->stepper setMinValue:((double) (self->minimum))];
|
||||
}
|
||||
|
||||
- (void)setMaximum:(NSInteger)max
|
||||
{
|
||||
self->maximum = max;
|
||||
[self->formatter setMaximum:[NSNumber numberWithInteger:self->maximum]];
|
||||
[self->stepper setMaxValue:((double) (self->maximum))];
|
||||
}
|
||||
|
||||
- (IBAction)stepperClicked:(id)sender
|
||||
{
|
||||
[self libui_setValue:[self->stepper integerValue]];
|
||||
(*(self->spinbox->onChanged))(self->spinbox, self->spinbox->onChangedData);
|
||||
}
|
||||
|
||||
- (void)controlTextDidChange:(NSNotification *)note
|
||||
{
|
||||
[self libui_setValue:[self->tf integerValue]];
|
||||
(*(self->spinbox->onChanged))(self->spinbox, self->spinbox->onChangedData);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
uiDarwinControlAllDefaults(uiSpinbox, spinbox)
|
||||
|
||||
int uiSpinboxValue(uiSpinbox *s)
|
||||
{
|
||||
return [s->spinbox libui_value];
|
||||
}
|
||||
|
||||
void uiSpinboxSetValue(uiSpinbox *s, int value)
|
||||
{
|
||||
[s->spinbox libui_setValue:value];
|
||||
}
|
||||
|
||||
void uiSpinboxOnChanged(uiSpinbox *s, void (*f)(uiSpinbox *, void *), void *data)
|
||||
{
|
||||
s->onChanged = f;
|
||||
s->onChangedData = data;
|
||||
}
|
||||
|
||||
static void defaultOnChanged(uiSpinbox *s, void *data)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
uiSpinbox *uiNewSpinbox(int min, int max)
|
||||
{
|
||||
uiSpinbox *s;
|
||||
int temp;
|
||||
|
||||
if (min >= max) {
|
||||
temp = min;
|
||||
min = max;
|
||||
max = temp;
|
||||
}
|
||||
|
||||
uiDarwinNewControl(uiSpinbox, s);
|
||||
|
||||
s->spinbox = [[libui_spinbox alloc] initWithFrame:NSZeroRect spinbox:s];
|
||||
[s->spinbox setMinimum:min];
|
||||
[s->spinbox setMaximum:max];
|
||||
[s->spinbox libui_setValue:min];
|
||||
|
||||
uiSpinboxOnChanged(s, defaultOnChanged, NULL);
|
||||
|
||||
return s;
|
||||
}
|
123
deps/libui/darwin/stddialogs.m
vendored
123
deps/libui/darwin/stddialogs.m
vendored
@ -1,123 +0,0 @@
|
||||
// 26 june 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// LONGTERM restructure this whole file
|
||||
// LONGTERM explicitly document this works as we want
|
||||
// LONGTERM note that font and color buttons also do this
|
||||
|
||||
#define windowWindow(w) ((NSWindow *) uiControlHandle(uiControl(w)))
|
||||
|
||||
// source of code modal logic: http://stackoverflow.com/questions/604768/wait-for-nsalert-beginsheetmodalforwindow
|
||||
|
||||
// note: whether extensions are actually shown depends on a user setting in Finder; we can't control it here
|
||||
static void setupSavePanel(NSSavePanel *s)
|
||||
{
|
||||
[s setCanCreateDirectories:YES];
|
||||
[s setShowsHiddenFiles:YES];
|
||||
[s setExtensionHidden:NO];
|
||||
[s setCanSelectHiddenExtension:NO];
|
||||
[s setTreatsFilePackagesAsDirectories:YES];
|
||||
}
|
||||
|
||||
static char *runSavePanel(NSWindow *parent, NSSavePanel *s)
|
||||
{
|
||||
char *filename;
|
||||
|
||||
[s beginSheetModalForWindow:parent completionHandler:^(NSInteger result) {
|
||||
[realNSApp() stopModalWithCode:result];
|
||||
}];
|
||||
if ([realNSApp() runModalForWindow:s] != NSFileHandlingPanelOKButton)
|
||||
return NULL;
|
||||
filename = uiDarwinNSStringToText([[s URL] path]);
|
||||
return filename;
|
||||
}
|
||||
|
||||
char *uiOpenFile(uiWindow *parent)
|
||||
{
|
||||
NSOpenPanel *o;
|
||||
|
||||
o = [NSOpenPanel openPanel];
|
||||
[o setCanChooseFiles:YES];
|
||||
[o setCanChooseDirectories:NO];
|
||||
[o setResolvesAliases:NO];
|
||||
[o setAllowsMultipleSelection:NO];
|
||||
setupSavePanel(o);
|
||||
// panel is autoreleased
|
||||
return runSavePanel(windowWindow(parent), o);
|
||||
}
|
||||
|
||||
char *uiSaveFile(uiWindow *parent)
|
||||
{
|
||||
NSSavePanel *s;
|
||||
|
||||
s = [NSSavePanel savePanel];
|
||||
setupSavePanel(s);
|
||||
// panel is autoreleased
|
||||
return runSavePanel(windowWindow(parent), s);
|
||||
}
|
||||
|
||||
// I would use a completion handler for NSAlert as well, but alas NSAlert's are 10.9 and higher only
|
||||
@interface libuiCodeModalAlertPanel : NSObject {
|
||||
NSAlert *panel;
|
||||
NSWindow *parent;
|
||||
}
|
||||
- (id)initWithPanel:(NSAlert *)p parent:(NSWindow *)w;
|
||||
- (NSInteger)run;
|
||||
- (void)panelEnded:(NSAlert *)panel result:(NSInteger)result data:(void *)data;
|
||||
@end
|
||||
|
||||
@implementation libuiCodeModalAlertPanel
|
||||
|
||||
- (id)initWithPanel:(NSAlert *)p parent:(NSWindow *)w
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self->panel = p;
|
||||
self->parent = w;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSInteger)run
|
||||
{
|
||||
[self->panel beginSheetModalForWindow:self->parent
|
||||
modalDelegate:self
|
||||
didEndSelector:@selector(panelEnded:result:data:)
|
||||
contextInfo:NULL];
|
||||
return [realNSApp() runModalForWindow:[self->panel window]];
|
||||
}
|
||||
|
||||
- (void)panelEnded:(NSAlert *)panel result:(NSInteger)result data:(void *)data
|
||||
{
|
||||
[realNSApp() stopModalWithCode:result];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static void msgbox(NSWindow *parent, const char *title, const char *description, NSAlertStyle style)
|
||||
{
|
||||
NSAlert *a;
|
||||
libuiCodeModalAlertPanel *cm;
|
||||
|
||||
a = [NSAlert new];
|
||||
[a setAlertStyle:style];
|
||||
[a setShowsHelp:NO];
|
||||
[a setShowsSuppressionButton:NO];
|
||||
[a setMessageText:toNSString(title)];
|
||||
[a setInformativeText:toNSString(description)];
|
||||
[a addButtonWithTitle:@"OK"];
|
||||
cm = [[libuiCodeModalAlertPanel alloc] initWithPanel:a parent:parent];
|
||||
[cm run];
|
||||
[cm release];
|
||||
[a release];
|
||||
}
|
||||
|
||||
void uiMsgBox(uiWindow *parent, const char *title, const char *description)
|
||||
{
|
||||
msgbox(windowWindow(parent), title, description, NSInformationalAlertStyle);
|
||||
}
|
||||
|
||||
void uiMsgBoxError(uiWindow *parent, const char *title, const char *description)
|
||||
{
|
||||
msgbox(windowWindow(parent), title, description, NSCriticalAlertStyle);
|
||||
}
|
292
deps/libui/darwin/tab.m
vendored
292
deps/libui/darwin/tab.m
vendored
@ -1,292 +0,0 @@
|
||||
// 15 august 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// TODO need to jiggle on tab change too (second page disabled tab label initially ambiguous)
|
||||
|
||||
@interface tabPage : NSObject {
|
||||
struct singleChildConstraints constraints;
|
||||
int margined;
|
||||
NSView *view; // the NSTabViewItem view itself
|
||||
NSObject *pageID;
|
||||
}
|
||||
@property uiControl *c;
|
||||
@property NSLayoutPriority oldHorzHuggingPri;
|
||||
@property NSLayoutPriority oldVertHuggingPri;
|
||||
- (id)initWithView:(NSView *)v pageID:(NSObject *)o;
|
||||
- (NSView *)childView;
|
||||
- (void)establishChildConstraints;
|
||||
- (void)removeChildConstraints;
|
||||
- (int)isMargined;
|
||||
- (void)setMargined:(int)m;
|
||||
@end
|
||||
|
||||
struct uiTab {
|
||||
uiDarwinControl c;
|
||||
NSTabView *tabview;
|
||||
NSMutableArray *pages;
|
||||
NSLayoutPriority horzHuggingPri;
|
||||
NSLayoutPriority vertHuggingPri;
|
||||
};
|
||||
|
||||
@implementation tabPage
|
||||
|
||||
- (id)initWithView:(NSView *)v pageID:(NSObject *)o
|
||||
{
|
||||
self = [super init];
|
||||
if (self != nil) {
|
||||
self->view = [v retain];
|
||||
self->pageID = [o retain];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[self removeChildConstraints];
|
||||
[self->view release];
|
||||
[self->pageID release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (NSView *)childView
|
||||
{
|
||||
return (NSView *) uiControlHandle(self.c);
|
||||
}
|
||||
|
||||
- (void)establishChildConstraints
|
||||
{
|
||||
[self removeChildConstraints];
|
||||
if (self.c == NULL)
|
||||
return;
|
||||
singleChildConstraintsEstablish(&(self->constraints),
|
||||
self->view, [self childView],
|
||||
uiDarwinControlHugsTrailingEdge(uiDarwinControl(self.c)),
|
||||
uiDarwinControlHugsBottom(uiDarwinControl(self.c)),
|
||||
self->margined,
|
||||
@"uiTab page");
|
||||
}
|
||||
|
||||
- (void)removeChildConstraints
|
||||
{
|
||||
singleChildConstraintsRemove(&(self->constraints), self->view);
|
||||
}
|
||||
|
||||
- (int)isMargined
|
||||
{
|
||||
return self->margined;
|
||||
}
|
||||
|
||||
- (void)setMargined:(int)m
|
||||
{
|
||||
self->margined = m;
|
||||
singleChildConstraintsSetMargined(&(self->constraints), self->margined);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static void uiTabDestroy(uiControl *c)
|
||||
{
|
||||
uiTab *t = uiTab(c);
|
||||
tabPage *page;
|
||||
|
||||
// first remove all tab pages so we can destroy all the children
|
||||
while ([t->tabview numberOfTabViewItems] != 0)
|
||||
[t->tabview removeTabViewItem:[t->tabview tabViewItemAtIndex:0]];
|
||||
// then destroy all the children
|
||||
for (page in t->pages) {
|
||||
[page removeChildConstraints];
|
||||
uiControlSetParent(page.c, NULL);
|
||||
uiDarwinControlSetSuperview(uiDarwinControl(page.c), nil);
|
||||
uiControlDestroy(page.c);
|
||||
}
|
||||
// and finally destroy ourselves
|
||||
[t->pages release];
|
||||
[t->tabview release];
|
||||
uiFreeControl(uiControl(t));
|
||||
}
|
||||
|
||||
uiDarwinControlDefaultHandle(uiTab, tabview)
|
||||
uiDarwinControlDefaultParent(uiTab, tabview)
|
||||
uiDarwinControlDefaultSetParent(uiTab, tabview)
|
||||
uiDarwinControlDefaultToplevel(uiTab, tabview)
|
||||
uiDarwinControlDefaultVisible(uiTab, tabview)
|
||||
uiDarwinControlDefaultShow(uiTab, tabview)
|
||||
uiDarwinControlDefaultHide(uiTab, tabview)
|
||||
uiDarwinControlDefaultEnabled(uiTab, tabview)
|
||||
uiDarwinControlDefaultEnable(uiTab, tabview)
|
||||
uiDarwinControlDefaultDisable(uiTab, tabview)
|
||||
|
||||
static void uiTabSyncEnableState(uiDarwinControl *c, int enabled)
|
||||
{
|
||||
uiTab *t = uiTab(c);
|
||||
tabPage *page;
|
||||
|
||||
if (uiDarwinShouldStopSyncEnableState(uiDarwinControl(t), enabled))
|
||||
return;
|
||||
for (page in t->pages)
|
||||
uiDarwinControlSyncEnableState(uiDarwinControl(page.c), enabled);
|
||||
}
|
||||
|
||||
uiDarwinControlDefaultSetSuperview(uiTab, tabview)
|
||||
|
||||
static void tabRelayout(uiTab *t)
|
||||
{
|
||||
tabPage *page;
|
||||
|
||||
for (page in t->pages)
|
||||
[page establishChildConstraints];
|
||||
// and this gets rid of some weird issues with regards to box alignment
|
||||
jiggleViewLayout(t->tabview);
|
||||
}
|
||||
|
||||
BOOL uiTabHugsTrailingEdge(uiDarwinControl *c)
|
||||
{
|
||||
uiTab *t = uiTab(c);
|
||||
|
||||
return t->horzHuggingPri < NSLayoutPriorityWindowSizeStayPut;
|
||||
}
|
||||
|
||||
BOOL uiTabHugsBottom(uiDarwinControl *c)
|
||||
{
|
||||
uiTab *t = uiTab(c);
|
||||
|
||||
return t->vertHuggingPri < NSLayoutPriorityWindowSizeStayPut;
|
||||
}
|
||||
|
||||
static void uiTabChildEdgeHuggingChanged(uiDarwinControl *c)
|
||||
{
|
||||
uiTab *t = uiTab(c);
|
||||
|
||||
tabRelayout(t);
|
||||
}
|
||||
|
||||
static NSLayoutPriority uiTabHuggingPriority(uiDarwinControl *c, NSLayoutConstraintOrientation orientation)
|
||||
{
|
||||
uiTab *t = uiTab(c);
|
||||
|
||||
if (orientation == NSLayoutConstraintOrientationHorizontal)
|
||||
return t->horzHuggingPri;
|
||||
return t->vertHuggingPri;
|
||||
}
|
||||
|
||||
static void uiTabSetHuggingPriority(uiDarwinControl *c, NSLayoutPriority priority, NSLayoutConstraintOrientation orientation)
|
||||
{
|
||||
uiTab *t = uiTab(c);
|
||||
|
||||
if (orientation == NSLayoutConstraintOrientationHorizontal)
|
||||
t->horzHuggingPri = priority;
|
||||
else
|
||||
t->vertHuggingPri = priority;
|
||||
uiDarwinNotifyEdgeHuggingChanged(uiDarwinControl(t));
|
||||
}
|
||||
|
||||
static void uiTabChildVisibilityChanged(uiDarwinControl *c)
|
||||
{
|
||||
uiTab *t = uiTab(c);
|
||||
|
||||
tabRelayout(t);
|
||||
}
|
||||
|
||||
void uiTabAppend(uiTab *t, const char *name, uiControl *child)
|
||||
{
|
||||
uiTabInsertAt(t, name, [t->pages count], child);
|
||||
}
|
||||
|
||||
void uiTabInsertAt(uiTab *t, const char *name, int n, uiControl *child)
|
||||
{
|
||||
tabPage *page;
|
||||
NSView *view;
|
||||
NSTabViewItem *i;
|
||||
NSObject *pageID;
|
||||
|
||||
uiControlSetParent(child, uiControl(t));
|
||||
|
||||
view = [[[NSView alloc] initWithFrame:NSZeroRect] autorelease];
|
||||
// note: if we turn off the autoresizing mask, nothing shows up
|
||||
uiDarwinControlSetSuperview(uiDarwinControl(child), view);
|
||||
uiDarwinControlSyncEnableState(uiDarwinControl(child), uiControlEnabledToUser(uiControl(t)));
|
||||
|
||||
// the documentation says these can be nil but the headers say these must not be; let's be safe and make them non-nil anyway
|
||||
pageID = [NSObject new];
|
||||
page = [[[tabPage alloc] initWithView:view pageID:pageID] autorelease];
|
||||
page.c = child;
|
||||
|
||||
// don't hug, just in case we're a stretchy tab
|
||||
page.oldHorzHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(page.c), NSLayoutConstraintOrientationHorizontal);
|
||||
page.oldVertHuggingPri = uiDarwinControlHuggingPriority(uiDarwinControl(page.c), NSLayoutConstraintOrientationVertical);
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(page.c), NSLayoutPriorityDefaultLow, NSLayoutConstraintOrientationHorizontal);
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(page.c), NSLayoutPriorityDefaultLow, NSLayoutConstraintOrientationVertical);
|
||||
|
||||
[t->pages insertObject:page atIndex:n];
|
||||
|
||||
i = [[[NSTabViewItem alloc] initWithIdentifier:pageID] autorelease];
|
||||
[i setLabel:toNSString(name)];
|
||||
[i setView:view];
|
||||
[t->tabview insertTabViewItem:i atIndex:n];
|
||||
|
||||
tabRelayout(t);
|
||||
}
|
||||
|
||||
void uiTabDelete(uiTab *t, int n)
|
||||
{
|
||||
tabPage *page;
|
||||
uiControl *child;
|
||||
NSTabViewItem *i;
|
||||
|
||||
page = (tabPage *) [t->pages objectAtIndex:n];
|
||||
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(page.c), page.oldHorzHuggingPri, NSLayoutConstraintOrientationHorizontal);
|
||||
uiDarwinControlSetHuggingPriority(uiDarwinControl(page.c), page.oldVertHuggingPri, NSLayoutConstraintOrientationVertical);
|
||||
|
||||
child = page.c;
|
||||
[page removeChildConstraints];
|
||||
[t->pages removeObjectAtIndex:n];
|
||||
|
||||
uiControlSetParent(child, NULL);
|
||||
uiDarwinControlSetSuperview(uiDarwinControl(child), nil);
|
||||
|
||||
i = [t->tabview tabViewItemAtIndex:n];
|
||||
[t->tabview removeTabViewItem:i];
|
||||
|
||||
tabRelayout(t);
|
||||
}
|
||||
|
||||
int uiTabNumPages(uiTab *t)
|
||||
{
|
||||
return [t->pages count];
|
||||
}
|
||||
|
||||
int uiTabMargined(uiTab *t, int n)
|
||||
{
|
||||
tabPage *page;
|
||||
|
||||
page = (tabPage *) [t->pages objectAtIndex:n];
|
||||
return [page isMargined];
|
||||
}
|
||||
|
||||
void uiTabSetMargined(uiTab *t, int n, int margined)
|
||||
{
|
||||
tabPage *page;
|
||||
|
||||
page = (tabPage *) [t->pages objectAtIndex:n];
|
||||
[page setMargined:margined];
|
||||
}
|
||||
|
||||
uiTab *uiNewTab(void)
|
||||
{
|
||||
uiTab *t;
|
||||
|
||||
uiDarwinNewControl(uiTab, t);
|
||||
|
||||
t->tabview = [[NSTabView alloc] initWithFrame:NSZeroRect];
|
||||
// also good for NSTabView (same selector and everything)
|
||||
uiDarwinSetControlFont((NSControl *) (t->tabview), NSRegularControlSize);
|
||||
|
||||
t->pages = [NSMutableArray new];
|
||||
|
||||
// default to low hugging to not hug edges
|
||||
t->horzHuggingPri = NSLayoutPriorityDefaultLow;
|
||||
t->vertHuggingPri = NSLayoutPriorityDefaultLow;
|
||||
|
||||
return t;
|
||||
}
|
19
deps/libui/darwin/text.m
vendored
19
deps/libui/darwin/text.m
vendored
@ -1,19 +0,0 @@
|
||||
// 10 april 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
char *uiDarwinNSStringToText(NSString *s)
|
||||
{
|
||||
char *out;
|
||||
|
||||
out = strdup([s UTF8String]);
|
||||
if (out == NULL) {
|
||||
fprintf(stderr, "memory exhausted in uiDarwinNSStringToText()\n");
|
||||
abort();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
void uiFreeText(char *s)
|
||||
{
|
||||
free(s);
|
||||
}
|
146
deps/libui/darwin/uipriv_darwin.h
vendored
146
deps/libui/darwin/uipriv_darwin.h
vendored
@ -1,146 +0,0 @@
|
||||
// 6 january 2015
|
||||
#define MAC_OS_X_VERSION_MIN_REQUIRED MAC_OS_X_VERSION_10_8
|
||||
#define MAC_OS_X_VERSION_MAX_ALLOWED MAC_OS_X_VERSION_10_8
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "../ui.h"
|
||||
#import "../ui_darwin.h"
|
||||
#import "../common/uipriv.h"
|
||||
|
||||
#if __has_feature(objc_arc)
|
||||
#error Sorry, libui cannot be compiled with ARC.
|
||||
#endif
|
||||
|
||||
#define toNSString(str) [NSString stringWithUTF8String:(str)]
|
||||
#define fromNSString(str) [(str) UTF8String]
|
||||
|
||||
#ifndef NSAppKitVersionNumber10_9
|
||||
#define NSAppKitVersionNumber10_9 1265
|
||||
#endif
|
||||
|
||||
/*TODO remove this*/typedef struct uiImage uiImage;
|
||||
|
||||
// menu.m
|
||||
@interface menuManager : NSObject {
|
||||
struct mapTable *items;
|
||||
BOOL hasQuit;
|
||||
BOOL hasPreferences;
|
||||
BOOL hasAbout;
|
||||
}
|
||||
@property (strong) NSMenuItem *quitItem;
|
||||
@property (strong) NSMenuItem *preferencesItem;
|
||||
@property (strong) NSMenuItem *aboutItem;
|
||||
// NSMenuValidation is only informal
|
||||
- (BOOL)validateMenuItem:(NSMenuItem *)item;
|
||||
- (NSMenu *)makeMenubar;
|
||||
@end
|
||||
extern void finalizeMenus(void);
|
||||
extern void uninitMenus(void);
|
||||
|
||||
// main.m
|
||||
@interface applicationClass : NSApplication
|
||||
@end
|
||||
// this is needed because NSApp is of type id, confusing clang
|
||||
#define realNSApp() ((applicationClass *) NSApp)
|
||||
@interface appDelegate : NSObject <NSApplicationDelegate>
|
||||
@property (strong) menuManager *menuManager;
|
||||
@end
|
||||
#define appDelegate() ((appDelegate *) [realNSApp() delegate])
|
||||
struct nextEventArgs {
|
||||
NSEventMask mask;
|
||||
NSDate *duration;
|
||||
// LONGTERM no NSRunLoopMode?
|
||||
NSString *mode;
|
||||
BOOL dequeue;
|
||||
};
|
||||
extern int mainStep(struct nextEventArgs *nea, BOOL (^interceptEvent)(NSEvent *));
|
||||
|
||||
// util.m
|
||||
extern void disableAutocorrect(NSTextView *);
|
||||
|
||||
// entry.m
|
||||
extern void finishNewTextField(NSTextField *, BOOL);
|
||||
extern NSTextField *newEditableTextField(void);
|
||||
|
||||
// window.m
|
||||
@interface libuiNSWindow : NSWindow
|
||||
- (void)libui_doMove:(NSEvent *)initialEvent;
|
||||
- (void)libui_doResize:(NSEvent *)initialEvent on:(uiWindowResizeEdge)edge;
|
||||
@end
|
||||
extern uiWindow *windowFromNSWindow(NSWindow *);
|
||||
|
||||
// alloc.m
|
||||
extern NSMutableArray *delegates;
|
||||
extern void initAlloc(void);
|
||||
extern void uninitAlloc(void);
|
||||
|
||||
// autolayout.m
|
||||
extern NSLayoutConstraint *mkConstraint(id view1, NSLayoutAttribute attr1, NSLayoutRelation relation, id view2, NSLayoutAttribute attr2, CGFloat multiplier, CGFloat c, NSString *desc);
|
||||
extern void jiggleViewLayout(NSView *view);
|
||||
struct singleChildConstraints {
|
||||
NSLayoutConstraint *leadingConstraint;
|
||||
NSLayoutConstraint *topConstraint;
|
||||
NSLayoutConstraint *trailingConstraintGreater;
|
||||
NSLayoutConstraint *trailingConstraintEqual;
|
||||
NSLayoutConstraint *bottomConstraintGreater;
|
||||
NSLayoutConstraint *bottomConstraintEqual;
|
||||
};
|
||||
extern void singleChildConstraintsEstablish(struct singleChildConstraints *c, NSView *contentView, NSView *childView, BOOL hugsTrailing, BOOL hugsBottom, int margined, NSString *desc);
|
||||
extern void singleChildConstraintsRemove(struct singleChildConstraints *c, NSView *cv);
|
||||
extern void singleChildConstraintsSetMargined(struct singleChildConstraints *c, int margined);
|
||||
|
||||
// map.m
|
||||
extern struct mapTable *newMap(void);
|
||||
extern void mapDestroy(struct mapTable *m);
|
||||
extern void *mapGet(struct mapTable *m, void *key);
|
||||
extern void mapSet(struct mapTable *m, void *key, void *value);
|
||||
extern void mapDelete(struct mapTable *m, void *key);
|
||||
extern void mapWalk(struct mapTable *m, void (*f)(void *key, void *value));
|
||||
extern void mapReset(struct mapTable *m);
|
||||
|
||||
// area.m
|
||||
extern int sendAreaEvents(NSEvent *);
|
||||
|
||||
// areaevents.m
|
||||
extern BOOL fromKeycode(unsigned short keycode, uiAreaKeyEvent *ke);
|
||||
extern BOOL keycodeModifier(unsigned short keycode, uiModifiers *mod);
|
||||
|
||||
// draw.m
|
||||
extern uiDrawContext *newContext(CGContextRef, CGFloat);
|
||||
extern void freeContext(uiDrawContext *);
|
||||
|
||||
// drawtext.m
|
||||
extern uiDrawTextFont *mkTextFont(CTFontRef f, BOOL retain);
|
||||
extern uiDrawTextFont *mkTextFontFromNSFont(NSFont *f);
|
||||
extern void doDrawText(CGContextRef c, CGFloat cheight, double x, double y, uiDrawTextLayout *layout);
|
||||
|
||||
// fontbutton.m
|
||||
extern BOOL fontButtonInhibitSendAction(SEL sel, id from, id to);
|
||||
extern BOOL fontButtonOverrideTargetForAction(SEL sel, id from, id to, id *override);
|
||||
extern void setupFontPanel(void);
|
||||
|
||||
// colorbutton.m
|
||||
extern BOOL colorButtonInhibitSendAction(SEL sel, id from, id to);
|
||||
|
||||
// scrollview.m
|
||||
struct scrollViewCreateParams {
|
||||
NSView *DocumentView;
|
||||
NSColor *BackgroundColor;
|
||||
BOOL DrawsBackground;
|
||||
BOOL Bordered;
|
||||
BOOL HScroll;
|
||||
BOOL VScroll;
|
||||
};
|
||||
struct scrollViewData;
|
||||
extern NSScrollView *mkScrollView(struct scrollViewCreateParams *p, struct scrollViewData **dout);
|
||||
extern void scrollViewSetScrolling(NSScrollView *sv, struct scrollViewData *d, BOOL hscroll, BOOL vscroll);
|
||||
extern void scrollViewFreeData(NSScrollView *sv, struct scrollViewData *d);
|
||||
|
||||
// label.m
|
||||
extern NSTextField *newLabel(NSString *str);
|
||||
|
||||
// image.m
|
||||
extern NSImage *imageImage(uiImage *);
|
||||
|
||||
// winmoveresize.m
|
||||
extern void doManualMove(NSWindow *w, NSEvent *initialEvent);
|
||||
extern void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdge edge);
|
15
deps/libui/darwin/util.m
vendored
15
deps/libui/darwin/util.m
vendored
@ -1,15 +0,0 @@
|
||||
// 7 april 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// LONGTERM do we really want to do this? make it an option?
|
||||
void disableAutocorrect(NSTextView *tv)
|
||||
{
|
||||
[tv setEnabledTextCheckingTypes:0];
|
||||
[tv setAutomaticDashSubstitutionEnabled:NO];
|
||||
// don't worry about automatic data detection; it won't change stringValue (thanks pretty_function in irc.freenode.net/#macdev)
|
||||
[tv setAutomaticSpellingCorrectionEnabled:NO];
|
||||
[tv setAutomaticTextReplacementEnabled:NO];
|
||||
[tv setAutomaticQuoteSubstitutionEnabled:NO];
|
||||
[tv setAutomaticLinkDetectionEnabled:NO];
|
||||
[tv setSmartInsertDeleteEnabled:NO];
|
||||
}
|
407
deps/libui/darwin/window.m
vendored
407
deps/libui/darwin/window.m
vendored
@ -1,407 +0,0 @@
|
||||
// 15 august 2015
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
#define defaultStyleMask (NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)
|
||||
|
||||
struct uiWindow {
|
||||
uiDarwinControl c;
|
||||
NSWindow *window;
|
||||
uiControl *child;
|
||||
int margined;
|
||||
int (*onClosing)(uiWindow *, void *);
|
||||
void *onClosingData;
|
||||
struct singleChildConstraints constraints;
|
||||
void (*onContentSizeChanged)(uiWindow *, void *);
|
||||
void *onContentSizeChangedData;
|
||||
BOOL suppressSizeChanged;
|
||||
int fullscreen;
|
||||
int borderless;
|
||||
};
|
||||
|
||||
@implementation libuiNSWindow
|
||||
|
||||
- (void)libui_doMove:(NSEvent *)initialEvent
|
||||
{
|
||||
doManualMove(self, initialEvent);
|
||||
}
|
||||
|
||||
- (void)libui_doResize:(NSEvent *)initialEvent on:(uiWindowResizeEdge)edge
|
||||
{
|
||||
doManualResize(self, initialEvent, edge);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface windowDelegateClass : NSObject<NSWindowDelegate> {
|
||||
struct mapTable *windows;
|
||||
}
|
||||
- (BOOL)windowShouldClose:(id)sender;
|
||||
- (void)windowDidResize:(NSNotification *)note;
|
||||
- (void)windowDidEnterFullScreen:(NSNotification *)note;
|
||||
- (void)windowDidExitFullScreen:(NSNotification *)note;
|
||||
- (void)registerWindow:(uiWindow *)w;
|
||||
- (void)unregisterWindow:(uiWindow *)w;
|
||||
- (uiWindow *)lookupWindow:(NSWindow *)w;
|
||||
@end
|
||||
|
||||
@implementation windowDelegateClass
|
||||
|
||||
- (id)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
self->windows = newMap();
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
mapDestroy(self->windows);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (BOOL)windowShouldClose:(id)sender
|
||||
{
|
||||
uiWindow *w;
|
||||
|
||||
w = [self lookupWindow:((NSWindow *) sender)];
|
||||
// w should not be NULL; we are only the delegate of registered windows
|
||||
if ((*(w->onClosing))(w, w->onClosingData))
|
||||
uiControlDestroy(uiControl(w));
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)windowDidResize:(NSNotification *)note
|
||||
{
|
||||
uiWindow *w;
|
||||
|
||||
w = [self lookupWindow:((NSWindow *) [note object])];
|
||||
if (!w->suppressSizeChanged)
|
||||
(*(w->onContentSizeChanged))(w, w->onContentSizeChangedData);
|
||||
}
|
||||
|
||||
- (void)windowDidEnterFullScreen:(NSNotification *)note
|
||||
{
|
||||
uiWindow *w;
|
||||
|
||||
w = [self lookupWindow:((NSWindow *) [note object])];
|
||||
if (!w->suppressSizeChanged)
|
||||
w->fullscreen = 1;
|
||||
}
|
||||
|
||||
- (void)windowDidExitFullScreen:(NSNotification *)note
|
||||
{
|
||||
uiWindow *w;
|
||||
|
||||
w = [self lookupWindow:((NSWindow *) [note object])];
|
||||
if (!w->suppressSizeChanged)
|
||||
w->fullscreen = 0;
|
||||
}
|
||||
|
||||
- (void)registerWindow:(uiWindow *)w
|
||||
{
|
||||
mapSet(self->windows, w->window, w);
|
||||
[w->window setDelegate:self];
|
||||
}
|
||||
|
||||
- (void)unregisterWindow:(uiWindow *)w
|
||||
{
|
||||
[w->window setDelegate:nil];
|
||||
mapDelete(self->windows, w->window);
|
||||
}
|
||||
|
||||
- (uiWindow *)lookupWindow:(NSWindow *)w
|
||||
{
|
||||
uiWindow *v;
|
||||
|
||||
v = uiWindow(mapGet(self->windows, w));
|
||||
// this CAN (and IS ALLOWED TO) return NULL, just in case we're called with some OS X-provided window as the key window
|
||||
return v;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static windowDelegateClass *windowDelegate = nil;
|
||||
|
||||
static void removeConstraints(uiWindow *w)
|
||||
{
|
||||
NSView *cv;
|
||||
|
||||
cv = [w->window contentView];
|
||||
singleChildConstraintsRemove(&(w->constraints), cv);
|
||||
}
|
||||
|
||||
static void uiWindowDestroy(uiControl *c)
|
||||
{
|
||||
uiWindow *w = uiWindow(c);
|
||||
|
||||
// hide the window
|
||||
[w->window orderOut:w->window];
|
||||
removeConstraints(w);
|
||||
if (w->child != NULL) {
|
||||
uiControlSetParent(w->child, NULL);
|
||||
uiDarwinControlSetSuperview(uiDarwinControl(w->child), nil);
|
||||
uiControlDestroy(w->child);
|
||||
}
|
||||
[windowDelegate unregisterWindow:w];
|
||||
[w->window release];
|
||||
uiFreeControl(uiControl(w));
|
||||
}
|
||||
|
||||
uiDarwinControlDefaultHandle(uiWindow, window)
|
||||
|
||||
uiControl *uiWindowParent(uiControl *c)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void uiWindowSetParent(uiControl *c, uiControl *parent)
|
||||
{
|
||||
uiUserBugCannotSetParentOnToplevel("uiWindow");
|
||||
}
|
||||
|
||||
static int uiWindowToplevel(uiControl *c)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int uiWindowVisible(uiControl *c)
|
||||
{
|
||||
uiWindow *w = uiWindow(c);
|
||||
|
||||
return [w->window isVisible];
|
||||
}
|
||||
|
||||
static void uiWindowShow(uiControl *c)
|
||||
{
|
||||
uiWindow *w = (uiWindow *) c;
|
||||
|
||||
[w->window makeKeyAndOrderFront:w->window];
|
||||
}
|
||||
|
||||
static void uiWindowHide(uiControl *c)
|
||||
{
|
||||
uiWindow *w = (uiWindow *) c;
|
||||
|
||||
[w->window orderOut:w->window];
|
||||
}
|
||||
|
||||
uiDarwinControlDefaultEnabled(uiWindow, window)
|
||||
uiDarwinControlDefaultEnable(uiWindow, window)
|
||||
uiDarwinControlDefaultDisable(uiWindow, window)
|
||||
|
||||
static void uiWindowSyncEnableState(uiDarwinControl *c, int enabled)
|
||||
{
|
||||
uiWindow *w = uiWindow(c);
|
||||
|
||||
if (uiDarwinShouldStopSyncEnableState(uiDarwinControl(w), enabled))
|
||||
return;
|
||||
if (w->child != NULL)
|
||||
uiDarwinControlSyncEnableState(uiDarwinControl(w->child), enabled);
|
||||
}
|
||||
|
||||
static void uiWindowSetSuperview(uiDarwinControl *c, NSView *superview)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
static void windowRelayout(uiWindow *w)
|
||||
{
|
||||
NSView *childView;
|
||||
NSView *contentView;
|
||||
|
||||
removeConstraints(w);
|
||||
if (w->child == NULL)
|
||||
return;
|
||||
childView = (NSView *) uiControlHandle(w->child);
|
||||
contentView = [w->window contentView];
|
||||
singleChildConstraintsEstablish(&(w->constraints),
|
||||
contentView, childView,
|
||||
uiDarwinControlHugsTrailingEdge(uiDarwinControl(w->child)),
|
||||
uiDarwinControlHugsBottom(uiDarwinControl(w->child)),
|
||||
w->margined,
|
||||
@"uiWindow");
|
||||
}
|
||||
|
||||
uiDarwinControlDefaultHugsTrailingEdge(uiWindow, window)
|
||||
uiDarwinControlDefaultHugsBottom(uiWindow, window)
|
||||
|
||||
static void uiWindowChildEdgeHuggingChanged(uiDarwinControl *c)
|
||||
{
|
||||
uiWindow *w = uiWindow(c);
|
||||
|
||||
windowRelayout(w);
|
||||
}
|
||||
|
||||
// TODO
|
||||
uiDarwinControlDefaultHuggingPriority(uiWindow, window)
|
||||
uiDarwinControlDefaultSetHuggingPriority(uiWindow, window)
|
||||
// end TODO
|
||||
|
||||
static void uiWindowChildVisibilityChanged(uiDarwinControl *c)
|
||||
{
|
||||
uiWindow *w = uiWindow(c);
|
||||
|
||||
windowRelayout(w);
|
||||
}
|
||||
|
||||
char *uiWindowTitle(uiWindow *w)
|
||||
{
|
||||
return uiDarwinNSStringToText([w->window title]);
|
||||
}
|
||||
|
||||
void uiWindowSetTitle(uiWindow *w, const char *title)
|
||||
{
|
||||
[w->window setTitle:toNSString(title)];
|
||||
}
|
||||
|
||||
void uiWindowContentSize(uiWindow *w, int *width, int *height)
|
||||
{
|
||||
NSRect r;
|
||||
|
||||
r = [w->window contentRectForFrameRect:[w->window frame]];
|
||||
*width = r.size.width;
|
||||
*height = r.size.height;
|
||||
}
|
||||
|
||||
void uiWindowSetContentSize(uiWindow *w, int width, int height)
|
||||
{
|
||||
w->suppressSizeChanged = YES;
|
||||
[w->window setContentSize:NSMakeSize(width, height)];
|
||||
w->suppressSizeChanged = NO;
|
||||
}
|
||||
|
||||
int uiWindowFullscreen(uiWindow *w)
|
||||
{
|
||||
return w->fullscreen;
|
||||
}
|
||||
|
||||
void uiWindowSetFullscreen(uiWindow *w, int fullscreen)
|
||||
{
|
||||
if (w->fullscreen && fullscreen)
|
||||
return;
|
||||
if (!w->fullscreen && !fullscreen)
|
||||
return;
|
||||
w->fullscreen = fullscreen;
|
||||
if (w->fullscreen && w->borderless) // borderless doesn't play nice with fullscreen; don't toggle while borderless
|
||||
return;
|
||||
w->suppressSizeChanged = YES;
|
||||
[w->window toggleFullScreen:w->window];
|
||||
w->suppressSizeChanged = NO;
|
||||
if (!w->fullscreen && w->borderless) // borderless doesn't play nice with fullscreen; restore borderless after removing
|
||||
[w->window setStyleMask:NSBorderlessWindowMask];
|
||||
}
|
||||
|
||||
void uiWindowOnContentSizeChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data)
|
||||
{
|
||||
w->onContentSizeChanged = f;
|
||||
w->onContentSizeChangedData = data;
|
||||
}
|
||||
|
||||
void uiWindowOnClosing(uiWindow *w, int (*f)(uiWindow *, void *), void *data)
|
||||
{
|
||||
w->onClosing = f;
|
||||
w->onClosingData = data;
|
||||
}
|
||||
|
||||
int uiWindowBorderless(uiWindow *w)
|
||||
{
|
||||
return w->borderless;
|
||||
}
|
||||
|
||||
void uiWindowSetBorderless(uiWindow *w, int borderless)
|
||||
{
|
||||
w->borderless = borderless;
|
||||
if (w->borderless) {
|
||||
// borderless doesn't play nice with fullscreen; wait for later
|
||||
if (!w->fullscreen)
|
||||
[w->window setStyleMask:NSBorderlessWindowMask];
|
||||
} else {
|
||||
[w->window setStyleMask:defaultStyleMask];
|
||||
// borderless doesn't play nice with fullscreen; restore state
|
||||
if (w->fullscreen) {
|
||||
w->suppressSizeChanged = YES;
|
||||
[w->window toggleFullScreen:w->window];
|
||||
w->suppressSizeChanged = NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void uiWindowSetChild(uiWindow *w, uiControl *child)
|
||||
{
|
||||
NSView *childView;
|
||||
|
||||
if (w->child != NULL) {
|
||||
childView = (NSView *) uiControlHandle(w->child);
|
||||
[childView removeFromSuperview];
|
||||
uiControlSetParent(w->child, NULL);
|
||||
}
|
||||
w->child = child;
|
||||
if (w->child != NULL) {
|
||||
uiControlSetParent(w->child, uiControl(w));
|
||||
childView = (NSView *) uiControlHandle(w->child);
|
||||
uiDarwinControlSetSuperview(uiDarwinControl(w->child), [w->window contentView]);
|
||||
uiDarwinControlSyncEnableState(uiDarwinControl(w->child), uiControlEnabledToUser(uiControl(w)));
|
||||
}
|
||||
windowRelayout(w);
|
||||
}
|
||||
|
||||
int uiWindowMargined(uiWindow *w)
|
||||
{
|
||||
return w->margined;
|
||||
}
|
||||
|
||||
void uiWindowSetMargined(uiWindow *w, int margined)
|
||||
{
|
||||
w->margined = margined;
|
||||
singleChildConstraintsSetMargined(&(w->constraints), w->margined);
|
||||
}
|
||||
|
||||
static int defaultOnClosing(uiWindow *w, void *data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void defaultOnPositionContentSizeChanged(uiWindow *w, void *data)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar)
|
||||
{
|
||||
uiWindow *w;
|
||||
|
||||
finalizeMenus();
|
||||
|
||||
uiDarwinNewControl(uiWindow, w);
|
||||
|
||||
w->window = [[libuiNSWindow alloc] initWithContentRect:NSMakeRect(0, 0, (CGFloat) width, (CGFloat) height)
|
||||
styleMask:defaultStyleMask
|
||||
backing:NSBackingStoreBuffered
|
||||
defer:YES];
|
||||
[w->window setTitle:toNSString(title)];
|
||||
|
||||
// do NOT release when closed
|
||||
// we manually do this in uiWindowDestroy() above
|
||||
[w->window setReleasedWhenClosed:NO];
|
||||
|
||||
if (windowDelegate == nil) {
|
||||
windowDelegate = [[windowDelegateClass new] autorelease];
|
||||
[delegates addObject:windowDelegate];
|
||||
}
|
||||
[windowDelegate registerWindow:w];
|
||||
uiWindowOnClosing(w, defaultOnClosing, NULL);
|
||||
uiWindowOnContentSizeChanged(w, defaultOnPositionContentSizeChanged, NULL);
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
// utility function for menus
|
||||
uiWindow *windowFromNSWindow(NSWindow *w)
|
||||
{
|
||||
if (w == nil)
|
||||
return NULL;
|
||||
if (windowDelegate == nil) // no windows were created yet; we're called with some OS X-provided window
|
||||
return NULL;
|
||||
return [windowDelegate lookupWindow:w];
|
||||
}
|
253
deps/libui/darwin/winmoveresize.m
vendored
253
deps/libui/darwin/winmoveresize.m
vendored
@ -1,253 +0,0 @@
|
||||
// 1 november 2016
|
||||
#import "uipriv_darwin.h"
|
||||
|
||||
// because we are changing the window frame each time the mouse moves, the successive -[NSEvent locationInWindow]s cannot be meaningfully used together
|
||||
// make sure they are all following some sort of standard to avoid this problem; the screen is the most obvious possibility since it requires only one conversion (the only one that a NSWindow provides)
|
||||
static NSPoint makeIndependent(NSPoint p, NSWindow *w)
|
||||
{
|
||||
NSRect r;
|
||||
|
||||
r.origin = p;
|
||||
// mikeash in irc.freenode.net/#macdev confirms both that any size will do and that we can safely ignore the resultant size
|
||||
r.size = NSZeroSize;
|
||||
return [w convertRectToScreen:r].origin;
|
||||
}
|
||||
|
||||
struct onMoveDragParams {
|
||||
NSWindow *w;
|
||||
// using the previous point causes weird issues like the mouse seeming to fall behind the window edge... so do this instead
|
||||
// TODO will this make things like the menubar and dock easier too?
|
||||
NSRect initialFrame;
|
||||
NSPoint initialPoint;
|
||||
};
|
||||
|
||||
void onMoveDrag(struct onMoveDragParams *p, NSEvent *e)
|
||||
{
|
||||
NSPoint new;
|
||||
NSRect frame;
|
||||
CGFloat offx, offy;
|
||||
|
||||
new = makeIndependent([e locationInWindow], p->w);
|
||||
frame = p->initialFrame;
|
||||
|
||||
offx = new.x - p->initialPoint.x;
|
||||
offy = new.y - p->initialPoint.y;
|
||||
frame.origin.x += offx;
|
||||
frame.origin.y += offy;
|
||||
|
||||
// TODO handle the menubar
|
||||
// TODO wait the system does this for us already?!
|
||||
|
||||
[p->w setFrameOrigin:frame.origin];
|
||||
}
|
||||
|
||||
void doManualMove(NSWindow *w, NSEvent *initialEvent)
|
||||
{
|
||||
__block struct onMoveDragParams mdp;
|
||||
struct nextEventArgs nea;
|
||||
BOOL (^handleEvent)(NSEvent *e);
|
||||
__block BOOL done;
|
||||
|
||||
// this is only available on 10.11 and newer (LONGTERM FUTURE)
|
||||
// but use it if available; this lets us use the real OS dragging code, which means we can take advantage of OS features like Spaces
|
||||
if ([w respondsToSelector:@selector(performWindowDragWithEvent:)]) {
|
||||
[((id) w) performWindowDragWithEvent:initialEvent];
|
||||
return;
|
||||
}
|
||||
|
||||
mdp.w = w;
|
||||
mdp.initialFrame = [mdp.w frame];
|
||||
mdp.initialPoint = makeIndependent([initialEvent locationInWindow], mdp.w);
|
||||
|
||||
nea.mask = NSLeftMouseDraggedMask | NSLeftMouseUpMask;
|
||||
nea.duration = [NSDate distantFuture];
|
||||
nea.mode = NSEventTrackingRunLoopMode; // nextEventMatchingMask: docs suggest using this for manual mouse tracking
|
||||
nea.dequeue = YES;
|
||||
handleEvent = ^(NSEvent *e) {
|
||||
if ([e type] == NSLeftMouseUp) {
|
||||
done = YES;
|
||||
return YES; // do not send
|
||||
}
|
||||
onMoveDrag(&mdp, e);
|
||||
return YES; // do not send
|
||||
};
|
||||
done = NO;
|
||||
while (mainStep(&nea, handleEvent))
|
||||
if (done)
|
||||
break;
|
||||
}
|
||||
|
||||
// see http://stackoverflow.com/a/40352996/3408572
|
||||
static void minMaxAutoLayoutSizes(NSWindow *w, NSSize *min, NSSize *max)
|
||||
{
|
||||
NSLayoutConstraint *cw, *ch;
|
||||
NSView *contentView;
|
||||
NSRect prevFrame;
|
||||
|
||||
// if adding these constraints causes the window to change size somehow, don't show it to the user and change it back afterwards
|
||||
NSDisableScreenUpdates();
|
||||
prevFrame = [w frame];
|
||||
|
||||
// minimum: encourage the window to be as small as possible
|
||||
contentView = [w contentView];
|
||||
cw = mkConstraint(contentView, NSLayoutAttributeWidth,
|
||||
NSLayoutRelationEqual,
|
||||
nil, NSLayoutAttributeNotAnAttribute,
|
||||
0, 0,
|
||||
@"window minimum width finding constraint");
|
||||
[cw setPriority:NSLayoutPriorityDragThatCanResizeWindow];
|
||||
[contentView addConstraint:cw];
|
||||
ch = mkConstraint(contentView, NSLayoutAttributeHeight,
|
||||
NSLayoutRelationEqual,
|
||||
nil, NSLayoutAttributeNotAnAttribute,
|
||||
0, 0,
|
||||
@"window minimum height finding constraint");
|
||||
[ch setPriority:NSLayoutPriorityDragThatCanResizeWindow];
|
||||
[contentView addConstraint:ch];
|
||||
*min = [contentView fittingSize];
|
||||
[contentView removeConstraint:cw];
|
||||
[contentView removeConstraint:ch];
|
||||
|
||||
// maximum: encourage the window to be as large as possible
|
||||
contentView = [w contentView];
|
||||
cw = mkConstraint(contentView, NSLayoutAttributeWidth,
|
||||
NSLayoutRelationEqual,
|
||||
nil, NSLayoutAttributeNotAnAttribute,
|
||||
0, CGFLOAT_MAX,
|
||||
@"window maximum width finding constraint");
|
||||
[cw setPriority:NSLayoutPriorityDragThatCanResizeWindow];
|
||||
[contentView addConstraint:cw];
|
||||
ch = mkConstraint(contentView, NSLayoutAttributeHeight,
|
||||
NSLayoutRelationEqual,
|
||||
nil, NSLayoutAttributeNotAnAttribute,
|
||||
0, CGFLOAT_MAX,
|
||||
@"window maximum height finding constraint");
|
||||
[ch setPriority:NSLayoutPriorityDragThatCanResizeWindow];
|
||||
[contentView addConstraint:ch];
|
||||
*max = [contentView fittingSize];
|
||||
[contentView removeConstraint:cw];
|
||||
[contentView removeConstraint:ch];
|
||||
|
||||
[w setFrame:prevFrame display:YES]; // TODO really YES?
|
||||
NSEnableScreenUpdates();
|
||||
}
|
||||
|
||||
static void handleResizeLeft(NSRect *frame, NSPoint old, NSPoint new)
|
||||
{
|
||||
frame->origin.x += new.x - old.x;
|
||||
frame->size.width -= new.x - old.x;
|
||||
}
|
||||
|
||||
// TODO properly handle the menubar
|
||||
// TODO wait, OS X does it for us?!
|
||||
static void handleResizeTop(NSRect *frame, NSPoint old, NSPoint new)
|
||||
{
|
||||
frame->size.height += new.y - old.y;
|
||||
}
|
||||
|
||||
static void handleResizeRight(NSRect *frame, NSPoint old, NSPoint new)
|
||||
{
|
||||
frame->size.width += new.x - old.x;
|
||||
}
|
||||
|
||||
|
||||
// TODO properly handle the menubar
|
||||
static void handleResizeBottom(NSRect *frame, NSPoint old, NSPoint new)
|
||||
{
|
||||
frame->origin.y += new.y - old.y;
|
||||
frame->size.height -= new.y - old.y;
|
||||
}
|
||||
|
||||
struct onResizeDragParams {
|
||||
NSWindow *w;
|
||||
// using the previous point causes weird issues like the mouse seeming to fall behind the window edge... so do this instead
|
||||
// TODO will this make things like the menubar and dock easier too?
|
||||
NSRect initialFrame;
|
||||
NSPoint initialPoint;
|
||||
uiWindowResizeEdge edge;
|
||||
NSSize min;
|
||||
NSSize max;
|
||||
};
|
||||
|
||||
static void onResizeDrag(struct onResizeDragParams *p, NSEvent *e)
|
||||
{
|
||||
NSPoint new;
|
||||
NSRect frame;
|
||||
|
||||
new = makeIndependent([e locationInWindow], p->w);
|
||||
frame = p->initialFrame;
|
||||
|
||||
// horizontal
|
||||
switch (p->edge) {
|
||||
case uiWindowResizeEdgeLeft:
|
||||
case uiWindowResizeEdgeTopLeft:
|
||||
case uiWindowResizeEdgeBottomLeft:
|
||||
handleResizeLeft(&frame, p->initialPoint, new);
|
||||
break;
|
||||
case uiWindowResizeEdgeRight:
|
||||
case uiWindowResizeEdgeTopRight:
|
||||
case uiWindowResizeEdgeBottomRight:
|
||||
handleResizeRight(&frame, p->initialPoint, new);
|
||||
break;
|
||||
}
|
||||
// vertical
|
||||
switch (p->edge) {
|
||||
case uiWindowResizeEdgeTop:
|
||||
case uiWindowResizeEdgeTopLeft:
|
||||
case uiWindowResizeEdgeTopRight:
|
||||
handleResizeTop(&frame, p->initialPoint, new);
|
||||
break;
|
||||
case uiWindowResizeEdgeBottom:
|
||||
case uiWindowResizeEdgeBottomLeft:
|
||||
case uiWindowResizeEdgeBottomRight:
|
||||
handleResizeBottom(&frame, p->initialPoint, new);
|
||||
break;
|
||||
}
|
||||
|
||||
// constrain
|
||||
// TODO should we constrain against anything else as well? minMaxAutoLayoutSizes() already gives us nonnegative sizes, but...
|
||||
if (frame.size.width < p->min.width)
|
||||
frame.size.width = p->min.width;
|
||||
if (frame.size.height < p->min.height)
|
||||
frame.size.height = p->min.height;
|
||||
// TODO > or >= ?
|
||||
if (frame.size.width > p->max.width)
|
||||
frame.size.width = p->max.width;
|
||||
if (frame.size.height > p->max.height)
|
||||
frame.size.height = p->max.height;
|
||||
|
||||
[p->w setFrame:frame display:YES]; // and do reflect the new frame immediately
|
||||
}
|
||||
|
||||
// TODO do our events get fired with this? *should* they?
|
||||
void doManualResize(NSWindow *w, NSEvent *initialEvent, uiWindowResizeEdge edge)
|
||||
{
|
||||
__block struct onResizeDragParams rdp;
|
||||
struct nextEventArgs nea;
|
||||
BOOL (^handleEvent)(NSEvent *e);
|
||||
__block BOOL done;
|
||||
|
||||
rdp.w = w;
|
||||
rdp.initialFrame = [rdp.w frame];
|
||||
rdp.initialPoint = makeIndependent([initialEvent locationInWindow], rdp.w);
|
||||
rdp.edge = edge;
|
||||
// TODO what happens if these change during the loop?
|
||||
minMaxAutoLayoutSizes(rdp.w, &(rdp.min), &(rdp.max));
|
||||
|
||||
nea.mask = NSLeftMouseDraggedMask | NSLeftMouseUpMask;
|
||||
nea.duration = [NSDate distantFuture];
|
||||
nea.mode = NSEventTrackingRunLoopMode; // nextEventMatchingMask: docs suggest using this for manual mouse tracking
|
||||
nea.dequeue = YES;
|
||||
handleEvent = ^(NSEvent *e) {
|
||||
if ([e type] == NSLeftMouseUp) {
|
||||
done = YES;
|
||||
return YES; // do not send
|
||||
}
|
||||
onResizeDrag(&rdp, e);
|
||||
return YES; // do not send
|
||||
};
|
||||
done = NO;
|
||||
while (mainStep(&nea, handleEvent))
|
||||
if (done)
|
||||
break;
|
||||
}
|
543
deps/libui/libui_main.c
vendored
543
deps/libui/libui_main.c
vendored
@ -1,543 +0,0 @@
|
||||
/* 2 september 2015 */
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "ui.h"
|
||||
#include "../../verbosity.h"
|
||||
|
||||
static int onClosing(uiWindow *w, void *data)
|
||||
{
|
||||
uiQuit();
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int onShouldQuit(void *data)
|
||||
{
|
||||
uiWindow *mainwin = uiWindow(data);
|
||||
|
||||
uiControlDestroy(uiControl(mainwin));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static uiControl *makeBasicControlsPage(void)
|
||||
{
|
||||
uiBox *vbox;
|
||||
uiBox *hbox;
|
||||
uiGroup *group;
|
||||
uiForm *entryForm;
|
||||
|
||||
vbox = uiNewVerticalBox();
|
||||
uiBoxSetPadded(vbox, 1);
|
||||
|
||||
hbox = uiNewHorizontalBox();
|
||||
uiBoxSetPadded(hbox, 1);
|
||||
uiBoxAppend(vbox, uiControl(hbox), 0);
|
||||
|
||||
uiBoxAppend(hbox,
|
||||
uiControl(uiNewButton("Button")),
|
||||
0);
|
||||
uiBoxAppend(hbox,
|
||||
uiControl(uiNewCheckbox("Checkbox")),
|
||||
0);
|
||||
|
||||
uiBoxAppend(vbox,
|
||||
uiControl(uiNewLabel("This is a label. Right now, labels can only span one line.")),
|
||||
0);
|
||||
|
||||
uiBoxAppend(vbox,
|
||||
uiControl(uiNewHorizontalSeparator()),
|
||||
0);
|
||||
|
||||
group = uiNewGroup("Entries");
|
||||
uiGroupSetMargined(group, 1);
|
||||
uiBoxAppend(vbox, uiControl(group), 1);
|
||||
|
||||
entryForm = uiNewForm();
|
||||
uiFormSetPadded(entryForm, 1);
|
||||
uiGroupSetChild(group, uiControl(entryForm));
|
||||
|
||||
uiFormAppend(entryForm,
|
||||
"Entry",
|
||||
uiControl(uiNewEntry()),
|
||||
0);
|
||||
uiFormAppend(entryForm,
|
||||
"Password Entry",
|
||||
uiControl(uiNewPasswordEntry()),
|
||||
0);
|
||||
uiFormAppend(entryForm,
|
||||
"Search Entry",
|
||||
uiControl(uiNewSearchEntry()),
|
||||
0);
|
||||
uiFormAppend(entryForm,
|
||||
"Multiline Entry",
|
||||
uiControl(uiNewMultilineEntry()),
|
||||
1);
|
||||
uiFormAppend(entryForm,
|
||||
"Multiline Entry No Wrap",
|
||||
uiControl(uiNewNonWrappingMultilineEntry()),
|
||||
1);
|
||||
|
||||
return uiControl(vbox);
|
||||
}
|
||||
|
||||
// TODO make these not global
|
||||
static uiSpinbox *spinbox;
|
||||
static uiSlider *slider;
|
||||
static uiProgressBar *pbar;
|
||||
|
||||
static void onSpinboxChanged(uiSpinbox *s, void *data)
|
||||
{
|
||||
uiSliderSetValue(slider, uiSpinboxValue(s));
|
||||
uiProgressBarSetValue(pbar, uiSpinboxValue(s));
|
||||
}
|
||||
|
||||
static void onSliderChanged(uiSlider *s, void *data)
|
||||
{
|
||||
uiSpinboxSetValue(spinbox, uiSliderValue(s));
|
||||
uiProgressBarSetValue(pbar, uiSliderValue(s));
|
||||
}
|
||||
|
||||
static uiControl *makeNumbersPage()
|
||||
{
|
||||
uiBox *hbox;
|
||||
uiGroup *group;
|
||||
uiBox *vbox;
|
||||
uiProgressBar *ip;
|
||||
uiCombobox *cbox;
|
||||
uiEditableCombobox *ecbox;
|
||||
uiRadioButtons *rb;
|
||||
|
||||
hbox = uiNewHorizontalBox();
|
||||
uiBoxSetPadded(hbox, 1);
|
||||
|
||||
group = uiNewGroup("Numbers");
|
||||
uiGroupSetMargined(group, 1);
|
||||
uiBoxAppend(hbox, uiControl(group), 1);
|
||||
|
||||
vbox = uiNewVerticalBox();
|
||||
uiBoxSetPadded(vbox, 1);
|
||||
uiGroupSetChild(group, uiControl(vbox));
|
||||
|
||||
spinbox = uiNewSpinbox(0, 100);
|
||||
slider = uiNewSlider(0, 100);
|
||||
pbar = uiNewProgressBar();
|
||||
uiSpinboxOnChanged(spinbox, onSpinboxChanged, NULL);
|
||||
uiSliderOnChanged(slider, onSliderChanged, NULL);
|
||||
uiBoxAppend(vbox, uiControl(spinbox), 0);
|
||||
uiBoxAppend(vbox, uiControl(slider), 0);
|
||||
uiBoxAppend(vbox, uiControl(pbar), 0);
|
||||
|
||||
ip = uiNewProgressBar();
|
||||
uiProgressBarSetValue(ip, -1);
|
||||
uiBoxAppend(vbox, uiControl(ip), 0);
|
||||
|
||||
group = uiNewGroup("Lists");
|
||||
uiGroupSetMargined(group, 1);
|
||||
uiBoxAppend(hbox, uiControl(group), 1);
|
||||
|
||||
vbox = uiNewVerticalBox();
|
||||
uiBoxSetPadded(vbox, 1);
|
||||
uiGroupSetChild(group, uiControl(vbox));
|
||||
|
||||
cbox = uiNewCombobox();
|
||||
uiComboboxAppend(cbox, "Combobox Item 1");
|
||||
uiComboboxAppend(cbox, "Combobox Item 2");
|
||||
uiComboboxAppend(cbox, "Combobox Item 3");
|
||||
uiBoxAppend(vbox, uiControl(cbox), 0);
|
||||
|
||||
ecbox = uiNewEditableCombobox();
|
||||
uiEditableComboboxAppend(ecbox, "Editable Item 1");
|
||||
uiEditableComboboxAppend(ecbox, "Editable Item 2");
|
||||
uiEditableComboboxAppend(ecbox, "Editable Item 3");
|
||||
uiBoxAppend(vbox, uiControl(ecbox), 0);
|
||||
|
||||
rb = uiNewRadioButtons();
|
||||
uiRadioButtonsAppend(rb, "Radio Button 1");
|
||||
uiRadioButtonsAppend(rb, "Radio Button 2");
|
||||
uiRadioButtonsAppend(rb, "Radio Button 3");
|
||||
uiBoxAppend(vbox, uiControl(rb), 0);
|
||||
|
||||
return uiControl(hbox);
|
||||
}
|
||||
|
||||
// TODO make this not global
|
||||
static uiWindow *mainwin;
|
||||
|
||||
static void onOpenFileClicked(uiButton *b, void *data)
|
||||
{
|
||||
uiEntry *entry = uiEntry(data);
|
||||
char *filename;
|
||||
|
||||
filename = uiOpenFile(mainwin);
|
||||
if (filename == NULL) {
|
||||
uiEntrySetText(entry, "(cancelled)");
|
||||
return;
|
||||
}
|
||||
uiEntrySetText(entry, filename);
|
||||
uiFreeText(filename);
|
||||
}
|
||||
|
||||
static void onSaveFileClicked(uiButton *b, void *data)
|
||||
{
|
||||
uiEntry *entry = uiEntry(data);
|
||||
char *filename;
|
||||
|
||||
filename = uiSaveFile(mainwin);
|
||||
if (filename == NULL) {
|
||||
uiEntrySetText(entry, "(cancelled)");
|
||||
return;
|
||||
}
|
||||
uiEntrySetText(entry, filename);
|
||||
uiFreeText(filename);
|
||||
}
|
||||
|
||||
static void onMsgBoxClicked(uiButton *b, void *data)
|
||||
{
|
||||
uiMsgBox(mainwin,
|
||||
"This is a normal message box.",
|
||||
"More detailed information can be shown here.");
|
||||
}
|
||||
|
||||
static void onMsgBoxErrorClicked(uiButton *b, void *data)
|
||||
{
|
||||
uiMsgBoxError(mainwin,
|
||||
"This message box describes an error.",
|
||||
"More detailed information can be shown here.");
|
||||
}
|
||||
|
||||
static uiControl *makeDataChoosersPage(void)
|
||||
{
|
||||
uiBox *hbox;
|
||||
uiBox *vbox;
|
||||
uiGrid *grid;
|
||||
uiButton *button;
|
||||
uiEntry *entry;
|
||||
uiGrid *msggrid;
|
||||
|
||||
hbox = uiNewHorizontalBox();
|
||||
uiBoxSetPadded(hbox, 1);
|
||||
|
||||
vbox = uiNewVerticalBox();
|
||||
uiBoxSetPadded(vbox, 1);
|
||||
uiBoxAppend(hbox, uiControl(vbox), 0);
|
||||
|
||||
uiBoxAppend(vbox,
|
||||
uiControl(uiNewDatePicker()),
|
||||
0);
|
||||
uiBoxAppend(vbox,
|
||||
uiControl(uiNewTimePicker()),
|
||||
0);
|
||||
uiBoxAppend(vbox,
|
||||
uiControl(uiNewDateTimePicker()),
|
||||
0);
|
||||
|
||||
uiBoxAppend(vbox,
|
||||
uiControl(uiNewFontButton()),
|
||||
0);
|
||||
uiBoxAppend(vbox,
|
||||
uiControl(uiNewColorButton()),
|
||||
0);
|
||||
|
||||
uiBoxAppend(hbox,
|
||||
uiControl(uiNewVerticalSeparator()),
|
||||
0);
|
||||
|
||||
vbox = uiNewVerticalBox();
|
||||
uiBoxSetPadded(vbox, 1);
|
||||
uiBoxAppend(hbox, uiControl(vbox), 1);
|
||||
|
||||
grid = uiNewGrid();
|
||||
uiGridSetPadded(grid, 1);
|
||||
uiBoxAppend(vbox, uiControl(grid), 0);
|
||||
|
||||
button = uiNewButton("Open File");
|
||||
entry = uiNewEntry();
|
||||
uiEntrySetReadOnly(entry, 1);
|
||||
uiButtonOnClicked(button, onOpenFileClicked, entry);
|
||||
uiGridAppend(grid, uiControl(button),
|
||||
0, 0, 1, 1,
|
||||
0, uiAlignFill, 0, uiAlignFill);
|
||||
uiGridAppend(grid, uiControl(entry),
|
||||
1, 0, 1, 1,
|
||||
1, uiAlignFill, 0, uiAlignFill);
|
||||
|
||||
button = uiNewButton("Save File");
|
||||
entry = uiNewEntry();
|
||||
uiEntrySetReadOnly(entry, 1);
|
||||
uiButtonOnClicked(button, onSaveFileClicked, entry);
|
||||
uiGridAppend(grid, uiControl(button),
|
||||
0, 1, 1, 1,
|
||||
0, uiAlignFill, 0, uiAlignFill);
|
||||
uiGridAppend(grid, uiControl(entry),
|
||||
1, 1, 1, 1,
|
||||
1, uiAlignFill, 0, uiAlignFill);
|
||||
|
||||
msggrid = uiNewGrid();
|
||||
uiGridSetPadded(msggrid, 1);
|
||||
uiGridAppend(grid, uiControl(msggrid),
|
||||
0, 2, 2, 1,
|
||||
0, uiAlignCenter, 0, uiAlignStart);
|
||||
|
||||
button = uiNewButton("Message Box");
|
||||
uiButtonOnClicked(button, onMsgBoxClicked, NULL);
|
||||
uiGridAppend(msggrid, uiControl(button),
|
||||
0, 0, 1, 1,
|
||||
0, uiAlignFill, 0, uiAlignFill);
|
||||
button = uiNewButton("Error Box");
|
||||
uiButtonOnClicked(button, onMsgBoxErrorClicked, NULL);
|
||||
uiGridAppend(msggrid, uiControl(button),
|
||||
1, 0, 1, 1,
|
||||
0, uiAlignFill, 0, uiAlignFill);
|
||||
|
||||
return uiControl(hbox);
|
||||
}
|
||||
|
||||
int libui_main(void)
|
||||
{
|
||||
uiInitOptions options;
|
||||
const char *err;
|
||||
uiTab *tab;
|
||||
|
||||
memset(&options, 0, sizeof (uiInitOptions));
|
||||
err = uiInit(&options);
|
||||
if (err != NULL)
|
||||
{
|
||||
RARCH_ERR("Failed to initialize uiInit\n");
|
||||
fprintf(stderr, "error initializing libui: %s", err);
|
||||
uiFreeInitError(err);
|
||||
return 1;
|
||||
}
|
||||
|
||||
mainwin = uiNewWindow("libui Control Gallery", 640, 480, 1);
|
||||
uiWindowOnClosing(mainwin, onClosing, NULL);
|
||||
uiOnShouldQuit(onShouldQuit, mainwin);
|
||||
|
||||
tab = uiNewTab();
|
||||
uiWindowSetChild(mainwin, uiControl(tab));
|
||||
uiWindowSetMargined(mainwin, 1);
|
||||
|
||||
uiTabAppend(tab, "Basic Controls", makeBasicControlsPage());
|
||||
uiTabSetMargined(tab, 0, 1);
|
||||
|
||||
uiTabAppend(tab, "Numbers and Lists", makeNumbersPage());
|
||||
uiTabSetMargined(tab, 1, 1);
|
||||
|
||||
uiTabAppend(tab, "Data Choosers", makeDataChoosersPage());
|
||||
uiTabSetMargined(tab, 2, 1);
|
||||
|
||||
uiControlShow(uiControl(mainwin));
|
||||
uiMain();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
static void openClicked(uiMenuItem *item, uiWindow *w, void *data)
|
||||
{
|
||||
char *filename;
|
||||
|
||||
filename = uiOpenFile(mainwin);
|
||||
if (filename == NULL) {
|
||||
uiMsgBoxError(mainwin, "No file selected", "Don't be alarmed!");
|
||||
return;
|
||||
}
|
||||
uiMsgBox(mainwin, "File selected", filename);
|
||||
uiFreeText(filename);
|
||||
}
|
||||
|
||||
static void saveClicked(uiMenuItem *item, uiWindow *w, void *data)
|
||||
{
|
||||
char *filename;
|
||||
|
||||
filename = uiSaveFile(mainwin);
|
||||
if (filename == NULL) {
|
||||
uiMsgBoxError(mainwin, "No file selected", "Don't be alarmed!");
|
||||
return;
|
||||
}
|
||||
uiMsgBox(mainwin, "File selected (don't worry, it's still there)", filename);
|
||||
uiFreeText(filename);
|
||||
}
|
||||
|
||||
static uiSpinbox *spinbox;
|
||||
static uiSlider *slider;
|
||||
static uiProgressBar *progressbar;
|
||||
|
||||
static void update(int value)
|
||||
{
|
||||
uiSpinboxSetValue(spinbox, value);
|
||||
uiSliderSetValue(slider, value);
|
||||
uiProgressBarSetValue(progressbar, value);
|
||||
}
|
||||
|
||||
static void onSpinboxChanged(uiSpinbox *s, void *data)
|
||||
{
|
||||
update(uiSpinboxValue(spinbox));
|
||||
}
|
||||
|
||||
static void onSliderChanged(uiSlider *s, void *data)
|
||||
{
|
||||
update(uiSliderValue(slider));
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
uiInitOptions o;
|
||||
const char *err;
|
||||
uiMenu *menu;
|
||||
uiMenuItem *item;
|
||||
uiBox *box;
|
||||
uiBox *hbox;
|
||||
uiGroup *group;
|
||||
uiBox *inner;
|
||||
uiBox *inner2;
|
||||
uiEntry *entry;
|
||||
uiCombobox *cbox;
|
||||
uiEditableCombobox *ecbox;
|
||||
uiRadioButtons *rb;
|
||||
uiTab *tab;
|
||||
|
||||
memset(&o, 0, sizeof (uiInitOptions));
|
||||
err = uiInit(&o);
|
||||
if (err != NULL) {
|
||||
fprintf(stderr, "error initializing ui: %s\n", err);
|
||||
uiFreeInitError(err);
|
||||
return 1;
|
||||
}
|
||||
|
||||
menu = uiNewMenu("File");
|
||||
item = uiMenuAppendItem(menu, "Open");
|
||||
uiMenuItemOnClicked(item, openClicked, NULL);
|
||||
item = uiMenuAppendItem(menu, "Save");
|
||||
uiMenuItemOnClicked(item, saveClicked, NULL);
|
||||
item = uiMenuAppendQuitItem(menu);
|
||||
uiOnShouldQuit(shouldQuit, NULL);
|
||||
|
||||
menu = uiNewMenu("Edit");
|
||||
item = uiMenuAppendCheckItem(menu, "Checkable Item");
|
||||
uiMenuAppendSeparator(menu);
|
||||
item = uiMenuAppendItem(menu, "Disabled Item");
|
||||
uiMenuItemDisable(item);
|
||||
item = uiMenuAppendPreferencesItem(menu);
|
||||
|
||||
menu = uiNewMenu("Help");
|
||||
item = uiMenuAppendItem(menu, "Help");
|
||||
item = uiMenuAppendAboutItem(menu);
|
||||
|
||||
mainwin = uiNewWindow("libui Control Gallery", 640, 480, 1);
|
||||
uiWindowSetMargined(mainwin, 1);
|
||||
uiWindowOnClosing(mainwin, onClosing, NULL);
|
||||
|
||||
box = uiNewVerticalBox();
|
||||
uiBoxSetPadded(box, 1);
|
||||
uiWindowSetChild(mainwin, uiControl(box));
|
||||
|
||||
hbox = uiNewHorizontalBox();
|
||||
uiBoxSetPadded(hbox, 1);
|
||||
uiBoxAppend(box, uiControl(hbox), 1);
|
||||
|
||||
group = uiNewGroup("Basic Controls");
|
||||
uiGroupSetMargined(group, 1);
|
||||
uiBoxAppend(hbox, uiControl(group), 0);
|
||||
|
||||
inner = uiNewVerticalBox();
|
||||
uiBoxSetPadded(inner, 1);
|
||||
uiGroupSetChild(group, uiControl(inner));
|
||||
|
||||
uiBoxAppend(inner,
|
||||
uiControl(uiNewButton("Button")),
|
||||
0);
|
||||
uiBoxAppend(inner,
|
||||
uiControl(uiNewCheckbox("Checkbox")),
|
||||
0);
|
||||
entry = uiNewEntry();
|
||||
uiEntrySetText(entry, "Entry");
|
||||
uiBoxAppend(inner,
|
||||
uiControl(entry),
|
||||
0);
|
||||
uiBoxAppend(inner,
|
||||
uiControl(uiNewLabel("Label")),
|
||||
0);
|
||||
|
||||
uiBoxAppend(inner,
|
||||
uiControl(uiNewHorizontalSeparator()),
|
||||
0);
|
||||
|
||||
uiBoxAppend(inner,
|
||||
uiControl(uiNewDatePicker()),
|
||||
0);
|
||||
uiBoxAppend(inner,
|
||||
uiControl(uiNewTimePicker()),
|
||||
0);
|
||||
uiBoxAppend(inner,
|
||||
uiControl(uiNewDateTimePicker()),
|
||||
0);
|
||||
|
||||
uiBoxAppend(inner,
|
||||
uiControl(uiNewFontButton()),
|
||||
0);
|
||||
|
||||
uiBoxAppend(inner,
|
||||
uiControl(uiNewColorButton()),
|
||||
0);
|
||||
|
||||
inner2 = uiNewVerticalBox();
|
||||
uiBoxSetPadded(inner2, 1);
|
||||
uiBoxAppend(hbox, uiControl(inner2), 1);
|
||||
|
||||
group = uiNewGroup("Numbers");
|
||||
uiGroupSetMargined(group, 1);
|
||||
uiBoxAppend(inner2, uiControl(group), 0);
|
||||
|
||||
inner = uiNewVerticalBox();
|
||||
uiBoxSetPadded(inner, 1);
|
||||
uiGroupSetChild(group, uiControl(inner));
|
||||
|
||||
spinbox = uiNewSpinbox(0, 100);
|
||||
uiSpinboxOnChanged(spinbox, onSpinboxChanged, NULL);
|
||||
uiBoxAppend(inner, uiControl(spinbox), 0);
|
||||
|
||||
slider = uiNewSlider(0, 100);
|
||||
uiSliderOnChanged(slider, onSliderChanged, NULL);
|
||||
uiBoxAppend(inner, uiControl(slider), 0);
|
||||
|
||||
progressbar = uiNewProgressBar();
|
||||
uiBoxAppend(inner, uiControl(progressbar), 0);
|
||||
|
||||
group = uiNewGroup("Lists");
|
||||
uiGroupSetMargined(group, 1);
|
||||
uiBoxAppend(inner2, uiControl(group), 0);
|
||||
|
||||
inner = uiNewVerticalBox();
|
||||
uiBoxSetPadded(inner, 1);
|
||||
uiGroupSetChild(group, uiControl(inner));
|
||||
|
||||
cbox = uiNewCombobox();
|
||||
uiComboboxAppend(cbox, "Combobox Item 1");
|
||||
uiComboboxAppend(cbox, "Combobox Item 2");
|
||||
uiComboboxAppend(cbox, "Combobox Item 3");
|
||||
uiBoxAppend(inner, uiControl(cbox), 0);
|
||||
|
||||
ecbox = uiNewEditableCombobox();
|
||||
uiEditableComboboxAppend(ecbox, "Editable Item 1");
|
||||
uiEditableComboboxAppend(ecbox, "Editable Item 2");
|
||||
uiEditableComboboxAppend(ecbox, "Editable Item 3");
|
||||
uiBoxAppend(inner, uiControl(ecbox), 0);
|
||||
|
||||
rb = uiNewRadioButtons();
|
||||
uiRadioButtonsAppend(rb, "Radio Button 1");
|
||||
uiRadioButtonsAppend(rb, "Radio Button 2");
|
||||
uiRadioButtonsAppend(rb, "Radio Button 3");
|
||||
uiBoxAppend(inner, uiControl(rb), 1);
|
||||
|
||||
tab = uiNewTab();
|
||||
uiTabAppend(tab, "Page 1", uiControl(uiNewHorizontalBox()));
|
||||
uiTabAppend(tab, "Page 2", uiControl(uiNewHorizontalBox()));
|
||||
uiTabAppend(tab, "Page 3", uiControl(uiNewHorizontalBox()));
|
||||
uiBoxAppend(inner2, uiControl(tab), 1);
|
||||
|
||||
uiControlShow(uiControl(mainwin));
|
||||
uiMain();
|
||||
uiUninit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
691
deps/libui/ui.h
vendored
691
deps/libui/ui.h
vendored
@ -1,691 +0,0 @@
|
||||
// 6 april 2015
|
||||
|
||||
// TODO add a uiVerifyControlType() function that can be used by control implementations to verify controls
|
||||
|
||||
#ifndef __LIBUI_UI_H__
|
||||
#define __LIBUI_UI_H__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// this macro is generated by cmake
|
||||
#ifdef libui_EXPORTS
|
||||
#ifdef _WIN32
|
||||
#define _UI_EXTERN __declspec(dllexport) extern
|
||||
#else
|
||||
#define _UI_EXTERN __attribute__((visibility("default"))) extern
|
||||
#endif
|
||||
#else
|
||||
// TODO add __declspec(dllimport) on windows, but only if not static
|
||||
#define _UI_EXTERN extern
|
||||
#endif
|
||||
|
||||
// C++ is really really really really really really dumb about enums, so screw that and just make them anonymous
|
||||
// This has the advantage of being ABI-able should we ever need an ABI...
|
||||
#define _UI_ENUM(s) typedef unsigned int s; enum
|
||||
|
||||
// This constant is provided because M_PI is nonstandard.
|
||||
// This comes from Go's math.Pi, which in turn comes from http://oeis.org/A000796.
|
||||
#define uiPi 3.14159265358979323846264338327950288419716939937510582097494459
|
||||
|
||||
// TODO uiBool?
|
||||
|
||||
typedef struct uiInitOptions uiInitOptions;
|
||||
|
||||
struct uiInitOptions {
|
||||
size_t Size;
|
||||
};
|
||||
|
||||
_UI_EXTERN const char *uiInit(uiInitOptions *options);
|
||||
_UI_EXTERN void uiUninit(void);
|
||||
_UI_EXTERN void uiFreeInitError(const char *err);
|
||||
|
||||
_UI_EXTERN void uiMain(void);
|
||||
_UI_EXTERN void uiMainSteps(void);
|
||||
_UI_EXTERN int uiMainStep(int wait);
|
||||
_UI_EXTERN void uiQuit(void);
|
||||
|
||||
_UI_EXTERN void uiQueueMain(void (*f)(void *data), void *data);
|
||||
|
||||
_UI_EXTERN void uiOnShouldQuit(int (*f)(void *data), void *data);
|
||||
|
||||
_UI_EXTERN void uiFreeText(char *text);
|
||||
|
||||
typedef struct uiControl uiControl;
|
||||
|
||||
struct uiControl {
|
||||
uint32_t Signature;
|
||||
uint32_t OSSignature;
|
||||
uint32_t TypeSignature;
|
||||
void (*Destroy)(uiControl *);
|
||||
uintptr_t (*Handle)(uiControl *);
|
||||
uiControl *(*Parent)(uiControl *);
|
||||
void (*SetParent)(uiControl *, uiControl *);
|
||||
int (*Toplevel)(uiControl *);
|
||||
int (*Visible)(uiControl *);
|
||||
void (*Show)(uiControl *);
|
||||
void (*Hide)(uiControl *);
|
||||
int (*Enabled)(uiControl *);
|
||||
void (*Enable)(uiControl *);
|
||||
void (*Disable)(uiControl *);
|
||||
};
|
||||
// TOOD add argument names to all arguments
|
||||
#define uiControl(this) ((uiControl *) (this))
|
||||
_UI_EXTERN void uiControlDestroy(uiControl *);
|
||||
_UI_EXTERN uintptr_t uiControlHandle(uiControl *);
|
||||
_UI_EXTERN uiControl *uiControlParent(uiControl *);
|
||||
_UI_EXTERN void uiControlSetParent(uiControl *, uiControl *);
|
||||
_UI_EXTERN int uiControlToplevel(uiControl *);
|
||||
_UI_EXTERN int uiControlVisible(uiControl *);
|
||||
_UI_EXTERN void uiControlShow(uiControl *);
|
||||
_UI_EXTERN void uiControlHide(uiControl *);
|
||||
_UI_EXTERN int uiControlEnabled(uiControl *);
|
||||
_UI_EXTERN void uiControlEnable(uiControl *);
|
||||
_UI_EXTERN void uiControlDisable(uiControl *);
|
||||
|
||||
_UI_EXTERN uiControl *uiAllocControl(size_t n, uint32_t OSsig, uint32_t typesig, const char *typenamestr);
|
||||
_UI_EXTERN void uiFreeControl(uiControl *);
|
||||
|
||||
// TODO make sure all controls have these
|
||||
_UI_EXTERN void uiControlVerifySetParent(uiControl *, uiControl *);
|
||||
_UI_EXTERN int uiControlEnabledToUser(uiControl *);
|
||||
|
||||
_UI_EXTERN void uiUserBugCannotSetParentOnToplevel(const char *type);
|
||||
|
||||
typedef struct uiWindow uiWindow;
|
||||
#define uiWindow(this) ((uiWindow *) (this))
|
||||
_UI_EXTERN char *uiWindowTitle(uiWindow *w);
|
||||
_UI_EXTERN void uiWindowSetTitle(uiWindow *w, const char *title);
|
||||
_UI_EXTERN void uiWindowContentSize(uiWindow *w, int *width, int *height);
|
||||
_UI_EXTERN void uiWindowSetContentSize(uiWindow *w, int width, int height);
|
||||
_UI_EXTERN int uiWindowFullscreen(uiWindow *w);
|
||||
_UI_EXTERN void uiWindowSetFullscreen(uiWindow *w, int fullscreen);
|
||||
_UI_EXTERN void uiWindowOnContentSizeChanged(uiWindow *w, void (*f)(uiWindow *, void *), void *data);
|
||||
_UI_EXTERN void uiWindowOnClosing(uiWindow *w, int (*f)(uiWindow *w, void *data), void *data);
|
||||
_UI_EXTERN int uiWindowBorderless(uiWindow *w);
|
||||
_UI_EXTERN void uiWindowSetBorderless(uiWindow *w, int borderless);
|
||||
_UI_EXTERN void uiWindowSetChild(uiWindow *w, uiControl *child);
|
||||
_UI_EXTERN int uiWindowMargined(uiWindow *w);
|
||||
_UI_EXTERN void uiWindowSetMargined(uiWindow *w, int margined);
|
||||
_UI_EXTERN uiWindow *uiNewWindow(const char *title, int width, int height, int hasMenubar);
|
||||
|
||||
typedef struct uiButton uiButton;
|
||||
#define uiButton(this) ((uiButton *) (this))
|
||||
_UI_EXTERN char *uiButtonText(uiButton *b);
|
||||
_UI_EXTERN void uiButtonSetText(uiButton *b, const char *text);
|
||||
_UI_EXTERN void uiButtonOnClicked(uiButton *b, void (*f)(uiButton *b, void *data), void *data);
|
||||
_UI_EXTERN uiButton *uiNewButton(const char *text);
|
||||
|
||||
typedef struct uiBox uiBox;
|
||||
#define uiBox(this) ((uiBox *) (this))
|
||||
_UI_EXTERN void uiBoxAppend(uiBox *b, uiControl *child, int stretchy);
|
||||
_UI_EXTERN void uiBoxDelete(uiBox *b, int index);
|
||||
_UI_EXTERN int uiBoxPadded(uiBox *b);
|
||||
_UI_EXTERN void uiBoxSetPadded(uiBox *b, int padded);
|
||||
_UI_EXTERN uiBox *uiNewHorizontalBox(void);
|
||||
_UI_EXTERN uiBox *uiNewVerticalBox(void);
|
||||
|
||||
typedef struct uiCheckbox uiCheckbox;
|
||||
#define uiCheckbox(this) ((uiCheckbox *) (this))
|
||||
_UI_EXTERN char *uiCheckboxText(uiCheckbox *c);
|
||||
_UI_EXTERN void uiCheckboxSetText(uiCheckbox *c, const char *text);
|
||||
_UI_EXTERN void uiCheckboxOnToggled(uiCheckbox *c, void (*f)(uiCheckbox *c, void *data), void *data);
|
||||
_UI_EXTERN int uiCheckboxChecked(uiCheckbox *c);
|
||||
_UI_EXTERN void uiCheckboxSetChecked(uiCheckbox *c, int checked);
|
||||
_UI_EXTERN uiCheckbox *uiNewCheckbox(const char *text);
|
||||
|
||||
typedef struct uiEntry uiEntry;
|
||||
#define uiEntry(this) ((uiEntry *) (this))
|
||||
_UI_EXTERN char *uiEntryText(uiEntry *e);
|
||||
_UI_EXTERN void uiEntrySetText(uiEntry *e, const char *text);
|
||||
_UI_EXTERN void uiEntryOnChanged(uiEntry *e, void (*f)(uiEntry *e, void *data), void *data);
|
||||
_UI_EXTERN int uiEntryReadOnly(uiEntry *e);
|
||||
_UI_EXTERN void uiEntrySetReadOnly(uiEntry *e, int readonly);
|
||||
_UI_EXTERN uiEntry *uiNewEntry(void);
|
||||
_UI_EXTERN uiEntry *uiNewPasswordEntry(void);
|
||||
_UI_EXTERN uiEntry *uiNewSearchEntry(void);
|
||||
|
||||
typedef struct uiLabel uiLabel;
|
||||
#define uiLabel(this) ((uiLabel *) (this))
|
||||
_UI_EXTERN char *uiLabelText(uiLabel *l);
|
||||
_UI_EXTERN void uiLabelSetText(uiLabel *l, const char *text);
|
||||
_UI_EXTERN uiLabel *uiNewLabel(const char *text);
|
||||
|
||||
typedef struct uiTab uiTab;
|
||||
#define uiTab(this) ((uiTab *) (this))
|
||||
_UI_EXTERN void uiTabAppend(uiTab *t, const char *name, uiControl *c);
|
||||
_UI_EXTERN void uiTabInsertAt(uiTab *t, const char *name, int before, uiControl *c);
|
||||
_UI_EXTERN void uiTabDelete(uiTab *t, int index);
|
||||
_UI_EXTERN int uiTabNumPages(uiTab *t);
|
||||
_UI_EXTERN int uiTabMargined(uiTab *t, int page);
|
||||
_UI_EXTERN void uiTabSetMargined(uiTab *t, int page, int margined);
|
||||
_UI_EXTERN uiTab *uiNewTab(void);
|
||||
|
||||
typedef struct uiGroup uiGroup;
|
||||
#define uiGroup(this) ((uiGroup *) (this))
|
||||
_UI_EXTERN char *uiGroupTitle(uiGroup *g);
|
||||
_UI_EXTERN void uiGroupSetTitle(uiGroup *g, const char *title);
|
||||
_UI_EXTERN void uiGroupSetChild(uiGroup *g, uiControl *c);
|
||||
_UI_EXTERN int uiGroupMargined(uiGroup *g);
|
||||
_UI_EXTERN void uiGroupSetMargined(uiGroup *g, int margined);
|
||||
_UI_EXTERN uiGroup *uiNewGroup(const char *title);
|
||||
|
||||
// spinbox/slider rules:
|
||||
// setting value outside of range will automatically clamp
|
||||
// initial value is minimum
|
||||
// complaint if min >= max?
|
||||
|
||||
typedef struct uiSpinbox uiSpinbox;
|
||||
#define uiSpinbox(this) ((uiSpinbox *) (this))
|
||||
_UI_EXTERN int uiSpinboxValue(uiSpinbox *s);
|
||||
_UI_EXTERN void uiSpinboxSetValue(uiSpinbox *s, int value);
|
||||
_UI_EXTERN void uiSpinboxOnChanged(uiSpinbox *s, void (*f)(uiSpinbox *s, void *data), void *data);
|
||||
_UI_EXTERN uiSpinbox *uiNewSpinbox(int min, int max);
|
||||
|
||||
typedef struct uiSlider uiSlider;
|
||||
#define uiSlider(this) ((uiSlider *) (this))
|
||||
_UI_EXTERN int uiSliderValue(uiSlider *s);
|
||||
_UI_EXTERN void uiSliderSetValue(uiSlider *s, int value);
|
||||
_UI_EXTERN void uiSliderOnChanged(uiSlider *s, void (*f)(uiSlider *s, void *data), void *data);
|
||||
_UI_EXTERN uiSlider *uiNewSlider(int min, int max);
|
||||
|
||||
typedef struct uiProgressBar uiProgressBar;
|
||||
#define uiProgressBar(this) ((uiProgressBar *) (this))
|
||||
_UI_EXTERN int uiProgressBarValue(uiProgressBar *p);
|
||||
_UI_EXTERN void uiProgressBarSetValue(uiProgressBar *p, int n);
|
||||
_UI_EXTERN uiProgressBar *uiNewProgressBar(void);
|
||||
|
||||
typedef struct uiSeparator uiSeparator;
|
||||
#define uiSeparator(this) ((uiSeparator *) (this))
|
||||
_UI_EXTERN uiSeparator *uiNewHorizontalSeparator(void);
|
||||
_UI_EXTERN uiSeparator *uiNewVerticalSeparator(void);
|
||||
|
||||
typedef struct uiCombobox uiCombobox;
|
||||
#define uiCombobox(this) ((uiCombobox *) (this))
|
||||
_UI_EXTERN void uiComboboxAppend(uiCombobox *c, const char *text);
|
||||
_UI_EXTERN int uiComboboxSelected(uiCombobox *c);
|
||||
_UI_EXTERN void uiComboboxSetSelected(uiCombobox *c, int n);
|
||||
_UI_EXTERN void uiComboboxOnSelected(uiCombobox *c, void (*f)(uiCombobox *c, void *data), void *data);
|
||||
_UI_EXTERN uiCombobox *uiNewCombobox(void);
|
||||
|
||||
typedef struct uiEditableCombobox uiEditableCombobox;
|
||||
#define uiEditableCombobox(this) ((uiEditableCombobox *) (this))
|
||||
_UI_EXTERN void uiEditableComboboxAppend(uiEditableCombobox *c, const char *text);
|
||||
_UI_EXTERN char *uiEditableComboboxText(uiEditableCombobox *c);
|
||||
_UI_EXTERN void uiEditableComboboxSetText(uiEditableCombobox *c, const char *text);
|
||||
// TODO what do we call a function that sets the currently selected item and fills the text field with it? editable comboboxes have no consistent concept of selected item
|
||||
_UI_EXTERN void uiEditableComboboxOnChanged(uiEditableCombobox *c, void (*f)(uiEditableCombobox *c, void *data), void *data);
|
||||
_UI_EXTERN uiEditableCombobox *uiNewEditableCombobox(void);
|
||||
|
||||
typedef struct uiRadioButtons uiRadioButtons;
|
||||
#define uiRadioButtons(this) ((uiRadioButtons *) (this))
|
||||
_UI_EXTERN void uiRadioButtonsAppend(uiRadioButtons *r, const char *text);
|
||||
_UI_EXTERN int uiRadioButtonsSelected(uiRadioButtons *r);
|
||||
_UI_EXTERN void uiRadioButtonsSetSelected(uiRadioButtons *r, int n);
|
||||
_UI_EXTERN void uiRadioButtonsOnSelected(uiRadioButtons *r, void (*f)(uiRadioButtons *, void *), void *data);
|
||||
_UI_EXTERN uiRadioButtons *uiNewRadioButtons(void);
|
||||
|
||||
typedef struct uiDateTimePicker uiDateTimePicker;
|
||||
#define uiDateTimePicker(this) ((uiDateTimePicker *) (this))
|
||||
_UI_EXTERN uiDateTimePicker *uiNewDateTimePicker(void);
|
||||
_UI_EXTERN uiDateTimePicker *uiNewDatePicker(void);
|
||||
_UI_EXTERN uiDateTimePicker *uiNewTimePicker(void);
|
||||
|
||||
// TODO provide a facility for entering tab stops?
|
||||
typedef struct uiMultilineEntry uiMultilineEntry;
|
||||
#define uiMultilineEntry(this) ((uiMultilineEntry *) (this))
|
||||
_UI_EXTERN char *uiMultilineEntryText(uiMultilineEntry *e);
|
||||
_UI_EXTERN void uiMultilineEntrySetText(uiMultilineEntry *e, const char *text);
|
||||
_UI_EXTERN void uiMultilineEntryAppend(uiMultilineEntry *e, const char *text);
|
||||
_UI_EXTERN void uiMultilineEntryOnChanged(uiMultilineEntry *e, void (*f)(uiMultilineEntry *e, void *data), void *data);
|
||||
_UI_EXTERN int uiMultilineEntryReadOnly(uiMultilineEntry *e);
|
||||
_UI_EXTERN void uiMultilineEntrySetReadOnly(uiMultilineEntry *e, int readonly);
|
||||
_UI_EXTERN uiMultilineEntry *uiNewMultilineEntry(void);
|
||||
_UI_EXTERN uiMultilineEntry *uiNewNonWrappingMultilineEntry(void);
|
||||
|
||||
typedef struct uiMenuItem uiMenuItem;
|
||||
#define uiMenuItem(this) ((uiMenuItem *) (this))
|
||||
_UI_EXTERN void uiMenuItemEnable(uiMenuItem *m);
|
||||
_UI_EXTERN void uiMenuItemDisable(uiMenuItem *m);
|
||||
_UI_EXTERN void uiMenuItemOnClicked(uiMenuItem *m, void (*f)(uiMenuItem *sender, uiWindow *window, void *data), void *data);
|
||||
_UI_EXTERN int uiMenuItemChecked(uiMenuItem *m);
|
||||
_UI_EXTERN void uiMenuItemSetChecked(uiMenuItem *m, int checked);
|
||||
|
||||
typedef struct uiMenu uiMenu;
|
||||
#define uiMenu(this) ((uiMenu *) (this))
|
||||
_UI_EXTERN uiMenuItem *uiMenuAppendItem(uiMenu *m, const char *name);
|
||||
_UI_EXTERN uiMenuItem *uiMenuAppendCheckItem(uiMenu *m, const char *name);
|
||||
_UI_EXTERN uiMenuItem *uiMenuAppendQuitItem(uiMenu *m);
|
||||
_UI_EXTERN uiMenuItem *uiMenuAppendPreferencesItem(uiMenu *m);
|
||||
_UI_EXTERN uiMenuItem *uiMenuAppendAboutItem(uiMenu *m);
|
||||
_UI_EXTERN void uiMenuAppendSeparator(uiMenu *m);
|
||||
_UI_EXTERN uiMenu *uiNewMenu(const char *name);
|
||||
|
||||
_UI_EXTERN char *uiOpenFile(uiWindow *parent);
|
||||
_UI_EXTERN char *uiSaveFile(uiWindow *parent);
|
||||
_UI_EXTERN void uiMsgBox(uiWindow *parent, const char *title, const char *description);
|
||||
_UI_EXTERN void uiMsgBoxError(uiWindow *parent, const char *title, const char *description);
|
||||
|
||||
typedef struct uiArea uiArea;
|
||||
typedef struct uiAreaHandler uiAreaHandler;
|
||||
typedef struct uiAreaDrawParams uiAreaDrawParams;
|
||||
typedef struct uiAreaMouseEvent uiAreaMouseEvent;
|
||||
typedef struct uiAreaKeyEvent uiAreaKeyEvent;
|
||||
|
||||
typedef struct uiDrawContext uiDrawContext;
|
||||
|
||||
struct uiAreaHandler {
|
||||
void (*Draw)(uiAreaHandler *, uiArea *, uiAreaDrawParams *);
|
||||
// TODO document that resizes cause a full redraw for non-scrolling areas; implementation-defined for scrolling areas
|
||||
void (*MouseEvent)(uiAreaHandler *, uiArea *, uiAreaMouseEvent *);
|
||||
// TODO document that on first show if the mouse is already in the uiArea then one gets sent with left=0
|
||||
// TODO what about when the area is hidden and then shown again?
|
||||
void (*MouseCrossed)(uiAreaHandler *, uiArea *, int left);
|
||||
void (*DragBroken)(uiAreaHandler *, uiArea *);
|
||||
int (*KeyEvent)(uiAreaHandler *, uiArea *, uiAreaKeyEvent *);
|
||||
};
|
||||
|
||||
// TODO RTL layouts?
|
||||
// TODO reconcile edge and corner naming
|
||||
_UI_ENUM(uiWindowResizeEdge) {
|
||||
uiWindowResizeEdgeLeft,
|
||||
uiWindowResizeEdgeTop,
|
||||
uiWindowResizeEdgeRight,
|
||||
uiWindowResizeEdgeBottom,
|
||||
uiWindowResizeEdgeTopLeft,
|
||||
uiWindowResizeEdgeTopRight,
|
||||
uiWindowResizeEdgeBottomLeft,
|
||||
uiWindowResizeEdgeBottomRight,
|
||||
// TODO have one for keyboard resizes?
|
||||
// TODO GDK doesn't seem to have any others, including for keyboards...
|
||||
// TODO way to bring up the system menu instead?
|
||||
};
|
||||
|
||||
#define uiArea(this) ((uiArea *) (this))
|
||||
// TODO give a better name
|
||||
// TODO document the types of width and height
|
||||
_UI_EXTERN void uiAreaSetSize(uiArea *a, int width, int height);
|
||||
// TODO uiAreaQueueRedraw()
|
||||
_UI_EXTERN void uiAreaQueueRedrawAll(uiArea *a);
|
||||
_UI_EXTERN void uiAreaScrollTo(uiArea *a, double x, double y, double width, double height);
|
||||
// TODO document these can only be called within Mouse() handlers
|
||||
// TODO should these be allowed on scrolling areas?
|
||||
// TODO decide which mouse events should be accepted; Down is the only one guaranteed to work right now
|
||||
// TODO what happens to events after calling this up to and including the next mouse up?
|
||||
// TODO release capture?
|
||||
_UI_EXTERN void uiAreaBeginUserWindowMove(uiArea *a);
|
||||
_UI_EXTERN void uiAreaBeginUserWindowResize(uiArea *a, uiWindowResizeEdge edge);
|
||||
_UI_EXTERN uiArea *uiNewArea(uiAreaHandler *ah);
|
||||
_UI_EXTERN uiArea *uiNewScrollingArea(uiAreaHandler *ah, int width, int height);
|
||||
|
||||
struct uiAreaDrawParams {
|
||||
uiDrawContext *Context;
|
||||
|
||||
// TODO document that this is only defined for nonscrolling areas
|
||||
double AreaWidth;
|
||||
double AreaHeight;
|
||||
|
||||
double ClipX;
|
||||
double ClipY;
|
||||
double ClipWidth;
|
||||
double ClipHeight;
|
||||
};
|
||||
|
||||
typedef struct uiDrawPath uiDrawPath;
|
||||
typedef struct uiDrawBrush uiDrawBrush;
|
||||
typedef struct uiDrawStrokeParams uiDrawStrokeParams;
|
||||
typedef struct uiDrawMatrix uiDrawMatrix;
|
||||
|
||||
typedef struct uiDrawBrushGradientStop uiDrawBrushGradientStop;
|
||||
|
||||
_UI_ENUM(uiDrawBrushType) {
|
||||
uiDrawBrushTypeSolid,
|
||||
uiDrawBrushTypeLinearGradient,
|
||||
uiDrawBrushTypeRadialGradient,
|
||||
uiDrawBrushTypeImage,
|
||||
};
|
||||
|
||||
_UI_ENUM(uiDrawLineCap) {
|
||||
uiDrawLineCapFlat,
|
||||
uiDrawLineCapRound,
|
||||
uiDrawLineCapSquare,
|
||||
};
|
||||
|
||||
_UI_ENUM(uiDrawLineJoin) {
|
||||
uiDrawLineJoinMiter,
|
||||
uiDrawLineJoinRound,
|
||||
uiDrawLineJoinBevel,
|
||||
};
|
||||
|
||||
// this is the default for botoh cairo and Direct2D (in the latter case, from the C++ helper functions)
|
||||
// Core Graphics doesn't explicitly specify a default, but NSBezierPath allows you to choose one, and this is the initial value
|
||||
// so we're good to use it too!
|
||||
#define uiDrawDefaultMiterLimit 10.0
|
||||
|
||||
_UI_ENUM(uiDrawFillMode) {
|
||||
uiDrawFillModeWinding,
|
||||
uiDrawFillModeAlternate,
|
||||
};
|
||||
|
||||
struct uiDrawMatrix {
|
||||
double M11;
|
||||
double M12;
|
||||
double M21;
|
||||
double M22;
|
||||
double M31;
|
||||
double M32;
|
||||
};
|
||||
|
||||
struct uiDrawBrush {
|
||||
uiDrawBrushType Type;
|
||||
|
||||
// solid brushes
|
||||
double R;
|
||||
double G;
|
||||
double B;
|
||||
double A;
|
||||
|
||||
// gradient brushes
|
||||
double X0; // linear: start X, radial: start X
|
||||
double Y0; // linear: start Y, radial: start Y
|
||||
double X1; // linear: end X, radial: outer circle center X
|
||||
double Y1; // linear: end Y, radial: outer circle center Y
|
||||
double OuterRadius; // radial gradients only
|
||||
uiDrawBrushGradientStop *Stops;
|
||||
size_t NumStops;
|
||||
// TODO extend mode
|
||||
// cairo: none, repeat, reflect, pad; no individual control
|
||||
// Direct2D: repeat, reflect, pad; no individual control
|
||||
// Core Graphics: none, pad; before and after individually
|
||||
// TODO cairo documentation is inconsistent about pad
|
||||
|
||||
// TODO images
|
||||
|
||||
// TODO transforms
|
||||
};
|
||||
|
||||
struct uiDrawBrushGradientStop {
|
||||
double Pos;
|
||||
double R;
|
||||
double G;
|
||||
double B;
|
||||
double A;
|
||||
};
|
||||
|
||||
struct uiDrawStrokeParams {
|
||||
uiDrawLineCap Cap;
|
||||
uiDrawLineJoin Join;
|
||||
// TODO what if this is 0? on windows there will be a crash with dashing
|
||||
double Thickness;
|
||||
double MiterLimit;
|
||||
double *Dashes;
|
||||
// TOOD what if this is 1 on Direct2D?
|
||||
// TODO what if a dash is 0 on Cairo or Quartz?
|
||||
size_t NumDashes;
|
||||
double DashPhase;
|
||||
};
|
||||
|
||||
_UI_EXTERN uiDrawPath *uiDrawNewPath(uiDrawFillMode fillMode);
|
||||
_UI_EXTERN void uiDrawFreePath(uiDrawPath *p);
|
||||
|
||||
_UI_EXTERN void uiDrawPathNewFigure(uiDrawPath *p, double x, double y);
|
||||
_UI_EXTERN void uiDrawPathNewFigureWithArc(uiDrawPath *p, double xCenter, double yCenter, double radius, double startAngle, double sweep, int negative);
|
||||
_UI_EXTERN void uiDrawPathLineTo(uiDrawPath *p, double x, double y);
|
||||
// notes: angles are both relative to 0 and go counterclockwise
|
||||
// TODO is the initial line segment on cairo and OS X a proper join?
|
||||
// TODO what if sweep < 0?
|
||||
_UI_EXTERN void uiDrawPathArcTo(uiDrawPath *p, double xCenter, double yCenter, double radius, double startAngle, double sweep, int negative);
|
||||
_UI_EXTERN void uiDrawPathBezierTo(uiDrawPath *p, double c1x, double c1y, double c2x, double c2y, double endX, double endY);
|
||||
// TODO quadratic bezier
|
||||
_UI_EXTERN void uiDrawPathCloseFigure(uiDrawPath *p);
|
||||
|
||||
// TODO effect of these when a figure is already started
|
||||
_UI_EXTERN void uiDrawPathAddRectangle(uiDrawPath *p, double x, double y, double width, double height);
|
||||
|
||||
_UI_EXTERN void uiDrawPathEnd(uiDrawPath *p);
|
||||
|
||||
_UI_EXTERN void uiDrawStroke(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b, uiDrawStrokeParams *p);
|
||||
_UI_EXTERN void uiDrawFill(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b);
|
||||
|
||||
// TODO primitives:
|
||||
// - rounded rectangles
|
||||
// - elliptical arcs
|
||||
// - quadratic bezier curves
|
||||
|
||||
_UI_EXTERN void uiDrawMatrixSetIdentity(uiDrawMatrix *m);
|
||||
_UI_EXTERN void uiDrawMatrixTranslate(uiDrawMatrix *m, double x, double y);
|
||||
_UI_EXTERN void uiDrawMatrixScale(uiDrawMatrix *m, double xCenter, double yCenter, double x, double y);
|
||||
_UI_EXTERN void uiDrawMatrixRotate(uiDrawMatrix *m, double x, double y, double amount);
|
||||
_UI_EXTERN void uiDrawMatrixSkew(uiDrawMatrix *m, double x, double y, double xamount, double yamount);
|
||||
_UI_EXTERN void uiDrawMatrixMultiply(uiDrawMatrix *dest, uiDrawMatrix *src);
|
||||
_UI_EXTERN int uiDrawMatrixInvertible(uiDrawMatrix *m);
|
||||
_UI_EXTERN int uiDrawMatrixInvert(uiDrawMatrix *m);
|
||||
_UI_EXTERN void uiDrawMatrixTransformPoint(uiDrawMatrix *m, double *x, double *y);
|
||||
_UI_EXTERN void uiDrawMatrixTransformSize(uiDrawMatrix *m, double *x, double *y);
|
||||
|
||||
_UI_EXTERN void uiDrawTransform(uiDrawContext *c, uiDrawMatrix *m);
|
||||
|
||||
// TODO add a uiDrawPathStrokeToFill() or something like that
|
||||
_UI_EXTERN void uiDrawClip(uiDrawContext *c, uiDrawPath *path);
|
||||
|
||||
_UI_EXTERN void uiDrawSave(uiDrawContext *c);
|
||||
_UI_EXTERN void uiDrawRestore(uiDrawContext *c);
|
||||
|
||||
// TODO manage the use of Text, Font, and TextFont, and of the uiDrawText prefix in general
|
||||
|
||||
///// TODO reconsider this
|
||||
typedef struct uiDrawFontFamilies uiDrawFontFamilies;
|
||||
|
||||
_UI_EXTERN uiDrawFontFamilies *uiDrawListFontFamilies(void);
|
||||
_UI_EXTERN int uiDrawFontFamiliesNumFamilies(uiDrawFontFamilies *ff);
|
||||
_UI_EXTERN char *uiDrawFontFamiliesFamily(uiDrawFontFamilies *ff, int n);
|
||||
_UI_EXTERN void uiDrawFreeFontFamilies(uiDrawFontFamilies *ff);
|
||||
///// END TODO
|
||||
|
||||
typedef struct uiDrawTextLayout uiDrawTextLayout;
|
||||
typedef struct uiDrawTextFont uiDrawTextFont;
|
||||
typedef struct uiDrawTextFontDescriptor uiDrawTextFontDescriptor;
|
||||
typedef struct uiDrawTextFontMetrics uiDrawTextFontMetrics;
|
||||
|
||||
_UI_ENUM(uiDrawTextWeight) {
|
||||
uiDrawTextWeightThin,
|
||||
uiDrawTextWeightUltraLight,
|
||||
uiDrawTextWeightLight,
|
||||
uiDrawTextWeightBook,
|
||||
uiDrawTextWeightNormal,
|
||||
uiDrawTextWeightMedium,
|
||||
uiDrawTextWeightSemiBold,
|
||||
uiDrawTextWeightBold,
|
||||
uiDrawTextWeightUltraBold,
|
||||
uiDrawTextWeightHeavy,
|
||||
uiDrawTextWeightUltraHeavy,
|
||||
};
|
||||
|
||||
_UI_ENUM(uiDrawTextItalic) {
|
||||
uiDrawTextItalicNormal,
|
||||
uiDrawTextItalicOblique,
|
||||
uiDrawTextItalicItalic,
|
||||
};
|
||||
|
||||
_UI_ENUM(uiDrawTextStretch) {
|
||||
uiDrawTextStretchUltraCondensed,
|
||||
uiDrawTextStretchExtraCondensed,
|
||||
uiDrawTextStretchCondensed,
|
||||
uiDrawTextStretchSemiCondensed,
|
||||
uiDrawTextStretchNormal,
|
||||
uiDrawTextStretchSemiExpanded,
|
||||
uiDrawTextStretchExpanded,
|
||||
uiDrawTextStretchExtraExpanded,
|
||||
uiDrawTextStretchUltraExpanded,
|
||||
};
|
||||
|
||||
struct uiDrawTextFontDescriptor {
|
||||
const char *Family;
|
||||
double Size;
|
||||
uiDrawTextWeight Weight;
|
||||
uiDrawTextItalic Italic;
|
||||
uiDrawTextStretch Stretch;
|
||||
};
|
||||
|
||||
struct uiDrawTextFontMetrics {
|
||||
double Ascent;
|
||||
double Descent;
|
||||
double Leading;
|
||||
// TODO do these two mean the same across all platforms?
|
||||
double UnderlinePos;
|
||||
double UnderlineThickness;
|
||||
};
|
||||
|
||||
_UI_EXTERN uiDrawTextFont *uiDrawLoadClosestFont(const uiDrawTextFontDescriptor *desc);
|
||||
_UI_EXTERN void uiDrawFreeTextFont(uiDrawTextFont *font);
|
||||
_UI_EXTERN uintptr_t uiDrawTextFontHandle(uiDrawTextFont *font);
|
||||
_UI_EXTERN void uiDrawTextFontDescribe(uiDrawTextFont *font, uiDrawTextFontDescriptor *desc);
|
||||
// TODO make copy with given attributes methods?
|
||||
// TODO yuck this name
|
||||
_UI_EXTERN void uiDrawTextFontGetMetrics(uiDrawTextFont *font, uiDrawTextFontMetrics *metrics);
|
||||
|
||||
// TODO initial line spacing? and what about leading?
|
||||
_UI_EXTERN uiDrawTextLayout *uiDrawNewTextLayout(const char *text, uiDrawTextFont *defaultFont, double width);
|
||||
_UI_EXTERN void uiDrawFreeTextLayout(uiDrawTextLayout *layout);
|
||||
// TODO get width
|
||||
_UI_EXTERN void uiDrawTextLayoutSetWidth(uiDrawTextLayout *layout, double width);
|
||||
_UI_EXTERN void uiDrawTextLayoutExtents(uiDrawTextLayout *layout, double *width, double *height);
|
||||
|
||||
// and the attributes that you can set on a text layout
|
||||
_UI_EXTERN void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, int startChar, int endChar, double r, double g, double b, double a);
|
||||
|
||||
_UI_EXTERN void uiDrawText(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout);
|
||||
|
||||
_UI_ENUM(uiModifiers) {
|
||||
uiModifierCtrl = 1 << 0,
|
||||
uiModifierAlt = 1 << 1,
|
||||
uiModifierShift = 1 << 2,
|
||||
uiModifierSuper = 1 << 3,
|
||||
};
|
||||
|
||||
// TODO document drag captures
|
||||
struct uiAreaMouseEvent {
|
||||
// TODO document what these mean for scrolling areas
|
||||
double X;
|
||||
double Y;
|
||||
|
||||
// TODO see draw above
|
||||
double AreaWidth;
|
||||
double AreaHeight;
|
||||
|
||||
int Down;
|
||||
int Up;
|
||||
|
||||
int Count;
|
||||
|
||||
uiModifiers Modifiers;
|
||||
|
||||
uint64_t Held1To64;
|
||||
};
|
||||
|
||||
_UI_ENUM(uiExtKey) {
|
||||
uiExtKeyEscape = 1,
|
||||
uiExtKeyInsert, // equivalent to "Help" on Apple keyboards
|
||||
uiExtKeyDelete,
|
||||
uiExtKeyHome,
|
||||
uiExtKeyEnd,
|
||||
uiExtKeyPageUp,
|
||||
uiExtKeyPageDown,
|
||||
uiExtKeyUp,
|
||||
uiExtKeyDown,
|
||||
uiExtKeyLeft,
|
||||
uiExtKeyRight,
|
||||
uiExtKeyF1, // F1..F12 are guaranteed to be consecutive
|
||||
uiExtKeyF2,
|
||||
uiExtKeyF3,
|
||||
uiExtKeyF4,
|
||||
uiExtKeyF5,
|
||||
uiExtKeyF6,
|
||||
uiExtKeyF7,
|
||||
uiExtKeyF8,
|
||||
uiExtKeyF9,
|
||||
uiExtKeyF10,
|
||||
uiExtKeyF11,
|
||||
uiExtKeyF12,
|
||||
uiExtKeyN0, // numpad keys; independent of Num Lock state
|
||||
uiExtKeyN1, // N0..N9 are guaranteed to be consecutive
|
||||
uiExtKeyN2,
|
||||
uiExtKeyN3,
|
||||
uiExtKeyN4,
|
||||
uiExtKeyN5,
|
||||
uiExtKeyN6,
|
||||
uiExtKeyN7,
|
||||
uiExtKeyN8,
|
||||
uiExtKeyN9,
|
||||
uiExtKeyNDot,
|
||||
uiExtKeyNEnter,
|
||||
uiExtKeyNAdd,
|
||||
uiExtKeyNSubtract,
|
||||
uiExtKeyNMultiply,
|
||||
uiExtKeyNDivide,
|
||||
};
|
||||
|
||||
struct uiAreaKeyEvent {
|
||||
char Key;
|
||||
uiExtKey ExtKey;
|
||||
uiModifiers Modifier;
|
||||
|
||||
uiModifiers Modifiers;
|
||||
|
||||
int Up;
|
||||
};
|
||||
|
||||
typedef struct uiFontButton uiFontButton;
|
||||
#define uiFontButton(this) ((uiFontButton *) (this))
|
||||
// TODO document this returns a new font
|
||||
_UI_EXTERN uiDrawTextFont *uiFontButtonFont(uiFontButton *b);
|
||||
// TOOD SetFont, mechanics
|
||||
_UI_EXTERN void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data);
|
||||
_UI_EXTERN uiFontButton *uiNewFontButton(void);
|
||||
|
||||
typedef struct uiColorButton uiColorButton;
|
||||
#define uiColorButton(this) ((uiColorButton *) (this))
|
||||
_UI_EXTERN void uiColorButtonColor(uiColorButton *b, double *r, double *g, double *bl, double *a);
|
||||
_UI_EXTERN void uiColorButtonSetColor(uiColorButton *b, double r, double g, double bl, double a);
|
||||
_UI_EXTERN void uiColorButtonOnChanged(uiColorButton *b, void (*f)(uiColorButton *, void *), void *data);
|
||||
_UI_EXTERN uiColorButton *uiNewColorButton(void);
|
||||
|
||||
typedef struct uiForm uiForm;
|
||||
#define uiForm(this) ((uiForm *) (this))
|
||||
_UI_EXTERN void uiFormAppend(uiForm *f, const char *label, uiControl *c, int stretchy);
|
||||
_UI_EXTERN void uiFormDelete(uiForm *f, int index);
|
||||
_UI_EXTERN int uiFormPadded(uiForm *f);
|
||||
_UI_EXTERN void uiFormSetPadded(uiForm *f, int padded);
|
||||
_UI_EXTERN uiForm *uiNewForm(void);
|
||||
|
||||
_UI_ENUM(uiAlign) {
|
||||
uiAlignFill,
|
||||
uiAlignStart,
|
||||
uiAlignCenter,
|
||||
uiAlignEnd,
|
||||
};
|
||||
|
||||
_UI_ENUM(uiAt) {
|
||||
uiAtLeading,
|
||||
uiAtTop,
|
||||
uiAtTrailing,
|
||||
uiAtBottom,
|
||||
};
|
||||
|
||||
typedef struct uiGrid uiGrid;
|
||||
#define uiGrid(this) ((uiGrid *) (this))
|
||||
_UI_EXTERN void uiGridAppend(uiGrid *g, uiControl *c, int left, int top, int xspan, int yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign);
|
||||
_UI_EXTERN void uiGridInsertAt(uiGrid *g, uiControl *c, uiControl *existing, uiAt at, int xspan, int yspan, int hexpand, uiAlign halign, int vexpand, uiAlign valign);
|
||||
_UI_EXTERN int uiGridPadded(uiGrid *g);
|
||||
_UI_EXTERN void uiGridSetPadded(uiGrid *g, int padded);
|
||||
_UI_EXTERN uiGrid *uiNewGrid(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
224
deps/libui/ui_darwin.h
vendored
224
deps/libui/ui_darwin.h
vendored
@ -1,224 +0,0 @@
|
||||
// 7 april 2015
|
||||
|
||||
/*
|
||||
This file assumes that you have imported <Cocoa/Cocoa.h> and "ui.h" beforehand. It provides API-specific functions for interfacing with foreign controls on Mac OS X.
|
||||
*/
|
||||
|
||||
#ifndef __LIBUI_UI_DARWIN_H__
|
||||
#define __LIBUI_UI_DARWIN_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct uiDarwinControl uiDarwinControl;
|
||||
struct uiDarwinControl {
|
||||
uiControl c;
|
||||
uiControl *parent;
|
||||
BOOL enabled;
|
||||
BOOL visible;
|
||||
void (*SyncEnableState)(uiDarwinControl *, int);
|
||||
void (*SetSuperview)(uiDarwinControl *, NSView *);
|
||||
BOOL (*HugsTrailingEdge)(uiDarwinControl *);
|
||||
BOOL (*HugsBottom)(uiDarwinControl *);
|
||||
void (*ChildEdgeHuggingChanged)(uiDarwinControl *);
|
||||
NSLayoutPriority (*HuggingPriority)(uiDarwinControl *, NSLayoutConstraintOrientation);
|
||||
void (*SetHuggingPriority)(uiDarwinControl *, NSLayoutPriority, NSLayoutConstraintOrientation);
|
||||
void (*ChildVisibilityChanged)(uiDarwinControl *);
|
||||
};
|
||||
#define uiDarwinControl(this) ((uiDarwinControl *) (this))
|
||||
// TODO document
|
||||
_UI_EXTERN void uiDarwinControlSyncEnableState(uiDarwinControl *, int);
|
||||
_UI_EXTERN void uiDarwinControlSetSuperview(uiDarwinControl *, NSView *);
|
||||
_UI_EXTERN BOOL uiDarwinControlHugsTrailingEdge(uiDarwinControl *);
|
||||
_UI_EXTERN BOOL uiDarwinControlHugsBottom(uiDarwinControl *);
|
||||
_UI_EXTERN void uiDarwinControlChildEdgeHuggingChanged(uiDarwinControl *);
|
||||
_UI_EXTERN NSLayoutPriority uiDarwinControlHuggingPriority(uiDarwinControl *, NSLayoutConstraintOrientation);
|
||||
_UI_EXTERN void uiDarwinControlSetHuggingPriority(uiDarwinControl *, NSLayoutPriority, NSLayoutConstraintOrientation);
|
||||
_UI_EXTERN void uiDarwinControlChildVisibilityChanged(uiDarwinControl *);
|
||||
|
||||
#define uiDarwinControlDefaultDestroy(type, handlefield) \
|
||||
static void type ## Destroy(uiControl *c) \
|
||||
{ \
|
||||
[type(c)->handlefield release]; \
|
||||
uiFreeControl(c); \
|
||||
}
|
||||
#define uiDarwinControlDefaultHandle(type, handlefield) \
|
||||
static uintptr_t type ## Handle(uiControl *c) \
|
||||
{ \
|
||||
return (uintptr_t) (type(c)->handlefield); \
|
||||
}
|
||||
#define uiDarwinControlDefaultParent(type, handlefield) \
|
||||
static uiControl *type ## Parent(uiControl *c) \
|
||||
{ \
|
||||
return uiDarwinControl(c)->parent; \
|
||||
}
|
||||
#define uiDarwinControlDefaultSetParent(type, handlefield) \
|
||||
static void type ## SetParent(uiControl *c, uiControl *parent) \
|
||||
{ \
|
||||
uiControlVerifySetParent(c, parent); \
|
||||
uiDarwinControl(c)->parent = parent; \
|
||||
}
|
||||
#define uiDarwinControlDefaultToplevel(type, handlefield) \
|
||||
static int type ## Toplevel(uiControl *c) \
|
||||
{ \
|
||||
return 0; \
|
||||
}
|
||||
#define uiDarwinControlDefaultVisible(type, handlefield) \
|
||||
static int type ## Visible(uiControl *c) \
|
||||
{ \
|
||||
return uiDarwinControl(c)->visible; \
|
||||
}
|
||||
#define uiDarwinControlDefaultShow(type, handlefield) \
|
||||
static void type ## Show(uiControl *c) \
|
||||
{ \
|
||||
uiDarwinControl(c)->visible = YES; \
|
||||
[type(c)->handlefield setHidden:NO]; \
|
||||
uiDarwinNotifyVisibilityChanged(uiDarwinControl(c)); \
|
||||
}
|
||||
#define uiDarwinControlDefaultHide(type, handlefield) \
|
||||
static void type ## Hide(uiControl *c) \
|
||||
{ \
|
||||
uiDarwinControl(c)->visible = NO; \
|
||||
[type(c)->handlefield setHidden:YES]; \
|
||||
uiDarwinNotifyVisibilityChanged(uiDarwinControl(c)); \
|
||||
}
|
||||
#define uiDarwinControlDefaultEnabled(type, handlefield) \
|
||||
static int type ## Enabled(uiControl *c) \
|
||||
{ \
|
||||
return uiDarwinControl(c)->enabled; \
|
||||
}
|
||||
#define uiDarwinControlDefaultEnable(type, handlefield) \
|
||||
static void type ## Enable(uiControl *c) \
|
||||
{ \
|
||||
uiDarwinControl(c)->enabled = YES; \
|
||||
uiDarwinControlSyncEnableState(uiDarwinControl(c), uiControlEnabledToUser(c)); \
|
||||
}
|
||||
#define uiDarwinControlDefaultDisable(type, handlefield) \
|
||||
static void type ## Disable(uiControl *c) \
|
||||
{ \
|
||||
uiDarwinControl(c)->enabled = NO; \
|
||||
uiDarwinControlSyncEnableState(uiDarwinControl(c), uiControlEnabledToUser(c)); \
|
||||
}
|
||||
#define uiDarwinControlDefaultSyncEnableState(type, handlefield) \
|
||||
static void type ## SyncEnableState(uiDarwinControl *c, int enabled) \
|
||||
{ \
|
||||
if (uiDarwinShouldStopSyncEnableState(c, enabled)) \
|
||||
return; \
|
||||
if ([type(c)->handlefield respondsToSelector:@selector(setEnabled:)]) \
|
||||
[((id) (type(c)->handlefield)) setEnabled:enabled]; /* id cast to make compiler happy; thanks mikeash in irc.freenode.net/#macdev */ \
|
||||
}
|
||||
#define uiDarwinControlDefaultSetSuperview(type, handlefield) \
|
||||
static void type ## SetSuperview(uiDarwinControl *c, NSView *superview) \
|
||||
{ \
|
||||
[type(c)->handlefield setTranslatesAutoresizingMaskIntoConstraints:NO]; \
|
||||
if (superview == nil) \
|
||||
[type(c)->handlefield removeFromSuperview]; \
|
||||
else \
|
||||
[superview addSubview:type(c)->handlefield]; \
|
||||
}
|
||||
#define uiDarwinControlDefaultHugsTrailingEdge(type, handlefield) \
|
||||
static BOOL type ## HugsTrailingEdge(uiDarwinControl *c) \
|
||||
{ \
|
||||
return YES; /* always hug by default */ \
|
||||
}
|
||||
#define uiDarwinControlDefaultHugsBottom(type, handlefield) \
|
||||
static BOOL type ## HugsBottom(uiDarwinControl *c) \
|
||||
{ \
|
||||
return YES; /* always hug by default */ \
|
||||
}
|
||||
#define uiDarwinControlDefaultChildEdgeHuggingChanged(type, handlefield) \
|
||||
static void type ## ChildEdgeHuggingChanged(uiDarwinControl *c) \
|
||||
{ \
|
||||
/* do nothing */ \
|
||||
}
|
||||
#define uiDarwinControlDefaultHuggingPriority(type, handlefield) \
|
||||
static NSLayoutPriority type ## HuggingPriority(uiDarwinControl *c, NSLayoutConstraintOrientation orientation) \
|
||||
{ \
|
||||
return [type(c)->handlefield contentHuggingPriorityForOrientation:orientation]; \
|
||||
}
|
||||
#define uiDarwinControlDefaultSetHuggingPriority(type, handlefield) \
|
||||
static void type ## SetHuggingPriority(uiDarwinControl *c, NSLayoutPriority priority, NSLayoutConstraintOrientation orientation) \
|
||||
{ \
|
||||
[type(c)->handlefield setContentHuggingPriority:priority forOrientation:orientation]; \
|
||||
}
|
||||
#define uiDarwinControlDefaultChildVisibilityChanged(type, handlefield) \
|
||||
static void type ## ChildVisibilityChanged(uiDarwinControl *c) \
|
||||
{ \
|
||||
/* do nothing */ \
|
||||
}
|
||||
|
||||
#define uiDarwinControlAllDefaultsExceptDestroy(type, handlefield) \
|
||||
uiDarwinControlDefaultHandle(type, handlefield) \
|
||||
uiDarwinControlDefaultParent(type, handlefield) \
|
||||
uiDarwinControlDefaultSetParent(type, handlefield) \
|
||||
uiDarwinControlDefaultToplevel(type, handlefield) \
|
||||
uiDarwinControlDefaultVisible(type, handlefield) \
|
||||
uiDarwinControlDefaultShow(type, handlefield) \
|
||||
uiDarwinControlDefaultHide(type, handlefield) \
|
||||
uiDarwinControlDefaultEnabled(type, handlefield) \
|
||||
uiDarwinControlDefaultEnable(type, handlefield) \
|
||||
uiDarwinControlDefaultDisable(type, handlefield) \
|
||||
uiDarwinControlDefaultSyncEnableState(type, handlefield) \
|
||||
uiDarwinControlDefaultSetSuperview(type, handlefield) \
|
||||
uiDarwinControlDefaultHugsTrailingEdge(type, handlefield) \
|
||||
uiDarwinControlDefaultHugsBottom(type, handlefield) \
|
||||
uiDarwinControlDefaultChildEdgeHuggingChanged(type, handlefield) \
|
||||
uiDarwinControlDefaultHuggingPriority(type, handlefield) \
|
||||
uiDarwinControlDefaultSetHuggingPriority(type, handlefield) \
|
||||
uiDarwinControlDefaultChildVisibilityChanged(type, handlefield)
|
||||
|
||||
#define uiDarwinControlAllDefaults(type, handlefield) \
|
||||
uiDarwinControlDefaultDestroy(type, handlefield) \
|
||||
uiDarwinControlAllDefaultsExceptDestroy(type, handlefield)
|
||||
|
||||
// TODO document
|
||||
#define uiDarwinNewControl(type, var) \
|
||||
var = type(uiDarwinAllocControl(sizeof (type), type ## Signature, #type)); \
|
||||
uiControl(var)->Destroy = type ## Destroy; \
|
||||
uiControl(var)->Handle = type ## Handle; \
|
||||
uiControl(var)->Parent = type ## Parent; \
|
||||
uiControl(var)->SetParent = type ## SetParent; \
|
||||
uiControl(var)->Toplevel = type ## Toplevel; \
|
||||
uiControl(var)->Visible = type ## Visible; \
|
||||
uiControl(var)->Show = type ## Show; \
|
||||
uiControl(var)->Hide = type ## Hide; \
|
||||
uiControl(var)->Enabled = type ## Enabled; \
|
||||
uiControl(var)->Enable = type ## Enable; \
|
||||
uiControl(var)->Disable = type ## Disable; \
|
||||
uiDarwinControl(var)->SyncEnableState = type ## SyncEnableState; \
|
||||
uiDarwinControl(var)->SetSuperview = type ## SetSuperview; \
|
||||
uiDarwinControl(var)->HugsTrailingEdge = type ## HugsTrailingEdge; \
|
||||
uiDarwinControl(var)->HugsBottom = type ## HugsBottom; \
|
||||
uiDarwinControl(var)->ChildEdgeHuggingChanged = type ## ChildEdgeHuggingChanged; \
|
||||
uiDarwinControl(var)->HuggingPriority = type ## HuggingPriority; \
|
||||
uiDarwinControl(var)->SetHuggingPriority = type ## SetHuggingPriority; \
|
||||
uiDarwinControl(var)->ChildVisibilityChanged = type ## ChildVisibilityChanged; \
|
||||
uiDarwinControl(var)->visible = YES; \
|
||||
uiDarwinControl(var)->enabled = YES;
|
||||
// TODO document
|
||||
_UI_EXTERN uiDarwinControl *uiDarwinAllocControl(size_t n, uint32_t typesig, const char *typenamestr);
|
||||
|
||||
// Use this function as a shorthand for setting control fonts.
|
||||
_UI_EXTERN void uiDarwinSetControlFont(NSControl *c, NSControlSize size);
|
||||
|
||||
// You can use this function from within your control implementations to return text strings that can be freed with uiFreeText().
|
||||
_UI_EXTERN char *uiDarwinNSStringToText(NSString *);
|
||||
|
||||
// TODO document
|
||||
_UI_EXTERN BOOL uiDarwinShouldStopSyncEnableState(uiDarwinControl *, BOOL);
|
||||
|
||||
// TODO document
|
||||
_UI_EXTERN void uiDarwinNotifyEdgeHuggingChanged(uiDarwinControl *);
|
||||
_UI_EXTERN void uiDarwinNotifyVisibilityChanged(uiDarwinControl *c);
|
||||
|
||||
// TODO document
|
||||
// TODO document that values should not be cached
|
||||
_UI_EXTERN CGFloat uiDarwinMarginAmount(void *reserved);
|
||||
_UI_EXTERN CGFloat uiDarwinPaddingAmount(void *reserved);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
140
deps/libui/ui_unix.h
vendored
140
deps/libui/ui_unix.h
vendored
@ -1,140 +0,0 @@
|
||||
// 7 april 2015
|
||||
|
||||
/*
|
||||
This file assumes that you have included <gtk/gtk.h> and "ui.h" beforehand. It provides API-specific functions for interfacing with foreign controls on Unix systems that use GTK+ to provide their UI (currently all except Mac OS X).
|
||||
*/
|
||||
|
||||
#ifndef __LIBUI_UI_UNIX_H__
|
||||
#define __LIBUI_UI_UNIX_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct uiUnixControl uiUnixControl;
|
||||
struct uiUnixControl {
|
||||
uiControl c;
|
||||
uiControl *parent;
|
||||
gboolean addedBefore;
|
||||
void (*SetContainer)(uiUnixControl *, GtkContainer *, gboolean);
|
||||
};
|
||||
#define uiUnixControl(this) ((uiUnixControl *) (this))
|
||||
// TODO document
|
||||
_UI_EXTERN void uiUnixControlSetContainer(uiUnixControl *, GtkContainer *, gboolean);
|
||||
|
||||
#define uiUnixControlDefaultDestroy(type) \
|
||||
static void type ## Destroy(uiControl *c) \
|
||||
{ \
|
||||
/* TODO is this safe on floating refs? */ \
|
||||
g_object_unref(type(c)->widget); \
|
||||
uiFreeControl(c); \
|
||||
}
|
||||
#define uiUnixControlDefaultHandle(type) \
|
||||
static uintptr_t type ## Handle(uiControl *c) \
|
||||
{ \
|
||||
return (uintptr_t) (type(c)->widget); \
|
||||
}
|
||||
#define uiUnixControlDefaultParent(type) \
|
||||
static uiControl *type ## Parent(uiControl *c) \
|
||||
{ \
|
||||
return uiUnixControl(c)->parent; \
|
||||
}
|
||||
#define uiUnixControlDefaultSetParent(type) \
|
||||
static void type ## SetParent(uiControl *c, uiControl *parent) \
|
||||
{ \
|
||||
uiControlVerifySetParent(c, parent); \
|
||||
uiUnixControl(c)->parent = parent; \
|
||||
}
|
||||
#define uiUnixControlDefaultToplevel(type) \
|
||||
static int type ## Toplevel(uiControl *c) \
|
||||
{ \
|
||||
return 0; \
|
||||
}
|
||||
#define uiUnixControlDefaultVisible(type) \
|
||||
static int type ## Visible(uiControl *c) \
|
||||
{ \
|
||||
return gtk_widget_get_visible(type(c)->widget); \
|
||||
}
|
||||
#define uiUnixControlDefaultShow(type) \
|
||||
static void type ## Show(uiControl *c) \
|
||||
{ \
|
||||
gtk_widget_show(type(c)->widget); \
|
||||
}
|
||||
#define uiUnixControlDefaultHide(type) \
|
||||
static void type ## Hide(uiControl *c) \
|
||||
{ \
|
||||
gtk_widget_hide(type(c)->widget); \
|
||||
}
|
||||
#define uiUnixControlDefaultEnabled(type) \
|
||||
static int type ## Enabled(uiControl *c) \
|
||||
{ \
|
||||
return gtk_widget_get_sensitive(type(c)->widget); \
|
||||
}
|
||||
#define uiUnixControlDefaultEnable(type) \
|
||||
static void type ## Enable(uiControl *c) \
|
||||
{ \
|
||||
gtk_widget_set_sensitive(type(c)->widget, TRUE); \
|
||||
}
|
||||
#define uiUnixControlDefaultDisable(type) \
|
||||
static void type ## Disable(uiControl *c) \
|
||||
{ \
|
||||
gtk_widget_set_sensitive(type(c)->widget, FALSE); \
|
||||
}
|
||||
// TODO this whole addedBefore stuff is a MASSIVE HACK.
|
||||
#define uiUnixControlDefaultSetContainer(type) \
|
||||
static void type ## SetContainer(uiUnixControl *c, GtkContainer *container, gboolean remove) \
|
||||
{ \
|
||||
if (!uiUnixControl(c)->addedBefore) { \
|
||||
g_object_ref_sink(type(c)->widget); /* our own reference, which we release in Destroy() */ \
|
||||
gtk_widget_show(type(c)->widget); \
|
||||
uiUnixControl(c)->addedBefore = TRUE; \
|
||||
} \
|
||||
if (remove) \
|
||||
gtk_container_remove(container, type(c)->widget); \
|
||||
else \
|
||||
gtk_container_add(container, type(c)->widget); \
|
||||
}
|
||||
|
||||
#define uiUnixControlAllDefaultsExceptDestroy(type) \
|
||||
uiUnixControlDefaultHandle(type) \
|
||||
uiUnixControlDefaultParent(type) \
|
||||
uiUnixControlDefaultSetParent(type) \
|
||||
uiUnixControlDefaultToplevel(type) \
|
||||
uiUnixControlDefaultVisible(type) \
|
||||
uiUnixControlDefaultShow(type) \
|
||||
uiUnixControlDefaultHide(type) \
|
||||
uiUnixControlDefaultEnabled(type) \
|
||||
uiUnixControlDefaultEnable(type) \
|
||||
uiUnixControlDefaultDisable(type) \
|
||||
uiUnixControlDefaultSetContainer(type)
|
||||
|
||||
#define uiUnixControlAllDefaults(type) \
|
||||
uiUnixControlDefaultDestroy(type) \
|
||||
uiUnixControlAllDefaultsExceptDestroy(type)
|
||||
|
||||
// TODO document
|
||||
#define uiUnixNewControl(type, var) \
|
||||
var = type(uiUnixAllocControl(sizeof (type), type ## Signature, #type)); \
|
||||
uiControl(var)->Destroy = type ## Destroy; \
|
||||
uiControl(var)->Handle = type ## Handle; \
|
||||
uiControl(var)->Parent = type ## Parent; \
|
||||
uiControl(var)->SetParent = type ## SetParent; \
|
||||
uiControl(var)->Toplevel = type ## Toplevel; \
|
||||
uiControl(var)->Visible = type ## Visible; \
|
||||
uiControl(var)->Show = type ## Show; \
|
||||
uiControl(var)->Hide = type ## Hide; \
|
||||
uiControl(var)->Enabled = type ## Enabled; \
|
||||
uiControl(var)->Enable = type ## Enable; \
|
||||
uiControl(var)->Disable = type ## Disable; \
|
||||
uiUnixControl(var)->SetContainer = type ## SetContainer;
|
||||
// TODO document
|
||||
_UI_EXTERN uiUnixControl *uiUnixAllocControl(size_t n, uint32_t typesig, const char *typenamestr);
|
||||
|
||||
// uiUnixStrdupText() takes the given string and produces a copy of it suitable for being freed by uiFreeText().
|
||||
_UI_EXTERN char *uiUnixStrdupText(const char *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
267
deps/libui/ui_windows.h
vendored
267
deps/libui/ui_windows.h
vendored
@ -1,267 +0,0 @@
|
||||
// 21 april 2016
|
||||
|
||||
/*
|
||||
This file assumes that you have included <windows.h> and "ui.h" beforehand. It provides API-specific functions for interfacing with foreign controls in Windows.
|
||||
*/
|
||||
|
||||
#ifndef __LIBUI_UI_WINDOWS_H__
|
||||
#define __LIBUI_UI_WINDOWS_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct uiWindowsSizing uiWindowsSizing;
|
||||
|
||||
typedef struct uiWindowsControl uiWindowsControl;
|
||||
struct uiWindowsControl {
|
||||
uiControl c;
|
||||
uiControl *parent;
|
||||
// TODO this should be int on both os x and windows
|
||||
BOOL enabled;
|
||||
BOOL visible;
|
||||
void (*SyncEnableState)(uiWindowsControl *, int);
|
||||
void (*SetParentHWND)(uiWindowsControl *, HWND);
|
||||
void (*MinimumSize)(uiWindowsControl *, int *, int *);
|
||||
void (*MinimumSizeChanged)(uiWindowsControl *);
|
||||
void (*LayoutRect)(uiWindowsControl *c, RECT *r);
|
||||
void (*AssignControlIDZOrder)(uiWindowsControl *, LONG_PTR *, HWND *);
|
||||
void (*ChildVisibilityChanged)(uiWindowsControl *);
|
||||
};
|
||||
#define uiWindowsControl(this) ((uiWindowsControl *) (this))
|
||||
// TODO document
|
||||
_UI_EXTERN void uiWindowsControlSyncEnableState(uiWindowsControl *, int);
|
||||
_UI_EXTERN void uiWindowsControlSetParentHWND(uiWindowsControl *, HWND);
|
||||
_UI_EXTERN void uiWindowsControlMinimumSize(uiWindowsControl *, int *, int *);
|
||||
_UI_EXTERN void uiWindowsControlMinimumSizeChanged(uiWindowsControl *);
|
||||
_UI_EXTERN void uiWindowsControlLayoutRect(uiWindowsControl *, RECT *);
|
||||
_UI_EXTERN void uiWindowsControlAssignControlIDZOrder(uiWindowsControl *, LONG_PTR *, HWND *);
|
||||
_UI_EXTERN void uiWindowsControlChildVisibilityChanged(uiWindowsControl *);
|
||||
|
||||
// TODO document
|
||||
#define uiWindowsControlDefaultDestroy(type) \
|
||||
static void type ## Destroy(uiControl *c) \
|
||||
{ \
|
||||
uiWindowsEnsureDestroyWindow(type(c)->hwnd); \
|
||||
uiFreeControl(c); \
|
||||
}
|
||||
#define uiWindowsControlDefaultHandle(type) \
|
||||
static uintptr_t type ## Handle(uiControl *c) \
|
||||
{ \
|
||||
return (uintptr_t) (type(c)->hwnd); \
|
||||
}
|
||||
#define uiWindowsControlDefaultParent(type) \
|
||||
static uiControl *type ## Parent(uiControl *c) \
|
||||
{ \
|
||||
return uiWindowsControl(c)->parent; \
|
||||
}
|
||||
#define uiWindowsControlDefaultSetParent(type) \
|
||||
static void type ## SetParent(uiControl *c, uiControl *parent) \
|
||||
{ \
|
||||
uiControlVerifySetParent(c, parent); \
|
||||
uiWindowsControl(c)->parent = parent; \
|
||||
}
|
||||
#define uiWindowsControlDefaultToplevel(type) \
|
||||
static int type ## Toplevel(uiControl *c) \
|
||||
{ \
|
||||
return 0; \
|
||||
}
|
||||
#define uiWindowsControlDefaultVisible(type) \
|
||||
static int type ## Visible(uiControl *c) \
|
||||
{ \
|
||||
return uiWindowsControl(c)->visible; \
|
||||
}
|
||||
#define uiWindowsControlDefaultShow(type) \
|
||||
static void type ## Show(uiControl *c) \
|
||||
{ \
|
||||
uiWindowsControl(c)->visible = 1; \
|
||||
ShowWindow(type(c)->hwnd, SW_SHOW); \
|
||||
uiWindowsControlNotifyVisibilityChanged(uiWindowsControl(c)); \
|
||||
}
|
||||
#define uiWindowsControlDefaultHide(type) \
|
||||
static void type ## Hide(uiControl *c) \
|
||||
{ \
|
||||
uiWindowsControl(c)->visible = 0; \
|
||||
ShowWindow(type(c)->hwnd, SW_HIDE); \
|
||||
uiWindowsControlNotifyVisibilityChanged(uiWindowsControl(c)); \
|
||||
}
|
||||
#define uiWindowsControlDefaultEnabled(type) \
|
||||
static int type ## Enabled(uiControl *c) \
|
||||
{ \
|
||||
return uiWindowsControl(c)->enabled; \
|
||||
}
|
||||
#define uiWindowsControlDefaultEnable(type) \
|
||||
static void type ## Enable(uiControl *c) \
|
||||
{ \
|
||||
uiWindowsControl(c)->enabled = 1; \
|
||||
uiWindowsControlSyncEnableState(uiWindowsControl(c), uiControlEnabledToUser(c)); \
|
||||
}
|
||||
#define uiWindowsControlDefaultDisable(type) \
|
||||
static void type ## Disable(uiControl *c) \
|
||||
{ \
|
||||
uiWindowsControl(c)->enabled = 0; \
|
||||
uiWindowsControlSyncEnableState(uiWindowsControl(c), uiControlEnabledToUser(c)); \
|
||||
}
|
||||
#define uiWindowsControlDefaultSyncEnableState(type) \
|
||||
static void type ## SyncEnableState(uiWindowsControl *c, int enabled) \
|
||||
{ \
|
||||
if (uiWindowsShouldStopSyncEnableState(c, enabled)) \
|
||||
return; \
|
||||
EnableWindow(type(c)->hwnd, enabled); \
|
||||
}
|
||||
#define uiWindowsControlDefaultSetParentHWND(type) \
|
||||
static void type ## SetParentHWND(uiWindowsControl *c, HWND parent) \
|
||||
{ \
|
||||
uiWindowsEnsureSetParentHWND(type(c)->hwnd, parent); \
|
||||
}
|
||||
// note that there is no uiWindowsControlDefaultMinimumSize(); you MUST define this yourself!
|
||||
#define uiWindowsControlDefaultMinimumSizeChanged(type) \
|
||||
static void type ## MinimumSizeChanged(uiWindowsControl *c) \
|
||||
{ \
|
||||
if (uiWindowsControlTooSmall(c)) { \
|
||||
uiWindowsControlContinueMinimumSizeChanged(c); \
|
||||
return; \
|
||||
} \
|
||||
/* otherwise do nothing; we have no children */ \
|
||||
}
|
||||
#define uiWindowsControlDefaultLayoutRect(type) \
|
||||
static void type ## LayoutRect(uiWindowsControl *c, RECT *r) \
|
||||
{ \
|
||||
/* use the window rect as we include the non-client area in the sizes */ \
|
||||
uiWindowsEnsureGetWindowRect(type(c)->hwnd, r); \
|
||||
}
|
||||
#define uiWindowsControlDefaultAssignControlIDZOrder(type) \
|
||||
static void type ## AssignControlIDZOrder(uiWindowsControl *c, LONG_PTR *controlID, HWND *insertAfter) \
|
||||
{ \
|
||||
uiWindowsEnsureAssignControlIDZOrder(type(c)->hwnd, controlID, insertAfter); \
|
||||
}
|
||||
#define uiWindowsControlDefaultChildVisibilityChanged(type) \
|
||||
static void type ## ChildVisibilityChanged(uiWindowsControl *c) \
|
||||
{ \
|
||||
/* do nothing */ \
|
||||
}
|
||||
|
||||
#define uiWindowsControlAllDefaultsExceptDestroy(type) \
|
||||
uiWindowsControlDefaultHandle(type) \
|
||||
uiWindowsControlDefaultParent(type) \
|
||||
uiWindowsControlDefaultSetParent(type) \
|
||||
uiWindowsControlDefaultToplevel(type) \
|
||||
uiWindowsControlDefaultVisible(type) \
|
||||
uiWindowsControlDefaultShow(type) \
|
||||
uiWindowsControlDefaultHide(type) \
|
||||
uiWindowsControlDefaultEnabled(type) \
|
||||
uiWindowsControlDefaultEnable(type) \
|
||||
uiWindowsControlDefaultDisable(type) \
|
||||
uiWindowsControlDefaultSyncEnableState(type) \
|
||||
uiWindowsControlDefaultSetParentHWND(type) \
|
||||
uiWindowsControlDefaultMinimumSizeChanged(type) \
|
||||
uiWindowsControlDefaultLayoutRect(type) \
|
||||
uiWindowsControlDefaultAssignControlIDZOrder(type) \
|
||||
uiWindowsControlDefaultChildVisibilityChanged(type)
|
||||
|
||||
#define uiWindowsControlAllDefaults(type) \
|
||||
uiWindowsControlDefaultDestroy(type) \
|
||||
uiWindowsControlAllDefaultsExceptDestroy(type)
|
||||
|
||||
// TODO document
|
||||
#define uiWindowsNewControl(type, var) \
|
||||
var = type(uiWindowsAllocControl(sizeof (type), type ## Signature, #type)); \
|
||||
uiControl(var)->Destroy = type ## Destroy; \
|
||||
uiControl(var)->Handle = type ## Handle; \
|
||||
uiControl(var)->Parent = type ## Parent; \
|
||||
uiControl(var)->SetParent = type ## SetParent; \
|
||||
uiControl(var)->Toplevel = type ## Toplevel; \
|
||||
uiControl(var)->Visible = type ## Visible; \
|
||||
uiControl(var)->Show = type ## Show; \
|
||||
uiControl(var)->Hide = type ## Hide; \
|
||||
uiControl(var)->Enabled = type ## Enabled; \
|
||||
uiControl(var)->Enable = type ## Enable; \
|
||||
uiControl(var)->Disable = type ## Disable; \
|
||||
uiWindowsControl(var)->SyncEnableState = type ## SyncEnableState; \
|
||||
uiWindowsControl(var)->SetParentHWND = type ## SetParentHWND; \
|
||||
uiWindowsControl(var)->MinimumSize = type ## MinimumSize; \
|
||||
uiWindowsControl(var)->MinimumSizeChanged = type ## MinimumSizeChanged; \
|
||||
uiWindowsControl(var)->LayoutRect = type ## LayoutRect; \
|
||||
uiWindowsControl(var)->AssignControlIDZOrder = type ## AssignControlIDZOrder; \
|
||||
uiWindowsControl(var)->ChildVisibilityChanged = type ## ChildVisibilityChanged; \
|
||||
uiWindowsControl(var)->visible = 1; \
|
||||
uiWindowsControl(var)->enabled = 1;
|
||||
// TODO document
|
||||
_UI_EXTERN uiWindowsControl *uiWindowsAllocControl(size_t n, uint32_t typesig, const char *typenamestr);
|
||||
|
||||
// TODO document
|
||||
_UI_EXTERN HWND uiWindowsEnsureCreateControlHWND(DWORD dwExStyle, LPCWSTR lpClassName, LPCWSTR lpWindowName, DWORD dwStyle, HINSTANCE hInstance, LPVOID lpParam, BOOL useStandardControlFont);
|
||||
|
||||
// TODO document
|
||||
_UI_EXTERN void uiWindowsEnsureDestroyWindow(HWND hwnd);
|
||||
|
||||
// TODO document
|
||||
// TODO document that this should only be used in SetParentHWND() implementations
|
||||
_UI_EXTERN void uiWindowsEnsureSetParentHWND(HWND hwnd, HWND parent);
|
||||
|
||||
// TODO document
|
||||
_UI_EXTERN void uiWindowsEnsureAssignControlIDZOrder(HWND hwnd, LONG_PTR *controlID, HWND *insertAfter);
|
||||
|
||||
// TODO document
|
||||
_UI_EXTERN void uiWindowsEnsureGetClientRect(HWND hwnd, RECT *r);
|
||||
_UI_EXTERN void uiWindowsEnsureGetWindowRect(HWND hwnd, RECT *r);
|
||||
|
||||
// TODO document
|
||||
_UI_EXTERN char *uiWindowsWindowText(HWND hwnd);
|
||||
_UI_EXTERN void uiWindowsSetWindowText(HWND hwnd, const char *text);
|
||||
|
||||
// TODO document
|
||||
_UI_EXTERN int uiWindowsWindowTextWidth(HWND hwnd);
|
||||
|
||||
// TODO document
|
||||
// TODO point out this should only be used in a resize cycle
|
||||
_UI_EXTERN void uiWindowsEnsureMoveWindowDuringResize(HWND hwnd, int x, int y, int width, int height);
|
||||
|
||||
// TODO document
|
||||
_UI_EXTERN void uiWindowsRegisterWM_COMMANDHandler(HWND hwnd, BOOL (*handler)(uiControl *, HWND, WORD, LRESULT *), uiControl *c);
|
||||
_UI_EXTERN void uiWindowsUnregisterWM_COMMANDHandler(HWND hwnd);
|
||||
|
||||
// TODO document
|
||||
_UI_EXTERN void uiWindowsRegisterWM_NOTIFYHandler(HWND hwnd, BOOL (*handler)(uiControl *, HWND, NMHDR *, LRESULT *), uiControl *c);
|
||||
_UI_EXTERN void uiWindowsUnregisterWM_NOTIFYHandler(HWND hwnd);
|
||||
|
||||
// TODO document
|
||||
_UI_EXTERN void uiWindowsRegisterWM_HSCROLLHandler(HWND hwnd, BOOL (*handler)(uiControl *, HWND, WORD, LRESULT *), uiControl *c);
|
||||
_UI_EXTERN void uiWindowsUnregisterWM_HSCROLLHandler(HWND hwnd);
|
||||
|
||||
// TODO document
|
||||
_UI_EXTERN void uiWindowsRegisterReceiveWM_WININICHANGE(HWND hwnd);
|
||||
_UI_EXTERN void uiWindowsUnregisterReceiveWM_WININICHANGE(HWND hwnd);
|
||||
|
||||
// TODO document
|
||||
typedef struct uiWindowsSizing uiWindowsSizing;
|
||||
struct uiWindowsSizing {
|
||||
int BaseX;
|
||||
int BaseY;
|
||||
LONG InternalLeading;
|
||||
};
|
||||
_UI_EXTERN void uiWindowsGetSizing(HWND hwnd, uiWindowsSizing *sizing);
|
||||
_UI_EXTERN void uiWindowsSizingDlgUnitsToPixels(uiWindowsSizing *sizing, int *x, int *y);
|
||||
_UI_EXTERN void uiWindowsSizingStandardPadding(uiWindowsSizing *sizing, int *x, int *y);
|
||||
|
||||
// TODO document
|
||||
_UI_EXTERN HWND uiWindowsMakeContainer(uiWindowsControl *c, void (*onResize)(uiWindowsControl *));
|
||||
|
||||
// TODO document
|
||||
_UI_EXTERN BOOL uiWindowsControlTooSmall(uiWindowsControl *c);
|
||||
_UI_EXTERN void uiWindowsControlContinueMinimumSizeChanged(uiWindowsControl *c);
|
||||
|
||||
// TODO document
|
||||
_UI_EXTERN void uiWindowsControlAssignSoleControlIDZOrder(uiWindowsControl *);
|
||||
|
||||
// TODO document
|
||||
_UI_EXTERN BOOL uiWindowsShouldStopSyncEnableState(uiWindowsControl *c, int enabled);
|
||||
|
||||
// TODO document
|
||||
_UI_EXTERN void uiWindowsControlNotifyVisibilityChanged(uiWindowsControl *c);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
85
deps/libui/unix/CMakeLists.txt
vendored
85
deps/libui/unix/CMakeLists.txt
vendored
@ -1,85 +0,0 @@
|
||||
# 3 june 2016
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(GTK REQUIRED gtk+-3.0)
|
||||
|
||||
list(APPEND _LIBUI_SOURCES
|
||||
unix/alloc.c
|
||||
unix/area.c
|
||||
unix/box.c
|
||||
unix/button.c
|
||||
unix/cellrendererbutton.c
|
||||
unix/checkbox.c
|
||||
unix/child.c
|
||||
unix/colorbutton.c
|
||||
unix/combobox.c
|
||||
unix/control.c
|
||||
unix/datetimepicker.c
|
||||
unix/debug.c
|
||||
unix/draw.c
|
||||
unix/drawmatrix.c
|
||||
unix/drawpath.c
|
||||
unix/drawtext.c
|
||||
unix/editablecombo.c
|
||||
unix/entry.c
|
||||
unix/fontbutton.c
|
||||
unix/form.c
|
||||
unix/future.c
|
||||
unix/graphemes.c
|
||||
unix/grid.c
|
||||
unix/group.c
|
||||
unix/image.c
|
||||
unix/label.c
|
||||
unix/main.c
|
||||
unix/menu.c
|
||||
unix/multilineentry.c
|
||||
unix/progressbar.c
|
||||
unix/radiobuttons.c
|
||||
unix/separator.c
|
||||
unix/slider.c
|
||||
unix/spinbox.c
|
||||
unix/stddialogs.c
|
||||
unix/tab.c
|
||||
unix/text.c
|
||||
unix/util.c
|
||||
unix/window.c
|
||||
)
|
||||
set(_LIBUI_SOURCES ${_LIBUI_SOURCES} PARENT_SCOPE)
|
||||
|
||||
list(APPEND _LIBUI_INCLUDEDIRS
|
||||
unix
|
||||
)
|
||||
set(_LIBUI_INCLUDEDIRS _LIBUI_INCLUDEDIRS PARENT_SCOPE)
|
||||
|
||||
set(_LIBUINAME libui PARENT_SCOPE)
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
set(_LIBUINAME libui-temporary PARENT_SCOPE)
|
||||
endif()
|
||||
macro(_handle_static)
|
||||
set_target_properties(${_LIBUINAME} PROPERTIES
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
|
||||
set(_aname $<TARGET_FILE:${_LIBUINAME}>)
|
||||
set(_oname libui-combined.o)
|
||||
add_custom_command(
|
||||
OUTPUT ${_oname}
|
||||
COMMAND
|
||||
ld -r --whole-archive ${_aname} -o ${_oname}
|
||||
COMMAND
|
||||
objcopy --localize-hidden ${_oname}
|
||||
COMMENT "Removing hidden symbols")
|
||||
add_library(libui STATIC ${_oname})
|
||||
# otherwise cmake won't know which linker to use
|
||||
set_target_properties(libui PROPERTIES
|
||||
LINKER_LANGUAGE C)
|
||||
set(_aname)
|
||||
set(_oname)
|
||||
endmacro()
|
||||
|
||||
# TODO the other variables don't work?
|
||||
set(_LIBUI_CFLAGS
|
||||
${GTK_CFLAGS}
|
||||
PARENT_SCOPE)
|
||||
|
||||
set(_LIBUI_LIBS
|
||||
${GTK_LDFLAGS} m ${CMAKE_DL_LIBS}
|
||||
PARENT_SCOPE)
|
84
deps/libui/unix/alloc.c
vendored
84
deps/libui/unix/alloc.c
vendored
@ -1,84 +0,0 @@
|
||||
// 7 april 2015
|
||||
#include <string.h>
|
||||
#include "uipriv_unix.h"
|
||||
|
||||
static GPtrArray *allocations;
|
||||
|
||||
#define UINT8(p) ((uint8_t *) (p))
|
||||
#define PVOID(p) ((void *) (p))
|
||||
#define EXTRA (sizeof (size_t) + sizeof (const char **))
|
||||
#define DATA(p) PVOID(UINT8(p) + EXTRA)
|
||||
#define BASE(p) PVOID(UINT8(p) - EXTRA)
|
||||
#define SIZE(p) ((size_t *) (p))
|
||||
#define CCHAR(p) ((const char **) (p))
|
||||
#define TYPE(p) CCHAR(UINT8(p) + sizeof (size_t))
|
||||
|
||||
void initAlloc(void)
|
||||
{
|
||||
allocations = g_ptr_array_new();
|
||||
}
|
||||
|
||||
static void uninitComplain(gpointer ptr, gpointer data)
|
||||
{
|
||||
char **str = (char **) data;
|
||||
char *str2;
|
||||
|
||||
if (*str == NULL)
|
||||
*str = g_strdup_printf("");
|
||||
str2 = g_strdup_printf("%s%p %s\n", *str, ptr, *TYPE(ptr));
|
||||
g_free(*str);
|
||||
*str = str2;
|
||||
}
|
||||
|
||||
void uninitAlloc(void)
|
||||
{
|
||||
char *str = NULL;
|
||||
|
||||
if (allocations->len == 0) {
|
||||
g_ptr_array_free(allocations, TRUE);
|
||||
return;
|
||||
}
|
||||
g_ptr_array_foreach(allocations, uninitComplain, &str);
|
||||
userbug("Some data was leaked; either you left a uiControl lying around or there's a bug in libui itself. Leaked data:\n%s", str);
|
||||
g_free(str);
|
||||
}
|
||||
|
||||
void *uiAlloc(size_t size, const char *type)
|
||||
{
|
||||
void *out;
|
||||
|
||||
out = g_malloc0(EXTRA + size);
|
||||
*SIZE(out) = size;
|
||||
*TYPE(out) = type;
|
||||
g_ptr_array_add(allocations, out);
|
||||
return DATA(out);
|
||||
}
|
||||
|
||||
void *uiRealloc(void *p, size_t new, const char *type)
|
||||
{
|
||||
void *out;
|
||||
size_t *s;
|
||||
|
||||
if (p == NULL)
|
||||
return uiAlloc(new, type);
|
||||
p = BASE(p);
|
||||
out = g_realloc(p, EXTRA + new);
|
||||
s = SIZE(out);
|
||||
if (new <= *s)
|
||||
memset(((uint8_t *) DATA(out)) + *s, 0, new - *s);
|
||||
*s = new;
|
||||
if (g_ptr_array_remove(allocations, p) == FALSE)
|
||||
implbug("%p not found in allocations array in uiRealloc()", p);
|
||||
g_ptr_array_add(allocations, out);
|
||||
return DATA(out);
|
||||
}
|
||||
|
||||
void uiFree(void *p)
|
||||
{
|
||||
if (p == NULL)
|
||||
implbug("attempt to uiFree(NULL)");
|
||||
p = BASE(p);
|
||||
g_free(p);
|
||||
if (g_ptr_array_remove(allocations, p) == FALSE)
|
||||
implbug("%p not found in allocations array in uiFree()", p);
|
||||
}
|
633
deps/libui/unix/area.c
vendored
633
deps/libui/unix/area.c
vendored
@ -1,633 +0,0 @@
|
||||
// 4 september 2015
|
||||
#include "uipriv_unix.h"
|
||||
|
||||
// notes:
|
||||
// - G_DECLARE_DERIVABLE/FINAL_INTERFACE() requires glib 2.44 and that's starting with debian stretch (testing) (GTK+ 3.18) and ubuntu 15.04 (GTK+ 3.14) - debian jessie has 2.42 (GTK+ 3.14)
|
||||
#define areaWidgetType (areaWidget_get_type())
|
||||
#define areaWidget(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), areaWidgetType, areaWidget))
|
||||
#define isAreaWidget(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), areaWidgetType))
|
||||
#define areaWidgetClass(class) (G_TYPE_CHECK_CLASS_CAST((class), areaWidgetType, areaWidgetClass))
|
||||
#define isAreaWidgetClass(class) (G_TYPE_CHECK_CLASS_TYPE((class), areaWidget))
|
||||
#define getAreaWidgetClass(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), areaWidgetType, areaWidgetClass))
|
||||
|
||||
typedef struct areaWidget areaWidget;
|
||||
typedef struct areaWidgetClass areaWidgetClass;
|
||||
|
||||
struct areaWidget {
|
||||
GtkDrawingArea parent_instance;
|
||||
uiArea *a;
|
||||
// construct-only parameters aare not set until after the init() function has returned
|
||||
// we need this particular object available during init(), so put it here instead of in uiArea
|
||||
// keep a pointer in uiArea for convenience, though
|
||||
clickCounter cc;
|
||||
};
|
||||
|
||||
struct areaWidgetClass {
|
||||
GtkDrawingAreaClass parent_class;
|
||||
};
|
||||
|
||||
struct uiArea {
|
||||
uiUnixControl c;
|
||||
GtkWidget *widget; // either swidget or areaWidget depending on whether it is scrolling
|
||||
|
||||
GtkWidget *swidget;
|
||||
GtkContainer *scontainer;
|
||||
GtkScrolledWindow *sw;
|
||||
|
||||
GtkWidget *areaWidget;
|
||||
GtkDrawingArea *drawingArea;
|
||||
areaWidget *area;
|
||||
|
||||
uiAreaHandler *ah;
|
||||
|
||||
gboolean scrolling;
|
||||
int scrollWidth;
|
||||
int scrollHeight;
|
||||
|
||||
// note that this is a pointer; see above
|
||||
clickCounter *cc;
|
||||
|
||||
// for user window drags
|
||||
GdkEventButton *dragevent;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(areaWidget, areaWidget, GTK_TYPE_DRAWING_AREA)
|
||||
|
||||
static void areaWidget_init(areaWidget *aw)
|
||||
{
|
||||
// for events
|
||||
gtk_widget_add_events(GTK_WIDGET(aw),
|
||||
GDK_POINTER_MOTION_MASK |
|
||||
GDK_BUTTON_MOTION_MASK |
|
||||
GDK_BUTTON_PRESS_MASK |
|
||||
GDK_BUTTON_RELEASE_MASK |
|
||||
GDK_KEY_PRESS_MASK |
|
||||
GDK_KEY_RELEASE_MASK |
|
||||
GDK_ENTER_NOTIFY_MASK |
|
||||
GDK_LEAVE_NOTIFY_MASK);
|
||||
|
||||
gtk_widget_set_can_focus(GTK_WIDGET(aw), TRUE);
|
||||
|
||||
clickCounterReset(&(aw->cc));
|
||||
}
|
||||
|
||||
static void areaWidget_dispose(GObject *obj)
|
||||
{
|
||||
G_OBJECT_CLASS(areaWidget_parent_class)->dispose(obj);
|
||||
}
|
||||
|
||||
static void areaWidget_finalize(GObject *obj)
|
||||
{
|
||||
G_OBJECT_CLASS(areaWidget_parent_class)->finalize(obj);
|
||||
}
|
||||
|
||||
static void areaWidget_size_allocate(GtkWidget *w, GtkAllocation *allocation)
|
||||
{
|
||||
areaWidget *aw = areaWidget(w);
|
||||
uiArea *a = aw->a;
|
||||
|
||||
// GtkDrawingArea has a size_allocate() implementation; we need to call it
|
||||
// this will call gtk_widget_set_allocation() for us
|
||||
GTK_WIDGET_CLASS(areaWidget_parent_class)->size_allocate(w, allocation);
|
||||
|
||||
if (!a->scrolling)
|
||||
// we must redraw everything on resize because Windows requires it
|
||||
gtk_widget_queue_resize(w);
|
||||
}
|
||||
|
||||
static void loadAreaSize(uiArea *a, double *width, double *height)
|
||||
{
|
||||
GtkAllocation allocation;
|
||||
|
||||
*width = 0;
|
||||
*height = 0;
|
||||
// don't provide size information for scrolling areas
|
||||
if (!a->scrolling) {
|
||||
gtk_widget_get_allocation(a->areaWidget, &allocation);
|
||||
// these are already in drawing space coordinates
|
||||
// for drawing, the size of drawing space has the same value as the widget allocation
|
||||
// thanks to tristan in irc.gimp.net/#gtk+
|
||||
*width = allocation.width;
|
||||
*height = allocation.height;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean areaWidget_draw(GtkWidget *w, cairo_t *cr)
|
||||
{
|
||||
areaWidget *aw = areaWidget(w);
|
||||
uiArea *a = aw->a;
|
||||
uiAreaDrawParams dp;
|
||||
double clipX0, clipY0, clipX1, clipY1;
|
||||
|
||||
dp.Context = newContext(cr);
|
||||
|
||||
loadAreaSize(a, &(dp.AreaWidth), &(dp.AreaHeight));
|
||||
|
||||
cairo_clip_extents(cr, &clipX0, &clipY0, &clipX1, &clipY1);
|
||||
dp.ClipX = clipX0;
|
||||
dp.ClipY = clipY0;
|
||||
dp.ClipWidth = clipX1 - clipX0;
|
||||
dp.ClipHeight = clipY1 - clipY0;
|
||||
|
||||
// no need to save or restore the graphics state to reset transformations; GTK+ does that for us
|
||||
(*(a->ah->Draw))(a->ah, a, &dp);
|
||||
|
||||
freeContext(dp.Context);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// to do this properly for scrolling areas, we need to
|
||||
// - return the same value for min and nat
|
||||
// - call gtk_widget_queue_resize() when the size changes
|
||||
// thanks to Company in irc.gimp.net/#gtk+
|
||||
static void areaWidget_get_preferred_height(GtkWidget *w, gint *min, gint *nat)
|
||||
{
|
||||
areaWidget *aw = areaWidget(w);
|
||||
uiArea *a = aw->a;
|
||||
|
||||
// always chain up just in case
|
||||
GTK_WIDGET_CLASS(areaWidget_parent_class)->get_preferred_height(w, min, nat);
|
||||
if (a->scrolling) {
|
||||
*min = a->scrollHeight;
|
||||
*nat = a->scrollHeight;
|
||||
}
|
||||
}
|
||||
|
||||
static void areaWidget_get_preferred_width(GtkWidget *w, gint *min, gint *nat)
|
||||
{
|
||||
areaWidget *aw = areaWidget(w);
|
||||
uiArea *a = aw->a;
|
||||
|
||||
// always chain up just in case
|
||||
GTK_WIDGET_CLASS(areaWidget_parent_class)->get_preferred_width(w, min, nat);
|
||||
if (a->scrolling) {
|
||||
*min = a->scrollWidth;
|
||||
*nat = a->scrollWidth;
|
||||
}
|
||||
}
|
||||
|
||||
static guint translateModifiers(guint state, GdkWindow *window)
|
||||
{
|
||||
GdkModifierType statetype;
|
||||
|
||||
// GDK doesn't initialize the modifier flags fully; we have to explicitly tell it to (thanks to Daniel_S and daniels (two different people) in irc.gimp.net/#gtk+)
|
||||
statetype = state;
|
||||
gdk_keymap_add_virtual_modifiers(
|
||||
gdk_keymap_get_for_display(gdk_window_get_display(window)),
|
||||
&statetype);
|
||||
return statetype;
|
||||
}
|
||||
|
||||
static uiModifiers toModifiers(guint state)
|
||||
{
|
||||
uiModifiers m;
|
||||
|
||||
m = 0;
|
||||
if ((state & GDK_CONTROL_MASK) != 0)
|
||||
m |= uiModifierCtrl;
|
||||
if ((state & GDK_META_MASK) != 0)
|
||||
m |= uiModifierAlt;
|
||||
if ((state & GDK_MOD1_MASK) != 0) // GTK+ itself requires this to be Alt (just read through gtkaccelgroup.c)
|
||||
m |= uiModifierAlt;
|
||||
if ((state & GDK_SHIFT_MASK) != 0)
|
||||
m |= uiModifierShift;
|
||||
if ((state & GDK_SUPER_MASK) != 0)
|
||||
m |= uiModifierSuper;
|
||||
return m;
|
||||
}
|
||||
|
||||
// capture on drag is done automatically on GTK+
|
||||
static void finishMouseEvent(uiArea *a, uiAreaMouseEvent *me, guint mb, gdouble x, gdouble y, guint state, GdkWindow *window)
|
||||
{
|
||||
// on GTK+, mouse buttons 4-7 are for scrolling; if we got here, that's a mistake
|
||||
if (mb >= 4 && mb <= 7)
|
||||
return;
|
||||
// if the button ID >= 8, continue counting from 4, as in the MouseEvent spec
|
||||
if (me->Down >= 8)
|
||||
me->Down -= 4;
|
||||
if (me->Up >= 8)
|
||||
me->Up -= 4;
|
||||
|
||||
state = translateModifiers(state, window);
|
||||
me->Modifiers = toModifiers(state);
|
||||
|
||||
// the mb != # checks exclude the Up/Down button from Held
|
||||
me->Held1To64 = 0;
|
||||
if (mb != 1 && (state & GDK_BUTTON1_MASK) != 0)
|
||||
me->Held1To64 |= 1 << 0;
|
||||
if (mb != 2 && (state & GDK_BUTTON2_MASK) != 0)
|
||||
me->Held1To64 |= 1 << 1;
|
||||
if (mb != 3 && (state & GDK_BUTTON3_MASK) != 0)
|
||||
me->Held1To64 |= 1 << 2;
|
||||
// don't check GDK_BUTTON4_MASK or GDK_BUTTON5_MASK because those are for the scrolling buttons mentioned above
|
||||
// GDK expressly does not support any more buttons in the GdkModifierType; see https://git.gnome.org/browse/gtk+/tree/gdk/x11/gdkdevice-xi2.c#n763 (thanks mclasen in irc.gimp.net/#gtk+)
|
||||
|
||||
// these are already in drawing space coordinates
|
||||
// the size of drawing space has the same value as the widget allocation
|
||||
// thanks to tristan in irc.gimp.net/#gtk+
|
||||
me->X = x;
|
||||
me->Y = y;
|
||||
|
||||
loadAreaSize(a, &(me->AreaWidth), &(me->AreaHeight));
|
||||
|
||||
(*(a->ah->MouseEvent))(a->ah, a, me);
|
||||
}
|
||||
|
||||
static gboolean areaWidget_button_press_event(GtkWidget *w, GdkEventButton *e)
|
||||
{
|
||||
areaWidget *aw = areaWidget(w);
|
||||
uiArea *a = aw->a;
|
||||
gint maxTime, maxDistance;
|
||||
GtkSettings *settings;
|
||||
uiAreaMouseEvent me;
|
||||
|
||||
// clicking doesn't automatically transfer keyboard focus; we must do so manually (thanks tristan in irc.gimp.net/#gtk+)
|
||||
gtk_widget_grab_focus(w);
|
||||
|
||||
// we handle multiple clicks ourselves here, in the same way as we do on Windows
|
||||
if (e->type != GDK_BUTTON_PRESS)
|
||||
// ignore GDK's generated double-clicks and beyond
|
||||
return GDK_EVENT_PROPAGATE;
|
||||
settings = gtk_widget_get_settings(w);
|
||||
g_object_get(settings,
|
||||
"gtk-double-click-time", &maxTime,
|
||||
"gtk-double-click-distance", &maxDistance,
|
||||
NULL);
|
||||
// don't unref settings; it's transfer-none (thanks gregier in irc.gimp.net/#gtk+)
|
||||
// e->time is guint32
|
||||
// e->x and e->y are floating-point; just make them 32-bit integers
|
||||
// maxTime and maxDistance... are gint, which *should* fit, hopefully...
|
||||
me.Count = clickCounterClick(a->cc, me.Down,
|
||||
e->x, e->y,
|
||||
e->time, maxTime,
|
||||
maxDistance, maxDistance);
|
||||
|
||||
me.Down = e->button;
|
||||
me.Up = 0;
|
||||
|
||||
// and set things up for window drags
|
||||
a->dragevent = e;
|
||||
finishMouseEvent(a, &me, e->button, e->x, e->y, e->state, e->window);
|
||||
a->dragevent = NULL;
|
||||
return GDK_EVENT_PROPAGATE;
|
||||
}
|
||||
|
||||
static gboolean areaWidget_button_release_event(GtkWidget *w, GdkEventButton *e)
|
||||
{
|
||||
areaWidget *aw = areaWidget(w);
|
||||
uiArea *a = aw->a;
|
||||
uiAreaMouseEvent me;
|
||||
|
||||
me.Down = 0;
|
||||
me.Up = e->button;
|
||||
me.Count = 0;
|
||||
finishMouseEvent(a, &me, e->button, e->x, e->y, e->state, e->window);
|
||||
return GDK_EVENT_PROPAGATE;
|
||||
}
|
||||
|
||||
static gboolean areaWidget_motion_notify_event(GtkWidget *w, GdkEventMotion *e)
|
||||
{
|
||||
areaWidget *aw = areaWidget(w);
|
||||
uiArea *a = aw->a;
|
||||
uiAreaMouseEvent me;
|
||||
|
||||
me.Down = 0;
|
||||
me.Up = 0;
|
||||
me.Count = 0;
|
||||
finishMouseEvent(a, &me, 0, e->x, e->y, e->state, e->window);
|
||||
return GDK_EVENT_PROPAGATE;
|
||||
}
|
||||
|
||||
// we want switching away from the control to reset the double-click counter, like with WM_ACTIVATE on Windows
|
||||
// according to tristan in irc.gimp.net/#gtk+, doing this on both enter-notify-event and leave-notify-event is correct (and it seems to be true in my own tests; plus the events DO get sent when switching programs with the keyboard (just pointing that out))
|
||||
static gboolean onCrossing(areaWidget *aw, int left)
|
||||
{
|
||||
uiArea *a = aw->a;
|
||||
|
||||
(*(a->ah->MouseCrossed))(a->ah, a, left);
|
||||
clickCounterReset(a->cc);
|
||||
return GDK_EVENT_PROPAGATE;
|
||||
}
|
||||
|
||||
static gboolean areaWidget_enter_notify_event(GtkWidget *w, GdkEventCrossing *e)
|
||||
{
|
||||
return onCrossing(areaWidget(w), 0);
|
||||
}
|
||||
|
||||
static gboolean areaWidget_leave_notify_event(GtkWidget *w, GdkEventCrossing *e)
|
||||
{
|
||||
return onCrossing(areaWidget(w), 1);
|
||||
}
|
||||
|
||||
// note: there is no equivalent to WM_CAPTURECHANGED on GTK+; there literally is no way to break a grab like that (at least not on X11 and Wayland)
|
||||
// even if I invoke the task switcher and switch processes, the mouse grab will still be held until I let go of all buttons
|
||||
// therefore, no DragBroken()
|
||||
|
||||
// we use GDK_KEY_Print as a sentinel because libui will never support the print screen key; that key belongs to the user
|
||||
|
||||
static const struct {
|
||||
guint keyval;
|
||||
uiExtKey extkey;
|
||||
} extKeys[] = {
|
||||
{ GDK_KEY_Escape, uiExtKeyEscape },
|
||||
{ GDK_KEY_Insert, uiExtKeyInsert },
|
||||
{ GDK_KEY_Delete, uiExtKeyDelete },
|
||||
{ GDK_KEY_Home, uiExtKeyHome },
|
||||
{ GDK_KEY_End, uiExtKeyEnd },
|
||||
{ GDK_KEY_Page_Up, uiExtKeyPageUp },
|
||||
{ GDK_KEY_Page_Down, uiExtKeyPageDown },
|
||||
{ GDK_KEY_Up, uiExtKeyUp },
|
||||
{ GDK_KEY_Down, uiExtKeyDown },
|
||||
{ GDK_KEY_Left, uiExtKeyLeft },
|
||||
{ GDK_KEY_Right, uiExtKeyRight },
|
||||
{ GDK_KEY_F1, uiExtKeyF1 },
|
||||
{ GDK_KEY_F2, uiExtKeyF2 },
|
||||
{ GDK_KEY_F3, uiExtKeyF3 },
|
||||
{ GDK_KEY_F4, uiExtKeyF4 },
|
||||
{ GDK_KEY_F5, uiExtKeyF5 },
|
||||
{ GDK_KEY_F6, uiExtKeyF6 },
|
||||
{ GDK_KEY_F7, uiExtKeyF7 },
|
||||
{ GDK_KEY_F8, uiExtKeyF8 },
|
||||
{ GDK_KEY_F9, uiExtKeyF9 },
|
||||
{ GDK_KEY_F10, uiExtKeyF10 },
|
||||
{ GDK_KEY_F11, uiExtKeyF11 },
|
||||
{ GDK_KEY_F12, uiExtKeyF12 },
|
||||
// numpad numeric keys and . are handled in events.c
|
||||
{ GDK_KEY_KP_Enter, uiExtKeyNEnter },
|
||||
{ GDK_KEY_KP_Add, uiExtKeyNAdd },
|
||||
{ GDK_KEY_KP_Subtract, uiExtKeyNSubtract },
|
||||
{ GDK_KEY_KP_Multiply, uiExtKeyNMultiply },
|
||||
{ GDK_KEY_KP_Divide, uiExtKeyNDivide },
|
||||
{ GDK_KEY_Print, 0 },
|
||||
};
|
||||
|
||||
static const struct {
|
||||
guint keyval;
|
||||
uiModifiers mod;
|
||||
} modKeys[] = {
|
||||
{ GDK_KEY_Control_L, uiModifierCtrl },
|
||||
{ GDK_KEY_Control_R, uiModifierCtrl },
|
||||
{ GDK_KEY_Alt_L, uiModifierAlt },
|
||||
{ GDK_KEY_Alt_R, uiModifierAlt },
|
||||
{ GDK_KEY_Meta_L, uiModifierAlt },
|
||||
{ GDK_KEY_Meta_R, uiModifierAlt },
|
||||
{ GDK_KEY_Shift_L, uiModifierShift },
|
||||
{ GDK_KEY_Shift_R, uiModifierShift },
|
||||
{ GDK_KEY_Super_L, uiModifierSuper },
|
||||
{ GDK_KEY_Super_R, uiModifierSuper },
|
||||
{ GDK_KEY_Print, 0 },
|
||||
};
|
||||
|
||||
static int areaKeyEvent(uiArea *a, int up, GdkEventKey *e)
|
||||
{
|
||||
uiAreaKeyEvent ke;
|
||||
guint state;
|
||||
int i;
|
||||
|
||||
ke.Key = 0;
|
||||
ke.ExtKey = 0;
|
||||
ke.Modifier = 0;
|
||||
|
||||
state = translateModifiers(e->state, e->window);
|
||||
ke.Modifiers = toModifiers(state);
|
||||
|
||||
ke.Up = up;
|
||||
|
||||
for (i = 0; extKeys[i].keyval != GDK_KEY_Print; i++)
|
||||
if (extKeys[i].keyval == e->keyval) {
|
||||
ke.ExtKey = extKeys[i].extkey;
|
||||
goto keyFound;
|
||||
}
|
||||
|
||||
for (i = 0; modKeys[i].keyval != GDK_KEY_Print; i++)
|
||||
if (modKeys[i].keyval == e->keyval) {
|
||||
ke.Modifier = modKeys[i].mod;
|
||||
// don't include the modifier in ke.Modifiers
|
||||
ke.Modifiers &= ~ke.Modifier;
|
||||
goto keyFound;
|
||||
}
|
||||
|
||||
if (fromScancode(e->hardware_keycode - 8, &ke))
|
||||
goto keyFound;
|
||||
|
||||
// no supported key found; treat as unhandled
|
||||
return 0;
|
||||
|
||||
keyFound:
|
||||
return (*(a->ah->KeyEvent))(a->ah, a, &ke);
|
||||
}
|
||||
|
||||
static gboolean areaWidget_key_press_event(GtkWidget *w, GdkEventKey *e)
|
||||
{
|
||||
areaWidget *aw = areaWidget(w);
|
||||
uiArea *a = aw->a;
|
||||
|
||||
if (areaKeyEvent(a, 0, e))
|
||||
return GDK_EVENT_STOP;
|
||||
return GDK_EVENT_PROPAGATE;
|
||||
}
|
||||
|
||||
static gboolean areaWidget_key_release_event(GtkWidget *w, GdkEventKey *e)
|
||||
{
|
||||
areaWidget *aw = areaWidget(w);
|
||||
uiArea *a = aw->a;
|
||||
|
||||
if (areaKeyEvent(a, 1, e))
|
||||
return GDK_EVENT_STOP;
|
||||
return GDK_EVENT_PROPAGATE;
|
||||
}
|
||||
|
||||
enum {
|
||||
pArea = 1,
|
||||
nProps,
|
||||
};
|
||||
|
||||
static GParamSpec *pspecArea;
|
||||
|
||||
static void areaWidget_set_property(GObject *obj, guint prop, const GValue *value, GParamSpec *pspec)
|
||||
{
|
||||
areaWidget *aw = areaWidget(obj);
|
||||
|
||||
switch (prop) {
|
||||
case pArea:
|
||||
aw->a = (uiArea *) g_value_get_pointer(value);
|
||||
aw->a->cc = &(aw->cc);
|
||||
return;
|
||||
}
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop, pspec);
|
||||
}
|
||||
|
||||
static void areaWidget_get_property(GObject *obj, guint prop, GValue *value, GParamSpec *pspec)
|
||||
{
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop, pspec);
|
||||
}
|
||||
|
||||
static void areaWidget_class_init(areaWidgetClass *class)
|
||||
{
|
||||
G_OBJECT_CLASS(class)->dispose = areaWidget_dispose;
|
||||
G_OBJECT_CLASS(class)->finalize = areaWidget_finalize;
|
||||
G_OBJECT_CLASS(class)->set_property = areaWidget_set_property;
|
||||
G_OBJECT_CLASS(class)->get_property = areaWidget_get_property;
|
||||
|
||||
GTK_WIDGET_CLASS(class)->size_allocate = areaWidget_size_allocate;
|
||||
GTK_WIDGET_CLASS(class)->draw = areaWidget_draw;
|
||||
GTK_WIDGET_CLASS(class)->get_preferred_height = areaWidget_get_preferred_height;
|
||||
GTK_WIDGET_CLASS(class)->get_preferred_width = areaWidget_get_preferred_width;
|
||||
GTK_WIDGET_CLASS(class)->button_press_event = areaWidget_button_press_event;
|
||||
GTK_WIDGET_CLASS(class)->button_release_event = areaWidget_button_release_event;
|
||||
GTK_WIDGET_CLASS(class)->motion_notify_event = areaWidget_motion_notify_event;
|
||||
GTK_WIDGET_CLASS(class)->enter_notify_event = areaWidget_enter_notify_event;
|
||||
GTK_WIDGET_CLASS(class)->leave_notify_event = areaWidget_leave_notify_event;
|
||||
GTK_WIDGET_CLASS(class)->key_press_event = areaWidget_key_press_event;
|
||||
GTK_WIDGET_CLASS(class)->key_release_event = areaWidget_key_release_event;
|
||||
|
||||
pspecArea = g_param_spec_pointer("libui-area",
|
||||
"libui-area",
|
||||
"uiArea.",
|
||||
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
|
||||
g_object_class_install_property(G_OBJECT_CLASS(class), pArea, pspecArea);
|
||||
}
|
||||
|
||||
// control implementation
|
||||
|
||||
uiUnixControlAllDefaults(uiArea)
|
||||
|
||||
void uiAreaSetSize(uiArea *a, int width, int height)
|
||||
{
|
||||
if (!a->scrolling)
|
||||
userbug("You cannot call uiAreaSetSize() on a non-scrolling uiArea. (area: %p)", a);
|
||||
a->scrollWidth = width;
|
||||
a->scrollHeight = height;
|
||||
gtk_widget_queue_resize(a->areaWidget);
|
||||
}
|
||||
|
||||
void uiAreaQueueRedrawAll(uiArea *a)
|
||||
{
|
||||
gtk_widget_queue_draw(a->areaWidget);
|
||||
}
|
||||
|
||||
void uiAreaScrollTo(uiArea *a, double x, double y, double width, double height)
|
||||
{
|
||||
// TODO
|
||||
// TODO adjust adjustments and find source for that
|
||||
}
|
||||
|
||||
void uiAreaBeginUserWindowMove(uiArea *a)
|
||||
{
|
||||
GtkWidget *toplevel;
|
||||
|
||||
if (a->dragevent == NULL)
|
||||
userbug("cannot call uiAreaBeginUserWindowMove() outside of a Mouse() with Down != 0");
|
||||
// TODO don't we have a libui function for this? did I scrap it?
|
||||
// TODO widget or areaWidget?
|
||||
toplevel = gtk_widget_get_toplevel(a->widget);
|
||||
if (toplevel == NULL) {
|
||||
// TODO
|
||||
return;
|
||||
}
|
||||
// the docs say to do this
|
||||
if (!gtk_widget_is_toplevel(toplevel)) {
|
||||
// TODO
|
||||
return;
|
||||
}
|
||||
if (!GTK_IS_WINDOW(toplevel)) {
|
||||
// TODO
|
||||
return;
|
||||
}
|
||||
gtk_window_begin_move_drag(GTK_WINDOW(toplevel),
|
||||
a->dragevent->button,
|
||||
a->dragevent->x_root, // TODO are these correct?
|
||||
a->dragevent->y_root,
|
||||
a->dragevent->time);
|
||||
}
|
||||
|
||||
static const GdkWindowEdge edges[] = {
|
||||
[uiWindowResizeEdgeLeft] = GDK_WINDOW_EDGE_WEST,
|
||||
[uiWindowResizeEdgeTop] = GDK_WINDOW_EDGE_NORTH,
|
||||
[uiWindowResizeEdgeRight] = GDK_WINDOW_EDGE_EAST,
|
||||
[uiWindowResizeEdgeBottom] = GDK_WINDOW_EDGE_SOUTH,
|
||||
[uiWindowResizeEdgeTopLeft] = GDK_WINDOW_EDGE_NORTH_WEST,
|
||||
[uiWindowResizeEdgeTopRight] = GDK_WINDOW_EDGE_NORTH_EAST,
|
||||
[uiWindowResizeEdgeBottomLeft] = GDK_WINDOW_EDGE_SOUTH_WEST,
|
||||
[uiWindowResizeEdgeBottomRight] = GDK_WINDOW_EDGE_SOUTH_EAST,
|
||||
};
|
||||
|
||||
void uiAreaBeginUserWindowResize(uiArea *a, uiWindowResizeEdge edge)
|
||||
{
|
||||
GtkWidget *toplevel;
|
||||
|
||||
if (a->dragevent == NULL)
|
||||
userbug("cannot call uiAreaBeginUserWindowResize() outside of a Mouse() with Down != 0");
|
||||
// TODO don't we have a libui function for this? did I scrap it?
|
||||
// TODO widget or areaWidget?
|
||||
toplevel = gtk_widget_get_toplevel(a->widget);
|
||||
if (toplevel == NULL) {
|
||||
// TODO
|
||||
return;
|
||||
}
|
||||
// the docs say to do this
|
||||
if (!gtk_widget_is_toplevel(toplevel)) {
|
||||
// TODO
|
||||
return;
|
||||
}
|
||||
if (!GTK_IS_WINDOW(toplevel)) {
|
||||
// TODO
|
||||
return;
|
||||
}
|
||||
gtk_window_begin_resize_drag(GTK_WINDOW(toplevel),
|
||||
edges[edge],
|
||||
a->dragevent->button,
|
||||
a->dragevent->x_root, // TODO are these correct?
|
||||
a->dragevent->y_root,
|
||||
a->dragevent->time);
|
||||
}
|
||||
|
||||
uiArea *uiNewArea(uiAreaHandler *ah)
|
||||
{
|
||||
uiArea *a;
|
||||
|
||||
uiUnixNewControl(uiArea, a);
|
||||
|
||||
a->ah = ah;
|
||||
a->scrolling = FALSE;
|
||||
|
||||
a->areaWidget = GTK_WIDGET(g_object_new(areaWidgetType,
|
||||
"libui-area", a,
|
||||
NULL));
|
||||
a->drawingArea = GTK_DRAWING_AREA(a->areaWidget);
|
||||
a->area = areaWidget(a->areaWidget);
|
||||
|
||||
a->widget = a->areaWidget;
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
uiArea *uiNewScrollingArea(uiAreaHandler *ah, int width, int height)
|
||||
{
|
||||
uiArea *a;
|
||||
|
||||
uiUnixNewControl(uiArea, a);
|
||||
|
||||
a->ah = ah;
|
||||
a->scrolling = TRUE;
|
||||
a->scrollWidth = width;
|
||||
a->scrollHeight = height;
|
||||
|
||||
a->swidget = gtk_scrolled_window_new(NULL, NULL);
|
||||
a->scontainer = GTK_CONTAINER(a->swidget);
|
||||
a->sw = GTK_SCROLLED_WINDOW(a->swidget);
|
||||
|
||||
a->areaWidget = GTK_WIDGET(g_object_new(areaWidgetType,
|
||||
"libui-area", a,
|
||||
NULL));
|
||||
a->drawingArea = GTK_DRAWING_AREA(a->areaWidget);
|
||||
a->area = areaWidget(a->areaWidget);
|
||||
|
||||
a->widget = a->swidget;
|
||||
|
||||
gtk_container_add(a->scontainer, a->areaWidget);
|
||||
// and make the area visible; only the scrolled window's visibility is controlled by libui
|
||||
gtk_widget_show(a->areaWidget);
|
||||
|
||||
return a;
|
||||
}
|
159
deps/libui/unix/box.c
vendored
159
deps/libui/unix/box.c
vendored
@ -1,159 +0,0 @@
|
||||
// 7 april 2015
|
||||
#include "uipriv_unix.h"
|
||||
|
||||
struct boxChild {
|
||||
uiControl *c;
|
||||
int stretchy;
|
||||
gboolean oldhexpand;
|
||||
GtkAlign oldhalign;
|
||||
gboolean oldvexpand;
|
||||
GtkAlign oldvalign;
|
||||
};
|
||||
|
||||
struct uiBox {
|
||||
uiUnixControl c;
|
||||
GtkWidget *widget;
|
||||
GtkContainer *container;
|
||||
GtkBox *box;
|
||||
GArray *controls;
|
||||
int vertical;
|
||||
int padded;
|
||||
GtkSizeGroup *stretchygroup; // ensures all stretchy controls have the same size
|
||||
};
|
||||
|
||||
uiUnixControlAllDefaultsExceptDestroy(uiBox)
|
||||
|
||||
#define ctrl(b, i) &g_array_index(b->controls, struct boxChild, i)
|
||||
|
||||
static void uiBoxDestroy(uiControl *c)
|
||||
{
|
||||
uiBox *b = uiBox(c);
|
||||
struct boxChild *bc;
|
||||
guint i;
|
||||
|
||||
// kill the size group
|
||||
g_object_unref(b->stretchygroup);
|
||||
// free all controls
|
||||
for (i = 0; i < b->controls->len; i++) {
|
||||
bc = ctrl(b, i);
|
||||
uiControlSetParent(bc->c, NULL);
|
||||
// and make sure the widget itself stays alive
|
||||
uiUnixControlSetContainer(uiUnixControl(bc->c), b->container, TRUE);
|
||||
uiControlDestroy(bc->c);
|
||||
}
|
||||
g_array_free(b->controls, TRUE);
|
||||
// and then ourselves
|
||||
g_object_unref(b->widget);
|
||||
uiFreeControl(uiControl(b));
|
||||
}
|
||||
|
||||
void uiBoxAppend(uiBox *b, uiControl *c, int stretchy)
|
||||
{
|
||||
struct boxChild bc;
|
||||
GtkWidget *widget;
|
||||
|
||||
bc.c = c;
|
||||
bc.stretchy = stretchy;
|
||||
widget = GTK_WIDGET(uiControlHandle(bc.c));
|
||||
bc.oldhexpand = gtk_widget_get_hexpand(widget);
|
||||
bc.oldhalign = gtk_widget_get_halign(widget);
|
||||
bc.oldvexpand = gtk_widget_get_vexpand(widget);
|
||||
bc.oldvalign = gtk_widget_get_valign(widget);
|
||||
|
||||
if (bc.stretchy) {
|
||||
if (b->vertical) {
|
||||
gtk_widget_set_vexpand(widget, TRUE);
|
||||
gtk_widget_set_valign(widget, GTK_ALIGN_FILL);
|
||||
} else {
|
||||
gtk_widget_set_hexpand(widget, TRUE);
|
||||
gtk_widget_set_halign(widget, GTK_ALIGN_FILL);
|
||||
}
|
||||
gtk_size_group_add_widget(b->stretchygroup, widget);
|
||||
} else
|
||||
if (b->vertical)
|
||||
gtk_widget_set_vexpand(widget, FALSE);
|
||||
else
|
||||
gtk_widget_set_hexpand(widget, FALSE);
|
||||
// and make them fill the opposite direction
|
||||
if (b->vertical) {
|
||||
gtk_widget_set_hexpand(widget, TRUE);
|
||||
gtk_widget_set_halign(widget, GTK_ALIGN_FILL);
|
||||
} else {
|
||||
gtk_widget_set_vexpand(widget, TRUE);
|
||||
gtk_widget_set_valign(widget, GTK_ALIGN_FILL);
|
||||
}
|
||||
|
||||
uiControlSetParent(bc.c, uiControl(b));
|
||||
uiUnixControlSetContainer(uiUnixControl(bc.c), b->container, FALSE);
|
||||
g_array_append_val(b->controls, bc);
|
||||
}
|
||||
|
||||
void uiBoxDelete(uiBox *b, int index)
|
||||
{
|
||||
struct boxChild *bc;
|
||||
GtkWidget *widget;
|
||||
|
||||
bc = ctrl(b, index);
|
||||
widget = GTK_WIDGET(uiControlHandle(bc->c));
|
||||
|
||||
uiControlSetParent(bc->c, NULL);
|
||||
uiUnixControlSetContainer(uiUnixControl(bc->c), b->container, TRUE);
|
||||
|
||||
if (bc->stretchy)
|
||||
gtk_size_group_remove_widget(b->stretchygroup, widget);
|
||||
gtk_widget_set_hexpand(widget, bc->oldhexpand);
|
||||
gtk_widget_set_halign(widget, bc->oldhalign);
|
||||
gtk_widget_set_vexpand(widget, bc->oldvexpand);
|
||||
gtk_widget_set_valign(widget, bc->oldvalign);
|
||||
|
||||
g_array_remove_index(b->controls, index);
|
||||
}
|
||||
|
||||
int uiBoxPadded(uiBox *b)
|
||||
{
|
||||
return b->padded;
|
||||
}
|
||||
|
||||
void uiBoxSetPadded(uiBox *b, int padded)
|
||||
{
|
||||
b->padded = padded;
|
||||
if (b->padded)
|
||||
if (b->vertical)
|
||||
gtk_box_set_spacing(b->box, gtkYPadding);
|
||||
else
|
||||
gtk_box_set_spacing(b->box, gtkXPadding);
|
||||
else
|
||||
gtk_box_set_spacing(b->box, 0);
|
||||
}
|
||||
|
||||
static uiBox *finishNewBox(GtkOrientation orientation)
|
||||
{
|
||||
uiBox *b;
|
||||
|
||||
uiUnixNewControl(uiBox, b);
|
||||
|
||||
b->widget = gtk_box_new(orientation, 0);
|
||||
b->container = GTK_CONTAINER(b->widget);
|
||||
b->box = GTK_BOX(b->widget);
|
||||
|
||||
b->vertical = orientation == GTK_ORIENTATION_VERTICAL;
|
||||
|
||||
if (b->vertical)
|
||||
b->stretchygroup = gtk_size_group_new(GTK_SIZE_GROUP_VERTICAL);
|
||||
else
|
||||
b->stretchygroup = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
|
||||
|
||||
b->controls = g_array_new(FALSE, TRUE, sizeof (struct boxChild));
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
uiBox *uiNewHorizontalBox(void)
|
||||
{
|
||||
return finishNewBox(GTK_ORIENTATION_HORIZONTAL);
|
||||
}
|
||||
|
||||
uiBox *uiNewVerticalBox(void)
|
||||
{
|
||||
return finishNewBox(GTK_ORIENTATION_VERTICAL);
|
||||
}
|
55
deps/libui/unix/button.c
vendored
55
deps/libui/unix/button.c
vendored
@ -1,55 +0,0 @@
|
||||
// 10 june 2015
|
||||
#include "uipriv_unix.h"
|
||||
|
||||
struct uiButton {
|
||||
uiUnixControl c;
|
||||
GtkWidget *widget;
|
||||
GtkButton *button;
|
||||
void (*onClicked)(uiButton *, void *);
|
||||
void *onClickedData;
|
||||
};
|
||||
|
||||
uiUnixControlAllDefaults(uiButton)
|
||||
|
||||
static void onClicked(GtkButton *button, gpointer data)
|
||||
{
|
||||
uiButton *b = uiButton(data);
|
||||
|
||||
(*(b->onClicked))(b, b->onClickedData);
|
||||
}
|
||||
|
||||
static void defaultOnClicked(uiButton *b, void *data)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
char *uiButtonText(uiButton *b)
|
||||
{
|
||||
return uiUnixStrdupText(gtk_button_get_label(b->button));
|
||||
}
|
||||
|
||||
void uiButtonSetText(uiButton *b, const char *text)
|
||||
{
|
||||
gtk_button_set_label(b->button, text);
|
||||
}
|
||||
|
||||
void uiButtonOnClicked(uiButton *b, void (*f)(uiButton *, void *), void *data)
|
||||
{
|
||||
b->onClicked = f;
|
||||
b->onClickedData = data;
|
||||
}
|
||||
|
||||
uiButton *uiNewButton(const char *text)
|
||||
{
|
||||
uiButton *b;
|
||||
|
||||
uiUnixNewControl(uiButton, b);
|
||||
|
||||
b->widget = gtk_button_new_with_label(text);
|
||||
b->button = GTK_BUTTON(b->widget);
|
||||
|
||||
g_signal_connect(b->widget, "clicked", G_CALLBACK(onClicked), b);
|
||||
uiButtonOnClicked(b, defaultOnClicked, NULL);
|
||||
|
||||
return b;
|
||||
}
|
299
deps/libui/unix/cellrendererbutton.c
vendored
299
deps/libui/unix/cellrendererbutton.c
vendored
@ -1,299 +0,0 @@
|
||||
// 28 june 2016
|
||||
#include "uipriv_unix.h"
|
||||
|
||||
// TODOs
|
||||
// - it's a rather tight fit
|
||||
// - selected row text color is white
|
||||
// - resizing a column with a button in it crashes the program
|
||||
// - accessibility
|
||||
// - right side too big?
|
||||
|
||||
#define cellRendererButtonType (cellRendererButton_get_type())
|
||||
#define cellRendererButton(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), cellRendererButtonType, cellRendererButton))
|
||||
#define isCellRendererButton(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), cellRendererButtonType))
|
||||
#define cellRendererButtonClass(class) (G_TYPE_CHECK_CLASS_CAST((class), cellRendererButtonType, cellRendererButtonClass))
|
||||
#define isCellRendererButtonClass(class) (G_TYPE_CHECK_CLASS_TYPE((class), cellRendererButton))
|
||||
#define getCellRendererButtonClass(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), cellRendererButtonType, cellRendererButtonClass))
|
||||
|
||||
typedef struct cellRendererButton cellRendererButton;
|
||||
typedef struct cellRendererButtonClass cellRendererButtonClass;
|
||||
|
||||
struct cellRendererButton {
|
||||
GtkCellRenderer parent_instance;
|
||||
char *text;
|
||||
};
|
||||
|
||||
struct cellRendererButtonClass {
|
||||
GtkCellRendererClass parent_class;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(cellRendererButton, cellRendererButton, GTK_TYPE_CELL_RENDERER)
|
||||
|
||||
static void cellRendererButton_init(cellRendererButton *c)
|
||||
{
|
||||
g_object_set(c, "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
|
||||
// the standard cell renderers all do this
|
||||
gtk_cell_renderer_set_padding(GTK_CELL_RENDERER(c), 2, 2);
|
||||
}
|
||||
|
||||
static void cellRendererButton_dispose(GObject *obj)
|
||||
{
|
||||
G_OBJECT_CLASS(cellRendererButton_parent_class)->dispose(obj);
|
||||
}
|
||||
|
||||
static void cellRendererButton_finalize(GObject *obj)
|
||||
{
|
||||
cellRendererButton *c = cellRendererButton(obj);
|
||||
|
||||
if (c->text != NULL) {
|
||||
g_free(c->text);
|
||||
c->text = NULL;
|
||||
}
|
||||
G_OBJECT_CLASS(cellRendererButton_parent_class)->finalize(obj);
|
||||
}
|
||||
|
||||
static GtkSizeRequestMode cellRendererButton_get_request_mode(GtkCellRenderer *r)
|
||||
{
|
||||
return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
|
||||
}
|
||||
|
||||
// this is basically what GtkCellRendererToggle did in 3.10 and does in 3.20, as well as what the Foreign Drawing gtk3-demo demo does
|
||||
static GtkStyleContext *setButtonStyle(GtkWidget *widget)
|
||||
{
|
||||
GtkStyleContext *base, *context;
|
||||
GtkWidgetPath *path;
|
||||
|
||||
base = gtk_widget_get_style_context(widget);
|
||||
context = gtk_style_context_new();
|
||||
|
||||
path = gtk_widget_path_copy(gtk_style_context_get_path(base));
|
||||
gtk_widget_path_append_type(path, G_TYPE_NONE);
|
||||
if (!FUTURE_gtk_widget_path_iter_set_object_name(path, -1, "button"))
|
||||
// not on 3.20; try the type
|
||||
gtk_widget_path_iter_set_object_type(path, -1, GTK_TYPE_BUTTON);
|
||||
|
||||
gtk_style_context_set_path(context, path);
|
||||
gtk_style_context_set_parent(context, base);
|
||||
// the gtk3-demo example (which says we need to do this) uses gtk_widget_path_iter_get_state(path, -1) but that's not available until 3.14
|
||||
// TODO make a future for that too
|
||||
gtk_style_context_set_state(context, gtk_style_context_get_state(base));
|
||||
gtk_widget_path_unref(path);
|
||||
|
||||
// and if the above widget path screwery stil doesn't work, this will
|
||||
gtk_style_context_add_class(context, GTK_STYLE_CLASS_BUTTON);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
void unsetButtonStyle(GtkStyleContext *context)
|
||||
{
|
||||
g_object_unref(context);
|
||||
}
|
||||
|
||||
// this is based on what GtkCellRendererText does
|
||||
static void cellRendererButton_get_preferred_width(GtkCellRenderer *r, GtkWidget *widget, gint *minimum, gint *natural)
|
||||
{
|
||||
cellRendererButton *c = cellRendererButton(r);
|
||||
gint xpad;
|
||||
PangoLayout *layout;
|
||||
PangoRectangle rect;
|
||||
gint out;
|
||||
|
||||
gtk_cell_renderer_get_padding(GTK_CELL_RENDERER(c), &xpad, NULL);
|
||||
|
||||
layout = gtk_widget_create_pango_layout(widget, c->text);
|
||||
pango_layout_set_width(layout, -1);
|
||||
pango_layout_get_extents(layout, NULL, &rect);
|
||||
g_object_unref(layout);
|
||||
|
||||
out = 2 * xpad + PANGO_PIXELS_CEIL(rect.width);
|
||||
if (minimum != NULL)
|
||||
*minimum = out;
|
||||
if (natural != NULL)
|
||||
*natural = out;
|
||||
}
|
||||
|
||||
// this is based on what GtkCellRendererText does
|
||||
static void cellRendererButton_get_preferred_height_for_width(GtkCellRenderer *r, GtkWidget *widget, gint width, gint *minimum, gint *natural)
|
||||
{
|
||||
cellRendererButton *c = cellRendererButton(r);
|
||||
gint xpad, ypad;
|
||||
PangoLayout *layout;
|
||||
gint height;
|
||||
gint out;
|
||||
|
||||
gtk_cell_renderer_get_padding(GTK_CELL_RENDERER(c), &xpad, &ypad);
|
||||
|
||||
layout = gtk_widget_create_pango_layout(widget, c->text);
|
||||
pango_layout_set_width(layout, ((2 * xpad + width) * PANGO_SCALE));
|
||||
pango_layout_get_pixel_size(layout, NULL, &height);
|
||||
g_object_unref(layout);
|
||||
|
||||
out = 2 * ypad + height;
|
||||
if (minimum != NULL)
|
||||
*minimum = out;
|
||||
if (natural != NULL)
|
||||
*natural = out;
|
||||
}
|
||||
|
||||
// this is basically what GtkCellRendererText does
|
||||
static void cellRendererButton_get_preferred_height(GtkCellRenderer *r, GtkWidget *widget, gint *minimum, gint *natural)
|
||||
{
|
||||
gint width;
|
||||
|
||||
gtk_cell_renderer_get_preferred_width(r, widget, &width, NULL);
|
||||
gtk_cell_renderer_get_preferred_height_for_width(r, widget, width, minimum, natural);
|
||||
}
|
||||
|
||||
// this is based on what GtkCellRendererText does
|
||||
static void cellRendererButton_get_aligned_area(GtkCellRenderer *r, GtkWidget *widget, GtkCellRendererState flags, const GdkRectangle *cell_area, GdkRectangle *aligned_area)
|
||||
{
|
||||
cellRendererButton *c = cellRendererButton(r);
|
||||
gint xpad, ypad;
|
||||
PangoLayout *layout;
|
||||
PangoRectangle rect;
|
||||
gfloat xalign, yalign;
|
||||
gint xoffset, yoffset;
|
||||
|
||||
gtk_cell_renderer_get_padding(GTK_CELL_RENDERER(c), &xpad, &ypad);
|
||||
|
||||
layout = gtk_widget_create_pango_layout(widget, c->text);
|
||||
pango_layout_set_width(layout, -1);
|
||||
pango_layout_get_pixel_extents(layout, NULL, &rect);
|
||||
|
||||
xoffset = 0;
|
||||
yoffset = 0;
|
||||
if (cell_area != NULL) {
|
||||
gtk_cell_renderer_get_alignment(GTK_CELL_RENDERER(c), &xalign, &yalign);
|
||||
xoffset = cell_area->width - (2 * xpad + rect.width);
|
||||
// use explicit casts just to be safe
|
||||
if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL)
|
||||
xoffset = ((gdouble) xoffset) * (1.0 - xalign);
|
||||
else
|
||||
xoffset *= ((gdouble) xoffset) * xalign;
|
||||
yoffset = yalign * (cell_area->height - (2 * ypad + rect.height));
|
||||
yoffset = MAX(yoffset, 0);
|
||||
}
|
||||
|
||||
aligned_area->x = cell_area->x + xoffset;
|
||||
aligned_area->y = cell_area->y + yoffset;
|
||||
aligned_area->width = 2 * xpad + rect.width;
|
||||
aligned_area->height = 2 * ypad + rect.height;
|
||||
|
||||
g_object_unref(layout);
|
||||
}
|
||||
|
||||
// this is based on both what GtkCellRendererText does and what GtkCellRendererToggle does
|
||||
static void cellRendererButton_render(GtkCellRenderer *r, cairo_t *cr, GtkWidget *widget, const GdkRectangle *background_area, const GdkRectangle *cell_area, GtkCellRendererState flags)
|
||||
{
|
||||
cellRendererButton *c = cellRendererButton(r);
|
||||
gint xpad, ypad;
|
||||
GdkRectangle alignedArea;
|
||||
gint xoffset, yoffset;
|
||||
GtkStyleContext *context;
|
||||
PangoLayout *layout;
|
||||
PangoRectangle rect;
|
||||
|
||||
gtk_cell_renderer_get_padding(GTK_CELL_RENDERER(c), &xpad, &ypad);
|
||||
gtk_cell_renderer_get_aligned_area(GTK_CELL_RENDERER(c), widget, flags, cell_area, &alignedArea);
|
||||
xoffset = alignedArea.x - cell_area->x;
|
||||
yoffset = alignedArea.y - cell_area->y;
|
||||
|
||||
context = setButtonStyle(widget);
|
||||
layout = gtk_widget_create_pango_layout(widget, c->text);
|
||||
|
||||
gtk_render_background(context, cr,
|
||||
background_area->x + xoffset + xpad,
|
||||
background_area->y + yoffset + ypad,
|
||||
background_area->width - 2 * xpad,
|
||||
background_area->height - 2 * ypad);
|
||||
gtk_render_frame(context, cr,
|
||||
background_area->x + xoffset + xpad,
|
||||
background_area->y + yoffset + ypad,
|
||||
background_area->width - 2 * xpad,
|
||||
background_area->height - 2 * ypad);
|
||||
|
||||
pango_layout_set_width(layout, -1);
|
||||
pango_layout_get_pixel_extents(layout, NULL, &rect);
|
||||
xoffset -= rect.x;
|
||||
gtk_render_layout(context, cr,
|
||||
cell_area->x + xoffset + xpad,
|
||||
cell_area->y + yoffset + ypad,
|
||||
layout);
|
||||
|
||||
g_object_unref(layout);
|
||||
unsetButtonStyle(context);
|
||||
}
|
||||
|
||||
static guint clickedSignal;
|
||||
|
||||
static gboolean cellRendererButton_activate(GtkCellRenderer *r, GdkEvent *e, GtkWidget *widget, const gchar *path, const GdkRectangle *background_area, const GdkRectangle *cell_area, GtkCellRendererState flags)
|
||||
{
|
||||
g_signal_emit(r, clickedSignal, 0, path);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GParamSpec *props[2] = { NULL, NULL };
|
||||
|
||||
static void cellRendererButton_set_property(GObject *object, guint prop, const GValue *value, GParamSpec *pspec)
|
||||
{
|
||||
cellRendererButton *c = cellRendererButton(object);
|
||||
|
||||
if (prop != 1) {
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID(c, prop, pspec);
|
||||
return;
|
||||
}
|
||||
if (c->text != NULL)
|
||||
g_free(c->text);
|
||||
c->text = g_value_dup_string(value);
|
||||
// GtkCellRendererText doesn't queue a redraw; we won't either
|
||||
}
|
||||
|
||||
static void cellRendererButton_get_property(GObject *object, guint prop, GValue *value, GParamSpec *pspec)
|
||||
{
|
||||
cellRendererButton *c = cellRendererButton(object);
|
||||
|
||||
if (prop != 1) {
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID(c, prop, pspec);
|
||||
return;
|
||||
}
|
||||
g_value_set_string(value, c->text);
|
||||
}
|
||||
|
||||
static void cellRendererButton_class_init(cellRendererButtonClass *class)
|
||||
{
|
||||
G_OBJECT_CLASS(class)->dispose = cellRendererButton_dispose;
|
||||
G_OBJECT_CLASS(class)->finalize = cellRendererButton_finalize;
|
||||
G_OBJECT_CLASS(class)->set_property = cellRendererButton_set_property;
|
||||
G_OBJECT_CLASS(class)->get_property = cellRendererButton_get_property;
|
||||
GTK_CELL_RENDERER_CLASS(class)->get_request_mode = cellRendererButton_get_request_mode;
|
||||
GTK_CELL_RENDERER_CLASS(class)->get_preferred_width = cellRendererButton_get_preferred_width;
|
||||
GTK_CELL_RENDERER_CLASS(class)->get_preferred_height_for_width = cellRendererButton_get_preferred_height_for_width;
|
||||
GTK_CELL_RENDERER_CLASS(class)->get_preferred_height = cellRendererButton_get_preferred_height;
|
||||
// don't provide a get_preferred_width_for_height()
|
||||
GTK_CELL_RENDERER_CLASS(class)->get_aligned_area = cellRendererButton_get_aligned_area;
|
||||
// don't provide a get_size()
|
||||
GTK_CELL_RENDERER_CLASS(class)->render = cellRendererButton_render;
|
||||
GTK_CELL_RENDERER_CLASS(class)->activate = cellRendererButton_activate;
|
||||
// don't provide a start_editing()
|
||||
|
||||
props[1] = g_param_spec_string("text",
|
||||
"Text",
|
||||
"Button text",
|
||||
"",
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
|
||||
g_object_class_install_properties(G_OBJECT_CLASS(class), 2, props);
|
||||
|
||||
clickedSignal = g_signal_new("clicked",
|
||||
G_TYPE_FROM_CLASS(class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL, NULL,
|
||||
G_TYPE_NONE,
|
||||
1, G_TYPE_STRING);
|
||||
}
|
||||
|
||||
GtkCellRenderer *newCellRendererButton(void)
|
||||
{
|
||||
return GTK_CELL_RENDERER(g_object_new(cellRendererButtonType, NULL));
|
||||
}
|
78
deps/libui/unix/checkbox.c
vendored
78
deps/libui/unix/checkbox.c
vendored
@ -1,78 +0,0 @@
|
||||
// 10 june 2015
|
||||
#include "uipriv_unix.h"
|
||||
|
||||
struct uiCheckbox {
|
||||
uiUnixControl c;
|
||||
GtkWidget *widget;
|
||||
GtkButton *button;
|
||||
GtkToggleButton *toggleButton;
|
||||
GtkCheckButton *checkButton;
|
||||
void (*onToggled)(uiCheckbox *, void *);
|
||||
void *onToggledData;
|
||||
gulong onToggledSignal;
|
||||
};
|
||||
|
||||
uiUnixControlAllDefaults(uiCheckbox)
|
||||
|
||||
static void onToggled(GtkToggleButton *b, gpointer data)
|
||||
{
|
||||
uiCheckbox *c = uiCheckbox(data);
|
||||
|
||||
(*(c->onToggled))(c, c->onToggledData);
|
||||
}
|
||||
|
||||
static void defaultOnToggled(uiCheckbox *c, void *data)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
char *uiCheckboxText(uiCheckbox *c)
|
||||
{
|
||||
return uiUnixStrdupText(gtk_button_get_label(c->button));
|
||||
}
|
||||
|
||||
void uiCheckboxSetText(uiCheckbox *c, const char *text)
|
||||
{
|
||||
gtk_button_set_label(GTK_BUTTON(c->button), text);
|
||||
}
|
||||
|
||||
void uiCheckboxOnToggled(uiCheckbox *c, void (*f)(uiCheckbox *, void *), void *data)
|
||||
{
|
||||
c->onToggled = f;
|
||||
c->onToggledData = data;
|
||||
}
|
||||
|
||||
int uiCheckboxChecked(uiCheckbox *c)
|
||||
{
|
||||
return gtk_toggle_button_get_active(c->toggleButton) != FALSE;
|
||||
}
|
||||
|
||||
void uiCheckboxSetChecked(uiCheckbox *c, int checked)
|
||||
{
|
||||
gboolean active;
|
||||
|
||||
active = FALSE;
|
||||
if (checked)
|
||||
active = TRUE;
|
||||
// we need to inhibit sending of ::toggled because this WILL send a ::toggled otherwise
|
||||
g_signal_handler_block(c->toggleButton, c->onToggledSignal);
|
||||
gtk_toggle_button_set_active(c->toggleButton, active);
|
||||
g_signal_handler_unblock(c->toggleButton, c->onToggledSignal);
|
||||
}
|
||||
|
||||
uiCheckbox *uiNewCheckbox(const char *text)
|
||||
{
|
||||
uiCheckbox *c;
|
||||
|
||||
uiUnixNewControl(uiCheckbox, c);
|
||||
|
||||
c->widget = gtk_check_button_new_with_label(text);
|
||||
c->button = GTK_BUTTON(c->widget);
|
||||
c->toggleButton = GTK_TOGGLE_BUTTON(c->widget);
|
||||
c->checkButton = GTK_CHECK_BUTTON(c->widget);
|
||||
|
||||
c->onToggledSignal = g_signal_connect(c->widget, "toggled", G_CALLBACK(onToggled), c);
|
||||
uiCheckboxOnToggled(c, defaultOnToggled, NULL);
|
||||
|
||||
return c;
|
||||
}
|
120
deps/libui/unix/child.c
vendored
120
deps/libui/unix/child.c
vendored
@ -1,120 +0,0 @@
|
||||
// 28 august 2015
|
||||
#include "uipriv_unix.h"
|
||||
|
||||
// This file contains helpers for managing child controls.
|
||||
|
||||
struct child {
|
||||
uiControl *c;
|
||||
GtkWidget *widget;
|
||||
|
||||
gboolean oldhexpand;
|
||||
GtkAlign oldhalign;
|
||||
gboolean oldvexpand;
|
||||
GtkAlign oldvalign;
|
||||
|
||||
// Some children can be boxed; that is, they can have an optionally-margined box around them.
|
||||
// uiGroup, uiTab, and uiWindow all do this.
|
||||
GtkWidget *box;
|
||||
|
||||
// If the child is not boxed, this is its parent.
|
||||
// If the child is boxed, this is the box.
|
||||
GtkContainer *parent;
|
||||
|
||||
// This flag is for users of these functions.
|
||||
// For uiBox, this is "spaced".
|
||||
// For uiTab, this is "margined". (uiGroup and uiWindow have to maintain their margined state themselves, since the margined state is independent of whether there is a child for those two.)
|
||||
int flag;
|
||||
};
|
||||
|
||||
struct child *newChild(uiControl *child, uiControl *parent, GtkContainer *parentContainer)
|
||||
{
|
||||
struct child *c;
|
||||
|
||||
if (child == NULL)
|
||||
return NULL;
|
||||
|
||||
c = uiNew(struct child);
|
||||
c->c = child;
|
||||
c->widget = GTK_WIDGET(uiControlHandle(c->c));
|
||||
|
||||
c->oldhexpand = gtk_widget_get_hexpand(c->widget);
|
||||
c->oldhalign = gtk_widget_get_halign(c->widget);
|
||||
c->oldvexpand = gtk_widget_get_vexpand(c->widget);
|
||||
c->oldvalign = gtk_widget_get_valign(c->widget);
|
||||
|
||||
uiControlSetParent(c->c, parent);
|
||||
uiUnixControlSetContainer(uiUnixControl(c->c), parentContainer, FALSE);
|
||||
c->parent = parentContainer;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
struct child *newChildWithBox(uiControl *child, uiControl *parent, GtkContainer *parentContainer, int margined)
|
||||
{
|
||||
struct child *c;
|
||||
GtkWidget *box;
|
||||
|
||||
if (child == NULL)
|
||||
return NULL;
|
||||
box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
|
||||
gtk_widget_show(box);
|
||||
c = newChild(child, parent, GTK_CONTAINER(box));
|
||||
gtk_widget_set_hexpand(c->widget, TRUE);
|
||||
gtk_widget_set_halign(c->widget, GTK_ALIGN_FILL);
|
||||
gtk_widget_set_vexpand(c->widget, TRUE);
|
||||
gtk_widget_set_valign(c->widget, GTK_ALIGN_FILL);
|
||||
c->box = box;
|
||||
gtk_container_add(parentContainer, c->box);
|
||||
childSetMargined(c, margined);
|
||||
return c;
|
||||
}
|
||||
|
||||
void childRemove(struct child *c)
|
||||
{
|
||||
uiControlSetParent(c->c, NULL);
|
||||
uiUnixControlSetContainer(uiUnixControl(c->c), c->parent, TRUE);
|
||||
|
||||
gtk_widget_set_hexpand(c->widget, c->oldhexpand);
|
||||
gtk_widget_set_halign(c->widget, c->oldhalign);
|
||||
gtk_widget_set_vexpand(c->widget, c->oldvexpand);
|
||||
gtk_widget_set_valign(c->widget, c->oldvalign);
|
||||
|
||||
if (c->box != NULL)
|
||||
gtk_widget_destroy(c->box);
|
||||
|
||||
uiFree(c);
|
||||
}
|
||||
|
||||
void childDestroy(struct child *c)
|
||||
{
|
||||
uiControl *child;
|
||||
|
||||
child = c->c;
|
||||
childRemove(c);
|
||||
uiControlDestroy(child);
|
||||
}
|
||||
|
||||
GtkWidget *childWidget(struct child *c)
|
||||
{
|
||||
return c->widget;
|
||||
}
|
||||
|
||||
int childFlag(struct child *c)
|
||||
{
|
||||
return c->flag;
|
||||
}
|
||||
|
||||
void childSetFlag(struct child *c, int flag)
|
||||
{
|
||||
c->flag = flag;
|
||||
}
|
||||
|
||||
GtkWidget *childBox(struct child *c)
|
||||
{
|
||||
return c->box;
|
||||
}
|
||||
|
||||
void childSetMargined(struct child *c, int margined)
|
||||
{
|
||||
setMargined(GTK_CONTAINER(c->box), margined);
|
||||
}
|
80
deps/libui/unix/colorbutton.c
vendored
80
deps/libui/unix/colorbutton.c
vendored
@ -1,80 +0,0 @@
|
||||
// 15 may 2016
|
||||
#include "uipriv_unix.h"
|
||||
|
||||
struct uiColorButton {
|
||||
uiUnixControl c;
|
||||
GtkWidget *widget;
|
||||
GtkButton *button;
|
||||
GtkColorButton *cb;
|
||||
GtkColorChooser *cc;
|
||||
void (*onChanged)(uiColorButton *, void *);
|
||||
void *onChangedData;
|
||||
};
|
||||
|
||||
uiUnixControlAllDefaults(uiColorButton)
|
||||
|
||||
static void onColorSet(GtkColorButton *button, gpointer data)
|
||||
{
|
||||
uiColorButton *b = uiColorButton(data);
|
||||
|
||||
(*(b->onChanged))(b, b->onChangedData);
|
||||
}
|
||||
|
||||
static void defaultOnChanged(uiColorButton *b, void *data)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void uiColorButtonColor(uiColorButton *b, double *r, double *g, double *bl, double *a)
|
||||
{
|
||||
GdkRGBA rgba;
|
||||
|
||||
gtk_color_chooser_get_rgba(b->cc, &rgba);
|
||||
*r = rgba.red;
|
||||
*g = rgba.green;
|
||||
*bl = rgba.blue;
|
||||
*a = rgba.alpha;
|
||||
}
|
||||
|
||||
void uiColorButtonSetColor(uiColorButton *b, double r, double g, double bl, double a)
|
||||
{
|
||||
GdkRGBA rgba;
|
||||
|
||||
rgba.red = r;
|
||||
rgba.green = g;
|
||||
rgba.blue = bl;
|
||||
rgba.alpha = a;
|
||||
// no need to inhibit the signal; color-set is documented as only being sent when the user changes the color
|
||||
gtk_color_chooser_set_rgba(b->cc, &rgba);
|
||||
}
|
||||
|
||||
void uiColorButtonOnChanged(uiColorButton *b, void (*f)(uiColorButton *, void *), void *data)
|
||||
{
|
||||
b->onChanged = f;
|
||||
b->onChangedData = data;
|
||||
}
|
||||
|
||||
uiColorButton *uiNewColorButton(void)
|
||||
{
|
||||
uiColorButton *b;
|
||||
GdkRGBA black;
|
||||
|
||||
uiUnixNewControl(uiColorButton, b);
|
||||
|
||||
// I'm not sure what the initial color is; set up a real one
|
||||
black.red = 0.0;
|
||||
black.green = 0.0;
|
||||
black.blue = 0.0;
|
||||
black.alpha = 1.0;
|
||||
b->widget = gtk_color_button_new_with_rgba(&black);
|
||||
b->button = GTK_BUTTON(b->widget);
|
||||
b->cb = GTK_COLOR_BUTTON(b->widget);
|
||||
b->cc = GTK_COLOR_CHOOSER(b->widget);
|
||||
|
||||
gtk_color_chooser_set_use_alpha(b->cc, TRUE);
|
||||
|
||||
g_signal_connect(b->widget, "color-set", G_CALLBACK(onColorSet), b);
|
||||
uiColorButtonOnChanged(b, defaultOnChanged, NULL);
|
||||
|
||||
return b;
|
||||
}
|
66
deps/libui/unix/combobox.c
vendored
66
deps/libui/unix/combobox.c
vendored
@ -1,66 +0,0 @@
|
||||
// 11 june 2015
|
||||
#include "uipriv_unix.h"
|
||||
|
||||
struct uiCombobox {
|
||||
uiUnixControl c;
|
||||
GtkWidget *widget;
|
||||
GtkComboBox *combobox;
|
||||
GtkComboBoxText *comboboxText;
|
||||
void (*onSelected)(uiCombobox *, void *);
|
||||
void *onSelectedData;
|
||||
gulong onSelectedSignal;
|
||||
};
|
||||
|
||||
uiUnixControlAllDefaults(uiCombobox)
|
||||
|
||||
static void onChanged(GtkComboBox *cbox, gpointer data)
|
||||
{
|
||||
uiCombobox *c = uiCombobox(data);
|
||||
|
||||
(*(c->onSelected))(c, c->onSelectedData);
|
||||
}
|
||||
|
||||
static void defaultOnSelected(uiCombobox *c, void *data)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void uiComboboxAppend(uiCombobox *c, const char *text)
|
||||
{
|
||||
gtk_combo_box_text_append(c->comboboxText, NULL, text);
|
||||
}
|
||||
|
||||
int uiComboboxSelected(uiCombobox *c)
|
||||
{
|
||||
return gtk_combo_box_get_active(c->combobox);
|
||||
}
|
||||
|
||||
void uiComboboxSetSelected(uiCombobox *c, int n)
|
||||
{
|
||||
// we need to inhibit sending of ::changed because this WILL send a ::changed otherwise
|
||||
g_signal_handler_block(c->combobox, c->onSelectedSignal);
|
||||
gtk_combo_box_set_active(c->combobox, n);
|
||||
g_signal_handler_unblock(c->combobox, c->onSelectedSignal);
|
||||
}
|
||||
|
||||
void uiComboboxOnSelected(uiCombobox *c, void (*f)(uiCombobox *c, void *data), void *data)
|
||||
{
|
||||
c->onSelected = f;
|
||||
c->onSelectedData = data;
|
||||
}
|
||||
|
||||
uiCombobox *uiNewCombobox(void)
|
||||
{
|
||||
uiCombobox *c;
|
||||
|
||||
uiUnixNewControl(uiCombobox, c);
|
||||
|
||||
c->widget = gtk_combo_box_text_new();
|
||||
c->combobox = GTK_COMBO_BOX(c->widget);
|
||||
c->comboboxText = GTK_COMBO_BOX_TEXT(c->widget);
|
||||
|
||||
c->onSelectedSignal = g_signal_connect(c->widget, "changed", G_CALLBACK(onChanged), c);
|
||||
uiComboboxOnSelected(c, defaultOnSelected, NULL);
|
||||
|
||||
return c;
|
||||
}
|
14
deps/libui/unix/control.c
vendored
14
deps/libui/unix/control.c
vendored
@ -1,14 +0,0 @@
|
||||
// 16 august 2015
|
||||
#include "uipriv_unix.h"
|
||||
|
||||
void uiUnixControlSetContainer(uiUnixControl *c, GtkContainer *container, gboolean remove)
|
||||
{
|
||||
(*(c->SetContainer))(c, container, remove);
|
||||
}
|
||||
|
||||
#define uiUnixControlSignature 0x556E6978
|
||||
|
||||
uiUnixControl *uiUnixAllocControl(size_t n, uint32_t typesig, const char *typenamestr)
|
||||
{
|
||||
return uiUnixControl(uiAllocControl(n, uiUnixControlSignature, typesig, typenamestr));
|
||||
}
|
599
deps/libui/unix/datetimepicker.c
vendored
599
deps/libui/unix/datetimepicker.c
vendored
@ -1,599 +0,0 @@
|
||||
// 4 september 2015
|
||||
#include "uipriv_unix.h"
|
||||
|
||||
// LONGTERM imitate gnome-calendar's day/month/year entries above the calendar
|
||||
// LONGTERM allow entering a 24-hour hour in the hour spinbutton and adjust accordingly
|
||||
|
||||
#define dateTimePickerWidgetType (dateTimePickerWidget_get_type())
|
||||
#define dateTimePickerWidget(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), dateTimePickerWidgetType, dateTimePickerWidget))
|
||||
#define isDateTimePickerWidget(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), dateTimePickerWidgetType))
|
||||
#define dateTimePickerWidgetClass(class) (G_TYPE_CHECK_CLASS_CAST((class), dateTimePickerWidgetType, dateTimePickerWidgetClass))
|
||||
#define isDateTimePickerWidgetClass(class) (G_TYPE_CHECK_CLASS_TYPE((class), dateTimePickerWidget))
|
||||
#define getDateTimePickerWidgetClass(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), dateTimePickerWidgetType, dateTimePickerWidgetClass))
|
||||
|
||||
typedef struct dateTimePickerWidget dateTimePickerWidget;
|
||||
typedef struct dateTimePickerWidgetClass dateTimePickerWidgetClass;
|
||||
|
||||
struct dateTimePickerWidget {
|
||||
GtkToggleButton parent_instance;
|
||||
|
||||
gulong toggledSignal;
|
||||
|
||||
gboolean hasTime;
|
||||
gboolean hasDate;
|
||||
|
||||
GtkWidget *window;
|
||||
GtkWidget *box;
|
||||
GtkWidget *calendar;
|
||||
GtkWidget *timebox;
|
||||
GtkWidget *hours;
|
||||
GtkWidget *minutes;
|
||||
GtkWidget *seconds;
|
||||
GtkWidget *ampm;
|
||||
|
||||
gulong hoursBlock;
|
||||
gulong minutesBlock;
|
||||
gulong secondsBlock;
|
||||
gulong ampmBlock;
|
||||
|
||||
GdkDevice *keyboard;
|
||||
GdkDevice *mouse;
|
||||
};
|
||||
|
||||
struct dateTimePickerWidgetClass {
|
||||
GtkToggleButtonClass parent_class;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(dateTimePickerWidget, dateTimePickerWidget, GTK_TYPE_TOGGLE_BUTTON)
|
||||
|
||||
static int realSpinValue(GtkSpinButton *spinButton)
|
||||
{
|
||||
GtkAdjustment *adj;
|
||||
|
||||
adj = gtk_spin_button_get_adjustment(spinButton);
|
||||
return (int) gtk_adjustment_get_value(adj);
|
||||
}
|
||||
|
||||
static void setRealSpinValue(GtkSpinButton *spinButton, int value, gulong block)
|
||||
{
|
||||
GtkAdjustment *adj;
|
||||
|
||||
g_signal_handler_block(spinButton, block);
|
||||
adj = gtk_spin_button_get_adjustment(spinButton);
|
||||
gtk_adjustment_set_value(adj, value);
|
||||
g_signal_handler_unblock(spinButton, block);
|
||||
}
|
||||
|
||||
static GDateTime *selected(dateTimePickerWidget *d)
|
||||
{
|
||||
// choose a day for which all times are likely to be valid for the default date in case we're only dealing with time
|
||||
guint year = 1970, month = 1, day = 1;
|
||||
guint hour = 0, minute = 0, second = 0;
|
||||
|
||||
if (d->hasDate) {
|
||||
gtk_calendar_get_date(GTK_CALENDAR(d->calendar), &year, &month, &day);
|
||||
month++; // GtkCalendar/GDateTime differences
|
||||
}
|
||||
if (d->hasTime) {
|
||||
hour = realSpinValue(GTK_SPIN_BUTTON(d->hours));
|
||||
if (realSpinValue(GTK_SPIN_BUTTON(d->ampm)) != 0)
|
||||
hour += 12;
|
||||
minute = realSpinValue(GTK_SPIN_BUTTON(d->minutes));
|
||||
second = realSpinValue(GTK_SPIN_BUTTON(d->seconds));
|
||||
}
|
||||
return g_date_time_new_local(year, month, day, hour, minute, second);
|
||||
}
|
||||
|
||||
static void setLabel(dateTimePickerWidget *d)
|
||||
{
|
||||
GDateTime *dt;
|
||||
char *fmt;
|
||||
char *msg;
|
||||
gboolean free;
|
||||
|
||||
dt = selected(d);
|
||||
free = FALSE;
|
||||
if (d->hasDate && d->hasTime) {
|
||||
// don't use D_T_FMT; that's too verbose
|
||||
fmt = g_strdup_printf("%s %s", nl_langinfo(D_FMT), nl_langinfo(T_FMT));
|
||||
free = TRUE;
|
||||
} else if (d->hasDate)
|
||||
fmt = nl_langinfo(D_FMT);
|
||||
else
|
||||
fmt = nl_langinfo(T_FMT);
|
||||
msg = g_date_time_format(dt, fmt);
|
||||
gtk_button_set_label(GTK_BUTTON(d), msg);
|
||||
g_free(msg);
|
||||
if (free)
|
||||
g_free(fmt);
|
||||
g_date_time_unref(dt);
|
||||
}
|
||||
|
||||
static void dateTimeChanged(dateTimePickerWidget *d)
|
||||
{
|
||||
setLabel(d);
|
||||
// TODO fire event here
|
||||
}
|
||||
|
||||
// we don't want ::toggled to be sent again
|
||||
static void setActive(dateTimePickerWidget *d, gboolean active)
|
||||
{
|
||||
g_signal_handler_block(d, d->toggledSignal);
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d), active);
|
||||
g_signal_handler_unblock(d, d->toggledSignal);
|
||||
}
|
||||
|
||||
// like startGrab() below, a lot of this is in the order that GtkComboBox does it
|
||||
static void endGrab(dateTimePickerWidget *d)
|
||||
{
|
||||
if (d->keyboard != NULL)
|
||||
gdk_device_ungrab(d->keyboard, GDK_CURRENT_TIME);
|
||||
gdk_device_ungrab(d->mouse, GDK_CURRENT_TIME);
|
||||
gtk_device_grab_remove(d->window, d->mouse);
|
||||
d->keyboard = NULL;
|
||||
d->mouse = NULL;
|
||||
}
|
||||
|
||||
static void hidePopup(dateTimePickerWidget *d)
|
||||
{
|
||||
endGrab(d);
|
||||
gtk_widget_hide(d->window);
|
||||
setActive(d, FALSE);
|
||||
}
|
||||
|
||||
// this consolidates a good chunk of what GtkComboBox does
|
||||
static gboolean startGrab(dateTimePickerWidget *d)
|
||||
{
|
||||
GdkDevice *dev;
|
||||
guint32 time;
|
||||
GdkWindow *window;
|
||||
GdkDevice *keyboard, *mouse;
|
||||
|
||||
dev = gtk_get_current_event_device();
|
||||
if (dev == NULL) {
|
||||
// this is what GtkComboBox does
|
||||
// since no device was set, just use the first available "master device"
|
||||
GdkDisplay *disp;
|
||||
GdkDeviceManager *dm;
|
||||
GList *list;
|
||||
|
||||
disp = gtk_widget_get_display(GTK_WIDGET(d));
|
||||
dm = gdk_display_get_device_manager(disp);
|
||||
list = gdk_device_manager_list_devices(dm, GDK_DEVICE_TYPE_MASTER);
|
||||
dev = (GdkDevice *) (list->data);
|
||||
g_list_free(list);
|
||||
}
|
||||
|
||||
time = gtk_get_current_event_time();
|
||||
keyboard = dev;
|
||||
mouse = gdk_device_get_associated_device(dev);
|
||||
if (gdk_device_get_source(dev) != GDK_SOURCE_KEYBOARD) {
|
||||
dev = mouse;
|
||||
mouse = keyboard;
|
||||
keyboard = dev;
|
||||
}
|
||||
|
||||
window = gtk_widget_get_window(d->window);
|
||||
if (keyboard != NULL)
|
||||
if (gdk_device_grab(keyboard, window,
|
||||
GDK_OWNERSHIP_WINDOW, TRUE,
|
||||
GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
|
||||
NULL, time) != GDK_GRAB_SUCCESS)
|
||||
return FALSE;
|
||||
if (mouse != NULL)
|
||||
if (gdk_device_grab(mouse, window,
|
||||
GDK_OWNERSHIP_WINDOW, TRUE,
|
||||
GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK,
|
||||
NULL, time) != GDK_GRAB_SUCCESS) {
|
||||
if (keyboard != NULL)
|
||||
gdk_device_ungrab(keyboard, time);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gtk_device_grab_add(d->window, mouse, TRUE);
|
||||
d->keyboard = keyboard;
|
||||
d->mouse = mouse;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// based on gtk_combo_box_list_position() in the GTK+ source code
|
||||
static void allocationToScreen(dateTimePickerWidget *d, gint *x, gint *y)
|
||||
{
|
||||
GdkWindow *window;
|
||||
GtkAllocation a;
|
||||
GtkRequisition aWin;
|
||||
GdkScreen *screen;
|
||||
GdkRectangle workarea;
|
||||
int otherY;
|
||||
|
||||
gtk_widget_get_allocation(GTK_WIDGET(d), &a);
|
||||
gtk_widget_get_preferred_size(d->window, &aWin, NULL);
|
||||
*x = 0;
|
||||
*y = 0;
|
||||
if (!gtk_widget_get_has_window(GTK_WIDGET(d))) {
|
||||
*x = a.x;
|
||||
*y = a.y;
|
||||
}
|
||||
window = gtk_widget_get_window(GTK_WIDGET(d));
|
||||
gdk_window_get_root_coords(window, *x, *y, x, y);
|
||||
if (gtk_widget_get_direction(GTK_WIDGET(d)) == GTK_TEXT_DIR_RTL)
|
||||
*x += a.width - aWin.width;
|
||||
|
||||
// now adjust to prevent the box from going offscreen
|
||||
screen = gtk_widget_get_screen(GTK_WIDGET(d));
|
||||
gdk_screen_get_monitor_workarea(screen,
|
||||
gdk_screen_get_monitor_at_window(screen, window),
|
||||
&workarea);
|
||||
if (*x < workarea.x) // too far to the left?
|
||||
*x = workarea.x;
|
||||
else if (*x + aWin.width > (workarea.x + workarea.width)) // too far to the right?
|
||||
*x = (workarea.x + workarea.width) - aWin.width;
|
||||
// this isn't the same algorithm used by GtkComboBox
|
||||
// first, get our two choices; *y for down and otherY for up
|
||||
otherY = *y - aWin.height;
|
||||
*y += a.height;
|
||||
// and use otherY if we're too low
|
||||
if (*y + aWin.height >= workarea.y + workarea.height)
|
||||
*y = otherY;
|
||||
}
|
||||
|
||||
static void showPopup(dateTimePickerWidget *d)
|
||||
{
|
||||
GtkWidget *toplevel;
|
||||
gint x, y;
|
||||
|
||||
// GtkComboBox does it
|
||||
toplevel = gtk_widget_get_toplevel(GTK_WIDGET(d));
|
||||
if (GTK_IS_WINDOW(toplevel))
|
||||
gtk_window_group_add_window(gtk_window_get_group(GTK_WINDOW(toplevel)), GTK_WINDOW(d->window));
|
||||
|
||||
allocationToScreen(d, &x, &y);
|
||||
gtk_window_move(GTK_WINDOW(d->window), x, y);
|
||||
|
||||
gtk_widget_show(d->window);
|
||||
setActive(d, TRUE);
|
||||
|
||||
if (!startGrab(d))
|
||||
hidePopup(d);
|
||||
}
|
||||
|
||||
static void onToggled(GtkToggleButton *b, gpointer data)
|
||||
{
|
||||
dateTimePickerWidget *d = dateTimePickerWidget(b);
|
||||
|
||||
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(d)))
|
||||
showPopup(d);
|
||||
else
|
||||
hidePopup(d);
|
||||
}
|
||||
|
||||
static gboolean grabBroken(GtkWidget *w, GdkEventGrabBroken *e, gpointer data)
|
||||
{
|
||||
dateTimePickerWidget *d = dateTimePickerWidget(data);
|
||||
|
||||
hidePopup(d);
|
||||
return TRUE; // this is what GtkComboBox does
|
||||
}
|
||||
|
||||
static gboolean buttonReleased(GtkWidget *w, GdkEventButton *e, gpointer data)
|
||||
{
|
||||
dateTimePickerWidget *d = dateTimePickerWidget(data);
|
||||
int winx, winy;
|
||||
GtkAllocation wina;
|
||||
gboolean in;
|
||||
|
||||
gtk_widget_get_allocation(d->window, &wina);
|
||||
winx = 0;
|
||||
winy = 0;
|
||||
if (!gtk_widget_get_has_window(d->window)) {
|
||||
winx = wina.x;
|
||||
winy = wina.y;
|
||||
}
|
||||
gdk_window_get_root_coords(gtk_widget_get_window(d->window), winx, winy, &winx, &winy);
|
||||
in = TRUE;
|
||||
if (e->x_root < winx)
|
||||
in = FALSE;
|
||||
if (e->x_root >= (winx + wina.width))
|
||||
in = FALSE;
|
||||
if (e->y_root < winy)
|
||||
in = FALSE;
|
||||
if (e->y_root >= (winy + wina.height))
|
||||
in = FALSE;
|
||||
if (!in)
|
||||
hidePopup(d);
|
||||
return TRUE; // this is what GtkComboBox does
|
||||
}
|
||||
|
||||
static gint hoursSpinboxInput(GtkSpinButton *sb, gpointer ptr, gpointer data)
|
||||
{
|
||||
double *out = (double *) ptr;
|
||||
const gchar *text;
|
||||
int value;
|
||||
|
||||
text = gtk_entry_get_text(GTK_ENTRY(sb));
|
||||
value = (int) g_strtod(text, NULL);
|
||||
if (value < 0 || value > 12)
|
||||
return GTK_INPUT_ERROR;
|
||||
if (value == 12) // 12 to the user is 0 internally
|
||||
value = 0;
|
||||
*out = (double) value;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean hoursSpinboxOutput(GtkSpinButton *sb, gpointer data)
|
||||
{
|
||||
gchar *text;
|
||||
int value;
|
||||
|
||||
value = realSpinValue(sb);
|
||||
if (value == 0) // 0 internally is 12 to the user
|
||||
value = 12;
|
||||
text = g_strdup_printf("%d", value);
|
||||
gtk_entry_set_text(GTK_ENTRY(sb), text);
|
||||
g_free(text);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean zeroPadSpinbox(GtkSpinButton *sb, gpointer data)
|
||||
{
|
||||
gchar *text;
|
||||
int value;
|
||||
|
||||
value = realSpinValue(sb);
|
||||
text = g_strdup_printf("%02d", value);
|
||||
gtk_entry_set_text(GTK_ENTRY(sb), text);
|
||||
g_free(text);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// this is really hacky but we can't use GtkCombobox here :(
|
||||
static gint ampmSpinboxInput(GtkSpinButton *sb, gpointer ptr, gpointer data)
|
||||
{
|
||||
double *out = (double *) ptr;
|
||||
const gchar *text;
|
||||
char firstAM, firstPM;
|
||||
|
||||
text = gtk_entry_get_text(GTK_ENTRY(sb));
|
||||
// LONGTERM don't use ASCII here for case insensitivity
|
||||
firstAM = g_ascii_tolower(nl_langinfo(AM_STR)[0]);
|
||||
firstPM = g_ascii_tolower(nl_langinfo(PM_STR)[0]);
|
||||
for (; *text != '\0'; text++)
|
||||
if (g_ascii_tolower(*text) == firstAM) {
|
||||
*out = 0;
|
||||
return TRUE;
|
||||
} else if (g_ascii_tolower(*text) == firstPM) {
|
||||
*out = 1;
|
||||
return TRUE;
|
||||
}
|
||||
return GTK_INPUT_ERROR;
|
||||
}
|
||||
|
||||
static gboolean ampmSpinboxOutput(GtkSpinButton *sb, gpointer data)
|
||||
{
|
||||
int value;
|
||||
|
||||
value = gtk_spin_button_get_value_as_int(sb);
|
||||
if (value == 0)
|
||||
gtk_entry_set_text(GTK_ENTRY(sb), nl_langinfo(AM_STR));
|
||||
else
|
||||
gtk_entry_set_text(GTK_ENTRY(sb), nl_langinfo(PM_STR));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void spinboxChanged(GtkSpinButton *sb, gpointer data)
|
||||
{
|
||||
dateTimePickerWidget *d = dateTimePickerWidget(data);
|
||||
|
||||
dateTimeChanged(d);
|
||||
}
|
||||
|
||||
static GtkWidget *newSpinbox(dateTimePickerWidget *d, int min, int max, gint (*input)(GtkSpinButton *, gpointer, gpointer), gboolean (*output)(GtkSpinButton *, gpointer), gulong *block)
|
||||
{
|
||||
GtkWidget *sb;
|
||||
|
||||
sb = gtk_spin_button_new_with_range(min, max, 1);
|
||||
gtk_spin_button_set_digits(GTK_SPIN_BUTTON(sb), 0);
|
||||
gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(sb), TRUE);
|
||||
gtk_orientable_set_orientation(GTK_ORIENTABLE(sb), GTK_ORIENTATION_VERTICAL);
|
||||
*block = g_signal_connect(sb, "value-changed", G_CALLBACK(spinboxChanged), d);
|
||||
if (input != NULL)
|
||||
g_signal_connect(sb, "input", G_CALLBACK(input), NULL);
|
||||
if (output != NULL)
|
||||
g_signal_connect(sb, "output", G_CALLBACK(output), NULL);
|
||||
return sb;
|
||||
}
|
||||
|
||||
static void dateChanged(GtkCalendar *c, gpointer data)
|
||||
{
|
||||
dateTimePickerWidget *d = dateTimePickerWidget(data);
|
||||
|
||||
dateTimeChanged(d);
|
||||
}
|
||||
|
||||
static void setDateOnly(dateTimePickerWidget *d)
|
||||
{
|
||||
d->hasTime = FALSE;
|
||||
gtk_container_remove(GTK_CONTAINER(d->box), d->timebox);
|
||||
}
|
||||
|
||||
static void setTimeOnly(dateTimePickerWidget *d)
|
||||
{
|
||||
d->hasDate = FALSE;
|
||||
gtk_container_remove(GTK_CONTAINER(d->box), d->calendar);
|
||||
}
|
||||
|
||||
static void dateTimePickerWidget_init(dateTimePickerWidget *d)
|
||||
{
|
||||
GDateTime *dt;
|
||||
gint year, month, day;
|
||||
gint hour;
|
||||
gulong calendarBlock;
|
||||
|
||||
d->window = gtk_window_new(GTK_WINDOW_POPUP);
|
||||
gtk_window_set_resizable(GTK_WINDOW(d->window), FALSE);
|
||||
gtk_window_set_attached_to(GTK_WINDOW(d->window), GTK_WIDGET(d));
|
||||
gtk_window_set_decorated(GTK_WINDOW(d->window), FALSE);
|
||||
gtk_window_set_deletable(GTK_WINDOW(d->window), FALSE);
|
||||
gtk_window_set_type_hint(GTK_WINDOW(d->window), GDK_WINDOW_TYPE_HINT_COMBO);
|
||||
gtk_window_set_skip_taskbar_hint(GTK_WINDOW(d->window), TRUE);
|
||||
gtk_window_set_skip_pager_hint(GTK_WINDOW(d->window), TRUE);
|
||||
gtk_window_set_has_resize_grip(GTK_WINDOW(d->window), FALSE);
|
||||
gtk_container_set_border_width(GTK_CONTAINER(d->window), 12);
|
||||
// and make it stand out a bit
|
||||
gtk_style_context_add_class(gtk_widget_get_style_context(d->window), "frame");
|
||||
|
||||
d->box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6);
|
||||
gtk_container_add(GTK_CONTAINER(d->window), d->box);
|
||||
|
||||
d->calendar = gtk_calendar_new();
|
||||
calendarBlock = g_signal_connect(d->calendar, "day-selected", G_CALLBACK(dateChanged), d);
|
||||
gtk_container_add(GTK_CONTAINER(d->box), d->calendar);
|
||||
|
||||
d->timebox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
|
||||
gtk_widget_set_valign(d->timebox, GTK_ALIGN_CENTER);
|
||||
gtk_container_add(GTK_CONTAINER(d->box), d->timebox);
|
||||
|
||||
d->hours = newSpinbox(d, 0, 11, hoursSpinboxInput, hoursSpinboxOutput, &(d->hoursBlock));
|
||||
gtk_container_add(GTK_CONTAINER(d->timebox), d->hours);
|
||||
|
||||
gtk_container_add(GTK_CONTAINER(d->timebox),
|
||||
gtk_label_new(":"));
|
||||
|
||||
d->minutes = newSpinbox(d, 0, 59, NULL, zeroPadSpinbox, &(d->minutesBlock));
|
||||
gtk_container_add(GTK_CONTAINER(d->timebox), d->minutes);
|
||||
|
||||
gtk_container_add(GTK_CONTAINER(d->timebox),
|
||||
gtk_label_new(":"));
|
||||
|
||||
d->seconds = newSpinbox(d, 0, 59, NULL, zeroPadSpinbox, &(d->secondsBlock));
|
||||
gtk_container_add(GTK_CONTAINER(d->timebox), d->seconds);
|
||||
|
||||
// LONGTERM this should be the case, but that interferes with grabs
|
||||
// switch to it when we can drop GTK+ 3.10 and use popovers
|
||||
#if 0
|
||||
d->ampm = gtk_combo_box_text_new();
|
||||
gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(d->ampm), NULL, "AM");
|
||||
gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(d->ampm), NULL, "PM");
|
||||
#endif
|
||||
d->ampm = newSpinbox(d, 0, 1, ampmSpinboxInput, ampmSpinboxOutput, &(d->ampmBlock));
|
||||
gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(d->ampm), FALSE);
|
||||
gtk_widget_set_valign(d->ampm, GTK_ALIGN_CENTER);
|
||||
gtk_container_add(GTK_CONTAINER(d->timebox), d->ampm);
|
||||
|
||||
gtk_widget_show_all(d->box);
|
||||
|
||||
g_signal_connect(d->window, "grab-broken-event", G_CALLBACK(grabBroken), d);
|
||||
g_signal_connect(d->window, "button-release-event", G_CALLBACK(buttonReleased), d);
|
||||
|
||||
d->toggledSignal = g_signal_connect(d, "toggled", G_CALLBACK(onToggled), NULL);
|
||||
d->keyboard = NULL;
|
||||
d->mouse = NULL;
|
||||
|
||||
d->hasTime = TRUE;
|
||||
d->hasDate = TRUE;
|
||||
|
||||
// set the current date/time
|
||||
// notice how we block signals from firing
|
||||
dt = g_date_time_new_now_local();
|
||||
g_date_time_get_ymd(dt, &year, &month, &day);
|
||||
month--; // GDateTime/GtkCalendar differences
|
||||
g_signal_handler_block(d->calendar, calendarBlock);
|
||||
gtk_calendar_select_month(GTK_CALENDAR(d->calendar), month, year);
|
||||
gtk_calendar_select_day(GTK_CALENDAR(d->calendar), day);
|
||||
g_signal_handler_unblock(d->calendar, calendarBlock);
|
||||
hour = g_date_time_get_hour(dt);
|
||||
if (hour >= 12) {
|
||||
hour -= 12;
|
||||
setRealSpinValue(GTK_SPIN_BUTTON(d->ampm), 1, d->ampmBlock);
|
||||
}
|
||||
setRealSpinValue(GTK_SPIN_BUTTON(d->hours), hour, d->hoursBlock);
|
||||
setRealSpinValue(GTK_SPIN_BUTTON(d->minutes), g_date_time_get_minute(dt), d->minutesBlock);
|
||||
setRealSpinValue(GTK_SPIN_BUTTON(d->seconds), g_date_time_get_seconds(dt), d->secondsBlock);
|
||||
g_date_time_unref(dt);
|
||||
}
|
||||
|
||||
static void dateTimePickerWidget_dispose(GObject *obj)
|
||||
{
|
||||
dateTimePickerWidget *d = dateTimePickerWidget(obj);
|
||||
|
||||
if (d->window != NULL) {
|
||||
gtk_widget_destroy(d->window);
|
||||
d->window = NULL;
|
||||
}
|
||||
G_OBJECT_CLASS(dateTimePickerWidget_parent_class)->dispose(obj);
|
||||
}
|
||||
|
||||
static void dateTimePickerWidget_finalize(GObject *obj)
|
||||
{
|
||||
G_OBJECT_CLASS(dateTimePickerWidget_parent_class)->finalize(obj);
|
||||
}
|
||||
|
||||
static void dateTimePickerWidget_class_init(dateTimePickerWidgetClass *class)
|
||||
{
|
||||
G_OBJECT_CLASS(class)->dispose = dateTimePickerWidget_dispose;
|
||||
G_OBJECT_CLASS(class)->finalize = dateTimePickerWidget_finalize;
|
||||
}
|
||||
|
||||
static GtkWidget *newDTP(void)
|
||||
{
|
||||
GtkWidget *w;
|
||||
|
||||
w = GTK_WIDGET(g_object_new(dateTimePickerWidgetType, "label", "", NULL));
|
||||
setLabel(dateTimePickerWidget(w));
|
||||
return w;
|
||||
}
|
||||
|
||||
static GtkWidget *newDP(void)
|
||||
{
|
||||
GtkWidget *w;
|
||||
|
||||
w = GTK_WIDGET(g_object_new(dateTimePickerWidgetType, "label", "", NULL));
|
||||
setDateOnly(dateTimePickerWidget(w));
|
||||
setLabel(dateTimePickerWidget(w));
|
||||
return w;
|
||||
}
|
||||
|
||||
static GtkWidget *newTP(void)
|
||||
{
|
||||
GtkWidget *w;
|
||||
|
||||
w = GTK_WIDGET(g_object_new(dateTimePickerWidgetType, "label", "", NULL));
|
||||
setTimeOnly(dateTimePickerWidget(w));
|
||||
setLabel(dateTimePickerWidget(w));
|
||||
return w;
|
||||
}
|
||||
|
||||
struct uiDateTimePicker {
|
||||
uiUnixControl c;
|
||||
GtkWidget *widget;
|
||||
dateTimePickerWidget *d;
|
||||
};
|
||||
|
||||
uiUnixControlAllDefaults(uiDateTimePicker)
|
||||
|
||||
uiDateTimePicker *finishNewDateTimePicker(GtkWidget *(*fn)(void))
|
||||
{
|
||||
uiDateTimePicker *d;
|
||||
|
||||
uiUnixNewControl(uiDateTimePicker, d);
|
||||
|
||||
d->widget = (*fn)();
|
||||
d->d = dateTimePickerWidget(d->widget);
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
uiDateTimePicker *uiNewDateTimePicker(void)
|
||||
{
|
||||
return finishNewDateTimePicker(newDTP);
|
||||
}
|
||||
|
||||
uiDateTimePicker *uiNewDatePicker(void)
|
||||
{
|
||||
return finishNewDateTimePicker(newDP);
|
||||
}
|
||||
|
||||
uiDateTimePicker *uiNewTimePicker(void)
|
||||
{
|
||||
return finishNewDateTimePicker(newTP);
|
||||
}
|
14
deps/libui/unix/debug.c
vendored
14
deps/libui/unix/debug.c
vendored
@ -1,14 +0,0 @@
|
||||
// 13 may 2016
|
||||
#include "uipriv_unix.h"
|
||||
|
||||
// LONGTERM don't halt on release builds
|
||||
|
||||
void realbug(const char *file, const char *line, const char *func, const char *prefix, const char *format, va_list ap)
|
||||
{
|
||||
char *a, *b;
|
||||
|
||||
a = g_strdup_printf("[libui] %s:%s:%s() %s", file, line, func, prefix);
|
||||
b = g_strdup_vprintf(format, ap);
|
||||
g_critical("%s%s", a, b);
|
||||
G_BREAKPOINT();
|
||||
}
|
141
deps/libui/unix/draw.c
vendored
141
deps/libui/unix/draw.c
vendored
@ -1,141 +0,0 @@
|
||||
// 6 september 2015
|
||||
#include "uipriv_unix.h"
|
||||
#include "draw.h"
|
||||
|
||||
uiDrawContext *newContext(cairo_t *cr)
|
||||
{
|
||||
uiDrawContext *c;
|
||||
|
||||
c = uiNew(uiDrawContext);
|
||||
c->cr = cr;
|
||||
return c;
|
||||
}
|
||||
|
||||
void freeContext(uiDrawContext *c)
|
||||
{
|
||||
uiFree(c);
|
||||
}
|
||||
|
||||
static cairo_pattern_t *mkbrush(uiDrawBrush *b)
|
||||
{
|
||||
cairo_pattern_t *pat;
|
||||
size_t i;
|
||||
|
||||
switch (b->Type) {
|
||||
case uiDrawBrushTypeSolid:
|
||||
pat = cairo_pattern_create_rgba(b->R, b->G, b->B, b->A);
|
||||
break;
|
||||
case uiDrawBrushTypeLinearGradient:
|
||||
pat = cairo_pattern_create_linear(b->X0, b->Y0, b->X1, b->Y1);
|
||||
break;
|
||||
case uiDrawBrushTypeRadialGradient:
|
||||
// make the start circle radius 0 to make it a point
|
||||
pat = cairo_pattern_create_radial(
|
||||
b->X0, b->Y0, 0,
|
||||
b->X1, b->Y1, b->OuterRadius);
|
||||
break;
|
||||
// case uiDrawBrushTypeImage:
|
||||
}
|
||||
if (cairo_pattern_status(pat) != CAIRO_STATUS_SUCCESS)
|
||||
implbug("error creating pattern in mkbrush(): %s",
|
||||
cairo_status_to_string(cairo_pattern_status(pat)));
|
||||
switch (b->Type) {
|
||||
case uiDrawBrushTypeLinearGradient:
|
||||
case uiDrawBrushTypeRadialGradient:
|
||||
for (i = 0; i < b->NumStops; i++)
|
||||
cairo_pattern_add_color_stop_rgba(pat,
|
||||
b->Stops[i].Pos,
|
||||
b->Stops[i].R,
|
||||
b->Stops[i].G,
|
||||
b->Stops[i].B,
|
||||
b->Stops[i].A);
|
||||
}
|
||||
return pat;
|
||||
}
|
||||
|
||||
void uiDrawStroke(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b, uiDrawStrokeParams *p)
|
||||
{
|
||||
cairo_pattern_t *pat;
|
||||
|
||||
runPath(path, c->cr);
|
||||
pat = mkbrush(b);
|
||||
cairo_set_source(c->cr, pat);
|
||||
switch (p->Cap) {
|
||||
case uiDrawLineCapFlat:
|
||||
cairo_set_line_cap(c->cr, CAIRO_LINE_CAP_BUTT);
|
||||
break;
|
||||
case uiDrawLineCapRound:
|
||||
cairo_set_line_cap(c->cr, CAIRO_LINE_CAP_ROUND);
|
||||
break;
|
||||
case uiDrawLineCapSquare:
|
||||
cairo_set_line_cap(c->cr, CAIRO_LINE_CAP_SQUARE);
|
||||
break;
|
||||
}
|
||||
switch (p->Join) {
|
||||
case uiDrawLineJoinMiter:
|
||||
cairo_set_line_join(c->cr, CAIRO_LINE_JOIN_MITER);
|
||||
cairo_set_miter_limit(c->cr, p->MiterLimit);
|
||||
break;
|
||||
case uiDrawLineJoinRound:
|
||||
cairo_set_line_join(c->cr, CAIRO_LINE_JOIN_ROUND);
|
||||
break;
|
||||
case uiDrawLineJoinBevel:
|
||||
cairo_set_line_join(c->cr, CAIRO_LINE_JOIN_BEVEL);
|
||||
break;
|
||||
}
|
||||
cairo_set_line_width(c->cr, p->Thickness);
|
||||
cairo_set_dash(c->cr, p->Dashes, p->NumDashes, p->DashPhase);
|
||||
cairo_stroke(c->cr);
|
||||
cairo_pattern_destroy(pat);
|
||||
}
|
||||
|
||||
void uiDrawFill(uiDrawContext *c, uiDrawPath *path, uiDrawBrush *b)
|
||||
{
|
||||
cairo_pattern_t *pat;
|
||||
|
||||
runPath(path, c->cr);
|
||||
pat = mkbrush(b);
|
||||
cairo_set_source(c->cr, pat);
|
||||
switch (pathFillMode(path)) {
|
||||
case uiDrawFillModeWinding:
|
||||
cairo_set_fill_rule(c->cr, CAIRO_FILL_RULE_WINDING);
|
||||
break;
|
||||
case uiDrawFillModeAlternate:
|
||||
cairo_set_fill_rule(c->cr, CAIRO_FILL_RULE_EVEN_ODD);
|
||||
break;
|
||||
}
|
||||
cairo_fill(c->cr);
|
||||
cairo_pattern_destroy(pat);
|
||||
}
|
||||
|
||||
void uiDrawTransform(uiDrawContext *c, uiDrawMatrix *m)
|
||||
{
|
||||
cairo_matrix_t cm;
|
||||
|
||||
m2c(m, &cm);
|
||||
cairo_transform(c->cr, &cm);
|
||||
}
|
||||
|
||||
void uiDrawClip(uiDrawContext *c, uiDrawPath *path)
|
||||
{
|
||||
runPath(path, c->cr);
|
||||
switch (pathFillMode(path)) {
|
||||
case uiDrawFillModeWinding:
|
||||
cairo_set_fill_rule(c->cr, CAIRO_FILL_RULE_WINDING);
|
||||
break;
|
||||
case uiDrawFillModeAlternate:
|
||||
cairo_set_fill_rule(c->cr, CAIRO_FILL_RULE_EVEN_ODD);
|
||||
break;
|
||||
}
|
||||
cairo_clip(c->cr);
|
||||
}
|
||||
|
||||
void uiDrawSave(uiDrawContext *c)
|
||||
{
|
||||
cairo_save(c->cr);
|
||||
}
|
||||
|
||||
void uiDrawRestore(uiDrawContext *c)
|
||||
{
|
||||
cairo_restore(c->cr);
|
||||
}
|
13
deps/libui/unix/draw.h
vendored
13
deps/libui/unix/draw.h
vendored
@ -1,13 +0,0 @@
|
||||
// 5 may 2016
|
||||
|
||||
// draw.c
|
||||
struct uiDrawContext {
|
||||
cairo_t *cr;
|
||||
};
|
||||
|
||||
// drawpath.c
|
||||
extern void runPath(uiDrawPath *p, cairo_t *cr);
|
||||
extern uiDrawFillMode pathFillMode(uiDrawPath *path);
|
||||
|
||||
// drawmatrix.c
|
||||
extern void m2c(uiDrawMatrix *m, cairo_matrix_t *c);
|
109
deps/libui/unix/drawmatrix.c
vendored
109
deps/libui/unix/drawmatrix.c
vendored
@ -1,109 +0,0 @@
|
||||
// 6 september 2015
|
||||
#include "uipriv_unix.h"
|
||||
#include "draw.h"
|
||||
|
||||
void m2c(uiDrawMatrix *m, cairo_matrix_t *c)
|
||||
{
|
||||
c->xx = m->M11;
|
||||
c->yx = m->M12;
|
||||
c->xy = m->M21;
|
||||
c->yy = m->M22;
|
||||
c->x0 = m->M31;
|
||||
c->y0 = m->M32;
|
||||
}
|
||||
|
||||
static void c2m(cairo_matrix_t *c, uiDrawMatrix *m)
|
||||
{
|
||||
m->M11 = c->xx;
|
||||
m->M12 = c->yx;
|
||||
m->M21 = c->xy;
|
||||
m->M22 = c->yy;
|
||||
m->M31 = c->x0;
|
||||
m->M32 = c->y0;
|
||||
}
|
||||
|
||||
void uiDrawMatrixTranslate(uiDrawMatrix *m, double x, double y)
|
||||
{
|
||||
cairo_matrix_t c;
|
||||
|
||||
m2c(m, &c);
|
||||
cairo_matrix_translate(&c, x, y);
|
||||
c2m(&c, m);
|
||||
}
|
||||
|
||||
void uiDrawMatrixScale(uiDrawMatrix *m, double xCenter, double yCenter, double x, double y)
|
||||
{
|
||||
cairo_matrix_t c;
|
||||
double xt, yt;
|
||||
|
||||
m2c(m, &c);
|
||||
xt = x;
|
||||
yt = y;
|
||||
scaleCenter(xCenter, yCenter, &xt, &yt);
|
||||
cairo_matrix_translate(&c, xt, yt);
|
||||
cairo_matrix_scale(&c, x, y);
|
||||
cairo_matrix_translate(&c, -xt, -yt);
|
||||
c2m(&c, m);
|
||||
}
|
||||
|
||||
void uiDrawMatrixRotate(uiDrawMatrix *m, double x, double y, double amount)
|
||||
{
|
||||
cairo_matrix_t c;
|
||||
|
||||
m2c(m, &c);
|
||||
cairo_matrix_translate(&c, x, y);
|
||||
cairo_matrix_rotate(&c, amount);
|
||||
cairo_matrix_translate(&c, -x, -y);
|
||||
c2m(&c, m);
|
||||
}
|
||||
|
||||
void uiDrawMatrixSkew(uiDrawMatrix *m, double x, double y, double xamount, double yamount)
|
||||
{
|
||||
fallbackSkew(m, x, y, xamount, yamount);
|
||||
}
|
||||
|
||||
void uiDrawMatrixMultiply(uiDrawMatrix *dest, uiDrawMatrix *src)
|
||||
{
|
||||
cairo_matrix_t c;
|
||||
cairo_matrix_t d;
|
||||
|
||||
m2c(dest, &c);
|
||||
m2c(src, &d);
|
||||
cairo_matrix_multiply(&c, &c, &d);
|
||||
c2m(&c, dest);
|
||||
}
|
||||
|
||||
int uiDrawMatrixInvertible(uiDrawMatrix *m)
|
||||
{
|
||||
cairo_matrix_t c;
|
||||
|
||||
m2c(m, &c);
|
||||
return cairo_matrix_invert(&c) == CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
int uiDrawMatrixInvert(uiDrawMatrix *m)
|
||||
{
|
||||
cairo_matrix_t c;
|
||||
|
||||
m2c(m, &c);
|
||||
if (cairo_matrix_invert(&c) != CAIRO_STATUS_SUCCESS)
|
||||
return 0;
|
||||
c2m(&c, m);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void uiDrawMatrixTransformPoint(uiDrawMatrix *m, double *x, double *y)
|
||||
{
|
||||
cairo_matrix_t c;
|
||||
|
||||
m2c(m, &c);
|
||||
cairo_matrix_transform_point(&c, x, y);
|
||||
}
|
||||
|
||||
void uiDrawMatrixTransformSize(uiDrawMatrix *m, double *x, double *y)
|
||||
{
|
||||
cairo_matrix_t c;
|
||||
|
||||
m2c(m, &c);
|
||||
cairo_matrix_transform_distance(&c, x, y);
|
||||
}
|
199
deps/libui/unix/drawpath.c
vendored
199
deps/libui/unix/drawpath.c
vendored
@ -1,199 +0,0 @@
|
||||
// 6 september 2015
|
||||
#include "uipriv_unix.h"
|
||||
#include "draw.h"
|
||||
|
||||
struct uiDrawPath {
|
||||
GArray *pieces;
|
||||
uiDrawFillMode fillMode;
|
||||
gboolean ended;
|
||||
};
|
||||
|
||||
struct piece {
|
||||
int type;
|
||||
double d[8];
|
||||
int b;
|
||||
};
|
||||
|
||||
enum {
|
||||
newFigure,
|
||||
newFigureArc,
|
||||
lineTo,
|
||||
arcTo,
|
||||
bezierTo,
|
||||
closeFigure,
|
||||
addRect,
|
||||
};
|
||||
|
||||
uiDrawPath *uiDrawNewPath(uiDrawFillMode mode)
|
||||
{
|
||||
uiDrawPath *p;
|
||||
|
||||
p = uiNew(uiDrawPath);
|
||||
p->pieces = g_array_new(FALSE, TRUE, sizeof (struct piece));
|
||||
p->fillMode = mode;
|
||||
return p;
|
||||
}
|
||||
|
||||
void uiDrawFreePath(uiDrawPath *p)
|
||||
{
|
||||
g_array_free(p->pieces, TRUE);
|
||||
uiFree(p);
|
||||
}
|
||||
|
||||
static void add(uiDrawPath *p, struct piece *piece)
|
||||
{
|
||||
if (p->ended)
|
||||
userbug("You cannot modify a uiDrawPath that has been ended. (path: %p)", p);
|
||||
g_array_append_vals(p->pieces, piece, 1);
|
||||
}
|
||||
|
||||
void uiDrawPathNewFigure(uiDrawPath *p, double x, double y)
|
||||
{
|
||||
struct piece piece;
|
||||
|
||||
piece.type = newFigure;
|
||||
piece.d[0] = x;
|
||||
piece.d[1] = y;
|
||||
add(p, &piece);
|
||||
}
|
||||
|
||||
void uiDrawPathNewFigureWithArc(uiDrawPath *p, double xCenter, double yCenter, double radius, double startAngle, double sweep, int negative)
|
||||
{
|
||||
struct piece piece;
|
||||
|
||||
if (sweep > 2 * uiPi)
|
||||
sweep = 2 * uiPi;
|
||||
piece.type = newFigureArc;
|
||||
piece.d[0] = xCenter;
|
||||
piece.d[1] = yCenter;
|
||||
piece.d[2] = radius;
|
||||
piece.d[3] = startAngle;
|
||||
piece.d[4] = sweep;
|
||||
piece.b = negative;
|
||||
add(p, &piece);
|
||||
}
|
||||
|
||||
void uiDrawPathLineTo(uiDrawPath *p, double x, double y)
|
||||
{
|
||||
struct piece piece;
|
||||
|
||||
piece.type = lineTo;
|
||||
piece.d[0] = x;
|
||||
piece.d[1] = y;
|
||||
add(p, &piece);
|
||||
}
|
||||
|
||||
void uiDrawPathArcTo(uiDrawPath *p, double xCenter, double yCenter, double radius, double startAngle, double sweep, int negative)
|
||||
{
|
||||
struct piece piece;
|
||||
|
||||
if (sweep > 2 * uiPi)
|
||||
sweep = 2 * uiPi;
|
||||
piece.type = arcTo;
|
||||
piece.d[0] = xCenter;
|
||||
piece.d[1] = yCenter;
|
||||
piece.d[2] = radius;
|
||||
piece.d[3] = startAngle;
|
||||
piece.d[4] = sweep;
|
||||
piece.b = negative;
|
||||
add(p, &piece);
|
||||
}
|
||||
|
||||
void uiDrawPathBezierTo(uiDrawPath *p, double c1x, double c1y, double c2x, double c2y, double endX, double endY)
|
||||
{
|
||||
struct piece piece;
|
||||
|
||||
piece.type = bezierTo;
|
||||
piece.d[0] = c1x;
|
||||
piece.d[1] = c1y;
|
||||
piece.d[2] = c2x;
|
||||
piece.d[3] = c2y;
|
||||
piece.d[4] = endX;
|
||||
piece.d[5] = endY;
|
||||
add(p, &piece);
|
||||
}
|
||||
|
||||
void uiDrawPathCloseFigure(uiDrawPath *p)
|
||||
{
|
||||
struct piece piece;
|
||||
|
||||
piece.type = closeFigure;
|
||||
add(p, &piece);
|
||||
}
|
||||
|
||||
void uiDrawPathAddRectangle(uiDrawPath *p, double x, double y, double width, double height)
|
||||
{
|
||||
struct piece piece;
|
||||
|
||||
piece.type = addRect;
|
||||
piece.d[0] = x;
|
||||
piece.d[1] = y;
|
||||
piece.d[2] = width;
|
||||
piece.d[3] = height;
|
||||
add(p, &piece);
|
||||
}
|
||||
|
||||
void uiDrawPathEnd(uiDrawPath *p)
|
||||
{
|
||||
p->ended = TRUE;
|
||||
}
|
||||
|
||||
void runPath(uiDrawPath *p, cairo_t *cr)
|
||||
{
|
||||
guint i;
|
||||
struct piece *piece;
|
||||
void (*arc)(cairo_t *, double, double, double, double, double);
|
||||
|
||||
if (!p->ended)
|
||||
userbug("You cannot draw with a uiDrawPath that has not been ended. (path: %p)", p);
|
||||
cairo_new_path(cr);
|
||||
for (i = 0; i < p->pieces->len; i++) {
|
||||
piece = &g_array_index(p->pieces, struct piece, i);
|
||||
switch (piece->type) {
|
||||
case newFigure:
|
||||
cairo_move_to(cr, piece->d[0], piece->d[1]);
|
||||
break;
|
||||
case newFigureArc:
|
||||
cairo_new_sub_path(cr);
|
||||
// fall through
|
||||
case arcTo:
|
||||
arc = cairo_arc;
|
||||
if (piece->b)
|
||||
arc = cairo_arc_negative;
|
||||
(*arc)(cr,
|
||||
piece->d[0],
|
||||
piece->d[1],
|
||||
piece->d[2],
|
||||
piece->d[3],
|
||||
piece->d[3] + piece->d[4]);
|
||||
break;
|
||||
case lineTo:
|
||||
cairo_line_to(cr, piece->d[0], piece->d[1]);
|
||||
break;
|
||||
case bezierTo:
|
||||
cairo_curve_to(cr,
|
||||
piece->d[0],
|
||||
piece->d[1],
|
||||
piece->d[2],
|
||||
piece->d[3],
|
||||
piece->d[4],
|
||||
piece->d[5]);
|
||||
break;
|
||||
case closeFigure:
|
||||
cairo_close_path(cr);
|
||||
break;
|
||||
case addRect:
|
||||
cairo_rectangle(cr,
|
||||
piece->d[0],
|
||||
piece->d[1],
|
||||
piece->d[2],
|
||||
piece->d[3]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uiDrawFillMode pathFillMode(uiDrawPath *path)
|
||||
{
|
||||
return path->fillMode;
|
||||
}
|
293
deps/libui/unix/drawtext.c
vendored
293
deps/libui/unix/drawtext.c
vendored
@ -1,293 +0,0 @@
|
||||
// 6 september 2015
|
||||
#include "uipriv_unix.h"
|
||||
#include "draw.h"
|
||||
|
||||
struct uiDrawFontFamilies {
|
||||
PangoFontFamily **f;
|
||||
int n;
|
||||
};
|
||||
|
||||
uiDrawFontFamilies *uiDrawListFontFamilies(void)
|
||||
{
|
||||
uiDrawFontFamilies *ff;
|
||||
PangoFontMap *map;
|
||||
|
||||
ff = uiNew(uiDrawFontFamilies);
|
||||
map = pango_cairo_font_map_get_default();
|
||||
pango_font_map_list_families(map, &(ff->f), &(ff->n));
|
||||
// do not free map; it's a shared resource
|
||||
return ff;
|
||||
}
|
||||
|
||||
int uiDrawFontFamiliesNumFamilies(uiDrawFontFamilies *ff)
|
||||
{
|
||||
return ff->n;
|
||||
}
|
||||
|
||||
char *uiDrawFontFamiliesFamily(uiDrawFontFamilies *ff, int n)
|
||||
{
|
||||
PangoFontFamily *f;
|
||||
|
||||
f = ff->f[n];
|
||||
return uiUnixStrdupText(pango_font_family_get_name(f));
|
||||
}
|
||||
|
||||
void uiDrawFreeFontFamilies(uiDrawFontFamilies *ff)
|
||||
{
|
||||
g_free(ff->f);
|
||||
uiFree(ff);
|
||||
}
|
||||
|
||||
struct uiDrawTextFont {
|
||||
PangoFont *f;
|
||||
};
|
||||
|
||||
uiDrawTextFont *mkTextFont(PangoFont *f, gboolean ref)
|
||||
{
|
||||
uiDrawTextFont *font;
|
||||
|
||||
font = uiNew(uiDrawTextFont);
|
||||
font->f = f;
|
||||
if (ref)
|
||||
g_object_ref(font->f);
|
||||
return font;
|
||||
}
|
||||
|
||||
static const PangoWeight pangoWeights[] = {
|
||||
[uiDrawTextWeightThin] = PANGO_WEIGHT_THIN,
|
||||
[uiDrawTextWeightUltraLight] = PANGO_WEIGHT_ULTRALIGHT,
|
||||
[uiDrawTextWeightLight] = PANGO_WEIGHT_LIGHT,
|
||||
[uiDrawTextWeightBook] = PANGO_WEIGHT_BOOK,
|
||||
[uiDrawTextWeightNormal] = PANGO_WEIGHT_NORMAL,
|
||||
[uiDrawTextWeightMedium] = PANGO_WEIGHT_MEDIUM,
|
||||
[uiDrawTextWeightSemiBold] = PANGO_WEIGHT_SEMIBOLD,
|
||||
[uiDrawTextWeightBold] = PANGO_WEIGHT_BOLD,
|
||||
[uiDrawTextWeightUltraBold] = PANGO_WEIGHT_ULTRABOLD,
|
||||
[uiDrawTextWeightHeavy] = PANGO_WEIGHT_HEAVY,
|
||||
[uiDrawTextWeightUltraHeavy] = PANGO_WEIGHT_ULTRAHEAVY,
|
||||
};
|
||||
|
||||
static const PangoStyle pangoItalics[] = {
|
||||
[uiDrawTextItalicNormal] = PANGO_STYLE_NORMAL,
|
||||
[uiDrawTextItalicOblique] = PANGO_STYLE_OBLIQUE,
|
||||
[uiDrawTextItalicItalic] = PANGO_STYLE_ITALIC,
|
||||
};
|
||||
|
||||
static const PangoStretch pangoStretches[] = {
|
||||
[uiDrawTextStretchUltraCondensed] = PANGO_STRETCH_ULTRA_CONDENSED,
|
||||
[uiDrawTextStretchExtraCondensed] = PANGO_STRETCH_EXTRA_CONDENSED,
|
||||
[uiDrawTextStretchCondensed] = PANGO_STRETCH_CONDENSED,
|
||||
[uiDrawTextStretchSemiCondensed] = PANGO_STRETCH_SEMI_CONDENSED,
|
||||
[uiDrawTextStretchNormal] = PANGO_STRETCH_NORMAL,
|
||||
[uiDrawTextStretchSemiExpanded] = PANGO_STRETCH_SEMI_EXPANDED,
|
||||
[uiDrawTextStretchExpanded] = PANGO_STRETCH_EXPANDED,
|
||||
[uiDrawTextStretchExtraExpanded] = PANGO_STRETCH_EXTRA_EXPANDED,
|
||||
[uiDrawTextStretchUltraExpanded] = PANGO_STRETCH_ULTRA_EXPANDED,
|
||||
};
|
||||
|
||||
// we need a context for a few things
|
||||
// the documentation suggests creating cairo_t-specific, GdkScreen-specific, or even GtkWidget-specific contexts, but we can't really do that because we want our uiDrawTextFonts and uiDrawTextLayouts to be context-independent
|
||||
// we could use pango_font_map_create_context(pango_cairo_font_map_get_default()) but that will ignore GDK-specific settings
|
||||
// so let's use gdk_pango_context_get() instead; even though it's for the default screen only, it's good enough for us
|
||||
#define mkGenericPangoCairoContext() (gdk_pango_context_get())
|
||||
|
||||
PangoFont *pangoDescToPangoFont(PangoFontDescription *pdesc)
|
||||
{
|
||||
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)
|
||||
{
|
||||
PangoFontDescription *desc;
|
||||
int width;
|
||||
|
||||
pango_layout_set_text(pl, layout->s, -1);
|
||||
|
||||
// again, this makes a copy
|
||||
desc = pango_font_describe(layout->defaultFont);
|
||||
// this is safe; the description is copied
|
||||
pango_layout_set_font_description(pl, desc);
|
||||
pango_font_description_free(desc);
|
||||
|
||||
width = cairoToPango(layout->width);
|
||||
if (layout->width < 0)
|
||||
width = -1;
|
||||
pango_layout_set_width(pl, width);
|
||||
|
||||
pango_layout_set_attributes(pl, layout->attrs);
|
||||
}
|
||||
|
||||
void uiDrawTextLayoutExtents(uiDrawTextLayout *layout, double *width, double *height)
|
||||
{
|
||||
PangoContext *context;
|
||||
PangoLayout *pl;
|
||||
PangoRectangle logical;
|
||||
|
||||
// in this case, the context is necessary to create the layout
|
||||
// the layout takes a ref on the context so we can unref it afterward
|
||||
context = mkGenericPangoCairoContext();
|
||||
pl = pango_layout_new(context);
|
||||
g_object_unref(context);
|
||||
prepareLayout(layout, pl);
|
||||
|
||||
pango_layout_get_extents(pl, NULL, &logical);
|
||||
|
||||
g_object_unref(pl);
|
||||
|
||||
*width = pangoToCairo(logical.width);
|
||||
*height = pangoToCairo(logical.height);
|
||||
}
|
||||
|
||||
void uiDrawText(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout)
|
||||
{
|
||||
PangoLayout *pl;
|
||||
|
||||
pl = pango_cairo_create_layout(c->cr);
|
||||
prepareLayout(layout, pl);
|
||||
|
||||
cairo_move_to(c->cr, x, y);
|
||||
pango_cairo_show_layout(c->cr, pl);
|
||||
|
||||
g_object_unref(pl);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
79
deps/libui/unix/editablecombo.c
vendored
79
deps/libui/unix/editablecombo.c
vendored
@ -1,79 +0,0 @@
|
||||
// 11 june 2015
|
||||
#include "uipriv_unix.h"
|
||||
|
||||
struct uiEditableCombobox {
|
||||
uiUnixControl c;
|
||||
GtkWidget *widget;
|
||||
GtkBin *bin;
|
||||
GtkComboBox *combobox;
|
||||
GtkComboBoxText *comboboxText;
|
||||
void (*onChanged)(uiEditableCombobox *, void *);
|
||||
void *onChangedData;
|
||||
gulong onChangedSignal;
|
||||
};
|
||||
|
||||
uiUnixControlAllDefaults(uiEditableCombobox)
|
||||
|
||||
static void onChanged(GtkComboBox *cbox, gpointer data)
|
||||
{
|
||||
uiEditableCombobox *c = uiEditableCombobox(data);
|
||||
|
||||
(*(c->onChanged))(c, c->onChangedData);
|
||||
}
|
||||
|
||||
static void defaultOnChanged(uiEditableCombobox *c, void *data)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void uiEditableComboboxAppend(uiEditableCombobox *c, const char *text)
|
||||
{
|
||||
gtk_combo_box_text_append(c->comboboxText, NULL, text);
|
||||
}
|
||||
|
||||
char *uiEditableComboboxText(uiEditableCombobox *c)
|
||||
{
|
||||
char *s;
|
||||
char *out;
|
||||
|
||||
s = gtk_combo_box_text_get_active_text(c->comboboxText);
|
||||
// s will always be non-NULL in the case of a combobox with an entry (according to the source code)
|
||||
out = uiUnixStrdupText(s);
|
||||
g_free(s);
|
||||
return out;
|
||||
}
|
||||
|
||||
void uiEditableComboboxSetText(uiEditableCombobox *c, const char *text)
|
||||
{
|
||||
GtkEntry *e;
|
||||
|
||||
// we need to inhibit sending of ::changed because this WILL send a ::changed otherwise
|
||||
g_signal_handler_block(c->combobox, c->onChangedSignal);
|
||||
// since there isn't a gtk_combo_box_text_set_active_text()...
|
||||
e = GTK_ENTRY(gtk_bin_get_child(c->bin));
|
||||
gtk_entry_set_text(e, text);
|
||||
g_signal_handler_unblock(c->combobox, c->onChangedSignal);
|
||||
}
|
||||
|
||||
void uiEditableComboboxOnChanged(uiEditableCombobox *c, void (*f)(uiEditableCombobox *c, void *data), void *data)
|
||||
{
|
||||
c->onChanged = f;
|
||||
c->onChangedData = data;
|
||||
}
|
||||
|
||||
uiEditableCombobox *uiNewEditableCombobox(void)
|
||||
{
|
||||
uiEditableCombobox *c;
|
||||
|
||||
uiUnixNewControl(uiEditableCombobox, c);
|
||||
|
||||
c->widget = gtk_combo_box_text_new_with_entry();
|
||||
c->bin = GTK_BIN(c->widget);
|
||||
c->combobox = GTK_COMBO_BOX(c->widget);
|
||||
c->comboboxText = GTK_COMBO_BOX_TEXT(c->widget);
|
||||
|
||||
c->onChangedSignal = g_signal_connect(c->widget, "changed", G_CALLBACK(onChanged), c);
|
||||
uiEditableComboboxOnChanged(c, defaultOnChanged, NULL);
|
||||
|
||||
return c;
|
||||
}
|
97
deps/libui/unix/entry.c
vendored
97
deps/libui/unix/entry.c
vendored
@ -1,97 +0,0 @@
|
||||
// 11 june 2015
|
||||
#include "uipriv_unix.h"
|
||||
|
||||
struct uiEntry {
|
||||
uiUnixControl c;
|
||||
GtkWidget *widget;
|
||||
GtkEntry *entry;
|
||||
GtkEditable *editable;
|
||||
void (*onChanged)(uiEntry *, void *);
|
||||
void *onChangedData;
|
||||
gulong onChangedSignal;
|
||||
};
|
||||
|
||||
uiUnixControlAllDefaults(uiEntry)
|
||||
|
||||
static void onChanged(GtkEditable *editable, gpointer data)
|
||||
{
|
||||
uiEntry *e = uiEntry(data);
|
||||
|
||||
(*(e->onChanged))(e, e->onChangedData);
|
||||
}
|
||||
|
||||
static void defaultOnChanged(uiEntry *e, void *data)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
char *uiEntryText(uiEntry *e)
|
||||
{
|
||||
return uiUnixStrdupText(gtk_entry_get_text(e->entry));
|
||||
}
|
||||
|
||||
void uiEntrySetText(uiEntry *e, const char *text)
|
||||
{
|
||||
// we need to inhibit sending of ::changed because this WILL send a ::changed otherwise
|
||||
g_signal_handler_block(e->editable, e->onChangedSignal);
|
||||
gtk_entry_set_text(e->entry, text);
|
||||
g_signal_handler_unblock(e->editable, e->onChangedSignal);
|
||||
// don't queue the control for resize; entry sizes are independent of their contents
|
||||
}
|
||||
|
||||
void uiEntryOnChanged(uiEntry *e, void (*f)(uiEntry *, void *), void *data)
|
||||
{
|
||||
e->onChanged = f;
|
||||
e->onChangedData = data;
|
||||
}
|
||||
|
||||
int uiEntryReadOnly(uiEntry *e)
|
||||
{
|
||||
return gtk_editable_get_editable(e->editable) == FALSE;
|
||||
}
|
||||
|
||||
void uiEntrySetReadOnly(uiEntry *e, int readonly)
|
||||
{
|
||||
gboolean editable;
|
||||
|
||||
editable = TRUE;
|
||||
if (readonly)
|
||||
editable = FALSE;
|
||||
gtk_editable_set_editable(e->editable, editable);
|
||||
}
|
||||
|
||||
static uiEntry *finishNewEntry(GtkWidget *w, const gchar *signal)
|
||||
{
|
||||
uiEntry *e;
|
||||
|
||||
uiUnixNewControl(uiEntry, e);
|
||||
|
||||
e->widget = w;
|
||||
e->entry = GTK_ENTRY(e->widget);
|
||||
e->editable = GTK_EDITABLE(e->widget);
|
||||
|
||||
e->onChangedSignal = g_signal_connect(e->widget, signal, G_CALLBACK(onChanged), e);
|
||||
uiEntryOnChanged(e, defaultOnChanged, NULL);
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
uiEntry *uiNewEntry(void)
|
||||
{
|
||||
return finishNewEntry(gtk_entry_new(), "changed");
|
||||
}
|
||||
|
||||
uiEntry *uiNewPasswordEntry(void)
|
||||
{
|
||||
GtkWidget *e;
|
||||
|
||||
e = gtk_entry_new();
|
||||
gtk_entry_set_visibility(GTK_ENTRY(e), FALSE);
|
||||
return finishNewEntry(e, "changed");
|
||||
}
|
||||
|
||||
// TODO make it use a separate function to be type-safe
|
||||
uiEntry *uiNewSearchEntry(void)
|
||||
{
|
||||
return finishNewEntry(gtk_search_entry_new(), "search-changed");
|
||||
}
|
70
deps/libui/unix/fontbutton.c
vendored
70
deps/libui/unix/fontbutton.c
vendored
@ -1,70 +0,0 @@
|
||||
// 14 april 2016
|
||||
#include "uipriv_unix.h"
|
||||
|
||||
struct uiFontButton {
|
||||
uiUnixControl c;
|
||||
GtkWidget *widget;
|
||||
GtkButton *button;
|
||||
GtkFontButton *fb;
|
||||
GtkFontChooser *fc;
|
||||
void (*onChanged)(uiFontButton *, void *);
|
||||
void *onChangedData;
|
||||
};
|
||||
|
||||
uiUnixControlAllDefaults(uiFontButton)
|
||||
|
||||
// TODO NOTE no need to inhibit the signal; font-set is documented as only being sent when the user changes the font
|
||||
static void onFontSet(GtkFontButton *button, gpointer data)
|
||||
{
|
||||
uiFontButton *b = uiFontButton(data);
|
||||
|
||||
(*(b->onChanged))(b, b->onChangedData);
|
||||
}
|
||||
|
||||
static void defaultOnChanged(uiFontButton *b, void *data)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
uiDrawTextFont *uiFontButtonFont(uiFontButton *b)
|
||||
{
|
||||
PangoFont *f;
|
||||
PangoFontDescription *desc;
|
||||
|
||||
desc = gtk_font_chooser_get_font_desc(b->fc);
|
||||
f = pangoDescToPangoFont(desc);
|
||||
// desc is transfer-full and thus is a copy
|
||||
pango_font_description_free(desc);
|
||||
return mkTextFont(f, FALSE); // we hold the initial reference; no need to ref
|
||||
}
|
||||
|
||||
void uiFontButtonOnChanged(uiFontButton *b, void (*f)(uiFontButton *, void *), void *data)
|
||||
{
|
||||
b->onChanged = f;
|
||||
b->onChangedData = data;
|
||||
}
|
||||
|
||||
uiFontButton *uiNewFontButton(void)
|
||||
{
|
||||
uiFontButton *b;
|
||||
|
||||
uiUnixNewControl(uiFontButton, b);
|
||||
|
||||
b->widget = gtk_font_button_new();
|
||||
b->button = GTK_BUTTON(b->widget);
|
||||
b->fb = GTK_FONT_BUTTON(b->widget);
|
||||
b->fc = GTK_FONT_CHOOSER(b->widget);
|
||||
|
||||
// match behavior on other platforms
|
||||
gtk_font_button_set_show_style(b->fb, TRUE);
|
||||
gtk_font_button_set_show_size(b->fb, TRUE);
|
||||
gtk_font_button_set_use_font(b->fb, FALSE);
|
||||
gtk_font_button_set_use_size(b->fb, FALSE);
|
||||
// other customizations
|
||||
gtk_font_chooser_set_show_preview_entry(b->fc, TRUE);
|
||||
|
||||
g_signal_connect(b->widget, "font-set", G_CALLBACK(onFontSet), b);
|
||||
uiFontButtonOnChanged(b, defaultOnChanged, NULL);
|
||||
|
||||
return b;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user