This commit is contained in:
Arisotura 2020-04-25 18:51:08 +02:00
parent 3b3a09ed2b
commit a85d41c53e
271 changed files with 0 additions and 46177 deletions

View File

@ -1,73 +0,0 @@
project(libui_sdl)
SET(SOURCES_LIBUI
main.cpp
Platform.cpp
PlatformConfig.cpp
LAN_Socket.cpp
LAN_PCap.cpp
DlgAudioSettings.cpp
DlgEmuSettings.cpp
DlgInputConfig.cpp
DlgVideoSettings.cpp
DlgWifiSettings.cpp
OSD.cpp
)
if (WIN32)
set(CMAKE_RC_COMPILE_OBJECT "<CMAKE_RC_COMPILER> -i <SOURCE> -o <OBJECT>")
endif()
option(BUILD_SHARED_LIBS "Whether to build libui as a shared library or a static library" ON)
set(BUILD_SHARED_LIBS OFF)
add_subdirectory(libui)
find_package(PkgConfig REQUIRED)
pkg_check_modules(SDL2 REQUIRED sdl2)
add_executable(melonDS ${SOURCES_LIBUI})
target_include_directories(melonDS PRIVATE ${SDL2_INCLUDE_DIRS})
target_link_libraries(melonDS core libui ${SDL2_LIBRARIES})
if (UNIX)
option(UNIX_PORTABLE "Make a portable build that looks for its configuration in the current directory" OFF)
if (UNIX_PORTABLE)
add_definitions(-DUNIX_PORTABLE)
endif()
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK3 REQUIRED gtk+-3.0)
target_include_directories(melonDS PRIVATE ${GTK3_INCLUDE_DIRS})
target_link_libraries(melonDS ${GTK3_LIBRARIES})
ADD_DEFINITIONS(${GTK3_CFLAGS_OTHER})
add_custom_command(OUTPUT melon_grc.c
COMMAND glib-compile-resources --sourcedir=${CMAKE_SOURCE_DIR}
--target=${CMAKE_CURRENT_BINARY_DIR}/melon_grc.c
--generate-source "${CMAKE_SOURCE_DIR}/melon_grc.xml"
COMMAND glib-compile-resources --sourcedir=${CMAKE_SOURCE_DIR}
--target=${CMAKE_CURRENT_BINARY_DIR}/melon_grc.h
--generate-header "${CMAKE_SOURCE_DIR}/melon_grc.xml")
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
target_link_libraries(melonDS dl)
endif ()
target_sources(melonDS PUBLIC melon_grc.c)
elseif (WIN32)
target_sources(melonDS PUBLIC "${CMAKE_SOURCE_DIR}/melon.rc")
target_include_directories(melonDS PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/..")
target_link_libraries(melonDS comctl32 d2d1 dwrite uxtheme ws2_32 iphlpapi gdi32)
endif ()
install(FILES ../../net.kuribo64.melonDS.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications)
install(FILES ../../icon/melon_16x16.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/16x16/apps RENAME net.kuribo64.melonDS.png)
install(FILES ../../icon/melon_32x32.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/32x32/apps RENAME net.kuribo64.melonDS.png)
install(FILES ../../icon/melon_48x48.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/48x48/apps RENAME net.kuribo64.melonDS.png)
install(FILES ../../icon/melon_64x64.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/64x64/apps RENAME net.kuribo64.melonDS.png)
install(FILES ../../icon/melon_128x128.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/128x128/apps RENAME net.kuribo64.melonDS.png)
install(FILES ../../icon/melon_256x256.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/256x256/apps RENAME net.kuribo64.melonDS.png)
install(FILES ../../romlist.bin DESTINATION ${CMAKE_INSTALL_PREFIX}/share/melonDS)
install(TARGETS melonDS RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)

View File

@ -1,197 +0,0 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "libui/ui.h"
#include "../types.h"
#include "PlatformConfig.h"
#include "DlgAudioSettings.h"
void MicLoadWav(char* path);
namespace DlgAudioSettings
{
bool opened;
uiWindow* win;
uiSlider* slVolume;
uiRadioButtons* rbMicInputType;
uiEntry* txMicWavPath;
int oldvolume;
void RevertSettings()
{
Config::AudioVolume = oldvolume;
}
int OnCloseWindow(uiWindow* window, void* blarg)
{
RevertSettings();
opened = false;
return 1;
}
void OnVolumeChanged(uiSlider* slider, void* blarg)
{
Config::AudioVolume = uiSliderValue(slVolume);
}
void OnMicWavBrowse(uiButton* btn, void* blarg)
{
char* file = uiOpenFile(win, "WAV file (*.wav)|*.wav|Any file|*.*", NULL);
if (!file)
{
return;
}
uiEntrySetText(txMicWavPath, file);
uiFreeText(file);
}
void OnCancel(uiButton* btn, void* blarg)
{
RevertSettings();
uiControlDestroy(uiControl(win));
opened = false;
}
void OnOk(uiButton* btn, void* blarg)
{
Config::AudioVolume = uiSliderValue(slVolume);
Config::MicInputType = uiRadioButtonsSelected(rbMicInputType);
char* wavpath = uiEntryText(txMicWavPath);
strncpy(Config::MicWavPath, wavpath, 511);
uiFreeText(wavpath);
Config::Save();
if (Config::MicInputType == 3) MicLoadWav(Config::MicWavPath);
uiControlDestroy(uiControl(win));
opened = false;
}
void Open()
{
if (opened)
{
uiControlSetFocus(uiControl(win));
return;
}
opened = true;
win = uiNewWindow("Audio settings - melonDS", 400, 100, 0, 0, 0);
uiWindowSetMargined(win, 1);
uiWindowOnClosing(win, OnCloseWindow, NULL);
uiBox* top = uiNewVerticalBox();
uiWindowSetChild(win, uiControl(top));
uiBoxSetPadded(top, 1);
{
uiGroup* grp = uiNewGroup("Audio output");
uiBoxAppend(top, uiControl(grp), 0);
uiGroupSetMargined(grp, 1);
uiBox* in_ctrl = uiNewVerticalBox();
uiGroupSetChild(grp, uiControl(in_ctrl));
uiLabel* label_vol = uiNewLabel("Volume:");
uiBoxAppend(in_ctrl, uiControl(label_vol), 0);
slVolume = uiNewSlider(0, 256);
uiSliderOnChanged(slVolume, OnVolumeChanged, NULL);
uiBoxAppend(in_ctrl, uiControl(slVolume), 0);
}
{
uiGroup* grp = uiNewGroup("Microphone input");
uiBoxAppend(top, uiControl(grp), 0);
uiGroupSetMargined(grp, 1);
uiBox* in_ctrl = uiNewVerticalBox();
uiGroupSetChild(grp, uiControl(in_ctrl));
rbMicInputType = uiNewRadioButtons();
uiRadioButtonsAppend(rbMicInputType, "None");
uiRadioButtonsAppend(rbMicInputType, "Microphone");
uiRadioButtonsAppend(rbMicInputType, "White noise");
uiRadioButtonsAppend(rbMicInputType, "WAV file:");
uiBoxAppend(in_ctrl, uiControl(rbMicInputType), 0);
uiBox* path_box = uiNewHorizontalBox();
uiBoxAppend(in_ctrl, uiControl(path_box), 0);
txMicWavPath = uiNewEntry();
uiBoxAppend(path_box, uiControl(txMicWavPath), 1);
uiButton* path_browse = uiNewButton("...");
uiButtonOnClicked(path_browse, OnMicWavBrowse, NULL);
uiBoxAppend(path_box, uiControl(path_browse), 0);
}
{
uiBox* in_ctrl = uiNewHorizontalBox();
uiBoxSetPadded(in_ctrl, 1);
uiBoxAppend(top, uiControl(in_ctrl), 0);
uiLabel* dummy = uiNewLabel("");
uiBoxAppend(in_ctrl, uiControl(dummy), 1);
uiButton* btncancel = uiNewButton("Cancel");
uiButtonOnClicked(btncancel, OnCancel, NULL);
uiBoxAppend(in_ctrl, uiControl(btncancel), 0);
uiButton* btnok = uiNewButton("Ok");
uiButtonOnClicked(btnok, OnOk, NULL);
uiBoxAppend(in_ctrl, uiControl(btnok), 0);
}
if (Config::AudioVolume < 0) Config::AudioVolume = 0;
else if (Config::AudioVolume > 256) Config::AudioVolume = 256;
oldvolume = Config::AudioVolume;
uiSliderSetValue(slVolume, Config::AudioVolume);
uiRadioButtonsSetSelected(rbMicInputType, Config::MicInputType);
uiEntrySetText(txMicWavPath, Config::MicWavPath);
uiControlShow(uiControl(win));
}
void Close()
{
if (!opened) return;
uiControlDestroy(uiControl(win));
opened = false;
}
}

View File

@ -1,30 +0,0 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef DLGAUDIOSETTINGS_H
#define DLGAUDIOSETTINGS_H
namespace DlgAudioSettings
{
void Open();
void Close();
}
#endif // DLGAUDIOSETTINGS_H

View File

@ -1,117 +0,0 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdlib.h>
#include <stdio.h>
#include "libui/ui.h"
#include "../types.h"
#include "PlatformConfig.h"
#include "DlgEmuSettings.h"
void ApplyNewSettings(int type);
namespace DlgEmuSettings
{
bool opened;
uiWindow* win;
uiCheckbox* cbDirectBoot;
int OnCloseWindow(uiWindow* window, void* blarg)
{
opened = false;
return 1;
}
void OnCancel(uiButton* btn, void* blarg)
{
uiControlDestroy(uiControl(win));
opened = false;
}
void OnOk(uiButton* btn, void* blarg)
{
Config::DirectBoot = uiCheckboxChecked(cbDirectBoot);
Config::Save();
uiControlDestroy(uiControl(win));
opened = false;
}
void Open()
{
if (opened)
{
uiControlSetFocus(uiControl(win));
return;
}
opened = true;
win = uiNewWindow("Emu settings - melonDS", 300, 200, 0, 0, 0);
uiWindowSetMargined(win, 1);
uiWindowOnClosing(win, OnCloseWindow, NULL);
uiBox* top = uiNewVerticalBox();
uiWindowSetChild(win, uiControl(top));
{
uiBox* in_ctrl = uiNewVerticalBox();
uiBoxAppend(top, uiControl(in_ctrl), 1);
cbDirectBoot = uiNewCheckbox("Boot game directly");
uiBoxAppend(in_ctrl, uiControl(cbDirectBoot), 0);
}
{
uiBox* in_ctrl = uiNewHorizontalBox();
uiBoxSetPadded(in_ctrl, 1);
uiBoxAppend(top, uiControl(in_ctrl), 0);
uiLabel* dummy = uiNewLabel("");
uiBoxAppend(in_ctrl, uiControl(dummy), 1);
uiButton* btncancel = uiNewButton("Cancel");
uiButtonOnClicked(btncancel, OnCancel, NULL);
uiBoxAppend(in_ctrl, uiControl(btncancel), 0);
uiButton* btnok = uiNewButton("Ok");
uiButtonOnClicked(btnok, OnOk, NULL);
uiBoxAppend(in_ctrl, uiControl(btnok), 0);
}
uiCheckboxSetChecked(cbDirectBoot, Config::DirectBoot);
uiControlShow(uiControl(win));
}
void Close()
{
if (!opened) return;
uiControlDestroy(uiControl(win));
opened = false;
}
}

View File

@ -1,30 +0,0 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef DLGEMUSETTINGS_H
#define DLGEMUSETTINGS_H
namespace DlgEmuSettings
{
void Open();
void Close();
}
#endif // DLGEMUSETTINGS_H

View File

@ -1,663 +0,0 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <SDL2/SDL.h>
#include "libui/ui.h"
#include "../types.h"
#include "PlatformConfig.h"
#include "DlgInputConfig.h"
extern int JoystickID;
extern SDL_Joystick* Joystick;
extern void OpenJoystick();
namespace DlgInputConfig
{
typedef struct
{
int type;
uiWindow* win;
uiAreaHandler areahandler;
uiArea* keypresscatcher;
int numkeys;
int keymap[32];
int joymap[32];
int pollid;
uiButton* pollbtn;
SDL_TimerID timer;
int axes_rest[16];
} InputDlgData;
int dskeyorder[12] = {0, 1, 10, 11, 5, 4, 6, 7, 9, 8, 3, 2};
char dskeylabels[12][8] = {"A:", "B:", "Select:", "Start:", "Right:", "Left:", "Up:", "Down:", "R:", "L:", "X:", "Y:"};
int identity[32] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
char hotkeylabels[HK_MAX][32] =
{
"Close/open lid:",
"Microphone:",
"Pause/resume:",
"Reset:",
"Fast forward:",
"Fast forward (toggle):",
"Decrease sunlight (Boktai):",
"Increase sunlight (Boktai):"
};
int openedmask;
InputDlgData inputdlg[2];
void KeyMappingName(int id, char* str)
{
if (id < 0)
{
strcpy(str, "None");
return;
}
int key = id & 0xFFFF;
char* keyname = uiKeyName(key);
strncpy(str, keyname, 63); str[63] = '\0';
uiFreeText(keyname);
int mod = id >> 16;
if (key == 0x11D) mod = 0;
else if (key == 0x138) mod = 0;
else if (key == 0x036) mod = 0;
if (mod != 0)
{
// CTRL / ALT / SHIFT
const int modscan[] = {0x1D, 0x38, 0x2A};
char tmp[64];
for (int m = 2; m >= 0; m--)
{
if (!(mod & (1<<m))) continue;
char* modname = uiKeyName(modscan[m]);
memcpy(tmp, str, 64);
snprintf(str, 64, "%s+%s", modname, tmp);
uiFreeText(modname);
}
}
str[63] = '\0';
}
void JoyMappingName(int id, char* str)
{
if (id < 0)
{
strcpy(str, "None");
return;
}
bool hasbtn = ((id & 0xFFFF) != 0xFFFF);
if (hasbtn)
{
if (id & 0x100)
{
int hatnum = ((id >> 4) & 0xF) + 1;
switch (id & 0xF)
{
case 0x1: sprintf(str, "Hat %d up", hatnum); break;
case 0x2: sprintf(str, "Hat %d right", hatnum); break;
case 0x4: sprintf(str, "Hat %d down", hatnum); break;
case 0x8: sprintf(str, "Hat %d left", hatnum); break;
}
}
else
{
sprintf(str, "Button %d", (id & 0xFFFF) + 1);
}
}
else
{
strcpy(str, "");
}
if (id & 0x10000)
{
int axisnum = ((id >> 24) & 0xF) + 1;
char tmp[64];
memcpy(tmp, str, 64);
switch ((id >> 20) & 0xF)
{
case 0: sprintf(str, "%s%sAxis %d +", tmp, hasbtn?" / ":"", axisnum); break;
case 1: sprintf(str, "%s%sAxis %d -", tmp, hasbtn?" / ":"", axisnum); break;
case 2: sprintf(str, "%s%sTrigger %d", tmp, hasbtn?" / ":"", axisnum); break;
}
}
}
void OnAreaDraw(uiAreaHandler* handler, uiArea* area, uiAreaDrawParams* params)
{
}
void OnAreaMouseEvent(uiAreaHandler* handler, uiArea* area, uiAreaMouseEvent* evt)
{
}
void OnAreaMouseCrossed(uiAreaHandler* handler, uiArea* area, int left)
{
}
void OnAreaDragBroken(uiAreaHandler* handler, uiArea* area)
{
}
void OnAreaResize(uiAreaHandler* handler, uiArea* area, int width, int height)
{
}
int OnAreaKeyEvent(uiAreaHandler* handler, uiArea* area, uiAreaKeyEvent* evt)
{
InputDlgData* dlg = (InputDlgData*)uiControl(area)->UserData;
if (dlg->pollid < 0)
return 0;
if (evt->Scancode == 0x1D) // CTRL
return 1;
if (evt->Scancode == 0x38) // ALT
return 1;
if (evt->Scancode == 0x2A) // SHIFT
return 1;
if (dlg->pollid > 12)
{
if (dlg->pollid < 0x100) return 0;
int id = dlg->pollid & 0xFF;
if (id > 12) return 0;
if (evt->Scancode != 0x1 || evt->Modifiers != 0) // ESC
{
if (evt->Scancode == 0xE && evt->Modifiers == 0) // backspace
dlg->joymap[id] = -1;
else
return 1;
}
char keyname[64];
JoyMappingName(dlg->joymap[id], keyname);
uiButtonSetText(dlg->pollbtn, keyname);
uiControlEnable(uiControl(dlg->pollbtn));
dlg->pollid = -1;
uiControlSetFocus(uiControl(dlg->pollbtn));
return 1;
}
if (!evt->Up)
{
// set key.
if (evt->Scancode != 0x1 || evt->Modifiers != 0) // ESC
{
int mod = (dlg->type == 0) ? 0 : evt->Modifiers;
if (evt->Scancode == 0xE && evt->Modifiers == 0) // backspace
dlg->keymap[dlg->pollid] = -1;
else
dlg->keymap[dlg->pollid] = evt->Scancode | (mod << 16);
}
char keyname[64];
KeyMappingName(dlg->keymap[dlg->pollid], keyname);
uiButtonSetText(dlg->pollbtn, keyname);
uiControlEnable(uiControl(dlg->pollbtn));
dlg->pollid = -1;
uiControlSetFocus(uiControl(dlg->pollbtn));
}
return 1;
}
void FinishJoyMapping(void* param)
{
InputDlgData* dlg = (InputDlgData*)param;
int id = dlg->pollid & 0xFF;
char keyname[64];
JoyMappingName(dlg->joymap[id], keyname);
uiButtonSetText(dlg->pollbtn, keyname);
uiControlEnable(uiControl(dlg->pollbtn));
dlg->pollid = -1;
uiControlSetFocus(uiControl(dlg->pollbtn));
}
Uint32 JoyPoll(Uint32 interval, void* param)
{
InputDlgData* dlg = (InputDlgData*)param;
if (dlg->pollid < 0x100)
{
dlg->timer = 0;
return 0;
}
int id = dlg->pollid & 0xFF;
if (id > 12)
{
dlg->timer = 0;
return 0;
}
SDL_Joystick* joy = Joystick;
if (!joy)
{
dlg->timer = 0;
return 0;
}
if (!SDL_JoystickGetAttached(joy))
{
dlg->timer = 0;
return 0;
}
int oldmap;
if (dlg->joymap[id] == -1) oldmap = 0xFFFF;
else oldmap = dlg->joymap[id];
int nbuttons = SDL_JoystickNumButtons(joy);
for (int i = 0; i < nbuttons; i++)
{
if (SDL_JoystickGetButton(joy, i))
{
dlg->joymap[id] = (oldmap & 0xFFFF0000) | i;
uiQueueMain(FinishJoyMapping, dlg);
dlg->timer = 0;
return 0;
}
}
int nhats = SDL_JoystickNumHats(joy);
if (nhats > 16) nhats = 16;
for (int i = 0; i < nhats; i++)
{
Uint8 blackhat = SDL_JoystickGetHat(joy, i);
if (blackhat)
{
if (blackhat & 0x1) blackhat = 0x1;
else if (blackhat & 0x2) blackhat = 0x2;
else if (blackhat & 0x4) blackhat = 0x4;
else blackhat = 0x8;
dlg->joymap[id] = (oldmap & 0xFFFF0000) | 0x100 | blackhat | (i << 4);
uiQueueMain(FinishJoyMapping, dlg);
dlg->timer = 0;
return 0;
}
}
int naxes = SDL_JoystickNumAxes(joy);
if (naxes > 16) naxes = 16;
for (int i = 0; i < naxes; i++)
{
Sint16 axisval = SDL_JoystickGetAxis(joy, i);
int diff = abs(axisval - dlg->axes_rest[i]);
if (dlg->axes_rest[i] < -16384 && axisval >= 0)
{
dlg->joymap[id] = (oldmap & 0xFFFF) | 0x10000 | (2 << 20) | (i << 24);
uiQueueMain(FinishJoyMapping, dlg);
dlg->timer = 0;
return 0;
}
else if (diff > 16384)
{
int axistype;
if (axisval > 0) axistype = 0;
else axistype = 1;
dlg->joymap[id] = (oldmap & 0xFFFF) | 0x10000 | (axistype << 20) | (i << 24);
uiQueueMain(FinishJoyMapping, dlg);
dlg->timer = 0;
return 0;
}
}
return interval;
}
void OnKeyStartConfig(uiButton* btn, void* data)
{
InputDlgData* dlg = (InputDlgData*)uiControl(btn)->UserData;
if (dlg->pollid != -1)
{
// TODO: handle this better?
if (dlg->pollid <= 12)
uiControlSetFocus(uiControl(dlg->keypresscatcher));
return;
}
int id = *(int*)data;
dlg->pollid = id;
dlg->pollbtn = btn;
uiButtonSetText(btn, "[press key]");
uiControlDisable(uiControl(btn));
uiControlSetFocus(uiControl(dlg->keypresscatcher));
}
void OnJoyStartConfig(uiButton* btn, void* data)
{
InputDlgData* dlg = (InputDlgData*)uiControl(btn)->UserData;
if (dlg->pollid != -1)
{
// TODO: handle this better?
if (dlg->pollid <= 12)
uiControlSetFocus(uiControl(dlg->keypresscatcher));
return;
}
int naxes = SDL_JoystickNumAxes(Joystick);
if (naxes > 16) naxes = 16;
for (int a = 0; a < naxes; a++)
{
dlg->axes_rest[a] = SDL_JoystickGetAxis(Joystick, a);
}
int id = *(int*)data;
dlg->pollid = id | 0x100;
dlg->pollbtn = btn;
uiButtonSetText(btn, "[press button / axis]");
uiControlDisable(uiControl(btn));
dlg->timer = SDL_AddTimer(100, JoyPoll, dlg);
uiControlSetFocus(uiControl(dlg->keypresscatcher));
}
void OnJoystickChanged(uiCombobox* cb, void* data)
{
JoystickID = uiComboboxSelected(cb);
OpenJoystick();
}
int OnCloseWindow(uiWindow* window, void* blarg)
{
InputDlgData* dlg = (InputDlgData*)(uiControl(window)->UserData);
openedmask &= ~(1 << dlg->type);
if (dlg->timer) SDL_RemoveTimer(dlg->timer);
JoystickID = Config::JoystickID;
OpenJoystick();
return 1;
}
void OnGetFocus(uiWindow* window, void* blarg)
{
InputDlgData* dlg = (InputDlgData*)(uiControl(window)->UserData);
if (dlg->pollid >= 0)
uiControlSetFocus(uiControl(dlg->keypresscatcher));
}
void OnLoseFocus(uiWindow* window, void* blarg)
{
}
void OnCancel(uiButton* btn, void* data)
{
InputDlgData* dlg = (InputDlgData*)data;
uiControlDestroy(uiControl(dlg->win));
openedmask &= ~(1 << dlg->type);
if (dlg->timer) SDL_RemoveTimer(dlg->timer);
JoystickID = Config::JoystickID;
OpenJoystick();
}
void OnOk(uiButton* btn, void* data)
{
InputDlgData* dlg = (InputDlgData*)data;
if (dlg->type == 0)
{
memcpy(Config::KeyMapping, dlg->keymap, sizeof(int)*12);
memcpy(Config::JoyMapping, dlg->joymap, sizeof(int)*12);
}
else if (dlg->type == 1)
{
memcpy(Config::HKKeyMapping, dlg->keymap, sizeof(int)*HK_MAX);
memcpy(Config::HKJoyMapping, dlg->joymap, sizeof(int)*HK_MAX);
}
Config::JoystickID = JoystickID;
Config::Save();
uiControlDestroy(uiControl(dlg->win));
openedmask &= ~(1 << dlg->type);
if (dlg->timer) SDL_RemoveTimer(dlg->timer);
}
void Open(int type)
{
InputDlgData* dlg = &inputdlg[type];
int mask = 1 << type;
if (openedmask & mask)
{
uiControlSetFocus(uiControl(dlg->win));
return;
}
openedmask |= mask;
dlg->type = type;
dlg->pollid = -1;
dlg->timer = 0;
if (type == 0)
{
dlg->numkeys = 12;
memcpy(dlg->keymap, Config::KeyMapping, sizeof(int)*12);
memcpy(dlg->joymap, Config::JoyMapping, sizeof(int)*12);
dlg->win = uiNewWindow("Input config - melonDS", 600, 100, 0, 0, 0);
}
else if (type == 1)
{
dlg->numkeys = HK_MAX;
memcpy(dlg->keymap, Config::HKKeyMapping, sizeof(int)*HK_MAX);
memcpy(dlg->joymap, Config::HKJoyMapping, sizeof(int)*HK_MAX);
dlg->win = uiNewWindow("Hotkey config - melonDS", 700, 100, 0, 0, 0);
}
uiControl(dlg->win)->UserData = dlg;
uiWindowSetMargined(dlg->win, 1);
uiWindowOnClosing(dlg->win, OnCloseWindow, NULL);
uiWindowOnGetFocus(dlg->win, OnGetFocus, NULL);
uiWindowOnLoseFocus(dlg->win, OnLoseFocus, NULL);
dlg->areahandler.Draw = OnAreaDraw;
dlg->areahandler.MouseEvent = OnAreaMouseEvent;
dlg->areahandler.MouseCrossed = OnAreaMouseCrossed;
dlg->areahandler.DragBroken = OnAreaDragBroken;
dlg->areahandler.KeyEvent = OnAreaKeyEvent;
dlg->areahandler.Resize = OnAreaResize;
uiBox* top = uiNewVerticalBox();
uiWindowSetChild(dlg->win, uiControl(top));
uiControlHide(uiControl(top));
{
uiBox* in_ctrl = uiNewHorizontalBox();
uiBoxAppend(top, uiControl(in_ctrl), 0);
uiBoxSetPadded(in_ctrl, 1);
uiGroup* g_key = uiNewGroup("Keyboard");
uiBoxAppend(in_ctrl, uiControl(g_key), 1);
uiGrid* b_key = uiNewGrid();
uiGroupSetChild(g_key, uiControl(b_key));
const int width = 240;
for (int i = 0; i < dlg->numkeys; i++)
{
int j = (type==0) ? dskeyorder[i] : i;
uiLabel* label = uiNewLabel((type==0) ? dskeylabels[j] : hotkeylabels[j]);
uiGridAppend(b_key, uiControl(label), 0, i, 1, 1, 1, uiAlignStart, 1, uiAlignCenter);
uiControlSetMinSize(uiControl(label), width, 1);
char keyname[64];
KeyMappingName(dlg->keymap[j], keyname);
uiButton* btn = uiNewButton(keyname);
uiControl(btn)->UserData = dlg;
uiGridAppend(b_key, uiControl(btn), 1, i, 1, 1, 1, uiAlignFill, 1, uiAlignCenter);
uiButtonOnClicked(btn, OnKeyStartConfig, (type==0) ? &dskeyorder[i] : &identity[i]);
uiControlSetMinSize(uiControl(btn), width, 1);
}
uiGroup* g_joy = uiNewGroup("Joystick");
uiBoxAppend(in_ctrl, uiControl(g_joy), 1);
uiGrid* b_joy = uiNewGrid();
uiGroupSetChild(g_joy, uiControl(b_joy));
for (int i = 0; i < dlg->numkeys; i++)
{
int j = (type==0) ? dskeyorder[i] : i;
uiLabel* label = uiNewLabel((type==0) ? dskeylabels[j] : hotkeylabels[j]);
uiGridAppend(b_joy, uiControl(label), 0, i, 1, 1, 1, uiAlignStart, 1, uiAlignCenter);
uiControlSetMinSize(uiControl(label), width, 1);
char keyname[64];
JoyMappingName(dlg->joymap[j], keyname);
uiButton* btn = uiNewButton(keyname);
uiControl(btn)->UserData = dlg;
uiGridAppend(b_joy, uiControl(btn), 1, i, 1, 1, 1, uiAlignFill, 1, uiAlignCenter);
uiButtonOnClicked(btn, OnJoyStartConfig, (type==0) ? &dskeyorder[i] : &identity[i]);
uiControlSetMinSize(uiControl(btn), width, 1);
}
if (type == 0)
{
uiLabel* dummy = uiNewLabel(" ");
uiGridAppend(b_key, uiControl(dummy), 0, dlg->numkeys, 2, 1, 1, uiAlignFill, 1, uiAlignCenter);
uiCombobox* joycombo = uiNewCombobox();
uiGridAppend(b_joy, uiControl(joycombo), 0, dlg->numkeys, 2, 1, 1, uiAlignFill, 1, uiAlignCenter);
int numjoys = SDL_NumJoysticks();
if (numjoys < 1)
{
uiComboboxAppend(joycombo, "(no joysticks available)");
uiControlDisable(uiControl(joycombo));
}
else
{
for (int i = 0; i < numjoys; i++)
{
const char* joyname = SDL_JoystickNameForIndex(i);
char fullname[256];
snprintf(fullname, 256, "%d. %s", i+1, joyname);
uiComboboxAppend(joycombo, fullname);
}
uiComboboxSetSelected(joycombo, JoystickID);
uiComboboxOnSelected(joycombo, OnJoystickChanged, NULL);
}
}
}
uiLabel* filler = uiNewLabel("");
uiBoxAppend(top, uiControl(filler), 1);
{
uiBox* in_ctrl = uiNewHorizontalBox();
uiBoxSetPadded(in_ctrl, 1);
uiBoxAppend(top, uiControl(in_ctrl), 0);
uiLabel* dummy = uiNewLabel("");
uiBoxAppend(in_ctrl, uiControl(dummy), 1);
dlg->keypresscatcher = uiNewArea(&dlg->areahandler);
uiControl(dlg->keypresscatcher)->UserData = dlg;
uiBoxAppend(in_ctrl, uiControl(dlg->keypresscatcher), 0);
uiButton* btncancel = uiNewButton("Cancel");
uiButtonOnClicked(btncancel, OnCancel, dlg);
uiBoxAppend(in_ctrl, uiControl(btncancel), 0);
uiButton* btnok = uiNewButton("Ok");
uiButtonOnClicked(btnok, OnOk, dlg);
uiBoxAppend(in_ctrl, uiControl(btnok), 0);
}
uiControlShow(uiControl(top));
uiControlShow(uiControl(dlg->win));
}
void Close(int type)
{
if (openedmask & (1<<type))
uiControlDestroy(uiControl(inputdlg[type].win));
openedmask &= ~(1<<type);
if (inputdlg[type].timer) SDL_RemoveTimer(inputdlg[type].timer);
}
}

View File

@ -1,31 +0,0 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef DLGINPUTCONFIG_H
#define DLGINPUTCONFIG_H
namespace DlgInputConfig
{
void Open(int type);
void Close(int type);
}
#endif // DLGINPUTCONFIG_H

View File

@ -1,354 +0,0 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "libui/ui.h"
#include "../types.h"
#include "PlatformConfig.h"
#include "DlgVideoSettings.h"
void ApplyNewSettings(int type);
namespace DlgVideoSettings
{
bool opened;
uiWindow* win;
uiRadioButtons* rbRenderer;
uiCheckbox* cbGLDisplay;
uiCheckbox* cbVSync;
uiCheckbox* cbThreaded3D;
uiCombobox* cbResolution;
uiCheckbox* cbAntialias;
int old_renderer;
int old_gldisplay;
int old_vsync;
int old_threaded3D;
int old_resolution;
int old_antialias;
void UpdateControls()
{
int renderer = uiRadioButtonsSelected(rbRenderer);
if (renderer == 0)
{
uiControlEnable(uiControl(cbGLDisplay));
uiControlEnable(uiControl(cbThreaded3D));
uiControlDisable(uiControl(cbResolution));
//uiControlDisable(uiControl(cbAntialias));
}
else
{
uiControlDisable(uiControl(cbGLDisplay));
uiControlDisable(uiControl(cbThreaded3D));
uiControlEnable(uiControl(cbResolution));
//uiControlEnable(uiControl(cbAntialias));
}
}
void RevertSettings()
{
bool apply0 = false;
bool apply2 = false;
bool apply3 = false;
bool old_usegl = (old_gldisplay != 0) || (old_renderer != 0);
bool new_usegl = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0);
if (old_renderer != Config::_3DRenderer)
{
Config::_3DRenderer = old_renderer;
apply3 = true;
}
if (old_gldisplay != Config::ScreenUseGL)
{
Config::ScreenUseGL = old_gldisplay;
}
if (old_vsync != Config::ScreenVSync)
{
Config::ScreenVSync = old_vsync;
//ApplyNewSettings(4);
}
if (old_usegl != new_usegl)
{
apply2 = true;
}
if (old_threaded3D != Config::Threaded3D)
{
Config::Threaded3D = old_threaded3D;
apply0 = true;
}
if (old_resolution != Config::GL_ScaleFactor ||
old_antialias != Config::GL_Antialias)
{
Config::GL_ScaleFactor = old_resolution;
Config::GL_Antialias = old_antialias;
apply0 = true;
}
if (apply2) ApplyNewSettings(2);
else if (apply3) ApplyNewSettings(3);
if (apply0) ApplyNewSettings(0);
}
int OnCloseWindow(uiWindow* window, void* blarg)
{
RevertSettings();
opened = false;
return 1;
}
void OnRendererChanged(uiRadioButtons* rb, void* blarg)
{
int id = uiRadioButtonsSelected(rb);
bool old_usegl = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0);
Config::_3DRenderer = id;
UpdateControls();
bool new_usegl = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0);
if (new_usegl) uiControlEnable(uiControl(cbVSync));
else uiControlDisable(uiControl(cbVSync));
if (new_usegl != old_usegl)
ApplyNewSettings(2);
else
ApplyNewSettings(3);
uiControlSetFocus(uiControl(win));
}
void OnGLDisplayChanged(uiCheckbox* cb, void* blarg)
{
Config::ScreenUseGL = uiCheckboxChecked(cb);
if (Config::ScreenUseGL) uiControlEnable(uiControl(cbVSync));
else uiControlDisable(uiControl(cbVSync));
ApplyNewSettings(2);
uiControlSetFocus(uiControl(win));
}
void OnVSyncChanged(uiCheckbox* cb, void* blarg)
{
Config::ScreenVSync = uiCheckboxChecked(cb);
//ApplyNewSettings(4);
}
void OnThreaded3DChanged(uiCheckbox* cb, void* blarg)
{
Config::Threaded3D = uiCheckboxChecked(cb);
ApplyNewSettings(0);
}
void OnResolutionChanged(uiCombobox* cb, void* blarg)
{
int id = uiComboboxSelected(cb);
Config::GL_ScaleFactor = id+1;
ApplyNewSettings(0);
}
void OnAntialiasChanged(uiCheckbox* cb, void* blarg)
{
Config::GL_Antialias = uiCheckboxChecked(cb);
ApplyNewSettings(0);
}
void OnCancel(uiButton* btn, void* blarg)
{
RevertSettings();
uiControlDestroy(uiControl(win));
opened = false;
}
void OnOk(uiButton* btn, void* blarg)
{
Config::Save();
uiControlDestroy(uiControl(win));
opened = false;
}
void Open()
{
if (opened)
{
uiControlSetFocus(uiControl(win));
return;
}
opened = true;
win = uiNewWindow("Video settings - melonDS", 400, 100, 0, 0, 0);
uiWindowSetMargined(win, 1);
uiWindowOnClosing(win, OnCloseWindow, NULL);
uiBox* top = uiNewVerticalBox();
uiWindowSetChild(win, uiControl(top));
uiBoxSetPadded(top, 1);
uiBox* splitter = uiNewHorizontalBox();
uiBoxAppend(top, uiControl(splitter), 0);
uiBoxSetPadded(splitter, 1);
uiBox* left = uiNewVerticalBox();
uiBoxAppend(splitter, uiControl(left), 1);
uiBoxSetPadded(left, 1);
uiBox* right = uiNewVerticalBox();
uiBoxAppend(splitter, uiControl(right), 1);
uiBoxSetPadded(right, 1);
{
uiGroup* grp = uiNewGroup("Display settings");
uiBoxAppend(left, uiControl(grp), 0);
uiGroupSetMargined(grp, 1);
uiBox* in_ctrl = uiNewVerticalBox();
uiGroupSetChild(grp, uiControl(in_ctrl));
uiLabel* lbl = uiNewLabel("3D renderer:");
uiBoxAppend(in_ctrl, uiControl(lbl), 0);
rbRenderer = uiNewRadioButtons();
uiRadioButtonsAppend(rbRenderer, "Software");
uiRadioButtonsAppend(rbRenderer, "OpenGL");
uiRadioButtonsOnSelected(rbRenderer, OnRendererChanged, NULL);
uiBoxAppend(in_ctrl, uiControl(rbRenderer), 0);
lbl = uiNewLabel("");
uiBoxAppend(in_ctrl, uiControl(lbl), 0);
cbGLDisplay = uiNewCheckbox("OpenGL display");
uiCheckboxOnToggled(cbGLDisplay, OnGLDisplayChanged, NULL);
uiBoxAppend(in_ctrl, uiControl(cbGLDisplay), 0);
cbVSync = uiNewCheckbox("VSync");
uiCheckboxOnToggled(cbVSync, OnVSyncChanged, NULL);
uiBoxAppend(in_ctrl, uiControl(cbVSync), 0);
}
{
uiGroup* grp = uiNewGroup("Software renderer");
uiBoxAppend(right, uiControl(grp), 0);
uiGroupSetMargined(grp, 1);
uiBox* in_ctrl = uiNewVerticalBox();
uiGroupSetChild(grp, uiControl(in_ctrl));
cbThreaded3D = uiNewCheckbox("Threaded");
uiCheckboxOnToggled(cbThreaded3D, OnThreaded3DChanged, NULL);
uiBoxAppend(in_ctrl, uiControl(cbThreaded3D), 0);
}
{
uiGroup* grp = uiNewGroup("OpenGL renderer");
uiBoxAppend(right, uiControl(grp), 0);
uiGroupSetMargined(grp, 1);
uiBox* in_ctrl = uiNewVerticalBox();
uiGroupSetChild(grp, uiControl(in_ctrl));
uiLabel* lbl = uiNewLabel("Internal resolution:");
uiBoxAppend(in_ctrl, uiControl(lbl), 0);
cbResolution = uiNewCombobox();
uiComboboxOnSelected(cbResolution, OnResolutionChanged, NULL);
for (int i = 1; i <= 8; i++)
{
char txt[64];
sprintf(txt, "%dx native (%dx%d)", i, 256*i, 192*i);
uiComboboxAppend(cbResolution, txt);
}
uiBoxAppend(in_ctrl, uiControl(cbResolution), 0);
//lbl = uiNewLabel("");
//uiBoxAppend(in_ctrl, uiControl(lbl), 0);
//cbAntialias = uiNewCheckbox("Antialiasing");
//uiCheckboxOnToggled(cbAntialias, OnAntialiasChanged, NULL);
//uiBoxAppend(in_ctrl, uiControl(cbAntialias), 0);
}
{
uiBox* in_ctrl = uiNewHorizontalBox();
uiBoxSetPadded(in_ctrl, 1);
uiBoxAppend(top, uiControl(in_ctrl), 0);
uiLabel* dummy = uiNewLabel("");
uiBoxAppend(in_ctrl, uiControl(dummy), 1);
uiButton* btncancel = uiNewButton("Cancel");
uiButtonOnClicked(btncancel, OnCancel, NULL);
uiBoxAppend(in_ctrl, uiControl(btncancel), 0);
uiButton* btnok = uiNewButton("Ok");
uiButtonOnClicked(btnok, OnOk, NULL);
uiBoxAppend(in_ctrl, uiControl(btnok), 0);
}
Config::_3DRenderer = Config::_3DRenderer ? 1 : 0;
if (Config::GL_ScaleFactor < 1) Config::GL_ScaleFactor = 1;
else if (Config::GL_ScaleFactor > 8) Config::GL_ScaleFactor = 8;
old_renderer = Config::_3DRenderer;
old_gldisplay = Config::ScreenUseGL;
old_vsync = Config::ScreenVSync;
old_threaded3D = Config::Threaded3D;
old_resolution = Config::GL_ScaleFactor;
old_antialias = Config::GL_Antialias;
uiCheckboxSetChecked(cbGLDisplay, Config::ScreenUseGL);
uiCheckboxSetChecked(cbVSync, Config::ScreenVSync);
uiCheckboxSetChecked(cbThreaded3D, Config::Threaded3D);
uiComboboxSetSelected(cbResolution, Config::GL_ScaleFactor-1);
//uiCheckboxSetChecked(cbAntialias, Config::GL_Antialias);
uiRadioButtonsSetSelected(rbRenderer, Config::_3DRenderer);
UpdateControls();
if (Config::ScreenUseGL || Config::_3DRenderer != 0)
uiControlEnable(uiControl(cbVSync));
else
uiControlDisable(uiControl(cbVSync));
uiControlShow(uiControl(win));
}
void Close()
{
if (!opened) return;
uiControlDestroy(uiControl(win));
opened = false;
}
}

View File

@ -1,30 +0,0 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef DLGVIDEOSETTINGS_H
#define DLGVIDEOSETTINGS_H
namespace DlgVideoSettings
{
void Open();
void Close();
}
#endif // DLGVIDEOSETTINGS_H

View File

@ -1,271 +0,0 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "libui/ui.h"
#include "../types.h"
#include "PlatformConfig.h"
#include "LAN_Socket.h"
#include "LAN_PCap.h"
#include "DlgWifiSettings.h"
#ifdef __WIN32__
#define PCAP_NAME "winpcap/npcap"
#else
#define PCAP_NAME "libpcap"
#endif // __WIN32__
void ApplyNewSettings(int type);
namespace DlgWifiSettings
{
bool opened;
uiWindow* win;
bool haspcap;
uiCheckbox* cbBindAnyAddr;
uiLabel* lbAdapterList;
uiCombobox* cmAdapterList;
uiCheckbox* cbDirectLAN;
uiLabel* lbAdapterMAC;
uiLabel* lbAdapterIP;
uiLabel* lbAdapterDNS0;
uiLabel* lbAdapterDNS1;
void UpdateAdapterControls()
{
bool enable = haspcap && uiCheckboxChecked(cbDirectLAN);
if (enable)
{
uiControlEnable(uiControl(lbAdapterList));
uiControlEnable(uiControl(cmAdapterList));
uiControlEnable(uiControl(lbAdapterMAC));
uiControlEnable(uiControl(lbAdapterIP));
}
else
{
uiControlDisable(uiControl(lbAdapterList));
uiControlDisable(uiControl(cmAdapterList));
uiControlDisable(uiControl(lbAdapterMAC));
uiControlDisable(uiControl(lbAdapterIP));
}
}
void UpdateAdapterInfo()
{
if (!haspcap) return;
int sel = uiComboboxSelected(cmAdapterList);
if (sel < 0 || sel >= LAN_PCap::NumAdapters) return;
if (LAN_PCap::NumAdapters < 1) return;
LAN_PCap::AdapterData* adapter = &LAN_PCap::Adapters[sel];
char tmp[64];
sprintf(tmp, "MAC: %02X:%02X:%02X:%02X:%02X:%02X",
adapter->MAC[0], adapter->MAC[1], adapter->MAC[2],
adapter->MAC[3], adapter->MAC[4], adapter->MAC[5]);
uiLabelSetText(lbAdapterMAC, tmp);
sprintf(tmp, "IP: %d.%d.%d.%d",
adapter->IP_v4[0], adapter->IP_v4[1],
adapter->IP_v4[2], adapter->IP_v4[3]);
uiLabelSetText(lbAdapterIP, tmp);
/*sprintf(tmp, "Primary DNS: %d.%d.%d.%d",
adapter->DNS[0][0], adapter->DNS[0][1],
adapter->DNS[0][2], adapter->DNS[0][3]);
uiLabelSetText(lbAdapterDNS0, tmp);
sprintf(tmp, "Secondary DNS: %d.%d.%d.%d",
adapter->DNS[1][0], adapter->DNS[1][1],
adapter->DNS[1][2], adapter->DNS[1][3]);
uiLabelSetText(lbAdapterDNS1, tmp);*/
}
int OnCloseWindow(uiWindow* window, void* blarg)
{
opened = false;
return 1;
}
void OnDirectModeToggle(uiCheckbox* c, void* blarg)
{
UpdateAdapterControls();
}
void OnAdapterSelect(uiCombobox* c, void* blarg)
{
UpdateAdapterInfo();
}
void OnCancel(uiButton* btn, void* blarg)
{
uiControlDestroy(uiControl(win));
opened = false;
}
void OnOk(uiButton* btn, void* blarg)
{
Config::SocketBindAnyAddr = uiCheckboxChecked(cbBindAnyAddr);
Config::DirectLAN = uiCheckboxChecked(cbDirectLAN);
int sel = uiComboboxSelected(cmAdapterList);
if (sel < 0 || sel >= LAN_PCap::NumAdapters) sel = 0;
if (LAN_PCap::NumAdapters < 1)
{
Config::LANDevice[0] = '\0';
}
else
{
strncpy(Config::LANDevice, LAN_PCap::Adapters[sel].DeviceName, 127);
Config::LANDevice[127] = '\0';
}
Config::Save();
uiControlDestroy(uiControl(win));
opened = false;
ApplyNewSettings(1);
}
void Open()
{
if (opened)
{
uiControlSetFocus(uiControl(win));
return;
}
LAN_Socket::Init();
haspcap = LAN_PCap::Init(false);
opened = true;
win = uiNewWindow("Wifi settings - melonDS", 400, 100, 0, 0, 0);
uiWindowSetMargined(win, 1);
uiWindowOnClosing(win, OnCloseWindow, NULL);
uiBox* top = uiNewVerticalBox();
uiWindowSetChild(win, uiControl(top));
uiBoxSetPadded(top, 1);
{
uiGroup* grp = uiNewGroup("Local");
uiBoxAppend(top, uiControl(grp), 0);
uiGroupSetMargined(grp, 1);
uiBox* in_ctrl = uiNewVerticalBox();
uiGroupSetChild(grp, uiControl(in_ctrl));
cbBindAnyAddr = uiNewCheckbox("Bind socket to any address");
uiBoxAppend(in_ctrl, uiControl(cbBindAnyAddr), 0);
}
{
uiLabel* lbl;
uiGroup* grp = uiNewGroup("Online");
uiBoxAppend(top, uiControl(grp), 0);
uiGroupSetMargined(grp, 1);
uiBox* in_ctrl = uiNewVerticalBox();
uiGroupSetChild(grp, uiControl(in_ctrl));
cbDirectLAN = uiNewCheckbox("Direct mode (requires " PCAP_NAME " and ethernet connection)");
uiCheckboxOnToggled(cbDirectLAN, OnDirectModeToggle, NULL);
uiBoxAppend(in_ctrl, uiControl(cbDirectLAN), 0);
lbAdapterList = uiNewLabel("Network adapter:");
uiBoxAppend(in_ctrl, uiControl(lbAdapterList), 0);
cmAdapterList = uiNewCombobox();
uiComboboxOnSelected(cmAdapterList, OnAdapterSelect, NULL);
uiBoxAppend(in_ctrl, uiControl(cmAdapterList), 0);
lbAdapterMAC = uiNewLabel("MAC: ??");
uiBoxAppend(in_ctrl, uiControl(lbAdapterMAC), 0);
lbAdapterIP = uiNewLabel("IP: ??");
uiBoxAppend(in_ctrl, uiControl(lbAdapterIP), 0);
/*lbAdapterDNS0 = uiNewLabel("DNS0");
uiBoxAppend(in_ctrl, uiControl(lbAdapterDNS0), 0);
lbAdapterDNS1 = uiNewLabel("DNS1");
uiBoxAppend(in_ctrl, uiControl(lbAdapterDNS1), 0);*/
}
{
uiBox* in_ctrl = uiNewHorizontalBox();
uiBoxSetPadded(in_ctrl, 1);
uiBoxAppend(top, uiControl(in_ctrl), 0);
uiLabel* dummy = uiNewLabel("");
uiBoxAppend(in_ctrl, uiControl(dummy), 1);
uiButton* btncancel = uiNewButton("Cancel");
uiButtonOnClicked(btncancel, OnCancel, NULL);
uiBoxAppend(in_ctrl, uiControl(btncancel), 0);
uiButton* btnok = uiNewButton("Ok");
uiButtonOnClicked(btnok, OnOk, NULL);
uiBoxAppend(in_ctrl, uiControl(btnok), 0);
}
uiCheckboxSetChecked(cbBindAnyAddr, Config::SocketBindAnyAddr);
int sel = 0;
for (int i = 0; i < LAN_PCap::NumAdapters; i++)
{
LAN_PCap::AdapterData* adapter = &LAN_PCap::Adapters[i];
uiComboboxAppend(cmAdapterList, adapter->FriendlyName);
if (!strncmp(adapter->DeviceName, Config::LANDevice, 128))
sel = i;
}
uiComboboxSetSelected(cmAdapterList, sel);
UpdateAdapterInfo();
uiCheckboxSetChecked(cbDirectLAN, Config::DirectLAN);
if (!haspcap) uiControlDisable(uiControl(cbDirectLAN));
UpdateAdapterControls();
uiControlShow(uiControl(win));
}
void Close()
{
if (!opened) return;
uiControlDestroy(uiControl(win));
opened = false;
}
}

View File

@ -1,31 +0,0 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef DLGWIFISETTINGS_H
#define DLGWIFISETTINGS_H
namespace DlgWifiSettings
{
void Open();
void Close();
}
#endif // DLGWIFISETTINGS_H

View File

@ -1,387 +0,0 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
// direct LAN interface. Currently powered by libpcap, may change.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <SDL2/SDL.h>
#include <pcap/pcap.h>
#include "../Wifi.h"
#include "LAN_PCap.h"
#include "PlatformConfig.h"
#ifdef __WIN32__
#include <iphlpapi.h>
#else
#include <sys/types.h>
#include <ifaddrs.h>
#include <netinet/in.h>
#include <linux/if_packet.h>
#endif
// welp
#ifndef PCAP_OPENFLAG_PROMISCUOUS
#define PCAP_OPENFLAG_PROMISCUOUS 1
#endif
#define DECL_PCAP_FUNC(ret, name, args, args2) \
typedef ret (*type_##name) args; \
type_##name ptr_##name = NULL; \
ret name args { return ptr_##name args2; }
DECL_PCAP_FUNC(int, pcap_findalldevs, (pcap_if_t** alldevs, char* errbuf), (alldevs,errbuf))
DECL_PCAP_FUNC(void, pcap_freealldevs, (pcap_if_t* alldevs), (alldevs))
DECL_PCAP_FUNC(pcap_t*, pcap_open_live, (const char* src, int snaplen, int flags, int readtimeout, char* errbuf), (src,snaplen,flags,readtimeout,errbuf))
DECL_PCAP_FUNC(void, pcap_close, (pcap_t* dev), (dev))
DECL_PCAP_FUNC(int, pcap_setnonblock, (pcap_t* dev, int nonblock, char* errbuf), (dev,nonblock,errbuf))
DECL_PCAP_FUNC(int, pcap_sendpacket, (pcap_t* dev, const u_char* data, int len), (dev,data,len))
DECL_PCAP_FUNC(int, pcap_dispatch, (pcap_t* dev, int num, pcap_handler callback, u_char* data), (dev,num,callback,data))
DECL_PCAP_FUNC(const u_char*, pcap_next, (pcap_t* dev, struct pcap_pkthdr* hdr), (dev,hdr))
namespace LAN_PCap
{
const char* PCapLibNames[] =
{
#ifdef __WIN32__
// TODO: name for npcap in non-WinPCap mode
"wpcap.dll",
#else
// Linux lib names
"libpcap.so.1",
"libpcap.so",
#endif
NULL
};
AdapterData* Adapters = NULL;
int NumAdapters = 0;
void* PCapLib = NULL;
pcap_t* PCapAdapter = NULL;
AdapterData* PCapAdapterData;
u8 PacketBuffer[2048];
int PacketLen;
volatile int RXNum;
#define LOAD_PCAP_FUNC(sym) \
ptr_##sym = (type_##sym)SDL_LoadFunction(lib, #sym); \
if (!ptr_##sym) return false;
bool TryLoadPCap(void* lib)
{
LOAD_PCAP_FUNC(pcap_findalldevs)
LOAD_PCAP_FUNC(pcap_freealldevs)
LOAD_PCAP_FUNC(pcap_open_live)
LOAD_PCAP_FUNC(pcap_close)
LOAD_PCAP_FUNC(pcap_setnonblock)
LOAD_PCAP_FUNC(pcap_sendpacket)
LOAD_PCAP_FUNC(pcap_dispatch)
LOAD_PCAP_FUNC(pcap_next)
return true;
}
bool Init(bool open_adapter)
{
// TODO: how to deal with cases where an adapter is unplugged or changes config??
if (!PCapLib)
{
PCapLib = NULL;
for (int i = 0; PCapLibNames[i]; i++)
{
void* lib = SDL_LoadObject(PCapLibNames[i]);
if (!lib) continue;
if (!TryLoadPCap(lib))
{
SDL_UnloadObject(lib);
continue;
}
printf("PCap: lib %s, init successful\n", PCapLibNames[i]);
PCapLib = lib;
break;
}
if (PCapLib == NULL)
{
printf("PCap: init failed\n");
return false;
}
}
PCapAdapter = NULL;
PacketLen = 0;
RXNum = 0;
NumAdapters = 0;
char errbuf[PCAP_ERRBUF_SIZE];
int ret;
pcap_if_t* alldevs;
ret = pcap_findalldevs(&alldevs, errbuf);
if (ret < 0 || alldevs == NULL)
{
printf("PCap: no devices available\n");
return false;
}
pcap_if_t* dev = alldevs;
while (dev) { NumAdapters++; dev = dev->next; }
Adapters = new AdapterData[NumAdapters];
memset(Adapters, 0, sizeof(AdapterData)*NumAdapters);
AdapterData* adata = &Adapters[0];
dev = alldevs;
while (dev)
{
adata->Internal = dev;
#ifdef __WIN32__
// hax
int len = strlen(dev->name);
len -= 12; if (len > 127) len = 127;
strncpy(adata->DeviceName, &dev->name[12], len);
adata->DeviceName[len] = '\0';
#else
strncpy(adata->DeviceName, dev->name, 127);
adata->DeviceName[127] = '\0';
strncpy(adata->FriendlyName, adata->DeviceName, 127);
adata->FriendlyName[127] = '\0';
#endif // __WIN32__
dev = dev->next;
adata++;
}
#ifdef __WIN32__
ULONG bufsize = 16384;
IP_ADAPTER_ADDRESSES* buf = (IP_ADAPTER_ADDRESSES*)HeapAlloc(GetProcessHeap(), 0, bufsize);
ULONG uret = GetAdaptersAddresses(AF_INET, 0, NULL, buf, &bufsize);
if (uret == ERROR_BUFFER_OVERFLOW)
{
HeapFree(GetProcessHeap(), 0, buf);
buf = (IP_ADAPTER_ADDRESSES*)HeapAlloc(GetProcessHeap(), 0, bufsize);
uret = GetAdaptersAddresses(AF_INET, 0, NULL, buf, &bufsize);
}
if (uret != ERROR_SUCCESS)
{
printf("GetAdaptersAddresses() shat itself: %08X\n", uret);
return false;
}
for (int i = 0; i < NumAdapters; i++)
{
adata = &Adapters[i];
IP_ADAPTER_ADDRESSES* addr = buf;
while (addr)
{
if (strcmp(addr->AdapterName, adata->DeviceName))
{
addr = addr->Next;
continue;
}
WideCharToMultiByte(CP_UTF8, 0, addr->FriendlyName, 127, adata->FriendlyName, 127, NULL, NULL);
adata->FriendlyName[127] = '\0';
WideCharToMultiByte(CP_UTF8, 0, addr->Description, 127, adata->Description, 127, NULL, NULL);
adata->Description[127] = '\0';
if (addr->PhysicalAddressLength != 6)
{
printf("weird MAC addr length %d for %s\n", addr->PhysicalAddressLength, addr->AdapterName);
}
else
memcpy(adata->MAC, addr->PhysicalAddress, 6);
IP_ADAPTER_UNICAST_ADDRESS* ipaddr = addr->FirstUnicastAddress;
while (ipaddr)
{
SOCKADDR* sa = ipaddr->Address.lpSockaddr;
if (sa->sa_family == AF_INET)
{
struct in_addr sa4 = ((sockaddr_in*)sa)->sin_addr;
memcpy(adata->IP_v4, &sa4, 4);
}
ipaddr = ipaddr->Next;
}
break;
}
}
HeapFree(GetProcessHeap(), 0, buf);
#else
struct ifaddrs* addrs;
if (getifaddrs(&addrs) != 0)
{
printf("getifaddrs() shat itself :(\n");
return false;
}
for (int i = 0; i < NumAdapters; i++)
{
adata = &Adapters[i];
struct ifaddrs* curaddr = addrs;
while (curaddr)
{
if (strcmp(curaddr->ifa_name, adata->DeviceName))
{
curaddr = curaddr->ifa_next;
continue;
}
if (!curaddr->ifa_addr)
{
printf("Device (%s) does not have an address :/\n", curaddr->ifa_name);
curaddr = curaddr->ifa_next;
continue;
}
u16 af = curaddr->ifa_addr->sa_family;
if (af == AF_INET)
{
struct sockaddr_in* sa = (sockaddr_in*)curaddr->ifa_addr;
memcpy(adata->IP_v4, &sa->sin_addr, 4);
}
else if (af == AF_PACKET)
{
struct sockaddr_ll* sa = (sockaddr_ll*)curaddr->ifa_addr;
if (sa->sll_halen != 6)
printf("weird MAC length %d for %s\n", sa->sll_halen, curaddr->ifa_name);
else
memcpy(adata->MAC, sa->sll_addr, 6);
}
curaddr = curaddr->ifa_next;
}
}
freeifaddrs(addrs);
#endif // __WIN32__
if (!open_adapter) return true;
if (PCapAdapter) pcap_close(PCapAdapter);
// open pcap device
PCapAdapterData = &Adapters[0];
for (int i = 0; i < NumAdapters; i++)
{
if (!strncmp(Adapters[i].DeviceName, Config::LANDevice, 128))
PCapAdapterData = &Adapters[i];
}
dev = (pcap_if_t*)PCapAdapterData->Internal;
PCapAdapter = pcap_open_live(dev->name, 2048, PCAP_OPENFLAG_PROMISCUOUS, 1, errbuf);
if (!PCapAdapter)
{
printf("PCap: failed to open adapter %s\n", errbuf);
return false;
}
pcap_freealldevs(alldevs);
if (pcap_setnonblock(PCapAdapter, 1, errbuf) < 0)
{
printf("PCap: failed to set nonblocking mode\n");
pcap_close(PCapAdapter); PCapAdapter = NULL;
return false;
}
return true;
}
void DeInit()
{
if (PCapLib)
{
if (PCapAdapter)
{
pcap_close(PCapAdapter);
PCapAdapter = NULL;
}
SDL_UnloadObject(PCapLib);
PCapLib = NULL;
}
}
void RXCallback(u_char* blarg, const struct pcap_pkthdr* header, const u_char* data)
{
while (RXNum > 0);
if (header->len > 2048-64) return;
PacketLen = header->len;
memcpy(PacketBuffer, data, PacketLen);
RXNum = 1;
}
int SendPacket(u8* data, int len)
{
if (PCapAdapter == NULL)
return 0;
if (len > 2048)
{
printf("LAN_SendPacket: error: packet too long (%d)\n", len);
return 0;
}
pcap_sendpacket(PCapAdapter, data, len);
// TODO: check success
return len;
}
int RecvPacket(u8* data)
{
if (PCapAdapter == NULL)
return 0;
int ret = 0;
if (RXNum > 0)
{
memcpy(data, PacketBuffer, PacketLen);
ret = PacketLen;
RXNum = 0;
}
pcap_dispatch(PCapAdapter, 1, RXCallback, NULL);
return ret;
}
}

View File

@ -1,53 +0,0 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef LAN_PCAP_H
#define LAN_PCAP_H
#include "../types.h"
namespace LAN_PCap
{
typedef struct
{
char DeviceName[128];
char FriendlyName[128];
char Description[128];
u8 MAC[6];
u8 IP_v4[4];
void* Internal;
} AdapterData;
extern AdapterData* Adapters;
extern int NumAdapters;
bool Init(bool open_adapter);
void DeInit();
int SendPacket(u8* data, int len);
int RecvPacket(u8* data);
}
#endif // LAN_PCAP_H

File diff suppressed because it is too large Load Diff

View File

@ -1,38 +0,0 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef LAN_SOCKET_H
#define LAN_SOCKET_H
#include "../types.h"
namespace LAN_Socket
{
//
bool Init();
void DeInit();
int SendPacket(u8* data, int len);
int RecvPacket(u8* data);
}
#endif // LAN_SOCKET_H

View File

@ -1,347 +0,0 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdio.h>
#include <string.h>
#include "MelonCap.h"
#include "libui/ui.h"
#include "../NDS.h"
#include "../GPU.h"
#include <windows.h>
#include <setupapi.h>
#include <guiddef.h>
#include <winusb.h>
namespace MelonCap
{
uiWindow* Window;
uiArea* Area;
uiAreaHandler AreaHandler;
uiDrawBitmap* WinBitmap;
bool WinBitmapInited;
u32* WinBitmapData;
// this crap was built from the reverse-engineering of ds_capture.exe
// mixed in with their Linux capture sample code
GUID InterfaceClass = {0xA0B880F6, 0xD6A5, 0x4700, {0xA8, 0xEA, 0x22, 0x28, 0x2A, 0xCA, 0x55, 0x87}};
HANDLE CapHandle;
WINUSB_INTERFACE_HANDLE CapUSBHandle;
void OnAreaDraw(uiAreaHandler* handler, uiArea* area, uiAreaDrawParams* params)
{
if (!WinBitmapInited)
{
if (WinBitmap) uiDrawFreeBitmap(WinBitmap);
WinBitmapInited = true;
WinBitmap = uiDrawNewBitmap(params->Context, 768, 384, 0);
}
if (!WinBitmap) return;
if (!WinBitmapData) return;
uiRect rc = {0, 0, 768, 384};
uiDrawBitmapUpdate(WinBitmap, WinBitmapData);
uiDrawBitmapDraw(params->Context, WinBitmap, &rc, &rc, 0);
}
void OnAreaMouseEvent(uiAreaHandler* handler, uiArea* area, uiAreaMouseEvent* evt)
{
}
void OnAreaMouseCrossed(uiAreaHandler* handler, uiArea* area, int left)
{
}
void OnAreaDragBroken(uiAreaHandler* handler, uiArea* area)
{
}
int OnAreaKeyEvent(uiAreaHandler* handler, uiArea* area, uiAreaKeyEvent* evt)
{
return 1;
}
void OnAreaResize(uiAreaHandler* handler, uiArea* area, int width, int height)
{
}
void Init()
{
printf("MelonCap init\n");
HDEVINFO devinfo = SetupDiGetClassDevsW(&InterfaceClass, NULL, NULL, DIGCF_DEVICEINTERFACE|DIGCF_PRESENT);
if (devinfo == INVALID_HANDLE_VALUE) return;
int member = 0;
bool good = false;
for (;;)
{
SP_DEVICE_INTERFACE_DATA interfacedata;
memset(&interfacedata, 0, sizeof(interfacedata));
interfacedata.cbSize = sizeof(interfacedata);
BOOL ret = SetupDiEnumDeviceInterfaces(devinfo, NULL, &InterfaceClass, member, &interfacedata);
if (!ret)
{
printf("found %d interfaces\n", member);
break;
}
DWORD requiredsize = 0;
SetupDiGetDeviceInterfaceDetailW(devinfo, &interfacedata, NULL, NULL, &requiredsize, NULL);
printf("%d: required size %d\n", member, requiredsize);
PSP_DEVICE_INTERFACE_DETAIL_DATA_W interfacedetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA_W)new u8[requiredsize];
interfacedetail->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA_W);
ret = SetupDiGetDeviceInterfaceDetailW(devinfo, &interfacedata, interfacedetail, requiredsize, NULL, NULL);
if (ret)
{
printf("got interface detail: path=%S\n", interfacedetail->DevicePath);
HANDLE file = CreateFileW(interfacedetail->DevicePath, GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
if (file != INVALID_HANDLE_VALUE)
{
WINUSB_INTERFACE_HANDLE usbhandle;
ret = WinUsb_Initialize(file, &usbhandle);
if (ret)
{
int val;
val = 0x1E;
WinUsb_SetPipePolicy(usbhandle, 0x00, PIPE_TRANSFER_TIMEOUT, 4, &val);
val = 0x32;
WinUsb_SetPipePolicy(usbhandle, 0x82, PIPE_TRANSFER_TIMEOUT, 4, &val);
val = 0x01;
WinUsb_SetPipePolicy(usbhandle, 0x82, RAW_IO, 1, &val);
printf("looking good\n");
good = true;
CapHandle = file;
CapUSBHandle = usbhandle;
}
else
CloseHandle(file);
}
}
delete[] (u8*)interfacedetail;
if (good) break;
member++;
}
SetupDiDestroyDeviceInfoList(devinfo);
AreaHandler.Draw = OnAreaDraw;
AreaHandler.MouseEvent = OnAreaMouseEvent;
AreaHandler.MouseCrossed = OnAreaMouseCrossed;
AreaHandler.DragBroken = OnAreaDragBroken;
AreaHandler.KeyEvent = OnAreaKeyEvent;
AreaHandler.Resize = OnAreaResize;
WinBitmapInited = false;
WinBitmapData = new u32[768*384];
Window = uiNewWindow("melonDS - topnotch pixel checker", 768, 384, 0, 0, 0);
Area = uiNewArea(&AreaHandler);
uiWindowSetChild(Window, uiControl(Area));
uiControlShow(uiControl(Window));
}
void DeInit()
{
uiControlDestroy(uiControl(Window));
uiDrawFreeBitmap(WinBitmap);
WinBitmapInited = false;
delete[] WinBitmapData;
WinUsb_Free(CapUSBHandle);
CloseHandle(CapHandle);
}
int VendorIn(u8 req, u16 len, u8* buf)
{
WINUSB_SETUP_PACKET pkt;
pkt.RequestType = 0xC0; // device to host
pkt.Request = req;
pkt.Value = 0; // ?????
pkt.Index = 0;
pkt.Length = len;
ULONG ret = 0;
BOOL res = WinUsb_ControlTransfer(CapUSBHandle, pkt, buf, len, &ret, NULL);
if (!res) return -1;
return ret;
}
int VendorOut(u8 req, u16 val, u16 len, u8* buf)
{
WINUSB_SETUP_PACKET pkt;
pkt.RequestType = 0x40; // host to device
pkt.Request = req;
pkt.Value = val;
pkt.Index = 0;
pkt.Length = len;
ULONG ret = 0;
BOOL res = WinUsb_ControlTransfer(CapUSBHandle, pkt, buf, len, &ret, NULL);
if (!res) return -1;
return ret;
}
int BulkIn(u8* buf, u32 len)
{
ULONG ret = 0;
BOOL res = WinUsb_ReadPipe(CapUSBHandle, 0x82, buf, len, &ret, NULL);
if (!res) return -1;
return ret;
}
u32 ConvertColor(u16 col)
{
u32 b = col & 0x001F;
u32 g = (col & 0x07E0) >> 5;
u32 r = (col & 0xF800) >> 11;
u32 ret = 0xFF000000;
ret |= ((r << 3) | (r >> 2)) << 16;
ret |= ((g << 2) | (g >> 4)) << 8;
ret |= (b << 3) | (b >> 2);
return ret;
}
void CaptureFrame()
{
u32 ret;
u8 derp;
u32 framelen = 256*384*2;
u16 frame[framelen/2];
u32 framepos = 0;
u8 frameinfo[64];
ret = VendorOut(0x30, 0, 0, &derp);
if (ret < 0) return;
int tries = 0;
while (framepos < framelen)
{
ret = BulkIn((u8*)&frame[framepos/2], framelen-framepos);
if (ret < 0) break;
if (ret == 0)
{
tries++;
if (tries >= 100) break;
continue;
}
framepos += ret;
}
ret = VendorIn(0x30, 64, frameinfo);
if (ret < 0) return;
if ((frameinfo[0] & 0x03) != 0x03) return;
if (!frameinfo[52]) return;
u16* in = &frame[0];
u32* out = &WinBitmapData[256];
for (int y = 0; y < 384; y++)
{
u32* out = &WinBitmapData[((y/2)*768) + ((y&1)*128) + 256];
if (!(frameinfo[y>>3] & (1<<(y&7))))
{
continue;
}
for (int x = 0; x < 256/2; x++)
{
out[0] = ConvertColor(in[1]);
out[768*192] = ConvertColor(in[0]);
out++;
in += 2;
}
}
}
void Update()
{
// melonDS output
int frontbuf = GPU::FrontBuffer;
u32* topbuf = GPU::Framebuffer[frontbuf][0];
if (topbuf)
{
for (int y = 0; y < 192; y++)
{
memcpy(&WinBitmapData[y*768], &topbuf[y*256], 256*4);
}
}
u32* botbuf = GPU::Framebuffer[frontbuf][1];
if (botbuf)
{
for (int y = 0; y < 192; y++)
{
memcpy(&WinBitmapData[(y+192)*768], &botbuf[y*256], 256*4);
}
}
// DS capture
CaptureFrame();
// compare
for (int y = 0; y < 384; y++)
{
for (int x = 0; x < 256; x++)
{
u32 colA = WinBitmapData[(y*768) + x + 0];
u32 colB = WinBitmapData[(y*768) + x + 256];
// best we get from the capture card is RGB565
// so we'll ignore the lower bits
const u32 mask = 0x00F8FCF8;
colA &= mask;
colB &= mask;
if (colA == colB) WinBitmapData[(y*768) + x + 512] = 0xFF000000;//0xFF00FF00;
else WinBitmapData[(y*768) + x + 512] = 0xFFFFFFFF;//0xFFFF0000;
}
}
uiAreaQueueRedrawAll(Area);
}
}

View File

@ -1,34 +0,0 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef MELONCAP_H
#define MELONCAP_H
#include "types.h"
namespace MelonCap
{
void Init();
void DeInit();
void Update();
}
#endif // MELONCAP_H

View File

@ -1,435 +0,0 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdio.h>
#include <string.h>
#include <deque>
#include <SDL2/SDL.h>
#include "../types.h"
#include "libui/ui.h"
#include "../OpenGLSupport.h"
#include "OSD.h"
#include "font.h"
#include "PlatformConfig.h"
extern int WindowWidth, WindowHeight;
namespace OSD
{
const u32 kOSDMargin = 6;
struct Item
{
Uint32 Timestamp;
char Text[256];
u32 Color;
u32 Width, Height;
u32* Bitmap;
bool DrawBitmapLoaded;
uiDrawBitmap* DrawBitmap;
bool GLTextureLoaded;
GLuint GLTexture;
};
std::deque<Item> ItemQueue;
GLint uOSDPos, uOSDSize;
GLuint OSDVertexArray;
GLuint OSDVertexBuffer;
volatile bool Rendering;
bool Init(bool opengl)
{
if (opengl)
{
GLuint prog; glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&prog);
uOSDPos = glGetUniformLocation(prog, "uOSDPos");
uOSDSize = glGetUniformLocation(prog, "uOSDSize");
float vertices[6*2] =
{
0, 0,
1, 1,
1, 0,
0, 0,
0, 1,
1, 1
};
glGenBuffers(1, &OSDVertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, OSDVertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glGenVertexArrays(1, &OSDVertexArray);
glBindVertexArray(OSDVertexArray);
glEnableVertexAttribArray(0); // position
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)(0));
}
return true;
}
void DeInit(bool opengl)
{
for (auto it = ItemQueue.begin(); it != ItemQueue.end(); )
{
Item& item = *it;
if (item.DrawBitmapLoaded && item.DrawBitmap) uiDrawFreeBitmap(item.DrawBitmap);
if (item.GLTextureLoaded && opengl) glDeleteTextures(1, &item.GLTexture);
if (item.Bitmap) delete[] item.Bitmap;
it = ItemQueue.erase(it);
}
}
int FindBreakPoint(const char* text, int i)
{
// i = character that went out of bounds
for (int j = i; j >= 0; j--)
{
if (text[j] == ' ')
return j;
}
return i;
}
void LayoutText(const char* text, u32* width, u32* height, int* breaks)
{
u32 w = 0;
u32 h = 14;
u32 totalw = 0;
u32 maxw = WindowWidth - (kOSDMargin*2);
int lastbreak = -1;
int numbrk = 0;
u16* ptr;
memset(breaks, 0, sizeof(int)*64);
for (int i = 0; text[i] != '\0'; )
{
int glyphsize;
if (text[i] == ' ')
{
glyphsize = 6;
}
else
{
u32 ch = text[i];
if (ch < 0x10 || ch > 0x7E) ch = 0x7F;
ptr = &font[(ch-0x10) << 4];
glyphsize = ptr[0];
if (!glyphsize) glyphsize = 6;
else glyphsize += 2; // space around the character
}
w += glyphsize;
if (w > maxw)
{
// wrap shit as needed
if (text[i] == ' ')
{
if (numbrk >= 64) break;
breaks[numbrk++] = i;
i++;
}
else
{
int brk = FindBreakPoint(text, i);
if (brk != lastbreak) i = brk;
if (numbrk >= 64) break;
breaks[numbrk++] = i;
lastbreak = brk;
}
w = 0;
h += 14;
}
else
i++;
if (w > totalw) totalw = w;
}
*width = totalw;
*height = h;
}
u32 RainbowColor(u32 inc)
{
// inspired from Acmlmboard
if (inc < 100) return 0xFFFF9B9B + (inc << 8);
else if (inc < 200) return 0xFFFFFF9B - ((inc-100) << 16);
else if (inc < 300) return 0xFF9BFF9B + (inc-200);
else if (inc < 400) return 0xFF9BFFFF - ((inc-300) << 8);
else if (inc < 500) return 0xFF9B9BFF + ((inc-400) << 16);
else return 0xFFFF9BFF - (inc-500);
}
void RenderText(u32 color, const char* text, Item* item)
{
u32 w, h;
int breaks[64];
bool rainbow = (color == 0);
u32 rainbowinc = ((text[0] * 17) + (SDL_GetTicks() * 13)) % 600;
color |= 0xFF000000;
const u32 shadow = 0xE0000000;
LayoutText(text, &w, &h, breaks);
item->Width = w;
item->Height = h;
item->Bitmap = new u32[w*h];
memset(item->Bitmap, 0, w*h*sizeof(u32));
u32 x = 0, y = 1;
u32 maxw = WindowWidth - (kOSDMargin*2);
int curline = 0;
u16* ptr;
for (int i = 0; text[i] != '\0'; )
{
int glyphsize;
if (text[i] == ' ')
{
x += 6;
}
else
{
u32 ch = text[i];
if (ch < 0x10 || ch > 0x7E) ch = 0x7F;
ptr = &font[(ch-0x10) << 4];
int glyphsize = ptr[0];
if (!glyphsize) x += 6;
else
{
x++;
if (rainbow)
{
color = RainbowColor(rainbowinc);
rainbowinc = (rainbowinc + 30) % 600;
}
// draw character
for (int cy = 0; cy < 12; cy++)
{
u16 val = ptr[4+cy];
for (int cx = 0; cx < glyphsize; cx++)
{
if (val & (1<<cx))
item->Bitmap[((y+cy) * w) + x+cx] = color;
}
}
x += glyphsize;
x++;
}
}
i++;
if (breaks[curline] && i >= breaks[curline])
{
i = breaks[curline++];
if (text[i] == ' ') i++;
x = 0;
y += 14;
}
}
// shadow
for (y = 0; y < h; y++)
{
for (x = 0; x < w; x++)
{
u32 val;
val = item->Bitmap[(y * w) + x];
if ((val >> 24) == 0xFF) continue;
if (x > 0) val = item->Bitmap[(y * w) + x-1];
if (x < w-1) val |= item->Bitmap[(y * w) + x+1];
if (y > 0)
{
if (x > 0) val |= item->Bitmap[((y-1) * w) + x-1];
val |= item->Bitmap[((y-1) * w) + x];
if (x < w-1) val |= item->Bitmap[((y-1) * w) + x+1];
}
if (y < h-1)
{
if (x > 0) val |= item->Bitmap[((y+1) * w) + x-1];
val |= item->Bitmap[((y+1) * w) + x];
if (x < w-1) val |= item->Bitmap[((y+1) * w) + x+1];
}
if ((val >> 24) == 0xFF)
item->Bitmap[(y * w) + x] = shadow;
}
}
}
void AddMessage(u32 color, const char* text)
{
if (!Config::ShowOSD) return;
while (Rendering);
Item item;
item.Timestamp = SDL_GetTicks();
strncpy(item.Text, text, 255); item.Text[255] = '\0';
item.Color = color;
item.Bitmap = NULL;
item.DrawBitmapLoaded = false;
item.GLTextureLoaded = false;
ItemQueue.push_back(item);
}
void WindowResized(bool opengl)
{
/*for (auto it = ItemQueue.begin(); it != ItemQueue.end(); )
{
Item& item = *it;
if (item->DrawBitmapLoaded && item->DrawBitmap) uiDrawFreeBitmap(item->DrawBitmap);
//if (item->GLTextureLoaded && opengl) glDeleteTextures(1, &item->GLTexture);
item->DrawBitmapLoaded = false;
item->GLTextureLoaded = false;
if (item->Bitmap) delete[] item->Bitmap;
it++;
}*/
}
void Update(bool opengl, uiAreaDrawParams* params)
{
if (!Config::ShowOSD)
{
Rendering = true;
if (ItemQueue.size() > 0) DeInit(opengl);
Rendering = false;
return;
}
Rendering = true;
Uint32 tick_now = SDL_GetTicks();
Uint32 tick_min = tick_now - 2500;
u32 y = kOSDMargin;
if (opengl)
{
glBindBuffer(GL_ARRAY_BUFFER, OSDVertexBuffer);
glBindVertexArray(OSDVertexArray);
glActiveTexture(GL_TEXTURE0);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
}
for (auto it = ItemQueue.begin(); it != ItemQueue.end(); )
{
Item& item = *it;
if (item.Timestamp < tick_min)
{
if (item.DrawBitmapLoaded && item.DrawBitmap) uiDrawFreeBitmap(item.DrawBitmap);
if (item.GLTextureLoaded && opengl) glDeleteTextures(1, &item.GLTexture);
if (item.Bitmap) delete[] item.Bitmap;
it = ItemQueue.erase(it);
continue;
}
if (!item.Bitmap)
{
RenderText(item.Color, item.Text, &item);
}
if (opengl)
{
if (!item.GLTextureLoaded)
{
glGenTextures(1, &item.GLTexture);
glBindTexture(GL_TEXTURE_2D, item.GLTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, item.Width, item.Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, item.Bitmap);
item.GLTextureLoaded = true;
}
glBindTexture(GL_TEXTURE_2D, item.GLTexture);
glUniform2i(uOSDPos, kOSDMargin, y);
glUniform2i(uOSDSize, item.Width, item.Height);
glDrawArrays(GL_TRIANGLES, 0, 2*3);
}
else
{
if (!item.DrawBitmapLoaded)
{
item.DrawBitmap = uiDrawNewBitmap(params->Context, item.Width, item.Height, 1);
uiDrawBitmapUpdate(item.DrawBitmap, item.Bitmap);
item.DrawBitmapLoaded = true;
}
uiRect rc_src = {0, 0, item.Width, item.Height};
uiRect rc_dst = {kOSDMargin, y, item.Width, item.Height};
uiDrawBitmapDraw(params->Context, item.DrawBitmap, &rc_src, &rc_dst, 0);
}
y += item.Height;
it++;
}
Rendering = false;
}
}

View File

@ -1,35 +0,0 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef OSD_H
#define OSD_H
namespace OSD
{
bool Init(bool opengl);
void DeInit(bool opengl);
void AddMessage(u32 color, const char* text);
void WindowResized(bool opengl);
void Update(bool opengl, uiAreaDrawParams* params);
}
#endif // OSD_H

View File

@ -1,557 +0,0 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <SDL2/SDL.h>
#include "../Platform.h"
#include "PlatformConfig.h"
#include "LAN_Socket.h"
#include "LAN_PCap.h"
#include "libui/ui.h"
#include <string>
#ifdef __WIN32__
#define NTDDI_VERSION 0x06000000 // GROSS FUCKING HACK
#include <windows.h>
//#include <knownfolders.h> // FUCK THAT SHIT
extern "C" const GUID DECLSPEC_SELECTANY FOLDERID_RoamingAppData = {0x3eb685db, 0x65f9, 0x4cf6, {0xa0, 0x3a, 0xe3, 0xef, 0x65, 0x72, 0x9f, 0x3d}};
#include <shlobj.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#define socket_t SOCKET
#define sockaddr_t SOCKADDR
#else
#include <glib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <sys/socket.h>
#define socket_t int
#define sockaddr_t struct sockaddr
#define closesocket close
#endif
#ifndef INVALID_SOCKET
#define INVALID_SOCKET (socket_t)-1
#endif
extern char* EmuDirectory;
void Stop(bool internal);
namespace Platform
{
typedef struct
{
SDL_Thread* ID;
void (*Func)();
} ThreadData;
int ThreadEntry(void* data)
{
ThreadData* thread = (ThreadData*)data;
thread->Func();
return 0;
}
socket_t MPSocket;
sockaddr_t MPSendAddr;
u8 PacketBuffer[2048];
#define NIFI_VER 1
void StopEmu()
{
Stop(true);
}
FILE* OpenFile(const char* path, const char* mode, bool mustexist)
{
FILE* ret;
#ifdef __WIN32__
int len = MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0);
if (len < 1) return NULL;
WCHAR* fatpath = new WCHAR[len];
int res = MultiByteToWideChar(CP_UTF8, 0, path, -1, fatpath, len);
if (res != len) { delete[] fatpath; return NULL; } // checkme?
// this will be more than enough
WCHAR fatmode[4];
fatmode[0] = mode[0];
fatmode[1] = mode[1];
fatmode[2] = mode[2];
fatmode[3] = 0;
if (mustexist)
{
ret = _wfopen(fatpath, L"rb");
if (ret) ret = _wfreopen(fatpath, fatmode, ret);
}
else
ret = _wfopen(fatpath, fatmode);
delete[] fatpath;
#else
if (mustexist)
{
ret = fopen(path, "rb");
if (ret) ret = freopen(path, mode, ret);
}
else
ret = fopen(path, mode);
#endif
return ret;
}
#if !defined(UNIX_PORTABLE) && !defined(__WIN32__)
FILE* OpenLocalFile(const char* path, const char* mode)
{
std::string fullpath;
if (path[0] == '/')
{
// If it's an absolute path, just open that.
fullpath = std::string(path);
}
else
{
// Check user configuration directory
std::string confpath = std::string(g_get_user_config_dir()) + "/melonDS/";
g_mkdir_with_parents(confpath.c_str(), 0755);
fullpath = confpath + path;
}
return OpenFile(fullpath.c_str(), mode, mode[0] != 'w');
}
FILE* OpenDataFile(const char* path)
{
const char* melondir = "melonDS";
const char* const* sys_dirs = g_get_system_data_dirs();
const char* user_dir = g_get_user_data_dir();
// First check the user's data directory
char* fullpath = g_build_path("/", user_dir, melondir, path, NULL);
if (access(fullpath, R_OK) == 0)
{
FILE* f = fopen(fullpath, "r");
g_free(fullpath);
return f;
}
free(fullpath);
// Then check the system data directories
for (size_t i = 0; sys_dirs[i] != NULL; i++)
{
const char* dir = sys_dirs[i];
char* fullpath = g_build_path("/", dir, melondir, path, NULL);
if (access(fullpath, R_OK) == 0)
{
FILE* f = fopen(fullpath, "r");
g_free(fullpath);
return f;
}
free(fullpath);
}
FILE* f = fopen(path, "rb");
if (f) return f;
return NULL;
}
#else
FILE* OpenLocalFile(const char* path, const char* mode)
{
bool relpath = false;
int pathlen = strlen(path);
#ifdef __WIN32__
if (pathlen > 3)
{
if (path[1] == ':' && path[2] == '\\')
return OpenFile(path, mode);
}
#else
if (pathlen > 1)
{
if (path[0] == '/')
return OpenFile(path, mode);
}
#endif
if (pathlen >= 3)
{
if (path[0] == '.' && path[1] == '.' && (path[2] == '/' || path[2] == '\\'))
relpath = true;
}
int emudirlen = strlen(EmuDirectory);
char* emudirpath;
if (emudirlen)
{
int len = emudirlen + 1 + pathlen + 1;
emudirpath = new char[len];
strncpy(&emudirpath[0], EmuDirectory, emudirlen);
emudirpath[emudirlen] = '/';
strncpy(&emudirpath[emudirlen+1], path, pathlen);
emudirpath[emudirlen+1+pathlen] = '\0';
}
else
{
emudirpath = new char[pathlen+1];
strncpy(&emudirpath[0], path, pathlen);
emudirpath[pathlen] = '\0';
}
// Locations are application directory, and AppData/melonDS on Windows or XDG_CONFIG_HOME/melonDS on Linux
FILE* f;
// First check current working directory
f = OpenFile(path, mode, true);
if (f) { delete[] emudirpath; return f; }
// then emu directory
f = OpenFile(emudirpath, mode, true);
if (f) { delete[] emudirpath; return f; }
#ifdef __WIN32__
// a path relative to AppData wouldn't make much sense
if (!relpath)
{
// Now check AppData
PWSTR appDataPath = NULL;
SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, NULL, &appDataPath);
if (!appDataPath)
{
delete[] emudirpath;
return NULL;
}
// this will be more than enough
WCHAR fatperm[4];
fatperm[0] = mode[0];
fatperm[1] = mode[1];
fatperm[2] = mode[2];
fatperm[3] = 0;
int fnlen = MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0);
if (fnlen < 1) { delete[] emudirpath; return NULL; }
WCHAR* wfileName = new WCHAR[fnlen];
int res = MultiByteToWideChar(CP_UTF8, 0, path, -1, wfileName, fnlen);
if (res != fnlen) { delete[] wfileName; delete[] emudirpath; return NULL; } // checkme?
const WCHAR* appdir = L"\\melonDS\\";
int pos = wcslen(appDataPath);
void* ptr = CoTaskMemRealloc(appDataPath, (pos+wcslen(appdir)+fnlen+1)*sizeof(WCHAR));
if (!ptr) { delete[] wfileName; delete[] emudirpath; return NULL; } // oh well
appDataPath = (PWSTR)ptr;
wcscpy(&appDataPath[pos], appdir); pos += wcslen(appdir);
wcscpy(&appDataPath[pos], wfileName);
f = _wfopen(appDataPath, L"rb");
if (f) f = _wfreopen(appDataPath, fatperm, f);
CoTaskMemFree(appDataPath);
delete[] wfileName;
if (f) { delete[] emudirpath; return f; }
}
#else
if (!relpath)
{
// Now check XDG_CONFIG_HOME
// TODO: check for memory leak there
std::string fullpath = std::string(g_get_user_config_dir()) + "/melonDS/" + path;
f = OpenFile(fullpath.c_str(), mode, true);
if (f) { delete[] emudirpath; return f; }
}
#endif
if (mode[0] != 'r')
{
f = OpenFile(emudirpath, mode);
if (f) { delete[] emudirpath; return f; }
}
delete[] emudirpath;
return NULL;
}
FILE* OpenDataFile(const char* path)
{
return OpenLocalFile(path, "rb");
}
#endif
void* Thread_Create(void (*func)())
{
ThreadData* data = new ThreadData;
data->Func = func;
data->ID = SDL_CreateThread(ThreadEntry, "melonDS core thread", data);
return data;
}
void Thread_Free(void* thread)
{
delete (ThreadData*)thread;
}
void Thread_Wait(void* thread)
{
SDL_WaitThread((SDL_Thread*)((ThreadData*)thread)->ID, NULL);
}
void* Semaphore_Create()
{
return SDL_CreateSemaphore(0);
}
void Semaphore_Free(void* sema)
{
SDL_DestroySemaphore((SDL_sem*)sema);
}
void Semaphore_Reset(void* sema)
{
while (SDL_SemTryWait((SDL_sem*)sema) == 0);
}
void Semaphore_Wait(void* sema)
{
SDL_SemWait((SDL_sem*)sema);
}
void Semaphore_Post(void* sema)
{
SDL_SemPost((SDL_sem*)sema);
}
void* GL_GetProcAddress(const char* proc)
{
return uiGLGetProcAddress(proc);
}
bool MP_Init()
{
int opt_true = 1;
int res;
#ifdef __WIN32__
WSADATA wsadata;
if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0)
{
return false;
}
#endif // __WIN32__
MPSocket = socket(AF_INET, SOCK_DGRAM, 0);
if (MPSocket < 0)
{
return false;
}
res = setsockopt(MPSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt_true, sizeof(int));
if (res < 0)
{
closesocket(MPSocket);
MPSocket = INVALID_SOCKET;
return false;
}
sockaddr_t saddr;
saddr.sa_family = AF_INET;
*(u32*)&saddr.sa_data[2] = htonl(Config::SocketBindAnyAddr ? INADDR_ANY : INADDR_LOOPBACK);
*(u16*)&saddr.sa_data[0] = htons(7064);
res = bind(MPSocket, &saddr, sizeof(sockaddr_t));
if (res < 0)
{
closesocket(MPSocket);
MPSocket = INVALID_SOCKET;
return false;
}
res = setsockopt(MPSocket, SOL_SOCKET, SO_BROADCAST, (const char*)&opt_true, sizeof(int));
if (res < 0)
{
closesocket(MPSocket);
MPSocket = INVALID_SOCKET;
return false;
}
MPSendAddr.sa_family = AF_INET;
*(u32*)&MPSendAddr.sa_data[2] = htonl(INADDR_BROADCAST);
*(u16*)&MPSendAddr.sa_data[0] = htons(7064);
return true;
}
void MP_DeInit()
{
if (MPSocket >= 0)
closesocket(MPSocket);
#ifdef __WIN32__
WSACleanup();
#endif // __WIN32__
}
int MP_SendPacket(u8* data, int len)
{
if (MPSocket < 0)
return 0;
if (len > 2048-8)
{
printf("MP_SendPacket: error: packet too long (%d)\n", len);
return 0;
}
*(u32*)&PacketBuffer[0] = htonl(0x4946494E); // NIFI
PacketBuffer[4] = NIFI_VER;
PacketBuffer[5] = 0;
*(u16*)&PacketBuffer[6] = htons(len);
memcpy(&PacketBuffer[8], data, len);
int slen = sendto(MPSocket, (const char*)PacketBuffer, len+8, 0, &MPSendAddr, sizeof(sockaddr_t));
if (slen < 8) return 0;
return slen - 8;
}
int MP_RecvPacket(u8* data, bool block)
{
if (MPSocket < 0)
return 0;
fd_set fd;
struct timeval tv;
FD_ZERO(&fd);
FD_SET(MPSocket, &fd);
tv.tv_sec = 0;
tv.tv_usec = block ? 5000 : 0;
if (!select(MPSocket+1, &fd, 0, 0, &tv))
{
return 0;
}
sockaddr_t fromAddr;
socklen_t fromLen = sizeof(sockaddr_t);
int rlen = recvfrom(MPSocket, (char*)PacketBuffer, 2048, 0, &fromAddr, &fromLen);
if (rlen < 8+24)
{
return 0;
}
rlen -= 8;
if (ntohl(*(u32*)&PacketBuffer[0]) != 0x4946494E)
{
return 0;
}
if (PacketBuffer[4] != NIFI_VER)
{
return 0;
}
if (ntohs(*(u16*)&PacketBuffer[6]) != rlen)
{
return 0;
}
memcpy(data, &PacketBuffer[8], rlen);
return rlen;
}
bool LAN_Init()
{
if (Config::DirectLAN)
{
if (!LAN_PCap::Init(true))
return false;
}
else
{
if (!LAN_Socket::Init())
return false;
}
return true;
}
void LAN_DeInit()
{
// checkme. blarg
//if (Config::DirectLAN)
// LAN_PCap::DeInit();
//else
// LAN_Socket::DeInit();
LAN_PCap::DeInit();
LAN_Socket::DeInit();
}
int LAN_SendPacket(u8* data, int len)
{
if (Config::DirectLAN)
return LAN_PCap::SendPacket(data, len);
else
return LAN_Socket::SendPacket(data, len);
}
int LAN_RecvPacket(u8* data)
{
if (Config::DirectLAN)
return LAN_PCap::RecvPacket(data);
else
return LAN_Socket::RecvPacket(data);
}
}

View File

@ -1,151 +0,0 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "PlatformConfig.h"
namespace Config
{
int KeyMapping[12];
int JoyMapping[12];
int HKKeyMapping[HK_MAX];
int HKJoyMapping[HK_MAX];
int JoystickID;
int WindowWidth;
int WindowHeight;
int WindowMaximized;
int ScreenRotation;
int ScreenGap;
int ScreenLayout;
int ScreenSizing;
int ScreenFilter;
int ScreenUseGL;
int ScreenVSync;
int ScreenRatio;
int LimitFPS;
int AudioSync;
int ShowOSD;
int DirectBoot;
int SocketBindAnyAddr;
char LANDevice[128];
int DirectLAN;
int SavestateRelocSRAM;
int AudioVolume;
int MicInputType;
char MicWavPath[512];
char LastROMFolder[512];
ConfigEntry PlatformConfigFile[] =
{
{"Key_A", 0, &KeyMapping[0], 32, NULL, 0},
{"Key_B", 0, &KeyMapping[1], 31, NULL, 0},
{"Key_Select", 0, &KeyMapping[2], 57, NULL, 0},
{"Key_Start", 0, &KeyMapping[3], 28, NULL, 0},
{"Key_Right", 0, &KeyMapping[4], 333, NULL, 0},
{"Key_Left", 0, &KeyMapping[5], 331, NULL, 0},
{"Key_Up", 0, &KeyMapping[6], 328, NULL, 0},
{"Key_Down", 0, &KeyMapping[7], 336, NULL, 0},
{"Key_R", 0, &KeyMapping[8], 54, NULL, 0},
{"Key_L", 0, &KeyMapping[9], 86, NULL, 0},
{"Key_X", 0, &KeyMapping[10], 17, NULL, 0},
{"Key_Y", 0, &KeyMapping[11], 30, NULL, 0},
{"Joy_A", 0, &JoyMapping[0], -1, NULL, 0},
{"Joy_B", 0, &JoyMapping[1], -1, NULL, 0},
{"Joy_Select", 0, &JoyMapping[2], -1, NULL, 0},
{"Joy_Start", 0, &JoyMapping[3], -1, NULL, 0},
{"Joy_Right", 0, &JoyMapping[4], -1, NULL, 0},
{"Joy_Left", 0, &JoyMapping[5], -1, NULL, 0},
{"Joy_Up", 0, &JoyMapping[6], -1, NULL, 0},
{"Joy_Down", 0, &JoyMapping[7], -1, NULL, 0},
{"Joy_R", 0, &JoyMapping[8], -1, NULL, 0},
{"Joy_L", 0, &JoyMapping[9], -1, NULL, 0},
{"Joy_X", 0, &JoyMapping[10], -1, NULL, 0},
{"Joy_Y", 0, &JoyMapping[11], -1, NULL, 0},
{"HKKey_Lid", 0, &HKKeyMapping[HK_Lid], 0x0D, NULL, 0},
{"HKKey_Mic", 0, &HKKeyMapping[HK_Mic], 0x35, NULL, 0},
{"HKKey_Pause", 0, &HKKeyMapping[HK_Pause], -1, NULL, 0},
{"HKKey_Reset", 0, &HKKeyMapping[HK_Reset], -1, NULL, 0},
{"HKKey_FastForward", 0, &HKKeyMapping[HK_FastForward], 0x0F, NULL, 0},
{"HKKey_FastForwardToggle", 0, &HKKeyMapping[HK_FastForwardToggle], -1, NULL, 0},
{"HKKey_SolarSensorDecrease", 0, &HKKeyMapping[HK_SolarSensorDecrease], 0x4B, NULL, 0},
{"HKKey_SolarSensorIncrease", 0, &HKKeyMapping[HK_SolarSensorIncrease], 0x4D, NULL, 0},
{"HKJoy_Lid", 0, &HKJoyMapping[HK_Lid], -1, NULL, 0},
{"HKJoy_Mic", 0, &HKJoyMapping[HK_Mic], -1, NULL, 0},
{"HKJoy_Pause", 0, &HKJoyMapping[HK_Pause], -1, NULL, 0},
{"HKJoy_Reset", 0, &HKJoyMapping[HK_Reset], -1, NULL, 0},
{"HKJoy_FastForward", 0, &HKJoyMapping[HK_FastForward], -1, NULL, 0},
{"HKJoy_FastForwardToggle", 0, &HKJoyMapping[HK_FastForwardToggle], -1, NULL, 0},
{"HKJoy_SolarSensorDecrease", 0, &HKJoyMapping[HK_SolarSensorDecrease], -1, NULL, 0},
{"HKJoy_SolarSensorIncrease", 0, &HKJoyMapping[HK_SolarSensorIncrease], -1, NULL, 0},
{"JoystickID", 0, &JoystickID, 0, NULL, 0},
{"WindowWidth", 0, &WindowWidth, 256, NULL, 0},
{"WindowHeight", 0, &WindowHeight, 384, NULL, 0},
{"WindowMax", 0, &WindowMaximized, 0, NULL, 0},
{"ScreenRotation", 0, &ScreenRotation, 0, NULL, 0},
{"ScreenGap", 0, &ScreenGap, 0, NULL, 0},
{"ScreenLayout", 0, &ScreenLayout, 0, NULL, 0},
{"ScreenSizing", 0, &ScreenSizing, 0, NULL, 0},
{"ScreenFilter", 0, &ScreenFilter, 1, NULL, 0},
{"ScreenUseGL", 0, &ScreenUseGL, 1, NULL, 0},
{"ScreenVSync", 0, &ScreenVSync, 0, NULL, 0},
{"ScreenRatio", 0, &ScreenRatio, 0, NULL, 0},
{"LimitFPS", 0, &LimitFPS, 0, NULL, 0},
{"AudioSync", 0, &AudioSync, 1, NULL, 0},
{"ShowOSD", 0, &ShowOSD, 1, NULL, 0},
{"DirectBoot", 0, &DirectBoot, 1, NULL, 0},
{"SockBindAnyAddr", 0, &SocketBindAnyAddr, 0, NULL, 0},
{"LANDevice", 1, LANDevice, 0, "", 127},
{"DirectLAN", 0, &DirectLAN, 0, NULL, 0},
{"SavStaRelocSRAM", 0, &SavestateRelocSRAM, 0, NULL, 0},
{"AudioVolume", 0, &AudioVolume, 256, NULL, 0},
{"MicInputType", 0, &MicInputType, 1, NULL, 0},
{"MicWavPath", 1, MicWavPath, 0, "", 511},
{"LastROMFolder", 1, LastROMFolder, 0, "", 511},
{"", -1, NULL, 0, NULL, 0}
};
}

View File

@ -1,82 +0,0 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef PLATFORMCONFIG_H
#define PLATFORMCONFIG_H
#include "../Config.h"
enum
{
HK_Lid = 0,
HK_Mic,
HK_Pause,
HK_Reset,
HK_FastForward,
HK_FastForwardToggle,
HK_SolarSensorDecrease,
HK_SolarSensorIncrease,
HK_MAX
};
namespace Config
{
extern int KeyMapping[12];
extern int JoyMapping[12];
extern int HKKeyMapping[HK_MAX];
extern int HKJoyMapping[HK_MAX];
extern int JoystickID;
extern int WindowWidth;
extern int WindowHeight;
extern int WindowMaximized;
extern int ScreenRotation;
extern int ScreenGap;
extern int ScreenLayout;
extern int ScreenSizing;
extern int ScreenFilter;
extern int ScreenUseGL;
extern int ScreenVSync;
extern int ScreenRatio;
extern int LimitFPS;
extern int AudioSync;
extern int ShowOSD;
extern int DirectBoot;
extern int SocketBindAnyAddr;
extern char LANDevice[128];
extern int DirectLAN;
extern int SavestateRelocSRAM;
extern int AudioVolume;
extern int MicInputType;
extern char MicWavPath[512];
extern char LastROMFolder[512];
}
#endif // PLATFORMCONFIG_H

View File

@ -1,135 +0,0 @@
/*
Copyright 2016-2020 Arisotura
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef FONT_H
#define FONT_H
unsigned short font[] = {
12, 0, 0, 0,0x0C03, 0x0E07, 0x070E, 0x039C, 0x01F8, 0x00F0, 0x00F0, 0x01F8, 0x039C, 0x070E, 0x0E07, 0x0C03,
12, 0, 0, 0,0x01C0, 0x00E0, 0x0060, 0x0860, 0x0C60, 0x0FE0, 0x07F0, 0x0038, 0x001C, 0x000E, 0x0007, 0x0003,
12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
12, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
2, 0, 0, 0,0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0000, 0x0000, 0x0003, 0x0003, 0x0000, 0x0000,
9, 0, 0, 0,0x01EF, 0x01EF, 0x018C, 0x01CE, 0x00E7, 0x0063, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
10, 0, 0, 0,0x00CC, 0x00CC, 0x03FF, 0x03FF, 0x00CC, 0x00CC, 0x03FF, 0x03FF, 0x00CC, 0x00CC, 0x0000, 0x0000,
8, 0, 0, 0,0x0018, 0x00FE, 0x00FF, 0x001B, 0x007F, 0x00FE, 0x00D8, 0x00FF, 0x007F, 0x0018, 0x0000, 0x0000,
10, 0, 0, 0,0x0306, 0x038F, 0x01CF, 0x00E6, 0x0070, 0x0038, 0x019C, 0x03CE, 0x03C7, 0x0183, 0x0000, 0x0000,
10, 0, 0, 0,0x007C, 0x00FE, 0x00C6, 0x00EE, 0x007C, 0x037E, 0x03E7, 0x01F3, 0x03BF, 0x031E, 0x0000, 0x0000,
4, 0, 0, 0,0x000F, 0x000F, 0x000C, 0x000E, 0x0007, 0x0003, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
4, 0, 0, 0,0x000C, 0x000E, 0x0007, 0x0003, 0x0003, 0x0003, 0x0003, 0x0007, 0x000E, 0x000C, 0x0000, 0x0000,
4, 0, 0, 0,0x0003, 0x0007, 0x000E, 0x000C, 0x000C, 0x000C, 0x000C, 0x000E, 0x0007, 0x0003, 0x0000, 0x0000,
10, 0, 0, 0,0x0030, 0x0333, 0x03B7, 0x01FE, 0x00FC, 0x00FC, 0x01FE, 0x03B7, 0x0333, 0x0030, 0x0000, 0x0000,
10, 0, 0, 0,0x0030, 0x0030, 0x0030, 0x0030, 0x03FF, 0x03FF, 0x0030, 0x0030, 0x0030, 0x0030, 0x0000, 0x0000,
4, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x000F, 0x000F, 0x000C, 0x000E, 0x0007, 0x0003,
10, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x03FF, 0x03FF, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
3, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0007, 0x0007, 0x0007, 0x0000, 0x0000,
10, 0, 0, 0,0x0300, 0x0380, 0x01C0, 0x00E0, 0x0070, 0x0038, 0x001C, 0x000E, 0x0007, 0x0003, 0x0000, 0x0000,
8, 0, 0, 0,0x007E, 0x00FF, 0x00C3, 0x00C3, 0x00C3, 0x00C3, 0x00C3, 0x00C3, 0x00FF, 0x007E, 0x0000, 0x0000,
4, 0, 0, 0,0x0006, 0x0007, 0x0007, 0x0006, 0x0006, 0x0006, 0x0006, 0x0006, 0x000F, 0x000F, 0x0000, 0x0000,
8, 0, 0, 0,0x007E, 0x00FF, 0x00C3, 0x00C0, 0x00FE, 0x007F, 0x0003, 0x0003, 0x00FF, 0x00FF, 0x0000, 0x0000,
8, 0, 0, 0,0x007F, 0x00FF, 0x00C0, 0x00C0, 0x007C, 0x00FC, 0x00C0, 0x00C0, 0x00FF, 0x007F, 0x0000, 0x0000,
8, 0, 0, 0,0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x00FF, 0x00FE, 0x0060, 0x0060, 0x0000, 0x0000,
8, 0, 0, 0,0x00FF, 0x00FF, 0x0003, 0x0003, 0x007F, 0x00FF, 0x00C0, 0x00C0, 0x00FF, 0x007F, 0x0000, 0x0000,
8, 0, 0, 0,0x007E, 0x007F, 0x0003, 0x0003, 0x007F, 0x00FF, 0x00C3, 0x00C3, 0x00FF, 0x007E, 0x0000, 0x0000,
8, 0, 0, 0,0x00FF, 0x00FF, 0x00C0, 0x00E0, 0x0070, 0x0038, 0x001C, 0x000C, 0x000C, 0x000C, 0x0000, 0x0000,
8, 0, 0, 0,0x007E, 0x00FF, 0x00C3, 0x00C3, 0x007E, 0x00FF, 0x00C3, 0x00C3, 0x00FF, 0x007E, 0x0000, 0x0000,
8, 0, 0, 0,0x007E, 0x00FF, 0x00C3, 0x00C3, 0x00FF, 0x00FE, 0x00C0, 0x00C0, 0x00FE, 0x007E, 0x0000, 0x0000,
3, 0, 0, 0,0x0000, 0x0000, 0x0007, 0x0007, 0x0000, 0x0000, 0x0000, 0x0007, 0x0007, 0x0000, 0x0000, 0x0000,
4, 0, 0, 0,0x0000, 0x0000, 0x000E, 0x000E, 0x0000, 0x0000, 0x000C, 0x000E, 0x0007, 0x0003, 0x0000, 0x0000,
6, 0, 0, 0,0x0030, 0x0038, 0x001C, 0x000E, 0x0007, 0x0007, 0x000E, 0x001C, 0x0038, 0x0030, 0x0000, 0x0000,
7, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x007F, 0x007F, 0x0000, 0x0000, 0x007F, 0x007F, 0x0000, 0x0000, 0x0000,
6, 0, 0, 0,0x0003, 0x0007, 0x000E, 0x001C, 0x0038, 0x0038, 0x001C, 0x000E, 0x0007, 0x0003, 0x0000, 0x0000,
8, 0, 0, 0,0x007E, 0x00FF, 0x00C3, 0x00C3, 0x00F0, 0x0078, 0x0018, 0x0000, 0x0018, 0x0018, 0x0000, 0x0000,
10, 0, 0, 0,0x00FC, 0x01FE, 0x0387, 0x0333, 0x037B, 0x03FB, 0x01F3, 0x0007, 0x03FE, 0x03FC, 0x0000, 0x0000,
9, 0, 0, 0,0x00FE, 0x01FF, 0x0183, 0x0183, 0x0183, 0x01FF, 0x01FF, 0x0183, 0x0183, 0x0183, 0x0000, 0x0000,
9, 0, 0, 0,0x00FF, 0x01FF, 0x0183, 0x0183, 0x00FF, 0x01FF, 0x0183, 0x0183, 0x01FF, 0x00FF, 0x0000, 0x0000,
8, 0, 0, 0,0x00FE, 0x00FF, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x00FF, 0x00FE, 0x0000, 0x0000,
9, 0, 0, 0,0x007F, 0x00FF, 0x01C3, 0x0183, 0x0183, 0x0183, 0x0183, 0x01C3, 0x00FF, 0x007F, 0x0000, 0x0000,
9, 0, 0, 0,0x01FF, 0x01FF, 0x0003, 0x0003, 0x00FF, 0x00FF, 0x0003, 0x0003, 0x01FF, 0x01FF, 0x0000, 0x0000,
9, 0, 0, 0,0x01FF, 0x01FF, 0x0003, 0x0003, 0x00FF, 0x00FF, 0x0003, 0x0003, 0x0003, 0x0003, 0x0000, 0x0000,
9, 0, 0, 0,0x01FE, 0x01FF, 0x0003, 0x0003, 0x01F3, 0x01F3, 0x0183, 0x0183, 0x01FF, 0x00FE, 0x0000, 0x0000,
9, 0, 0, 0,0x0183, 0x0183, 0x0183, 0x0183, 0x01FF, 0x01FF, 0x0183, 0x0183, 0x0183, 0x0183, 0x0000, 0x0000,
6, 0, 0, 0,0x003F, 0x003F, 0x000C, 0x000C, 0x000C, 0x000C, 0x000C, 0x000C, 0x003F, 0x003F, 0x0000, 0x0000,
9, 0, 0, 0,0x01F0, 0x01F0, 0x00C0, 0x00C0, 0x00C0, 0x00C0, 0x00C3, 0x00C3, 0x00FF, 0x007E, 0x0000, 0x0000,
9, 0, 0, 0,0x0183, 0x01C3, 0x00E3, 0x0073, 0x003F, 0x003F, 0x0073, 0x00E3, 0x01C3, 0x0183, 0x0000, 0x0000,
7, 0, 0, 0,0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x007F, 0x007F, 0x0000, 0x0000,
10, 0, 0, 0,0x0303, 0x0387, 0x03CF, 0x03FF, 0x037B, 0x0333, 0x0303, 0x0303, 0x0303, 0x0303, 0x0000, 0x0000,
10, 0, 0, 0,0x0303, 0x0307, 0x030F, 0x031F, 0x033B, 0x0373, 0x03E3, 0x03C3, 0x0383, 0x0303, 0x0000, 0x0000,
10, 0, 0, 0,0x01FE, 0x03FF, 0x0303, 0x0303, 0x0303, 0x0303, 0x0303, 0x0303, 0x03FF, 0x01FE, 0x0000, 0x0000,
9, 0, 0, 0,0x00FF, 0x01FF, 0x0183, 0x0183, 0x01FF, 0x00FF, 0x0003, 0x0003, 0x0003, 0x0003, 0x0000, 0x0000,
10, 0, 0, 0,0x01FE, 0x03FF, 0x0303, 0x0303, 0x0333, 0x0373, 0x03E3, 0x01C3, 0x03FF, 0x037E, 0x0000, 0x0000,
9, 0, 0, 0,0x00FF, 0x01FF, 0x0183, 0x0183, 0x01FF, 0x00FF, 0x0073, 0x00E3, 0x01C3, 0x0183, 0x0000, 0x0000,
10, 0, 0, 0,0x01FE, 0x01FF, 0x0003, 0x0003, 0x01FF, 0x03FE, 0x0300, 0x0300, 0x03FE, 0x01FE, 0x0000, 0x0000,
10, 0, 0, 0,0x03FF, 0x03FF, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0000, 0x0000,
9, 0, 0, 0,0x0183, 0x0183, 0x0183, 0x0183, 0x0183, 0x0183, 0x0183, 0x0183, 0x01FF, 0x00FE, 0x0000, 0x0000,
10, 0, 0, 0,0x0303, 0x0303, 0x0303, 0x0303, 0x0303, 0x0387, 0x01CE, 0x00FC, 0x0078, 0x0030, 0x0000, 0x0000,
10, 0, 0, 0,0x0303, 0x0303, 0x0303, 0x0303, 0x0333, 0x037B, 0x03FF, 0x03CF, 0x0387, 0x0303, 0x0000, 0x0000,
10, 0, 0, 0,0x0303, 0x0387, 0x01CE, 0x00FC, 0x0078, 0x0078, 0x00FC, 0x01CE, 0x0387, 0x0303, 0x0000, 0x0000,
10, 0, 0, 0,0x0303, 0x0387, 0x01CE, 0x00FC, 0x0078, 0x0030, 0x0030, 0x0030, 0x0030, 0x0030, 0x0000, 0x0000,
10, 0, 0, 0,0x03FF, 0x03FF, 0x01C0, 0x00E0, 0x0070, 0x0038, 0x001C, 0x000E, 0x03FF, 0x03FF, 0x0000, 0x0000,
4, 0, 0, 0,0x000F, 0x000F, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x000F, 0x000F, 0x0000, 0x0000,
10, 0, 0, 0,0x0003, 0x0007, 0x000E, 0x001C, 0x0038, 0x0070, 0x00E0, 0x01C0, 0x0380, 0x0300, 0x0000, 0x0000,
4, 0, 0, 0,0x000F, 0x000F, 0x000C, 0x000C, 0x000C, 0x000C, 0x000C, 0x000C, 0x000F, 0x000F, 0x0000, 0x0000,
8, 0, 0, 0,0x0018, 0x003C, 0x007E, 0x00E7, 0x00C3, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
10, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x03FF, 0x03FF,
4, 0, 0, 0,0x000F, 0x000F, 0x0003, 0x0007, 0x000E, 0x000C, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
7, 0, 0, 0,0x0000, 0x0000, 0x003E, 0x007E, 0x0060, 0x007E, 0x007F, 0x0063, 0x007F, 0x007E, 0x0000, 0x0000,
7, 0, 0, 0,0x0003, 0x0003, 0x0003, 0x003F, 0x007F, 0x0063, 0x0063, 0x0063, 0x007F, 0x003F, 0x0000, 0x0000,
7, 0, 0, 0,0x0000, 0x0000, 0x007E, 0x007F, 0x0003, 0x0003, 0x0003, 0x0003, 0x007F, 0x007E, 0x0000, 0x0000,
7, 0, 0, 0,0x0060, 0x0060, 0x0060, 0x007E, 0x007F, 0x0063, 0x0063, 0x0063, 0x007F, 0x007E, 0x0000, 0x0000,
7, 0, 0, 0,0x0000, 0x0000, 0x003E, 0x007F, 0x0063, 0x007F, 0x003F, 0x0003, 0x003F, 0x003E, 0x0000, 0x0000,
6, 0, 0, 0,0x003C, 0x003E, 0x0006, 0x0006, 0x001F, 0x001F, 0x0006, 0x0006, 0x0006, 0x0006, 0x0000, 0x0000,
7, 0, 0, 0,0x0000, 0x0000, 0x007E, 0x007F, 0x0063, 0x0063, 0x007F, 0x007E, 0x0060, 0x0060, 0x007E, 0x003E,
7, 0, 0, 0,0x0003, 0x0003, 0x0003, 0x003F, 0x007F, 0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x0000, 0x0000,
2, 0, 0, 0,0x0003, 0x0003, 0x0000, 0x0000, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0000, 0x0000,
7, 0, 0, 0,0x0060, 0x0060, 0x0000, 0x0000, 0x0060, 0x0060, 0x0060, 0x0060, 0x0060, 0x0063, 0x007F, 0x003E,
8, 0, 0, 0,0x0003, 0x0003, 0x00E3, 0x0073, 0x003B, 0x001F, 0x001F, 0x003B, 0x0073, 0x00E3, 0x0000, 0x0000,
4, 0, 0, 0,0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x000F, 0x000E, 0x0000, 0x0000,
10, 0, 0, 0,0x0000, 0x0000, 0x01FF, 0x03FF, 0x0333, 0x0333, 0x0333, 0x0333, 0x0333, 0x0333, 0x0000, 0x0000,
7, 0, 0, 0,0x0000, 0x0000, 0x003F, 0x007F, 0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x0000, 0x0000,
8, 0, 0, 0,0x0000, 0x0000, 0x007E, 0x00FF, 0x00C3, 0x00C3, 0x00C3, 0x00C3, 0x00FF, 0x007E, 0x0000, 0x0000,
7, 0, 0, 0,0x0000, 0x0000, 0x003F, 0x007F, 0x0063, 0x0063, 0x007F, 0x003F, 0x0003, 0x0003, 0x0003, 0x0003,
7, 0, 0, 0,0x0000, 0x0000, 0x007E, 0x007F, 0x0063, 0x0063, 0x007F, 0x007E, 0x0060, 0x0060, 0x0060, 0x0060,
7, 0, 0, 0,0x0000, 0x0000, 0x003B, 0x007F, 0x0067, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0000, 0x0000,
8, 0, 0, 0,0x0000, 0x0000, 0x007E, 0x007F, 0x0003, 0x007F, 0x00FE, 0x00C0, 0x00FE, 0x007E, 0x0000, 0x0000,
6, 0, 0, 0,0x0006, 0x0006, 0x003F, 0x003F, 0x0006, 0x0006, 0x0006, 0x0006, 0x003E, 0x003C, 0x0000, 0x0000,
7, 0, 0, 0,0x0000, 0x0000, 0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x0063, 0x007F, 0x007E, 0x0000, 0x0000,
10, 0, 0, 0,0x0000, 0x0000, 0x0303, 0x0303, 0x0303, 0x0387, 0x01CE, 0x00FC, 0x0078, 0x0030, 0x0000, 0x0000,
10, 0, 0, 0,0x0000, 0x0000, 0x0303, 0x0303, 0x0333, 0x037B, 0x03FF, 0x03CF, 0x0387, 0x0303, 0x0000, 0x0000,
8, 0, 0, 0,0x0000, 0x0000, 0x00C3, 0x00E7, 0x007E, 0x003C, 0x003C, 0x007E, 0x00E7, 0x00C3, 0x0000, 0x0000,
10, 0, 0, 0,0x0000, 0x0000, 0x0303, 0x0307, 0x038E, 0x01DC, 0x00F8, 0x0070, 0x0038, 0x001C, 0x000E, 0x0006,
8, 0, 0, 0,0x0000, 0x0000, 0x00FF, 0x00FF, 0x0070, 0x0038, 0x001C, 0x000E, 0x00FF, 0x00FF, 0x0000, 0x0000,
6, 0, 0, 0,0x0038, 0x003C, 0x000C, 0x000C, 0x000F, 0x000F, 0x000C, 0x000C, 0x003C, 0x0038, 0x0000, 0x0000,
2, 0, 0, 0,0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0003, 0x0000, 0x0000,
6, 0, 0, 0,0x0007, 0x000F, 0x000C, 0x000C, 0x003C, 0x003C, 0x000C, 0x000C, 0x000F, 0x0007, 0x0000, 0x0000,
10, 0, 0, 0,0x0000, 0x0000, 0x0000, 0x031C, 0x03BE, 0x01F7, 0x00E3, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
11, 0, 0, 0,0x0555, 0x0000, 0x0401, 0x0000, 0x0401, 0x0000, 0x0401, 0x0000, 0x0401, 0x0000, 0x0555, 0x0000,
};
#endif

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,185 +0,0 @@
# libui: a portable GUI library for C
This README is being written.<br>
[![Build Status](https://travis-ci.org/andlabs/libui.svg)](https://travis-ci.org/andlabs/libui)
## Announcements
* **27 November 2016**
* Decided to split the table stuff into its own branch. It will be developed independently of everything else, along with a few other features.
* **2 November 2016**
* Added two new functions to replace the deleted `uiWindowPosition()` and friends: `uiAreaBeginUserWindowMove()` and `uiAreaBeginUserWindowResize()`. When used in a `uiAreaHandler.Mouse()` event handler, these let you initiate a user-driven mouse move or mouse resize of the window at any point in a uiArea.
* **31 October 2016**
* @krakjoe noticed that I accidentally used thread-unsafe code in uiQueueMain() on Unix. Fixed.
* **24 October 2016**
* `uiWindowSetContentSize()` on Unix no longer needs to call up the GTK+ main loop. As a result, bugs related to strange behavior using that function (and the now-deleted `uiWindowSetPosition()` and `uiWindowCenter()`) should go away. I'll need to go through the bugs to verify as much, though.
* **22 October 2016**
* Due to being unable to guarantee they will work (especially as we move toward capability-driven window systems like Wayland), or being unable to work without hacking that breaks other things, the following functions have been removed: `uiWindowPosition()`, `uiWindowSetPosition()`, `uiWindowCenter()`, and `uiWindowOnPositionChanged()`. Centering may come back at some point in the future, albeit in a possibly restricted form. A function to initiate a user move when a part of a uiArea is clicked will be provided soon.
* **21 October 2016**
* `uiDrawTextWeightUltraBold` is now spelled correctly. Thanks to @krakjoe.
* **18 June 2016**
* Help decide [the design of tables and trees in libui](https://github.com/andlabs/libui/issues/159); the implementation starts within the next few days, if not tomorrow!
* **17 June 2016**
* **CMake 3.1.0 is now required.** This is due to CMake's rapid development pace in the past few years adding things libui needs to build on as many systems as possible. If your OS is supported by libui but its repositories ship with an older version of CMake, you will need to find an updated one somewhere.
* Please help [plan out a better menu API](https://github.com/andlabs/libui/issues/152).
* **5 June 2016**
* **Alpha 3.1 is here.** This was a much-needed update to Alpha 3 that changes a few things:
* **The build system is now cmake.** cmake 2.8.11 or higher is needed.
* Static linking is now fully possible.
* MinGW linking is back, but static only.
*Old announcements can be found in the ANNOUNCE.md file.*
## Updates
*Note that today's entry (Eastern Time) may be updated later today.*
* **<codedate**
* Added `uiTable` TODO
* **17 June 2016**
* `uiMainSteps()` no longer takes any arguments and no longer needs to invoke a function to do the work. You still need to call it, but once you do, it will return immediately and you can then get right to your main loop.
* **CMake 3.1.0 is now required.** This is due to CMake's rapid development pace in the past few years adding things libui needs to build on as many systems as possible. If your OS is supported by libui but its repositories ship with an older version of CMake, you will need to find an updated one somewhere.
* Added `uiNewVerticalSeparator()` to complement `uiNewHorizontalSeparator()`.
* **16 June 2016**
* Added `uiWindowContentSize()`, `uiWindowSetContentSize()`, and `uiWindowOnContentSizeChanged()` methods for manipulating uiWindow content sizes. Note the use of "content size"; the size you work with does NOT include window decorations (titlebars, menus, etc.).
* Added `uiWindowFullscreen()` and `uiWindowSetFullscreen()` to allow making fullscreen uiWindows, taking advantage of OS facilities for fullscreen and without changing the screen resolution (!).
* Added `uiWindowBorderless()` and `uiWindowSetBorderless()` for allowing borderless uiWindows.
* Added `uiMainSteps()`. You call this instead of `uiMain()` if you want to run the main loop yourself. You pass in a function that will be called; within that function, you call `uiMainStep()` repeatedly until it returns 0, doing whatever you need to do in the meantime. (This was needed because just having `uiMainStep()` by itself only worked on some systems.)
* Added `uiProgressBarValue()` and allowed passing -1 to `uiProgressBarSetValue()` to make an indeterminate progress bar. Thanks to @emersion.
* **15 June 2016**
* Added `uiFormDelete()`; thanks to @emersion.
* Added `uiWindowPosition()`, `uiWindowSetPosition()`, `uiWindowCenter()`, and `uiWindowOnPositionChanged()`, methods for manipulating uiWindow position.
* **14 June 2016**
* uiDarwinControl now has a `ChildVisibilityChanged()` method and a corresponding `NotifyVisibilityChanged()` function that is called by the default show/hide handlers. This is used to make visibility changes work on OS X; uiBox, uiForm, and uiGrid all respect these now.
* The same has been done on the Windows side as well.
* Hiding and showing controls and padding calculations are now correct on Windows at long last.
* Hiding a control in a uiForm now hides its label on all platforms.
* **13 June 2016**
* `intmax_t` and `uintmax_t` are no longer used for libui API functions; now we use `int`. This should make things much easier for bindings. `int` should be at least 32 bits wide; this should be sufficient for all but the most extreme cases.
* **12 June 2016**
* Added `uiGrid`, a new container control that arranges controls in rows and columns, with stretchy ("expanding") rows, stretchy ("expanding") columns, cells that span rows and columns, and cells whose content is aligned in either direction rather than just filling. It's quite powerful, is it? =P
* **8 June 2016**
* Added `uiForm`, a new container control that arranges controls vertically, with properly aligned labels on each. Have fun!
* **6 June 2016**
* Added `uiRadioButtonsSelected()`, `uiRadioButtonsSetSelected()`, and `uiRadioButtonsOnSelected()` to control selection of a radio button and catch an event when such a thing happens.
* **5 June 2016**
* Added `uiNewPasswordEntry()`, which creates a new `uiEntry` suitable for entering passwords.
* Added `uiNewSearchEntry()`, which creates a new `uiEntry` suitable for searching. On some systems, the `OnChanged()` event will be slightly delayed and/or combined, to produce a more natural feel when searching.
*Old updates can be found in the Changelog.md file.*
## Runtime Requirements
* Windows: Windows Vista SP2 with Platform Update or newer
* Unix: GTK+ 3.10 or newer
* Mac OS X: OS X 10.8 or newer
## Build Requirements
* All platforms:
* CMake 3.1.0 or newer
* Windows: either
* Microsoft Visual Studio 2013 or newer (2013 is needed for `va_copy()`) — you can build either a static or a shared library
* MinGW-w64 (other flavors of MinGW may not work) — **you can only build a static library**; shared library support will be re-added once the following features come in:
* [Isolation awareness](https://msdn.microsoft.com/en-us/library/aa375197%28v=vs.85%29.aspx), which is how you get themed controls from a DLL without needing a manifest
* Unix: nothing else specific
* Mac OS X: nothing else specific, so long as you can build Cocoa programs
## Building
Out-of-tree builds typical of cmake are preferred:
```
$ # you must be in the top-level libui directory, otherwise this won't work
$ mkdir build
$ cd build
$ cmake ..
```
Pass `-DBUILD_SHARED_LIBS=OFF` to `cmake` to build a static library. The standard cmake build configurations are provided; if none is specified, `Debug` is used.
If you use a makefile generator with cmake, then
```
$ make
$ make tester # for the test program
$ make examples # for examples
```
and pass `VERBOSE=1` to see build commands. Build targets will be in the `build/out` folder.
Project file generators should work, but are untested by me.
On Windows, I use the `Unix Makefiles` generator and GNU make (built using the `build_w32.bat` script included in the source and run in the Visual Studio command line). In this state, if MinGW-w64 (either 32-bit or 64-bit) is not in your `%PATH%`, cmake will use MSVC by default; otherwise, cmake will use with whatever MinGW-w64 is in your path. `set PATH=%PATH%;c:\msys2\mingw(32/64)\bin` should be enough to temporarily change to a MinGW-w64 build for the current command line session only if you installed MinGW-w64 through [MSYS2](https://msys2.github.io/); no need to change global environment variables constantly.
## Installation
#### Arch Linux
Can be built from AUR: https://aur.archlinux.org/packages/libui-git/
## Documentation
Needs to be written. Consult `ui.h` and the examples for details for now.
## Language Bindings
libui was originally written as part of my [package ui for Go](https://github.com/andlabs/ui). Now that libui is separate, package ui has become a binding to libui. As such, package ui is the only official binding.
Other people have made bindings to other languages:
Language | Bindings
--- | ---
C#/.net | [LibUI.Binding](https://github.com/NattyNarwhal/LibUI.Binding), [SharpUI](https://github.com/benpye/sharpui/)
CHICKEN Scheme | [wasamasa/libui](https://github.com/wasamasa/libui)
Crystal | [libui.cr](https://github.com/Fusion/libui.cr)
D | [DerelictLibui (flat API)](https://github.com/Extrawurst/DerelictLibui), [libuid (object-oriented)](https://github.com/mogud/libuid)
Euphoria | [libui-euphoria](https://github.com/ghaberek/libui-euphoria)
Harbour | [HBUI](https://github.com/RJopek/HBUI)
Haskell | [libui-haskell](https://github.com/ajnsit/libui-haskell), [beijaflor-io/haskell-libui (complete FFI bindings, extensions and higher-level API)](https://github.com/beijaflor-io/haskell-libui)
JavaScript | [libui.js (merged into libui-node?)](https://github.com/mavenave/libui.js)
Julia | [Libui.jl](https://github.com/joa-quim/Libui.jl)
Lua | [libuilua](https://github.com/zevv/libuilua), [libui-lua](https://github.com/mdombroski/libui-lua)
Nim | [ui](https://github.com/nim-lang/ui)
Node.js | [libui-node](https://github.com/parro-it/libui-node)
PHP | [ui](https://github.com/krakjoe/ui)
Python | [pylibui](https://github.com/joaoventura/pylibui)
Ruby | [libui-ruby](https://github.com/jamescook/libui-ruby)
Rust | [libui-rs](https://github.com/pcwalton/libui-rs)
Swift | [libui-swift](https://github.com/sclukey/libui-swift)
## Frequently Asked Questions
### Why does my program start in the background on OS X if I run from the command line?
OS X normally does not start program executables directly; instead, it uses [Launch Services](https://developer.apple.com/reference/coreservices/1658613-launch_services?language=objc) to coordinate the launching of the program between the various parts of the system and the loading of info from an .app bundle. One of these coordination tasks is responsible for bringing a newly launched app into the foreground. This is called "activation".
When you run a binary directly from the Terminal, however, you are running it directly, not through Launch Services. Therefore, the program starts in the background, because no one told it to activate! Now, it turns out [there is an API](https://developer.apple.com/reference/appkit/nsapplication/1428468-activateignoringotherapps) that we can use to force our app to be activated. But if we use it, then we'd be trampling over Launch Services, which already knows whether it should activate or not. Therefore, libui does not step over Launch Services, at the cost of requiring an extra user step if running directly from the command line.
See also [this](https://github.com/andlabs/libui/pull/20#issuecomment-211381971) and [this](http://stackoverflow.com/questions/25318524/what-exactly-should-i-pass-to-nsapp-activateignoringotherapps-to-get-my-appl).
## Screenshots
From examples/controlgallery:
![Windows](examples/controlgallery/windows.png)
![Unix](examples/controlgallery/unix.png)
![OS X](examples/controlgallery/darwin.png)

View File

@ -1,129 +0,0 @@
- make sure the last line of text layouts include leading
- documentation notes:
- static binaries do not link system libraries, meaning apps still depend on shared GTK+, etc.
- ui*Buttons are NOT compatible with uiButton functions
- more robust layout handling
- uiFormTie() for ensuring multiple uiForms have the same label area widths
- uiSizeGroup for size groups (GtkSizeGroup on GTK+, auto layout constraints on OS X; consider adding after 10.8 is gone)
- windows: should the initial hwndInsertAfter be HWND_BOTTOM for what we want?
- windows: document the rules for controls and containers
- windows: document the minimum size change propagation system
- provisions for controls that cannot be grown? especiailly for windows
- LC_VERSION_MIN_MACOSX has the 10.11 SDK; see if we can knock it down to 10.8 too; OS version is fine
- apply the OS version stuff to the test program and examples too
- what about micro versions (10.8.x)? force 10.8.0?
- go through ALL the objective-c objects we create and make sure we are using the proper ownership (alloc/init and new are owned by us, all class method constructors are autoreleased - thanks mikeash)
- on OS X, edit shortcuts like command-C working require that the menu entries be defined, or so it seems, even for NSAlert
- other platforms?
- make sure all OS X event handlers that use target-action set the action to NULL when the target is unset
- provide a way to get the currently selected uiTab page? set?
- make it so that the windows cntrols only register a resize if their new minimum size is larger than their current size to easen the effect of flicker
- it won't remove that outright, but it'll help
- add an option to the test program to run page7b as an independent test in its own window
- same for page7c
- http://blogs.msdn.com/b/oldnewthing/archive/2004/01/12/57833.aspx provide a DEF file on Windows
- all ports: update state when adding a control to a parent
- should uiWindowsSizing be computed against the window handle, not the parent?
- DPI awareness on windows
- http://stackoverflow.com/questions/4543087/applicationwillterminate-and-the-dock-but-wanting-to-cancel-this-action
ultimately:
- MAYBE readd lifetime handling/destruction blocking
- related? [12:25] <ZeroOne> And the blue outline on those buttons [ALL clicked buttons on Windows 7] won't go away
- I get this too
- not anymore
- SWP_NOCOPYBITS to avoid button redraw issues on Windows when not in tab, but only when making resize faster
- secondary side alignment control in uiBox
- Windows: don't abort if a cleanup function fails?
- 32-bit Mac OS X support (requires lots of code changes)
- change the build system to be more receptive to arch changes
notes to self
- explicitly document label position at top-left corner
- explicitly document that if number of radio buttons >= 1 there will always be a selection
- mark that uiControlShow() on a uiWindow() will bring to front and give keyboard focus because of OS X
- make sure ShowWindow() is sufficient for zorder on Windows
- document that you CAN use InsertAt functions to insert at the first invalid index, even if the array is empty
- note that uiTabInsertAt() does NOT change the current tab page (it may change its index if inserting before the current page)
- note that the default action for uiWindowOnClosing() is to return 0 (keep the window open)
- note that uiInitOptions should be initialized to zero
- explicitly document that uiCheckboxSetChecked() and uiEntrySetText() do not fire uiCheckboxOnToggled() and uiEntryOnChanged(), respectively
- note that if a menu is requested on systems with menubars on windows but no menus are defined, the result is a blank menubar, with whatever that means left up to the OS to decide
- note that handling of multiple consecutive separators in menus, leading separators in menus, and trailing separators in menus are all OS-defined
- note that uiDrawMatrixInvert() does not change the matrix if it fails
- note that the use of strings that are not strictly valid UTF-8 results in undefined behavior
- test RTL
- automate RTL
- now that stock items are deprecated, I have to maintain translations of the Cancel, Open, and Save buttons on GTK+ myself (thanks baedert in irc.gimp.net/#gtk+)
- either that or keep using stock items
- http://blogs.msdn.com/b/oldnewthing/archive/2014/02/26/10503148.aspx
- build optimizations
- use http://www.appveyor.com/ to do Windows build CI since people want CI
- consider just having the windows backend in C++
- consider having it all in C++
don't forget LONGTERMs as well
notes
- http://blogs.msdn.com/b/oldnewthing/archive/2004/03/29/101121.aspx on accelerators
- group and tab should act as if they have no child if the child is hidden
on windows
- a way to do recursive main loops
- how do we handle 0 returns from non-recursive uiMainStep() calls that aren't the main loop? (event handlers, for instance)
- should repeated calls to uiMainStep() after uiQuit() return 0 reliably? this will be needed for non-recursive loops
http://stackoverflow.com/questions/38338426/meaning-of-ampersand-in-rc-files/38338841?noredirect=1#comment64093084_38338841
label shortcut keys
- remove whining from source code
[01:41:47] <vrishab> Hi. does pango support "fgalpha". I see that foreground="112233xx" works ( alpha=xx ), but fgalpha is a no-op
[01:52:29] <vrishab> pango_attr_foreground_alpha_new (32767) seems to be called in either case, but only the "foreground" attr works
[01:56:09] lolek (lolek@ip-91-244-230-76.simant.pl) joined the channel
[01:57:48] <vrishab> ok. seems like "foreground" is mandatory attr, 1. "foreground-without-alpha" + "alpha" works 2. "foreground-with-alpha" works. 3. "alpha" alone doesn
[01:57:52] <vrishab> 't work
[01:58:29] <vrishab> Is there a way to just specify alpha on the current foreground color ?
[02:00:23] lolek (lolek@ip-91-244-230-76.simant.pl) left the channel
[02:07:41] mjog (mjog@uniwide-pat-pool-129-94-8-98.gw.unsw.edu.au) left IRC (Quit: mjog)
[02:08:10] seb128 (seb128@53542B83.cm-6-5a.dynamic.ziggo.nl) joined the channel
[02:12:37] <andlabs> huh
[02:12:41] <andlabs> what version of pango?
[02:13:05] <vrishab> the latest .
[02:15:00] <vrishab> 1.40.3
[02:20:46] <andlabs> I'll ahve to keep this in mind then, thanks
[02:20:59] <andlabs> if only there was a cairo-specific attribute for alpha...

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +0,0 @@
every rule in ui_darwin.h
SetParent must be followed by SetSuperview and SyncEnableState
TODO can child cache it?
adding a child must be followed by a call to SyncEnableState
SyncEnableState() must call ShouldStopSyncEnableState() first thing
Enable() and Disable() must call SyncEnableState() AFTER CHANGING WHAT Enabled() WILL RETURN

View File

@ -1,3 +0,0 @@
every rule in ui_unix.h
SetParent must be followed by SetContainer
TODO can child cache it?

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,80 +0,0 @@
// 18 october 2015
#include "test.h"
// TODO manage the memory of the uiTableModel
static intmax_t nColumns = 4;
static uiTableColumnType coltypes[] = {
uiTableColumnText,
uiTableColumnText,
uiTableColumnCheckbox,
uiTableColumnCheckbox,
};
static intmax_t nRows = 6;
static intmax_t modelNumRows(uiTableModel *m, void *mData)
{
return nRows;
}
void *modelCellValue(uiTableModel *m, void *mData, intmax_t row, intmax_t column)
{
char line[20];
line[0] = 'R';
line[1] = 'o';
line[2] = 'w';
line[3] = ' ';
line[4] = row + '0';
line[5] = '\0';
switch (column) {
case 0:
case 1:
return uiTableModelFromString(line);
case 2:
return uiTableModelFromBool(row % 2 == 0);
case 3:
return uiTableModelFromBool(row % 3 == 0);
}
// TODO
return NULL;
}
// TODO make this not need to be static
uiTableModelSpec spec;
void modelSetCellValue(uiTableModel *m, void *mData, intmax_t row, intmax_t column, void *value)
{
// TODO
}
uiBox *makePage9(void)
{
uiBox *page9;
uiTable *table;
uiTableModel *model;
uiTableColumnParams p;
intmax_t i;
page9 = newVerticalBox();
table = uiNewTable();
uiBoxAppend(page9, uiControl(table), 1);
spec.NumRows = modelNumRows;
spec.CellValue = modelCellValue;
spec.SetCellValue = modelSetCellValue;
model = uiNewTableModel(nColumns, coltypes, &spec, NULL);
uiTableSetModel(table, model);
for (i = 0; i < nColumns; i++) {
p.Name = "Column";
p.Type = coltypes[i];
p.Mutable = i % 2 == 1;
p.ValueColumn = i;
uiTableAppendColumn(table, &p);
}
return page9;
}

View File

@ -1,46 +0,0 @@
typedef struct uiTable uiTable;
typedef struct uiTableModel uiTableModel;
typedef struct uiTableModelSpec uiTableModelSpec;
typedef struct uiTableColumnParams uiTableColumnParams;
typedef enum uiTableColumnType uiTableColumnType;
typedef enum uiTableNotification uiTableNotification;
_UI_EXTERN uintmax_t uiTableType(void);
#define uiTable(this) ((uiTable *) uiIsA((this), uiTableType(), 1))
_UI_EXTERN void uiTableSetModel(uiTable *t, uiTableModel *m);
_UI_EXTERN void uiTableAppendColumn(uiTable *t, uiTableColumnParams *p);
_UI_EXTERN uiTable *uiNewTable(void);
enum uiTableColumnType {
uiTableColumnText,
//TODO uiTableColumnImage,
uiTableColumnCheckbox,
};
struct uiTableModelSpec {
intmax_t (*NumRows)(uiTableModel *m, void *mData);
void *(*CellValue)(uiTableModel *m, void *mData, intmax_t row, intmax_t column);
void (*SetCellValue)(uiTableModel *m, void *mData, intmax_t row, intmax_t column, void *value);
};
enum uiTableNotification {
uiTableRowInserted,
uiTableRowDeleted,
uiTableCellChanged,
};
_UI_EXTERN uiTableModel *uiNewTableModel(uintmax_t nCols, uiTableColumnType *types, uiTableModelSpec *spec, void *mData);
_UI_EXTERN void uiFreeTableModel(uiTableModel *m);
_UI_EXTERN void uiTableModelNotify(uiTableModel *m, uiTableNotification notification, intmax_t row, intmax_t column);
#define uiTableModelFromBool(b) ((void *) ((intptr_t) (b)))
_UI_EXTERN void *uiTableModelFromString(const char *str);
struct uiTableColumnParams {
const char *Name;
// TODO make this unnecessary
uiTableColumnType Type;
int Mutable; // TODO move to the model?
intmax_t ValueColumn;
// TODO background color
};

View File

@ -1,87 +0,0 @@
// 18 october 2015
#include "uipriv_unix.h"
struct uiTable {
uiUnixControl c;
GtkWidget *widget;
GtkContainer *scontainer;
GtkScrolledWindow *sw;
GtkWidget *treeWidget;
GtkTreeView *treeview;
GtkTreeSelection *selection;
uiTableModel *model;
};
uiUnixDefineControl(
uiTable // type name
)
void uiTableSetModel(uiTable *t, uiTableModel *m)
{
t->model = m;
gtk_tree_view_set_model(t->treeview, GTK_TREE_MODEL(t->model));
}
void uiTableAppendColumn(uiTable *t, uiTableColumnParams *p)
{
GtkTreeViewColumn *col;
GtkCellRenderer *r;
const char *attribute;
const char *mutableAttr;
gboolean mutable;
switch (p->Type) {
case uiTableColumnText:
r = gtk_cell_renderer_text_new();
attribute = "text";
mutableAttr = "editable";
break;
//TODO case uiTableColumnImage:
// TODO
case uiTableColumnCheckbox:
r = gtk_cell_renderer_toggle_new();
attribute = "active";
mutableAttr = "activatable";
break;
default:
complain("unknown table column type %d in uiTableAppendColumn()", p->Type);
}
mutable = FALSE;
if (p->Mutable)
mutable = TRUE;
g_object_set(r,
mutableAttr, mutable,
NULL);
col = gtk_tree_view_column_new_with_attributes(p->Name, r,
attribute, p->ValueColumn,
NULL);
// allow columns to be resized
gtk_tree_view_column_set_resizable(col, TRUE);
gtk_tree_view_append_column(t->treeview, col);
}
uiTable *uiNewTable(void)
{
uiTable *t;
t = (uiTable *) uiNewControl(uiTableType());
t->widget = gtk_scrolled_window_new(NULL, NULL);
t->scontainer = GTK_CONTAINER(t->widget);
t->sw = GTK_SCROLLED_WINDOW(t->widget);
t->treeWidget = gtk_tree_view_new();
t->treeview = GTK_TREE_VIEW(t->treeWidget);
t->selection = gtk_tree_view_get_selection(t->treeview);
// give a border and add the table
gtk_scrolled_window_set_shadow_type(t->sw, GTK_SHADOW_IN);
gtk_container_add(t->scontainer, t->treeWidget);
// and make the table visible; only the scrolled window's visibility is controlled by libui
gtk_widget_show(t->treeWidget);
uiUnixFinishNewControl(t, uiTable);
return t;
}

View File

@ -1,303 +0,0 @@
// 18 october 2015
#include "uipriv_unix.h"
// On GTK+, uiTableModel is a GtkTreeModel.
#define uiTableModelType (uiTableModel_get_type())
#define uiTableModel(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), uiTableModelType, uiTableModel))
#define isAreaWidget(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), uiTableModelType))
#define uiTableModelClass(class) (G_TYPE_CHECK_CLASS_CAST((class), uiTableModelType, uiTableModelClass))
#define isAreaWidgetClass(class) (G_TYPE_CHECK_CLASS_TYPE((class), uiTableModel))
#define getAreaWidgetClass(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), uiTableModelType, uiTableModelClass))
typedef struct uiTableModel uiTableModel;
typedef struct uiTableModelClass uiTableModelClass;
struct uiTableModel {
GObject parent_instance;
uiTableModelSpec *spec;
void *mData;
intmax_t nColumns;
GType *coltypes;
};
struct uiTableModelClass {
GObjectClass parent_class;
};
static void uiTableModel_treeModel_init(GtkTreeModelIface *);
G_DEFINE_TYPE_WITH_CODE(uiTableModel, uiTableModel, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE(GTK_TYPE_TREE_MODEL, uiTableModel_treeModel_init))
static void uiTableModel_init(uiTableModel *m)
{
// do nothing
}
static void uiTableModel_dispose(GObject *obj)
{
G_OBJECT_CLASS(uiTableModel_parent_class)->dispose(obj);
}
static void uiTableModel_finalize(GObject *obj)
{
uiTableModel *m = uiTableModel(obj);
uiFree(m->coltypes);
G_OBJECT_CLASS(uiTableModel_parent_class)->finalize(obj);
}
static GtkTreeModelFlags uiTableModel_get_flags(GtkTreeModel *mb)
{
return GTK_TREE_MODEL_LIST_ONLY;
}
static gint uiTableModel_get_n_columns(GtkTreeModel *mb)
{
uiTableModel *m = uiTableModel(mb);
return m->nColumns;
}
static GType uiTableModel_get_column_type(GtkTreeModel *mb, gint index)
{
uiTableModel *m = uiTableModel(mb);
return m->coltypes[index];
}
/*
how our GtkTreeIters are stored:
stamp: either GOOD_STAMP or BAD_STAMP
user_data: row index
Thanks to Company in irc.gimp.net/#gtk+ for suggesting the GSIZE_TO_POINTER() t
rick.
*/
#define GOOD_STAMP 0x1234
#define BAD_STAMP 0x5678
#define FROM(x) ((gint) GPOINTER_TO_SIZE((x)))
#define TO(x) GSIZE_TO_POINTER((gsize) (x))
#define numRows(m) ((*((m)->spec->NumRows))((m), (m)->mData))
#define cellValue(m, row, col) ((*((m)->spec->CellValue))((m), (m)->mData, row, column))
static gboolean uiTableModel_get_iter(GtkTreeModel *mb, GtkTreeIter *iter, GtkTreePath *path)
{
uiTableModel *m = uiTableModel(mb);
gint index;
if (gtk_tree_path_get_depth(path) != 1)
goto bad;
index = gtk_tree_path_get_indices(path)[0];
if (index < 0)
goto bad;
if (index >= numRows(m))
goto bad;
iter->stamp = GOOD_STAMP;
iter->user_data = TO(index);
return TRUE;
bad:
iter->stamp = BAD_STAMP;
return FALSE;
}
static GtkTreePath *uiTableModel_get_path(GtkTreeModel *mb, GtkTreeIter *iter)
{
// note: from this point forward, the GOOD_STAMP checks ensure that the index stored in iter is nonnegative
if (iter->stamp != GOOD_STAMP)
return NULL; // this is what both GtkListStore and GtkTreeStore do
return gtk_tree_path_new_from_indices(FROM(iter->user_data), -1);
}
void *uiTableModelFromString(const char *str)
{
return g_strdup(str);
}
#define toBool(v) ((int) ((intptr_t) (v)))
#define toStr(v) ((char *) (v))
static void uiTableModel_get_value(GtkTreeModel *mb, GtkTreeIter *iter, gint column, GValue *value)
{
uiTableModel *m = uiTableModel(mb);
void *v;
GType type;
if (iter->stamp != GOOD_STAMP)
return; // this is what both GtkListStore and GtkTreeStore do
v = cellValue(m, FROM(iter->user_data), column);
type = m->coltypes[column];
g_value_init(value, type);
if (type == G_TYPE_STRING)
g_value_take_string(value, toStr(v));
// the GValue now manages the memory of the string that was g_strdup()'d before
// TODO image
else if (type == G_TYPE_BOOLEAN)
g_value_set_boolean(value, toBool(v));
else
complain("unknown GType in uiTableModel_get_value()");
}
static gboolean uiTableModel_iter_next(GtkTreeModel *mb, GtkTreeIter *iter)
{
uiTableModel *m = uiTableModel(mb);
gint index;
if (iter->stamp != GOOD_STAMP)
return FALSE; // this is what both GtkListStore and GtkTreeStore do
index = FROM(iter->user_data);
index++;
if (index >= numRows(m)) {
iter->stamp = BAD_STAMP;
return FALSE;
}
iter->user_data = TO(index);
return TRUE;
}
static gboolean uiTableModel_iter_previous(GtkTreeModel *mb, GtkTreeIter *iter)
{
uiTableModel *m = uiTableModel(mb);
gint index;
if (iter->stamp != GOOD_STAMP)
return FALSE; // this is what both GtkListStore and GtkTreeStore do
index = FROM(iter->user_data);
if (index <= 0) {
iter->stamp = BAD_STAMP;
return FALSE;
}
index--;
iter->user_data = TO(index);
return TRUE;
}
static gboolean uiTableModel_iter_children(GtkTreeModel *mb, GtkTreeIter *iter, GtkTreeIter *parent)
{
uiTableModel *m = uiTableModel(mb);
if (parent == NULL && numRows(m) > 0) {
iter->stamp = GOOD_STAMP;
iter->user_data = 0;
return TRUE;
}
iter->stamp = BAD_STAMP;
return FALSE;
}
static gboolean uiTableModel_iter_has_child(GtkTreeModel *mb, GtkTreeIter *iter)
{
return FALSE;
}
static gint uiTableModel_iter_n_children(GtkTreeModel *mb, GtkTreeIter *iter)
{
uiTableModel *m = uiTableModel(mb);
if (iter == NULL)
return numRows(m);
return 0;
}
static gboolean uiTableModel_iter_nth_child(GtkTreeModel *mb, GtkTreeIter *iter, GtkTreeIter *parent, gint n)
{
uiTableModel *m = uiTableModel(mb);
if (parent == NULL && n >= 0 && n < numRows(m)) {
iter->stamp = GOOD_STAMP;
iter->user_data = TO(n);
return TRUE;
}
iter->stamp = BAD_STAMP;
return FALSE;
}
static gboolean uiTableModel_iter_parent(GtkTreeModel *mb, GtkTreeIter *iter, GtkTreeIter *child)
{
iter->stamp = BAD_STAMP;
return FALSE;
}
static void uiTableModel_class_init(uiTableModelClass *class)
{
G_OBJECT_CLASS(class)->dispose = uiTableModel_dispose;
G_OBJECT_CLASS(class)->finalize = uiTableModel_finalize;
}
static void uiTableModel_treeModel_init(GtkTreeModelIface *iface)
{
iface->get_flags = uiTableModel_get_flags;
iface->get_n_columns = uiTableModel_get_n_columns;
iface->get_column_type = uiTableModel_get_column_type;
iface->get_iter = uiTableModel_get_iter;
iface->get_path = uiTableModel_get_path;
iface->get_value = uiTableModel_get_value;
iface->iter_next = uiTableModel_iter_next;
iface->iter_previous = uiTableModel_iter_previous;
iface->iter_children = uiTableModel_iter_children;
iface->iter_has_child = uiTableModel_iter_has_child;
iface->iter_n_children = uiTableModel_iter_n_children;
iface->iter_nth_child = uiTableModel_iter_nth_child;
iface->iter_parent = uiTableModel_iter_parent;
// no need for ref_node or unref_node
}
uiTableModel *uiNewTableModel(uintmax_t nCols, uiTableColumnType *types, uiTableModelSpec *spec, void *mData)
{
uiTableModel *m;
intmax_t i;
m = uiTableModel(g_object_new(uiTableModelType, NULL));
m->spec = spec;
m->mData = mData;
m->nColumns = nCols;
m->coltypes = (GType *) uiAlloc(m->nColumns * sizeof (GType), "GType[]");
for (i = 0; i < m->nColumns; i++)
switch (types[i]) {
case uiTableColumnText:
m->coltypes[i] = G_TYPE_STRING;
break;
//TODO case uiTableColumnImage:
// TODO
case uiTableColumnCheckbox:
m->coltypes[i] = G_TYPE_BOOLEAN;
break;
default:
complain("unknown column type %d in uiNewTableModel()", types[i]);
}
return m;
}
// TODO ensure no tables are subscribed
void uiFreeTableModel(uiTableModel *m)
{
g_object_unref(m);
}
void uiTableModelNotify(uiTableModel *m, uiTableNotification notification, intmax_t row, intmax_t column)
{
GtkTreeModel *model = GTK_TREE_MODEL(m);
GtkTreePath *path;
GtkTreeIter iter;
path = gtk_tree_path_new_from_indices(row, -1);
switch (notification) {
case uiTableRowInserted:
if (gtk_tree_model_get_iter(model, &iter, path) == FALSE)
complain("invalid row given to row inserted in uiTableModelNotify()");
gtk_tree_model_row_inserted(model, path, &iter);
break;
case uiTableRowDeleted:
gtk_tree_model_row_deleted(model, path);
break;
case uiTableCellChanged:
if (gtk_tree_model_get_iter(model, &iter, path) == FALSE)
complain("invalid row given to row changed in uiTableModelNotify()");
gtk_tree_model_row_changed(model, path, &iter);
break;
default:
complain("unknown uiTable notification %d in uiTableModelNotify()", notification);
}
gtk_tree_path_free(path);
}

View File

@ -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)

View File

@ -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;
}

View File

@ -1,117 +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);
}
void uiControlSetFocus(uiControl *c)
{
(*(c->SetFocus))(c);
}
void uiControlSetMinSize(uiControl *c, int w, int h)
{
c->MinWidth = w;
c->MinHeight = h;
(*(c->SetMinSize))(c, w, h);
}
#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;
c->MinWidth = -1;
c->MinHeight = -1;
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;
}

View File

@ -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

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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

View File

@ -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);
}

View File

@ -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)

View File

@ -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]];
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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];
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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));
}

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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?
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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];
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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, const char* filter, const char* initpath)
{
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, const char* filter, const char* initpath)
{
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);
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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];
}

View File

@ -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];
}

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