diff --git a/.gitignore b/.gitignore deleted file mode 100644 index ffa62a02..00000000 --- a/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -ananke/libananke.so -icarus/icarus diff --git a/higan/data/Info.plist b/higan/data/higan.plist similarity index 100% rename from higan/data/Info.plist rename to higan/data/higan.plist diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index 87c38ede..738cf3dd 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -7,7 +7,7 @@ using namespace nall; namespace Emulator { static const string Name = "higan"; - static const string Version = "096.01"; + static const string Version = "096.02"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "http://byuu.org/"; diff --git a/higan/target-tomoko/GNUmakefile b/higan/target-tomoko/GNUmakefile index 7f12aa0a..05798755 100644 --- a/higan/target-tomoko/GNUmakefile +++ b/higan/target-tomoko/GNUmakefile @@ -21,7 +21,7 @@ ifeq ($(platform),windows) else ifeq ($(platform),macosx) ruby += video.cgl ruby += audio.openal - ruby += input.carbon + ruby += input.quartz input.carbon else ifeq ($(platform),linux) ruby += video.glx video.xv video.xshm video.sdl ruby += audio.alsa audio.openal audio.oss audio.pulseaudio audio.pulseaudiosimple audio.ao @@ -67,12 +67,25 @@ obj/ui-resource.o: # targets build: $(objects) $(strip $(compiler) -o out/$(name) $(objects) $(link)) +ifeq ($(platform),macosx) + @if [ -d out/higan.app ]; then rm -r out/higan.app; fi + mkdir -p out/higan.app/Contents/MacOS/ + mkdir -p out/higan.app/Contents/Resources/ + mv out/$(name) out/higan.app/Contents/MacOS/higan + cp data/higan.plist out/higan.app/Contents/Info.plist + sips -s format icns data/higan.png --out out/higan.app/Contents/Resources/higan.icns +endif install: ifeq ($(shell id -un),root) $(error "make install should not be run as root") else ifeq ($(platform),windows) else ifeq ($(platform),macosx) + mkdir -p ~/Library/Application\ Support/$(name)/ + mkdir -p ~/Emulation/System/ + cp -R out/higan.app /Applications/higan.app + cp data/cheats.bml ~/Library/Application\ Support/$(name)/ + cp -R profile/* ~/Emulation/System/ else mkdir -p $(prefix)/bin/ mkdir -p $(prefix)/share/icons/ @@ -89,6 +102,7 @@ ifeq ($(shell id -un),root) $(error "make uninstall should not be run as root") else ifeq ($(platform),windows) else ifeq ($(platform),macosx) + if [ -d /Applications/higan.app ]; then rm -r /Applications/higan.app; fi else if [ -f $(prefix)/bin/$(name) ]; then rm $(prefix)/bin/$(name); fi if [ -f $(prefix)/share/icons/$(name).png ]; then rm $(prefix)/share/icons/$(name).png; fi diff --git a/higan/target-tomoko/presentation/presentation.cpp b/higan/target-tomoko/presentation/presentation.cpp index c02657a5..b8024437 100644 --- a/higan/target-tomoko/presentation/presentation.cpp +++ b/higan/target-tomoko/presentation/presentation.cpp @@ -105,6 +105,16 @@ Presentation::Presentation() { stateManager.setText("State Manager").onActivate([&] { toolsManager->show(1); }); manifestViewer.setText("Manifest Viewer").onActivate([&] { toolsManager->show(2); }); + helpMenu.setText("Help"); + about.setText("About ...").onActivate([&] { + MessageDialog().setParent(*this).setTitle("About higan ...").setText({ + Emulator::Name, " v", Emulator::Version, " (", Emulator::Profile, ")\n\n", + "Author: ", Emulator::Author, "\n", + "License: ", Emulator::License, "\n", + "Website: ", Emulator::Website + }).information(); + }); + statusBar.setFont(Font().setBold()); statusBar.setVisible(settings["UserInterface/ShowStatusBar"].boolean()); @@ -115,6 +125,17 @@ Presentation::Presentation() { setBackgroundColor({0, 0, 0}); resizeViewport(); setCentered(); + + #if defined(PLATFORM_MACOSX) + showConfigurationSeparator.setVisible(false); + showConfiguration.setVisible(false); + helpMenu.setVisible(false); + + Application::Cocoa::onAbout([&] { about.doActivate(); }); + Application::Cocoa::onActivate([&] { setFocused(); }); + Application::Cocoa::onPreferences([&] { showConfiguration.doActivate(); }); + Application::Cocoa::onQuit([&] { doClose(); }); + #endif } auto Presentation::updateEmulator() -> void { diff --git a/higan/target-tomoko/presentation/presentation.hpp b/higan/target-tomoko/presentation/presentation.hpp index 12da4c81..f56aa180 100644 --- a/higan/target-tomoko/presentation/presentation.hpp +++ b/higan/target-tomoko/presentation/presentation.hpp @@ -36,12 +36,12 @@ struct Presentation : Window { Menu videoShaderMenu{&settingsMenu}; MenuRadioItem videoShaderNone{&videoShaderMenu}; Group videoShaders{&videoShaderNone}; - MenuSeparator settingsMenuSeparator1{&settingsMenu}; + MenuSeparator videoSettingsSeparator{&settingsMenu}; MenuCheckItem synchronizeVideo{&settingsMenu}; MenuCheckItem synchronizeAudio{&settingsMenu}; MenuCheckItem muteAudio{&settingsMenu}; MenuCheckItem showStatusBar{&settingsMenu}; - MenuSeparator settingsMenuSeparator2{&settingsMenu}; + MenuSeparator showConfigurationSeparator{&settingsMenu}; MenuItem showConfiguration{&settingsMenu}; Menu toolsMenu{&menuBar}; Menu saveStateMenu{&toolsMenu}; @@ -60,6 +60,8 @@ struct Presentation : Window { MenuItem cheatEditor{&toolsMenu}; MenuItem stateManager{&toolsMenu}; MenuItem manifestViewer{&toolsMenu}; + Menu helpMenu{&menuBar}; + MenuItem about{&helpMenu}; FixedLayout layout{this}; Viewport viewport{&layout, Geometry{0, 0, 1, 1}}; diff --git a/hiro/GNUmakefile b/hiro/GNUmakefile index 224d049a..ac6f28b3 100644 --- a/hiro/GNUmakefile +++ b/hiro/GNUmakefile @@ -1,7 +1,9 @@ ifeq ($(platform),) hiroflags = $(cppflags) $(flags) -DHIRO_REFERENCE hirolink = -else ifeq ($(platform),windows) +endif + +ifeq ($(platform),windows) ifeq ($(hiro),) hiro := windows endif @@ -15,16 +17,20 @@ else ifeq ($(platform),windows) hiroflags = $(cppflags) $(flags) -DHIRO_GTK $(shell pkg-config --cflags gtk+-2.0 gtksourceview-2.0) hirolink = $(shell pkg-config --libs gtk+-2.0 gtksourceview-2.0) endif -else ifeq ($(platform),macosx) +endif + +ifeq ($(platform),macosx) ifeq ($(hiro),) hiro := cocoa endif ifeq ($(hiro),cocoa) - hiroflags = $(objcppflags) $(flags) -DHIRO_COCOA + hiroflags = $(objcppflags) $(flags) -w -DHIRO_COCOA hirolink = -framework Cocoa -framework Carbon endif -else +endif + +ifneq ($(filter $(platform),linux bsd),) ifeq ($(hiro),) hiro := gtk endif diff --git a/hiro/cocoa/action/menu.cpp b/hiro/cocoa/action/menu.cpp index a6ba104a..d914171d 100644 --- a/hiro/cocoa/action/menu.cpp +++ b/hiro/cocoa/action/menu.cpp @@ -39,20 +39,24 @@ auto pMenu::destruct() -> void { auto pMenu::append(sAction action) -> void { @autoreleasepool { -// [[cocoaAction cocoaMenu] addItem:action.p.cocoaAction]; + if(auto pAction = action->self()) { + [[cocoaAction cocoaMenu] addItem:pAction->cocoaAction]; + } } } auto pMenu::remove(sAction action) -> void { @autoreleasepool { -// [[cocoaAction cocoaMenu] removeItem:action.p.cocoaAction]; + if(auto pAction = action->self()) { + [[cocoaAction cocoaMenu] removeItem:pAction->cocoaAction]; + } } } auto pMenu::setImage(const Image& image) -> void { @autoreleasepool { uint size = 15; //there is no API to retrieve the optimal size -// [cocoaAction setImage:NSMakeImage(image, size, size)]; + [cocoaAction setImage:NSMakeImage(image, size, size)]; } } diff --git a/hiro/cocoa/layout.cpp b/hiro/cocoa/layout.cpp index 01fda4f6..0412e00b 100644 --- a/hiro/cocoa/layout.cpp +++ b/hiro/cocoa/layout.cpp @@ -10,6 +10,24 @@ auto pLayout::destruct() -> void { for(auto& sizable : state().sizables) sizable->destruct(); } +auto pLayout::setEnabled(bool enabled) -> void { + for(auto& sizable : state().sizables) { + if(auto self = sizable->self()) self->setEnabled(enabled && sizable->enabled(true)); + } +} + +auto pLayout::setFont(const Font& font) -> void { + for(auto& sizable : state().sizables) { + if(auto self = sizable->self()) self->setFont(font ? font : sizable->font(true)); + } +} + +auto pLayout::setVisible(bool visible) -> void { + for(auto& sizable : state().sizables) { + if(auto self = sizable->self()) self->setVisible(visible && sizable->visible(true)); + } +} + } #endif diff --git a/hiro/cocoa/layout.hpp b/hiro/cocoa/layout.hpp index 4c7d8e1c..fc2e5505 100644 --- a/hiro/cocoa/layout.hpp +++ b/hiro/cocoa/layout.hpp @@ -4,6 +4,10 @@ namespace hiro { struct pLayout : pSizable { Declare(Layout, Sizable); + + auto setEnabled(bool enabled) -> void override; + auto setFont(const Font& font) -> void override; + auto setVisible(bool visible) -> void override; }; } diff --git a/hiro/cocoa/menu-bar.cpp b/hiro/cocoa/menu-bar.cpp index f57fbff8..bbe65b4a 100644 --- a/hiro/cocoa/menu-bar.cpp +++ b/hiro/cocoa/menu-bar.cpp @@ -9,9 +9,30 @@ auto pMenuBar::destruct() -> void { } auto pMenuBar::append(sMenu menu) -> void { + @autoreleasepool { + if(auto parent = _parent()) { + if(auto pMenu = menu->self()) { + [[parent->cocoaWindow menuBar] addItem:pMenu->cocoaAction]; + } + } + } } auto pMenuBar::remove(sMenu menu) -> void { + @autoreleasepool { + if(auto parent = _parent()) { + if(auto pMenu = menu->self()) { + [[parent->cocoaWindow menuBar] removeItem:pMenu->cocoaAction]; + } + } + } +} + +auto pMenuBar::_parent() -> maybe { + if(auto parent = self().parentWindow()) { + if(auto self = parent->self()) return *self; + } + return nothing; } } diff --git a/hiro/cocoa/menu-bar.hpp b/hiro/cocoa/menu-bar.hpp index ca1c724b..3d7c44de 100644 --- a/hiro/cocoa/menu-bar.hpp +++ b/hiro/cocoa/menu-bar.hpp @@ -7,6 +7,8 @@ struct pMenuBar : pObject { auto append(sMenu menu) -> void; auto remove(sMenu menu) -> void; + + auto _parent() -> maybe; }; } diff --git a/hiro/cocoa/status-bar.cpp b/hiro/cocoa/status-bar.cpp index 393cad5a..964af900 100644 --- a/hiro/cocoa/status-bar.cpp +++ b/hiro/cocoa/status-bar.cpp @@ -8,7 +8,43 @@ auto pStatusBar::construct() -> void { auto pStatusBar::destruct() -> void { } +auto pStatusBar::setEnabled(bool enabled) -> void { + @autoreleasepool { + if(auto parent = _parent()) { + [[parent->cocoaWindow statusBar] setEnabled:enabled]; + } + } +} + +auto pStatusBar::setFont(const Font& font) -> void { + @autoreleasepool { + if(auto parent = _parent()) { + [[parent->cocoaWindow statusBar] setFont:pFont::create(font)]; + } + } +} + auto pStatusBar::setText(const string& text) -> void { + @autoreleasepool { + if(auto parent = _parent()) { + [[parent->cocoaWindow statusBar] setStringValue:[NSString stringWithUTF8String:state().text]]; + } + } +} + +auto pStatusBar::setVisible(bool visible) -> void { + @autoreleasepool { + if(auto parent = _parent()) { + [[parent->cocoaWindow statusBar] setHidden:!visible]; + } + } +} + +auto pStatusBar::_parent() -> maybe { + if(auto parent = self().parentWindow()) { + if(auto self = parent->self()) return *self; + } + return nothing; } } diff --git a/hiro/cocoa/status-bar.hpp b/hiro/cocoa/status-bar.hpp index 23ffa3b6..aed5cc96 100644 --- a/hiro/cocoa/status-bar.hpp +++ b/hiro/cocoa/status-bar.hpp @@ -5,7 +5,12 @@ namespace hiro { struct pStatusBar : pObject { Declare(StatusBar, Object) + auto setEnabled(bool enabled) -> void override; + auto setFont(const Font& font) -> void override; auto setText(const string& text) -> void; + auto setVisible(bool visible) -> void override; + + auto _parent() -> maybe; }; } diff --git a/hiro/cocoa/utility.cpp b/hiro/cocoa/utility.cpp index 32c12dfc..184105a1 100644 --- a/hiro/cocoa/utility.cpp +++ b/hiro/cocoa/utility.cpp @@ -1,3 +1,7 @@ +auto NSMakeColor(const hiro::Color& color) -> NSColor* { + return [NSColor colorWithRed:(color.red() / 255.0) green:(color.green() / 255.0) blue:(color.blue() / 255.0) alpha:(color.alpha() / 255.0)]; +} + auto NSMakeImage(hiro::Image image, uint scaleWidth = 0, uint scaleHeight = 0) -> NSImage* { if(!image.state.data) return nil; diff --git a/hiro/cocoa/widget/frame.cpp b/hiro/cocoa/widget/frame.cpp index 21186dac..e8d77de9 100644 --- a/hiro/cocoa/widget/frame.cpp +++ b/hiro/cocoa/widget/frame.cpp @@ -30,16 +30,22 @@ auto pFrame::destruct() -> void { } } +auto pFrame::append(sLayout layout) -> void { +} + +auto pFrame::remove(sLayout layout) -> void { +} + auto pFrame::setEnabled(bool enabled) -> void { - if(auto layout = _layout()) layout->setEnabled(layout->self().enabled(true)); pWidget::setEnabled(enabled); + if(auto layout = _layout()) layout->setEnabled(layout->self().enabled(true)); } auto pFrame::setFont(const Font& font) -> void { @autoreleasepool { - if(auto layout = _layout()) layout->setFont(layout->self().font(true)); [cocoaView setTitleFont:pFont::create(font)]; } + if(auto layout = _layout()) layout->setFont(layout->self().font(true)); } auto pFrame::setGeometry(Geometry geometry) -> void { @@ -49,12 +55,11 @@ auto pFrame::setGeometry(Geometry geometry) -> void { geometry.x() - 3, geometry.y() - (empty ? size.height() - 2 : 1), geometry.width() + 6, geometry.height() + (empty ? size.height() + 2 : 5) }); - if(auto layout = _layout()) { - geometry.setX(geometry.x() + 1); - geometry.setY(geometry.y() + (empty ? 1 : size.height() - 2)); - geometry.setWidth(geometry.width() - 2); - geometry.setHeight(geometry.height() - (empty ? 1 : size.height() - 1)); - layout->setGeometry(geometry); + if(auto layout = state().layout) { + layout->setGeometry({ + geometry.x() + 1, geometry.y() + (empty ? 1 : size.height() - 2), + geometry.width() - 2, geometry.height() - (empty ? 1 : size.height() - 1) + }); } } @@ -65,8 +70,8 @@ auto pFrame::setText(const string& text) -> void { } auto pFrame::setVisible(bool visible) -> void { - if(auto layout = _layout()) layout->setVisible(layout->self().visible(true)); pWidget::setVisible(visible); + if(auto layout = _layout()) layout->setVisible(layout->self().visible(true)); } auto pFrame::_layout() -> maybe { diff --git a/hiro/cocoa/widget/frame.hpp b/hiro/cocoa/widget/frame.hpp index 94db247e..13335f91 100644 --- a/hiro/cocoa/widget/frame.hpp +++ b/hiro/cocoa/widget/frame.hpp @@ -12,6 +12,8 @@ namespace hiro { struct pFrame : pWidget { Declare(Frame, Widget) + auto append(sLayout layout) -> void; + auto remove(sLayout layout) -> void; auto setEnabled(bool enabled) -> void override; auto setFont(const Font& font) -> void override; auto setGeometry(Geometry geometry) -> void override; diff --git a/hiro/cocoa/widget/label.cpp b/hiro/cocoa/widget/label.cpp index e83253c6..c1c338b8 100644 --- a/hiro/cocoa/widget/label.cpp +++ b/hiro/cocoa/widget/label.cpp @@ -1,15 +1,14 @@ #if defined(Hiro_Label) -@implementation CocoaLabel : NSTextField +@implementation CocoaLabel : NSTextView -(id) initWith:(hiro::mLabel&)labelReference { if(self = [super initWithFrame:NSMakeRect(0, 0, 0, 0)]) { label = &labelReference; - [self setAlignment:NSLeftTextAlignment]; - [self setBordered:NO]; [self setDrawsBackground:NO]; [self setEditable:NO]; + [self setRichText:NO]; } return self; } @@ -23,6 +22,7 @@ auto pLabel::construct() -> void { cocoaView = cocoaLabel = [[CocoaLabel alloc] initWith:self()]; pWidget::construct(); + setAlignment(state().alignment); setText(state().text); } } @@ -38,29 +38,36 @@ auto pLabel::minimumSize() const -> Size { } auto pLabel::setAlignment(Alignment alignment) -> void { + @autoreleasepool { + NSMutableParagraphStyle* paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; + paragraphStyle.alignment = NSTextAlignmentCenter; + if(alignment.horizontal() < 0.333) paragraphStyle.alignment = NSTextAlignmentLeft; + if(alignment.horizontal() > 0.666) paragraphStyle.alignment = NSTextAlignmentRight; + [cocoaView setDefaultParagraphStyle:paragraphStyle]; + } } auto pLabel::setGeometry(Geometry geometry) -> void { - //NSTextField does not support vertical text centering: + //NSTextView does not support vertical text centering: //simulate this by adjusting the geometry placement (reduce height, move view down) - uint height = pFont::size(self().font(true), " ").height(); + uint height = pFont::size(self().font(true), state().text).height(); auto offset = geometry; if(geometry.height() > height) { uint diff = geometry.height() - height; - offset.setY(offset.y() + diff >> 1); - offset.setHeight(offset.height() - diff >> 1); + offset.setY(offset.y() + (diff >> 1)); + offset.setHeight(offset.height() - (diff >> 1)); } pWidget::setGeometry({ - offset.x() - 3, offset.y() - 3, - offset.width() + 6, offset.height() + 6 + offset.x() - 6, offset.y(), + offset.width() + 12, offset.height() }); } auto pLabel::setText(const string& text) -> void { @autoreleasepool { - [cocoaView setStringValue:[NSString stringWithUTF8String:text]]; + [cocoaView setString:[NSString stringWithUTF8String:text]]; } } diff --git a/hiro/cocoa/widget/label.hpp b/hiro/cocoa/widget/label.hpp index db079544..56df8f4c 100644 --- a/hiro/cocoa/widget/label.hpp +++ b/hiro/cocoa/widget/label.hpp @@ -1,6 +1,6 @@ #if defined(Hiro_Label) -@interface CocoaLabel : NSTextField { +@interface CocoaLabel : NSTextView { @public hiro::mLabel* label; } diff --git a/hiro/cocoa/widget/list-view-cell.cpp b/hiro/cocoa/widget/list-view-cell.cpp index 8dc815ef..a1a47a44 100644 --- a/hiro/cocoa/widget/list-view-cell.cpp +++ b/hiro/cocoa/widget/list-view-cell.cpp @@ -27,6 +27,22 @@ auto pListViewCell::setImage(const Image& image) -> void { } auto pListViewCell::setText(const string& text) -> void { + @autoreleasepool { + if(auto pListView = _grandparent()) { + [[pListView->cocoaView content] reloadData]; + } + } +} + +auto pListViewCell::_grandparent() -> maybe { + if(auto parent = _parent()) return parent->_parent(); +} + +auto pListViewCell::_parent() -> maybe { + if(auto parent = self().parentListViewItem()) { + if(auto self = parent->self()) return *self; + } + return nothing; } } diff --git a/hiro/cocoa/widget/list-view-cell.hpp b/hiro/cocoa/widget/list-view-cell.hpp index ab07e337..4c82d27d 100644 --- a/hiro/cocoa/widget/list-view-cell.hpp +++ b/hiro/cocoa/widget/list-view-cell.hpp @@ -12,6 +12,9 @@ struct pListViewCell : pObject { auto setForegroundColor(Color color) -> void; auto setImage(const Image& image) -> void; auto setText(const string& text) -> void; + + auto _grandparent() -> maybe; + auto _parent() -> maybe; }; } diff --git a/hiro/cocoa/widget/list-view-column.cpp b/hiro/cocoa/widget/list-view-column.cpp index 7649fff3..d1f99be6 100644 --- a/hiro/cocoa/widget/list-view-column.cpp +++ b/hiro/cocoa/widget/list-view-column.cpp @@ -3,9 +3,19 @@ namespace hiro { auto pListViewColumn::construct() -> void { + @autoreleasepool { + if(auto listView = _grandparent()) { + [listView->cocoaView reloadColumns]; + } + } } auto pListViewColumn::destruct() -> void { + @autoreleasepool { + if(auto listView = _grandparent()) { + [listView->cocoaView reloadColumns]; + } + } } auto pListViewColumn::setActive() -> void { @@ -42,6 +52,13 @@ auto pListViewColumn::setSortable(bool sortable) -> void { } auto pListViewColumn::setText(const string& text) -> void { + @autoreleasepool { + if(auto pListView = _grandparent()) { + NSTableColumn* tableColumn = [[pListView->cocoaView content] tableColumnWithIdentifier:[[NSNumber numberWithInteger:self().offset()] stringValue]]; + [[tableColumn headerCell] setStringValue:[NSString stringWithUTF8STring:text]]; + [[pListView->cocoaView headerView] setNeedsDisplay:YES]; + } + } } auto pListViewColumn::setVerticalAlignment(double alignment) -> void { @@ -53,6 +70,18 @@ auto pListViewColumn::setVisible(bool visible) -> void { auto pListViewColumn::setWidth(signed width) -> void { } +auto pListViewColumn::_grandparent() -> maybe { + if(auto parent = _parent()) return parent->_parent(); + return nothing; +} + +auto pListViewColumn::_parent() -> maybe { + if(auto parent = self().parentListViewHeader()) { + if(auto self = parent->self()) return *self; + } + return nothing; +} + } #endif diff --git a/hiro/cocoa/widget/list-view-column.hpp b/hiro/cocoa/widget/list-view-column.hpp index 81365cec..7507f3a2 100644 --- a/hiro/cocoa/widget/list-view-column.hpp +++ b/hiro/cocoa/widget/list-view-column.hpp @@ -20,6 +20,9 @@ struct pListViewColumn : pObject { auto setVerticalAlignment(double) -> void; auto setVisible(bool visible) -> void override; auto setWidth(signed width) -> void; + + auto _grandparent() -> maybe; + auto _parent() -> maybe; }; } diff --git a/hiro/cocoa/widget/list-view-header.cpp b/hiro/cocoa/widget/list-view-header.cpp index 323d8de3..2b2756a1 100644 --- a/hiro/cocoa/widget/list-view-header.cpp +++ b/hiro/cocoa/widget/list-view-header.cpp @@ -14,6 +14,25 @@ auto pListViewHeader::append(sListViewColumn column) -> void { auto pListViewHeader::remove(sListViewColumn column) -> void { } +auto pListViewHeader::setVisible(bool visible) -> void { + @autoreleasepool { + if(auto pListView = _parent()) { + if(visible) { + [[pListView->cocoaView content] setHeaderView:[[[NSTableHeaderView alloc] init] autorelease]]; + } else { + [[pListView->cocoaView content] setHeaderView:nil]; + } + } + } +} + +auto pListViewHeader::_parent() -> maybe { + if(auto parent = self().parentListView()) { + if(auto self = parent->self()) return *self; + } + return nothing; +} + } #endif diff --git a/hiro/cocoa/widget/list-view-header.hpp b/hiro/cocoa/widget/list-view-header.hpp index 7cb3251b..b2c1aa89 100644 --- a/hiro/cocoa/widget/list-view-header.hpp +++ b/hiro/cocoa/widget/list-view-header.hpp @@ -7,6 +7,9 @@ struct pListViewHeader : pObject { auto append(sListViewColumn column) -> void; auto remove(sListViewColumn column) -> void; + auto setVisible(bool visible) -> void override; + + auto _parent() -> maybe; }; } diff --git a/hiro/cocoa/widget/list-view-item.cpp b/hiro/cocoa/widget/list-view-item.cpp index db9b5743..0f043877 100644 --- a/hiro/cocoa/widget/list-view-item.cpp +++ b/hiro/cocoa/widget/list-view-item.cpp @@ -9,9 +9,19 @@ auto pListViewItem::destruct() -> void { } auto pListViewItem::append(sListViewCell cell) -> void { + @autoreleasepool { + if(auto listView = _parent()) { + [[listView->cocoaView content] reloadData]; + } + } } auto pListViewItem::remove(sListViewCell cell) -> void { + @autoreleasepool { + if(auto listView = _parent()) { + [[listView->cocoaView content] reloadData]; + } + } } auto pListViewItem::setAlignment(Alignment alignment) -> void { @@ -29,6 +39,13 @@ auto pListViewItem::setForegroundColor(Color color) -> void { auto pListViewItem::setSelected(bool selected) -> void { } +auto pListViewItem::_parent() -> maybe { + if(auto parent = self().parentListView()) { + if(auto self = parent->self()) return *self; + } + return nothing; +} + } #endif diff --git a/hiro/cocoa/widget/list-view-item.hpp b/hiro/cocoa/widget/list-view-item.hpp index bf6e6663..c95ff096 100644 --- a/hiro/cocoa/widget/list-view-item.hpp +++ b/hiro/cocoa/widget/list-view-item.hpp @@ -12,6 +12,8 @@ struct pListViewItem : pObject { auto setFocused() -> void; auto setForegroundColor(Color color) -> void; auto setSelected(bool selected) -> void; + + auto _parent() -> maybe; }; } diff --git a/hiro/cocoa/widget/list-view.cpp b/hiro/cocoa/widget/list-view.cpp index e1fe65e6..27fcb3c9 100644 --- a/hiro/cocoa/widget/list-view.cpp +++ b/hiro/cocoa/widget/list-view.cpp @@ -20,7 +20,6 @@ [content setAllowsColumnResizing:YES]; [content setAllowsColumnSelection:NO]; [content setAllowsEmptySelection:YES]; - [content setAllowsMultipleSelection:NO]; [content setColumnAutoresizingStyle:NSTableViewLastColumnOnlyAutoresizingStyle]; font = nil; @@ -60,60 +59,37 @@ [content removeTableColumn:[[content tableColumns] lastObject]]; } - if(false) { //listView->state.checkable) { - NSTableColumn* tableColumn = [[NSTableColumn alloc] initWithIdentifier:@"check"]; - NSTableHeaderCell* headerCell = [[NSTableHeaderCell alloc] initTextCell:@""]; - NSButtonCell* dataCell = [[NSButtonCell alloc] initTextCell:@""]; + if(auto listViewHeader = listView->state.header) { + for(auto& listViewColumn : listViewHeader->state.columns) { + auto column = listViewColumn->offset(); - [dataCell setButtonType:NSSwitchButton]; - [dataCell setControlSize:NSSmallControlSize]; - [dataCell setRefusesFirstResponder:YES]; + NSTableColumn* tableColumn = [[NSTableColumn alloc] initWithIdentifier:[[NSNumber numberWithInteger:column] stringValue]]; + NSTableHeaderCell* headerCell = [[NSTableHeaderCell alloc] initTextCell:[NSString stringWithUTF8String:listViewColumn->state.text]]; + CocoaListViewCell* dataCell = [[CocoaListViewCell alloc] initWith:*listView]; - [tableColumn setResizingMask:NSTableColumnNoResizing]; - [tableColumn setHeaderCell:headerCell]; - [tableColumn setDataCell:dataCell]; - [tableColumn setWidth:20.0]; + [dataCell setEditable:NO]; - [content addTableColumn:tableColumn]; - } + [tableColumn setResizingMask:NSTableColumnAutoresizingMask | NSTableColumnUserResizingMask]; + [tableColumn setHeaderCell:headerCell]; + [tableColumn setDataCell:dataCell]; - lstring headers; // = listView->state.headerText; - if(headers.size() == 0) headers.append(""); -//[content setUsesAlternatingRowBackgroundColors:headers.size() >= 2]; - - for(auto column : range(headers)) { - NSTableColumn* tableColumn = [[NSTableColumn alloc] initWithIdentifier:[[NSNumber numberWithInteger:column] stringValue]]; - NSTableHeaderCell* headerCell = [[NSTableHeaderCell alloc] initTextCell:[NSString stringWithUTF8String:headers(column)]]; - CocoaListViewCell* dataCell = [[CocoaListViewCell alloc] initTextCell:@""]; - - [dataCell setEditable:NO]; - - [tableColumn setResizingMask:NSTableColumnAutoresizingMask | NSTableColumnUserResizingMask]; - [tableColumn setHeaderCell:headerCell]; - [tableColumn setDataCell:dataCell]; - - [content addTableColumn:tableColumn]; + [content addTableColumn:tableColumn]; + } } } -(NSInteger) numberOfRowsInTableView:(NSTableView*)table { - return 0; //listView->state.text.size(); + return listView->state.items.size(); } -(id) tableView:(NSTableView*)table objectValueForTableColumn:(NSTableColumn*)tableColumn row:(NSInteger)row { - if([[tableColumn identifier] isEqualToString:@"check"]) { - auto checked = false; //listView->state.checked(row) ? NSOnState : NSOffState; - return [NSNumber numberWithInteger:checked]; + if(auto listViewItem = listView->item(row)) { + if(auto listViewCell = listViewItem->cell([[tableColumn identifier] integerValue])) { + NSString* text = [NSString stringWithUTF8String:listViewCell->state.text]; + return @{ @"text":text }; //used by type-ahead + } } - - NSInteger column = [[tableColumn identifier] integerValue]; - uint height = [table rowHeight]; - - NSString* text = nil; //[NSString stringWithUTF8String:listView->state.text(row)(column)]; - NSImage* image = nil; //NSMakeImage(listView->state.image(row)(column), height, height); - - if(image) return @{ @"text":text, @"image":image }; - return @{ @"text":text }; + return @{}; } -(BOOL) tableView:(NSTableView*)table shouldShowCellExpansionForTableColumn:(NSTableColumn*)tableColumn row:(NSInteger)row { @@ -124,20 +100,14 @@ return nil; } --(void) tableView:(NSTableView*)table setObjectValue:(id)object forTableColumn:(NSTableColumn*)tableColumn row:(NSInteger)row { - if([[tableColumn identifier] isEqualToString:@"check"]) { - //listView->state.checked(row) = [object integerValue] != NSOffState; - //listView->doToggle(row); - } -} - -(void) tableView:(NSTableView*)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn*)tableColumn row:(NSInteger)row { [cell setFont:[self font]]; } -(void) tableViewSelectionDidChange:(NSNotification*)notification { -//listView->state.selected = true; -//listView->state.selection = [content selectedRow]; + for(auto& listViewItem : listView->state.items) { + listViewItem->state.selected = listViewItem->offset() == [content selectedRow]; + } listView->doChange(); } @@ -169,7 +139,19 @@ @end -@implementation CocoaListViewCell : NSTextFieldCell +@implementation CocoaListViewCell : NSCell + +-(id) initWith:(hiro::mListView&)listViewReference { + if(self = [super initTextCell:@""]) { + listView = &listViewReference; + buttonCell = [[NSButtonCell alloc] initTextCell:@""]; + [buttonCell setButtonType:NSSwitchButton]; + [buttonCell setControlSize:NSSmallControlSize]; + [buttonCell setRefusesFirstResponder:YES]; + [buttonCell setTarget:self]; + } + return self; +} //used by type-ahead -(NSString*) stringValue { @@ -177,34 +159,94 @@ } -(void) drawWithFrame:(NSRect)frame inView:(NSView*)view { - NSString* text = [[self objectValue] objectForKey:@"text"]; - NSImage* image = [[self objectValue] objectForKey:@"image"]; - uint textDisplacement = 0; + if(auto listViewItem = listView->item([view rowAtPoint:frame.origin])) { + if(auto listViewCell = listViewItem->cell([view columnAtPoint:frame.origin])) { + NSColor* backgroundColor = nil; + if([self isHighlighted]) backgroundColor = [NSColor alternateSelectedControlColor]; + else if(auto color = listViewCell->state.backgroundColor) backgroundColor = NSMakeColor(color); + else backgroundColor = [NSColor controlBackgroundColor]; - if(image) { - [[NSGraphicsContext currentContext] saveGraphicsState]; + [backgroundColor set]; + [NSBezierPath fillRect:frame]; - NSRect targetRect = NSMakeRect(frame.origin.x, frame.origin.y, frame.size.height, frame.size.height); - NSRect sourceRect = NSMakeRect(0, 0, [image size].width, [image size].height); - [image drawInRect:targetRect fromRect:sourceRect operation:NSCompositeSourceOver fraction:1.0 respectFlipped:YES hints:nil]; + if(listViewCell->state.checkable) { + [buttonCell setHighlighted:YES]; + [buttonCell setState:(listViewCell->state.checked ? NSOnState : NSOffState)]; + [buttonCell drawWithFrame:frame inView:view]; + frame.origin.x += frame.size.height + 2; + frame.size.width -= frame.size.height + 2; + } - [[NSGraphicsContext currentContext] restoreGraphicsState]; - textDisplacement = frame.size.height + 2; + if(listViewCell->state.image) { + NSImage* image = NSMakeImage(listViewCell->state.image, frame.size.height, frame.size.height); + [[NSGraphicsContext currentContext] saveGraphicsState]; + NSRect targetRect = NSMakeRect(frame.origin.x, frame.origin.y, frame.size.height, frame.size.height); + NSRect sourceRect = NSMakeRect(0, 0, [image size].width, [image size].height); + [image drawInRect:targetRect fromRect:sourceRect operation:NSCompositeSourceOver fraction:1.0 respectFlipped:YES hints:nil]; + [[NSGraphicsContext currentContext] restoreGraphicsState]; + frame.origin.x += frame.size.height + 2; + frame.size.width -= frame.size.height + 2; + } + + if(listViewCell->state.text) { + NSMutableParagraphStyle* paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; + paragraphStyle.alignment = NSTextAlignmentCenter; + if(listViewCell->state.alignment.horizontal() < 0.333) paragraphStyle.alignment = NSTextAlignmentLeft; + if(listViewCell->state.alignment.horizontal() > 0.666) paragraphStyle.alignment = NSTextAlignmentRight; + NSColor* foregroundColor = nil; + if([self isHighlighted]) foregroundColor = [NSColor alternateSelectedControlTextColor]; + else if(auto color = listViewCell->state.foregroundColor) foregroundColor = NSMakeColor(color); + else foregroundColor = [NSColor textColor]; + NSString* text = [NSString stringWithUTF8String:listViewCell->state.text]; + [text drawInRect:frame withAttributes:@{ + NSBackgroundColorAttributeName:backgroundColor, + NSForegroundColorAttributeName:foregroundColor, + NSFontAttributeName:hiro::pFont::create(listViewCell->font(true)), + NSParagraphStyleAttributeName:paragraphStyle + }]; + } + } } +} - NSRect textRect = NSMakeRect( - frame.origin.x + textDisplacement, frame.origin.y, - frame.size.width - textDisplacement, frame.size.height - ); +//needed to trigger trackMouse events +-(NSUInteger) hitTestForEvent:(NSEvent*)event inRect:(NSRect)frame ofView:(NSView*)view { + NSUInteger hitTest = [super hitTestForEvent:event inRect:frame ofView:view]; + NSPoint point = [view convertPointFromBase:[event locationInWindow]]; + NSRect rect = NSMakeRect(frame.origin.x, frame.origin.y, frame.size.height, frame.size.height); + if(NSMouseInRect(point, rect, [view isFlipped])) { + hitTest |= NSCellHitTrackableArea; + } + return hitTest; +} - NSColor* textColor = [self isHighlighted] - ? [NSColor alternateSelectedControlTextColor] - : [NSColor textColor]; +//I am unable to get startTrackingAt:, continueTracking:, stopTracking: to work +//so instead, I have to run a modal loop on events until the mouse button is released +-(BOOL) trackMouse:(NSEvent*)event inRect:(NSRect)frame ofView:(NSView*)view untilMouseUp:(BOOL)flag { + if([event type] == NSLeftMouseDown) { + NSWindow* window = [view window]; + NSEvent* nextEvent; + while((nextEvent = [window nextEventMatchingMask:(NSLeftMouseDragged | NSLeftMouseUp)])) { + if([nextEvent type] == NSLeftMouseUp) { + NSPoint point = [view convertPointFromBase:[nextEvent locationInWindow]]; + NSRect rect = NSMakeRect(frame.origin.x, frame.origin.y, frame.size.height, frame.size.height); + if(NSMouseInRect(point, rect, [view isFlipped])) { + if(auto listViewItem = listView->item([view rowAtPoint:point])) { + if(auto listViewCell = listViewItem->cell([view columnAtPoint:point])) { + listViewCell->state.checked = !listViewCell->state.checked; + listView->doToggle(listViewCell->instance); + } + } + } + break; + } + } + } + return YES; +} - [text drawInRect:textRect withAttributes:@{ - NSForegroundColorAttributeName:textColor, - NSFontAttributeName:[self font] - }]; ++(BOOL) prefersTrackingUntilMouseUp { + return YES; } @end @@ -232,18 +274,60 @@ auto pListView::destruct() -> void { } auto pListView::append(sListViewHeader header) -> void { + @autoreleasepool { + [cocoaView reloadColumns]; + + header->setVisible(header->visible()); + } } auto pListView::append(sListViewItem item) -> void { + @autoreleasepool { + [[cocoaView content] reloadData]; + } } auto pListView::remove(sListViewHeader header) -> void { + @autoreleasepool { + [cocoaView reloadColumns]; + } } auto pListView::remove(sListViewItem item) -> void { + @autoreleasepool { + [[cocoaView content] reloadData]; + } } auto pListView::resizeColumns() -> void { + @autoreleasepool { + if(auto& header = state().header) { + vector widths; + int minimumWidth = 0; + int expandable = 0; + for(auto column : range(header->columnCount())) { + int width = _width(column); + widths.append(width); + minimumWidth += width; + if(header->column(column).expandable()) expandable++; + } + + int maximumWidth = self().geometry().width() - 18; //include margin for vertical scroll bar + int expandWidth = 0; + if(expandable && maximumWidth > minimumWidth) { + expandWidth = (maximumWidth - minimumWidth) / expandable; + } + + for(auto column : range(header->columnCount())) { + if(auto self = header->state.columns[column]->self()) { + int width = widths[column]; + if(self->state().expandable) width += expandWidth; + NSTableColumn* tableColumn = [[cocoaView content] tableColumnWithIdentifier:[[NSNumber numberWithInteger:column] stringValue]]; + [tableColumn setWidth:width]; + } + } + } + } } auto pListView::setAlignment(Alignment alignment) -> void { @@ -253,6 +337,9 @@ auto pListView::setBackgroundColor(Color color) -> void { } auto pListView::setBatchable(bool batchable) -> void { + @autoreleasepool { + [[cocoaView content] setAllowsMultipleSelection:(batchable ? YES : NO)]; + } } auto pListView::setBordered(bool bordered) -> void { @@ -267,13 +354,54 @@ auto pListView::setFont(const Font& font) -> void { auto pListView::setForegroundColor(Color color) -> void { } -/* -auto pListView::append(const lstring& text) -> void { - @autoreleasepool { - [[cocoaView content] reloadData]; +auto pListView::_cellWidth(uint row, uint column) -> uint { + uint width = 8; + if(auto pListViewItem = self().item(row)) { + if(auto pListViewCell = pListViewItem->cell(column)) { + if(pListViewCell->state.checkable) { + width += 24; + } + if(auto& image = pListViewCell->state.image) { + width += image.width() + 2; + } + if(auto& text = pListViewCell->state.text) { + width += pFont::size(pListViewCell->font(true), text).width(); + } + } } + return width; } +auto pListView::_columnWidth(uint column) -> uint { + uint width = 8; + if(auto& header = state().header) { + if(auto pListViewColumn = header->column(column)) { + if(auto& image = pListViewColumn->state.image) { + width += image.width() + 2; + } + if(auto& text = pListViewColumn->state.text) { + width += pFont::size(pListViewColumn->font(true), text).width(); + } + } + } + return width; +} + +auto pListView::_width(uint column) -> uint { + if(auto& header = state().header) { + if(auto width = header->column(column).width()) return width; + uint width = 1; + if(!header->column(column).visible()) return width; + if(header->visible()) width = max(width, _columnWidth(column)); + for(auto row : range(state().items)) { + width = max(width, _cellWidth(row, column)); + } + return width; + } + return 1; +} + +/* auto pListView::autoSizeColumns() -> void { @autoreleasepool { if(listView.state.checkable) { @@ -297,52 +425,6 @@ auto pListView::autoSizeColumns() -> void { } } -auto pListView::remove(unsigned selection) -> void { - @autoreleasepool { - [[cocoaView content] reloadData]; - } -} - -auto pListView::reset() -> void { - @autoreleasepool { - [[cocoaView content] reloadData]; - } -} - -auto pListView::setCheckable(bool checkable) -> void { - @autoreleasepool { - [cocoaView reloadColumns]; - } -} - -auto pListView::setChecked(unsigned selection, bool checked) -> void { - @autoreleasepool { - [[cocoaView content] reloadData]; - } -} - -auto pListView::setHeaderText(const lstring& text) -> void { - @autoreleasepool { - [cocoaView reloadColumns]; - } -} - -auto pListView::setHeaderVisible(bool visible) -> void { - @autoreleasepool { - if(visible) { - [[cocoaView content] setHeaderView:[[[NSTableHeaderView alloc] init] autorelease]]; - } else { - [[cocoaView content] setHeaderView:nil]; - } - } -} - -auto pListView::setImage(unsigned selection, unsigned position, const image& image) -> void { - @autoreleasepool { - [[cocoaView content] reloadData]; - } -} - auto pListView::setSelected(bool selected) -> void { @autoreleasepool { if(selected == false) { @@ -356,12 +438,6 @@ auto pListView::setSelection(unsigned selection) -> void { [[cocoaView content] selectRowIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(selection, 1)] byExtendingSelection:NO]; } } - -auto pListView::setText(unsigned selection, unsigned position, const string text) -> void { - @autoreleasepool { - [[cocoaView content] reloadData]; - } -} */ } diff --git a/hiro/cocoa/widget/list-view.hpp b/hiro/cocoa/widget/list-view.hpp index 09840d31..f846c97e 100644 --- a/hiro/cocoa/widget/list-view.hpp +++ b/hiro/cocoa/widget/list-view.hpp @@ -18,7 +18,6 @@ -(id) tableView:(NSTableView*)table objectValueForTableColumn:(NSTableColumn*)tableColumn row:(NSInteger)row; -(BOOL) tableView:(NSTableView*)table shouldShowCellExpansionForTableColumn:(NSTableColumn*)tableColumn row:(NSInteger)row; -(NSString*) tableView:(NSTableView*)table toolTipForCell:(NSCell*)cell rect:(NSRectPointer)rect tableColumn:(NSTableColumn*)tableColumn row:(NSInteger)row mouseLocation:(NSPoint)mouseLocation; --(void) tableView:(NSTableView*)table setObjectValue:(id)object forTableColumn:(NSTableColumn*)tableColumn row:(NSInteger)row; -(void) tableView:(NSTableView*)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn*)tableColumn row:(NSInteger)row; -(void) tableViewSelectionDidChange:(NSNotification*)notification; -(IBAction) activate:(id)sender; @@ -30,10 +29,16 @@ -(void) keyDown:(NSEvent*)event; @end -@interface CocoaListViewCell : NSTextFieldCell { +@interface CocoaListViewCell : NSCell { + hiro::mListView* listView; + NSButtonCell* buttonCell; } +-(id) initWith:(hiro::mListView&)listViewReference; -(NSString*) stringValue; -(void) drawWithFrame:(NSRect)frame inView:(NSView*)view; +-(NSUInteger) hitTestForEvent:(NSEvent*)event inRect:(NSRect)frame ofView:(NSView*)view; +-(BOOL) trackMouse:(NSEvent*)event inRect:(NSRect)frame ofView:(NSView*)view untilMouseUp:(BOOL)flag; ++(BOOL) prefersTrackingUntilMouseUp; @end namespace hiro { @@ -53,6 +58,10 @@ struct pListView : pWidget { auto setFont(const Font& font) -> void override; auto setForegroundColor(Color color) -> void; + auto _cellWidth(uint row, uint column) -> uint; + auto _columnWidth(uint column) -> uint; + auto _width(uint column) -> uint; + CocoaListView* cocoaListView = nullptr; }; diff --git a/hiro/cocoa/widget/tab-frame-item.cpp b/hiro/cocoa/widget/tab-frame-item.cpp index 12c4a4ef..474ff4f1 100644 --- a/hiro/cocoa/widget/tab-frame-item.cpp +++ b/hiro/cocoa/widget/tab-frame-item.cpp @@ -24,9 +24,24 @@ auto pTabFrameItem::setMovable(bool movable) -> void { } auto pTabFrameItem::setSelected() -> void { + @autoreleasepool { + if(auto parent = _parent()) { + [parent->cocoaView selectTabViewItem:cocoaTabFrameItem]; + } + } } auto pTabFrameItem::setText(const string& text) -> void { + @autoreleasepool { + [cocoaTabFrameItem setLabel:[NSString stringWithUTF8String:state().text]]; + } +} + +auto pTabFrameItem::_parent() -> maybe { + if(auto parent = self().parentTabFrame()) { + if(auto self = parent->self()) return *self; + } + return nothing; } } diff --git a/hiro/cocoa/widget/tab-frame-item.hpp b/hiro/cocoa/widget/tab-frame-item.hpp index ba11b5a0..a8c96b18 100644 --- a/hiro/cocoa/widget/tab-frame-item.hpp +++ b/hiro/cocoa/widget/tab-frame-item.hpp @@ -12,6 +12,10 @@ struct pTabFrameItem : pObject { auto setMovable(bool movable) -> void; auto setSelected() -> void; auto setText(const string& text) -> void; + + CocoaTabFrameItem* cocoaTabFrameItem = nullptr; + + auto _parent() -> maybe; }; } diff --git a/hiro/cocoa/widget/tab-frame.cpp b/hiro/cocoa/widget/tab-frame.cpp index 4fd21211..a1f680cc 100644 --- a/hiro/cocoa/widget/tab-frame.cpp +++ b/hiro/cocoa/widget/tab-frame.cpp @@ -12,7 +12,6 @@ } -(void) tabView:(NSTabView*)tabView didSelectTabViewItem:(NSTabViewItem*)tabViewItem { - tabFrame->self()->_updateSelected([tabView indexOfTabViewItem:tabViewItem]); tabFrame->self()->_synchronizeLayout(); tabFrame->doChange(); } @@ -32,28 +31,35 @@ -(NSSize) sizeOfLabel:(BOOL)shouldTruncateLabel { NSSize sizeOfLabel = [super sizeOfLabel:shouldTruncateLabel]; int selection = [cocoaTabFrame indexOfTabViewItem:self]; - if(selection < 0) return sizeOfLabel; //should never happen -// if(!tabFrame->state.image[selection].empty()) { -// uint iconSize = hiro::pFont::size(tabFrame->font(true), " ").height(); -// sizeOfLabel.width += iconSize + 2; -// } + if(selection >= 0) { + if(auto item = tabFrame->item(selection)) { + if(item->state.image) { + uint iconSize = hiro::pFont::size(tabFrame->font(true), " ").height(); + sizeOfLabel.width += iconSize + 2; + } + } + } return sizeOfLabel; } -(void) drawLabel:(BOOL)shouldTruncateLabel inRect:(NSRect)tabRect { int selection = [cocoaTabFrame indexOfTabViewItem:self]; -/*if(selection >= 0 && !tabFrame->state.image[selection].empty()) { - uint iconSize = phoenix::Font::size(tabFrame->font(), " ").height; - NSImage* image = NSMakeImage(tabFrame->state.image[selection]); + if(selection >= 0) { + if(auto item = tabFrame->item(selection)) { + if(item->state.image) { + uint iconSize = hiro::pFont::size(tabFrame->font(true), " ").height(); + NSImage* image = NSMakeImage(item->state.image); - [[NSGraphicsContext currentContext] saveGraphicsState]; - NSRect targetRect = NSMakeRect(tabRect.origin.x, tabRect.origin.y + 2, iconSize, iconSize); - [image drawInRect:targetRect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0 respectFlipped:YES hints:nil]; - [[NSGraphicsContext currentContext] restoreGraphicsState]; + [[NSGraphicsContext currentContext] saveGraphicsState]; + NSRect targetRect = NSMakeRect(tabRect.origin.x, tabRect.origin.y + 2, iconSize, iconSize); + [image drawInRect:targetRect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0 respectFlipped:YES hints:nil]; + [[NSGraphicsContext currentContext] restoreGraphicsState]; - tabRect.origin.x += iconSize + 2; - tabRect.size.width -= iconSize + 2; - }*/ + tabRect.origin.x += iconSize + 2; + tabRect.size.width -= iconSize + 2; + } + } + } [super drawLabel:shouldTruncateLabel inRect:tabRect]; } @@ -77,91 +83,81 @@ auto pTabFrame::destruct() -> void { } auto pTabFrame::append(sTabFrameItem item) -> void { -} - -auto pTabFrame::remove(sTabFrameItem item) -> void { -} - -/* -auto pTabFrame::append(string text, const image& image) -> void { @autoreleasepool { - CocoaTabFrameItem* item = [[CocoaTabFrameItem alloc] initWith:tabFrame]; - [item setLabel:[NSString stringWithUTF8String:text]]; - [cocoaView addTabViewItem:item]; - tabs.append(item); + if(auto p = item->self()) { + p->cocoaTabFrameItem = [[CocoaTabFrameItem alloc] initWith:self()]; + [p->cocoaTabFrameItem setLabel:[NSString stringWithUTF8String:item->state.text]]; + [cocoaView addTabViewItem:p->cocoaTabFrameItem]; + } } } -auto pTabFrame::remove(unsigned selection) -> void { +auto pTabFrame::remove(sTabFrameItem item) -> void { @autoreleasepool { - CocoaTabFrameItem* item = tabs[selection]; - [cocoaView removeTabViewItem:item]; - tabs.remove(selection); + if(auto p = item->self()) { + [cocoaView removeTabViewItem:p->cocoaTabFrameItem]; + } } } auto pTabFrame::setEnabled(bool enabled) -> void { - for(auto& layout : tabFrame.state.layout) { - if(layout) layout->setEnabled(layout->enabled()); - } pWidget::setEnabled(enabled); + for(auto& item : state().items) { + if(auto& layout = item->state.layout) { + if(auto self = layout->self()) self->setEnabled(layout->enabled(true)); + } + } +} + +auto pTabFrame::setFont(const Font& font) -> void { + pWidget::setFont(font); + for(auto& item : state().items) { + if(auto& layout = item->state.layout) { + if(auto self = layout->self()) self->setFont(layout->font(true)); + } + } } -*/ auto pTabFrame::setGeometry(Geometry geometry) -> void { -/* pWidget::setGeometry({ - geometry.x - 7, geometry.y - 5, - geometry.width + 14, geometry.height + 6 + geometry.x() - 7, geometry.y() - 5, + geometry.width() + 14, geometry.height() + 6 }); - geometry.x += 1, geometry.width -= 2; - geometry.y += 22, geometry.height -= 32; - for(auto& layout : tabFrame.state.layout) { - if(layout == nullptr) continue; - layout->setGeometry(geometry); + geometry.setGeometry({ + geometry.x() + 1, geometry.y() + 22, + geometry.width() - 2, geometry.height() - 32 + }); + for(auto& item : state().items) { + if(auto& layout = item->state.layout) { + layout->setGeometry(geometry); + } } - synchronizeLayout(); -*/ + _synchronizeLayout(); } auto pTabFrame::setNavigation(Navigation navigation) -> void { } -/* -auto pTabFrame::setSelection(unsigned selection) -> void { - @autoreleasepool { - CocoaTabFrameItem* item = tabs[selection]; - [cocoaView selectTabViewItem:item]; - } - synchronizeLayout(); -} - -auto pTabFrame::setText(unsigned selection, string text) -> void { - @autoreleasepool { - CocoaTabFrameItem* item = tabs[selection]; - [item setLabel:[NSString stringWithUTF8String:text]]; - } -} - auto pTabFrame::setVisible(bool visible) -> void { - for(auto& layout : tabFrame.state.layout) { - if(layout) layout->setVisible(layout->visible()); - } pWidget::setVisible(visible); + for(auto& item : state().items) { + if(auto& layout = item->state.layout) { + if(auto self = layout->self()) self->setVisible(layout->visible(true)); + } + } } -*/ auto pTabFrame::_synchronizeLayout() -> void { -/* - uint selection = 0; - for(auto& layout : tabFrame.state.layout) { - if(layout) layout->setVisible(selection == tabFrame.state.selection); - selection++; + @autoreleasepool { + NSTabViewItem* tabViewItem = [cocoaView selectedTabViewItem]; + int selected = tabViewItem ? [cocoaView indexOfTabViewItem:tabViewItem] : -1; + for(auto& item : state().items) { + item->state.selected = item->offset() == selected; + if(auto& layout = item->state.layout) { + if(auto self = layout->self()) self->setVisible(layout->visible(true) && item->selected()); + } + } } -*/ -} - -auto pTabFrame::_updateSelected(int selected) -> void { } } diff --git a/hiro/cocoa/widget/tab-frame.hpp b/hiro/cocoa/widget/tab-frame.hpp index f4d97ba2..531e0561 100644 --- a/hiro/cocoa/widget/tab-frame.hpp +++ b/hiro/cocoa/widget/tab-frame.hpp @@ -25,11 +25,13 @@ struct pTabFrame : pWidget { auto append(sTabFrameItem item) -> void; auto remove(sTabFrameItem item) -> void; + auto setEnabled(bool enabled) -> void override; + auto setFont(const Font& font) -> void override; auto setGeometry(Geometry geometry) -> void override; auto setNavigation(Navigation navigation) -> void; + auto setVisible(bool visible) -> void override; auto _synchronizeLayout() -> void; - auto _updateSelected(int selected) -> void; CocoaTabFrame* cocoaTabFrame = nullptr; vector tabs; diff --git a/hiro/cocoa/window.cpp b/hiro/cocoa/window.cpp index 3c04a9cd..b54ae171 100644 --- a/hiro/cocoa/window.cpp +++ b/hiro/cocoa/window.cpp @@ -166,12 +166,13 @@ auto pWindow::append(sLayout layout) -> void { } auto pWindow::append(sMenuBar menuBar) -> void { - @autoreleasepool { -// [[cocoaWindow menuBar] addItem:menu.p.cocoaAction]; - } } auto pWindow::append(sStatusBar statusBar) -> void { + statusBar->setEnabled(statusBar->enabled(true)); + statusBar->setFont(statusBar->font(true)); + statusBar->setText(statusBar->text()); + statusBar->setVisible(statusBar->visible(true)); } auto pWindow::focused() const -> bool { @@ -187,16 +188,6 @@ auto pWindow::frameMargin() const -> Geometry { } } -/* -auto pWindow::geometry() -> Geometry { - @autoreleasepool { - NSRect area = [cocoaWindow contentRectForFrameRect:[cocoaWindow frame]]; - area.size.height -= statusBarHeight(); - return {area.origin.x, Desktop::size().height - area.origin.y - area.size.height, area.size.width, area.size.height}; - } -} -*/ - auto pWindow::remove(sLayout layout) -> void { @autoreleasepool { [[cocoaWindow contentView] setNeedsDisplay:YES]; @@ -204,22 +195,13 @@ auto pWindow::remove(sLayout layout) -> void { } auto pWindow::remove(sMenuBar menuBar) -> void { - @autoreleasepool { -// [[cocoaWindow menuBar] removeItem:menu.p.cocoaAction]; - } } auto pWindow::remove(sStatusBar statusBar) -> void { -} - -/* -auto pWindow::remove(Widget& widget) -> void { @autoreleasepool { - [widget.p.cocoaView removeFromSuperview]; - [[cocoaWindow contentView] setNeedsDisplay:YES]; + [[cocoaWindow statusBar] setHidden:YES]; } } -*/ auto pWindow::setBackgroundColor(Color color) -> void { @autoreleasepool { @@ -308,28 +290,6 @@ auto pWindow::setResizable(bool resizable) -> void { } } -/* -auto pWindow::setStatusFont(string font) -> void { - @autoreleasepool { - [[cocoaWindow statusBar] setFont:pFont::cocoaFont(font)]; - } - statusBarReposition(); -} - -auto pWindow::setStatusText(string text) -> void { - @autoreleasepool { - [[cocoaWindow statusBar] setStringValue:[NSString stringWithUTF8String:text]]; - } -} - -auto pWindow::setStatusVisible(bool visible) -> void { - @autoreleasepool { - [[cocoaWindow statusBar] setHidden:!visible]; - setGeometry(geometry()); - } -} -*/ - auto pWindow::setTitle(const string& text) -> void { @autoreleasepool { [cocoaWindow setTitle:[NSString stringWithUTF8String:text]]; @@ -376,8 +336,9 @@ auto pWindow::sizeEvent() -> void { } auto pWindow::statusBarHeight() -> uint { - if(!state().statusBar) return 0; -//return Font::size(window.state.statusFont, " ").height + 6; + if(auto& statusBar = state().statusBar) { + return pFont::size(statusBar->font(true), " ").height() + 6; + } return 0; } @@ -400,6 +361,23 @@ auto pWindow::_append(mWidget& widget) -> void { } } +/* +auto pWindow::geometry() -> Geometry { + @autoreleasepool { + NSRect area = [cocoaWindow contentRectForFrameRect:[cocoaWindow frame]]; + area.size.height -= statusBarHeight(); + return {area.origin.x, Desktop::size().height - area.origin.y - area.size.height, area.size.width, area.size.height}; + } +} + +auto pWindow::remove(Widget& widget) -> void { + @autoreleasepool { + [widget.p.cocoaView removeFromSuperview]; + [[cocoaWindow contentView] setNeedsDisplay:YES]; + } +} +*/ + } #endif diff --git a/hiro/core/widget/frame.cpp b/hiro/core/widget/frame.cpp index 9a172a36..e269c96c 100644 --- a/hiro/core/widget/frame.cpp +++ b/hiro/core/widget/frame.cpp @@ -15,6 +15,7 @@ auto mFrame::append(sLayout layout) -> type& { if(auto& layout = state.layout) remove(layout); state.layout = layout; layout->setParent(this, 0); + signal(append, layout); return *this; } @@ -23,6 +24,7 @@ auto mFrame::layout() const -> Layout { } auto mFrame::remove(sLayout layout) -> type& { + signal(remove, layout); layout->setParent(); state.layout.reset(); return *this; diff --git a/icarus/GNUmakefile b/icarus/GNUmakefile index 24a2e725..8f1418a1 100644 --- a/icarus/GNUmakefile +++ b/icarus/GNUmakefile @@ -3,11 +3,18 @@ include ../hiro/GNUmakefile flags += -I.. -O3 link += -objects := obj/hiro.o obj/icarus.o + +objects := obj/hiro.o +objects += obj/icarus.o objects += $(if $(call streq,$(platform),windows),obj/resource.o) all: $(objects) - $(compiler) -o icarus $(objects) $(link) $(hirolink) + $(compiler) -o out/icarus $(objects) $(link) $(hirolink) +ifeq ($(platform),macosx) + @if [ -d out/icarus.app ]; then rm -r out/icarus.app; fi + mkdir -p out/icarus.app/Contents/MacOS/ + mv out/icarus out/icarus.app/Contents/MacOS/ +endif obj/hiro.o: ../hiro/hiro.cpp $(compiler) $(hiroflags) -o obj/hiro.o -c ../hiro/hiro.cpp @@ -19,11 +26,22 @@ obj/resource.o: windres ../hiro/windows/hiro.rc obj/resource.o clean: - if [ -f ./icarus ]; then rm ./icarus; fi - $(call delete,obj/*.o) +ifeq ($(platform),macosx) + @if [ -d out/icarus.app ]; then rm out/icarus.app; fi +endif + $(call delete,obj/*) + $(call delete,out/*) install: +ifeq ($(platform),macosx) + cp -r out/icarus.app /Applications/icarus.app +else if [ -f ./icarus ]; then cp ./icarus $(prefix)/bin/icarus; fi +endif uninstall: +ifeq ($(platform),macosx) + if [ -d /Applications/icarus.app ]; then rm -r /Applications/icarus.app; fi +else if [ -f $(prefix)/bin/icarus ]; then rm $(prefix)/bin/icarus; fi +endif diff --git a/icarus/out/.gitignore b/icarus/out/.gitignore new file mode 100644 index 00000000..094b84a5 --- /dev/null +++ b/icarus/out/.gitignore @@ -0,0 +1 @@ +icarus diff --git a/libco/libco.c b/libco/libco.c index c48ffd97..13eb2379 100644 --- a/libco/libco.c +++ b/libco/libco.c @@ -3,6 +3,10 @@ license: public domain */ +#if defined(__clang__) + #pragma clang diagnostic ignored "-Wparentheses" +#endif + #if defined(__clang__) || defined(__GNUC__) #if defined(__i386__) #include "x86.c" diff --git a/nall/intrinsics.hpp b/nall/intrinsics.hpp index d0d6cb36..aba93a5f 100644 --- a/nall/intrinsics.hpp +++ b/nall/intrinsics.hpp @@ -34,6 +34,11 @@ namespace nall { #pragma clang diagnostic ignored "-Wswitch" #pragma clang diagnostic ignored "-Wswitch-bool" #pragma clang diagnostic ignored "-Wtautological-compare" + #pragma clang diagnostic ignored "-Wabsolute-value" + + //temporary + #pragma clang diagnostic ignored "-Winconsistent-missing-override" + #pragma clang diagnostic ignored "-Wdeprecated-declarations" #elif defined(__GNUC__) #define COMPILER_GCC auto Intrinsics::compiler() -> Compiler { return Compiler::GCC; } diff --git a/ruby/GNUmakefile b/ruby/GNUmakefile index 44cdb57c..0c3d8a1b 100644 --- a/ruby/GNUmakefile +++ b/ruby/GNUmakefile @@ -31,8 +31,18 @@ rubylink += $(if $(findstring .sdl,$(ruby)),$(shell sdl-config --libs)) ifeq ($(platform),windows) rubylink += $(if $(findstring audio.openal,$(ruby)),-lopenal32) -else ifeq ($(platform),macosx) +endif + +ifeq ($(platform),macosx) rubylink += $(if $(findstring audio.openal,$(ruby)),-framework OpenAL) -else +endif + +ifeq ($(platform),linux) + rubylink += -lX11 -lXext + rubylink += $(if $(findstring audio.openal,$(ruby)),-lopenal) +endif + +ifeq ($(platform),bsd) + rubylink += -lX11 -lXext rubylink += $(if $(findstring audio.openal,$(ruby)),-lopenal) endif diff --git a/ruby/input/carbon.cpp b/ruby/input/carbon.cpp index bb7df4fd..42728fe1 100644 --- a/ruby/input/carbon.cpp +++ b/ruby/input/carbon.cpp @@ -1,16 +1,10 @@ +#include "keyboard/carbon.cpp" + struct InputCarbon : Input { + InputKeyboardCarbon carbonKeyboard; + InputCarbon() : carbonKeyboard(*this) {} ~InputCarbon() { term(); } - struct Key { - uint8_t id; - string name; - }; - vector keys; - - struct Keyboard { - shared_pointer hid{new HID::Keyboard}; - } kb; - auto cap(const string& name) -> bool { if(name == Input::KeyboardSupport) return true; return false; @@ -28,154 +22,22 @@ struct InputCarbon : Input { auto release() -> bool { return false; } auto acquired() -> bool { return false; } - auto assign(shared_pointer hid, unsigned groupID, unsigned inputID, int16_t value) -> void { - auto& group = hid->group(groupID); - if(group.input(inputID).value() == value) return; - if(input.onChange) input.onChange(hid, groupID, inputID, group.input(inputID).value(), value); - group.input(inputID).setValue(value); - } - auto poll() -> vector> { vector> devices; - - KeyMap keymap; - GetKeys(keymap); - auto buffer = (uint8_t*)keymap; - - unsigned inputID = 0; - for(auto& key : keys) { - bool value = buffer[key.id >> 3] & (1 << (key.id & 7)); - assign(kb.hid, HID::Keyboard::GroupID::Button, inputID++, value); - } - - devices.append(kb.hid); + carbonKeyboard.poll(devices); return devices; } - auto rumble(uint64_t id, bool enable) -> bool { + auto rumble(uint64 id, bool enable) -> bool { return false; } auto init() -> bool { - keys.append({0x35, "Escape"}); - keys.append({0x7a, "F1"}); - keys.append({0x78, "F2"}); - keys.append({0x63, "F3"}); - keys.append({0x76, "F4"}); - keys.append({0x60, "F5"}); - keys.append({0x61, "F6"}); - keys.append({0x62, "F7"}); - keys.append({0x64, "F8"}); - keys.append({0x65, "F9"}); - keys.append({0x6d, "F10"}); - keys.append({0x67, "F11"}); - //keys.append({0x??, "F12"}); - - keys.append({0x69, "PrintScreen"}); - //keys.append({0x??, "ScrollLock"}); - keys.append({0x71, "Pause"}); - - keys.append({0x32, "Tilde"}); - keys.append({0x12, "Num1"}); - keys.append({0x13, "Num2"}); - keys.append({0x14, "Num3"}); - keys.append({0x15, "Num4"}); - keys.append({0x17, "Num5"}); - keys.append({0x16, "Num6"}); - keys.append({0x1a, "Num7"}); - keys.append({0x1c, "Num8"}); - keys.append({0x19, "Num9"}); - keys.append({0x1d, "Num0"}); - - keys.append({0x1b, "Dash"}); - keys.append({0x18, "Equal"}); - keys.append({0x33, "Backspace"}); - - keys.append({0x72, "Insert"}); - keys.append({0x75, "Delete"}); - keys.append({0x73, "Home"}); - keys.append({0x77, "End"}); - keys.append({0x74, "PageUp"}); - keys.append({0x79, "PageDown"}); - - keys.append({0x00, "A"}); - keys.append({0x0b, "B"}); - keys.append({0x08, "C"}); - keys.append({0x02, "D"}); - keys.append({0x0e, "E"}); - keys.append({0x03, "F"}); - keys.append({0x05, "G"}); - keys.append({0x04, "H"}); - keys.append({0x22, "I"}); - keys.append({0x26, "J"}); - keys.append({0x28, "K"}); - keys.append({0x25, "L"}); - keys.append({0x2e, "M"}); - keys.append({0x2d, "N"}); - keys.append({0x1f, "O"}); - keys.append({0x23, "P"}); - keys.append({0x0c, "Q"}); - keys.append({0x0f, "R"}); - keys.append({0x01, "S"}); - keys.append({0x11, "T"}); - keys.append({0x20, "U"}); - keys.append({0x09, "V"}); - keys.append({0x0d, "W"}); - keys.append({0x07, "X"}); - keys.append({0x10, "Y"}); - keys.append({0x06, "Z"}); - - keys.append({0x21, "LeftBracket"}); - keys.append({0x1e, "RightBracket"}); - keys.append({0x2a, "Backslash"}); - keys.append({0x29, "Semicolon"}); - keys.append({0x27, "Apostrophe"}); - keys.append({0x2b, "Comma"}); - keys.append({0x2f, "Period"}); - keys.append({0x2c, "Slash"}); - - keys.append({0x53, "Keypad1"}); - keys.append({0x54, "Keypad2"}); - keys.append({0x55, "Keypad3"}); - keys.append({0x56, "Keypad4"}); - keys.append({0x57, "Keypad5"}); - keys.append({0x58, "Keypad6"}); - keys.append({0x59, "Keypad7"}); - keys.append({0x5b, "Keypad8"}); - keys.append({0x5c, "Keypad9"}); - keys.append({0x52, "Keypad0"}); - - //keys.append({0x??, "Point"}); - keys.append({0x4c, "Enter"}); - keys.append({0x45, "Add"}); - keys.append({0x4e, "Subtract"}); - keys.append({0x43, "Multiply"}); - keys.append({0x4b, "Divide"}); - - keys.append({0x47, "NumLock"}); - //keys.append({0x39, "CapsLock"}); - - keys.append({0x7e, "Up"}); - keys.append({0x7d, "Down"}); - keys.append({0x7b, "Left"}); - keys.append({0x7c, "Right"}); - - keys.append({0x30, "Tab"}); - keys.append({0x24, "Return"}); - keys.append({0x31, "Spacebar"}); - //keys.append({0x??, "Menu"}); - - keys.append({0x38, "Shift"}); - keys.append({0x3b, "Control"}); - keys.append({0x3a, "Alt"}); - keys.append({0x37, "Super"}); - - kb.hid->setID(1); - for(auto& key : keys) kb.hid->buttons().append(key.name); - + if(!carbonKeyboard.init()) return false; return true; } auto term() -> void { + carbonKeyboard.term(); } }; diff --git a/ruby/input/keyboard/carbon.cpp b/ruby/input/keyboard/carbon.cpp new file mode 100644 index 00000000..d2beb90f --- /dev/null +++ b/ruby/input/keyboard/carbon.cpp @@ -0,0 +1,158 @@ +struct InputKeyboardCarbon { + Input& input; + InputKeyboardCarbon(Input& input) : input(input) {} + + shared_pointer hid{new HID::Keyboard}; + + struct Key { + uint8 id; + string name; + }; + vector keys; + + auto assign(uint inputID, bool value) -> void { + auto& group = hid->buttons(); + if(group.input(inputID).value() == value) return; + input.doChange(hid, HID::Keyboard::GroupID::Button, inputID, group.input(inputID).value(), value); + group.input(inputID).setValue(value); + } + + auto poll(vector>& devices) -> void { + KeyMap keymap; + GetKeys(keymap); + auto buffer = (const uint8*)keymap; + + uint inputID = 0; + for(auto& key : keys) { + bool value = buffer[key.id >> 3] & (1 << (key.id & 7)); + assign(inputID++, value); + } + + devices.append(hid); + } + + auto init() -> bool { + keys.append({0x35, "Escape"}); + keys.append({0x7a, "F1"}); + keys.append({0x78, "F2"}); + keys.append({0x63, "F3"}); + keys.append({0x76, "F4"}); + keys.append({0x60, "F5"}); + keys.append({0x61, "F6"}); + keys.append({0x62, "F7"}); + keys.append({0x64, "F8"}); + keys.append({0x65, "F9"}); + keys.append({0x6d, "F10"}); + keys.append({0x67, "F11"}); + //keys.append({0x??, "F12"}); + + keys.append({0x69, "PrintScreen"}); + //keys.append({0x??, "ScrollLock"}); + keys.append({0x71, "Pause"}); + + keys.append({0x32, "Tilde"}); + keys.append({0x12, "Num1"}); + keys.append({0x13, "Num2"}); + keys.append({0x14, "Num3"}); + keys.append({0x15, "Num4"}); + keys.append({0x17, "Num5"}); + keys.append({0x16, "Num6"}); + keys.append({0x1a, "Num7"}); + keys.append({0x1c, "Num8"}); + keys.append({0x19, "Num9"}); + keys.append({0x1d, "Num0"}); + + keys.append({0x1b, "Dash"}); + keys.append({0x18, "Equal"}); + keys.append({0x33, "Backspace"}); + + keys.append({0x72, "Insert"}); + keys.append({0x75, "Delete"}); + keys.append({0x73, "Home"}); + keys.append({0x77, "End"}); + keys.append({0x74, "PageUp"}); + keys.append({0x79, "PageDown"}); + + keys.append({0x00, "A"}); + keys.append({0x0b, "B"}); + keys.append({0x08, "C"}); + keys.append({0x02, "D"}); + keys.append({0x0e, "E"}); + keys.append({0x03, "F"}); + keys.append({0x05, "G"}); + keys.append({0x04, "H"}); + keys.append({0x22, "I"}); + keys.append({0x26, "J"}); + keys.append({0x28, "K"}); + keys.append({0x25, "L"}); + keys.append({0x2e, "M"}); + keys.append({0x2d, "N"}); + keys.append({0x1f, "O"}); + keys.append({0x23, "P"}); + keys.append({0x0c, "Q"}); + keys.append({0x0f, "R"}); + keys.append({0x01, "S"}); + keys.append({0x11, "T"}); + keys.append({0x20, "U"}); + keys.append({0x09, "V"}); + keys.append({0x0d, "W"}); + keys.append({0x07, "X"}); + keys.append({0x10, "Y"}); + keys.append({0x06, "Z"}); + + keys.append({0x21, "LeftBracket"}); + keys.append({0x1e, "RightBracket"}); + keys.append({0x2a, "Backslash"}); + keys.append({0x29, "Semicolon"}); + keys.append({0x27, "Apostrophe"}); + keys.append({0x2b, "Comma"}); + keys.append({0x2f, "Period"}); + keys.append({0x2c, "Slash"}); + + keys.append({0x53, "Keypad1"}); + keys.append({0x54, "Keypad2"}); + keys.append({0x55, "Keypad3"}); + keys.append({0x56, "Keypad4"}); + keys.append({0x57, "Keypad5"}); + keys.append({0x58, "Keypad6"}); + keys.append({0x59, "Keypad7"}); + keys.append({0x5b, "Keypad8"}); + keys.append({0x5c, "Keypad9"}); + keys.append({0x52, "Keypad0"}); + + //keys.append({0x??, "Point"}); + keys.append({0x45, "Add"}); + keys.append({0x4e, "Subtract"}); + keys.append({0x43, "Multiply"}); + keys.append({0x4b, "Divide"}); + keys.append({0x4c, "Enter"}); + + keys.append({0x47, "NumLock"}); + //keys.append({0x39, "CapsLock"}); + + keys.append({0x7e, "Up"}); + keys.append({0x7d, "Down"}); + keys.append({0x7b, "Left"}); + keys.append({0x7c, "Right"}); + + keys.append({0x30, "Tab"}); + keys.append({0x24, "Return"}); + keys.append({0x31, "Spacebar"}); + //keys.append({0x??, "Menu"}); + + keys.append({0x38, "Shift"}); + keys.append({0x3b, "Control"}); + keys.append({0x3a, "Alt"}); + keys.append({0x37, "Super"}); + + hid->setID(1); + for(auto& key : keys) { + hid->buttons().append(key.name); + } + + return true; + } + + auto term() -> void { + } +}; diff --git a/ruby/input/keyboard/quartz.cpp b/ruby/input/keyboard/quartz.cpp new file mode 100644 index 00000000..7757fa62 --- /dev/null +++ b/ruby/input/keyboard/quartz.cpp @@ -0,0 +1,153 @@ +struct InputKeyboardQuartz { + Input& input; + InputKeyboardQuartz(Input& input) : input(input) {} + + shared_pointer hid{new HID::Keyboard}; + + struct Key { + string name; + uint id; + }; + vector keys; + + auto assign(uint inputID, bool value) -> void { + auto& group = hid->buttons(); + if(group.input(inputID).value() == value) return; + input.doChange(hid, HID::Keyboard::GroupID::Button, inputID, group.input(inputID).value(), value); + group.input(inputID).setValue(value); + } + + auto poll(vector>& devices) -> void { + uint inputID = 0; + for(auto& key : keys) { + bool value = CGEventSourceKeyState(kCGEventSourceStateCombinedSessionState, key.id); + assign(inputID++, value); + } + devices.append(hid); + } + + auto init() -> bool { + keys.append({"Escape", kVK_Escape}); + keys.append({"F1", kVK_F1}); + keys.append({"F2", kVK_F2}); + keys.append({"F3", kVK_F3}); + keys.append({"F4", kVK_F4}); + keys.append({"F5", kVK_F5}); + keys.append({"F6", kVK_F6}); + keys.append({"F7", kVK_F7}); + keys.append({"F8", kVK_F8}); + keys.append({"F9", kVK_F9}); + keys.append({"F10", kVK_F10}); + keys.append({"F11", kVK_F11}); + keys.append({"F12", kVK_F12}); + keys.append({"F13", kVK_F13}); + keys.append({"F14", kVK_F14}); + keys.append({"F15", kVK_F15}); + keys.append({"F16", kVK_F16}); + keys.append({"F17", kVK_F17}); + keys.append({"F18", kVK_F18}); + keys.append({"F19", kVK_F19}); + keys.append({"F20", kVK_F20}); + + keys.append({"Tilde", kVK_ANSI_Grave}); + keys.append({"Num1", kVK_ANSI_1}); + keys.append({"Num2", kVK_ANSI_2}); + keys.append({"Num3", kVK_ANSI_3}); + keys.append({"Num4", kVK_ANSI_4}); + keys.append({"Num5", kVK_ANSI_5}); + keys.append({"Num6", kVK_ANSI_6}); + keys.append({"Num7", kVK_ANSI_7}); + keys.append({"Num8", kVK_ANSI_8}); + keys.append({"Num9", kVK_ANSI_9}); + keys.append({"Num0", kVK_ANSI_0}); + + keys.append({"Dash", kVK_ANSI_Minus}); + keys.append({"Equal", kVK_ANSI_Equal}); + keys.append({"Delete", kVK_Delete}); + + keys.append({"Erase", kVK_ForwardDelete}); + keys.append({"Home", kVK_Home}); + keys.append({"End", kVK_End}); + keys.append({"PageUp", kVK_PageUp}); + keys.append({"PageDown", kVK_PageDown}); + + keys.append({"A", kVK_ANSI_A}); + keys.append({"B", kVK_ANSI_B}); + keys.append({"C", kVK_ANSI_C}); + keys.append({"D", kVK_ANSI_D}); + keys.append({"E", kVK_ANSI_E}); + keys.append({"F", kVK_ANSI_F}); + keys.append({"G", kVK_ANSI_G}); + keys.append({"H", kVK_ANSI_H}); + keys.append({"I", kVK_ANSI_I}); + keys.append({"J", kVK_ANSI_J}); + keys.append({"K", kVK_ANSI_K}); + keys.append({"L", kVK_ANSI_L}); + keys.append({"M", kVK_ANSI_M}); + keys.append({"N", kVK_ANSI_N}); + keys.append({"O", kVK_ANSI_O}); + keys.append({"P", kVK_ANSI_P}); + keys.append({"Q", kVK_ANSI_Q}); + keys.append({"R", kVK_ANSI_R}); + keys.append({"S", kVK_ANSI_S}); + keys.append({"T", kVK_ANSI_T}); + keys.append({"U", kVK_ANSI_U}); + keys.append({"V", kVK_ANSI_V}); + keys.append({"W", kVK_ANSI_W}); + keys.append({"X", kVK_ANSI_X}); + keys.append({"Y", kVK_ANSI_Y}); + keys.append({"Z", kVK_ANSI_Z}); + + keys.append({"LeftBracket", kVK_ANSI_LeftBracket}); + keys.append({"RightBracket", kVK_ANSI_RightBracket}); + keys.append({"Backslash", kVK_ANSI_Backslash}); + keys.append({"Semicolon", kVK_ANSI_Semicolon}); + keys.append({"Apostrophe", kVK_ANSI_Quote}); + keys.append({"Comma", kVK_ANSI_Comma}); + keys.append({"Period", kVK_ANSI_Period}); + keys.append({"Slash", kVK_ANSI_Slash}); + + keys.append({"Keypad1", kVK_ANSI_Keypad1}); + keys.append({"Keypad2", kVK_ANSI_Keypad2}); + keys.append({"Keypad3", kVK_ANSI_Keypad3}); + keys.append({"Keypad4", kVK_ANSI_Keypad4}); + keys.append({"Keypad5", kVK_ANSI_Keypad5}); + keys.append({"Keypad6", kVK_ANSI_Keypad6}); + keys.append({"Keypad7", kVK_ANSI_Keypad7}); + keys.append({"Keypad8", kVK_ANSI_Keypad8}); + keys.append({"Keypad9", kVK_ANSI_Keypad9}); + keys.append({"Keypad0", kVK_ANSI_Keypad0}); + + keys.append({"Clear", kVK_ANSI_KeypadClear}); + keys.append({"Equals", kVK_ANSI_KeypadEquals}); + keys.append({"Divide", kVK_ANSI_KeypadDivide}); + keys.append({"Multiply", kVK_ANSI_KeypadMultiply}); + keys.append({"Subtract", kVK_ANSI_KeypadMinus}); + keys.append({"Add", kVK_ANSI_KeypadPlus}); + keys.append({"Enter", kVK_ANSI_KeypadEnter}); + keys.append({"Decimal", kVK_ANSI_KeypadDecimal}); + + keys.append({"Up", kVK_UpArrow}); + keys.append({"Down", kVK_DownArrow}); + keys.append({"Left", kVK_LeftArrow}); + keys.append({"Right", kVK_RightArrow}); + + keys.append({"Tab", kVK_Tab}); + keys.append({"Return", kVK_Return}); + keys.append({"Spacebar", kVK_Space}); + keys.append({"Shift", kVK_Shift}); + keys.append({"Control", kVK_Control}); + keys.append({"Option", kVK_Option}); + keys.append({"Command", kVK_Command}); + + hid->setID(1); + for(auto& key : keys) { + hid->buttons().append(key.name); + } + + return true; + } + + auto term() -> void { + } +}; diff --git a/ruby/input/keyboard/xlib.cpp b/ruby/input/keyboard/xlib.cpp index 149cc3cf..547ed436 100644 --- a/ruby/input/keyboard/xlib.cpp +++ b/ruby/input/keyboard/xlib.cpp @@ -11,12 +11,12 @@ struct InputKeyboardXlib { struct Key { string name; - unsigned keysym; - unsigned keycode; + uint keysym; + uint keycode; }; vector keys; - auto assign(unsigned inputID, bool value) -> void { + auto assign(uint inputID, bool value) -> void { auto& group = hid->buttons(); if(group.input(inputID).value() == value) return; input.doChange(hid, HID::Keyboard::GroupID::Button, inputID, group.input(inputID).value(), value); @@ -27,9 +27,10 @@ struct InputKeyboardXlib { char state[32]; XQueryKeymap(display, state); - for(unsigned n = 0; n < keys.size(); n++) { - bool value = state[keys[n].keycode >> 3] & (1 << (keys[n].keycode & 7)); - assign(n, value); + uint inputID = 0; + for(auto& key : keys) { + bool value = state[key.keycode >> 3] & (1 << (key.keycode & 7)); + assign(inputID++, value); } devices.append(hid); @@ -154,9 +155,9 @@ struct InputKeyboardXlib { hid->setID(1); - for(unsigned n = 0; n < keys.size(); n++) { - hid->buttons().append(keys[n].name); - keys[n].keycode = XKeysymToKeycode(display, keys[n].keysym); + for(auto& key : keys) { + hid->buttons().append(key.name); + key.keycode = XKeysymToKeycode(display, key.keysym); } return true; diff --git a/ruby/input/quartz.cpp b/ruby/input/quartz.cpp new file mode 100644 index 00000000..82320042 --- /dev/null +++ b/ruby/input/quartz.cpp @@ -0,0 +1,43 @@ +#include "keyboard/quartz.cpp" + +struct InputQuartz : Input { + InputKeyboardQuartz quartzKeyboard; + InputQuartz() : quartzKeyboard(*this) {} + ~InputQuartz() { term(); } + + auto cap(const string& name) -> bool { + if(name == Input::KeyboardSupport) return true; + return false; + } + + auto get(const string& name) -> any { + return {}; + } + + auto set(const string& name, const any& value) -> bool { + return false; + } + + auto acquire() -> bool { return false; } + auto release() -> bool { return false; } + auto acquired() -> bool { return false; } + + auto poll() -> vector> { + vector> devices; + quartzKeyboard.poll(devices); + return devices; + } + + auto rumble(uint64 id, bool enable) -> bool { + return false; + } + + auto init() -> bool { + if(!quartzKeyboard.init()) return false; + return true; + } + + auto term() -> void { + quartzKeyboard.term(); + } +}; diff --git a/ruby/input/xlib.cpp b/ruby/input/xlib.cpp index 77919f65..b9043462 100644 --- a/ruby/input/xlib.cpp +++ b/ruby/input/xlib.cpp @@ -14,7 +14,7 @@ struct InputXlib : Input { ~InputXlib() { term(); } struct Settings { - uintptr_t handle = 0; + uintptr handle = 0; } settings; auto cap(const string& name) -> bool { @@ -29,8 +29,8 @@ struct InputXlib : Input { } auto set(const string& name, const any& value) -> bool { - if(name == Input::Handle && value.is()) { - settings.handle = value.get(); + if(name == Input::Handle && value.is()) { + settings.handle = value.get(); return true; } @@ -56,7 +56,7 @@ struct InputXlib : Input { return devices; } - auto rumble(uint64_t id, bool enable) -> bool { + auto rumble(uint64 id, bool enable) -> bool { return false; } diff --git a/ruby/ruby.cpp b/ruby/ruby.cpp index fc3e84e3..6eeaf06f 100644 --- a/ruby/ruby.cpp +++ b/ruby/ruby.cpp @@ -18,9 +18,11 @@ using namespace ruby; #include #include #elif defined(DISPLAY_QUARTZ) + #define Boolean CocoaBoolean #define decimal CocoaDecimal #include #include + #undef Boolean #undef decimal #elif defined(DISPLAY_WINDOWS) #include @@ -412,6 +414,10 @@ auto Audio::availableDrivers() -> lstring { #include #endif +#if defined(INPUT_QUARTZ) + #include +#endif + #if defined(INPUT_SDL) #include #endif @@ -443,6 +449,10 @@ auto Input::create(const string& driver) -> Input* { if(driver == "Windows") return new InputWindows; #endif + #if defined(INPUT_QUARTZ) + if(driver == "Quartz") return new InputQuartz; + #endif + #if defined(INPUT_CARBON) if(driver == "Carbon") return new InputCarbon; #endif @@ -465,6 +475,8 @@ auto Input::create(const string& driver) -> Input* { auto Input::optimalDriver() -> string { #if defined(INPUT_WINDOWS) return "Windows"; + #elif defined(INPUT_QUARTZ) + return "Quartz"; #elif defined(INPUT_CARBON) return "Carbon"; #elif defined(INPUT_UDEV) @@ -481,6 +493,8 @@ auto Input::optimalDriver() -> string { auto Input::safestDriver() -> string { #if defined(INPUT_WINDOWS) return "Windows"; + #elif defined(INPUT_QUARTZ) + return "Quartz"; #elif defined(INPUT_CARBON) return "Carbon"; #elif defined(INPUT_UDEV) @@ -501,6 +515,10 @@ auto Input::availableDrivers() -> lstring { "Windows", #endif + #if defined(INPUT_QUARTZ) + "Quartz", + #endif + #if defined(INPUT_CARBON) "Carbon", #endif diff --git a/ruby/video/cgl.cpp b/ruby/video/cgl.cpp index 3db3a464..eed6cce9 100644 --- a/ruby/video/cgl.cpp +++ b/ruby/video/cgl.cpp @@ -1,12 +1,13 @@ +#define GL_ALPHA_TEST 0x0bc0 #include "opengl/opengl.hpp" struct VideoCGL; @interface RubyVideoCGL : NSOpenGLView { @public - ruby::VideoCGL* video; + VideoCGL* video; } --(id) initWith:(ruby::VideoCGL*)video pixelFormat:(NSOpenGLPixelFormat*)pixelFormat; +-(id) initWith:(VideoCGL*)video pixelFormat:(NSOpenGLPixelFormat*)pixelFormat; -(void) reshape; @end @@ -18,7 +19,7 @@ struct VideoCGL : Video, OpenGL { struct { NSView* handle = nullptr; bool synchronize = false; - unsigned filter = Video::FilterNearest; + uint filter = Video::FilterNearest; string shader; } settings; @@ -31,15 +32,15 @@ struct VideoCGL : Video, OpenGL { } auto get(const string& name) -> any { - if(name == Video::Handle) return (uintptr_t)settings.handle; + if(name == Video::Handle) return (uintptr)settings.handle; if(name == Video::Synchronize) return settings.synchronize; if(name == Video::Filter) return settings.filter; return {}; } auto set(const string& name, const any& value) -> bool { - if(name == Video::Handle && value.is()) { - settings.handle = (NSView*)value.get(); + if(name == Video::Handle && value.is()) { + settings.handle = (NSView*)value.get(); return true; } @@ -58,8 +59,8 @@ struct VideoCGL : Video, OpenGL { return true; } - if(name == Video::Filter && value.is()) { - settings.filter = value.get(); + if(name == Video::Filter && value.is()) { + settings.filter = value.get(); if(!settings.shader) OpenGL::filter = settings.filter ? GL_LINEAR : GL_NEAREST; return true; } @@ -77,7 +78,7 @@ struct VideoCGL : Video, OpenGL { return false; } - auto lock(uint32_t*& data, unsigned& pitch, unsigned width, unsigned height) -> bool { + auto lock(uint32*& data, uint& pitch, uint width, uint height) -> bool { OpenGL::size(width, height); return OpenGL::lock(data, pitch); } @@ -98,7 +99,8 @@ struct VideoCGL : Video, OpenGL { @autoreleasepool { if([view lockFocusIfCanDraw]) { auto area = [view frame]; - outputWidth = area.size.width, outputHeight = area.size.height; + outputWidth = area.size.width; + outputHeight = area.size.height; OpenGL::refresh(); [[view openGLContext] flushBuffer]; [view unlockFocus]; @@ -155,7 +157,7 @@ struct VideoCGL : Video, OpenGL { @implementation RubyVideoCGL : NSOpenGLView --(id) initWith:(ruby::VideoCGL*)videoPointer pixelFormat:(NSOpenGLPixelFormat*)pixelFormat { +-(id) initWith:(VideoCGL*)videoPointer pixelFormat:(NSOpenGLPixelFormat*)pixelFormat { if(self = [super initWithFrame:NSMakeRect(0, 0, 0, 0) pixelFormat:pixelFormat]) { video = videoPointer; } diff --git a/ruby/video/opengl/opengl.hpp b/ruby/video/opengl/opengl.hpp index a35bb8ba..797f3fa7 100644 --- a/ruby/video/opengl/opengl.hpp +++ b/ruby/video/opengl/opengl.hpp @@ -3,7 +3,6 @@ #include #define glGetProcAddress(name) (*glXGetProcAddress)((const GLubyte*)(name)) #elif defined(DISPLAY_QUARTZ) - #include #include #elif defined(DISPLAY_WINDOWS) #include diff --git a/ruby/video/wgl.cpp b/ruby/video/wgl.cpp index 85c9fec6..382e913f 100644 --- a/ruby/video/wgl.cpp +++ b/ruby/video/wgl.cpp @@ -6,8 +6,8 @@ struct VideoWGL : Video, OpenGL { ~VideoWGL() { term(); } - HGLRC (APIENTRY* wglCreateContextAttribs)(HDC, HGLRC, const int*) = nullptr; - BOOL (APIENTRY* wglSwapInterval)(int) = nullptr; + auto (APIENTRY* wglCreateContextAttribs)(HDC, HGLRC, const int*) -> HGLRC = nullptr; + auto (APIENTRY* wglSwapInterval)(int) -> BOOL = nullptr; HDC display = nullptr; HGLRC wglcontext = nullptr; @@ -17,7 +17,7 @@ struct VideoWGL : Video, OpenGL { struct { HWND handle = nullptr; bool synchronize = false; - unsigned filter = Video::FilterNearest; + uint filter = Video::FilterNearest; string shader; } settings; @@ -30,15 +30,15 @@ struct VideoWGL : Video, OpenGL { } auto get(const string& name) -> any { - if(name == Video::Handle) return (uintptr_t)settings.handle; + if(name == Video::Handle) return (uintptr)settings.handle; if(name == Video::Synchronize) return settings.synchronize; if(name == Video::Filter) return settings.filter; return {}; } auto set(const string& name, const any& value) -> bool { - if(name == Video::Handle && value.is()) { - settings.handle = (HWND)value.get(); + if(name == Video::Handle && value.is()) { + settings.handle = (HWND)value.get(); return true; } @@ -53,8 +53,8 @@ struct VideoWGL : Video, OpenGL { } } - if(name == Video::Filter && value.is()) { - settings.filter = value.get(); + if(name == Video::Filter && value.is()) { + settings.filter = value.get(); if(!settings.shader) OpenGL::filter = settings.filter ? GL_LINEAR : GL_NEAREST; return true; } @@ -69,7 +69,7 @@ struct VideoWGL : Video, OpenGL { return false; } - auto lock(uint32_t*& data, unsigned& pitch, unsigned width, unsigned height) -> bool { + auto lock(uint32*& data, uint& pitch, uint width, uint height) -> bool { OpenGL::size(width, height); return OpenGL::lock(data, pitch); }