Update to v106r48 release.

byuu says:

The problems with the Windows and Qt4 ports have all been resolved,
although there's a fairly gross hack on a few Qt widgets to not destruct
once Application::quit() is called to avoid a double free crash (I'm
unsure where Qt is destructing the widgets internally.) The Cocoa port
compiles again at least, though it's bound to have endless problems. I
improved the Label painting in the GTK ports, which fixes the background
color on labels inside TabFrame widgets.

I've optimized the Makefile system even further.

I added a "redo state" command to bsnes, which is created whenever you
load the undo state. There are also hotkeys for both now, although I
don't think they're really something you want to map hotkeys to.

I moved the nall::Locale object inside hiro::Application, so that it can
be used to translate the BrowserDialog and MessageDialog window strings.

I improved the Super Game Boy emulation of `MLT_REQ`, fixing Pokemon
Yellow's custom border and probably more stuff.

Lots of other small fixes and improvements. Things are finally stable
once again after the harrowing layout redesign catastrophe.

Errata:

  - ICD::joypID should be set to 3 on reset(). joypWrite() may as well
    take uint1 instead of bool.
  - hiro/Qt: remove pWindow::setMaximumSize() comment; found a
    workaround for it
  - nall/GNUmakefile: don't set object.path if it's already set (allow
    overrides before including the file)
This commit is contained in:
Tim Allen 2018-07-16 16:16:26 +10:00
parent 6090c63958
commit 393c2395bb
78 changed files with 701 additions and 658 deletions

View File

@ -1,22 +1,21 @@
name := genius name := genius
build := stable build := stable
flags += -I..
nall.path := ../nall nall.path := ../nall
include $(nall.path)/GNUmakefile include $(nall.path)/GNUmakefile
object.path := obj
flags += -I..
hiro.path := ../hiro hiro.path := ../hiro
hiro.resource := data/$(name).rc hiro.resource := data/$(name).rc
include $(hiro.path)/GNUmakefile include $(hiro.path)/GNUmakefile
objects := obj/hiro.o $(if $(call streq,$(platform),windows),obj/hiro-resource.o) objects := obj/genius.o
objects += obj/genius.o
all: $(objects) obj/genius.o: genius.cpp
all: $(hiro.objects) $(objects)
$(info Linking out/$(name) ...) $(info Linking out/$(name) ...)
+@$(strip $(compiler) -o out/$(name) $(objects) $(options) $(hiro.options)) +@$(compiler) -o out/$(name) $(hiro.objects) $(objects) $(hiro.options) $(options)
ifeq ($(platform),macos) ifeq ($(platform),macos)
rm -rf out/$(name).app rm -rf out/$(name).app
mkdir -p out/$(name).app/Contents/MacOS/ mkdir -p out/$(name).app/Contents/MacOS/
@ -26,16 +25,14 @@ ifeq ($(platform),macos)
sips -s format icns data/$(name).png --out out/$(name).app/Contents/Resources/$(name).icns sips -s format icns data/$(name).png --out out/$(name).app/Contents/Resources/$(name).icns
endif endif
obj/genius.o: genius.cpp genius.hpp verbose: hiro.verbose nall.verbose all;
$(info Compiling $< ...)
@$(compiler) $(flags.cpp) $(flags) -o obj/genius.o -c genius.cpp
clean: clean:
ifeq ($(platform),macos) ifeq ($(platform),macos)
rm -rf out/$(name).app rm -rf out/$(name).app
endif endif
$(call rm,obj/*) $(call delete,obj/*)
$(call rm,out/*) $(call delete,out/*)
install: install:
ifeq ($(platform),macos) ifeq ($(platform),macos)

View File

@ -1,15 +1,12 @@
target := bsnes
binary := application
build := performance build := performance
openmp := true openmp := true
flags += -I. -I..
nall.path := ../nall nall.path := ../nall
include $(nall.path)/GNUmakefile include $(nall.path)/GNUmakefile
binary := application
target := bsnes
objects := libco emulator audio video resource
object.path := obj
flags += -I. -I..
ifeq ($(platform),windows) ifeq ($(platform),windows)
ifeq ($(binary),application) ifeq ($(binary),application)
link += -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32 link += -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32
@ -37,18 +34,7 @@ else
$(error "unsupported platform") $(error "unsupported platform")
endif endif
compile = \ objects := libco emulator audio video resource
$(strip \
$(if $(filter %.c,$<), \
$(compiler) $(flags.c) $(flags) $1 -c $< -o $@ -MMD -MP -MF $(@:.o=.d), \
$(if $(filter %.cpp,$<), \
$(compiler) $(flags.cpp) $(flags) $1 -c $< -o $@ -MMD -MP -MF $(@:.o=.d) \
)) \
)
%.o: $<
$(info Compiling $< ...)
@$(call compile)
obj/libco.o: ../libco/libco.c obj/libco.o: ../libco/libco.c
obj/emulator.o: emulator/emulator.cpp obj/emulator.o: emulator/emulator.cpp
@ -61,5 +47,5 @@ include $(ui)/GNUmakefile
-include obj/*.d -include obj/*.d
clean: clean:
$(call rm,out/*) $(call delete,out/*)
$(call rm,obj/*) $(call delete,obj/*)

View File

@ -13,7 +13,7 @@ using namespace nall;
namespace Emulator { namespace Emulator {
static const string Name = "higan"; static const string Name = "higan";
static const string Version = "106.47"; static const string Version = "106.48";
static const string Author = "byuu"; static const string Author = "byuu";
static const string License = "GPLv3"; static const string License = "GPLv3";
static const string Website = "https://byuu.org/"; static const string Website = "https://byuu.org/";

View File

@ -41,7 +41,6 @@ struct CPU : Processor::LR35902, Thread, MMIO {
bool p15 = 0; bool p15 = 0;
bool p14 = 0; bool p14 = 0;
uint8 joyp; uint8 joyp;
uint8 mltReq;
//$ff01 SB //$ff01 SB
uint8 serialData; uint8 serialData;

View File

@ -29,7 +29,9 @@ auto CPU::joypPoll() -> void {
} }
status.joyp = 0x0f; status.joyp = 0x0f;
if(status.p15 == 1 && status.p14 == 1) status.joyp -= status.mltReq; if(status.p15 == 1 && status.p14 == 1 && Model::SuperGameBoy()) {
status.joyp = superGameBoy->joypRead();
}
if(status.p15 == 0) status.joyp &= button ^ 0x0f; if(status.p15 == 0) status.joyp &= button ^ 0x0f;
if(status.p14 == 0) status.joyp &= dpad ^ 0x0f; if(status.p14 == 0) status.joyp &= dpad ^ 0x0f;
if(status.joyp != 0x0f) raise(Interrupt::Joypad); if(status.joyp != 0x0f) raise(Interrupt::Joypad);

View File

@ -10,7 +10,6 @@ auto CPU::serialize(serializer& s) -> void {
s.integer(status.p15); s.integer(status.p15);
s.integer(status.p14); s.integer(status.p14);
s.integer(status.joyp); s.integer(status.joyp);
s.integer(status.mltReq);
s.integer(status.serialData); s.integer(status.serialData);
s.integer(status.serialBits); s.integer(status.serialBits);

View File

@ -72,6 +72,7 @@ struct SuperGameBoyInterface {
virtual auto lcdScanline() -> void = 0; virtual auto lcdScanline() -> void = 0;
virtual auto lcdOutput(uint2 color) -> void = 0; virtual auto lcdOutput(uint2 color) -> void = 0;
virtual auto joypRead() -> uint4 = 0;
virtual auto joypWrite(bool p15, bool p14) -> void = 0; virtual auto joypWrite(bool p15, bool p14) -> void = 0;
}; };

View File

@ -91,7 +91,7 @@ auto ICD::reset() -> void {
writeAddress = 0; writeAddress = 0;
packetSize = 0; packetSize = 0;
joypID = 3; joypID = 0;
joyp15Lock = 0; joyp15Lock = 0;
joyp14Lock = 0; joyp14Lock = 0;
pulseLock = true; pulseLock = true;

View File

@ -18,6 +18,7 @@ struct ICD : Emulator::Platform, GameBoy::SuperGameBoyInterface, Thread {
//interface.cpp //interface.cpp
auto lcdScanline() -> void override; auto lcdScanline() -> void override;
auto lcdOutput(uint2 color) -> void override; auto lcdOutput(uint2 color) -> void override;
auto joypRead() -> uint4 override;
auto joypWrite(bool p15, bool p14) -> void override; auto joypWrite(bool p15, bool p14) -> void override;
//io.cpp //io.cpp

View File

@ -10,18 +10,26 @@ auto ICD::lcdOutput(uint2 color) -> void {
uint y = writeAddress / 160; uint y = writeAddress / 160;
uint x = writeAddress % 160; uint x = writeAddress % 160;
uint addr = writeBank * 512 + y * 2 + x / 8 * 16; uint addr = writeBank * 512 + y * 2 + x / 8 * 16;
output[addr + 0] = (output[addr + 0] << 1) | (bool)(color & 1); output[addr + 0] = (output[addr + 0] << 1) | color.bit(0);
output[addr + 1] = (output[addr + 1] << 1) | (bool)(color & 2); output[addr + 1] = (output[addr + 1] << 1) | color.bit(1);
writeAddress = (writeAddress + 1) % 1280; writeAddress = (writeAddress + 1) % 1280;
} }
auto ICD::joypRead() -> uint4 {
return 0xf - joypID;
}
auto ICD::joypWrite(bool p15, bool p14) -> void { auto ICD::joypWrite(bool p15, bool p14) -> void {
//joypad handling //joypad handling
if(p15 == 1 && p14 == 1) { if(p15 == 1 && p14 == 1) {
if(joyp15Lock == 0 && joyp14Lock == 0) { if(joyp15Lock == 0 && joyp14Lock == 0) {
joyp15Lock = 1; joyp15Lock = 1;
joyp14Lock = 1; joyp14Lock = 1;
joypID = (joypID + 1) & 3; joypID++;
if(mltReq == 0) joypID &= 0; //1-player mode
if(mltReq == 1) joypID &= 1; //2-player mode
if(mltReq == 2) joypID &= 3; //4-player mode (unverified; but the most likely behavior)
if(mltReq == 3) joypID &= 3; //4-player mode
} }
} }
@ -65,8 +73,7 @@ auto ICD::joypWrite(bool p15, bool p14) -> void {
if(p15 == 1 && p14 == 0) { if(p15 == 1 && p14 == 0) {
if((joypPacket[0] >> 3) == 0x11) { if((joypPacket[0] >> 3) == 0x11) {
mltReq = joypPacket[1] & 3; mltReq = joypPacket[1] & 3;
if(mltReq == 2) mltReq = 3; joypID = 3; //required: the next time P14==1 && P15==1; increment and start from ID=0 (Joypad 1)
joypID = 0;
} }
if(packetSize < 64) packet[packetSize++] = joypPacket; if(packetSize < 64) packet[packetSize++] = joypPacket;

View File

@ -3,10 +3,8 @@ auto ICD::audioSample(const double* samples, uint channels) -> void {
} }
auto ICD::inputPoll(uint port, uint device, uint id) -> int16 { auto ICD::inputPoll(uint port, uint device, uint id) -> int16 {
GameBoy::cpu.status.mltReq = joypID & mltReq; uint8 data = 0x00;
switch(joypID) {
uint data = 0x00;
switch(joypID & mltReq) {
case 0: data = ~r6004; break; case 0: data = ~r6004; break;
case 1: data = ~r6005; break; case 1: data = ~r6005; break;
case 2: data = ~r6006; break; case 2: data = ~r6006; break;
@ -14,14 +12,14 @@ auto ICD::inputPoll(uint port, uint device, uint id) -> int16 {
} }
switch((GameBoy::Input)id) { switch((GameBoy::Input)id) {
case GameBoy::Input::Start: return (bool)(data & 0x80); case GameBoy::Input::Right: return data.bit(0);
case GameBoy::Input::Select: return (bool)(data & 0x40); case GameBoy::Input::Left: return data.bit(1);
case GameBoy::Input::B: return (bool)(data & 0x20); case GameBoy::Input::Up: return data.bit(2);
case GameBoy::Input::A: return (bool)(data & 0x10); case GameBoy::Input::Down: return data.bit(3);
case GameBoy::Input::Down: return (bool)(data & 0x08); case GameBoy::Input::A: return data.bit(4);
case GameBoy::Input::Up: return (bool)(data & 0x04); case GameBoy::Input::B: return data.bit(5);
case GameBoy::Input::Left: return (bool)(data & 0x02); case GameBoy::Input::Select: return data.bit(6);
case GameBoy::Input::Right: return (bool)(data & 0x01); case GameBoy::Input::Start: return data.bit(7);
} }
return 0; return 0;

View File

@ -5,35 +5,16 @@ include sfc/GNUmakefile
include gb/GNUmakefile include gb/GNUmakefile
include processor/GNUmakefile include processor/GNUmakefile
objects := ruby hiro $(if $(call streq,$(platform),windows),hiro-resource) $(objects) hiro.path := ../hiro
objects += ui-bsnes ui-program ui-input ui-presentation hiro.resource := $(ui)/resource/bsnes.rc
objects += ui-settings ui-tools ui-resource include $(hiro.path)/GNUmakefile
objects := $(objects:%=obj/%.o)
ifeq ($(platform),windows)
ruby += video.wgl video.direct3d video.directdraw video.gdi
ruby += audio.asio audio.wasapi audio.xaudio2 audio.directsound
ruby += input.windows
else ifeq ($(platform),macos)
ruby += video.cgl
ruby += audio.openal
ruby += input.quartz input.carbon
else ifeq ($(platform),linux)
ruby += video.glx video.xvideo video.xshm
ruby += audio.oss audio.alsa audio.openal audio.pulseaudio audio.pulseaudiosimple audio.ao
ruby += input.sdl input.xlib input.udev
else ifeq ($(platform),bsd)
ruby += video.glx video.xvideo video.xshm
ruby += audio.oss audio.openal
ruby += input.sdl input.xlib
endif
ruby.path := ../ruby ruby.path := ../ruby
include $(ruby.path)/GNUmakefile include $(ruby.path)/GNUmakefile
hiro.path := ../hiro objects += ui-bsnes ui-program ui-input ui-presentation
hiro.resource := $(ui)/resource/bsnes.rc objects += ui-settings ui-tools ui-resource
include $(hiro.path)/GNUmakefile objects := $(objects:%=obj/%.o)
obj/ui-bsnes.o: $(ui)/bsnes.cpp obj/ui-bsnes.o: $(ui)/bsnes.cpp
obj/ui-program.o: $(ui)/program/program.cpp obj/ui-program.o: $(ui)/program/program.cpp
@ -43,10 +24,9 @@ obj/ui-settings.o: $(ui)/settings/settings.cpp
obj/ui-tools.o: $(ui)/tools/tools.cpp obj/ui-tools.o: $(ui)/tools/tools.cpp
obj/ui-resource.o: $(ui)/resource/resource.cpp obj/ui-resource.o: $(ui)/resource/resource.cpp
# targets all: $(hiro.objects) $(ruby.objects) $(objects)
all: $(objects)
$(info Linking out/$(name) ...) $(info Linking out/$(name) ...)
+@$(strip $(compiler) -o out/$(name) $(objects) $(options) $(ruby.options) $(hiro.options)) +@$(compiler) -o out/$(name) $(hiro.objects) $(ruby.objects) $(objects) $(hiro.options) $(ruby.options) $(options)
ifeq ($(platform),macos) ifeq ($(platform),macos)
rm -rf out/$(name).app rm -rf out/$(name).app
mkdir -p out/$(name).app/Contents/MacOS/ mkdir -p out/$(name).app/Contents/MacOS/
@ -56,6 +36,8 @@ ifeq ($(platform),macos)
sips -s format icns $(ui)/resource/$(name).png --out out/$(name).app/Contents/Resources/$(name).icns sips -s format icns $(ui)/resource/$(name).png --out out/$(name).app/Contents/Resources/$(name).icns
endif endif
verbose: hiro.verbose ruby.verbose nall.verbose all;
install: install:
ifeq ($(shell id -un),root) ifeq ($(shell id -un),root)
$(error "make install should not be run as root") $(error "make install should not be run as root")

View File

@ -4,7 +4,6 @@ unique_pointer<Video> video;
unique_pointer<Audio> audio; unique_pointer<Audio> audio;
unique_pointer<Input> input; unique_pointer<Input> input;
unique_pointer<Emulator::Interface> emulator; unique_pointer<Emulator::Interface> emulator;
Locale ns;
auto locate(string name) -> string { auto locate(string name) -> string {
string location = {Path::program(), name}; string location = {Path::program(), name};
@ -21,14 +20,15 @@ auto locate(string name) -> string {
#include <nall/main.hpp> #include <nall/main.hpp>
auto nall::main(string_vector arguments) -> void { auto nall::main(string_vector arguments) -> void {
ns.scan(locate("locales/")); string locale; // = "日本語";
ns.select("日本語");
for(auto argument : arguments) { for(auto argument : arguments) {
if(argument.beginsWith("--locale=")) { if(argument.beginsWith("--locale=")) {
ns.select(argument.trimLeft("--locale=", 1L)); locale = argument.trimLeft("--locale=", 1L);
} }
} }
Application::setName("bsnes"); Application::setName("bsnes");
Application::locale().scan(locate("locales/"));
Application::locale().select(locale);
emulator = new SuperFamicom::Interface; emulator = new SuperFamicom::Interface;
new Program(arguments); new Program(arguments);
Application::run(); Application::run();

View File

@ -11,7 +11,6 @@ extern unique_pointer<Input> input;
#include <emulator/emulator.hpp> #include <emulator/emulator.hpp>
extern unique_pointer<Emulator::Interface> emulator; extern unique_pointer<Emulator::Interface> emulator;
extern Locale ns;
#include "program/program.hpp" #include "program/program.hpp"
#include "input/input.hpp" #include "input/input.hpp"
#include "presentation/presentation.hpp" #include "presentation/presentation.hpp"

View File

@ -24,6 +24,14 @@ auto InputManager::bindHotkeys() -> void {
program->loadState({"quick/slot ", stateSlot}); program->loadState({"quick/slot ", stateSlot});
})); }));
hotkeys.append(InputHotkey("Load Undo State").onPress([&] {
program->loadState("quick/undo");
}));
hotkeys.append(InputHotkey("Load Redo State").onPress([&] {
program->loadState("quick/redo");
}));
hotkeys.append(InputHotkey("Increment State Slot").onPress([&] { hotkeys.append(InputHotkey("Increment State Slot").onPress([&] {
if(--stateSlot < 1) stateSlot = 9; if(--stateSlot < 1) stateSlot = 9;
program->showMessage({"Selected state slot ", stateSlot}); program->showMessage({"Selected state slot ", stateSlot});

View File

@ -1,7 +1,7 @@
AboutWindow::AboutWindow() : Locale::Namespace(ns, "About") { AboutWindow::AboutWindow() {
aboutWindow = this; aboutWindow = this;
setTitle(tr("About {0} ...", "bsnes")); setTitle({tr("About {0}", "bsnes"), " ..."});
setBackgroundColor({255, 255, 240}); setBackgroundColor({255, 255, 240});
layout.setPadding(10); layout.setPadding(10);
auto logo = image{Resource::Logo}; auto logo = image{Resource::Logo};
@ -9,14 +9,17 @@ AboutWindow::AboutWindow() : Locale::Namespace(ns, "About") {
canvas.setIcon(logo); canvas.setIcon(logo);
tableLayout.setFont(Font().setBold()); tableLayout.setFont(Font().setBold());
tableLayout.setSize({2, 4}); tableLayout.setSize({2, 4});
tableLayout.column(0).setSpacing(3); tableLayout.column(0).setSpacing(4);
versionLabel.setText(tr("Version:")).setAlignment(1.0); tableLayout.row(0).setSpacing(2);
tableLayout.row(1).setSpacing(2);
tableLayout.row(2).setSpacing(2);
versionLabel.setText({tr("Version"), ":"}).setAlignment(1.0);
versionValue.setText(Emulator::Version); versionValue.setText(Emulator::Version);
authorLabel.setText(tr("Author:")).setAlignment(1.0); authorLabel.setText({tr("Author"), ":"}).setAlignment(1.0);
authorValue.setText(Emulator::Author); authorValue.setText(Emulator::Author);
licenseLabel.setText(tr("License:")).setAlignment(1.0); licenseLabel.setText({tr("License"), ":"}).setAlignment(1.0);
licenseValue.setText(Emulator::License); licenseValue.setText(Emulator::License);
websiteLabel.setText(tr("Website:")).setAlignment(1.0); websiteLabel.setText({tr("Website"), ":"}).setAlignment(1.0);
websiteValue.setText(Emulator::Website); websiteValue.setText(Emulator::Website);
setResizable(false); setResizable(false);

View File

@ -3,24 +3,24 @@
unique_pointer<AboutWindow> aboutWindow; unique_pointer<AboutWindow> aboutWindow;
unique_pointer<Presentation> presentation; unique_pointer<Presentation> presentation;
Presentation::Presentation() : Locale::Namespace(ns, "Presentation") { Presentation::Presentation() {
presentation = this; presentation = this;
systemMenu.setText(tr("System")); systemMenu.setText(tr("System"));
loadGame.setIcon(Icon::Action::Open).setText("Load Game ...").onActivate([&] { loadGame.setIcon(Icon::Action::Open).setText({tr("Load Game"), " ..."}).onActivate([&] {
program->load(); program->load();
}); });
loadRecentGame.setIcon(Icon::Action::Open).setText("Load Recent Game"); loadRecentGame.setIcon(Icon::Action::Open).setText(tr("Load Recent Game"));
updateRecentGames(); updateRecentGames();
resetSystem.setIcon(Icon::Action::Refresh).setText("Reset System").setEnabled(false).onActivate([&] { resetSystem.setIcon(Icon::Action::Refresh).setText(tr("Reset System")).setEnabled(false).onActivate([&] {
program->reset(); program->reset();
}); });
unloadGame.setIcon(Icon::Action::Remove).setText("Unload Game").setEnabled(false).onActivate([&] { unloadGame.setIcon(Icon::Action::Remove).setText(tr("Unload Game")).setEnabled(false).onActivate([&] {
program->unload(); program->unload();
}); });
controllerPort1.setIcon(Icon::Device::Joypad).setText("Controller Port 1"); controllerPort1.setIcon(Icon::Device::Joypad).setText(tr("Controller Port 1"));
controllerPort2.setIcon(Icon::Device::Joypad).setText("Controller Port 2"); controllerPort2.setIcon(Icon::Device::Joypad).setText(tr("Controller Port 2"));
expansionPort.setIcon(Icon::Device::Storage).setText("Expansion Port"); expansionPort.setIcon(Icon::Device::Storage).setText(tr("Expansion Port"));
for(auto& port : emulator->ports) { for(auto& port : emulator->ports) {
Menu* menu = nullptr; Menu* menu = nullptr;
if(port.name == "Controller Port 1") menu = &controllerPort1; if(port.name == "Controller Port 1") menu = &controllerPort1;
@ -33,7 +33,7 @@ Presentation::Presentation() : Locale::Namespace(ns, "Presentation") {
if(port.name != "Expansion Port" && device.name == "None") continue; if(port.name != "Expansion Port" && device.name == "None") continue;
if(port.name == "Expansion Port" && device.name == "21fx") continue; if(port.name == "Expansion Port" && device.name == "21fx") continue;
MenuRadioItem item{menu}; MenuRadioItem item{menu};
item.setText(device.name).onActivate([=] { item.setText(tr(device.name)).onActivate([=] {
auto path = string{"Emulator/", port.name}.replace(" ", ""); auto path = string{"Emulator/", port.name}.replace(" ", "");
settings(path).setValue(device.name); settings(path).setValue(device.name);
emulator->connect(port.id, device.id); emulator->connect(port.id, device.id);
@ -53,7 +53,7 @@ Presentation::Presentation() : Locale::Namespace(ns, "Presentation") {
devices.objects<MenuRadioItem>()(0).doActivate(); devices.objects<MenuRadioItem>()(0).doActivate();
} }
} }
quit.setIcon(Icon::Action::Quit).setText("Quit").onActivate([&] { program->quit(); }); quit.setIcon(Icon::Action::Quit).setText(tr("Quit")).onActivate([&] { program->quit(); });
settingsMenu.setText(tr("Settings")); settingsMenu.setText(tr("Settings"));
sizeMenu.setIcon(Icon::Emblem::Image).setText("Size"); sizeMenu.setIcon(Icon::Emblem::Image).setText("Size");
@ -132,6 +132,9 @@ Presentation::Presentation() : Locale::Namespace(ns, "Presentation") {
loadState.append(MenuItem().setIcon(Icon::Edit::Undo).setText("Undo Last Save").onActivate([&] { loadState.append(MenuItem().setIcon(Icon::Edit::Undo).setText("Undo Last Save").onActivate([&] {
program->loadState("quick/undo"); program->loadState("quick/undo");
})); }));
loadState.append(MenuItem().setIcon(Icon::Edit::Redo).setText("Redo Last Undo").onActivate([&] {
program->loadState("quick/redo");
}));
speedMenu.setIcon(Icon::Device::Clock).setText("Speed"); speedMenu.setIcon(Icon::Device::Clock).setText("Speed");
speedSlowest.setText("50% (Slowest)").setProperty("multiplier", "2.0").onActivate([&] { program->updateAudioFrequency(); }); speedSlowest.setText("50% (Slowest)").setProperty("multiplier", "2.0").onActivate([&] { program->updateAudioFrequency(); });
speedSlow.setText("75% (Slow)").setProperty("multiplier", "1.333").onActivate([&] { program->updateAudioFrequency(); }); speedSlow.setText("75% (Slow)").setProperty("multiplier", "1.333").onActivate([&] { program->updateAudioFrequency(); });
@ -153,10 +156,10 @@ Presentation::Presentation() : Locale::Namespace(ns, "Presentation") {
manifestViewer.setIcon(Icon::Emblem::Text).setText("Manifest Viewer ...").onActivate([&] { toolsWindow->show(2); }); manifestViewer.setIcon(Icon::Emblem::Text).setText("Manifest Viewer ...").onActivate([&] { toolsWindow->show(2); });
helpMenu.setText(tr("Help")); helpMenu.setText(tr("Help"));
documentation.setIcon(Icon::Application::Browser).setText("Documentation ...").onActivate([&] { documentation.setIcon(Icon::Application::Browser).setText({tr("Documentation"), " ..."}).onActivate([&] {
invoke("https://doc.byuu.org/bsnes/"); invoke("https://doc.byuu.org/bsnes/");
}); });
about.setIcon(Icon::Prompt::Question).setText("About ...").onActivate([&] { about.setIcon(Icon::Prompt::Question).setText({tr("About"), " ..."}).onActivate([&] {
aboutWindow->setCentered(*this).setVisible().setFocused(); aboutWindow->setCentered(*this).setVisible().setFocused();
}); });
@ -245,7 +248,7 @@ auto Presentation::updateStatusIcon() -> void {
auto Presentation::drawIcon(uint32_t* output, uint length, uint width, uint height) -> void { auto Presentation::drawIcon(uint32_t* output, uint length, uint width, uint height) -> void {
return; return;
int ox = width - 144; int ox = width - 144;
int oy = height - 128; int oy = height - 128;
if(ox >= 0 && oy >= 0) { if(ox >= 0 && oy >= 0) {
image icon{Resource::Icon}; image icon{Resource::Icon};
@ -259,11 +262,8 @@ auto Presentation::drawIcon(uint32_t* output, uint length, uint width, uint heig
} }
auto Presentation::clearViewport() -> void { auto Presentation::clearViewport() -> void {
if(!visible() && !video) return; if(!emulator->loaded()) viewportLayout.setPadding();
if(!visible() || !video) return;
if(!emulator->loaded()) {
viewportLayout.setPadding();
}
uint32_t* output; uint32_t* output;
uint length; uint length;
@ -288,7 +288,7 @@ auto Presentation::resizeViewport() -> void {
uint height = (settings["View/OverscanCropping"].boolean() ? 224.0 : 240.0); uint height = (settings["View/OverscanCropping"].boolean() ? 224.0 : 240.0);
uint viewportWidth, viewportHeight; uint viewportWidth, viewportHeight;
if(!fullScreen()) { if(visible() && !fullScreen()) {
uint widthMultiplier = windowWidth / width; uint widthMultiplier = windowWidth / width;
uint heightMultiplier = windowHeight / height; uint heightMultiplier = windowHeight / height;
uint multiplier = max(1, min(widthMultiplier, heightMultiplier)); uint multiplier = max(1, min(widthMultiplier, heightMultiplier));
@ -300,8 +300,8 @@ auto Presentation::resizeViewport() -> void {
} }
} }
if(!visible() || !video) return;
if(!emulator->loaded()) return clearViewport(); if(!emulator->loaded()) return clearViewport();
if(!video) return;
if(settings["View/Output"].text() == "Center") { if(settings["View/Output"].text() == "Center") {
uint widthMultiplier = windowWidth / width; uint widthMultiplier = windowWidth / width;
@ -450,14 +450,14 @@ auto Presentation::updateRecentGames() -> void {
program->load(); program->load();
}); });
} else { } else {
item.setText("<empty>"); item.setText(tr("Empty"));
item.setEnabled(false); item.setEnabled(false);
} }
loadRecentGame.append(item); loadRecentGame.append(item);
} }
loadRecentGame.append(MenuSeparator()); loadRecentGame.append(MenuSeparator());
loadRecentGame.append(MenuItem().setIcon(Icon::Edit::Clear).setText("Clear List").onActivate([&] { loadRecentGame.append(MenuItem().setIcon(Icon::Edit::Clear).setText(tr("Clear List")).onActivate([&] {
for(auto index : range(RecentGames)) { for(auto index : range(RecentGames)) {
settings({"Game/Recent/", 1 + index}).setValue(""); settings({"Game/Recent/", 1 + index}).setValue("");
} }

View File

@ -1,4 +1,6 @@
struct AboutWindow : Locale::Namespace, Window { struct AboutWindow : Window {
Application::Namespace tr{"AboutWindow"};
AboutWindow(); AboutWindow();
VerticalLayout layout{this}; VerticalLayout layout{this};
@ -17,7 +19,9 @@ struct AboutWindow : Locale::Namespace, Window {
Label websiteValue{&tableLayout, Size{~0, 0}}; Label websiteValue{&tableLayout, Size{~0, 0}};
}; };
struct Presentation : Locale::Namespace, Window { struct Presentation : Window {
Application::Namespace tr{"Presentation"};
enum : uint { RecentGames = 9, QuickStates = 9 }; enum : uint { RecentGames = 9, QuickStates = 9 };
enum : uint { StatusHeight = 24 }; enum : uint { StatusHeight = 24 };
@ -103,7 +107,7 @@ struct Presentation : Locale::Namespace, Window {
Canvas statusIcon{&statusLayout, Size{16, ~0}, 0}; Canvas statusIcon{&statusLayout, Size{16, ~0}, 0};
Label spacerLeft{&statusLayout, Size{4, ~0}, 0}; Label spacerLeft{&statusLayout, Size{4, ~0}, 0};
Label statusLeft{&statusLayout, Size{~0, ~0}, 0}; Label statusLeft{&statusLayout, Size{~0, ~0}, 0};
Label statusRight{&statusLayout, Size{80, ~0}, 0}; Label statusRight{&statusLayout, Size{100, ~0}, 0};
Label spacerRight{&statusLayout, Size{8, ~0}, 0}; Label spacerRight{&statusLayout, Size{8, ~0}, 0};
}; };

View File

@ -27,7 +27,7 @@ auto Program::load() -> void {
program->loadState("quick/undo"); program->loadState("quick/undo");
} }
showMessage({ showMessage({
verified() ? "Verified" : "Unverified", " game loaded", verified() ? "Verified game loaded" : "Game loaded",
appliedPatch() ? " and patch applied" : "" appliedPatch() ? " and patch applied" : ""
}); });
presentation->setTitle(emulator->title()); presentation->setTitle(emulator->title());

View File

@ -13,7 +13,7 @@
#include "hacks.cpp" #include "hacks.cpp"
unique_pointer<Program> program; unique_pointer<Program> program;
Program::Program(string_vector arguments) : Locale::Namespace(ns, "Program") { Program::Program(string_vector arguments) {
program = this; program = this;
Emulator::platform = this; Emulator::platform = this;

View File

@ -1,4 +1,6 @@
struct Program : Locale::Namespace, Emulator::Platform { struct Program : Emulator::Platform {
Application::Namespace tr{"Program"};
//program.cpp //program.cpp
Program(string_vector arguments); Program(string_vector arguments);
auto main() -> void; auto main() -> void;
@ -51,6 +53,7 @@ struct Program : Locale::Namespace, Emulator::Platform {
auto loadState(string filename) -> bool; auto loadState(string filename) -> bool;
auto saveState(string filename) -> bool; auto saveState(string filename) -> bool;
auto saveUndoState() -> bool; auto saveUndoState() -> bool;
auto saveRedoState() -> bool;
auto removeState(string filename) -> bool; auto removeState(string filename) -> bool;
auto renameState(string from, string to) -> bool; auto renameState(string from, string to) -> bool;

View File

@ -28,6 +28,7 @@ auto Program::loadState(string filename) -> bool {
string location = {statePath(), filename, ".bst"}; string location = {statePath(), filename, ".bst"};
if(!file::exists(location)) return showMessage({"[", prefix, "] not found"}), false; if(!file::exists(location)) return showMessage({"[", prefix, "] not found"}), false;
if(filename != "quick/undo") saveUndoState(); if(filename != "quick/undo") saveUndoState();
if(filename == "quick/undo") saveRedoState();
memory = file::read(location); memory = file::read(location);
} else { } else {
string location = {filename, ".bst"}; string location = {filename, ".bst"};
@ -96,6 +97,15 @@ auto Program::saveUndoState() -> bool {
return result; return result;
} }
auto Program::saveRedoState() -> bool {
auto statusTime = this->statusTime;
auto statusMessage = this->statusMessage;
auto result = saveState("quick/redo");
this->statusTime = statusTime;
this->statusMessage = statusMessage;
return result;
}
auto Program::removeState(string filename) -> bool { auto Program::removeState(string filename) -> bool {
if(!emulator->loaded()) return false; if(!emulator->loaded()) return false;

View File

@ -1,7 +1,35 @@
locale locale
language: 日本語 language: 日本語
namespace: MessageDialog
map
input: Yes
value: はい
map
input: No
value: いいえ
map
input: Cancel
value: キャンセル
namespace: BrowserDialog
map
input: Open
value: 開く
map
input: Save
value: 保存
map
input: Select
value: 選択
map
input: Cancel
value: キャンセル
namespace: Program namespace: Program
map
input: Unloaded
value: アンロードされる
map map
input: Paused input: Paused
value: ポーズ value: ポーズ
@ -10,6 +38,60 @@ namespace: Presentation
map map
input: System input: System
value: システム value: システム
map
input: Load Game
value: ゲームを読み込み
map
input: Load Recent Game
value: 最新ゲームを読み込み
map
input: Empty
value: なし
map
input: Clear List
value: 全部を消す
map
input: Reset System
value: システムをリセット
map
input: Unload Game
value: ゲームをアンロード
map
input: Controller Port 1
value: コントローラポート1
map
input: Controller Port 2
value: コントローラポート2
map
input: Expansion Port
value: 拡張ポート
map
input: Gamepad
value: ゲームパッド
map
input: Mouse
value: マウス
map
input: Super Multitap
value: スーパーマルチタップ
map
input: Super Scope
value: スーパースコップ
map
input: Justifier
value: 1挺のジャスティファイアー
map
input: Justifiers
value: 2挺のジャスティファイアー
map
input: None
value: なし
map
input: Satellaview
value: サテラビュー
map
input: Quit
value: 終了
map map
input: Settings input: Settings
value: 設定 value: 設定
@ -19,22 +101,27 @@ namespace: Presentation
map map
input: Help input: Help
value: ヘルプ value: ヘルプ
map
input: Documentation
value: オンラインマニュアル
map
input: About
value: 情報
namespace: About namespace: AboutWindow
map map
input: About {0} ... input: About {0}
value: {0}について ... value: {0}について
map
map input: Version
input: Version: value: バージョン
value: バージョン: map
map input: Author
input: Author: value: 作者
value: 作者: map
map input: License
input: License: value: ライセンス
value: ライセンス: map
map input: Website
input: Website: value: 公式サイト
value: 公式サイト:

View File

@ -11,35 +11,16 @@ include gba/GNUmakefile
include ws/GNUmakefile include ws/GNUmakefile
include processor/GNUmakefile include processor/GNUmakefile
objects := ruby hiro $(if $(call streq,$(platform),windows),hiro-resource) $(objects) hiro.path := ../hiro
objects += ui-higan ui-program ui-input hiro.resource := $(ui)/resource/higan.rc
objects += ui-settings ui-tools ui-presentation ui-resource include $(hiro.path)/GNUmakefile
objects := $(objects:%=obj/%.o)
ifeq ($(platform),windows)
ruby += video.wgl video.direct3d video.directdraw video.gdi
ruby += audio.asio audio.wasapi audio.xaudio2 audio.directsound
ruby += input.windows
else ifeq ($(platform),macos)
ruby += video.cgl
ruby += audio.openal
ruby += input.quartz input.carbon
else ifeq ($(platform),linux)
ruby += video.glx video.xvideo video.xshm
ruby += audio.oss audio.alsa audio.openal audio.pulseaudio audio.pulseaudiosimple audio.ao
ruby += input.sdl input.xlib input.udev
else ifeq ($(platform),bsd)
ruby += video.glx video.xvideo video.xshm
ruby += audio.oss audio.openal
ruby += input.sdl input.xlib
endif
ruby.path := ../ruby ruby.path := ../ruby
include $(ruby.path)/GNUmakefile include $(ruby.path)/GNUmakefile
hiro.path := ../hiro objects += ui-higan ui-program ui-input
hiro.resource := $(ui)/resource/higan.rc objects += ui-settings ui-tools ui-presentation ui-resource
include $(hiro.path)/GNUmakefile objects := $(objects:%=obj/%.o)
obj/ui-higan.o: $(ui)/higan.cpp obj/ui-higan.o: $(ui)/higan.cpp
obj/ui-program.o: $(ui)/program/program.cpp obj/ui-program.o: $(ui)/program/program.cpp
@ -49,10 +30,9 @@ obj/ui-tools.o: $(ui)/tools/tools.cpp
obj/ui-presentation.o: $(ui)/presentation/presentation.cpp obj/ui-presentation.o: $(ui)/presentation/presentation.cpp
obj/ui-resource.o: $(ui)/resource/resource.cpp obj/ui-resource.o: $(ui)/resource/resource.cpp
# targets all: $(hiro.objects) $(ruby.objects) $(objects)
all: $(objects)
$(info Linking out/$(name) ...) $(info Linking out/$(name) ...)
+@$(strip $(compiler) -o out/$(name) $(objects) $(options) $(ruby.options) $(hiro.options)) +@$(compiler) -o out/$(name) $(hiro.objects) $(ruby.objects) $(objects) $(hiro.options) $(ruby.options) $(options)
ifeq ($(platform),macos) ifeq ($(platform),macos)
rm -rf out/$(name).app rm -rf out/$(name).app
mkdir -p out/$(name).app/Contents/MacOS/ mkdir -p out/$(name).app/Contents/MacOS/
@ -62,6 +42,8 @@ ifeq ($(platform),macos)
sips -s format icns $(ui)/resource/$(name).png --out out/$(name).app/Contents/Resources/$(name).icns sips -s format icns $(ui)/resource/$(name).png --out out/$(name).app/Contents/Resources/$(name).icns
endif endif
verbose: hiro.verbose ruby.verbose nall.verbose all;
install: install:
ifeq ($(shell id -un),root) ifeq ($(shell id -un),root)
$(error "make install should not be run as root") $(error "make install should not be run as root")

View File

@ -63,16 +63,24 @@ ifneq ($(filter $(platform),linux bsd),)
endif endif
endif endif
$(object.path)/hiro.o: $(hiro.path)/hiro.cpp $(call rwildcard,$(hiro.path)) $(call rwildcard,$(nall.path))
$(if $(filter qt%,$(hiro)),$(info Compiling $(hiro.path)/qt/qt.moc ...))
$(if $(filter qt%,$(hiro)),@$(moc) -i -o $(hiro.path)/qt/qt.moc $(hiro.path)/qt/qt.hpp)
$(info Compiling $< ...)
@$(compiler) $(flags) $(hiro.flags) -c $< -o $@
ifeq ($(hiro.resource),) ifeq ($(hiro.resource),)
hiro.resource := $(hiro.path)/windows/hiro.rc hiro.resource := $(hiro.path)/windows/hiro.rc
endif endif
hiro.objects := \
$(object.path)/hiro-$(hiro).o \
$(if $(filter windows,$(hiro)),$(object.path)/hiro-resource.o)
$(object.path)/hiro-$(hiro).o: $(hiro.path)/hiro.cpp
$(if $(filter qt%,$(hiro)),$(info Compiling $(hiro.path)/qt/qt.moc ...))
$(if $(filter qt%,$(hiro)),@$(moc) -i -o $(hiro.path)/qt/qt.moc $(hiro.path)/qt/qt.hpp)
$(info Compiling $< ...)
@$(compiler) $(hiro.flags) $(flags) $(flags.deps) -c $< -o $@
$(object.path)/hiro-resource.o: $(hiro.resource) $(object.path)/hiro-resource.o: $(hiro.resource)
$(if $(filter windows,$(hiro)),$(info Compiling $< ...)) $(info Compiling $< ...)
$(if $(filter windows,$(hiro)),@$(windres) $< $@) @$(windres) $< $@
hiro.verbose:
$(info hiro Target:)
$(info $([space]) $(hiro))

View File

@ -5,7 +5,10 @@ namespace hiro {
auto pDesktop::size() -> Size { auto pDesktop::size() -> Size {
@autoreleasepool { @autoreleasepool {
NSRect primary = [[[NSScreen screens] objectAtIndex:0] frame]; NSRect primary = [[[NSScreen screens] objectAtIndex:0] frame];
return {(int)primary.size.width, (int)primary.size.height}; return {
(int)primary.size.width,
(int)primary.size.height
};
} }
} }
@ -13,7 +16,12 @@ auto pDesktop::workspace() -> Geometry {
@autoreleasepool { @autoreleasepool {
auto screen = Desktop::size(); auto screen = Desktop::size();
NSRect area = [[[NSScreen screens] objectAtIndex:0] visibleFrame]; NSRect area = [[[NSScreen screens] objectAtIndex:0] visibleFrame];
return {(int)area.origin.x, (int)(screen.height() - area.size.height - area.origin.y), (int)area.size.width, (int)area.size.height}; return {
(int)area.origin.x,
(int)area.origin.y,
(int)area.size.width,
(int)area.size.height
};
} }
} }

View File

@ -20,15 +20,33 @@ auto pMonitor::dpi(uint monitor) -> Position {
auto pMonitor::geometry(uint monitor) -> Geometry { auto pMonitor::geometry(uint monitor) -> Geometry {
@autoreleasepool { @autoreleasepool {
NSRect rectangle = [[[NSScreen screens] objectAtIndex:monitor] frame]; NSRect rectangle = [[[NSScreen screens] objectAtIndex:monitor] frame];
return {(int)rectangle.origin.x, (int)rectangle.origin.y, (int)rectangle.size.width, (int)rectangle.size.height}; return {
(int)rectangle.origin.x,
(int)rectangle.origin.y,
(int)rectangle.size.width,
(int)rectangle.size.height
};
} }
} }
auto pMonitor::primary() -> uint { auto pMonitor::primary() -> uint {
//on OS X, the primary monitor is always the first monitor //on macOS, the primary monitor is always the first monitor
return 0; return 0;
} }
auto pMonitor::workspace(uint monitor) -> Geometry {
@autoreleasepool {
NSRect size = [[[NSScreen screens] objectAtIndex:monitor] frame];
NSRect area = [[[NSScreen screens] objectAtIndex:monitor] visibleFrame];
return {
(int)area.origin.x,
(int)area.origin.y,
(int)area.size.width,
(int)area.size.height
};
}
}
} }
#endif #endif

View File

@ -7,6 +7,7 @@ struct pMonitor {
static auto dpi(uint monitor) -> Position; static auto dpi(uint monitor) -> Position;
static auto geometry(uint monitor) -> Geometry; static auto geometry(uint monitor) -> Geometry;
static auto primary() -> uint; static auto primary() -> uint;
static auto workspace(uint monitor) -> Geometry;
}; };
} }

View File

@ -1,11 +1,3 @@
namespace hiro {
struct pFont;
struct pWindow;
struct pMenu;
struct pLayout;
struct pWidget;
}
#define Declare(Name, Base) \ #define Declare(Name, Base) \
p##Name(m##Name& reference) : p##Base(reference) {} \ p##Name(m##Name& reference) : p##Base(reference) {} \
auto self() const -> m##Name& { return (m##Name&)reference; } \ auto self() const -> m##Name& { return (m##Name&)reference; } \

View File

@ -39,14 +39,14 @@ auto pFrame::remove(sSizable sizable) -> void {
auto pFrame::setEnabled(bool enabled) -> void { auto pFrame::setEnabled(bool enabled) -> void {
pWidget::setEnabled(enabled); pWidget::setEnabled(enabled);
if(auto& sizable = _sizable()) sizable->setEnabled(sizable->self().enabled(true)); if(auto sizable = _sizable()) sizable->setEnabled(sizable->self().enabled(true));
} }
auto pFrame::setFont(const Font& font) -> void { auto pFrame::setFont(const Font& font) -> void {
@autoreleasepool { @autoreleasepool {
[cocoaView setTitleFont:pFont::create(font)]; [cocoaView setTitleFont:pFont::create(font)];
} }
if(auto& sizable = _sizable()) sizable->setFont(sizable->self().font(true)); if(auto sizable = _sizable()) sizable->setFont(sizable->self().font(true));
} }
auto pFrame::setGeometry(Geometry geometry) -> void { auto pFrame::setGeometry(Geometry geometry) -> void {
@ -56,7 +56,7 @@ auto pFrame::setGeometry(Geometry geometry) -> void {
geometry.x() - 3, geometry.y() - (empty ? size.height() - 2 : 1), geometry.x() - 3, geometry.y() - (empty ? size.height() - 2 : 1),
geometry.width() + 6, geometry.height() + (empty ? size.height() + 2 : 5) geometry.width() + 6, geometry.height() + (empty ? size.height() + 2 : 5)
}); });
if(auto& sizable = state().sizable) { if(auto sizable = _sizable()) {
sizable->setGeometry({ sizable->setGeometry({
geometry.x() + 1, geometry.y() + (empty ? 1 : size.height() - 2), geometry.x() + 1, geometry.y() + (empty ? 1 : size.height() - 2),
geometry.width() - 2, geometry.height() - (empty ? 1 : size.height() - 1) geometry.width() - 2, geometry.height() - (empty ? 1 : size.height() - 1)
@ -72,14 +72,14 @@ auto pFrame::setText(const string& text) -> void {
auto pFrame::setVisible(bool visible) -> void { auto pFrame::setVisible(bool visible) -> void {
pWidget::setVisible(visible); pWidget::setVisible(visible);
if(auto& sizable = _sizable()) sizable->setVisible(sizable->self().visible(true)); if(auto sizable = _sizable()) sizable->setVisible(sizable->self().visible(true));
} }
auto pFrame::_sizable() -> maybe<pSizable&> { auto pFrame::_sizable() -> maybe<pSizable&> {
if(auto sizable = state().sizable) { if(auto sizable = state().sizable) {
if(auto self = sizable->self()) return *self; if(auto self = sizable->self()) return *self;
} }
return nothing; return {};
} }
} }

View File

@ -12,7 +12,7 @@
} }
-(void) tabView:(NSTabView*)tabView didSelectTabViewItem:(NSTabViewItem*)tabViewItem { -(void) tabView:(NSTabView*)tabView didSelectTabViewItem:(NSTabViewItem*)tabViewItem {
tabFrame->self()->_synchronizeLayout(); tabFrame->self()->_synchronizeSizable();
tabFrame->doChange(); tabFrame->doChange();
} }
@ -104,8 +104,8 @@ auto pTabFrame::remove(sTabFrameItem item) -> void {
auto pTabFrame::setEnabled(bool enabled) -> void { auto pTabFrame::setEnabled(bool enabled) -> void {
pWidget::setEnabled(enabled); pWidget::setEnabled(enabled);
for(auto& item : state().items) { for(auto& item : state().items) {
if(auto& layout = item->state.layout) { if(auto& sizable = item->state.sizable) {
if(auto self = layout->self()) self->setEnabled(layout->enabled(true)); if(auto self = sizable->self()) self->setEnabled(sizable->enabled(true));
} }
} }
} }
@ -113,8 +113,8 @@ auto pTabFrame::setEnabled(bool enabled) -> void {
auto pTabFrame::setFont(const Font& font) -> void { auto pTabFrame::setFont(const Font& font) -> void {
pWidget::setFont(font); pWidget::setFont(font);
for(auto& item : state().items) { for(auto& item : state().items) {
if(auto& layout = item->state.layout) { if(auto& sizable = item->state.sizable) {
if(auto self = layout->self()) self->setFont(layout->font(true)); if(auto self = sizable->self()) self->setFont(sizable->font(true));
} }
} }
} }
@ -129,11 +129,11 @@ auto pTabFrame::setGeometry(Geometry geometry) -> void {
geometry.width() - 2, geometry.height() - 32 geometry.width() - 2, geometry.height() - 32
}); });
for(auto& item : state().items) { for(auto& item : state().items) {
if(auto& layout = item->state.layout) { if(auto& sizable = item->state.sizable) {
layout->setGeometry(geometry); sizable->setGeometry(geometry);
} }
} }
_synchronizeLayout(); _synchronizeSizable();
} }
auto pTabFrame::setNavigation(Navigation navigation) -> void { auto pTabFrame::setNavigation(Navigation navigation) -> void {
@ -142,20 +142,20 @@ auto pTabFrame::setNavigation(Navigation navigation) -> void {
auto pTabFrame::setVisible(bool visible) -> void { auto pTabFrame::setVisible(bool visible) -> void {
pWidget::setVisible(visible); pWidget::setVisible(visible);
for(auto& item : state().items) { for(auto& item : state().items) {
if(auto& layout = item->state.layout) { if(auto& sizable = item->state.sizable) {
if(auto self = layout->self()) self->setVisible(layout->visible(true)); if(auto self = sizable->self()) self->setVisible(sizable->visible(true));
} }
} }
} }
auto pTabFrame::_synchronizeLayout() -> void { auto pTabFrame::_synchronizeSizable() -> void {
@autoreleasepool { @autoreleasepool {
NSTabViewItem* tabViewItem = [cocoaView selectedTabViewItem]; NSTabViewItem* tabViewItem = [cocoaView selectedTabViewItem];
int selected = tabViewItem ? [cocoaView indexOfTabViewItem:tabViewItem] : -1; int selected = tabViewItem ? [cocoaView indexOfTabViewItem:tabViewItem] : -1;
for(auto& item : state().items) { for(auto& item : state().items) {
item->state.selected = item->offset() == selected; item->state.selected = item->offset() == selected;
if(auto& layout = item->state.layout) { if(auto& sizable = item->state.sizable) {
if(auto self = layout->self()) self->setVisible(layout->visible(true) && item->selected()); if(auto self = sizable->self()) self->setVisible(sizable->visible(true) && item->selected());
} }
} }
} }

View File

@ -31,7 +31,7 @@ struct pTabFrame : pWidget {
auto setNavigation(Navigation navigation) -> void; auto setNavigation(Navigation navigation) -> void;
auto setVisible(bool visible) -> void override; auto setVisible(bool visible) -> void override;
auto _synchronizeLayout() -> void; auto _synchronizeSizable() -> void;
CocoaTabFrame* cocoaTabFrame = nullptr; CocoaTabFrame* cocoaTabFrame = nullptr;
vector<CocoaTabFrameItem*> tabs; vector<CocoaTabFrameItem*> tabs;

View File

@ -406,7 +406,7 @@ auto pTableView::_width(uint column) -> uint {
uint width = 1; uint width = 1;
if(!header->column(column).visible()) return width; if(!header->column(column).visible()) return width;
if(header->visible()) width = max(width, _columnWidth(column)); if(header->visible()) width = max(width, _columnWidth(column));
for(auto row : range(state().items)) { for(auto row : range(state().items.size())) {
width = max(width, _cellWidth(row, column)); width = max(width, _cellWidth(row, column));
} }
return width; return width;

View File

@ -217,7 +217,7 @@ auto pWindow::append(sMenuBar menuBar) -> void {
} }
auto pWindow::append(sSizable sizable) -> void { auto pWindow::append(sSizable sizable) -> void {
layout->setGeometry(self().geometry().setPosition(0, 0)); sizable->setGeometry(self().geometry().setPosition());
statusBarReposition(); statusBarReposition();
} }
@ -320,8 +320,8 @@ auto pWindow::setGeometry(Geometry geometry) -> void {
display:YES display:YES
]; ];
if(auto& layout = state().layout) { if(auto& sizable = state().sizable) {
layout->setGeometry(self().geometry().setPosition(0, 0)); sizable->setGeometry(self().geometry().setPosition());
} }
statusBarReposition(); statusBarReposition();
@ -402,8 +402,8 @@ auto pWindow::sizeEvent() -> void {
} }
} }
if(auto& layout = state().layout) { if(auto& sizable = state().sizable) {
layout->setGeometry(self().geometry().setPosition(0, 0)); sizable->setGeometry(self().geometry().setPosition());
} }
statusBarReposition(); statusBarReposition();

View File

@ -10,6 +10,10 @@ auto Application::font() -> Font {
return state.font; return state.font;
} }
auto Application::locale() -> Locale& {
return state.locale;
}
auto Application::modal() -> bool { auto Application::modal() -> bool {
return state.modal > 0; return state.modal > 0;
} }

View File

@ -3,6 +3,7 @@
#include <nall/directory.hpp> #include <nall/directory.hpp>
#include <nall/function.hpp> #include <nall/function.hpp>
#include <nall/image.hpp> #include <nall/image.hpp>
#include <nall/locale.hpp>
#include <nall/maybe.hpp> #include <nall/maybe.hpp>
#include <nall/path.hpp> #include <nall/path.hpp>
#include <nall/range.hpp> #include <nall/range.hpp>
@ -16,6 +17,7 @@
using nall::function; using nall::function;
using nall::image; using nall::image;
using nall::Locale;
using nall::maybe; using nall::maybe;
using nall::nothing; using nall::nothing;
using nall::set; using nall::set;
@ -373,6 +375,7 @@ struct Application {
static auto doMain() -> void; static auto doMain() -> void;
static auto font() -> Font; static auto font() -> Font;
static auto locale() -> Locale&;
static auto modal() -> bool; static auto modal() -> bool;
static auto name() -> string; static auto name() -> string;
static auto onMain(const function<void ()>& callback = {}) -> void; static auto onMain(const function<void ()>& callback = {}) -> void;
@ -405,9 +408,14 @@ struct Application {
static auto onQuit(const function<void ()>& callback = {}) -> void; static auto onQuit(const function<void ()>& callback = {}) -> void;
}; };
struct Namespace : Locale::Namespace {
Namespace(const string& value) : Locale::Namespace(Application::locale(), value) {}
};
//private: //private:
struct State { struct State {
Font font; Font font;
Locale locale;
int modal = 0; int modal = 0;
string name; string name;
function<void ()> onMain; function<void ()> onMain;

View File

@ -3,16 +3,50 @@
//shared functionality used for pObject on all platforms //shared functionality used for pObject on all platforms
struct mLock { struct mLock {
auto locked() const -> bool { return locks || Application::state.quit; }
auto lock() -> void { ++locks; }
auto unlock() -> void { --locks; }
struct Handle { struct Handle {
Handle(const mLock& self) : self(self) { ++self.locks; } Handle(const mLock* self) : self(self) {
~Handle() { --self.locks; } if(self) {
const mLock& self; ++self->locks;
}
}
~Handle() {
release();
}
auto release() -> bool {
if(self) {
--self->locks;
self = nullptr;
return true;
}
return false;
}
const mLock* self = nullptr;
}; };
auto acquire() const -> Handle { return {*this}; }
auto acquired() const -> bool {
return locks || Application::state.quit;
}
auto acquire() const -> Handle {
return {this};
}
//deprecated C-style manual functions
//prefer RAII acquire() functionality instead in newly written code
auto locked() const -> bool {
return acquired();
}
auto lock() -> void {
++locks;
}
auto unlock() -> void {
--locks;
}
mutable int locks = 0; mutable int locks = 0;
}; };

View File

@ -1,6 +1,8 @@
#if defined(Hiro_BrowserDialog) #if defined(Hiro_BrowserDialog)
struct BrowserDialogWindow { struct BrowserDialogWindow {
Application::Namespace tr{"BrowserDialog"};
BrowserDialogWindow(BrowserDialog::State& state) : state(state) {} BrowserDialogWindow(BrowserDialog::State& state) : state(state) {}
auto accept() -> void; auto accept() -> void;
auto activate() -> void; auto activate() -> void;
@ -171,10 +173,10 @@ auto BrowserDialogWindow::run() -> BrowserDialog::Response {
fileName.setBackgroundColor(acceptButton.enabled() ? Color{} : Color{255, 224, 224}); fileName.setBackgroundColor(acceptButton.enabled() ? Color{} : Color{255, 224, 224});
}); });
acceptButton.onActivate([&] { accept(); }); acceptButton.onActivate([&] { accept(); });
if(state.action.beginsWith("open")) acceptButton.setText("Open"); if(state.action.beginsWith("open")) acceptButton.setText(tr("Open"));
if(state.action.beginsWith("save")) acceptButton.setText("Save"); if(state.action.beginsWith("save")) acceptButton.setText(tr("Save"));
if(state.action.beginsWith("select")) acceptButton.setText("Select"); if(state.action.beginsWith("select")) acceptButton.setText(tr("Select"));
cancelButton.setText("Cancel").onActivate([&] { window.setModal(false); }); cancelButton.setText(tr("Cancel")).onActivate([&] { window.setModal(false); });
if(!state.filters) state.filters.append("All|*"); if(!state.filters) state.filters.append("All|*");
for(auto& filter : state.filters) { for(auto& filter : state.filters) {

View File

@ -129,7 +129,7 @@ auto mFixedLayoutCell::setVisible(bool visible) -> type& {
} }
auto mFixedLayoutCell::sizable() const -> Sizable { auto mFixedLayoutCell::sizable() const -> Sizable {
return state.sizable; return state.sizable ? state.sizable : Sizable();
} }
auto mFixedLayoutCell::synchronize() -> type& { auto mFixedLayoutCell::synchronize() -> type& {

View File

@ -99,8 +99,6 @@ auto mHorizontalLayout::setFont(const Font& font) -> type& {
auto mHorizontalLayout::setGeometry(Geometry geometry) -> type& { auto mHorizontalLayout::setGeometry(Geometry geometry) -> type& {
mSizable::setGeometry(geometry); mSizable::setGeometry(geometry);
auto window = parentWindow(true);
if(!window || !window->visible()) return *this;
geometry.setX(geometry.x() + padding().x()); geometry.setX(geometry.x() + padding().x());
geometry.setY(geometry.y() + padding().y()); geometry.setY(geometry.y() + padding().y());
@ -259,7 +257,7 @@ auto mHorizontalLayoutCell::setVisible(bool visible) -> type& {
} }
auto mHorizontalLayoutCell::sizable() const -> Sizable { auto mHorizontalLayoutCell::sizable() const -> Sizable {
return state.sizable; return state.sizable ? state.sizable : Sizable();
} }
auto mHorizontalLayoutCell::size() const -> Size { auto mHorizontalLayoutCell::size() const -> Size {

View File

@ -44,6 +44,8 @@ auto MessageDialog::warning(const string_vector& buttons) -> string {
} }
auto MessageDialog::_run() -> string { auto MessageDialog::_run() -> string {
Application::Namespace tr{"MessageDialog"};
Window window; Window window;
VerticalLayout layout{&window}; VerticalLayout layout{&window};
HorizontalLayout messageLayout{&layout, Size{~0, 0}, 5}; HorizontalLayout messageLayout{&layout, Size{~0, 0}, 5};
@ -57,14 +59,17 @@ auto MessageDialog::_run() -> string {
messageText.setText(state.text); messageText.setText(state.text);
for(auto n : range(state.buttons.size())) { for(auto n : range(state.buttons.size())) {
Button button{&controlLayout, Size{80, 0}, 5}; Button button{&controlLayout, Size{80, 0}, 5};
button.onActivate([&, n] { state.response = state.buttons[n]; window.setModal(false); }); button.onActivate([&, n] {
button.setText(state.buttons[n]); state.response = state.buttons[n];
window.setModal(false);
});
button.setText(tr(state.buttons[n]));
button.setFocused(); //the last button will have effective focus button.setFocused(); //the last button will have effective focus
} }
signed widthMessage = 5 + 16 + 5 + Font().size(state.text).width() + 5; int widthMessage = 5 + 16 + 5 + Font().size(state.text).width() + 5;
signed widthButtons = 5 + state.buttons.size() * 85; int widthButtons = 5 + state.buttons.size() * 85;
signed width = max(320, widthMessage, widthButtons); int width = max(320, widthMessage, widthButtons);
window.onClose([&] { window.setModal(false); }); window.onClose([&] { window.setModal(false); });
window.setTitle(state.title); window.setTitle(state.title);

View File

@ -134,8 +134,6 @@ auto mTableLayout::setFont(const Font& font) -> type& {
auto mTableLayout::setGeometry(Geometry geometry) -> type& { auto mTableLayout::setGeometry(Geometry geometry) -> type& {
mSizable::setGeometry(geometry); mSizable::setGeometry(geometry);
auto window = parentWindow(true);
if(!window || !window->visible()) return *this;
geometry.setX(geometry.x() + padding().x()); geometry.setX(geometry.x() + padding().x());
geometry.setY(geometry.y() + padding().y()); geometry.setY(geometry.y() + padding().y());
@ -394,7 +392,7 @@ auto mTableLayoutCell::setVisible(bool visible) -> type& {
} }
auto mTableLayoutCell::sizable() const -> Sizable { auto mTableLayoutCell::sizable() const -> Sizable {
return state.sizable; return state.sizable ? state.sizable : Sizable();
} }
auto mTableLayoutCell::size() const -> Size { auto mTableLayoutCell::size() const -> Size {

View File

@ -109,8 +109,6 @@ auto mVerticalLayout::setFont(const Font& font) -> type& {
auto mVerticalLayout::setGeometry(Geometry geometry) -> type& { auto mVerticalLayout::setGeometry(Geometry geometry) -> type& {
mSizable::setGeometry(geometry); mSizable::setGeometry(geometry);
auto window = parentWindow(true);
if(!window || !window->visible()) return *this;
geometry.setX(geometry.x() + padding().x()); geometry.setX(geometry.x() + padding().x());
geometry.setY(geometry.y() + padding().y()); geometry.setY(geometry.y() + padding().y());
@ -269,7 +267,7 @@ auto mVerticalLayoutCell::setVisible(bool visible) -> type& {
} }
auto mVerticalLayoutCell::sizable() const -> Sizable { auto mVerticalLayoutCell::sizable() const -> Sizable {
return state.sizable; return state.sizable ? state.sizable : Sizable();
} }
auto mVerticalLayoutCell::size() const -> Size { auto mVerticalLayoutCell::size() const -> Size {

View File

@ -1,10 +1,3 @@
namespace hiro {
struct pWindow;
struct pMenu;
struct pLayout;
struct pWidget;
}
#define Declare(Name, Base) \ #define Declare(Name, Base) \
p##Name(m##Name& reference) : p##Base(reference) {} \ p##Name(m##Name& reference) : p##Base(reference) {} \
auto self() const -> m##Name& { return (m##Name&)reference; } \ auto self() const -> m##Name& { return (m##Name&)reference; } \

View File

@ -39,6 +39,7 @@ auto pFrame::setFont(const Font& font) -> void {
} }
auto pFrame::setGeometry(Geometry geometry) -> void { auto pFrame::setGeometry(Geometry geometry) -> void {
pWidget::setGeometry(geometry);
if(auto& sizable = state().sizable) { if(auto& sizable = state().sizable) {
Size size = pFont::size(self().font(true), state().text); Size size = pFont::size(self().font(true), state().text);
if(!state().text) size.setHeight(10); if(!state().text) size.setHeight(10);

View File

@ -2,6 +2,50 @@
namespace hiro { namespace hiro {
static auto Label_draw(GtkWidget* widget, cairo_t* context, pLabel* p) -> int {
auto color = p->state().backgroundColor;
if(auto window = p->self().parentWindow(true)) {
if(!color) color = window->backgroundColor();
}
if(color) {
double red = (double)color.red() / 255.0;
double green = (double)color.green() / 255.0;
double blue = (double)color.blue() / 255.0;
double alpha = (double)color.alpha() / 255.0;
if(gdk_screen_is_composited(gdk_screen_get_default())
&& gdk_screen_get_rgba_visual(gdk_screen_get_default())
) {
cairo_set_source_rgba(context, red, green, blue, alpha);
} else {
cairo_set_source_rgb(context, red, green, blue);
}
cairo_set_operator(context, CAIRO_OPERATOR_SOURCE);
cairo_paint(context);
} else {
#if HIRO_GTK==3
auto style = gtk_widget_get_style_context(widget);
if(auto tabFrame = p->self().parentTabFrame(true)) {
if(auto self = tabFrame->self()) style = gtk_widget_get_style_context(self->gtkWidget);
}
GtkAllocation allocation;
gtk_widget_get_allocation(widget, &allocation);
gtk_render_background(style, context, 0, 0, allocation.width, allocation.height);
#endif
}
return false;
}
static auto Label_expose(GtkWidget* widget, GdkEvent* event, pLabel* p) -> int {
cairo_t* context = gdk_cairo_create(gtk_widget_get_window(widget));
Label_draw(widget, context, p);
cairo_destroy(context);
return false;
}
auto pLabel::construct() -> void { auto pLabel::construct() -> void {
gtkWidget = gtk_event_box_new(); gtkWidget = gtk_event_box_new();
subWidget = gtk_label_new(""); subWidget = gtk_label_new("");
@ -13,6 +57,12 @@ auto pLabel::construct() -> void {
setForegroundColor(state().foregroundColor); setForegroundColor(state().foregroundColor);
setText(state().text); setText(state().text);
#if HIRO_GTK==2
g_signal_connect(G_OBJECT(subWidget), "expose-event", G_CALLBACK(Label_expose), (gpointer)this);
#elif HIRO_GTK==3
g_signal_connect(G_OBJECT(subWidget), "draw", G_CALLBACK(Label_draw), (gpointer)this);
#endif
pWidget::construct(); pWidget::construct();
} }
@ -36,9 +86,9 @@ auto pLabel::setAlignment(Alignment alignment) -> void {
} }
auto pLabel::setBackgroundColor(Color color) -> void { auto pLabel::setBackgroundColor(Color color) -> void {
if(!color) color = self().parentWindow(true)->backgroundColor(); //GTK3 will paint the wrong background color when a label is inside of a TabFrame
auto gdkColor = CreateColor(color); //this is caused by the GtkEventBox wrapper that is required to paint custom backgrounds
gtk_widget_modify_bg(gtkWidget, GTK_STATE_NORMAL, color ? &gdkColor : nullptr); //handle background painting via "draw" or "expose-event" signals instead
} }
auto pLabel::setForegroundColor(Color color) -> void { auto pLabel::setForegroundColor(Color color) -> void {

View File

@ -157,8 +157,7 @@ auto pTabFrame::setFont(const Font& font) -> void {
auto pTabFrame::setGeometry(Geometry geometry) -> void { auto pTabFrame::setGeometry(Geometry geometry) -> void {
pWidget::setGeometry(geometry); pWidget::setGeometry(geometry);
geometry.setPosition();
geometry.setPosition(0, 0);
if(state().navigation == Navigation::Top || state().navigation == Navigation::Bottom) { if(state().navigation == Navigation::Top || state().navigation == Navigation::Bottom) {
geometry.setWidth(geometry.width() - 6); geometry.setWidth(geometry.width() - 6);
geometry.setHeight(geometry.height() - (15 + _tabHeight())); geometry.setHeight(geometry.height() - (15 + _tabHeight()));

View File

@ -66,7 +66,7 @@ GtkSelectionData* data, unsigned type, unsigned timestamp, pWindow* p) -> void {
static auto Window_getPreferredWidth(GtkWidget* widget, int* minimalWidth, int* naturalWidth) -> void { static auto Window_getPreferredWidth(GtkWidget* widget, int* minimalWidth, int* naturalWidth) -> void {
if(auto p = (pWindow*)g_object_get_data(G_OBJECT(widget), "hiro::window")) { if(auto p = (pWindow*)g_object_get_data(G_OBJECT(widget), "hiro::window")) {
*minimalWidth = 1; *minimalWidth = 1;
*naturalWidth = 1; //p->state().geometry.width(); *naturalWidth = p->state().geometry.width();
} }
} }
@ -114,7 +114,7 @@ static auto Window_sizeRequest(GtkWidget* widget, GtkRequisition* requisition, p
static auto Window_stateEvent(GtkWidget* widget, GdkEvent* event, pWindow* p) -> void { static auto Window_stateEvent(GtkWidget* widget, GdkEvent* event, pWindow* p) -> void {
p->_synchronizeState(); p->_synchronizeState();
/*if(event->type == GDK_WINDOW_STATE) { if(event->type == GDK_WINDOW_STATE) {
auto windowStateEvent = (GdkEventWindowState*)event; auto windowStateEvent = (GdkEventWindowState*)event;
if(windowStateEvent->changed_mask & GDK_WINDOW_STATE_MAXIMIZED) { if(windowStateEvent->changed_mask & GDK_WINDOW_STATE_MAXIMIZED) {
p->state().maximized = windowStateEvent->new_window_state & GDK_WINDOW_STATE_MAXIMIZED; p->state().maximized = windowStateEvent->new_window_state & GDK_WINDOW_STATE_MAXIMIZED;
@ -122,7 +122,7 @@ static auto Window_stateEvent(GtkWidget* widget, GdkEvent* event, pWindow* p) ->
if(windowStateEvent->changed_mask & GDK_WINDOW_STATE_ICONIFIED) { if(windowStateEvent->changed_mask & GDK_WINDOW_STATE_ICONIFIED) {
p->state().minimized = windowStateEvent->new_window_state & GDK_WINDOW_STATE_ICONIFIED; p->state().minimized = windowStateEvent->new_window_state & GDK_WINDOW_STATE_ICONIFIED;
} }
}*/ }
} }
auto pWindow::construct() -> void { auto pWindow::construct() -> void {
@ -191,7 +191,7 @@ auto pWindow::construct() -> void {
g_signal_connect(G_OBJECT(formContainer), "size-request", G_CALLBACK(Window_sizeRequest), (gpointer)this); g_signal_connect(G_OBJECT(formContainer), "size-request", G_CALLBACK(Window_sizeRequest), (gpointer)this);
#elif HIRO_GTK==3 #elif HIRO_GTK==3
auto widgetClass = GTK_WIDGET_GET_CLASS(formContainer); auto widgetClass = GTK_WIDGET_GET_CLASS(formContainer);
widgetClass->get_preferred_width = Window_getPreferredWidth; widgetClass->get_preferred_width = Window_getPreferredWidth;
widgetClass->get_preferred_height = Window_getPreferredHeight; widgetClass->get_preferred_height = Window_getPreferredHeight;
#endif #endif
g_signal_connect(G_OBJECT(widget), "window-state-event", G_CALLBACK(Window_stateEvent), (gpointer)this); g_signal_connect(G_OBJECT(widget), "window-state-event", G_CALLBACK(Window_stateEvent), (gpointer)this);

View File

@ -1,8 +1,6 @@
#ifndef HIRO_CPP #ifndef HIRO_CPP
#define HIRO_CPP #define HIRO_CPP
#define foreach(T, ...) ([&] { for(auto& $ : T) { __VA_ARGS__; }}())
#include "components.hpp" #include "components.hpp"
#include "core/core.cpp" #include "core/core.cpp"
#include "extension/extension.cpp" #include "extension/extension.cpp"

View File

@ -23,6 +23,7 @@ auto pMenuRadioItem::construct() -> void {
} }
auto pMenuRadioItem::destruct() -> void { auto pMenuRadioItem::destruct() -> void {
if(Application::state.quit) return; //TODO: hack
delete qtMenuRadioItem; delete qtMenuRadioItem;
delete qtActionGroup; delete qtActionGroup;
qtMenuRadioItem = nullptr; qtMenuRadioItem = nullptr;
@ -47,6 +48,7 @@ auto pMenuRadioItem::setGroup(sGroup group) -> void {
} }
} }
} }
_setState();
} }
auto pMenuRadioItem::setText(const string& text) -> void { auto pMenuRadioItem::setText(const string& text) -> void {

View File

@ -23,6 +23,7 @@ auto pMenu::construct() -> void {
} }
auto pMenu::destruct() -> void { auto pMenu::destruct() -> void {
if(Application::state.quit) return; //TODO: hack
delete qtMenu; delete qtMenu;
qtMenu = nullptr; qtMenu = nullptr;
} }

View File

@ -38,12 +38,10 @@
#include "widget/check-label.cpp" #include "widget/check-label.cpp"
#include "widget/combo-button.cpp" #include "widget/combo-button.cpp"
#include "widget/combo-button-item.cpp" #include "widget/combo-button-item.cpp"
#include "widget/console.cpp"
#include "widget/frame.cpp" #include "widget/frame.cpp"
#include "widget/hex-edit.cpp" #include "widget/hex-edit.cpp"
#include "widget/horizontal-scroll-bar.cpp" #include "widget/horizontal-scroll-bar.cpp"
#include "widget/horizontal-slider.cpp" #include "widget/horizontal-slider.cpp"
#include "widget/icon-view.cpp"
#include "widget/label.cpp" #include "widget/label.cpp"
#include "widget/line-edit.cpp" #include "widget/line-edit.cpp"
#include "widget/progress-bar.cpp" #include "widget/progress-bar.cpp"

View File

@ -1,5 +1,3 @@
#include "settings.hpp"
#define Declare(Name, Base) \ #define Declare(Name, Base) \
p##Name(m##Name& reference) : p##Base(reference) {} \ p##Name(m##Name& reference) : p##Base(reference) {} \
auto self() const -> m##Name& { return (m##Name&)reference; } \ auto self() const -> m##Name& { return (m##Name&)reference; } \
@ -8,6 +6,7 @@
auto destruct() -> void override; \ auto destruct() -> void override; \
#include "application.hpp" #include "application.hpp"
#include "settings.hpp"
#include "font.hpp" #include "font.hpp"
#include "desktop.hpp" #include "desktop.hpp"
#include "monitor.hpp" #include "monitor.hpp"

View File

@ -15,6 +15,10 @@ static auto CreateBrush(Color color) -> QBrush {
return color ? QColor(color.red(), color.green(), color.blue()) : QBrush(); return color ? QColor(color.red(), color.green(), color.blue()) : QBrush();
} }
static auto CreateColor(Color color, QColor fallback = {}) -> QColor {
return color ? QColor(color.red(), color.green(), color.blue()) : fallback;
}
static auto CreateIcon(const image& icon, bool scale = false) -> QIcon { static auto CreateIcon(const image& icon, bool scale = false) -> QIcon {
if(!icon) return QIcon(); if(!icon) return QIcon();
auto qtBuffer = icon; auto qtBuffer = icon;

View File

@ -18,7 +18,7 @@ auto pCanvas::destruct() -> void {
} }
auto pCanvas::minimumSize() const -> Size { auto pCanvas::minimumSize() const -> Size {
if(auto& icon = state().icon) return {(int)icon.width(), (int)icon.height()}; if(auto& icon = state().icon) return {icon.width(), icon.height()};
return {0, 0}; return {0, 0};
} }

View File

@ -10,6 +10,7 @@ auto pComboButton::construct() -> void {
} }
auto pComboButton::destruct() -> void { auto pComboButton::destruct() -> void {
if(Application::state.quit) return; //TODO: hack
delete qtComboButton; delete qtComboButton;
qtWidget = qtComboButton = nullptr; qtWidget = qtComboButton = nullptr;
} }

View File

@ -1,56 +0,0 @@
#if defined(Hiro_Console)
namespace hiro {
void pConsole::print(string text) {
}
void pConsole::reset() {
}
void pConsole::setBackgroundColor(Color color) {
QPalette palette = qtConsole->palette();
palette.setColor(QPalette::Base, QColor(color.red, color.green, color.blue));
qtConsole->setPalette(palette);
qtConsole->setAutoFillBackground(true);
}
void pConsole::setForegroundColor(Color color) {
QPalette palette = qtConsole->palette();
palette.setColor(QPalette::Text, QColor(color.red, color.green, color.blue));
qtConsole->setPalette(palette);
}
void pConsole::setPrompt(string prompt) {
}
void pConsole::constructor() {
qtWidget = qtConsole = new QtConsole(*this);
pWidget::synchronizeState();
}
void pConsole::destructor() {
delete qtConsole;
qtWidget = qtConsole = nullptr;
}
void pConsole::orphan() {
destructor();
constructor();
}
void pConsole::keyPressEvent(QKeyEvent* event) {
}
void pConsole::QtConsole::keyPressEvent(QKeyEvent* event) {
self.keyPressEvent(event);
}
void pConsole::QtConsole::keyPressEventAcknowledge(QKeyEvent* event) {
QTextEdit::keyPressEvent(event);
}
}
#endif

View File

@ -22,6 +22,8 @@ auto pHexEdit::construct() -> void {
qtScrollBar->connect(qtScrollBar, SIGNAL(actionTriggered(int)), SLOT(onScroll())); qtScrollBar->connect(qtScrollBar, SIGNAL(actionTriggered(int)), SLOT(onScroll()));
pWidget::construct(); pWidget::construct();
setBackgroundColor(state().backgroundColor);
setForegroundColor(state().foregroundColor);
_setState(); _setState();
} }
@ -39,7 +41,12 @@ auto pHexEdit::setAddress(unsigned address) -> void {
} }
auto pHexEdit::setBackgroundColor(Color color) -> void { auto pHexEdit::setBackgroundColor(Color color) -> void {
_setState(); static auto defaultColor = qtHexEdit->palette().color(QPalette::Base);
auto palette = qtHexEdit->palette();
palette.setColor(QPalette::Base, CreateColor(color, defaultColor));
qtHexEdit->setPalette(palette);
qtHexEdit->setAutoFillBackground((bool)color);
} }
auto pHexEdit::setColumns(unsigned columns) -> void { auto pHexEdit::setColumns(unsigned columns) -> void {
@ -47,7 +54,11 @@ auto pHexEdit::setColumns(unsigned columns) -> void {
} }
auto pHexEdit::setForegroundColor(Color color) -> void { auto pHexEdit::setForegroundColor(Color color) -> void {
_setState(); static auto defaultColor = qtHexEdit->palette().color(QPalette::Text);
auto palette = qtHexEdit->palette();
palette.setColor(QPalette::Text, color ? CreateColor(color) : defaultColor);
qtHexEdit->setPalette(palette);
} }
auto pHexEdit::setLength(unsigned length) -> void { auto pHexEdit::setLength(unsigned length) -> void {
@ -242,29 +253,13 @@ auto pHexEdit::_scrollTo(signed position) -> void {
} }
auto pHexEdit::_setState() -> void { auto pHexEdit::_setState() -> void {
lock(); auto lock = acquire();
if(auto color = state().backgroundColor) {
QPalette palette = qtHexEdit->palette();
palette.setColor(QPalette::Base, QColor(color.red(), color.green(), color.blue()));
qtHexEdit->setPalette(palette);
qtHexEdit->setAutoFillBackground(true);
} else {
//todo
}
if(auto color = state().foregroundColor) {
QPalette palette = qtHexEdit->palette();
palette.setColor(QPalette::Text, QColor(color.red(), color.green(), color.blue()));
qtHexEdit->setPalette(palette);
} else {
//todo
}
//add one if last row is not equal to column length (eg only part of the row is present) //add one if last row is not equal to column length (eg only part of the row is present)
bool indivisible = state().columns == 0 || (state().length % state().columns) != 0; bool indivisible = state().columns == 0 || (state().length % state().columns) != 0;
qtScrollBar->setRange(0, state().length / state().columns + indivisible - state().rows); qtScrollBar->setRange(0, state().length / state().columns + indivisible - state().rows);
qtScrollBar->setSliderPosition(state().address / state().columns); qtScrollBar->setSliderPosition(state().address / state().columns);
qtScrollBar->setPageStep(state().rows); qtScrollBar->setPageStep(state().rows);
update(); update();
unlock();
} }
auto QtHexEdit::keyPressEvent(QKeyEvent* event) -> void { auto QtHexEdit::keyPressEvent(QKeyEvent* event) -> void {

View File

@ -1,152 +0,0 @@
#if defined(Hiro_IconView)
namespace hiro {
void pIconView::append() {
lock();
auto item = new QListWidgetItem(qtIconView);
unlock();
}
void pIconView::remove(unsigned selection) {
lock();
if(auto item = qtIconView->item(selection)) {
delete item;
}
unlock();
}
void pIconView::reset() {
lock();
qtIconView->clear();
unlock();
}
void pIconView::setBackgroundColor(Color color) {
QPalette palette = qtIconView->palette();
palette.setColor(QPalette::Base, QColor(color.red, color.green, color.blue));
qtIconView->setPalette(palette);
qtIconView->setAutoFillBackground(true);
}
void pIconView::setFlow(Orientation flow) {
qtIconView->setFlow(flow == Orientation::Horizontal ? QListView::LeftToRight : QListView::TopToBottom);
qtIconView->resize(qtIconView->size()); //adjust visibility of scroll bars
}
void pIconView::setForegroundColor(Color color) {
QPalette palette = qtIconView->palette();
palette.setColor(QPalette::Text, QColor(color.red, color.green, color.blue));
qtIconView->setPalette(palette);
}
void pIconView::setImage(unsigned selection, const image& image) {
if(auto item = qtIconView->item(selection)) {
item->setIcon(CreateIcon(image));
}
}
void pIconView::setOrientation(Orientation orientation) {
qtIconView->setViewMode(orientation == Orientation::Horizontal ? QListView::ListMode : QListView::IconMode);
qtIconView->setWrapping(true);
}
void pIconView::setSelected(unsigned selection, bool selected) {
lock();
if(auto item = qtIconView->item(selection)) {
item->setSelected(selected);
}
unlock();
}
void pIconView::setSelected(const vector<unsigned>& selections) {
lock();
qtIconView->clearSelection();
for(auto& selection : selections) {
if(auto item = qtIconView->item(selection)) {
item->setSelected(true);
}
}
unlock();
}
void pIconView::setSelectedAll() {
lock();
qtIconView->selectAll();
unlock();
}
void pIconView::setSelectedNone() {
lock();
qtIconView->clearSelection();
unlock();
}
void pIconView::setSingleSelection(bool singleSelection) {
qtIconView->setSelectionMode(singleSelection ? QAbstractItemView::SingleSelection : QAbstractItemView::ExtendedSelection);
}
void pIconView::setText(unsigned selection, const string& text) {
if(auto item = qtIconView->item(selection)) {
item->setText(QString::fromUtf8(text));
}
}
void pIconView::constructor() {
qtWidget = qtIconView = new QtListWidget;
qtIconView->setContextMenuPolicy(Qt::CustomContextMenu);
qtIconView->setMovement(QListView::Static);
qtIconView->setResizeMode(QListView::Adjust);
qtIconView->setSelectionRectVisible(true);
qtIconView->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
qtIconView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
connect(qtIconView, SIGNAL(itemActivated(QListWidgetItem*)), SLOT(onActivate()));
connect(qtIconView, SIGNAL(itemSelectionChanged()), SLOT(onChange()));
connect(qtIconView, SIGNAL(customContextMenuRequested(const QPoint&)), SLOT(onContext()));
setFlow(iconView.state.flow);
setOrientation(iconView.state.orientation);
setSingleSelection(iconView.state.singleSelection);
}
void pIconView::destructor() {
delete qtIconView;
qtWidget = qtIconView = nullptr;
}
void pIconView::orphan() {
destructor();
constructor();
}
void pIconView::onActivate() {
if(!locked() && iconView.onActivate) iconView.onActivate();
}
void pIconView::onChange() {
for(auto& selected : iconView.state.selected) selected = false;
for(unsigned n = 0; n < qtIconView->count(); n++) {
if(auto item = qtIconView->item(n)) {
if(item->isSelected()) iconView.state.selected[n] = true;
}
}
if(!locked() && iconView.onChange) iconView.onChange();
}
void pIconView::onContext() {
if(!locked() && iconView.onContext) iconView.onContext();
}
void pIconView::QtListWidget::resizeEvent(QResizeEvent* event) {
//Qt::ScrollBarAsNeeded results in the scroll bar area being reserved from the icon viewport even when scroll bar is hidden
//this creates the appearance of an invisible gap that wastes precious screen space
//below code simulates a Qt::ScrollBarAsNeeded which uses the extra space when the scroll bar is hidden
setHorizontalScrollBarPolicy(horizontalScrollBar()->maximum() > horizontalScrollBar()->minimum() ? Qt::ScrollBarAlwaysOn : Qt::ScrollBarAlwaysOff);
setVerticalScrollBarPolicy(verticalScrollBar()->maximum() > verticalScrollBar()->minimum() ? Qt::ScrollBarAlwaysOn : Qt::ScrollBarAlwaysOff);
return QListWidget::resizeEvent(event);
}
}
#endif

View File

@ -28,27 +28,20 @@ auto pLabel::setAlignment(Alignment alignment) -> void {
} }
auto pLabel::setBackgroundColor(Color color) -> void { auto pLabel::setBackgroundColor(Color color) -> void {
if(!color) color = self().parentWindow(true)->backgroundColor(); static auto defaultColor = qtLabel->palette().color(QPalette::Base);
if(color) {
QPalette palette = qtLabel->palette(); auto palette = qtLabel->palette();
palette.setColor(QPalette::Base, QColor(color.red(), color.green(), color.blue())); palette.setColor(QPalette::Base, CreateColor(color, defaultColor));
qtLabel->setBackgroundRole(QPalette::Base); qtLabel->setPalette(palette);
qtLabel->setPalette(palette); qtLabel->setAutoFillBackground((bool)color);
qtLabel->setAutoFillBackground(true);
} else {
//todo
}
} }
auto pLabel::setForegroundColor(Color color) -> void { auto pLabel::setForegroundColor(Color color) -> void {
if(color) { static auto defaultColor = qtLabel->palette().color(QPalette::Text);
QPalette palette = qtLabel->palette();
palette.setColor(QPalette::Text, QColor(color.red(), color.green(), color.blue())); auto palette = qtLabel->palette();
qtLabel->setForegroundRole(QPalette::Text); palette.setColor(QPalette::Text, CreateColor(color, defaultColor));
qtLabel->setPalette(palette); qtLabel->setPalette(palette);
} else {
//todo
}
} }
auto pLabel::setText(const string& text) -> void { auto pLabel::setText(const string& text) -> void {

View File

@ -8,7 +8,10 @@ auto pLineEdit::construct() -> void {
qtLineEdit->connect(qtLineEdit, SIGNAL(textEdited(const QString&)), SLOT(onChange())); qtLineEdit->connect(qtLineEdit, SIGNAL(textEdited(const QString&)), SLOT(onChange()));
pWidget::construct(); pWidget::construct();
_setState(); setBackgroundColor(state().backgroundColor);
setEditable(state().editable);
setForegroundColor(state().foregroundColor);
setText(state().text);
} }
auto pLineEdit::destruct() -> void { auto pLineEdit::destruct() -> void {
@ -22,38 +25,27 @@ auto pLineEdit::minimumSize() const -> Size {
} }
auto pLineEdit::setBackgroundColor(Color color) -> void { auto pLineEdit::setBackgroundColor(Color color) -> void {
_setState(); static auto defaultColor = qtLineEdit->palette().color(QPalette::Base);
auto palette = qtLineEdit->palette();
palette.setColor(QPalette::Base, CreateColor(color, defaultColor));
qtLineEdit->setPalette(palette);
qtLineEdit->setAutoFillBackground((bool)color);
} }
auto pLineEdit::setEditable(bool editable) -> void { auto pLineEdit::setEditable(bool editable) -> void {
_setState(); qtLineEdit->setReadOnly(!state().editable);
} }
auto pLineEdit::setForegroundColor(Color color) -> void { auto pLineEdit::setForegroundColor(Color color) -> void {
_setState(); static auto defaultColor = qtLineEdit->palette().color(QPalette::Text);
auto palette = qtLineEdit->palette();
palette.setColor(QPalette::Text, CreateColor(color, defaultColor));
qtLineEdit->setPalette(palette);
} }
auto pLineEdit::setText(const string& text) -> void { auto pLineEdit::setText(const string& text) -> void {
_setState();
}
auto pLineEdit::_setState() -> void {
if(auto color = state().backgroundColor) {
QPalette palette = qtLineEdit->palette();
palette.setColor(QPalette::Base, QColor(color.red(), color.green(), color.blue()));
qtLineEdit->setPalette(palette);
qtLineEdit->setAutoFillBackground(true);
} else {
//todo
}
qtLineEdit->setReadOnly(!state().editable);
if(auto color = state().foregroundColor) {
QPalette palette = qtLineEdit->palette();
palette.setColor(QPalette::Text, QColor(color.red(), color.green(), color.blue()));
qtLineEdit->setPalette(palette);
} else {
//todo
}
qtLineEdit->setText(QString::fromUtf8(state().text)); qtLineEdit->setText(QString::fromUtf8(state().text));
} }

View File

@ -11,8 +11,6 @@ struct pLineEdit : pWidget {
auto setForegroundColor(Color color) -> void; auto setForegroundColor(Color color) -> void;
auto setText(const string& text) -> void; auto setText(const string& text) -> void;
auto _setState() -> void;
QtLineEdit* qtLineEdit = nullptr; QtLineEdit* qtLineEdit = nullptr;
}; };

View File

@ -27,7 +27,7 @@ auto pTabFrameItem::setClosable(bool closable) -> void {
auto pTabFrameItem::setGeometry(Geometry geometry) -> void { auto pTabFrameItem::setGeometry(Geometry geometry) -> void {
if(auto& sizable = state().sizable) { if(auto& sizable = state().sizable) {
auto offset = qtTabFrameItem->geometry(); auto offset = qtTabFrameItem->geometry();
geometry.setPosition({0, 0}); geometry.setPosition();
geometry.setWidth(geometry.width() - (geometry.width() - offset.width())); geometry.setWidth(geometry.width() - (geometry.width() - offset.width()));
geometry.setHeight(geometry.height() - (geometry.height() - offset.height())); geometry.setHeight(geometry.height() - (geometry.height() - offset.height()));
sizable->setGeometry(geometry); sizable->setGeometry(geometry);
@ -72,7 +72,7 @@ auto pTabFrameItem::_setState() -> void {
geometry.setWidth(geometry.width() - (geometry.width() - offset.width())); geometry.setWidth(geometry.width() - (geometry.width() - offset.width()));
geometry.setHeight(geometry.height() - (geometry.height() - offset.height())); geometry.setHeight(geometry.height() - (geometry.height() - offset.height()));
sizable->setGeometry(geometry); sizable->setGeometry(geometry);
sizable->setVisible(sizable->visible(true)); sizable->setVisible(sizable->visible());
} }
} }
} }

View File

@ -11,6 +11,7 @@ auto pTabFrame::construct() -> void {
} }
auto pTabFrame::destruct() -> void { auto pTabFrame::destruct() -> void {
if(Application::state.quit) return; //TODO: hack
delete qtTabFrame; delete qtTabFrame;
qtWidget = qtTabFrame = nullptr; qtWidget = qtTabFrame = nullptr;
} }

View File

@ -52,7 +52,7 @@ auto pTableViewItem::_parent() -> maybe<pTableView&> {
auto pTableViewItem::_setState() -> void { auto pTableViewItem::_setState() -> void {
if(auto parent = _parent()) { if(auto parent = _parent()) {
parent->lock(); auto lock = parent->acquire();
qtItem->setSelected(state().selected); qtItem->setSelected(state().selected);
if(state().selected) { if(state().selected) {
parent->qtTableView->setCurrentItem(qtItem); parent->qtTableView->setCurrentItem(qtItem);

View File

@ -32,6 +32,7 @@ auto pTableView::construct() -> void {
} }
auto pTableView::destruct() -> void { auto pTableView::destruct() -> void {
if(Application::state.quit) return; //TODO: hack
delete qtTableViewDelegate; delete qtTableViewDelegate;
delete qtTableView; delete qtTableView;
qtWidget = qtTableView = nullptr; qtWidget = qtTableView = nullptr;
@ -99,15 +100,13 @@ auto pTableView::setAlignment(Alignment alignment) -> void {
} }
auto pTableView::setBackgroundColor(Color color) -> void { auto pTableView::setBackgroundColor(Color color) -> void {
if(color) { //note: QPalette::AlternateBase can be used for alternating row colors
QPalette palette = qtTableView->palette(); static auto defaultColor = qtTableView->palette().color(QPalette::Base);
palette.setColor(QPalette::Base, QColor(color.red(), color.green(), color.blue()));
palette.setColor(QPalette::AlternateBase, QColor(max(0, (signed)color.red() - 17), max(0, (signed)color.green() - 17), max(0, (signed)color.blue() - 17))); auto palette = qtTableView->palette();
qtTableView->setPalette(palette); palette.setColor(QPalette::Base, CreateColor(color, defaultColor));
qtTableView->setAutoFillBackground(true); qtTableView->setPalette(palette);
} else { qtTableView->setAutoFillBackground((bool)color);
//todo: set default color
}
} }
auto pTableView::setBatchable(bool batchable) -> void { auto pTableView::setBatchable(bool batchable) -> void {
@ -121,13 +120,12 @@ auto pTableView::setBordered(bool bordered) -> void {
} }
auto pTableView::setForegroundColor(Color color) -> void { auto pTableView::setForegroundColor(Color color) -> void {
if(color) { static auto defaultColor = qtTableView->palette().color(QPalette::Text);
QPalette palette = qtTableView->palette();
palette.setColor(QPalette::Text, QColor(color.red(), color.green(), color.blue())); auto palette = qtTableView->palette();
qtTableView->setPalette(palette); palette.setColor(QPalette::Text, CreateColor(color, defaultColor));
} else { qtTableView->setPalette(palette);
//todo: set default color qtTableView->setAutoFillBackground((bool)color);
}
} }
//called on resize/show events //called on resize/show events

View File

@ -7,16 +7,24 @@ auto pTextEdit::construct() -> void {
qtTextEdit->connect(qtTextEdit, SIGNAL(textChanged()), SLOT(onChange())); qtTextEdit->connect(qtTextEdit, SIGNAL(textChanged()), SLOT(onChange()));
pWidget::construct(); pWidget::construct();
setBackgroundColor(state().backgroundColor);
setForegroundColor(state().foregroundColor);
_setState(); _setState();
} }
auto pTextEdit::destruct() -> void { auto pTextEdit::destruct() -> void {
if(Application::state.quit) return; //TODO: hack
delete qtTextEdit; delete qtTextEdit;
qtWidget = qtTextEdit = nullptr; qtWidget = qtTextEdit = nullptr;
} }
auto pTextEdit::setBackgroundColor(Color color) -> void { auto pTextEdit::setBackgroundColor(Color color) -> void {
_setState(); static auto defaultColor = qtTextEdit->palette().color(QPalette::Base);
auto palette = qtTextEdit->palette();
palette.setColor(QPalette::Base, CreateColor(color, defaultColor));
qtTextEdit->setPalette(palette);
qtTextEdit->setAutoFillBackground((bool)color);
} }
auto pTextEdit::setCursor(Cursor cursor) -> void { auto pTextEdit::setCursor(Cursor cursor) -> void {
@ -28,7 +36,11 @@ auto pTextEdit::setEditable(bool editable) -> void {
} }
auto pTextEdit::setForegroundColor(Color color) -> void { auto pTextEdit::setForegroundColor(Color color) -> void {
_setState(); static auto defaultColor = qtTextEdit->palette().color(QPalette::Text);
auto palette = qtTextEdit->palette();
palette.setColor(QPalette::Text, CreateColor(color, defaultColor));
qtTextEdit->setPalette(palette);
} }
auto pTextEdit::setText(const string& text) -> void { auto pTextEdit::setText(const string& text) -> void {
@ -44,14 +56,6 @@ auto pTextEdit::text() const -> string {
} }
auto pTextEdit::_setState() -> void { auto pTextEdit::_setState() -> void {
if(auto color = state().backgroundColor) {
QPalette palette = qtTextEdit->palette();
palette.setColor(QPalette::Base, QColor(color.red(), color.green(), color.blue()));
qtTextEdit->setPalette(palette);
qtTextEdit->setAutoFillBackground(true);
} else {
//todo
}
QTextCursor cursor = qtTextEdit->textCursor(); QTextCursor cursor = qtTextEdit->textCursor();
signed lastCharacter = strlen(qtTextEdit->toPlainText().toUtf8().constData()); signed lastCharacter = strlen(qtTextEdit->toPlainText().toUtf8().constData());
cursor.setPosition(max(0, min(lastCharacter, state().cursor.offset()))); cursor.setPosition(max(0, min(lastCharacter, state().cursor.offset())));
@ -61,13 +65,6 @@ auto pTextEdit::_setState() -> void {
? Qt::TextEditorInteraction ? Qt::TextEditorInteraction
: Qt::TextSelectableByKeyboard | Qt::TextSelectableByMouse : Qt::TextSelectableByKeyboard | Qt::TextSelectableByMouse
); );
if(auto color = state().foregroundColor) {
QPalette palette = qtTextEdit->palette();
palette.setColor(QPalette::Text, QColor(color.red(), color.green(), color.blue()));
qtTextEdit->setPalette(palette);
} else {
//todo
}
qtTextEdit->setWordWrapMode(state().wordWrap ? QTextOption::WordWrap : QTextOption::NoWrap); qtTextEdit->setWordWrapMode(state().wordWrap ? QTextOption::WordWrap : QTextOption::NoWrap);
qtTextEdit->setHorizontalScrollBarPolicy(state().wordWrap ? Qt::ScrollBarAlwaysOff : Qt::ScrollBarAlwaysOn); qtTextEdit->setHorizontalScrollBarPolicy(state().wordWrap ? Qt::ScrollBarAlwaysOff : Qt::ScrollBarAlwaysOn);
qtTextEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); qtTextEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);

View File

@ -44,6 +44,7 @@ auto pWindow::construct() -> void {
} }
auto pWindow::destruct() -> void { auto pWindow::destruct() -> void {
if(Application::state.quit) return; //TODO: hack
delete qtStatusBar; delete qtStatusBar;
delete qtContainer; delete qtContainer;
delete qtMenuBar; delete qtMenuBar;
@ -90,14 +91,14 @@ auto pWindow::remove(sStatusBar statusBar) -> void {
} }
auto pWindow::setBackgroundColor(Color color) -> void { auto pWindow::setBackgroundColor(Color color) -> void {
if(color) { static auto defaultColor = qtContainer->palette().color(QPalette::Background);
QPalette palette;
palette.setColor(QPalette::Background, QColor(color.red(), color.green(), color.blue() /*, color.alpha() */)); auto palette = qtContainer->palette();
qtContainer->setPalette(palette); palette.setColor(QPalette::Background, CreateColor(color, defaultColor));
qtContainer->setAutoFillBackground(true); qtContainer->setPalette(palette);
//translucency results are very unpleasant without a compositor; so disable for now qtContainer->setAutoFillBackground((bool)color);
//qtWindow->setAttribute(Qt::WA_TranslucentBackground, color.alpha() != 255); //translucency results are very unpleasant without a compositor; so disable for now
} //qtWindow->setAttribute(Qt::WA_TranslucentBackground, color && color.alpha() != 255);
} }
auto pWindow::setDismissable(bool dismissable) -> void { auto pWindow::setDismissable(bool dismissable) -> void {
@ -133,7 +134,7 @@ auto pWindow::setFullScreen(bool fullScreen) -> void {
} }
auto pWindow::setGeometry(Geometry geometry) -> void { auto pWindow::setGeometry(Geometry geometry) -> void {
lock(); auto lock = acquire();
Application::processEvents(); Application::processEvents();
#if HIRO_QT==4 #if HIRO_QT==4
QApplication::syncX(); QApplication::syncX();
@ -145,13 +146,14 @@ auto pWindow::setGeometry(Geometry geometry) -> void {
qtWindow->move(geometry.x() - frameMargin().x(), geometry.y() - frameMargin().y()); qtWindow->move(geometry.x() - frameMargin().x(), geometry.y() - frameMargin().y());
//qtWindow->adjustSize() fails if larger than 2/3rds screen size //qtWindow->adjustSize() fails if larger than 2/3rds screen size
qtWindow->resize(qtWindow->sizeHint()); qtWindow->resize(qtWindow->sizeHint());
qtContainer->setMinimumSize(1, 1);
if(state().resizable) { if(state().resizable) {
//required to allow shrinking window from default size setMaximumSize(state().maximumSize);
qtWindow->setMinimumSize(1, 1); setMinimumSize(state().minimumSize);
qtContainer->setMinimumSize(1, 1); } else {
setMaximumSize(geometry.size());
setMinimumSize(geometry.size());
} }
unlock();
} }
auto pWindow::setMaximized(bool maximized) -> void { auto pWindow::setMaximized(bool maximized) -> void {
@ -159,7 +161,14 @@ auto pWindow::setMaximized(bool maximized) -> void {
} }
auto pWindow::setMaximumSize(Size size) -> void { auto pWindow::setMaximumSize(Size size) -> void {
//todo static auto maximumSize = qtWindow->maximumSize();
if(size) {
//once this is called, no matter what the size is, Qt will no longer allow the window to be maximized
qtWindow->setMaximumSize(size.width(), size.height() + _menuHeight() + _statusHeight());
} else {
qtWindow->setMaximumSize(maximumSize);
}
} }
auto pWindow::setMinimized(bool minimized) -> void { auto pWindow::setMinimized(bool minimized) -> void {
@ -167,7 +176,7 @@ auto pWindow::setMinimized(bool minimized) -> void {
} }
auto pWindow::setMinimumSize(Size size) -> void { auto pWindow::setMinimumSize(Size size) -> void {
//todo qtWindow->setMinimumSize(size.width(), size.height() + _menuHeight() + _statusHeight());
} }
auto pWindow::setModal(bool modal) -> void { auto pWindow::setModal(bool modal) -> void {
@ -200,7 +209,7 @@ auto pWindow::setResizable(bool resizable) -> void {
} }
auto pWindow::setTitle(const string& text) -> void { auto pWindow::setTitle(const string& text) -> void {
qtWindow->setWindowTitle(QString::fromUtf8(text)); qtWindow->setWindowTitle(text ? QString::fromUtf8(text) : " ");
} }
auto pWindow::setVisible(bool visible) -> void { auto pWindow::setVisible(bool visible) -> void {
@ -220,8 +229,10 @@ auto pWindow::_append(mWidget& widget) -> void {
} }
auto pWindow::_menuHeight() const -> uint { auto pWindow::_menuHeight() const -> uint {
if(!qtMenuBar->isVisible()) return 0; if(auto& menuBar = state().menuBar) {
return settings.geometry.menuHeight + _menuTextHeight(); if(menuBar->visible()) return settings.geometry.menuHeight + _menuTextHeight();
}
return 0;
} }
auto pWindow::_menuTextHeight() const -> uint { auto pWindow::_menuTextHeight() const -> uint {
@ -235,8 +246,10 @@ auto pWindow::_menuTextHeight() const -> uint {
} }
auto pWindow::_statusHeight() const -> uint { auto pWindow::_statusHeight() const -> uint {
if(!qtStatusBar->isVisible()) return 0; if(auto& statusBar = state().statusBar) {
return settings.geometry.statusHeight + _statusTextHeight(); if(statusBar->visible()) return settings.geometry.statusHeight + _statusTextHeight();
}
return 0;
} }
auto pWindow::_statusTextHeight() const -> uint { auto pWindow::_statusTextHeight() const -> uint {
@ -314,7 +327,7 @@ auto QtWindow::keyReleaseEvent(QKeyEvent* event) -> void {
//if(sym != Keyboard::Keycode::None && self.window.onKeyRelease) self.window.onKeyRelease(sym); //if(sym != Keyboard::Keycode::None && self.window.onKeyRelease) self.window.onKeyRelease(sym);
} }
auto QtWindow::resizeEvent(QResizeEvent*) -> void { auto QtWindow::resizeEvent(QResizeEvent* event) -> void {
if(!p.locked() && !p.state().fullScreen && p.qtWindow->isVisible()) { if(!p.locked() && !p.state().fullScreen && p.qtWindow->isVisible()) {
p.state().geometry.setSize({ p.state().geometry.setSize({
p.qtContainer->geometry().width(), p.qtContainer->geometry().width(),
@ -334,8 +347,8 @@ auto QtWindow::resizeEvent(QResizeEvent*) -> void {
auto QtWindow::sizeHint() const -> QSize { auto QtWindow::sizeHint() const -> QSize {
uint width = p.state().geometry.width(); uint width = p.state().geometry.width();
uint height = p.state().geometry.height(); uint height = p.state().geometry.height();
if(p.qtMenuBar->isVisible()) height += settings.geometry.menuHeight; height += p._menuHeight();
if(p.qtStatusBar->isVisible()) height += settings.geometry.statusHeight; height += p._statusHeight();
return QSize(width, height); return QSize(width, height);
} }

View File

@ -131,6 +131,7 @@ auto pApplication::initialize() -> void {
#endif #endif
pKeyboard::initialize(); pKeyboard::initialize();
pWindow::initialize();
} }
static auto Application_keyboardProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> bool { static auto Application_keyboardProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> bool {

View File

@ -1,12 +1,5 @@
namespace hiro { namespace hiro {
struct pFont;
struct pObject;
struct pWindow;
struct pMenu;
struct pLayout;
struct pWidget;
struct AppMessage { struct AppMessage {
enum : uint { enum : uint {
None = WM_APP, None = WM_APP,

View File

@ -6,37 +6,49 @@ auto pStatusBar::construct() -> void {
if(auto parent = _parent()) { if(auto parent = _parent()) {
hwnd = CreateWindow(STATUSCLASSNAME, L"", WS_CHILD, 0, 0, 0, 0, parent->hwnd, nullptr, GetModuleHandle(0), 0); hwnd = CreateWindow(STATUSCLASSNAME, L"", WS_CHILD, 0, 0, 0, 0, parent->hwnd, nullptr, GetModuleHandle(0), 0);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
setEnabled(self().enabled(true)); setEnabled(self().enabled());
setFont(self().font(true)); setFont(self().font());
setText(self().text()); setText(self().text());
setVisible(self().visible(true)); setVisible(self().visible());
} }
} }
auto pStatusBar::destruct() -> void { auto pStatusBar::destruct() -> void {
if(hfont) { DeleteObject(hfont); hfont = nullptr; } if(hfont) { DeleteObject(hfont); hfont = nullptr; }
if(hwnd) { DestroyWindow(hwnd); hwnd = nullptr; } if(hwnd) { DestroyWindow(hwnd); hwnd = nullptr; }
if(auto parent = _parent()) { if(auto parent = _parent()) {
parent->setGeometry(parent->state().geometry); parent->setGeometry(parent->state().geometry);
} }
} }
auto pStatusBar::setEnabled(bool enabled) -> void { auto pStatusBar::setEnabled(bool) -> void {
//unsupported //unsupported
} }
auto pStatusBar::setFont(const Font& font) -> void { auto pStatusBar::setFont(const Font&) -> void {
auto font = self().font(true);
if(hfont) DeleteObject(hfont); if(hfont) DeleteObject(hfont);
hfont = pFont::create(font); hfont = pFont::create(font);
if(hwnd) SendMessage(hwnd, WM_SETFONT, (WPARAM)hfont, 0); SendMessage(hwnd, WM_SETFONT, (WPARAM)hfont, 0);
auto& text = state().text;
auto height = font.size(text ? text : " ").height();
SendMessage(hwnd, SB_SETMINHEIGHT, (WPARAM)height, 0);
if(auto parent = _parent()) {
parent->setGeometry(parent->state().geometry);
}
} }
auto pStatusBar::setText(const string& text) -> void { auto pStatusBar::setText(const string&) -> void {
if(hwnd) SendMessage(hwnd, SB_SETTEXT, 0, (LPARAM)(wchar_t*)utf16_t(text)); auto& text = state().text;
SendMessage(hwnd, WM_SETTEXT, 0, (LPARAM)(wchar_t*)utf16_t(text));
} }
auto pStatusBar::setVisible(bool visible) -> void { auto pStatusBar::setVisible(bool) -> void {
if(hwnd) ShowWindow(hwnd, visible ? SW_SHOWNORMAL : SW_HIDE); ShowWindow(hwnd, self().visible() ? SW_SHOWNORMAL : SW_HIDE);
if(auto parent = _parent()) { if(auto parent = _parent()) {
parent->setGeometry(parent->state().geometry); parent->setGeometry(parent->state().geometry);
} }
@ -46,7 +58,7 @@ auto pStatusBar::_parent() -> maybe<pWindow&> {
if(auto parent = self().parentWindow(true)) { if(auto parent = self().parentWindow(true)) {
if(auto self = parent->self()) return *self; if(auto self = parent->self()) return *self;
} }
return nothing; return {};
} }
} }

View File

@ -219,6 +219,20 @@ static auto CALLBACK Shared_windowProc(WindowProc windowProc, HWND hwnd, UINT ms
break; break;
} }
case WM_GETMINMAXINFO: {
auto info = (LPMINMAXINFO)lparam;
auto frameMargin = pWindow->frameMargin();
if(auto minimumSize = window->state.minimumSize) {
info->ptMinTrackSize.x = minimumSize.width() + frameMargin.width();
info->ptMinTrackSize.y = minimumSize.height() + frameMargin.height();
}
if(auto maximumSize = window->state.maximumSize) {
info->ptMaxTrackSize.x = maximumSize.width() + frameMargin.width();
info->ptMaxTrackSize.y = maximumSize.height() + frameMargin.height();
}
break;
}
case WM_MENUCOMMAND: { case WM_MENUCOMMAND: {
return Menu_windowProc(hwnd, msg, wparam, lparam); return Menu_windowProc(hwnd, msg, wparam, lparam);
} }

View File

@ -5,6 +5,19 @@ namespace hiro {
static const uint FixedStyle = WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX | WS_BORDER | WS_CLIPCHILDREN; static const uint FixedStyle = WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX | WS_BORDER | WS_CLIPCHILDREN;
static const uint ResizableStyle = WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CLIPCHILDREN; static const uint ResizableStyle = WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CLIPCHILDREN;
uint pWindow::minimumStatusHeight = 0;
auto pWindow::initialize() -> void {
HWND hwnd = CreateWindow(L"hiroWindow", L"", ResizableStyle, 128, 128, 256, 256, 0, 0, GetModuleHandle(0), 0);
HWND hstatus = CreateWindow(STATUSCLASSNAME, L"", WS_CHILD, 0, 0, 0, 0, hwnd, nullptr, GetModuleHandle(0), 0);
SetWindowPos(hstatus, nullptr, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED);
RECT rc;
GetWindowRect(hstatus, &rc);
minimumStatusHeight = rc.bottom - rc.top;
DestroyWindow(hstatus);
DestroyWindow(hwnd);
}
auto pWindow::construct() -> void { auto pWindow::construct() -> void {
hwnd = CreateWindow(L"hiroWindow", L"", ResizableStyle, 128, 128, 256, 256, 0, 0, GetModuleHandle(0), 0); hwnd = CreateWindow(L"hiroWindow", L"", ResizableStyle, 128, 128, 256, 256, 0, 0, GetModuleHandle(0), 0);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&reference);
@ -35,21 +48,11 @@ auto pWindow::focused() const -> bool {
} }
auto pWindow::frameMargin() const -> Geometry { auto pWindow::frameMargin() const -> Geometry {
unsigned style = state().resizable ? ResizableStyle : FixedStyle;
if(state().fullScreen) style = 0;
RECT rc{0, 0, 640, 480}; RECT rc{0, 0, 640, 480};
AdjustWindowRect(&rc, style, (bool)GetMenu(hwnd)); uint style = state().fullScreen ? 0 : state().resizable ? ResizableStyle : FixedStyle;
signed statusHeight = 0; bool menuVisible = state().menuBar && state().menuBar->visible();
if(auto& statusBar = state().statusBar) { AdjustWindowRect(&rc, style, menuVisible);
if(auto self = statusBar->self()) { return {abs(rc.left), abs(rc.top), (rc.right - rc.left) - 640, (rc.bottom - rc.top) + _statusHeight() - 480};
if(statusBar->visible()) {
RECT src;
GetClientRect(self->hwnd, &src);
statusHeight = src.bottom - src.top;
}
}
}
return {abs(rc.left), abs(rc.top), (rc.right - rc.left) - 640, (rc.bottom - rc.top) + statusHeight - 480};
} }
auto pWindow::remove(sMenuBar menuBar) -> void { auto pWindow::remove(sMenuBar menuBar) -> void {
@ -183,14 +186,9 @@ auto pWindow::setTitle(string text) -> void {
} }
auto pWindow::setVisible(bool visible) -> void { auto pWindow::setVisible(bool visible) -> void {
lock(); auto lock = acquire();
ShowWindow(hwnd, visible ? SW_SHOWNORMAL : SW_HIDE); ShowWindow(hwnd, visible ? SW_SHOWNORMAL : SW_HIDE);
if(!visible) setModal(false); if(!visible) setModal(false);
if(auto& sizable = state().sizable) {
if(auto self = sizable->self()) self->setVisible(sizable->visible(true));
}
unlock();
} }
// //
@ -301,6 +299,18 @@ auto pWindow::_modalityUpdate() -> void {
} }
} }
auto pWindow::_statusHeight() const -> int {
int height = 0;
if(auto& statusBar = state().statusBar) {
if(statusBar->visible()) {
auto& text = statusBar->state.text;
height = statusBar->font(true).size(text ? text : " ").height();
height = max(height, minimumStatusHeight);
}
}
return height;
}
} }
#endif #endif

View File

@ -5,6 +5,10 @@ namespace hiro {
struct pWindow : pObject { struct pWindow : pObject {
Declare(Window, Object) Declare(Window, Object)
static auto initialize() -> void;
static uint minimumStatusHeight;
auto append(sMenuBar menuBar) -> void; auto append(sMenuBar menuBar) -> void;
auto append(sSizable sizable) -> void; auto append(sSizable sizable) -> void;
auto append(sStatusBar statusBar) -> void; auto append(sStatusBar statusBar) -> void;
@ -42,6 +46,7 @@ struct pWindow : pObject {
auto _modalityCount() -> unsigned; auto _modalityCount() -> unsigned;
auto _modalityDisabled() -> bool; auto _modalityDisabled() -> bool;
auto _modalityUpdate() -> void; auto _modalityUpdate() -> void;
auto _statusHeight() const -> int;
HWND hwnd = nullptr; HWND hwnd = nullptr;
HFONT hstatusfont = nullptr; HFONT hstatusfont = nullptr;

View File

@ -1,22 +1,21 @@
name := icarus name := icarus
build := performance build := performance
flags += -I..
nall.path := ../nall nall.path := ../nall
include $(nall.path)/GNUmakefile include $(nall.path)/GNUmakefile
object.path := obj
flags += -I..
hiro.path := ../hiro hiro.path := ../hiro
hiro.resource := data/$(name).rc hiro.resource := data/$(name).rc
include $(hiro.path)/GNUmakefile include $(hiro.path)/GNUmakefile
objects += obj/hiro.o $(if $(call streq,$(platform),windows),obj/hiro-resource.o) objects := obj/icarus.o
objects += obj/icarus.o
all: $(objects) obj/icarus.o: icarus.cpp
all: $(hiro.objects) $(objects)
$(info Linking out/$(name) ...) $(info Linking out/$(name) ...)
+@$(strip $(compiler) -o out/$(name) $(objects) $(options) $(hiro.options)) +@$(compiler) -o out/$(name) $(hiro.objects) $(objects) $(hiro.options) $(options)
ifeq ($(platform),macos) ifeq ($(platform),macos)
rm -rf out/$(name).app rm -rf out/$(name).app
mkdir -p out/$(name).app/Contents/MacOS/ mkdir -p out/$(name).app/Contents/MacOS/
@ -26,16 +25,14 @@ ifeq ($(platform),macos)
sips -s format icns data/$(name).png --out out/$(name).app/Contents/Resources/$(name).icns sips -s format icns data/$(name).png --out out/$(name).app/Contents/Resources/$(name).icns
endif endif
obj/icarus.o: icarus.cpp $(call rwildcard,core/) $(call rwildcard,heuristics/) $(call rwildcard,ui/) verbose: hiro.verbose nall.verbose all;
$(info Compiling $< ...)
@$(compiler) $(flags.cpp) $(flags) -o obj/icarus.o -c icarus.cpp
clean: clean:
ifeq ($(platform),macos) ifeq ($(platform),macos)
rm -rf out/$(name).app rm -rf out/$(name).app
endif endif
$(call rm,obj/*) $(call delete,obj/*)
$(call rm,out/*) $(call delete,out/*)
install: install:
ifeq ($(platform),macos) ifeq ($(platform),macos)

View File

@ -31,21 +31,19 @@ ifeq ($(platform),)
# common commands # common commands
ifeq ($(uname),) ifeq ($(uname),)
rm = $(info Deleting $1 ...) @del /q $(subst /,\,$1) delete = $(info Deleting $1 ...) @del /q $(subst /,\,$1)
rmdir = $(info Deleting $1 ...) @del /s /q $(subst /,\,$1) && if exist $(subst /,\,$1) (rmdir /s /q $(subst /,\,$1)) rdelete = $(info Deleting $1 ...) @del /s /q $(subst /,\,$1) && if exist $(subst /,\,$1) (rmdir /s /q $(subst /,\,$1))
else else
rm = $(info Deleting $1 ...) @rm -f $1 delete = $(info Deleting $1 ...) @rm -f $1
rmdir = $(info Deleting $1 ...) @rm -rf $1 rdelete = $(info Deleting $1 ...) @rm -rf $1
endif endif
endif endif
flags.c := -x c -std=c11 flags.c = -x c -std=c11
flags.cpp := -x c++ -std=c++14 flags.cpp = -x c++ -std=c++14
flags.objc := -x objective-c -std=c11 flags.objc = -x objective-c -std=c11
flags.objcpp := -x objective-c++ -std=c++14 flags.objcpp = -x objective-c++ -std=c++14
flags.deps = -MMD -MP -MF $(@:.o=.d)
flags :=
options :=
# compiler detection # compiler detection
ifeq ($(compiler),) ifeq ($(compiler),)
@ -135,16 +133,31 @@ endif
# paths # paths
prefix := $(HOME)/.local prefix := $(HOME)/.local
object.path := obj
# targets # rules
default: all; default: all;
verbose: information all;
information: nall.verbose:
$(info Compiler Flags:) $(info Compiler Flags:)
$(foreach n,$(sort $(call unique,$(flags))),$(if $(filter-out -I%,$n),$(info $([space]) $n))) $(foreach n,$(sort $(call unique,$(flags))),$(if $(filter-out -I%,$n),$(info $([space]) $n)))
$(info Linker Options:) $(info Linker Options:)
$(foreach n,$(sort $(call unique,$(options))),$(if $(filter-out -l%,$n),$(info $([space]) $n))) $(foreach n,$(sort $(call unique,$(options))),$(if $(filter-out -l%,$n),$(info $([space]) $n)))
%.o: $<
$(info Compiling $< ...)
@$(call compile)
# function compile([arguments])
compile = \
$(strip \
$(if $(filter %.c,$<), \
$(compiler) $(flags.c) $(flags.deps) $(flags) $1 -c $< -o $@ \
,$(if $(filter %.cpp,$<), \
$(compiler) $(flags.cpp) $(flags.deps) $(flags) $1 -c $< -o $@ \
)) \
)
# function rwildcard(directory, pattern) # function rwildcard(directory, pattern)
rwildcard = \ rwildcard = \
$(strip \ $(strip \

View File

@ -63,6 +63,11 @@ struct Locale {
struct Namespace { struct Namespace {
Namespace(Locale& _locale, string _namespace) : _locale(_locale), _namespace(_namespace) {} Namespace(Locale& _locale, string _namespace) : _locale(_locale), _namespace(_namespace) {}
template<typename... P>
auto operator()(string input, P&&... p) const -> string {
return _locale(_namespace, input, forward<P>(p)...);
}
template<typename... P> template<typename... P>
auto tr(string input, P&&... p) const -> string { auto tr(string input, P&&... p) const -> string {
return _locale(_namespace, input, forward<P>(p)...); return _locale(_namespace, input, forward<P>(p)...);

View File

@ -1,3 +1,23 @@
ifeq ($(ruby),)
ifeq ($(platform),windows)
ruby += video.wgl video.direct3d video.directdraw video.gdi
ruby += audio.asio audio.wasapi audio.xaudio2 audio.directsound
ruby += input.windows
else ifeq ($(platform),macos)
ruby += video.cgl
ruby += audio.openal
ruby += input.quartz input.carbon
else ifeq ($(platform),linux)
ruby += video.glx video.xvideo video.xshm
ruby += audio.oss audio.alsa audio.openal audio.pulseaudio audio.pulseaudiosimple audio.ao
ruby += input.sdl input.xlib input.udev
else ifeq ($(platform),bsd)
ruby += video.glx video.xvideo video.xshm
ruby += audio.oss audio.openal
ruby += input.sdl input.xlib
endif
endif
ifeq ($(platform),macos) ifeq ($(platform),macos)
ruby.flags := $(flags.objcpp) ruby.flags := $(flags.objcpp)
else else
@ -46,6 +66,12 @@ ifeq ($(platform),bsd)
ruby.options += $(if $(findstring audio.openal,$(ruby)),-lopenal) ruby.options += $(if $(findstring audio.openal,$(ruby)),-lopenal)
endif endif
$(object.path)/ruby.o: $(ruby.path)/ruby.cpp $(call rwildcard,$(ruby.path)) $(call rwildcard,$(nall.path)) ruby.objects := $(object.path)/ruby.o
$(object.path)/ruby.o: $(ruby.path)/ruby.cpp
$(info Compiling $< ...) $(info Compiling $< ...)
@$(compiler) $(ruby.flags) $(flags) -c $< -o $@ @$(compiler) $(ruby.flags) $(flags) $(flags.deps) -c $< -o $@
ruby.verbose:
$(info ruby Drivers:)
$(foreach n,$(ruby),$(info $([space]) $n))