Update to v106r54 release.

byuu says:

Changes to hiro will break all but the GTK target. Not that it matters
much given that the only ruby drivers that function are all on BSD
anyway.

But if you are fortunate enough to be able to run this ... you'll find
lots of polishing improvements to the bsnes GUI. I posted some
screenshots on Twitter, if anyone were interested.
This commit is contained in:
Tim Allen 2018-08-04 21:44:00 +10:00
parent 5d135b556d
commit 41e127a07c
65 changed files with 1387 additions and 1093 deletions

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.53"; static const string Version = "106.54";
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

@ -11,6 +11,9 @@ extern Input input;
#include <emulator/emulator.hpp> #include <emulator/emulator.hpp>
extern unique_pointer<Emulator::Interface> emulator; extern unique_pointer<Emulator::Interface> emulator;
#include <nall/encode/rle.hpp>
#include <nall/decode/rle.hpp>
#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

@ -448,9 +448,8 @@ auto Presentation::updateStateMenus() -> void {
for(auto& action : saveState.actions()) { for(auto& action : saveState.actions()) {
if(auto item = action.cast<MenuItem>()) { if(auto item = action.cast<MenuItem>()) {
if(auto name = item.property("name")) { if(auto name = item.property("name")) {
if(states.find(name)) { if(auto offset = states.find([&](auto& state) { return state.name == name; })) {
auto timestamp = program.stateTimestamp(item.property("name")); item.setText({item.property("title"), " [", chrono::local::datetime(states[*offset].date), "]"});
item.setText({item.property("title"), " [", chrono::local::datetime(timestamp), "]"});
} else { } else {
item.setText({item.property("title"), " [Empty]"}); item.setText({item.property("title"), " [Empty]"});
} }
@ -461,10 +460,9 @@ auto Presentation::updateStateMenus() -> void {
for(auto& action : loadState.actions()) { for(auto& action : loadState.actions()) {
if(auto item = action.cast<MenuItem>()) { if(auto item = action.cast<MenuItem>()) {
if(auto name = item.property("name")) { if(auto name = item.property("name")) {
if(states.find(name)) { if(auto offset = states.find([&](auto& state) { return state.name == name; })) {
auto timestamp = program.stateTimestamp(item.property("name"));
item.setEnabled(true); item.setEnabled(true);
item.setText({item.property("title"), " [", chrono::local::datetime(timestamp), "]"}); item.setText({item.property("title"), " [", chrono::local::datetime(states[*offset].date), "]"});
} else { } else {
item.setEnabled(false); item.setEnabled(false);
item.setText({item.property("title"), " [Empty]"}); item.setText({item.property("title"), " [Empty]"});

View File

@ -73,14 +73,14 @@ auto Program::screenshotPath() -> string {
if(!emulator->loaded()) return ""; if(!emulator->loaded()) return "";
auto location = gamePath(); auto location = gamePath();
if(location.endsWith("/")) { if(location.endsWith("/")) {
directory::create({location, "bsnes/screenshots/"}); location = {location, "bsnes/screenshots/"};
location = {location, "bsnes/screenshots/capture"}; directory::create(location);
} else { } else {
location = path("Screenshots", location); location = {path("Screenshots", location), "-"};
} }
for(uint n : range(1, 999)) { for(uint n : range(1, 1000)) {
string filename = {location, ".", pad(n, 3, '0'), ".bmp"}; string filename = {location, pad(n, 3, '0'), ".bmp"};
if(!file::exists(filename)) return filename; if(!file::exists(filename)) return filename;
} }
return {location, ".000.bmp"}; return {location, "000.bmp"};
} }

View File

@ -196,20 +196,20 @@ auto Program::videoRefresh(uint display, const uint32* data, uint pitch, uint wi
uint32_t* output; uint32_t* output;
uint length; uint length;
//this relies on the UI only running between Emulator::Scheduler::Event::Frame events
//this will always be the case; so we can avoid an unnecessary copy or one-frame delay here
//if the core were to exit between a frame event, the next frame might've been only partially rendered
screenshot.data = data;
screenshot.pitch = pitch;
screenshot.width = width;
screenshot.height = height;
pitch >>= 2; pitch >>= 2;
if(presentation.overscanCropping.checked()) { if(presentation.overscanCropping.checked()) {
if(height == 240) data += 8 * pitch, height -= 16; if(height == 240) data += 8 * pitch, height -= 16;
if(height == 480) data += 16 * pitch, height -= 32; if(height == 480) data += 16 * pitch, height -= 32;
} }
//this relies on the UI only running between Emulator::Scheduler::Event::Frame events
//this will always be the case; so we can avoid an unnecessary copy or one-frame delay here
//if the core were to exit between a frame event, the next frame might've been only partially rendered
screenshot.data = data;
screenshot.pitch = pitch << 2;
screenshot.width = width;
screenshot.height = height;
if(video.acquire(output, length, width, height)) { if(video.acquire(output, length, width, height)) {
length >>= 2; length >>= 2;

View File

@ -49,8 +49,14 @@ struct Program : Emulator::Platform {
auto screenshotPath() -> string; auto screenshotPath() -> string;
//states.cpp //states.cpp
auto availableStates(string type) -> vector<string>; struct State {
auto stateTimestamp(string filename) -> uint64_t; string name;
uint64_t date;
static const uint Signature;
};
auto availableStates(string type) -> vector<State>;
auto hasState(string filename) -> bool;
auto loadStateData(string filename) -> vector<uint8_t>;
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;

View File

@ -1,53 +1,42 @@
auto Program::availableStates(string type) -> vector<string> { const uint Program::State::Signature = 0x5a22'0000;
vector<string> result;
auto Program::availableStates(string type) -> vector<State> {
vector<State> result;
if(!emulator->loaded()) return result; if(!emulator->loaded()) return result;
if(gamePath().endsWith("/")) { if(gamePath().endsWith("/")) {
for(auto& file : directory::ifiles({statePath(), type}, "*.bst")) { for(auto& file : directory::ifiles({statePath(), type}, "*.bst")) {
result.append({type, file.trimRight(".bst", 1L)}); auto timestamp = file::timestamp({statePath(), type, file}, file::time::modify);
result.append({{type, file.trimRight(".bst", 1L)}, timestamp});
} }
} else { } else {
Decode::ZIP input; Decode::ZIP input;
if(input.open(statePath())) { if(input.open(statePath())) {
vector<string> filenames;
for(auto& file : input.file) { for(auto& file : input.file) {
if(file.name.match({type, "*.bst"})) result.append(file.name.trimRight(".bst", 1L)); if(!file.name.match({type, "*.bst"})) continue;
result.append({file.name.trimRight(".bst", 1L), (uint64_t)file.timestamp});
} }
} }
} }
result.isort();
return result; return result;
} }
auto Program::stateTimestamp(string filename) -> uint64_t { auto Program::hasState(string filename) -> bool {
auto timestamp = chrono::timestamp();
if(!emulator->loaded()) return timestamp;
if(gamePath().endsWith("/")) {
string location = {statePath(), filename, ".bst"};
timestamp = file::timestamp(location, file::time::modify);
} else {
string location = {filename, ".bst"};
Decode::ZIP input;
if(input.open(statePath())) {
for(auto& file : input.file) {
if(file.name != location) continue;
timestamp = file.timestamp;
break;
}
}
}
return timestamp;
}
auto Program::loadState(string filename) -> bool {
if(!emulator->loaded()) return false; if(!emulator->loaded()) return false;
string prefix = Location::file(filename); if(gamePath().endsWith("/")) {
vector<uint8_t> memory; return file::exists({statePath(), filename, ".bst"});
} else {
auto type = string{filename.split("/").first(), "/"};
return (bool)availableStates(type).find([&](auto& state) { return state.name == filename; });
}
}
auto Program::loadStateData(string filename) -> vector<uint8_t> {
if(!emulator->loaded()) return {};
vector<uint8_t> memory;
if(gamePath().endsWith("/")) { if(gamePath().endsWith("/")) {
string location = {statePath(), filename, ".bst"}; string location = {statePath(), filename, ".bst"};
memory = file::read(location); memory = file::read(location);
@ -63,10 +52,18 @@ auto Program::loadState(string filename) -> bool {
} }
} }
if(memory) { if(memory.size() < 3 * sizeof(uint)) return {}; //too small to be a valid state file
if(memory::readl<sizeof(uint)>(memory.data()) != State::Signature) return {}; //wrong format or version
return memory;
}
auto Program::loadState(string filename) -> bool {
string prefix = Location::file(filename);
if(auto memory = loadStateData(filename)) {
if(filename != "quick/undo") saveUndoState(); if(filename != "quick/undo") saveUndoState();
if(filename == "quick/undo") saveRedoState(); if(filename == "quick/undo") saveRedoState();
serializer s{memory.data(), memory.size()}; auto serializerRLE = Decode::RLE<uint8_t>(memory.data() + 3 * sizeof(uint));
serializer s{serializerRLE.data(), serializerRLE.size()};
if(!emulator->unserialize(s)) return showMessage({"[", prefix, "] is in incompatible format"}), false; if(!emulator->unserialize(s)) return showMessage({"[", prefix, "] is in incompatible format"}), false;
return showMessage({"Loaded [", prefix, "]"}), true; return showMessage({"Loaded [", prefix, "]"}), true;
} else { } else {
@ -76,15 +73,32 @@ auto Program::loadState(string filename) -> bool {
auto Program::saveState(string filename) -> bool { auto Program::saveState(string filename) -> bool {
if(!emulator->loaded()) return false; if(!emulator->loaded()) return false;
string prefix = Location::file(filename); string prefix = Location::file(filename);
serializer s = emulator->serialize(); serializer s = emulator->serialize();
if(!s.size()) return showMessage({"Failed to save [", prefix, "]"}), false; if(!s.size()) return showMessage({"Failed to save [", prefix, "]"}), false;
auto serializerRLE = Encode::RLE<uint8_t>(s.data(), s.size());
image preview;
preview.allocate(screenshot.data, screenshot.pitch, screenshot.width, screenshot.height);
if(preview.width() != 256 || preview.height() != 240) preview.scale(256, 240, true);
preview.transform(0, 15, 0x8000, 0x7c00, 0x03e0, 0x001f);
auto previewRLE = Encode::RLE<uint16_t>(preview.data(), preview.size());
vector<uint8_t> saveState;
saveState.resize(3 * sizeof(uint));
memory::writel<sizeof(uint)>(saveState.data() + 0 * sizeof(uint), State::Signature);
memory::writel<sizeof(uint)>(saveState.data() + 1 * sizeof(uint), serializerRLE.size());
memory::writel<sizeof(uint)>(saveState.data() + 2 * sizeof(uint), previewRLE.size());
saveState.append(serializerRLE);
saveState.append(previewRLE);
if(gamePath().endsWith("/")) { if(gamePath().endsWith("/")) {
string location = {statePath(), filename, ".bst"}; string location = {statePath(), filename, ".bst"};
directory::create(Location::path(location)); directory::create(Location::path(location));
if(!file::write(location, s.data(), s.size())) return showMessage({"Unable to write [", prefix, "] to disk"}), false; if(!file::write(location, saveState.data(), saveState.size())) {
return showMessage({"Unable to write [", prefix, "] to disk"}), false;
}
} else { } else {
string location = {filename, ".bst"}; string location = {filename, ".bst"};
@ -105,10 +119,11 @@ auto Program::saveState(string filename) -> bool {
for(auto& state : states) { for(auto& state : states) {
output.append(state.name, state.memory.data(), state.memory.size(), state.timestamp); output.append(state.name, state.memory.data(), state.memory.size(), state.timestamp);
} }
output.append(location, s.data(), s.size()); output.append(location, saveState.data(), saveState.size());
} }
if(filename.beginsWith("quick/")) presentation.updateStateMenus(); if(filename.beginsWith("quick/")) presentation.updateStateMenus();
stateManager.stateEvent(filename);
return showMessage({"Saved [", prefix, "]"}), true; return showMessage({"Saved [", prefix, "]"}), true;
} }
@ -132,10 +147,11 @@ auto Program::saveRedoState() -> bool {
auto Program::removeState(string filename) -> bool { auto Program::removeState(string filename) -> bool {
if(!emulator->loaded()) return false; if(!emulator->loaded()) return false;
bool result = false;
if(gamePath().endsWith("/")) { if(gamePath().endsWith("/")) {
string location = {statePath(), filename, ".bst"}; string location = {statePath(), filename, ".bst"};
return file::remove(location); result = file::remove(location);
} else { } else {
bool found = false; bool found = false;
string location = {filename, ".bst"}; string location = {filename, ".bst"};
@ -162,21 +178,28 @@ auto Program::removeState(string filename) -> bool {
file::remove(statePath()); file::remove(statePath());
} }
return found; result = found;
} }
if(result) {
presentation.updateStateMenus();
stateManager.stateEvent(filename);
}
return result;
} }
auto Program::renameState(string from, string to) -> bool { auto Program::renameState(string from_, string to_) -> bool {
if(!emulator->loaded()) return false; if(!emulator->loaded()) return false;
bool result = false;
if(gamePath().endsWith("/")) { if(gamePath().endsWith("/")) {
from = {statePath(), from, ".bst"}; string from = {statePath(), from_, ".bst"};
to = {statePath(), to, ".bst."}; string to = {statePath(), to_, ".bst"};
return file::rename(from, to); result = file::rename(from, to);
} else { } else {
bool found = false; bool found = false;
from = {from, ".bst"}; string from = {from_, ".bst"};
to = {to, ".bst"}; string to = {to_, ".bst"};
struct State { string name; time_t timestamp; vector<uint8_t> memory; }; struct State { string name; time_t timestamp; vector<uint8_t> memory; };
vector<State> states; vector<State> states;
@ -195,6 +218,11 @@ auto Program::renameState(string from, string to) -> bool {
output.append(state.name, state.memory.data(), state.memory.size(), state.timestamp); output.append(state.name, state.memory.data(), state.memory.size(), state.timestamp);
} }
return found; result = found;
} }
if(result) {
stateManager.stateEvent(to_);
}
return result;
} }

View File

@ -34,9 +34,24 @@ auto Program::updateStatus() -> void {
auto Program::captureScreenshot() -> bool { auto Program::captureScreenshot() -> bool {
if(emulator->loaded() && screenshot.data) { if(emulator->loaded() && screenshot.data) {
if(auto filename = screenshotPath()) { if(auto filename = screenshotPath()) {
if(Encode::BMP::create(filename, image capture;
(const uint32_t*)screenshot.data, screenshot.pitch, screenshot.width, screenshot.height, false capture.allocate(screenshot.data, screenshot.pitch, screenshot.width, screenshot.height);
)) {
//normalize pixel aspect ratio to 1:1
if(capture.width() == 512 && capture.height() == 240) capture.scale(512, 480, false); //hires
if(capture.width() == 256 && capture.height() == 480) capture.scale(512, 480, false); //interlace
auto data = capture.data();
auto pitch = capture.pitch();
auto width = capture.width();
auto height = capture.height();
if(presentation.overscanCropping.checked()) {
if(height == 240) data += 8 * pitch, height -= 16;
if(height == 480) data += 16 * pitch, height -= 32;
}
if(Encode::BMP::create(filename, data, width << 2, width, height, /* alpha = */ false)) {
showMessage({"Captured screenshot [", Location::file(filename), "]"}); showMessage({"Captured screenshot [", Location::file(filename), "]"});
return true; return true;
} }

View File

@ -25,6 +25,7 @@ auto DriverSettings::create() -> void {
settings["Video/Flush"].setValue(videoFlushToggle.checked()); settings["Video/Flush"].setValue(videoFlushToggle.checked());
program.updateVideoFlush(); program.updateVideoFlush();
}); });
videoSpacer.setColor({192, 192, 192});
audioLabel.setText("Audio").setFont(Font().setBold()); audioLabel.setText("Audio").setFont(Font().setBold());
audioLayout.setSize({2, 2}); audioLayout.setSize({2, 2});
@ -51,6 +52,7 @@ auto DriverSettings::create() -> void {
settings["Audio/Dynamic"].setValue(audioDynamicToggle.checked()); settings["Audio/Dynamic"].setValue(audioDynamicToggle.checked());
program.updateAudioDynamic(); program.updateAudioDynamic();
}); });
audioSpacer.setColor({192, 192, 192});
inputLabel.setText("Input").setFont(Font().setBold()); inputLabel.setText("Input").setFont(Font().setBold());
inputLayout.setSize({2, 1}); inputLayout.setSize({2, 1});

View File

@ -39,6 +39,7 @@ auto EmulatorSettings::create() -> void {
settings["UserInterface/SuppressScreenSaver"].setValue(suppressScreenSaver.checked()); settings["UserInterface/SuppressScreenSaver"].setValue(suppressScreenSaver.checked());
Application::setScreenSaver(!suppressScreenSaver.checked()); Application::setScreenSaver(!suppressScreenSaver.checked());
}); });
optionsSpacer.setColor({192, 192, 192});
hacksLabel.setText("Hacks").setFont(Font().setBold()); hacksLabel.setText("Hacks").setFont(Font().setBold());
fastPPUOption.setText("Fast PPU").setChecked(settings["Emulator/Hack/FastPPU"].boolean()).onToggle([&] { fastPPUOption.setText("Fast PPU").setChecked(settings["Emulator/Hack/FastPPU"].boolean()).onToggle([&] {

View File

@ -4,6 +4,7 @@ auto HotkeySettings::create() -> void {
layout.setPadding(5); layout.setPadding(5);
mappingList.setBatchable(); mappingList.setBatchable();
mappingList.setHeadered();
mappingList.onActivate([&] { mappingList.onActivate([&] {
if(assignButton.enabled()) assignButton.doActivate(); if(assignButton.enabled()) assignButton.doActivate();
}); });
@ -25,10 +26,8 @@ auto HotkeySettings::create() -> void {
auto HotkeySettings::reloadMappings() -> void { auto HotkeySettings::reloadMappings() -> void {
mappingList.reset(); mappingList.reset();
mappingList.append(TableViewHeader().setVisible() mappingList.append(TableViewColumn().setText("Name"));
.append(TableViewColumn().setText("Name")) mappingList.append(TableViewColumn().setText("Mapping").setExpandable());
.append(TableViewColumn().setText("Mapping").setExpandable())
);
for(auto& hotkey : inputManager.hotkeys) { for(auto& hotkey : inputManager.hotkeys) {
mappingList.append(TableViewItem() mappingList.append(TableViewItem()
.append(TableViewCell().setText(hotkey.name).setFont(Font().setBold()).setBackgroundColor({240, 240, 255})) .append(TableViewCell().setText(hotkey.name).setFont(Font().setBold()).setBackgroundColor({240, 240, 255}))

View File

@ -20,6 +20,7 @@ auto InputSettings::create() -> void {
inputManager.turboFrequency = frequency; inputManager.turboFrequency = frequency;
}); });
mappingList.setBatchable(); mappingList.setBatchable();
mappingList.setHeadered();
mappingList.onActivate([&] { if(assignButton.enabled()) assignButton.doActivate(); }); mappingList.onActivate([&] { if(assignButton.enabled()) assignButton.doActivate(); });
mappingList.onChange([&] { updateControls(); }); mappingList.onChange([&] { updateControls(); });
assignMouse1.onActivate([&] { assignMouseInput(0); }); assignMouse1.onActivate([&] { assignMouseInput(0); });
@ -89,10 +90,8 @@ auto InputSettings::reloadDevices() -> void {
auto InputSettings::reloadMappings() -> void { auto InputSettings::reloadMappings() -> void {
mappingList.reset(); mappingList.reset();
mappingList.append(TableViewHeader().setVisible() mappingList.append(TableViewColumn().setText("Name"));
.append(TableViewColumn().setText("Name")) mappingList.append(TableViewColumn().setText("Mapping").setExpandable());
.append(TableViewColumn().setText("Mapping").setExpandable())
);
for(auto& mapping : activeDevice().mappings) { for(auto& mapping : activeDevice().mappings) {
mappingList.append(TableViewItem() mappingList.append(TableViewItem()
.append(TableViewCell().setText(mapping.name).setFont(Font().setBold()).setBackgroundColor({240, 240, 255})) .append(TableViewCell().setText(mapping.name).setFont(Font().setBold()).setBackgroundColor({240, 240, 255}))

View File

@ -17,7 +17,7 @@ DriverSettings driverSettings;
SettingsWindow settingsWindow; SettingsWindow settingsWindow;
Settings::Settings() { Settings::Settings() {
Markup::Node::operator=(BML::unserialize(string::read(locate("settings.bml")))); Markup::Node::operator=(BML::unserialize(string::read(locate("settings.bml")), " "));
auto set = [&](string name, string value) { auto set = [&](string name, string value) {
//create node and set to default value only if it does not already exist //create node and set to default value only if it does not already exist
@ -88,7 +88,7 @@ Settings::Settings() {
} }
auto Settings::save() -> void { auto Settings::save() -> void {
file::write(locate("settings.bml"), BML::serialize(*this)); file::write(locate("settings.bml"), BML::serialize(*this, " "));
} }
auto SettingsWindow::create() -> void { auto SettingsWindow::create() -> void {
@ -123,13 +123,14 @@ auto SettingsWindow::setVisible(bool visible) -> SettingsWindow& {
if(visible) { if(visible) {
inputSettings.refreshMappings(); inputSettings.refreshMappings();
hotkeySettings.refreshMappings(); hotkeySettings.refreshMappings();
Application::processEvents();
doSize();
} }
return Window::setVisible(visible), *this; return Window::setVisible(visible), *this;
} }
auto SettingsWindow::show(uint index) -> void { auto SettingsWindow::show(uint index) -> void {
panel.item(index)->setSelected(); panel.item(index).setSelected();
setVisible(); setVisible();
setFocused(); setFocused();
doSize();
} }

View File

@ -155,7 +155,7 @@ public:
CheckLabel autoSaveStateOnUnload{&autoStateLayout, Size{0, 0}}; CheckLabel autoSaveStateOnUnload{&autoStateLayout, Size{0, 0}};
CheckLabel autoLoadStateOnLoad{&autoStateLayout, Size{0, 0}}; CheckLabel autoLoadStateOnLoad{&autoStateLayout, Size{0, 0}};
CheckLabel suppressScreenSaver{&layout, Size{~0, 0}}; CheckLabel suppressScreenSaver{&layout, Size{~0, 0}};
Widget optionsSpacer{&layout, Size{~0, 10}}; Canvas optionsSpacer{&layout, Size{~0, 1}};
Label hacksLabel{&layout, Size{~0, 0}, 2}; Label hacksLabel{&layout, Size{~0, 0}, 2};
HorizontalLayout fastPPULayout{&layout, Size{~0, 0}}; HorizontalLayout fastPPULayout{&layout, Size{~0, 0}};
CheckLabel fastPPUOption{&fastPPULayout, Size{0, 0}}; CheckLabel fastPPUOption{&fastPPULayout, Size{0, 0}};
@ -202,7 +202,7 @@ public:
CheckLabel videoExclusiveToggle{&videoToggleLayout, Size{0, 0}}; CheckLabel videoExclusiveToggle{&videoToggleLayout, Size{0, 0}};
CheckLabel videoBlockingToggle{&videoToggleLayout, Size{0, 0}}; CheckLabel videoBlockingToggle{&videoToggleLayout, Size{0, 0}};
CheckLabel videoFlushToggle{&videoToggleLayout, Size{0, 0}}; CheckLabel videoFlushToggle{&videoToggleLayout, Size{0, 0}};
Widget videoSpacer{&layout, Size{~0, 10}}; Canvas videoSpacer{&layout, Size{~0, 1}};
Label audioLabel{&layout, Size{~0, 0}, 2}; Label audioLabel{&layout, Size{~0, 0}, 2};
TableLayout audioLayout{&layout, Size{~0, 0}}; TableLayout audioLayout{&layout, Size{~0, 0}};
Label audioDriverLabel{&audioLayout, Size{0, 0}}; Label audioDriverLabel{&audioLayout, Size{0, 0}};
@ -221,7 +221,7 @@ public:
CheckLabel audioExclusiveToggle{&audioToggleLayout, Size{0, 0}}; CheckLabel audioExclusiveToggle{&audioToggleLayout, Size{0, 0}};
CheckLabel audioBlockingToggle{&audioToggleLayout, Size{0, 0}}; CheckLabel audioBlockingToggle{&audioToggleLayout, Size{0, 0}};
CheckLabel audioDynamicToggle{&audioToggleLayout, Size{0, 0}}; CheckLabel audioDynamicToggle{&audioToggleLayout, Size{0, 0}};
Widget audioSpacer{&layout, Size{~0, 10}}; Canvas audioSpacer{&layout, Size{~0, 1}};
Label inputLabel{&layout, Size{~0, 0}, 2}; Label inputLabel{&layout, Size{~0, 0}, 2};
TableLayout inputLayout{&layout, Size{~0, 0}}; TableLayout inputLayout{&layout, Size{~0, 0}};
Label inputDriverLabel{&inputLayout, Size{0, 0}}; Label inputDriverLabel{&inputLayout, Size{0, 0}};

View File

@ -55,20 +55,21 @@ auto CheatWindow::create() -> void {
nameLabel.setText("Name:"); nameLabel.setText("Name:");
nameValue.onActivate([&] { if(acceptButton.enabled()) acceptButton.doActivate(); }); nameValue.onActivate([&] { if(acceptButton.enabled()) acceptButton.doActivate(); });
nameValue.onChange([&] { doChange(); }); nameValue.onChange([&] { doChange(); });
codeLayout.setAlignment(0.0);
codeLabel.setText("Code:"); codeLabel.setText("Code:");
codeValue.onActivate([&] { if(acceptButton.enabled()) acceptButton.doActivate(); }); codeValue.setFont(Font().setFamily(Font::Mono));
codeValue.onChange([&] { doChange(); }); codeValue.onChange([&] { doChange(); });
enableOption.setText("Enable"); enableOption.setText("Enable");
acceptButton.onActivate([&] { doAccept(); }); acceptButton.onActivate([&] { doAccept(); });
cancelButton.setText("Cancel").onActivate([&] { setVisible(false); }); cancelButton.setText("Cancel").onActivate([&] { setVisible(false); });
setSize({400, layout.minimumSize().height()}); setSize({400, layout.minimumSize().height() + 100});
setDismissable(); setDismissable();
} }
auto CheatWindow::show(Cheat cheat) -> void { auto CheatWindow::show(Cheat cheat) -> void {
nameValue.setText(cheat.name); nameValue.setText(cheat.name);
codeValue.setText(cheat.code); codeValue.setText(cheat.code.split("+").strip().merge("\n"));
enableOption.setChecked(cheat.enable); enableOption.setChecked(cheat.enable);
doChange(); doChange();
setTitle(!cheat.name ? "Add Cheat" : "Edit Cheat"); setTitle(!cheat.name ? "Add Cheat" : "Edit Cheat");
@ -87,7 +88,7 @@ auto CheatWindow::doChange() -> void {
} }
auto CheatWindow::doAccept() -> void { auto CheatWindow::doAccept() -> void {
Cheat cheat = {nameValue.text().strip(), codeValue.text().strip(), enableOption.checked()}; Cheat cheat = {nameValue.text().strip(), codeValue.text().split("\n").strip().merge("+"), enableOption.checked()};
if(acceptButton.text() == "Add") { if(acceptButton.text() == "Add") {
cheatEditor.addCheat(cheat); cheatEditor.addCheat(cheat);
} else { } else {
@ -148,10 +149,8 @@ auto CheatEditor::create() -> void {
auto CheatEditor::refresh() -> void { auto CheatEditor::refresh() -> void {
cheatList.reset(); cheatList.reset();
cheatList.append(TableViewHeader().setVisible(false) cheatList.append(TableViewColumn());
.append(TableViewColumn()) cheatList.append(TableViewColumn().setExpandable());
.append(TableViewColumn().setExpandable())
);
for(auto& cheat : cheats) { for(auto& cheat : cheats) {
cheatList.append(TableViewItem() cheatList.append(TableViewItem()
.append(TableViewCell().setCheckable().setChecked(cheat.enable)) .append(TableViewCell().setCheckable().setChecked(cheat.enable))

View File

@ -3,18 +3,52 @@ auto ManifestViewer::create() -> void {
setText("Manifest Viewer"); setText("Manifest Viewer");
layout.setPadding(5); layout.setPadding(5);
manifestView.setEditable(false).setWordWrap(false).setFont(Font().setFamily(Font::Mono)); manifestLabel.setText("Manifest:");
manifestOption.onChange([&] { selectManifest(); });
manifestSpacer.setColor({192, 192, 192});
#if 0 && defined(Hiro_SourceEdit)
manifestView.setFont(Font().setFamily(Font::Mono).setSize(10));
#else
manifestView.setFont(Font().setFamily(Font::Mono));
#endif
manifestView.setEditable(false);
manifestView.setWordWrap(false);
typeIcon.setIcon(Icon::Device::Storage);
nameLabel.setText("...");
} }
auto ManifestViewer::loadManifest() -> void { auto ManifestViewer::loadManifest() -> void {
if(!emulator->loaded()) { manifestOption.reset();
manifestView.setText(""); manifestView.setText("");
verifiedIcon.setIcon({}); if(!emulator->loaded()) return;
verifiedLabel.setText("");
return;
}
manifestView.setText(emulator->manifests().merge("\n")); auto manifests = emulator->manifests();
verifiedIcon.setIcon(program.verified() ? Icon::Emblem::Program : Icon::Emblem::Binary); auto titles = emulator->titles();
verifiedLabel.setText(program.verified() ? "Verified" : "Unverified"); for(uint offset : range(manifests.size())) {
ComboButtonItem item{&manifestOption};
item.setProperty("manifest", manifests[offset]);
item.setText(titles[offset]);
bool verified = false;
if(offset == 0) verified = program.superFamicom.verified;
if(offset == 1 && program.gameBoy) verified = program.gameBoy.verified;
if(offset == 1 && program.bsMemory) verified = program.bsMemory.verified;
if(offset == 1 && program.sufamiTurboA) verified = program.sufamiTurboA.verified;
if(offset == 2 && program.sufamiTurboB) verified = program.sufamiTurboB.verified;
item.setIcon(verified ? Icon::Emblem::Program : Icon::Emblem::Binary);
}
manifestOption.doChange();
}
auto ManifestViewer::selectManifest() -> void {
auto selected = manifestOption.selected();
uint offset = selected->offset();
manifestView.setText(selected.property("manifest"));
string location;
if(offset == 0) location = program.superFamicom.location;
if(offset == 1 && program.gameBoy) location = program.gameBoy.location;
if(offset == 1 && program.bsMemory) location = program.bsMemory.location;
if(offset == 1 && program.sufamiTurboA) location = program.sufamiTurboA.location;
if(offset == 2 && program.sufamiTurboB) location = program.sufamiTurboB.location;
typeIcon.setIcon(location.endsWith("/") ? Icon::Action::Open : Icon::Emblem::File);
nameLabel.setText(location.trimRight("/", 1L));
} }

View File

@ -1,62 +1,45 @@
auto StateWindow::create() -> void { auto StateWindow::create() -> void {
layout.setPadding(5); layout.setPadding(5);
nameLabel.setText("Name:"); nameLabel.setText("Name:");
nameValue.onActivate([&] { nameValue.onActivate([&] { if(acceptButton.enabled()) acceptButton.doActivate(); });
if(acceptButton.enabled()) acceptButton.doActivate(); nameValue.onChange([&] { doChange(); });
}); acceptButton.onActivate([&] { doAccept(); });
nameValue.onChange([&] { cancelButton.setText("Cancel").onActivate([&] { setVisible(false); });
doChange();
});
acceptButton.onActivate([&] {
doAccept();
});
cancelButton.setText("Cancel").onActivate([&] {
setVisible(false);
});
setSize({400, layout.minimumSize().height()}); setSize({400, layout.minimumSize().height()});
setDismissable(); setDismissable();
} }
auto StateWindow::show(string name) -> void { auto StateWindow::show(string name) -> void {
nameValue.setText(name).setProperty("input", name); setProperty("type", {name.split("/").first(), "/"});
setProperty("name", {name.split("/").last()});
nameValue.setText(property("name"));
doChange(); doChange();
setTitle(!name ? "Add State" : "Edit State"); setTitle(!property("name") ? "Add State" : "Rename State");
setCentered(*toolsWindow); setCentered(*toolsWindow);
setVisible(); setVisible();
setFocused(); setFocused();
nameValue.setFocused(); nameValue.setFocused();
acceptButton.setText(!name ? "Add" : "Edit"); acceptButton.setText(!property("name") ? "Add" : "Rename");
} }
auto StateWindow::doChange() -> void { auto StateWindow::doChange() -> void {
bool valid = true;
auto name = nameValue.text().strip(); auto name = nameValue.text().strip();
if(!name) valid = false; bool valid = name && !name.contains("\\\"\t/:*?<>|");
for(auto c : name) { if(property("name") && name != property("name")) {
if(c == '\\' //don't allow a state to be renamed to the same name as an existing state (as this would overwrite it)
|| c == '\"' if(program.hasState({property("type"), name})) valid = false;
|| c == '\t'
|| c == '/'
|| c == ':'
|| c == '*'
|| c == '?'
|| c == '<'
|| c == '>'
|| c == '|') valid = false;
}
if(auto input = nameValue.property("input")) {
if(name != input && file::exists({program.statePath(), "managed/", name, ".bst"})) valid = false;
} }
nameValue.setBackgroundColor(valid ? Color{} : Color{255, 224, 224}); nameValue.setBackgroundColor(valid ? Color{} : Color{255, 224, 224});
acceptButton.setEnabled(valid); acceptButton.setEnabled(valid);
} }
auto StateWindow::doAccept() -> void { auto StateWindow::doAccept() -> void {
string name = {property("type"), nameValue.text().strip()};
if(acceptButton.text() == "Add") { if(acceptButton.text() == "Add") {
stateManager.createState(nameValue.text()); stateManager.createState(name);
} else { } else {
stateManager.modifyState(nameValue.text()); stateManager.modifyState(name);
} }
setVisible(false); setVisible(false);
} }
@ -66,76 +49,76 @@ auto StateManager::create() -> void {
setText("State Manager"); setText("State Manager");
layout.setPadding(5); layout.setPadding(5);
stateLayout.setAlignment(0.0);
stateList.setBatchable(); stateList.setBatchable();
stateList.onActivate([&] { stateList.setHeadered();
editButton.doActivate(); stateList.setSortable();
}); stateList.onActivate([&] { loadButton.doActivate(); });
stateList.onChange([&] { stateList.onChange([&] { updateSelection(); });
auto batched = stateList.batched(); stateList.onSort([&](TableViewColumn column) {
loadButton.setEnabled(batched.size() == 1); column.setSorting(column.sorting() == Sort::Ascending ? Sort::Descending : Sort::Ascending);
saveButton.setEnabled(batched.size() == 1); stateList.sort();
editButton.setEnabled(batched.size() == 1);
removeButton.setEnabled(batched.size() >= 1);
}); });
categoryLabel.setText("Category:");
categoryOption.append(ComboButtonItem().setText("Managed States").setProperty("type", "managed/"));
categoryOption.append(ComboButtonItem().setText("Quick States").setProperty("type", "quick/"));
categoryOption.onChange([&] { loadStates(); });
statePreviewSeparator.setColor({192, 192, 192});
statePreviewLabel.setFont(Font().setBold()).setText("Preview");
loadButton.setText("Load").onActivate([&] { loadButton.setText("Load").onActivate([&] {
if(auto item = stateList.selected()) { if(auto item = stateList.selected()) program.loadState(item.property("name"));
string filename = {"managed/", item.cell(0).text()};
program.loadState(filename);
}
}); });
saveButton.setText("Save").onActivate([&] { saveButton.setText("Save").onActivate([&] {
if(auto item = stateList.selected()) { if(auto item = stateList.selected()) program.saveState(item.property("name"));
string filename = {"managed/", item.cell(0).text()};
program.saveState(filename);
item.cell(1).setText(chrono::local::datetime(program.stateTimestamp(filename)));
}
}); });
addButton.setText("Add").onActivate([&] { addButton.setText("Add").onActivate([&] {
stateWindow.show(); stateWindow.show(type());
}); });
editButton.setText("Edit").onActivate([&] { editButton.setText("Rename").onActivate([&] {
if(auto item = stateList.selected()) { if(auto item = stateList.selected()) stateWindow.show(item.property("name"));
stateWindow.show(item.cell(0).text());
}
}); });
removeButton.setText("Remove").onActivate([&] { removeButton.setText("Remove").onActivate([&] {
removeStates(); removeStates();
}); });
} }
auto StateManager::type() const -> string {
return categoryOption.selected().property("type");
}
auto StateManager::loadStates() -> void { auto StateManager::loadStates() -> void {
stateList.reset(); stateList.reset();
stateList.append(TableViewHeader() stateList.append(TableViewColumn().setText("Name").setSorting(Sort::Ascending).setExpandable());
.append(TableViewColumn().setText("Name").setExpandable()) stateList.append(TableViewColumn().setText("Date").setForegroundColor({160, 160, 160}));
.append(TableViewColumn().setText("Date").setForegroundColor({160, 160, 160})) auto type = this->type();
); for(auto& state : program.availableStates(type)) {
for(auto& filename : program.availableStates("managed/")) { TableViewItem item{&stateList};
stateList.append(TableViewItem() item.setProperty("name", state.name);
.append(TableViewCell().setText(string{filename}.trimLeft("managed/", 1L))) item.append(TableViewCell().setText(state.name.trimLeft(type, 1L)));
.append(TableViewCell().setText(chrono::local::datetime(program.stateTimestamp(filename)))) item.append(TableViewCell().setText(chrono::local::datetime(state.date)));
);
} }
stateList.resizeColumns().doChange(); stateList.resizeColumns().doChange();
} }
auto StateManager::createState(string name) -> void { auto StateManager::createState(string name) -> void {
program.saveState({"managed/", name});
loadStates();
for(auto& item : stateList.items()) { for(auto& item : stateList.items()) {
if(item.cell(0).text() == name) item.setSelected(); item.setSelected(false);
}
program.saveState(name);
for(auto& item : stateList.items()) {
item.setSelected(item.property("name") == name);
} }
stateList.doChange(); stateList.doChange();
} }
auto StateManager::modifyState(string name) -> void { auto StateManager::modifyState(string name) -> void {
if(auto item = stateList.selected()) { if(auto item = stateList.selected()) {
string from = {"managed/", item.cell(0).text()}; string from = item.property("name");
string to = {"managed/", name}; string to = name;
if(from != to) { if(from != to) {
program.renameState(from, to); program.renameState(from, to);
loadStates();
for(auto& item : stateList.items()) { for(auto& item : stateList.items()) {
if(item.cell(0).text() == name) item.setSelected(); item.setSelected(item.property("name") == to);
} }
stateList.doChange(); stateList.doChange();
} }
@ -146,10 +129,43 @@ auto StateManager::removeStates() -> void {
if(auto batched = stateList.batched()) { if(auto batched = stateList.batched()) {
if(MessageDialog("Are you sure you want to permanently remove the selected state(s)?") if(MessageDialog("Are you sure you want to permanently remove the selected state(s)?")
.setParent(*toolsWindow).question() == "Yes") { .setParent(*toolsWindow).question() == "Yes") {
for(auto& item : batched) { auto lock = acquire();
program.removeState({"managed/", item.cell(0).text()}); for(auto& item : batched) program.removeState(item.property("name"));
}
loadStates(); loadStates();
} }
} }
} }
auto StateManager::updateSelection() -> void {
auto batched = stateList.batched();
statePreview.setVisible(batched.size() == 1);
loadButton.setEnabled(batched.size() == 1);
saveButton.setEnabled(batched.size() == 1);
editButton.setEnabled(batched.size() == 1);
addButton.setVisible(type() != "quick/");
editButton.setVisible(type() != "quick/");
removeButton.setEnabled(batched.size() >= 1);
statePreview.setColor({0, 0, 0});
if(batched.size() == 1) {
if(auto saveState = program.loadStateData(batched.first().property("name"))) {
uint skip = memory::readl<sizeof(uint)>(saveState.data() + sizeof(uint));
uint seek = 3 * sizeof(uint) + skip;
auto preview = Decode::RLE<uint16_t>(saveState.data() + seek, max(seek, saveState.size()) - seek);
image icon{0, 15, 0x8000, 0x7c00, 0x03e0, 0x001f};
icon.allocate(preview.data(), 256 * sizeof(uint16_t), 256, 240);
icon.transform();
statePreview.setIcon(icon);
}
}
}
auto StateManager::stateEvent(string name) -> void {
if(locked() || !name.beginsWith(type())) return;
auto selected = stateList.selected().property("name");
loadStates();
for(auto& item : stateList.items()) {
item.setSelected(item.property("name") == selected);
}
stateList.doChange();
}

View File

@ -15,9 +15,14 @@ auto ToolsWindow::create() -> void {
panel.append(cheatEditor); panel.append(cheatEditor);
panel.append(stateManager); panel.append(stateManager);
panel.append(manifestViewer); panel.append(manifestViewer);
panel.onChange([&] {
uint offset = panel.selected().offset();
if(offset != 0) cheatDatabase.setVisible(false), cheatWindow.setVisible(false);
if(offset != 1) stateWindow.setVisible(false);
});
setTitle("Tools"); setTitle("Tools");
setSize({600, 400}); setSize({720, 480});
setAlignment({1.0, 1.0}); setAlignment({1.0, 1.0});
setDismissable(); setDismissable();
@ -37,6 +42,9 @@ auto ToolsWindow::setVisible(bool visible) -> ToolsWindow& {
cheatDatabase.setVisible(false); cheatDatabase.setVisible(false);
cheatWindow.setVisible(false); cheatWindow.setVisible(false);
stateWindow.setVisible(false); stateWindow.setVisible(false);
} else {
Application::processEvents();
doSize();
} }
return *this; return *this;
} }
@ -45,5 +53,4 @@ auto ToolsWindow::show(uint index) -> void {
panel.item(index).setSelected(); panel.item(index).setSelected();
setVisible(); setVisible();
setFocused(); setFocused();
doSize();
} }

View File

@ -38,9 +38,9 @@ public:
HorizontalLayout nameLayout{&layout, Size{~0, 0}}; HorizontalLayout nameLayout{&layout, Size{~0, 0}};
Label nameLabel{&nameLayout, Size{40, 0}}; Label nameLabel{&nameLayout, Size{40, 0}};
LineEdit nameValue{&nameLayout, Size{~0, 0}}; LineEdit nameValue{&nameLayout, Size{~0, 0}};
HorizontalLayout codeLayout{&layout, Size{~0, 0}}; HorizontalLayout codeLayout{&layout, Size{~0, ~0}};
Label codeLabel{&codeLayout, Size{40, 0}}; Label codeLabel{&codeLayout, Size{40, 0}};
LineEdit codeValue{&codeLayout, Size{~0, 0}}; TextEdit codeValue{&codeLayout, Size{~0, ~0}};
HorizontalLayout controlLayout{&layout, Size{~0, 0}}; HorizontalLayout controlLayout{&layout, Size{~0, 0}};
Widget spacer{&controlLayout, Size{40, 0}}; Widget spacer{&controlLayout, Size{40, 0}};
CheckLabel enableOption{&controlLayout, Size{~0, 0}}; CheckLabel enableOption{&controlLayout, Size{~0, 0}};
@ -89,16 +89,34 @@ public:
Button cancelButton{&controlLayout, Size{80, 0}}; Button cancelButton{&controlLayout, Size{80, 0}};
}; };
struct StateManager : TabFrameItem { struct StateManager : TabFrameItem, Lock {
auto create() -> void; auto create() -> void;
auto type() const -> string;
auto loadStates() -> void; auto loadStates() -> void;
auto createState(string name) -> void; auto createState(string name) -> void;
auto modifyState(string name) -> void; auto modifyState(string name) -> void;
auto removeStates() -> void; auto removeStates() -> void;
auto updateSelection() -> void;
auto stateEvent(string name) -> void;
public: public:
enum class SortBy : uint {
NameAscending,
NameDescending,
DateAscending,
DateDescending,
} sortBy = SortBy::NameAscending;
VerticalLayout layout{this}; VerticalLayout layout{this};
TableView stateList{&layout, Size{~0, ~0}}; HorizontalLayout stateLayout{&layout, Size{~0, ~0}};
TableView stateList{&stateLayout, Size{~0, ~0}};
VerticalLayout previewLayout{&stateLayout, Size{0, ~0}};
HorizontalLayout categoryLayout{&previewLayout, Size{~0, 0}};
Label categoryLabel{&categoryLayout, Size{0, 0}};
ComboButton categoryOption{&categoryLayout, Size{~0, 0}};
Canvas statePreviewSeparator{&previewLayout, Size{~0, 1}};
Label statePreviewLabel{&previewLayout, Size{~0, 0}};
Canvas statePreview{&previewLayout, Size{256, 224}};
HorizontalLayout controlLayout{&layout, Size{~0, 0}}; HorizontalLayout controlLayout{&layout, Size{~0, 0}};
Button loadButton{&controlLayout, Size{80, 0}}; Button loadButton{&controlLayout, Size{80, 0}};
Button saveButton{&controlLayout, Size{80, 0}}; Button saveButton{&controlLayout, Size{80, 0}};
@ -111,13 +129,22 @@ public:
struct ManifestViewer : TabFrameItem { struct ManifestViewer : TabFrameItem {
auto create() -> void; auto create() -> void;
auto loadManifest() -> void; auto loadManifest() -> void;
auto selectManifest() -> void;
public: public:
VerticalLayout layout{this}; VerticalLayout layout{this};
HorizontalLayout manifestLayout{&layout, Size{~0, 0}};
Label manifestLabel{&manifestLayout, Size{0, 0}};
ComboButton manifestOption{&manifestLayout, Size{~0, 0}};
Canvas manifestSpacer{&layout, Size{~0, 1}};
HorizontalLayout informationLayout{&layout, Size{~0, 0}};
Canvas typeIcon{&informationLayout, Size{16, 16}};
Label nameLabel{&informationLayout, Size{~0, 0}};
#if 0 && defined(Hiro_SourceEdit)
SourceEdit manifestView{&layout, Size{~0, ~0}};
#else
TextEdit manifestView{&layout, Size{~0, ~0}}; TextEdit manifestView{&layout, Size{~0, ~0}};
HorizontalLayout verifiedLayout{&layout, Size{~0, 0}}; #endif
Canvas verifiedIcon{&verifiedLayout, Size{16, 16}};
Label verifiedLabel{&verifiedLayout, Size{~0, 0}};
}; };
struct ToolsWindow : Window { struct ToolsWindow : Window {

View File

@ -26,6 +26,9 @@ using namespace nall;
#define signal(function, ...) \ #define signal(function, ...) \
(delegate ? self()->function(__VA_ARGS__) : decltype(self()->function(__VA_ARGS__))()) (delegate ? self()->function(__VA_ARGS__) : decltype(self()->function(__VA_ARGS__))())
#define signalex(object, function, ...) \
(object->delegate ? object->self()->function(__VA_ARGS__) : decltype(object->self()->function(__VA_ARGS__))())
namespace hiro { namespace hiro {
#include "color.cpp" #include "color.cpp"
#include "gradient.cpp" #include "gradient.cpp"
@ -90,7 +93,6 @@ namespace hiro {
#include "widget/tab-frame.cpp" #include "widget/tab-frame.cpp"
#include "widget/tab-frame-item.cpp" #include "widget/tab-frame-item.cpp"
#include "widget/table-view.cpp" #include "widget/table-view.cpp"
#include "widget/table-view-header.cpp"
#include "widget/table-view-column.cpp" #include "widget/table-view-column.cpp"
#include "widget/table-view-item.cpp" #include "widget/table-view-item.cpp"
#include "widget/table-view-cell.cpp" #include "widget/table-view-cell.cpp"
@ -103,3 +105,4 @@ namespace hiro {
} }
#undef signal #undef signal
#undef signalex

View File

@ -25,6 +25,7 @@ using nall::set;
using nall::shared_pointer; using nall::shared_pointer;
using nall::shared_pointer_weak; using nall::shared_pointer_weak;
using nall::string; using nall::string;
using nall::string_pascal;
using nall::vector; using nall::vector;
namespace hiro { namespace hiro {
@ -95,6 +96,7 @@ Declare(Viewport)
enum class Orientation : uint { Horizontal, Vertical }; enum class Orientation : uint { Horizontal, Vertical };
enum class Navigation : uint { Top, Bottom, Left, Right }; enum class Navigation : uint { Top, Bottom, Left, Right };
enum class Sort : uint { None, Ascending, Descending };
#if defined(Hiro_Color) #if defined(Hiro_Color)
struct Color { struct Color {
@ -305,40 +307,7 @@ struct Geometry {
}; };
#endif #endif
#if defined(Hiro_Font) #include "font.hpp"
struct Font {
using type = Font;
Font(const string& family = "", float size = 0);
explicit operator bool() const;
auto operator==(const Font& source) const -> bool;
auto operator!=(const Font& source) const -> bool;
auto bold() const -> bool;
auto family() const -> string;
auto italic() const -> bool;
auto reset() -> type&;
auto setBold(bool bold = true) -> type&;
auto setFamily(const string& family = "") -> type&;
auto setItalic(bool italic = true) -> type&;
auto setSize(float size = 0) -> type&;
auto size() const -> float;
auto size(const string& text) const -> Size;
static const string Sans;
static const string Serif;
static const string Mono;
//private:
struct State {
string family;
float size;
bool bold;
bool italic;
} state;
};
#endif
#if defined(Hiro_Hotkey) #if defined(Hiro_Hotkey)
struct Hotkey { struct Hotkey {
@ -707,35 +676,9 @@ struct mMenuRadioItem : mAction {
}; };
#endif #endif
#if defined(Hiro_Sizable) #include "sizable.hpp"
struct mSizable : mObject {
Declare(Sizable)
auto geometry() const -> Geometry; #include "widget/widget.hpp"
virtual auto minimumSize() const -> Size;
virtual auto setGeometry(Geometry geometry) -> type&;
//private:
struct State {
Geometry geometry;
} state;
};
#endif
#if defined(Hiro_Widget)
struct mWidget : mSizable {
Declare(Widget)
auto doSize() const -> void;
auto onSize(const function<void ()>& callback = {}) -> type&;
auto remove() -> type& override;
//private:
struct State {
function<void ()> onSize;
} state;
};
#endif
#if defined(Hiro_Button) #if defined(Hiro_Button)
struct mButton : mWidget { struct mButton : mWidget {
@ -1274,28 +1217,7 @@ struct mRadioLabel : mWidget {
}; };
#endif #endif
#if defined(Hiro_SourceEdit) #include "widget/source-edit.hpp"
struct mSourceEdit : mWidget {
Declare(SourceEdit)
auto cursor() const -> Cursor;
auto doChange() const -> void;
auto doMove() const -> void;
auto onChange(const function<void ()>& callback = {}) -> type&;
auto onMove(const function<void ()>& callback = {}) -> type&;
auto setCursor(Cursor cursor = {}) -> type&;
auto setText(const string& text = "") -> type&;
auto text() const -> string;
//private:
struct State {
Cursor cursor;
function<void ()> onChange;
function<void ()> onMove;
string text;
} state;
};
#endif
#if defined(Hiro_TabFrame) #if defined(Hiro_TabFrame)
struct mTabFrame : mWidget { struct mTabFrame : mWidget {
@ -1374,210 +1296,10 @@ struct mTabFrameItem : mObject {
}; };
#endif #endif
#if defined(Hiro_TableView) #include "widget/table-view.hpp"
struct mTableView : mWidget { #include "widget/table-view-column.hpp"
Declare(TableView) #include "widget/table-view-item.hpp"
using mObject::remove; #include "widget/table-view-cell.hpp"
auto alignment() const -> Alignment;
auto append(sTableViewHeader column) -> type&;
auto append(sTableViewItem item) -> type&;
auto backgroundColor() const -> Color;
auto batchable() const -> bool;
auto batched() const -> vector<TableViewItem>;
auto bordered() const -> bool;
auto doActivate() const -> void;
auto doChange() const -> void;
auto doContext() const -> void;
auto doEdit(sTableViewCell cell) const -> void;
auto doSort(sTableViewColumn column) const -> void;
auto doToggle(sTableViewCell cell) const -> void;
auto foregroundColor() const -> Color;
auto header() const -> TableViewHeader;
auto item(uint position) const -> TableViewItem;
auto itemCount() const -> uint;
auto items() const -> vector<TableViewItem>;
auto onActivate(const function<void ()>& callback = {}) -> type&;
auto onChange(const function<void ()>& callback = {}) -> type&;
auto onContext(const function<void ()>& callback = {}) -> type&;
auto onEdit(const function<void (TableViewCell)>& callback = {}) -> type&;
auto onSort(const function<void (TableViewColumn)>& callback = {}) -> type&;
auto onToggle(const function<void (TableViewCell)>& callback = {}) -> type&;
auto remove(sTableViewHeader column) -> type&;
auto remove(sTableViewItem item) -> type&;
auto reset() -> type&;
auto resizeColumns() -> type&;
auto selected() const -> TableViewItem;
auto setAlignment(Alignment alignment = {}) -> type&;
auto setBackgroundColor(Color color = {}) -> type&;
auto setBatchable(bool batchable = true) -> type&;
auto setBordered(bool bordered = true) -> type&;
auto setForegroundColor(Color color = {}) -> type&;
auto setParent(mObject* parent = nullptr, int offset = -1) -> type& override;
//private:
struct State {
uint activeColumn = 0;
Alignment alignment;
Color backgroundColor;
bool batchable = false;
bool bordered = false;
Color foregroundColor;
sTableViewHeader header;
vector<sTableViewItem> items;
function<void ()> onActivate;
function<void ()> onChange;
function<void ()> onContext;
function<void (TableViewCell)> onEdit;
function<void (TableViewColumn)> onSort;
function<void (TableViewCell)> onToggle;
} state;
auto destruct() -> void override;
};
#endif
#if defined(Hiro_TableView)
struct mTableViewHeader : mObject {
Declare(TableViewHeader)
auto append(sTableViewColumn column) -> type&;
auto column(uint position) const -> TableViewColumn;
auto columnCount() const -> uint;
auto columns() const -> vector<TableViewColumn>;
auto remove() -> type& override;
auto remove(sTableViewColumn column) -> type&;
auto reset() -> type&;
auto setParent(mObject* parent = nullptr, int offset = -1) -> type& override;
//private:
struct State {
vector<sTableViewColumn> columns;
} state;
auto destruct() -> void override;
};
#endif
#if defined(Hiro_TableView)
struct mTableViewColumn : mObject {
Declare(TableViewColumn)
auto active() const -> bool;
auto alignment() const -> Alignment;
auto backgroundColor() const -> Color;
auto editable() const -> bool;
auto expandable() const -> bool;
auto foregroundColor() const -> Color;
auto horizontalAlignment() const -> float;
auto icon() const -> image;
auto remove() -> type& override;
auto resizable() const -> bool;
auto setActive() -> type&;
auto setAlignment(Alignment alignment = {}) -> type&;
auto setBackgroundColor(Color color = {}) -> type&;
auto setEditable(bool editable = true) -> type&;
auto setExpandable(bool expandable = true) -> type&;
auto setForegroundColor(Color color = {}) -> type&;
auto setHorizontalAlignment(float alignment = 0.0) -> type&;
auto setIcon(const image& icon = {}) -> type&;
auto setResizable(bool resizable = true) -> type&;
auto setSortable(bool sortable = true) -> type&;
auto setText(const string& text = "") -> type&;
auto setVerticalAlignment(float alignment = 0.5) -> type&;
auto setVisible(bool visible = true) -> type&;
auto setWidth(float width = 0) -> type&;
auto sortable() const -> bool;
auto text() const -> string;
auto verticalAlignment() const -> float;
auto width() const -> float;
//private:
struct State {
Alignment alignment;
Color backgroundColor;
bool editable = false;
bool expandable = false;
Color foregroundColor;
float horizontalAlignment = 0.0;
image icon;
bool resizable = true;
bool sortable = false;
string text;
float verticalAlignment = 0.5;
bool visible = true;
float width = 0;
} state;
};
#endif
#if defined(Hiro_TableView)
struct mTableViewItem : mObject {
Declare(TableViewItem)
auto alignment() const -> Alignment;
auto append(sTableViewCell cell) -> type&;
auto backgroundColor() const -> Color;
auto cell(uint position) const -> TableViewCell;
auto cellCount() const -> uint;
auto cells() const -> vector<TableViewCell>;
auto foregroundColor() const -> Color;
auto remove() -> type& override;
auto remove(sTableViewCell cell) -> type&;
auto reset() -> type&;
auto selected() const -> bool;
auto setAlignment(Alignment alignment = {}) -> type&;
auto setBackgroundColor(Color color = {}) -> type&;
auto setFocused() -> type& override;
auto setForegroundColor(Color color = {}) -> type&;
auto setParent(mObject* parent = nullptr, int offset = -1) -> type& override;
auto setSelected(bool selected = true) -> type&;
//private:
struct State {
Alignment alignment;
Color backgroundColor;
vector<sTableViewCell> cells;
Color foregroundColor;
bool selected = false;
} state;
auto destruct() -> void override;
};
#endif
#if defined(Hiro_TableView)
struct mTableViewCell : mObject {
Declare(TableViewCell)
auto alignment(bool recursive = false) const -> Alignment;
auto backgroundColor(bool recursive = false) const -> Color;
auto checkable() const -> bool;
auto checked() const -> bool;
auto font(bool recursive = false) const -> Font;
auto foregroundColor(bool recursive = false) const -> Color;
auto icon() const -> image;
auto setAlignment(Alignment alignment = {}) -> type&;
auto setBackgroundColor(Color color = {}) -> type&;
auto setCheckable(bool checkable = true) -> type&;
auto setChecked(bool checked = true) -> type&;
auto setForegroundColor(Color color = {}) -> type&;
auto setIcon(const image& icon = {}) -> type&;
auto setText(const string& text = "") -> type&;
auto text() const -> string;
//private:
struct State {
Alignment alignment;
Color backgroundColor;
bool checkable = false;
bool checked = false;
Color foregroundColor;
image icon;
string text;
} state;
};
#endif
#if defined(Hiro_TextEdit) #if defined(Hiro_TextEdit)
struct mTextEdit : mWidget { struct mTextEdit : mWidget {

35
hiro/core/font.hpp Normal file
View File

@ -0,0 +1,35 @@
#if defined(Hiro_Font)
struct Font {
using type = Font;
Font(const string& family = "", float size = 0);
explicit operator bool() const;
auto operator==(const Font& source) const -> bool;
auto operator!=(const Font& source) const -> bool;
auto bold() const -> bool;
auto family() const -> string;
auto italic() const -> bool;
auto reset() -> type&;
auto setBold(bool bold = true) -> type&;
auto setFamily(const string& family = "") -> type&;
auto setItalic(bool italic = true) -> type&;
auto setSize(float size = 0) -> type&;
auto size() const -> float;
auto size(const string& text) const -> Size;
static const string Sans;
static const string Serif;
static const string Mono;
//private:
//sizeof(Font) == 32
struct State {
string family; //24
float size = 8.0; //4
char bold = false; //1+
char italic = false; //1=4
} state;
};
#endif

View File

@ -50,3 +50,5 @@ struct mLock {
mutable int locks = 0; mutable int locks = 0;
}; };
using Lock = mLock;

View File

@ -199,16 +199,6 @@ auto mObject::parentTableView(bool recursive) const -> mTableView* {
} }
#endif #endif
#if defined(Hiro_TableView)
auto mObject::parentTableViewHeader(bool recursive) const -> mTableViewHeader* {
if(auto tableViewHeader = dynamic_cast<mTableViewHeader*>(parent())) return tableViewHeader;
if(recursive) {
if(auto object = parent()) return object->parentTableViewHeader(true);
}
return nullptr;
}
#endif
#if defined(Hiro_TableView) #if defined(Hiro_TableView)
auto mObject::parentTableViewItem(bool recursive) const -> mTableViewItem* { auto mObject::parentTableViewItem(bool recursive) const -> mTableViewItem* {
if(auto tableViewItem = dynamic_cast<mTableViewItem*>(parent())) return tableViewItem; if(auto tableViewItem = dynamic_cast<mTableViewItem*>(parent())) return tableViewItem;

View File

@ -28,7 +28,6 @@ struct mObject {
auto parentTabFrame(bool recursive = false) const -> mTabFrame*; auto parentTabFrame(bool recursive = false) const -> mTabFrame*;
auto parentTabFrameItem(bool recursive = false) const -> mTabFrameItem*; auto parentTabFrameItem(bool recursive = false) const -> mTabFrameItem*;
auto parentTableView(bool recursive = false) const -> mTableView*; auto parentTableView(bool recursive = false) const -> mTableView*;
auto parentTableViewHeader(bool recursive = false) const -> mTableViewHeader*;
auto parentTableViewItem(bool recursive = false) const -> mTableViewItem*; auto parentTableViewItem(bool recursive = false) const -> mTableViewItem*;
auto parentTreeView(bool recursive = false) const -> mTreeView*; auto parentTreeView(bool recursive = false) const -> mTreeView*;
auto parentTreeViewItem(bool recursive = false) const -> mTreeViewItem*; auto parentTreeViewItem(bool recursive = false) const -> mTreeViewItem*;
@ -47,17 +46,19 @@ struct mObject {
auto visible(bool recursive = false) const -> bool; auto visible(bool recursive = false) const -> bool;
//private: //private:
//sizeof(mObject) == 72
struct State { struct State {
bool enabled = true; Font font; //16
Font font; set<Property> properties; //16
int offset = -1; mObject* parent = nullptr; // 8
mObject* parent = nullptr; int offset = -1; // 4
set<Property> properties; char enabled = true; // 1+
bool visible = true; char visible = true; // 1=4
} state; } state;
wObject instance; wObject instance; // 8
pObject* delegate = nullptr; pObject* delegate = nullptr; // 8
//vtable // 8
virtual auto construct() -> void; virtual auto construct() -> void;
virtual auto destruct() -> void; virtual auto destruct() -> void;

View File

@ -1,5 +1,6 @@
#define DeclareShared(Name) \ #define DeclareShared(Name) \
using type = Name; \ using type = Name; \
using internalType = m##Name; \
Name() : s##Name(new m##Name, [](auto p) { \ Name() : s##Name(new m##Name, [](auto p) { \
p->unbind(); \ p->unbind(); \
delete p; \ delete p; \
@ -7,6 +8,9 @@
(*this)->bind(*this); \ (*this)->bind(*this); \
} \ } \
Name(const s##Name& source) : s##Name(source) { assert(source); } \ Name(const s##Name& source) : s##Name(source) { assert(source); } \
template<typename T> Name(const T& source, \
std::enable_if_t<std::is_base_of<internalType, typename T::internalType>::value>* = 0) : \
s##Name((s##Name&)source) { assert(source); } \
explicit operator bool() const { return self().operator bool(); } \ explicit operator bool() const { return self().operator bool(); } \
auto self() const -> m##Name& { return (m##Name&)operator*(); } \ auto self() const -> m##Name& { return (m##Name&)operator*(); } \
@ -15,6 +19,9 @@
template<typename T, typename... P> Name(T* parent, P&&... p) : Name() { \ template<typename T, typename... P> Name(T* parent, P&&... p) : Name() { \
if(parent) (*parent)->append(*this, std::forward<P>(p)...); \ if(parent) (*parent)->append(*this, std::forward<P>(p)...); \
} \ } \
template<typename T> auto is() -> bool { \
return dynamic_cast<typename T::internalType*>(data()); \
} \
template<typename T> auto cast() -> T { \ template<typename T> auto cast() -> T { \
if(auto pointer = dynamic_cast<typename T::internalType*>(data())) { \ if(auto pointer = dynamic_cast<typename T::internalType*>(data())) { \
if(auto shared = pointer->instance.acquire()) return T(shared); \ if(auto shared = pointer->instance.acquire()) return T(shared); \
@ -57,14 +64,12 @@
#if defined(Hiro_Object) #if defined(Hiro_Object)
struct Object : sObject { struct Object : sObject {
DeclareSharedObject(Object) DeclareSharedObject(Object)
using internalType = mObject;
}; };
#endif #endif
#if defined(Hiro_Group) #if defined(Hiro_Group)
struct Group : sGroup { struct Group : sGroup {
DeclareShared(Group) DeclareShared(Group)
using internalType = mGroup;
template<typename... P> Group(P&&... p) : Group() { _append(std::forward<P>(p)...); } template<typename... P> Group(P&&... p) : Group() { _append(std::forward<P>(p)...); }
auto append(sObject object) -> type& { return self().append(object), *this; } auto append(sObject object) -> type& { return self().append(object), *this; }
@ -93,7 +98,6 @@ private:
#if defined(Hiro_Timer) #if defined(Hiro_Timer)
struct Timer : sTimer { struct Timer : sTimer {
DeclareSharedObject(Timer) DeclareSharedObject(Timer)
using internalType = mTimer;
auto doActivate() const { return self().doActivate(); } auto doActivate() const { return self().doActivate(); }
auto interval() const { return self().interval(); } auto interval() const { return self().interval(); }
@ -105,14 +109,12 @@ struct Timer : sTimer {
#if defined(Hiro_Action) #if defined(Hiro_Action)
struct Action : sAction { struct Action : sAction {
DeclareSharedAction(Action) DeclareSharedAction(Action)
using internalType = mAction;
}; };
#endif #endif
#if defined(Hiro_Menu) #if defined(Hiro_Menu)
struct Menu : sMenu { struct Menu : sMenu {
DeclareSharedAction(Menu) DeclareSharedAction(Menu)
using internalType = mMenu;
auto action(unsigned position) const { return self().action(position); } auto action(unsigned position) const { return self().action(position); }
auto actionCount() const { return self().actionCount(); } auto actionCount() const { return self().actionCount(); }
@ -130,14 +132,12 @@ struct Menu : sMenu {
#if defined(Hiro_MenuSeparator) #if defined(Hiro_MenuSeparator)
struct MenuSeparator : sMenuSeparator { struct MenuSeparator : sMenuSeparator {
DeclareSharedAction(MenuSeparator) DeclareSharedAction(MenuSeparator)
using internalType = mMenuSeparator;
}; };
#endif #endif
#if defined(Hiro_MenuItem) #if defined(Hiro_MenuItem)
struct MenuItem : sMenuItem { struct MenuItem : sMenuItem {
DeclareSharedAction(MenuItem) DeclareSharedAction(MenuItem)
using internalType = mMenuItem;
auto doActivate() const { return self().doActivate(); } auto doActivate() const { return self().doActivate(); }
auto icon() const { return self().icon(); } auto icon() const { return self().icon(); }
@ -151,7 +151,6 @@ struct MenuItem : sMenuItem {
#if defined(Hiro_MenuCheckItem) #if defined(Hiro_MenuCheckItem)
struct MenuCheckItem : sMenuCheckItem { struct MenuCheckItem : sMenuCheckItem {
DeclareSharedAction(MenuCheckItem) DeclareSharedAction(MenuCheckItem)
using internalType = mMenuCheckItem;
auto checked() const { return self().checked(); } auto checked() const { return self().checked(); }
auto doToggle() const { return self().doToggle(); } auto doToggle() const { return self().doToggle(); }
@ -165,7 +164,6 @@ struct MenuCheckItem : sMenuCheckItem {
#if defined(Hiro_MenuRadioItem) #if defined(Hiro_MenuRadioItem)
struct MenuRadioItem : sMenuRadioItem { struct MenuRadioItem : sMenuRadioItem {
DeclareSharedAction(MenuRadioItem) DeclareSharedAction(MenuRadioItem)
using internalType = mMenuRadioItem;
auto checked() const { return self().checked(); } auto checked() const { return self().checked(); }
auto doActivate() const { return self().doActivate(); } auto doActivate() const { return self().doActivate(); }
@ -180,21 +178,18 @@ struct MenuRadioItem : sMenuRadioItem {
#if defined(Hiro_Sizable) #if defined(Hiro_Sizable)
struct Sizable : sSizable { struct Sizable : sSizable {
DeclareSharedSizable(Sizable) DeclareSharedSizable(Sizable)
using internalType = mSizable;
}; };
#endif #endif
#if defined(Hiro_Widget) #if defined(Hiro_Widget)
struct Widget : sWidget { struct Widget : sWidget {
DeclareSharedWidget(Widget) DeclareSharedWidget(Widget)
using internalType = mWidget;
}; };
#endif #endif
#if defined(Hiro_Button) #if defined(Hiro_Button)
struct Button : sButton { struct Button : sButton {
DeclareSharedWidget(Button) DeclareSharedWidget(Button)
using internalType = mButton;
auto bordered() const { return self().bordered(); } auto bordered() const { return self().bordered(); }
auto doActivate() const { return self().doActivate(); } auto doActivate() const { return self().doActivate(); }
@ -212,7 +207,6 @@ struct Button : sButton {
#if defined(Hiro_Canvas) #if defined(Hiro_Canvas)
struct Canvas : sCanvas { struct Canvas : sCanvas {
DeclareSharedWidget(Canvas) DeclareSharedWidget(Canvas)
using internalType = mCanvas;
auto color() const { return self().color(); } auto color() const { return self().color(); }
auto data() { return self().data(); } auto data() { return self().data(); }
@ -241,7 +235,6 @@ struct Canvas : sCanvas {
#if defined(Hiro_CheckButton) #if defined(Hiro_CheckButton)
struct CheckButton : sCheckButton { struct CheckButton : sCheckButton {
DeclareSharedWidget(CheckButton) DeclareSharedWidget(CheckButton)
using internalType = mCheckButton;
auto bordered() const { return self().bordered(); } auto bordered() const { return self().bordered(); }
auto checked() const { return self().checked(); } auto checked() const { return self().checked(); }
@ -261,7 +254,6 @@ struct CheckButton : sCheckButton {
#if defined(Hiro_CheckLabel) #if defined(Hiro_CheckLabel)
struct CheckLabel : sCheckLabel { struct CheckLabel : sCheckLabel {
DeclareSharedWidget(CheckLabel) DeclareSharedWidget(CheckLabel)
using internalType = mCheckLabel;
auto checked() const { return self().checked(); } auto checked() const { return self().checked(); }
auto doToggle() const { return self().doToggle(); } auto doToggle() const { return self().doToggle(); }
@ -275,7 +267,6 @@ struct CheckLabel : sCheckLabel {
#if defined(Hiro_ComboButton) #if defined(Hiro_ComboButton)
struct ComboButtonItem : sComboButtonItem { struct ComboButtonItem : sComboButtonItem {
DeclareSharedObject(ComboButtonItem) DeclareSharedObject(ComboButtonItem)
using internalType = mComboButtonItem;
auto icon() const { return self().icon(); } auto icon() const { return self().icon(); }
auto selected() const { return self().selected(); } auto selected() const { return self().selected(); }
@ -289,7 +280,6 @@ struct ComboButtonItem : sComboButtonItem {
#if defined(Hiro_ComboButton) #if defined(Hiro_ComboButton)
struct ComboButton : sComboButton { struct ComboButton : sComboButton {
DeclareSharedWidget(ComboButton) DeclareSharedWidget(ComboButton)
using internalType = mComboButton;
auto append(sComboButtonItem item) { return self().append(item), *this; } auto append(sComboButtonItem item) { return self().append(item), *this; }
auto doChange() const { return self().doChange(); } auto doChange() const { return self().doChange(); }
@ -307,7 +297,6 @@ struct ComboButton : sComboButton {
#if defined(Hiro_ComboEdit) #if defined(Hiro_ComboEdit)
struct ComboEditItem : sComboEditItem { struct ComboEditItem : sComboEditItem {
DeclareSharedObject(ComboEditItem) DeclareSharedObject(ComboEditItem)
using internalType = mComboEditItem;
auto icon() const { return self().icon(); } auto icon() const { return self().icon(); }
auto setIcon(const image& icon = {}) { return self().setIcon(icon), *this; } auto setIcon(const image& icon = {}) { return self().setIcon(icon), *this; }
@ -319,7 +308,6 @@ struct ComboEditItem : sComboEditItem {
#if defined(Hiro_ComboEdit) #if defined(Hiro_ComboEdit)
struct ComboEdit : sComboEdit { struct ComboEdit : sComboEdit {
DeclareSharedWidget(ComboEdit) DeclareSharedWidget(ComboEdit)
using internalType = mComboEdit;
auto append(sComboEditItem item) { return self().append(item), *this; } auto append(sComboEditItem item) { return self().append(item), *this; }
auto backgroundColor() const { return self().backgroundColor(); } auto backgroundColor() const { return self().backgroundColor(); }
@ -345,7 +333,6 @@ struct ComboEdit : sComboEdit {
#if defined(Hiro_Console) #if defined(Hiro_Console)
struct Console : sConsole { struct Console : sConsole {
DeclareSharedWidget(Console) DeclareSharedWidget(Console)
using internalType = mConsole;
auto backgroundColor() const { return self().backgroundColor(); } auto backgroundColor() const { return self().backgroundColor(); }
auto doActivate(string command) const { return self().doActivate(command); } auto doActivate(string command) const { return self().doActivate(command); }
@ -363,7 +350,6 @@ struct Console : sConsole {
#if defined(Hiro_Frame) #if defined(Hiro_Frame)
struct Frame : sFrame { struct Frame : sFrame {
DeclareSharedWidget(Frame) DeclareSharedWidget(Frame)
using internalType = mFrame;
auto append(sSizable sizable) { return self().append(sizable), *this; } auto append(sSizable sizable) { return self().append(sizable), *this; }
auto remove(sSizable sizable) { return self().remove(sizable), *this; } auto remove(sSizable sizable) { return self().remove(sizable), *this; }
@ -377,7 +363,6 @@ struct Frame : sFrame {
#if defined(Hiro_HexEdit) #if defined(Hiro_HexEdit)
struct HexEdit : sHexEdit { struct HexEdit : sHexEdit {
DeclareSharedWidget(HexEdit) DeclareSharedWidget(HexEdit)
using internalType = mHexEdit;
auto address() const { return self().address(); } auto address() const { return self().address(); }
auto backgroundColor() const { return self().backgroundColor(); } auto backgroundColor() const { return self().backgroundColor(); }
@ -402,7 +387,6 @@ struct HexEdit : sHexEdit {
#if defined(Hiro_HorizontalScrollBar) #if defined(Hiro_HorizontalScrollBar)
struct HorizontalScrollBar : sHorizontalScrollBar { struct HorizontalScrollBar : sHorizontalScrollBar {
DeclareSharedWidget(HorizontalScrollBar) DeclareSharedWidget(HorizontalScrollBar)
using internalType = mHorizontalScrollBar;
auto doChange() const { return self().doChange(); } auto doChange() const { return self().doChange(); }
auto length() const { return self().length(); } auto length() const { return self().length(); }
@ -416,7 +400,6 @@ struct HorizontalScrollBar : sHorizontalScrollBar {
#if defined(Hiro_HorizontalSlider) #if defined(Hiro_HorizontalSlider)
struct HorizontalSlider : sHorizontalSlider { struct HorizontalSlider : sHorizontalSlider {
DeclareSharedWidget(HorizontalSlider) DeclareSharedWidget(HorizontalSlider)
using internalType = mHorizontalSlider;
auto doChange() const { return self().doChange(); } auto doChange() const { return self().doChange(); }
auto length() const { return self().length(); } auto length() const { return self().length(); }
@ -430,7 +413,6 @@ struct HorizontalSlider : sHorizontalSlider {
#if defined(Hiro_IconView) #if defined(Hiro_IconView)
struct IconViewItem : sIconViewItem { struct IconViewItem : sIconViewItem {
DeclareSharedObject(IconViewItem) DeclareSharedObject(IconViewItem)
using internalType = mIconViewItem;
auto icon() const { return self().icon(); } auto icon() const { return self().icon(); }
auto selected() const { return self().selected(); } auto selected() const { return self().selected(); }
@ -444,7 +426,6 @@ struct IconViewItem : sIconViewItem {
#if defined(Hiro_IconView) #if defined(Hiro_IconView)
struct IconView : sIconView { struct IconView : sIconView {
DeclareSharedWidget(IconView) DeclareSharedWidget(IconView)
using internalType = mIconView;
auto append(sIconViewItem item) { return self().append(item), *this; } auto append(sIconViewItem item) { return self().append(item), *this; }
auto backgroundColor() const { return self().backgroundColor(); } auto backgroundColor() const { return self().backgroundColor(); }
@ -477,7 +458,6 @@ struct IconView : sIconView {
#if defined(Hiro_Label) #if defined(Hiro_Label)
struct Label : sLabel { struct Label : sLabel {
DeclareSharedWidget(Label) DeclareSharedWidget(Label)
using internalType = mLabel;
auto alignment() const { return self().alignment(); } auto alignment() const { return self().alignment(); }
auto backgroundColor() const { return self().backgroundColor(); } auto backgroundColor() const { return self().backgroundColor(); }
@ -493,7 +473,6 @@ struct Label : sLabel {
#if defined(Hiro_LineEdit) #if defined(Hiro_LineEdit)
struct LineEdit : sLineEdit { struct LineEdit : sLineEdit {
DeclareSharedWidget(LineEdit) DeclareSharedWidget(LineEdit)
using internalType = mLineEdit;
auto backgroundColor() const { return self().backgroundColor(); } auto backgroundColor() const { return self().backgroundColor(); }
auto doActivate() const { return self().doActivate(); } auto doActivate() const { return self().doActivate(); }
@ -513,7 +492,6 @@ struct LineEdit : sLineEdit {
#if defined(Hiro_ProgressBar) #if defined(Hiro_ProgressBar)
struct ProgressBar : sProgressBar { struct ProgressBar : sProgressBar {
DeclareSharedWidget(ProgressBar) DeclareSharedWidget(ProgressBar)
using internalType = mProgressBar;
auto position() const { return self().position(); } auto position() const { return self().position(); }
auto setPosition(unsigned position = 0) { return self().setPosition(position), *this; } auto setPosition(unsigned position = 0) { return self().setPosition(position), *this; }
@ -523,7 +501,6 @@ struct ProgressBar : sProgressBar {
#if defined(Hiro_RadioButton) #if defined(Hiro_RadioButton)
struct RadioButton : sRadioButton { struct RadioButton : sRadioButton {
DeclareSharedWidget(RadioButton) DeclareSharedWidget(RadioButton)
using internalType = mRadioButton;
auto bordered() const { return self().bordered(); } auto bordered() const { return self().bordered(); }
auto checked() const { return self().checked(); } auto checked() const { return self().checked(); }
@ -544,7 +521,6 @@ struct RadioButton : sRadioButton {
#if defined(Hiro_RadioLabel) #if defined(Hiro_RadioLabel)
struct RadioLabel : sRadioLabel { struct RadioLabel : sRadioLabel {
DeclareSharedWidget(RadioLabel) DeclareSharedWidget(RadioLabel)
using internalType = mRadioLabel;
auto checked() const { return self().checked(); } auto checked() const { return self().checked(); }
auto doActivate() const { return self().doActivate(); } auto doActivate() const { return self().doActivate(); }
@ -559,23 +535,25 @@ struct RadioLabel : sRadioLabel {
#if defined(Hiro_SourceEdit) #if defined(Hiro_SourceEdit)
struct SourceEdit : sSourceEdit { struct SourceEdit : sSourceEdit {
DeclareSharedWidget(SourceEdit) DeclareSharedWidget(SourceEdit)
using internalType = mSourceEdit;
auto cursor() const { return self().cursor(); } auto cursor() const { return self().cursor(); }
auto doChange() const { return self().doChange(); } auto doChange() const { return self().doChange(); }
auto doMove() const { return self().doMove(); } auto doMove() const { return self().doMove(); }
auto editable() const { return self().editable(); }
auto onChange(const function<void ()>& callback = {}) { return self().onChange(callback), *this; } auto onChange(const function<void ()>& callback = {}) { return self().onChange(callback), *this; }
auto onMove(const function<void ()>& callback = {}) { return self().onMove(callback), *this; } auto onMove(const function<void ()>& callback = {}) { return self().onMove(callback), *this; }
auto setCursor(Cursor cursor = {}) { return self().setCursor(cursor), *this; } auto setCursor(Cursor cursor = {}) { return self().setCursor(cursor), *this; }
auto setEditable(bool editable = true) { return self().setEditable(editable), *this; }
auto setText(const string& text = "") { return self().setText(text), *this; } auto setText(const string& text = "") { return self().setText(text), *this; }
auto setWordWrap(bool wordWrap = true) { return self().setWordWrap(wordWrap), *this; }
auto text() const { return self().text(); } auto text() const { return self().text(); }
auto wordWrap() const { return self().wordWrap(); }
}; };
#endif #endif
#if defined(Hiro_TabFrame) #if defined(Hiro_TabFrame)
struct TabFrameItem : sTabFrameItem { struct TabFrameItem : sTabFrameItem {
DeclareSharedObject(TabFrameItem) DeclareSharedObject(TabFrameItem)
using internalType = mTabFrameItem;
auto append(sSizable sizable) { return self().append(sizable), *this; } auto append(sSizable sizable) { return self().append(sizable), *this; }
auto closable() const { return self().closable(); } auto closable() const { return self().closable(); }
@ -597,7 +575,6 @@ struct TabFrameItem : sTabFrameItem {
#if defined(Hiro_TabFrame) #if defined(Hiro_TabFrame)
struct TabFrame : sTabFrame { struct TabFrame : sTabFrame {
DeclareSharedWidget(TabFrame) DeclareSharedWidget(TabFrame)
using internalType = mTabFrame;
auto append(sTabFrameItem item) { return self().append(item), *this; } auto append(sTabFrameItem item) { return self().append(item), *this; }
auto doChange() const { return self().doChange(); } auto doChange() const { return self().doChange(); }
@ -620,7 +597,6 @@ struct TabFrame : sTabFrame {
#if defined(Hiro_TableView) #if defined(Hiro_TableView)
struct TableViewColumn : sTableViewColumn { struct TableViewColumn : sTableViewColumn {
DeclareSharedObject(TableViewColumn) DeclareSharedObject(TableViewColumn)
using internalType = mTableViewColumn;
auto active() const { return self().active(); } auto active() const { return self().active(); }
auto alignment() const { return self().alignment(); } auto alignment() const { return self().alignment(); }
@ -640,35 +616,21 @@ struct TableViewColumn : sTableViewColumn {
auto setHorizontalAlignment(float alignment = 0.0) { return self().setHorizontalAlignment(alignment), *this; } auto setHorizontalAlignment(float alignment = 0.0) { return self().setHorizontalAlignment(alignment), *this; }
auto setIcon(const image& icon = {}) { return self().setIcon(icon), *this; } auto setIcon(const image& icon = {}) { return self().setIcon(icon), *this; }
auto setResizable(bool resizable = true) { return self().setResizable(resizable), *this; } auto setResizable(bool resizable = true) { return self().setResizable(resizable), *this; }
auto setSortable(bool sortable = true) { return self().setSortable(sortable), *this; } auto setSorting(Sort sorting = Sort::None) { return self().setSorting(sorting), *this; }
auto setText(const string& text = "") { return self().setText(text), *this; } auto setText(const string& text = "") { return self().setText(text), *this; }
auto setVerticalAlignment(float alignment = 0.5) { return self().setVerticalAlignment(alignment), *this; } auto setVerticalAlignment(float alignment = 0.5) { return self().setVerticalAlignment(alignment), *this; }
auto setWidth(float width = 0) { return self().setWidth(width), *this; } auto setWidth(float width = 0) { return self().setWidth(width), *this; }
auto sortable() const { return self().sortable(); } auto sort(Sort sorting) { return self().sort(sorting), *this; }
auto sorting() const { return self().sorting(); }
auto text() const { return self().text(); } auto text() const { return self().text(); }
auto verticalAlignment() const { return self().verticalAlignment(); } auto verticalAlignment() const { return self().verticalAlignment(); }
auto width() const { return self().width(); } auto width() const { return self().width(); }
}; };
#endif #endif
#if defined(Hiro_TableView)
struct TableViewHeader : sTableViewHeader {
DeclareSharedObject(TableViewHeader)
using internalType = mTableViewHeader;
auto append(sTableViewColumn column) { return self().append(column), *this; }
auto column(unsigned position) const { return self().column(position); }
auto columnCount() const { return self().columnCount(); }
auto columns() const { return self().columns(); }
auto remove(sTableViewColumn column) { return self().remove(column), *this; }
auto reset() { return self().reset(), *this; }
};
#endif
#if defined(Hiro_TableView) #if defined(Hiro_TableView)
struct TableViewCell : sTableViewCell { struct TableViewCell : sTableViewCell {
DeclareSharedObject(TableViewCell) DeclareSharedObject(TableViewCell)
using internalType = mTableViewCell;
auto alignment() const { return self().alignment(); } auto alignment() const { return self().alignment(); }
auto backgroundColor() const { return self().backgroundColor(); } auto backgroundColor() const { return self().backgroundColor(); }
@ -690,7 +652,6 @@ struct TableViewCell : sTableViewCell {
#if defined(Hiro_TableView) #if defined(Hiro_TableView)
struct TableViewItem : sTableViewItem { struct TableViewItem : sTableViewItem {
DeclareSharedObject(TableViewItem) DeclareSharedObject(TableViewItem)
using internalType = mTableViewItem;
auto alignment() const { return self().alignment(); } auto alignment() const { return self().alignment(); }
auto append(sTableViewCell cell) { return self().append(cell), *this; } auto append(sTableViewCell cell) { return self().append(cell), *this; }
@ -712,15 +673,17 @@ struct TableViewItem : sTableViewItem {
#if defined(Hiro_TableView) #if defined(Hiro_TableView)
struct TableView : sTableView { struct TableView : sTableView {
DeclareSharedWidget(TableView) DeclareSharedWidget(TableView)
using internalType = mTableView;
auto alignment() const { return self().alignment(); } auto alignment() const { return self().alignment(); }
auto append(sTableViewHeader header) { return self().append(header), *this; } auto append(sTableViewColumn column) { return self().append(column), *this; }
auto append(sTableViewItem item) { return self().append(item), *this; } auto append(sTableViewItem item) { return self().append(item), *this; }
auto backgroundColor() const { return self().backgroundColor(); } auto backgroundColor() const { return self().backgroundColor(); }
auto batchable() const { return self().batchable(); } auto batchable() const { return self().batchable(); }
auto batched() const { return self().batched(); } auto batched() const { return self().batched(); }
auto bordered() const { return self().bordered(); } auto bordered() const { return self().bordered(); }
auto column(uint position) const { return self().column(position); }
auto columnCount() const { return self().columnCount(); }
auto columns() const { return self().columns(); }
auto doActivate() const { return self().doActivate(); } auto doActivate() const { return self().doActivate(); }
auto doChange() const { return self().doChange(); } auto doChange() const { return self().doChange(); }
auto doContext() const { return self().doContext(); } auto doContext() const { return self().doContext(); }
@ -728,7 +691,7 @@ struct TableView : sTableView {
auto doSort(sTableViewColumn column) const { return self().doSort(column); } auto doSort(sTableViewColumn column) const { return self().doSort(column); }
auto doToggle(sTableViewCell cell) const { return self().doToggle(cell); } auto doToggle(sTableViewCell cell) const { return self().doToggle(cell); }
auto foregroundColor() const { return self().foregroundColor(); } auto foregroundColor() const { return self().foregroundColor(); }
auto header() const { return self().header(); } auto headered() const { return self().headered(); }
auto item(unsigned position) const { return self().item(position); } auto item(unsigned position) const { return self().item(position); }
auto itemCount() const { return self().itemCount(); } auto itemCount() const { return self().itemCount(); }
auto items() const { return self().items(); } auto items() const { return self().items(); }
@ -738,7 +701,7 @@ struct TableView : sTableView {
auto onEdit(const function<void (TableViewCell)>& callback = {}) { return self().onEdit(callback), *this; } auto onEdit(const function<void (TableViewCell)>& callback = {}) { return self().onEdit(callback), *this; }
auto onSort(const function<void (TableViewColumn)>& callback = {}) { return self().onSort(callback), *this; } auto onSort(const function<void (TableViewColumn)>& callback = {}) { return self().onSort(callback), *this; }
auto onToggle(const function<void (TableViewCell)>& callback = {}) { return self().onToggle(callback), *this; } auto onToggle(const function<void (TableViewCell)>& callback = {}) { return self().onToggle(callback), *this; }
auto remove(sTableViewHeader header) { return self().remove(header), *this; } auto remove(sTableViewColumn column) { return self().remove(column), *this; }
auto remove(sTableViewItem item) { return self().remove(item), *this; } auto remove(sTableViewItem item) { return self().remove(item), *this; }
auto reset() { return self().reset(), *this; } auto reset() { return self().reset(), *this; }
auto resizeColumns() { return self().resizeColumns(), *this; } auto resizeColumns() { return self().resizeColumns(), *this; }
@ -748,13 +711,16 @@ struct TableView : sTableView {
auto setBatchable(bool batchable = true) { return self().setBatchable(batchable), *this; } auto setBatchable(bool batchable = true) { return self().setBatchable(batchable), *this; }
auto setBordered(bool bordered = true) { return self().setBordered(bordered), *this; } auto setBordered(bool bordered = true) { return self().setBordered(bordered), *this; }
auto setForegroundColor(Color color = {}) { return self().setForegroundColor(color), *this; } auto setForegroundColor(Color color = {}) { return self().setForegroundColor(color), *this; }
auto setHeadered(bool headered = true) { return self().setHeadered(headered), *this; }
auto setSortable(bool sortable = true) { return self().setSortable(sortable), *this; }
auto sort() { return self().sort(), *this; }
auto sortable() const { return self().sortable(); }
}; };
#endif #endif
#if defined(Hiro_TextEdit) #if defined(Hiro_TextEdit)
struct TextEdit : sTextEdit { struct TextEdit : sTextEdit {
DeclareSharedWidget(TextEdit) DeclareSharedWidget(TextEdit)
using internalType = mTextEdit;
auto backgroundColor() const { return self().backgroundColor(); } auto backgroundColor() const { return self().backgroundColor(); }
auto cursor() const { return self().cursor(); } auto cursor() const { return self().cursor(); }
@ -778,7 +744,6 @@ struct TextEdit : sTextEdit {
#if defined(Hiro_TreeView) #if defined(Hiro_TreeView)
struct TreeViewItem : sTreeViewItem { struct TreeViewItem : sTreeViewItem {
DeclareSharedObject(TreeViewItem) DeclareSharedObject(TreeViewItem)
using internalType = mTreeViewItem;
auto append(sTreeViewItem item) { return self().append(item), *this; } auto append(sTreeViewItem item) { return self().append(item), *this; }
auto backgroundColor() const { return self().backgroundColor(); } auto backgroundColor() const { return self().backgroundColor(); }
@ -807,7 +772,6 @@ struct TreeViewItem : sTreeViewItem {
#if defined(Hiro_TreeView) #if defined(Hiro_TreeView)
struct TreeView : sTreeView { struct TreeView : sTreeView {
DeclareSharedWidget(TreeView) DeclareSharedWidget(TreeView)
using internalType = mTreeView;
auto append(sTreeViewItem item) { return self().append(item), *this; } auto append(sTreeViewItem item) { return self().append(item), *this; }
auto backgroundColor() const { return self().backgroundColor(); } auto backgroundColor() const { return self().backgroundColor(); }
@ -834,7 +798,6 @@ struct TreeView : sTreeView {
#if defined(Hiro_VerticalScrollBar) #if defined(Hiro_VerticalScrollBar)
struct VerticalScrollBar : sVerticalScrollBar { struct VerticalScrollBar : sVerticalScrollBar {
DeclareSharedWidget(VerticalScrollBar) DeclareSharedWidget(VerticalScrollBar)
using internalType = mVerticalScrollBar;
auto doChange() const { return self().doChange(); } auto doChange() const { return self().doChange(); }
auto length() const { return self().length(); } auto length() const { return self().length(); }
@ -848,7 +811,6 @@ struct VerticalScrollBar : sVerticalScrollBar {
#if defined(Hiro_VerticalSlider) #if defined(Hiro_VerticalSlider)
struct VerticalSlider : sVerticalSlider { struct VerticalSlider : sVerticalSlider {
DeclareSharedWidget(VerticalSlider) DeclareSharedWidget(VerticalSlider)
using internalType = mVerticalSlider;
auto doChange() const { return self().doChange(); } auto doChange() const { return self().doChange(); }
auto length() const { return self().length(); } auto length() const { return self().length(); }
@ -862,7 +824,6 @@ struct VerticalSlider : sVerticalSlider {
#if defined(Hiro_Viewport) #if defined(Hiro_Viewport)
struct Viewport : sViewport { struct Viewport : sViewport {
DeclareSharedWidget(Viewport) DeclareSharedWidget(Viewport)
using internalType = mViewport;
auto doDrop(vector<string> names) const { return self().doDrop(names); } auto doDrop(vector<string> names) const { return self().doDrop(names); }
auto doMouseLeave() const { return self().doMouseLeave(); } auto doMouseLeave() const { return self().doMouseLeave(); }
@ -883,7 +844,6 @@ struct Viewport : sViewport {
#if defined(Hiro_StatusBar) #if defined(Hiro_StatusBar)
struct StatusBar : sStatusBar { struct StatusBar : sStatusBar {
DeclareSharedObject(StatusBar) DeclareSharedObject(StatusBar)
using internalType = mStatusBar;
auto setText(const string& text = "") { return self().setText(text), *this; } auto setText(const string& text = "") { return self().setText(text), *this; }
auto text() const { return self().text(); } auto text() const { return self().text(); }
@ -893,7 +853,6 @@ struct StatusBar : sStatusBar {
#if defined(Hiro_PopupMenu) #if defined(Hiro_PopupMenu)
struct PopupMenu : sPopupMenu { struct PopupMenu : sPopupMenu {
DeclareSharedObject(PopupMenu) DeclareSharedObject(PopupMenu)
using internalType = mPopupMenu;
auto action(unsigned position) const { return self().action(position); } auto action(unsigned position) const { return self().action(position); }
auto actionCount() const { return self().actionCount(); } auto actionCount() const { return self().actionCount(); }
@ -907,7 +866,6 @@ struct PopupMenu : sPopupMenu {
#if defined(Hiro_MenuBar) #if defined(Hiro_MenuBar)
struct MenuBar : sMenuBar { struct MenuBar : sMenuBar {
DeclareSharedObject(MenuBar) DeclareSharedObject(MenuBar)
using internalType = mMenuBar;
auto append(sMenu menu) { return self().append(menu), *this; } auto append(sMenu menu) { return self().append(menu), *this; }
auto menu(unsigned position) const { return self().menu(position); } auto menu(unsigned position) const { return self().menu(position); }
@ -921,7 +879,6 @@ struct MenuBar : sMenuBar {
#if defined(Hiro_Window) #if defined(Hiro_Window)
struct Window : sWindow { struct Window : sWindow {
DeclareSharedObject(Window) DeclareSharedObject(Window)
using internalType = mWindow;
auto append(sMenuBar menuBar) { return self().append(menuBar), *this; } auto append(sMenuBar menuBar) { return self().append(menuBar), *this; }
auto append(sSizable sizable) { return self().append(sizable), *this; } auto append(sSizable sizable) { return self().append(sizable), *this; }

15
hiro/core/sizable.hpp Normal file
View File

@ -0,0 +1,15 @@
#if defined(Hiro_Sizable)
struct mSizable : mObject {
Declare(Sizable)
auto geometry() const -> Geometry;
virtual auto minimumSize() const -> Size;
virtual auto setGeometry(Geometry geometry) -> type&;
//private:
//sizeof(mSizable) == 16
struct State {
Geometry geometry;
} state;
};
#endif

View File

@ -73,6 +73,8 @@ auto mCanvas::onMouseRelease(const function<void (Mouse::Button)>& callback) ->
auto mCanvas::setColor(Color color) -> type& { auto mCanvas::setColor(Color color) -> type& {
state.color = color; state.color = color;
state.gradient = {};
state.icon = {};
signal(setColor, color); signal(setColor, color);
return *this; return *this;
} }
@ -84,12 +86,16 @@ auto mCanvas::setDroppable(bool droppable) -> type& {
} }
auto mCanvas::setGradient(Gradient gradient) -> type& { auto mCanvas::setGradient(Gradient gradient) -> type& {
state.color = {};
state.gradient = gradient; state.gradient = gradient;
state.icon = {};
signal(setGradient, gradient); signal(setGradient, gradient);
return *this; return *this;
} }
auto mCanvas::setIcon(const image& icon) -> type& { auto mCanvas::setIcon(const image& icon) -> type& {
state.color = {};
state.gradient = {};
state.icon = icon; state.icon = icon;
signal(setIcon, icon); signal(setIcon, icon);
return *this; return *this;

View File

@ -18,6 +18,10 @@ auto mSourceEdit::doMove() const -> void {
if(state.onMove) return state.onMove(); if(state.onMove) return state.onMove();
} }
auto mSourceEdit::editable() const -> bool {
return state.editable;
}
auto mSourceEdit::onChange(const function<void ()>& callback) -> type& { auto mSourceEdit::onChange(const function<void ()>& callback) -> type& {
state.onChange = callback; state.onChange = callback;
return *this; return *this;
@ -34,14 +38,30 @@ auto mSourceEdit::setCursor(Cursor cursor) -> type& {
return *this; return *this;
} }
auto mSourceEdit::setEditable(bool editable) -> type& {
state.editable = editable;
signal(setEditable, editable);
return *this;
}
auto mSourceEdit::setText(const string& text) -> type& { auto mSourceEdit::setText(const string& text) -> type& {
state.text = text; state.text = text;
signal(setText, text); signal(setText, text);
return *this; return *this;
} }
auto mSourceEdit::setWordWrap(bool wordWrap) -> type& {
state.wordWrap = wordWrap;
signal(setWordWrap, wordWrap);
return *this;
}
auto mSourceEdit::text() const -> string { auto mSourceEdit::text() const -> string {
return signal(text); return signal(text);
} }
auto mSourceEdit::wordWrap() const -> bool {
return state.wordWrap;
}
#endif #endif

View File

@ -0,0 +1,28 @@
#if defined(Hiro_SourceEdit)
struct mSourceEdit : mWidget {
Declare(SourceEdit)
auto cursor() const -> Cursor;
auto doChange() const -> void;
auto doMove() const -> void;
auto editable() const -> bool;
auto onChange(const function<void ()>& callback = {}) -> type&;
auto onMove(const function<void ()>& callback = {}) -> type&;
auto setCursor(Cursor cursor = {}) -> type&;
auto setEditable(bool editable) -> type&;
auto setText(const string& text = "") -> type&;
auto setWordWrap(bool wordWrap = true) -> type&;
auto text() const -> string;
auto wordWrap() const -> bool;
//private:
struct State {
Cursor cursor;
bool editable = true;
function<void ()> onChange;
function<void ()> onMove;
string text;
bool wordWrap = true;
} state;
};
#endif

View File

@ -12,13 +12,11 @@ auto mTableViewCell::alignment(bool recursive) const -> Alignment {
if(auto parent = parentTableViewItem()) { if(auto parent = parentTableViewItem()) {
if(auto alignment = parent->state.alignment) return alignment; if(auto alignment = parent->state.alignment) return alignment;
if(auto grandparent = parent->parentTableView()) { if(auto grandparent = parent->parentTableView()) {
if(auto header = grandparent->state.header) { if(offset() < grandparent->columnCount()) {
if(offset() < header->columnCount()) { if(auto column = grandparent->state.columns[offset()]) {
if(auto column = header->state.columns[offset()]) {
if(auto alignment = column->state.alignment) return alignment; if(auto alignment = column->state.alignment) return alignment;
} }
} }
}
if(auto alignment = grandparent->state.alignment) return alignment; if(auto alignment = grandparent->state.alignment) return alignment;
} }
} }
@ -32,13 +30,11 @@ auto mTableViewCell::backgroundColor(bool recursive) const -> Color {
if(auto parent = parentTableViewItem()) { if(auto parent = parentTableViewItem()) {
if(auto color = parent->state.backgroundColor) return color; if(auto color = parent->state.backgroundColor) return color;
if(auto grandparent = parent->parentTableView()) { if(auto grandparent = parent->parentTableView()) {
if(auto header = grandparent->state.header) { if(offset() < grandparent->columnCount()) {
if(offset() < header->columnCount()) { if(auto column = grandparent->state.columns[offset()]) {
if(auto column = header->state.columns[offset()]) {
if(auto color = column->state.backgroundColor) return color; if(auto color = column->state.backgroundColor) return color;
} }
} }
}
if(auto color = grandparent->state.backgroundColor) return color; if(auto color = grandparent->state.backgroundColor) return color;
} }
} }
@ -60,13 +56,11 @@ auto mTableViewCell::font(bool recursive) const -> Font {
if(auto parent = parentTableViewItem()) { if(auto parent = parentTableViewItem()) {
if(auto font = parent->font()) return font; if(auto font = parent->font()) return font;
if(auto grandparent = parent->parentTableView()) { if(auto grandparent = parent->parentTableView()) {
if(auto header = grandparent->state.header) { if(offset() < grandparent->columnCount()) {
if(offset() < header->columnCount()) { if(auto column = grandparent->state.columns[offset()]) {
if(auto column = header->state.columns[offset()]) {
if(auto font = column->font()) return font; if(auto font = column->font()) return font;
} }
} }
}
if(auto font = grandparent->font(true)) return font; if(auto font = grandparent->font(true)) return font;
} }
} }
@ -80,13 +74,11 @@ auto mTableViewCell::foregroundColor(bool recursive) const -> Color {
if(auto parent = parentTableViewItem()) { if(auto parent = parentTableViewItem()) {
if(auto color = parent->state.foregroundColor) return color; if(auto color = parent->state.foregroundColor) return color;
if(auto grandparent = parent->parentTableView()) { if(auto grandparent = parent->parentTableView()) {
if(auto header = grandparent->state.header) { if(offset() < grandparent->columnCount()) {
if(offset() < header->columnCount()) { if(auto column = grandparent->state.columns[offset()]) {
if(auto column = header->state.columns[offset()]) {
if(auto color = column->state.foregroundColor) return color; if(auto color = column->state.foregroundColor) return color;
} }
} }
}
if(auto color = grandparent->state.foregroundColor) return color; if(auto color = grandparent->state.foregroundColor) return color;
} }
} }

View File

@ -0,0 +1,32 @@
#if defined(Hiro_TableView)
struct mTableViewCell : mObject {
Declare(TableViewCell)
auto alignment(bool recursive = false) const -> Alignment;
auto backgroundColor(bool recursive = false) const -> Color;
auto checkable() const -> bool;
auto checked() const -> bool;
auto font(bool recursive = false) const -> Font;
auto foregroundColor(bool recursive = false) const -> Color;
auto icon() const -> image;
auto setAlignment(Alignment alignment = {}) -> type&;
auto setBackgroundColor(Color color = {}) -> type&;
auto setCheckable(bool checkable = true) -> type&;
auto setChecked(bool checked = true) -> type&;
auto setForegroundColor(Color color = {}) -> type&;
auto setIcon(const image& icon = {}) -> type&;
auto setText(const string& text = "") -> type&;
auto text() const -> string;
//private:
struct State {
Alignment alignment;
Color backgroundColor;
bool checkable = false;
bool checked = false;
Color foregroundColor;
image icon;
string text;
} state;
};
#endif

View File

@ -42,7 +42,7 @@ auto mTableViewColumn::icon() const -> image {
} }
auto mTableViewColumn::remove() -> type& { auto mTableViewColumn::remove() -> type& {
if(auto tableViewHeader = parentTableViewHeader()) tableViewHeader->remove(*this); if(auto tableView = parentTableView()) tableView->remove(*this);
return *this; return *this;
} }
@ -105,9 +105,15 @@ auto mTableViewColumn::setResizable(bool resizable) -> type& {
return *this; return *this;
} }
auto mTableViewColumn::setSortable(bool sortable) -> type& { auto mTableViewColumn::setSorting(Sort sorting) -> type& {
state.sortable = sortable; if(auto tableView = parentTableView()) {
signal(setSortable, sortable); for(auto& column : tableView->state.columns) {
column->state.sorting = Sort::None;
signalex(column, setSorting, Sort::None);
}
}
state.sorting = sorting;
signal(setSorting, sorting);
return *this; return *this;
} }
@ -136,8 +142,8 @@ auto mTableViewColumn::setWidth(float width) -> type& {
return *this; return *this;
} }
auto mTableViewColumn::sortable() const -> bool { auto mTableViewColumn::sorting() const -> Sort {
return state.sortable; return state.sorting;
} }
auto mTableViewColumn::text() const -> string { auto mTableViewColumn::text() const -> string {

View File

@ -0,0 +1,53 @@
#if defined(Hiro_TableView)
struct mTableViewColumn : mObject {
Declare(TableViewColumn)
auto active() const -> bool;
auto alignment() const -> Alignment;
auto backgroundColor() const -> Color;
auto editable() const -> bool;
auto expandable() const -> bool;
auto foregroundColor() const -> Color;
auto horizontalAlignment() const -> float;
auto icon() const -> image;
auto remove() -> type& override;
auto resizable() const -> bool;
auto setActive() -> type&;
auto setAlignment(Alignment alignment = {}) -> type&;
auto setBackgroundColor(Color color = {}) -> type&;
auto setEditable(bool editable = true) -> type&;
auto setExpandable(bool expandable = true) -> type&;
auto setForegroundColor(Color color = {}) -> type&;
auto setHorizontalAlignment(float alignment = 0.0) -> type&;
auto setIcon(const image& icon = {}) -> type&;
auto setResizable(bool resizable = true) -> type&;
auto setSorting(Sort sorting = Sort::None) -> type&;
auto setText(const string& text = "") -> type&;
auto setVerticalAlignment(float alignment = 0.5) -> type&;
auto setVisible(bool visible = true) -> type&;
auto setWidth(float width = 0) -> type&;
auto sort(Sort sorting) -> type&;
auto sorting() const -> Sort;
auto text() const -> string;
auto verticalAlignment() const -> float;
auto width() const -> float;
//private:
struct State {
bool active = false;
Alignment alignment;
Color backgroundColor;
bool editable = false;
bool expandable = false;
Color foregroundColor;
float horizontalAlignment = 0.0;
image icon;
bool resizable = true;
Sort sorting = Sort::None;
string text;
float verticalAlignment = 0.5;
bool visible = true;
float width = 0;
} state;
};
#endif

View File

@ -1,63 +0,0 @@
#if defined(Hiro_TableView)
auto mTableViewHeader::allocate() -> pObject* {
return new pTableViewHeader(*this);
}
auto mTableViewHeader::destruct() -> void {
for(auto& column : state.columns) column->destruct();
mObject::destruct();
}
//
auto mTableViewHeader::append(sTableViewColumn column) -> type& {
state.columns.append(column);
column->setParent(this, columnCount() - 1);
signal(append, column);
return *this;
}
auto mTableViewHeader::column(unsigned position) const -> TableViewColumn {
if(position < columnCount()) return state.columns[position];
return {};
}
auto mTableViewHeader::columnCount() const -> unsigned {
return state.columns.size();
}
auto mTableViewHeader::columns() const -> vector<TableViewColumn> {
vector<TableViewColumn> columns;
for(auto& column : state.columns) columns.append(column);
return columns;
}
auto mTableViewHeader::remove() -> type& {
if(auto tableView = parentTableView()) tableView->remove(*this);
return *this;
}
auto mTableViewHeader::remove(sTableViewColumn column) -> type& {
signal(remove, column);
state.columns.remove(column->offset());
for(auto n : range(column->offset(), columnCount())) {
state.columns[n]->adjustOffset(-1);
}
column->setParent();
return *this;
}
auto mTableViewHeader::reset() -> type& {
while(state.columns) remove(state.columns.right());
return *this;
}
auto mTableViewHeader::setParent(mObject* parent, signed offset) -> type& {
for(auto& column : state.columns) column->destruct();
mObject::setParent(parent, offset);
for(auto& column : state.columns) column->setParent(this, column->offset());
return *this;
}
#endif

View File

@ -0,0 +1,34 @@
#if defined(Hiro_TableView)
struct mTableViewItem : mObject {
Declare(TableViewItem)
auto alignment() const -> Alignment;
auto append(sTableViewCell cell) -> type&;
auto backgroundColor() const -> Color;
auto cell(uint position) const -> TableViewCell;
auto cellCount() const -> uint;
auto cells() const -> vector<TableViewCell>;
auto foregroundColor() const -> Color;
auto remove() -> type& override;
auto remove(sTableViewCell cell) -> type&;
auto reset() -> type&;
auto selected() const -> bool;
auto setAlignment(Alignment alignment = {}) -> type&;
auto setBackgroundColor(Color color = {}) -> type&;
auto setFocused() -> type& override;
auto setForegroundColor(Color color = {}) -> type&;
auto setParent(mObject* parent = nullptr, int offset = -1) -> type& override;
auto setSelected(bool selected = true) -> type&;
//private:
struct State {
Alignment alignment;
Color backgroundColor;
vector<sTableViewCell> cells;
Color foregroundColor;
bool selected = false;
} state;
auto destruct() -> void override;
};
#endif

View File

@ -6,7 +6,7 @@ auto mTableView::allocate() -> pObject* {
auto mTableView::destruct() -> void { auto mTableView::destruct() -> void {
for(auto& item : state.items) item->destruct(); for(auto& item : state.items) item->destruct();
if(auto& header = state.header) header->destruct(); for(auto& column : state.columns) column->destruct();
mWidget::destruct(); mWidget::destruct();
} }
@ -16,11 +16,10 @@ auto mTableView::alignment() const -> Alignment {
return state.alignment; return state.alignment;
} }
auto mTableView::append(sTableViewHeader header) -> type& { auto mTableView::append(sTableViewColumn column) -> type& {
if(auto& header = state.header) remove(header); state.columns.append(column);
state.header = header; column->setParent(this, columnCount() - 1);
header->setParent(this, 0); signal(append, column);
signal(append, header);
return *this; return *this;
} }
@ -51,6 +50,21 @@ auto mTableView::bordered() const -> bool {
return state.bordered; return state.bordered;
} }
auto mTableView::column(uint position) const -> TableViewColumn {
if(position < columnCount()) return state.columns[position];
return {};
}
auto mTableView::columnCount() const -> uint {
return state.columns.size();
}
auto mTableView::columns() const -> vector<TableViewColumn> {
vector<TableViewColumn> columns;
for(auto& column : columns) columns.append(column);
return columns;
}
auto mTableView::doActivate() const -> void { auto mTableView::doActivate() const -> void {
if(state.onActivate) return state.onActivate(); if(state.onActivate) return state.onActivate();
} }
@ -79,8 +93,8 @@ auto mTableView::foregroundColor() const -> Color {
return state.foregroundColor; return state.foregroundColor;
} }
auto mTableView::header() const -> TableViewHeader { auto mTableView::headered() const -> bool {
return state.header; return state.headered;
} }
auto mTableView::item(unsigned position) const -> TableViewItem { auto mTableView::item(unsigned position) const -> TableViewItem {
@ -128,17 +142,20 @@ auto mTableView::onToggle(const function<void (TableViewCell)>& callback) -> typ
return *this; return *this;
} }
auto mTableView::remove(sTableViewHeader header) -> type& { auto mTableView::remove(sTableViewColumn column) -> type& {
signal(remove, header); signal(remove, column);
header->setParent(); state.columns.remove(column->offset());
state.header.reset(); for(uint n : range(column->offset(), columnCount())) {
state.columns[n]->adjustOffset(-1);
}
column->setParent();
return *this; return *this;
} }
auto mTableView::remove(sTableViewItem item) -> type& { auto mTableView::remove(sTableViewItem item) -> type& {
signal(remove, item); signal(remove, item);
state.items.remove(item->offset()); state.items.remove(item->offset());
for(auto n : range(item->offset(), itemCount())) { for(uint n : range(item->offset(), itemCount())) {
state.items[n]->adjustOffset(-1); state.items[n]->adjustOffset(-1);
} }
item->setParent(); item->setParent();
@ -146,8 +163,8 @@ auto mTableView::remove(sTableViewItem item) -> type& {
} }
auto mTableView::reset() -> type& { auto mTableView::reset() -> type& {
while(state.items) remove(state.items.right()); while(state.items) remove(state.items.last());
if(auto& header = state.header) remove(header); while(state.columns) remove(state.columns.last());
return *this; return *this;
} }
@ -193,13 +210,51 @@ auto mTableView::setForegroundColor(Color color) -> type& {
return *this; return *this;
} }
auto mTableView::setHeadered(bool headered) -> type& {
state.headered = headered;
signal(setHeadered, headered);
return *this;
}
auto mTableView::setParent(mObject* parent, signed offset) -> type& { auto mTableView::setParent(mObject* parent, signed offset) -> type& {
for(auto& item : reverse(state.items)) item->destruct(); for(auto& item : reverse(state.items)) item->destruct();
if(auto& header = state.header) header->destruct(); for(auto& column : reverse(state.columns)) column->destruct();
mObject::setParent(parent, offset); mObject::setParent(parent, offset);
if(auto& header = state.header) header->setParent(this, 0); for(auto& column : state.columns) column->setParent(this, column->offset());
for(auto& item : state.items) item->setParent(this, item->offset()); for(auto& item : state.items) item->setParent(this, item->offset());
return *this; return *this;
} }
auto mTableView::setSortable(bool sortable) -> type& {
state.sortable = sortable;
signal(setSortable, sortable);
return *this;
}
auto mTableView::sort() -> type& {
Sort sorting = Sort::None;
uint offset = 0;
for(auto& column : state.columns) {
if(column->sorting() == Sort::None) continue;
sorting = column->sorting();
offset = column->offset();
break;
}
auto sorted = state.items;
sorted.sort([&](auto& lhs, auto& rhs) {
string x = offset < lhs->cellCount() ? lhs->state.cells[offset]->state.text : "";
string y = offset < rhs->cellCount() ? rhs->state.cells[offset]->state.text : "";
if(sorting == Sort::Ascending ) return string::icompare(x, y) < 0;
if(sorting == Sort::Descending) return string::icompare(y, x) < 0;
return false; //unreachable
});
while(state.items) remove(state.items.last());
for(auto& item : sorted) append(item);
return *this;
}
auto mTableView::sortable() const -> bool {
return state.sortable;
}
#endif #endif

View File

@ -0,0 +1,71 @@
#if defined(Hiro_TableView)
struct mTableView : mWidget {
Declare(TableView)
using mObject::remove;
auto alignment() const -> Alignment;
auto append(sTableViewColumn column) -> type&;
auto append(sTableViewItem item) -> type&;
auto backgroundColor() const -> Color;
auto batchable() const -> bool;
auto batched() const -> vector<TableViewItem>;
auto bordered() const -> bool;
auto column(uint position) const -> TableViewColumn;
auto columnCount() const -> uint;
auto columns() const -> vector<TableViewColumn>;
auto doActivate() const -> void;
auto doChange() const -> void;
auto doContext() const -> void;
auto doEdit(sTableViewCell cell) const -> void;
auto doSort(sTableViewColumn column) const -> void;
auto doToggle(sTableViewCell cell) const -> void;
auto foregroundColor() const -> Color;
auto headered() const -> bool;
auto item(uint position) const -> TableViewItem;
auto itemCount() const -> uint;
auto items() const -> vector<TableViewItem>;
auto onActivate(const function<void ()>& callback = {}) -> type&;
auto onChange(const function<void ()>& callback = {}) -> type&;
auto onContext(const function<void ()>& callback = {}) -> type&;
auto onEdit(const function<void (TableViewCell)>& callback = {}) -> type&;
auto onSort(const function<void (TableViewColumn)>& callback = {}) -> type&;
auto onToggle(const function<void (TableViewCell)>& callback = {}) -> type&;
auto remove(sTableViewColumn column) -> type&;
auto remove(sTableViewItem item) -> type&;
auto reset() -> type&;
auto resizeColumns() -> type&;
auto selected() const -> TableViewItem;
auto setAlignment(Alignment alignment = {}) -> type&;
auto setBackgroundColor(Color color = {}) -> type&;
auto setBatchable(bool batchable = true) -> type&;
auto setBordered(bool bordered = true) -> type&;
auto setForegroundColor(Color color = {}) -> type&;
auto setHeadered(bool headered = true) -> type&;
auto setParent(mObject* parent = nullptr, int offset = -1) -> type& override;
auto setSortable(bool sortable = true) -> type&;
auto sort() -> type&;
auto sortable() const -> bool;
//private:
struct State {
uint activeColumn = 0;
Alignment alignment;
Color backgroundColor;
bool batchable = false;
bool bordered = false;
vector<sTableViewColumn> columns;
Color foregroundColor;
bool headered = false;
vector<sTableViewItem> items;
function<void ()> onActivate;
function<void ()> onChange;
function<void ()> onContext;
function<void (TableViewCell)> onEdit;
function<void (TableViewColumn)> onSort;
function<void (TableViewCell)> onToggle;
bool sortable = false;
} state;
auto destruct() -> void override;
};
#endif

View File

@ -0,0 +1,15 @@
#if defined(Hiro_Widget)
struct mWidget : mSizable {
Declare(Widget)
auto doSize() const -> void;
auto onSize(const function<void ()>& callback = {}) -> type&;
auto remove() -> type& override;
//private:
//sizeof(mWidget) == 8
struct State {
function<void ()> onSize;
} state;
};
#endif

View File

@ -14,7 +14,7 @@ mListView::mListView() {
} }
} }
}); });
append(TableViewHeader().setVisible(false).append(TableViewColumn().setExpandable())); append(TableViewColumn().setExpandable());
} }
auto mListView::batched() const -> vector<ListViewItem> { auto mListView::batched() const -> vector<ListViewItem> {
@ -64,7 +64,7 @@ auto mListView::onToggle(const function<void (ListViewItem)>& callback) -> type&
auto mListView::reset() -> type& { auto mListView::reset() -> type& {
mTableView::reset(); mTableView::reset();
append(TableViewHeader().setVisible(false).append(TableViewColumn().setExpandable())); append(TableViewColumn().setExpandable());
return *this; return *this;
} }

View File

@ -52,7 +52,6 @@
#include "widget/tab-frame.cpp" #include "widget/tab-frame.cpp"
#include "widget/tab-frame-item.cpp" #include "widget/tab-frame-item.cpp"
#include "widget/table-view.cpp" #include "widget/table-view.cpp"
#include "widget/table-view-header.cpp"
#include "widget/table-view-column.cpp" #include "widget/table-view-column.cpp"
#include "widget/table-view-item.cpp" #include "widget/table-view-item.cpp"
#include "widget/table-view-cell.cpp" #include "widget/table-view-cell.cpp"

View File

@ -56,7 +56,6 @@
#include "widget/tab-frame.hpp" #include "widget/tab-frame.hpp"
#include "widget/tab-frame-item.hpp" #include "widget/tab-frame-item.hpp"
#include "widget/table-view.hpp" #include "widget/table-view.hpp"
#include "widget/table-view-header.hpp"
#include "widget/table-view-column.hpp" #include "widget/table-view-column.hpp"
#include "widget/table-view-item.hpp" #include "widget/table-view-item.hpp"
#include "widget/table-view-cell.hpp" #include "widget/table-view-cell.hpp"

View File

@ -54,7 +54,9 @@ auto pSourceEdit::construct() -> void {
gtk_container_add(gtkContainer, gtkWidgetSourceView); gtk_container_add(gtkContainer, gtkWidgetSourceView);
gtk_widget_show(gtkWidgetSourceView); gtk_widget_show(gtkWidgetSourceView);
setEditable(state().editable);
setText(state().text); setText(state().text);
setWordWrap(state().wordWrap);
g_signal_connect(G_OBJECT(gtkSourceBuffer), "changed", G_CALLBACK(SourceEdit_change), (gpointer)this); g_signal_connect(G_OBJECT(gtkSourceBuffer), "changed", G_CALLBACK(SourceEdit_change), (gpointer)this);
g_signal_connect(G_OBJECT(gtkSourceBuffer), "notify::cursor-position", G_CALLBACK(SourceEdit_move), (gpointer)this); g_signal_connect(G_OBJECT(gtkSourceBuffer), "notify::cursor-position", G_CALLBACK(SourceEdit_move), (gpointer)this);
@ -82,6 +84,10 @@ auto pSourceEdit::setCursor(Cursor cursor) -> void {
unlock(); unlock();
} }
auto pSourceEdit::setEditable(bool editable) -> void {
gtk_text_view_set_editable(gtkTextView, editable);
}
auto pSourceEdit::setFocused() -> void { auto pSourceEdit::setFocused() -> void {
gtk_widget_grab_focus(gtkWidgetSourceView); gtk_widget_grab_focus(gtkWidgetSourceView);
} }
@ -128,6 +134,11 @@ auto pSourceEdit::setText(const string& text) -> void {
unlock(); unlock();
} }
auto pSourceEdit::setWordWrap(bool wordWrap) -> void {
gtk_text_view_set_wrap_mode(gtkTextView, wordWrap ? GTK_WRAP_WORD_CHAR : GTK_WRAP_NONE);
gtk_scrolled_window_set_policy(gtkScrolledWindow, wordWrap ? GTK_POLICY_NEVER : GTK_POLICY_ALWAYS, GTK_POLICY_ALWAYS);
}
auto pSourceEdit::text() const -> string { auto pSourceEdit::text() const -> string {
GtkTextIter startIter; GtkTextIter startIter;
gtk_text_buffer_get_start_iter(gtkTextBuffer, &startIter); gtk_text_buffer_get_start_iter(gtkTextBuffer, &startIter);

View File

@ -6,8 +6,10 @@ struct pSourceEdit : pWidget {
Declare(SourceEdit, Widget) Declare(SourceEdit, Widget)
auto setCursor(Cursor cursor) -> void; auto setCursor(Cursor cursor) -> void;
auto setEditable(bool editable) -> void;
auto setFocused() -> void override; auto setFocused() -> void override;
auto setText(const string& text) -> void; auto setText(const string& text) -> void;
auto setWordWrap(bool wordWrap) -> void;
auto text() const -> string; auto text() const -> string;
GtkScrolledWindow* gtkScrolledWindow = nullptr; GtkScrolledWindow* gtkScrolledWindow = nullptr;

View File

@ -48,8 +48,7 @@ auto pTableViewCell::_parent() -> maybe<pTableViewItem&> {
auto pTableViewCell::_setState() -> void { auto pTableViewCell::_setState() -> void {
if(auto parent = _parent()) { if(auto parent = _parent()) {
if(auto grandparent = _grandparent()) { if(auto grandparent = _grandparent()) {
if(auto& tableViewHeader = grandparent->state().header) { if(self().offset() < grandparent->self().columnCount()) {
if(self().offset() < tableViewHeader->columnCount()) {
auto lock = grandparent->acquire(); auto lock = grandparent->acquire();
gtk_list_store_set(grandparent->gtkListStore, &parent->gtkIter, 3 * self().offset() + 0, state().checked, -1); gtk_list_store_set(grandparent->gtkListStore, &parent->gtkIter, 3 * self().offset() + 0, state().checked, -1);
gtk_list_store_set(grandparent->gtkListStore, &parent->gtkIter, 3 * self().offset() + 1, CreatePixbuf(state().icon), -1); gtk_list_store_set(grandparent->gtkListStore, &parent->gtkIter, 3 * self().offset() + 1, CreatePixbuf(state().icon), -1);
@ -57,7 +56,6 @@ auto pTableViewCell::_setState() -> void {
} }
} }
} }
}
} }
} }

View File

@ -3,8 +3,8 @@
namespace hiro { namespace hiro {
auto pTableViewColumn::construct() -> void { auto pTableViewColumn::construct() -> void {
if(auto grandparent = _grandparent()) { if(auto parent = _parent()) {
auto handle = grandparent.data(); auto handle = parent.data();
uint offset = self().offset(); uint offset = self().offset();
#if HIRO_GTK==2 #if HIRO_GTK==2
@ -16,9 +16,12 @@ auto pTableViewColumn::construct() -> void {
gtkHeaderIcon = gtk_image_new(); gtkHeaderIcon = gtk_image_new();
gtk_box_pack_start(GTK_BOX(gtkHeader), gtkHeaderIcon, false, false, 0); gtk_box_pack_start(GTK_BOX(gtkHeader), gtkHeaderIcon, false, false, 0);
gtkHeaderText = gtk_label_new(state().text); gtkHeaderText = gtk_label_new("");
gtk_box_pack_start(GTK_BOX(gtkHeader), gtkHeaderText, true, false, 2); gtk_box_pack_start(GTK_BOX(gtkHeader), gtkHeaderText, true, false, 2);
gtkHeaderSort = gtk_label_new("");
gtk_box_pack_start(GTK_BOX(gtkHeader), gtkHeaderSort, false, false, 0);
gtkColumn = gtk_tree_view_column_new(); gtkColumn = gtk_tree_view_column_new();
gtk_tree_view_column_set_sizing(gtkColumn, GTK_TREE_VIEW_COLUMN_FIXED); gtk_tree_view_column_set_sizing(gtkColumn, GTK_TREE_VIEW_COLUMN_FIXED);
gtk_tree_view_column_set_title(gtkColumn, ""); gtk_tree_view_column_set_title(gtkColumn, "");
@ -43,24 +46,34 @@ auto pTableViewColumn::construct() -> void {
g_signal_connect(G_OBJECT(gtkCellText), "edited", G_CALLBACK(TableView_edit), (gpointer)handle); g_signal_connect(G_OBJECT(gtkCellText), "edited", G_CALLBACK(TableView_edit), (gpointer)handle);
g_signal_connect(G_OBJECT(gtkCellToggle), "toggled", G_CALLBACK(TableView_toggle), (gpointer)handle); g_signal_connect(G_OBJECT(gtkCellToggle), "toggled", G_CALLBACK(TableView_toggle), (gpointer)handle);
gtk_tree_view_append_column(grandparent->gtkTreeView, gtkColumn); gtk_tree_view_append_column(parent->gtkTreeView, gtkColumn);
gtk_widget_show_all(gtkHeader); gtk_widget_show_all(gtkHeader);
grandparent->_createModel(); parent->_createModel();
_setState(); gtk_tree_view_column_set_clickable(gtkColumn, parent->state().sortable);
if(state().active) setActive();
setEditable(state().editable);
setIcon(state().icon);
setResizable(state().resizable);
setSorting(state().sorting);
setText(state().text);
setVisible(state().visible);
} }
} }
auto pTableViewColumn::destruct() -> void { auto pTableViewColumn::destruct() -> void {
if(auto grandparent = _grandparent()) { if(auto parent = _parent()) {
gtk_tree_view_remove_column(grandparent->gtkTreeView, gtkColumn); gtk_tree_view_remove_column(parent->gtkTreeView, gtkColumn);
gtkColumn = nullptr; gtkColumn = nullptr;
grandparent->_createModel(); parent->_createModel();
} }
} }
auto pTableViewColumn::setActive() -> void { auto pTableViewColumn::setActive() -> void {
_setState(); if(auto parent = _parent()) {
gtk_tree_view_set_search_column(parent->gtkTreeView, 3 * self().offset() + 2);
}
} }
auto pTableViewColumn::setAlignment(Alignment alignment) -> void { auto pTableViewColumn::setAlignment(Alignment alignment) -> void {
@ -74,8 +87,8 @@ auto pTableViewColumn::setEditable(bool editable) -> void {
} }
auto pTableViewColumn::setExpandable(bool expandable) -> void { auto pTableViewColumn::setExpandable(bool expandable) -> void {
if(auto grandparent = _grandparent()) { if(auto parent = _parent()) {
grandparent->resizeColumns(); parent->resizeColumns();
} }
} }
@ -94,47 +107,35 @@ auto pTableViewColumn::setIcon(const image& icon) -> void {
} }
auto pTableViewColumn::setResizable(bool resizable) -> void { auto pTableViewColumn::setResizable(bool resizable) -> void {
_setState(); gtk_tree_view_column_set_resizable(gtkColumn, resizable);
} }
auto pTableViewColumn::setSortable(bool sortable) -> void { auto pTableViewColumn::setSorting(Sort sorting) -> void {
_setState(); string text;
if(sorting == Sort::Ascending ) text = "\u25b4";
if(sorting == Sort::Descending) text = "\u25be";
gtk_label_set_text(GTK_LABEL(gtkHeaderSort), text);
} }
auto pTableViewColumn::setText(const string& text) -> void { auto pTableViewColumn::setText(const string& text) -> void {
_setState(); gtk_label_set_text(GTK_LABEL(gtkHeaderText), text);
} }
auto pTableViewColumn::setVisible(bool visible) -> void { auto pTableViewColumn::setVisible(bool visible) -> void {
_setState(); gtk_tree_view_column_set_visible(gtkColumn, visible);
} }
auto pTableViewColumn::setWidth(signed width) -> void { auto pTableViewColumn::setWidth(signed width) -> void {
if(auto grandparent = _grandparent()) { if(auto parent = _parent()) {
grandparent->resizeColumns(); parent->resizeColumns();
} }
} }
auto pTableViewColumn::_grandparent() -> maybe<pTableView&> { auto pTableViewColumn::_parent() -> maybe<pTableView&> {
if(auto parent = _parent()) return parent->_parent(); if(auto parent = self().parentTableView()) {
return nothing;
}
auto pTableViewColumn::_parent() -> maybe<pTableViewHeader&> {
if(auto parent = self().parentTableViewHeader()) {
if(auto self = parent->self()) return *self; if(auto self = parent->self()) return *self;
} }
return nothing; return {};
}
auto pTableViewColumn::_setState() -> void {
if(auto grandparent = _grandparent()) {
gtk_tree_view_set_search_column(grandparent->gtkTreeView, 3 * self().offset() + 2);
gtk_tree_view_column_set_resizable(gtkColumn, state().resizable);
gtk_tree_view_column_set_clickable(gtkColumn, state().sortable);
gtk_label_set_text(GTK_LABEL(gtkHeaderText), state().text);
gtk_tree_view_column_set_visible(gtkColumn, self().visible());
}
} }
} }

View File

@ -15,20 +15,19 @@ struct pTableViewColumn : pObject {
auto setHorizontalAlignment(double) -> void {} auto setHorizontalAlignment(double) -> void {}
auto setIcon(const image& icon) -> void; auto setIcon(const image& icon) -> void;
auto setResizable(bool resizable) -> void; auto setResizable(bool resizable) -> void;
auto setSortable(bool sortable) -> void; auto setSorting(Sort sorting) -> void;
auto setText(const string& text) -> void; auto setText(const string& text) -> void;
auto setVerticalAlignment(double) -> void {} auto setVerticalAlignment(double) -> void {}
auto setVisible(bool visible) -> void override; auto setVisible(bool visible) -> void override;
auto setWidth(signed width) -> void; auto setWidth(signed width) -> void;
auto _grandparent() -> maybe<pTableView&>; auto _parent() -> maybe<pTableView&>;
auto _parent() -> maybe<pTableViewHeader&>;
auto _setState() -> void;
GtkTreeViewColumn* gtkColumn = nullptr; GtkTreeViewColumn* gtkColumn = nullptr;
GtkWidget* gtkHeader = nullptr; GtkWidget* gtkHeader = nullptr;
GtkWidget* gtkHeaderIcon = nullptr; GtkWidget* gtkHeaderIcon = nullptr;
GtkWidget* gtkHeaderText = nullptr; GtkWidget* gtkHeaderText = nullptr;
GtkWidget* gtkHeaderSort = nullptr;
GtkCellRenderer* gtkCellToggle = nullptr; GtkCellRenderer* gtkCellToggle = nullptr;
GtkCellRenderer* gtkCellIcon = nullptr; GtkCellRenderer* gtkCellIcon = nullptr;
GtkCellRenderer* gtkCellText = nullptr; GtkCellRenderer* gtkCellText = nullptr;

View File

@ -1,41 +0,0 @@
#if defined(Hiro_TableView)
namespace hiro {
auto pTableViewHeader::construct() -> void {
_setState();
}
auto pTableViewHeader::destruct() -> void {
}
auto pTableViewHeader::append(sTableViewColumn column) -> void {
_setState();
}
auto pTableViewHeader::remove(sTableViewColumn column) -> void {
}
auto pTableViewHeader::setVisible(bool visible) -> void {
_setState();
}
auto pTableViewHeader::_parent() -> maybe<pTableView&> {
if(auto parent = self().parentTableView()) {
if(auto self = parent->self()) return *self;
}
return nothing;
}
auto pTableViewHeader::_setState() -> void {
if(auto parent = _parent()) {
gtk_tree_view_set_headers_visible(parent->gtkTreeView, self().visible());
for(auto& column : state().columns) {
if(auto self = column->self()) self->_setState();
}
}
}
}
#endif

View File

@ -1,18 +0,0 @@
#if defined(Hiro_TableView)
namespace hiro {
struct pTableViewHeader : pObject {
Declare(TableViewHeader, Object)
auto append(sTableViewColumn column) -> void;
auto remove(sTableViewColumn column) -> void;
auto setVisible(bool visible) -> void override;
auto _parent() -> maybe<pTableView&>;
auto _setState() -> void;
};
}
#endif

View File

@ -34,6 +34,8 @@ auto pTableView::construct() -> void {
setBordered(state().bordered); setBordered(state().bordered);
setFont(self().font(true)); setFont(self().font(true));
setForegroundColor(state().foregroundColor); setForegroundColor(state().foregroundColor);
setHeadered(state().headered);
setSortable(state().sortable);
g_signal_connect(G_OBJECT(gtkTreeView), "button-press-event", G_CALLBACK(TableView_buttonEvent), (gpointer)this); g_signal_connect(G_OBJECT(gtkTreeView), "button-press-event", G_CALLBACK(TableView_buttonEvent), (gpointer)this);
g_signal_connect(G_OBJECT(gtkTreeView), "button-release-event", G_CALLBACK(TableView_buttonEvent), (gpointer)this); g_signal_connect(G_OBJECT(gtkTreeView), "button-release-event", G_CALLBACK(TableView_buttonEvent), (gpointer)this);
@ -50,7 +52,7 @@ auto pTableView::destruct() -> void {
gtk_widget_destroy(gtkWidget); gtk_widget_destroy(gtkWidget);
} }
auto pTableView::append(sTableViewHeader header) -> void { auto pTableView::append(sTableViewColumn column) -> void {
} }
auto pTableView::append(sTableViewItem item) -> void { auto pTableView::append(sTableViewItem item) -> void {
@ -60,24 +62,23 @@ auto pTableView::focused() const -> bool {
return gtk_widget_has_focus(GTK_WIDGET(gtkTreeView)); return gtk_widget_has_focus(GTK_WIDGET(gtkTreeView));
} }
auto pTableView::remove(sTableViewHeader header) -> void { auto pTableView::remove(sTableViewColumn column) -> void {
} }
auto pTableView::remove(sTableViewItem item) -> void { auto pTableView::remove(sTableViewItem item) -> void {
} }
auto pTableView::resizeColumns() -> void { auto pTableView::resizeColumns() -> void {
lock(); auto lock = acquire();
if(auto& header = state().header) {
vector<signed> widths; vector<signed> widths;
signed minimumWidth = 0; signed minimumWidth = 0;
signed expandable = 0; signed expandable = 0;
for(auto column : range(header->columnCount())) { for(uint position : range(self().columnCount())) {
signed width = _width(column); signed width = _width(position);
widths.append(width); widths.append(width);
minimumWidth += width; minimumWidth += width;
if(header->column(column).expandable()) expandable++; if(self().column(position).expandable()) expandable++;
} }
signed maximumWidth = self().geometry().width() - 6; signed maximumWidth = self().geometry().width() - 6;
@ -92,16 +93,13 @@ auto pTableView::resizeColumns() -> void {
expandWidth = (maximumWidth - minimumWidth) / expandable; expandWidth = (maximumWidth - minimumWidth) / expandable;
} }
for(auto column : range(header->columnCount())) { for(uint position : range(self().columnCount())) {
if(auto self = header->state.columns[column]->self()) { if(auto column = self().column(position)->self()) {
signed width = widths[column]; signed width = widths[position];
if(self->state().expandable) width += expandWidth; if(column->state().expandable) width += expandWidth;
gtk_tree_view_column_set_fixed_width(self->gtkColumn, width); gtk_tree_view_column_set_fixed_width(column->gtkColumn, width);
} }
} }
}
unlock();
} }
auto pTableView::setAlignment(Alignment alignment) -> void { auto pTableView::setAlignment(Alignment alignment) -> void {
@ -131,9 +129,6 @@ auto pTableView::setFocused() -> void {
} }
auto pTableView::setFont(const Font& font) -> void { auto pTableView::setFont(const Font& font) -> void {
if(auto& header = state().header) {
if(auto self = header->self()) self->_setState();
}
} }
auto pTableView::setForegroundColor(Color color) -> void { auto pTableView::setForegroundColor(Color color) -> void {
@ -143,10 +138,18 @@ auto pTableView::setForegroundColor(Color color) -> void {
auto pTableView::setGeometry(Geometry geometry) -> void { auto pTableView::setGeometry(Geometry geometry) -> void {
pWidget::setGeometry(geometry); pWidget::setGeometry(geometry);
if(auto& header = state().header) { for(auto& column : state().columns) {
for(auto& column : header->state.columns) { if(column->expandable()) return resizeColumns();
if(column->state.expandable) return resizeColumns();
} }
}
auto pTableView::setHeadered(bool headered) -> void {
gtk_tree_view_set_headers_visible(gtkTreeView, headered);
}
auto pTableView::setSortable(bool sortable) -> void {
for(auto& column : state().columns) {
if(auto self = column->self()) gtk_tree_view_column_set_clickable(self->gtkColumn, sortable);
} }
} }
@ -170,14 +173,15 @@ auto pTableView::_cellWidth(unsigned _row, unsigned _column) -> unsigned {
auto pTableView::_columnWidth(unsigned _column) -> unsigned { auto pTableView::_columnWidth(unsigned _column) -> unsigned {
unsigned width = 8; unsigned width = 8;
if(auto& header = state().header) { if(auto column = self().column(_column)) {
if(auto column = header->column(_column)) {
if(auto& icon = column->state.icon) { if(auto& icon = column->state.icon) {
width += icon.width() + 2; width += icon.width() + 2;
} }
if(auto& text = column->state.text) { if(auto& text = column->state.text) {
width += pFont::size(column->font(true), text).width(); width += pFont::size(column->font(true), text).width();
} }
if(column->state.sorting != Sort::None) {
width += 20;
} }
} }
return width; return width;
@ -191,8 +195,7 @@ auto pTableView::_createModel() -> void {
gtkTreeModel = nullptr; gtkTreeModel = nullptr;
vector<GType> types; vector<GType> types;
if(auto& header = state().header) { for(auto& column : state().columns) {
for(auto column : header->state.columns) {
if(auto self = column->self()) { if(auto self = column->self()) {
if(!self->gtkColumn) continue; //may not have been created yet; or recently destroyed if(!self->gtkColumn) continue; //may not have been created yet; or recently destroyed
types.append(G_TYPE_BOOLEAN); types.append(G_TYPE_BOOLEAN);
@ -200,7 +203,6 @@ auto pTableView::_createModel() -> void {
types.append(G_TYPE_STRING); types.append(G_TYPE_STRING);
} }
} }
}
if(!types) return; //no columns available if(!types) return; //no columns available
gtkListStore = gtk_list_store_newv(types.size(), types.data()); gtkListStore = gtk_list_store_newv(types.size(), types.data());
@ -230,8 +232,7 @@ auto pTableView::_doDataFunc(GtkTreeViewColumn* gtkColumn, GtkCellRenderer* rend
auto row = toNatural(path); auto row = toNatural(path);
g_free(path); g_free(path);
if(auto& header = state().header) { for(auto& column : state().columns) {
for(auto& column : header->state.columns) {
if(auto p = column->self()) { if(auto p = column->self()) {
if(renderer != GTK_CELL_RENDERER(p->gtkCellToggle) if(renderer != GTK_CELL_RENDERER(p->gtkCellToggle)
&& renderer != GTK_CELL_RENDERER(p->gtkCellIcon) && renderer != GTK_CELL_RENDERER(p->gtkCellIcon)
@ -272,12 +273,10 @@ auto pTableView::_doDataFunc(GtkTreeViewColumn* gtkColumn, GtkCellRenderer* rend
} }
} }
} }
}
} }
auto pTableView::_doEdit(GtkCellRendererText* gtkCellRendererText, const char* path, const char* text) -> void { auto pTableView::_doEdit(GtkCellRendererText* gtkCellRendererText, const char* path, const char* text) -> void {
if(auto& header = state().header) { for(auto& column : state().columns) {
for(auto& column : header->state.columns) {
if(auto delegate = column->self()) { if(auto delegate = column->self()) {
if(gtkCellRendererText == GTK_CELL_RENDERER_TEXT(delegate->gtkCellText)) { if(gtkCellRendererText == GTK_CELL_RENDERER_TEXT(delegate->gtkCellText)) {
auto row = toNatural(path); auto row = toNatural(path);
@ -293,7 +292,6 @@ auto pTableView::_doEdit(GtkCellRendererText* gtkCellRendererText, const char* p
} }
} }
} }
}
} }
auto pTableView::_doEvent(GdkEventButton* event) -> signed { auto pTableView::_doEvent(GdkEventButton* event) -> signed {
@ -326,8 +324,7 @@ auto pTableView::_doEvent(GdkEventButton* event) -> signed {
} }
auto pTableView::_doHeaderActivate(GtkTreeViewColumn* gtkTreeViewColumn) -> void { auto pTableView::_doHeaderActivate(GtkTreeViewColumn* gtkTreeViewColumn) -> void {
if(auto& header = state().header) { for(auto& column : state().columns) {
for(auto& column : header->state.columns) {
if(auto delegate = column->self()) { if(auto delegate = column->self()) {
if(gtkTreeViewColumn == delegate->gtkColumn) { if(gtkTreeViewColumn == delegate->gtkColumn) {
if(!locked()) self().doSort(column); if(!locked()) self().doSort(column);
@ -335,7 +332,6 @@ auto pTableView::_doHeaderActivate(GtkTreeViewColumn* gtkTreeViewColumn) -> void
} }
} }
} }
}
} }
//GtkTreeView::cursor-changed and GtkTreeSelection::changed do not send signals for changes during rubber-banding selection //GtkTreeView::cursor-changed and GtkTreeSelection::changed do not send signals for changes during rubber-banding selection
@ -348,8 +344,7 @@ auto pTableView::_doMouseMove() -> signed {
} }
auto pTableView::_doToggle(GtkCellRendererToggle* gtkCellRendererToggle, const char* path) -> void { auto pTableView::_doToggle(GtkCellRendererToggle* gtkCellRendererToggle, const char* path) -> void {
if(auto& header = state().header) { for(auto& column : state().columns) {
for(auto& column : header->state.columns) {
if(auto delegate = column->self()) { if(auto delegate = column->self()) {
if(gtkCellRendererToggle == GTK_CELL_RENDERER_TOGGLE(delegate->gtkCellToggle)) { if(gtkCellRendererToggle == GTK_CELL_RENDERER_TOGGLE(delegate->gtkCellToggle)) {
auto row = toNatural(path); auto row = toNatural(path);
@ -363,7 +358,6 @@ auto pTableView::_doToggle(GtkCellRendererToggle* gtkCellRendererToggle, const c
} }
} }
} }
}
} }
//compare currently selected items to previously selected items //compare currently selected items to previously selected items
@ -412,17 +406,14 @@ auto pTableView::_updateSelected() -> void {
} }
auto pTableView::_width(unsigned column) -> unsigned { auto pTableView::_width(unsigned column) -> unsigned {
if(auto& header = state().header) { if(auto width = self().column(column).width()) return width;
if(auto width = header->column(column).width()) return width;
unsigned width = 1; unsigned width = 1;
if(!header->column(column).visible()) return width; if(!self().column(column).visible()) return width;
if(header->visible()) width = max(width, _columnWidth(column)); if(self().headered()) width = max(width, _columnWidth(column));
for(auto row : range(self().itemCount())) { for(auto row : range(self().itemCount())) {
width = max(width, _cellWidth(row, column)); width = max(width, _cellWidth(row, column));
} }
return width; return width;
}
return 1;
} }
} }

View File

@ -5,10 +5,10 @@ namespace hiro {
struct pTableView : pWidget { struct pTableView : pWidget {
Declare(TableView, Widget) Declare(TableView, Widget)
auto append(sTableViewHeader column) -> void; auto append(sTableViewColumn column) -> void;
auto append(sTableViewItem item) -> void; auto append(sTableViewItem item) -> void;
auto focused() const -> bool override; auto focused() const -> bool override;
auto remove(sTableViewHeader column) -> void; auto remove(sTableViewColumn column) -> void;
auto remove(sTableViewItem item) -> void; auto remove(sTableViewItem item) -> void;
auto resizeColumns() -> void; auto resizeColumns() -> void;
auto setAlignment(Alignment alignment) -> void; auto setAlignment(Alignment alignment) -> void;
@ -19,21 +19,23 @@ struct pTableView : pWidget {
auto setFont(const Font& font) -> void override; auto setFont(const Font& font) -> void override;
auto setForegroundColor(Color color) -> void; auto setForegroundColor(Color color) -> void;
auto setGeometry(Geometry geometry) -> void override; auto setGeometry(Geometry geometry) -> void override;
auto setHeadered(bool headered) -> void;
auto setSortable(bool sortable) -> void;
auto _cellWidth(unsigned row, unsigned column) -> unsigned; auto _cellWidth(uint row, uint column) -> uint;
auto _columnWidth(unsigned column) -> unsigned; auto _columnWidth(uint column) -> uint;
auto _createModel() -> void; auto _createModel() -> void;
auto _doActivate() -> void; auto _doActivate() -> void;
auto _doChange() -> void; auto _doChange() -> void;
auto _doContext() -> void; auto _doContext() -> void;
auto _doDataFunc(GtkTreeViewColumn* column, GtkCellRenderer* renderer, GtkTreeIter* iter) -> void; auto _doDataFunc(GtkTreeViewColumn* column, GtkCellRenderer* renderer, GtkTreeIter* iter) -> void;
auto _doEdit(GtkCellRendererText* gtkCellRendererText, const char* path, const char* text) -> void; auto _doEdit(GtkCellRendererText* gtkCellRendererText, const char* path, const char* text) -> void;
auto _doEvent(GdkEventButton* event) -> signed; auto _doEvent(GdkEventButton* event) -> int;
auto _doHeaderActivate(GtkTreeViewColumn* column) -> void; auto _doHeaderActivate(GtkTreeViewColumn* column) -> void;
auto _doMouseMove() -> signed; auto _doMouseMove() -> int;
auto _doToggle(GtkCellRendererToggle* gtkCellRendererToggle, const char* path) -> void; auto _doToggle(GtkCellRendererToggle* gtkCellRendererToggle, const char* path) -> void;
auto _updateSelected() -> void; auto _updateSelected() -> void;
auto _width(unsigned column) -> unsigned; auto _width(uint column) -> uint;
GtkScrolledWindow* gtkScrolledWindow = nullptr; GtkScrolledWindow* gtkScrolledWindow = nullptr;
GtkWidget* gtkWidgetChild = nullptr; GtkWidget* gtkWidgetChild = nullptr;
@ -41,7 +43,7 @@ struct pTableView : pWidget {
GtkTreeSelection* gtkTreeSelection = nullptr; GtkTreeSelection* gtkTreeSelection = nullptr;
GtkListStore* gtkListStore = nullptr; GtkListStore* gtkListStore = nullptr;
GtkTreeModel* gtkTreeModel = nullptr; GtkTreeModel* gtkTreeModel = nullptr;
vector<unsigned> currentSelection; vector<uint> currentSelection;
}; };
} }

46
nall/decode/rle.hpp Normal file
View File

@ -0,0 +1,46 @@
#pragma once
namespace nall { namespace Decode {
template<typename T> inline auto RLE(const uint8_t* data, uint remaining = ~0, uint minimum = 0) -> vector<T> {
if(!minimum) minimum = max(1, 4 / sizeof(T));
vector<T> result;
auto load = [&]() -> uint8_t {
if(!remaining) return 0x00;
return --remaining, *data++;
};
uint base = 0;
uint size = 0;
for(uint byte : range(sizeof(uint))) size |= load() << byte * 8;
size /= sizeof(T);
result.resize(size);
auto read = [&]() -> T {
T value = 0;
for(uint byte : range(sizeof(T))) value |= load() << byte * 8;
return value;
};
auto write = [&](T value) -> void {
if(base >= size) return;
result[base++] = value;
};
while(base < size) {
auto byte = *data++;
if(byte < 128) {
byte++;
while(byte--) write(read());
} else {
auto value = read();
byte = (byte & 127) + minimum;
while(byte--) write(value);
}
}
return result;
}
}}

View File

@ -3,7 +3,7 @@
namespace nall { namespace Encode { namespace nall { namespace Encode {
struct BMP { struct BMP {
static auto create(const string& filename, const uint32_t* data, uint pitch, uint width, uint height, bool alpha) -> bool { static auto create(const string& filename, const void* data, uint pitch, uint width, uint height, bool alpha) -> bool {
file fp{filename, file::mode::write}; file fp{filename, file::mode::write};
if(!fp) return false; if(!fp) return false;
@ -35,7 +35,7 @@ struct BMP {
pitch >>= 2; pitch >>= 2;
for(auto y : range(height)) { for(auto y : range(height)) {
const uint32_t* p = data + y * pitch; auto p = (const uint32_t*)data + y * pitch;
for(auto x : range(width)) fp.writel(*p++, bytesPerPixel); for(auto x : range(width)) fp.writel(*p++, bytesPerPixel);
if(paddingLength) fp.writel(0, paddingLength); if(paddingLength) fp.writel(0, paddingLength);
} }

50
nall/encode/rle.hpp Normal file
View File

@ -0,0 +1,50 @@
#pragma once
namespace nall { namespace Encode {
template<typename T> inline auto RLE(const void* data_, uint size, uint minimum = 0) -> vector<uint8_t> {
if(!minimum) minimum = max(1, 4 / sizeof(T));
vector<uint8_t> result;
auto data = (const T*)data_;
uint base = 0;
uint skip = 0;
for(uint byte : range(sizeof(uint))) result.append(size * sizeof(T) >> byte * 8);
auto read = [&](uint offset) -> T {
if(offset >= size) return {};
return data[offset];
};
auto write = [&](T value) -> void {
for(uint byte : range(sizeof(T))) result.append(value >> byte * 8);
};
auto flush = [&]() -> void {
result.append(skip - 1);
do { write(read(base++)); } while(--skip);
};
while(base + skip < size) {
uint same = 1;
for(uint offset = base + skip + 1; offset < size; offset++) {
if(read(offset) != read(base + skip)) break;
if(++same == 127 + minimum) break;
}
if(same < minimum) {
if(++skip == 128) flush();
} else {
if(skip) flush();
result.append(128 | same - minimum);
write(read(base));
base += same;
}
}
if(skip) flush();
return result;
}
}}

View File

@ -7,6 +7,8 @@ namespace nall {
template<typename T> struct function; template<typename T> struct function;
template<typename R, typename... P> struct function<auto (P...) -> R> { template<typename R, typename... P> struct function<auto (P...) -> R> {
using cast = auto (*)(P...) -> R;
//value = true if auto L::operator()(P...) -> R exists //value = true if auto L::operator()(P...) -> R exists
template<typename L> struct is_compatible { template<typename L> struct is_compatible {
template<typename T> static auto exists(T*) -> const typename is_same<R, decltype(declval<T>().operator()(declval<P>()...))>::type; template<typename T> static auto exists(T*) -> const typename is_same<R, decltype(declval<T>().operator()(declval<P>()...))>::type;
@ -16,11 +18,11 @@ template<typename R, typename... P> struct function<auto (P...) -> R> {
function() {} function() {}
function(const function& source) { operator=(source); } function(const function& source) { operator=(source); }
function(void* function) { if(function) callback = new global((auto (*)(P...) -> R)function); }
function(auto (*function)(P...) -> R) { callback = new global(function); } function(auto (*function)(P...) -> R) { callback = new global(function); }
template<typename C> function(auto (C::*function)(P...) -> R, C* object) { callback = new member<C>(function, object); } template<typename C> function(auto (C::*function)(P...) -> R, C* object) { callback = new member<C>(function, object); }
template<typename C> function(auto (C::*function)(P...) const -> R, C* object) { callback = new member<C>((auto (C::*)(P...) -> R)function, object); } template<typename C> function(auto (C::*function)(P...) const -> R, C* object) { callback = new member<C>((auto (C::*)(P...) -> R)function, object); }
template<typename L, typename = enable_if_t<is_compatible<L>::value>> function(const L& object) { callback = new lambda<L>(object); } template<typename L, typename = enable_if_t<is_compatible<L>::value>> function(const L& object) { callback = new lambda<L>(object); }
explicit function(void* function) { if(function) callback = new global((auto (*)(P...) -> R)function); }
~function() { if(callback) delete callback; } ~function() { if(callback) delete callback; }
explicit operator bool() const { return callback; } explicit operator bool() const { return callback; }
@ -35,6 +37,12 @@ template<typename R, typename... P> struct function<auto (P...) -> R> {
return *this; return *this;
} }
auto operator=(void* source) -> function& {
if(callback) { delete callback; callback = nullptr; }
callback = new global((auto (*)(P...) -> R)source);
return *this;
}
private: private:
struct container { struct container {
virtual auto operator()(P... p) const -> R = 0; virtual auto operator()(P... p) const -> R = 0;

View File

@ -7,7 +7,156 @@
#include <nall/stdint.hpp> #include <nall/stdint.hpp>
#include <nall/decode/bmp.hpp> #include <nall/decode/bmp.hpp>
#include <nall/decode/png.hpp> #include <nall/decode/png.hpp>
#include <nall/image/base.hpp>
namespace nall {
struct image {
enum class blend : uint {
add,
sourceAlpha, //color = sourceColor * sourceAlpha + targetColor * (1 - sourceAlpha)
sourceColor, //color = sourceColor
targetAlpha, //color = targetColor * targetAlpha + sourceColor * (1 - targetAlpha)
targetColor, //color = targetColor
};
struct channel {
channel(uint64_t mask, uint depth, uint shift) : _mask(mask), _depth(depth), _shift(shift) {
}
auto operator==(const channel& source) const -> bool {
return _mask == source._mask && _depth == source._depth && _shift == source._shift;
}
auto operator!=(const channel& source) const -> bool {
return !operator==(source);
}
alwaysinline auto mask() const { return _mask; }
alwaysinline auto depth() const { return _depth; }
alwaysinline auto shift() const { return _shift; }
private:
uint64_t _mask;
uint _depth;
uint _shift;
};
//core.hpp
inline image(const image& source);
inline image(image&& source);
inline image(bool endian, uint depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask);
inline image(const string& filename);
inline image(const vector<uint8_t>& buffer);
inline image(const uint8_t* data, uint size);
inline image();
inline ~image();
inline auto operator=(const image& source) -> image&;
inline auto operator=(image&& source) -> image&;
inline explicit operator bool() const;
inline auto operator==(const image& source) const -> bool;
inline auto operator!=(const image& source) const -> bool;
inline auto read(const uint8_t* data) const -> uint64_t;
inline auto write(uint8_t* data, uint64_t value) const -> void;
inline auto free() -> void;
inline auto load(const string& filename) -> bool;
inline auto allocate(uint width, uint height) -> void;
inline auto allocate(const void* data, uint pitch, uint width, uint height) -> void;
//fill.hpp
inline auto fill(uint64_t color = 0) -> void;
inline auto gradient(uint64_t a, uint64_t b, uint64_t c, uint64_t d) -> void;
inline auto gradient(uint64_t a, uint64_t b, int radiusX, int radiusY, int centerX, int centerY, function<double (double, double)> callback) -> void;
inline auto crossGradient(uint64_t a, uint64_t b, int radiusX, int radiusY, int centerX, int centerY) -> void;
inline auto diamondGradient(uint64_t a, uint64_t b, int radiusX, int radiusY, int centerX, int centerY) -> void;
inline auto horizontalGradient(uint64_t a, uint64_t b, int radiusX, int radiusY, int centerX, int centerY) -> void;
inline auto radialGradient(uint64_t a, uint64_t b, int radiusX, int radiusY, int centerX, int centerY) -> void;
inline auto sphericalGradient(uint64_t a, uint64_t b, int radiusX, int radiusY, int centerX, int centerY) -> void;
inline auto squareGradient(uint64_t a, uint64_t b, int radiusX, int radiusY, int centerX, int centerY) -> void;
inline auto verticalGradient(uint64_t a, uint64_t b, int radiusX, int radiusY, int centerX, int centerY) -> void;
//scale.hpp
inline auto scale(uint width, uint height, bool linear = true) -> void;
//blend.hpp
inline auto impose(blend mode, uint targetX, uint targetY, image source, uint x, uint y, uint width, uint height) -> void;
//utility.hpp
inline auto crop(uint x, uint y, uint width, uint height) -> bool;
inline auto alphaBlend(uint64_t alphaColor) -> void;
inline auto alphaMultiply() -> void;
inline auto transform(const image& source = {}) -> void;
inline auto transform(bool endian, uint depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask) -> void;
//static.hpp
static inline auto bitDepth(uint64_t color) -> uint;
static inline auto bitShift(uint64_t color) -> uint;
static inline auto normalize(uint64_t color, uint sourceDepth, uint targetDepth) -> uint64_t;
//access
alwaysinline auto data() { return _data; }
alwaysinline auto data() const { return _data; }
alwaysinline auto width() const { return _width; }
alwaysinline auto height() const { return _height; }
alwaysinline auto endian() const { return _endian; }
alwaysinline auto depth() const { return _depth; }
alwaysinline auto stride() const { return (_depth + 7) >> 3; }
alwaysinline auto pitch() const { return _width * stride(); }
alwaysinline auto size() const { return _height * pitch(); }
alwaysinline auto alpha() const { return _alpha; }
alwaysinline auto red() const { return _red; }
alwaysinline auto green() const { return _green; }
alwaysinline auto blue() const { return _blue; }
private:
//core.hpp
inline auto allocate(uint width, uint height, uint stride) -> uint8_t*;
//scale.hpp
inline auto scaleLinearWidth(uint width) -> void;
inline auto scaleLinearHeight(uint height) -> void;
inline auto scaleLinear(uint width, uint height) -> void;
inline auto scaleNearest(uint width, uint height) -> void;
//load.hpp
inline auto loadBMP(const string& filename) -> bool;
inline auto loadBMP(const uint8_t* data, uint size) -> bool;
inline auto loadPNG(const string& filename) -> bool;
inline auto loadPNG(const uint8_t* data, uint size) -> bool;
//interpolation.hpp
alwaysinline auto isplit(uint64_t* component, uint64_t color) -> void;
alwaysinline auto imerge(const uint64_t* component) -> uint64_t;
alwaysinline auto interpolate1f(uint64_t a, uint64_t b, double x) -> uint64_t;
alwaysinline auto interpolate1f(uint64_t a, uint64_t b, uint64_t c, uint64_t d, double x, double y) -> uint64_t;
alwaysinline auto interpolate1i(int64_t a, int64_t b, uint32_t x) -> uint64_t;
alwaysinline auto interpolate1i(int64_t a, int64_t b, int64_t c, int64_t d, uint32_t x, uint32_t y) -> uint64_t;
inline auto interpolate4f(uint64_t a, uint64_t b, double x) -> uint64_t;
inline auto interpolate4f(uint64_t a, uint64_t b, uint64_t c, uint64_t d, double x, double y) -> uint64_t;
inline auto interpolate4i(uint64_t a, uint64_t b, uint32_t x) -> uint64_t;
inline auto interpolate4i(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint32_t x, uint32_t y) -> uint64_t;
uint8_t* _data = nullptr;
uint _width = 0;
uint _height = 0;
bool _endian = 0; //0 = lsb, 1 = msb
uint _depth = 32;
channel _alpha{255u << 24, 8, 24};
channel _red {255u << 16, 8, 16};
channel _green{255u << 8, 8, 8};
channel _blue {255u << 0, 8, 0};
};
}
#include <nall/image/static.hpp> #include <nall/image/static.hpp>
#include <nall/image/core.hpp> #include <nall/image/core.hpp>
#include <nall/image/load.hpp> #include <nall/image/load.hpp>

View File

@ -1,149 +0,0 @@
#pragma once
namespace nall {
struct image {
enum class blend : unsigned {
add,
sourceAlpha, //color = sourceColor * sourceAlpha + targetColor * (1 - sourceAlpha)
sourceColor, //color = sourceColor
targetAlpha, //color = targetColor * targetAlpha + sourceColor * (1 - targetAlpha)
targetColor, //color = targetColor
};
struct channel {
channel(uint64_t mask, unsigned depth, unsigned shift) : _mask(mask), _depth(depth), _shift(shift) {
}
auto operator==(const channel& source) const -> bool {
return _mask == source._mask && _depth == source._depth && _shift == source._shift;
}
auto operator!=(const channel& source) const -> bool {
return !operator==(source);
}
alwaysinline auto mask() const { return _mask; }
alwaysinline auto depth() const { return _depth; }
alwaysinline auto shift() const { return _shift; }
private:
uint64_t _mask;
unsigned _depth;
unsigned _shift;
};
//core.hpp
inline image(const image& source);
inline image(image&& source);
inline image(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask);
inline image(const string& filename);
inline image(const vector<uint8_t>& buffer);
inline image(const uint8_t* data, unsigned size);
inline image();
inline ~image();
inline auto operator=(const image& source) -> image&;
inline auto operator=(image&& source) -> image&;
inline explicit operator bool() const;
inline auto operator==(const image& source) const -> bool;
inline auto operator!=(const image& source) const -> bool;
inline auto read(const uint8_t* data) const -> uint64_t;
inline auto write(uint8_t* data, uint64_t value) const -> void;
inline auto free() -> void;
inline auto load(const string& filename) -> bool;
inline auto allocate(unsigned width, unsigned height) -> void;
//fill.hpp
inline auto fill(uint64_t color = 0) -> void;
inline auto gradient(uint64_t a, uint64_t b, uint64_t c, uint64_t d) -> void;
inline auto gradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY, function<double (double, double)> callback) -> void;
inline auto crossGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY) -> void;
inline auto diamondGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY) -> void;
inline auto horizontalGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY) -> void;
inline auto radialGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY) -> void;
inline auto sphericalGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY) -> void;
inline auto squareGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY) -> void;
inline auto verticalGradient(uint64_t a, uint64_t b, signed radiusX, signed radiusY, signed centerX, signed centerY) -> void;
//scale.hpp
inline auto scale(unsigned width, unsigned height, bool linear = true) -> void;
//blend.hpp
inline auto impose(blend mode, unsigned targetX, unsigned targetY, image source, unsigned x, unsigned y, unsigned width, unsigned height) -> void;
//utility.hpp
inline auto crop(unsigned x, unsigned y, unsigned width, unsigned height) -> bool;
inline auto alphaBlend(uint64_t alphaColor) -> void;
inline auto alphaMultiply() -> void;
inline auto transform(const image& source = {}) -> void;
inline auto transform(bool endian, unsigned depth, uint64_t alphaMask, uint64_t redMask, uint64_t greenMask, uint64_t blueMask) -> void;
//static.hpp
static inline auto bitDepth(uint64_t color) -> unsigned;
static inline auto bitShift(uint64_t color) -> unsigned;
static inline auto normalize(uint64_t color, unsigned sourceDepth, unsigned targetDepth) -> uint64_t;
//access
alwaysinline auto data() { return _data; }
alwaysinline auto data() const { return _data; }
alwaysinline auto width() const { return _width; }
alwaysinline auto height() const { return _height; }
alwaysinline auto endian() const { return _endian; }
alwaysinline auto depth() const { return _depth; }
alwaysinline auto stride() const { return (_depth + 7) >> 3; }
alwaysinline auto pitch() const { return _width * stride(); }
alwaysinline auto size() const { return _height * pitch(); }
alwaysinline auto alpha() const { return _alpha; }
alwaysinline auto red() const { return _red; }
alwaysinline auto green() const { return _green; }
alwaysinline auto blue() const { return _blue; }
private:
//core.hpp
inline auto allocate(unsigned width, unsigned height, unsigned stride) -> uint8_t*;
//scale.hpp
inline auto scaleLinearWidth(unsigned width) -> void;
inline auto scaleLinearHeight(unsigned height) -> void;
inline auto scaleLinear(unsigned width, unsigned height) -> void;
inline auto scaleNearest(unsigned width, unsigned height) -> void;
//load.hpp
inline auto loadBMP(const string& filename) -> bool;
inline auto loadBMP(const uint8_t* data, unsigned size) -> bool;
inline auto loadPNG(const string& filename) -> bool;
inline auto loadPNG(const uint8_t* data, unsigned size) -> bool;
//interpolation.hpp
alwaysinline auto isplit(uint64_t* component, uint64_t color) -> void;
alwaysinline auto imerge(const uint64_t* component) -> uint64_t;
alwaysinline auto interpolate1f(uint64_t a, uint64_t b, double x) -> uint64_t;
alwaysinline auto interpolate1f(uint64_t a, uint64_t b, uint64_t c, uint64_t d, double x, double y) -> uint64_t;
alwaysinline auto interpolate1i(int64_t a, int64_t b, uint32_t x) -> uint64_t;
alwaysinline auto interpolate1i(int64_t a, int64_t b, int64_t c, int64_t d, uint32_t x, uint32_t y) -> uint64_t;
inline auto interpolate4f(uint64_t a, uint64_t b, double x) -> uint64_t;
inline auto interpolate4f(uint64_t a, uint64_t b, uint64_t c, uint64_t d, double x, double y) -> uint64_t;
inline auto interpolate4i(uint64_t a, uint64_t b, uint32_t x) -> uint64_t;
inline auto interpolate4i(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint32_t x, uint32_t y) -> uint64_t;
uint8_t* _data = nullptr;
unsigned _width = 0;
unsigned _height = 0;
bool _endian = 0; //0 = lsb, 1 = msb
unsigned _depth = 32;
channel _alpha{255u << 24, 8, 24};
channel _red {255u << 16, 8, 16};
channel _green{255u << 8, 8, 8};
channel _blue {255u << 0, 8, 0};
};
}

View File

@ -160,4 +160,14 @@ auto image::allocate(unsigned width, unsigned height, unsigned stride) -> uint8_
return data; return data;
} }
//assumes image and data are in the same format; pitch is adapted to image
auto image::allocate(const void* data, uint pitch, uint width, uint height) -> void {
allocate(width, height);
for(uint y : range(height)) {
auto input = (const uint8_t*)data + y * pitch;
auto output = (uint8_t*)_data + y * this->pitch();
memory::copy(output, input, width * stride());
}
}
} }

View File

@ -47,6 +47,9 @@ template<> struct view<string> {
inline auto data() const -> const char*; inline auto data() const -> const char*;
inline auto size() const -> uint; inline auto size() const -> uint;
inline auto begin() const { return &_data[0]; }
inline auto end() const { return &_data[size()]; }
protected: protected:
string* _string; string* _string;
const char* _data; const char* _data;
@ -188,6 +191,8 @@ public:
inline auto length() const -> uint; inline auto length() const -> uint;
//find.hpp //find.hpp
inline auto contains(view<string> characters) const -> maybe<uint>;
template<bool, bool> inline auto _find(int, view<string>) const -> maybe<uint>; template<bool, bool> inline auto _find(int, view<string>) const -> maybe<uint>;
inline auto find(view<string> source) const -> maybe<uint>; inline auto find(view<string> source) const -> maybe<uint>;
@ -307,6 +312,8 @@ struct string_format : vector<string> {
} }
#include <nall/string/view.hpp> #include <nall/string/view.hpp>
#include <nall/string/pascal.hpp>
#include <nall/string/atoi.hpp> #include <nall/string/atoi.hpp>
#include <nall/string/cast.hpp> #include <nall/string/cast.hpp>
#include <nall/string/compare.hpp> #include <nall/string/compare.hpp>
@ -320,13 +327,16 @@ struct string_format : vector<string> {
#include <nall/string/trim.hpp> #include <nall/string/trim.hpp>
#include <nall/string/utility.hpp> #include <nall/string/utility.hpp>
#include <nall/string/vector.hpp> #include <nall/string/vector.hpp>
#include <nall/string/eval/node.hpp> #include <nall/string/eval/node.hpp>
#include <nall/string/eval/literal.hpp> #include <nall/string/eval/literal.hpp>
#include <nall/string/eval/parser.hpp> #include <nall/string/eval/parser.hpp>
#include <nall/string/eval/evaluator.hpp> #include <nall/string/eval/evaluator.hpp>
#include <nall/string/markup/node.hpp> #include <nall/string/markup/node.hpp>
#include <nall/string/markup/find.hpp> #include <nall/string/markup/find.hpp>
#include <nall/string/markup/bml.hpp> #include <nall/string/markup/bml.hpp>
#include <nall/string/markup/xml.hpp> #include <nall/string/markup/xml.hpp>
#include <nall/string/transform/cml.hpp> #include <nall/string/transform/cml.hpp>
#include <nall/string/transform/dml.hpp> #include <nall/string/transform/dml.hpp>

View File

@ -234,6 +234,20 @@ template<> struct stringify<const view<string>&> {
const view<string>& _view; const view<string>& _view;
}; };
template<> struct stringify<string_pascal> {
stringify(const string_pascal& source) : _text(source) {}
auto data() const -> const char* { return _text.data(); }
auto size() const -> uint { return _text.size(); }
const string_pascal& _text;
};
template<> struct stringify<const string_pascal&> {
stringify(const string_pascal& source) : _text(source) {}
auto data() const -> const char* { return _text.data(); }
auto size() const -> uint { return _text.size(); }
const string_pascal& _text;
};
//pointers //pointers
template<typename T> struct stringify<T*> { template<typename T> struct stringify<T*> {

View File

@ -2,6 +2,15 @@
namespace nall { namespace nall {
auto string::contains(view<string> characters) const -> maybe<uint> {
for(uint x : range(size())) {
for(char y : characters) {
if(operator[](x) == y) return x;
}
}
return nothing;
}
template<bool Insensitive, bool Quoted> auto string::_find(int offset, view<string> source) const -> maybe<uint> { template<bool Insensitive, bool Quoted> auto string::_find(int offset, view<string> source) const -> maybe<uint> {
if(source.size() == 0) return nothing; if(source.size() == 0) return nothing;

View File

@ -40,7 +40,7 @@ protected:
p += length; p += length;
} }
auto parseData(const char*& p) -> void { auto parseData(const char*& p, view<string> spacing) -> void {
if(*p == '=' && *(p + 1) == '\"') { if(*p == '=' && *(p + 1) == '\"') {
uint length = 2; uint length = 2;
while(p[length] && p[length] != '\n' && p[length] != '\"') length++; while(p[length] && p[length] != '\n' && p[length] != '\"') length++;
@ -56,13 +56,13 @@ protected:
} else if(*p == ':') { } else if(*p == ':') {
uint length = 1; uint length = 1;
while(p[length] && p[length] != '\n') length++; while(p[length] && p[length] != '\n') length++;
_value = {slice(p, 1, length - 1), "\n"}; _value = {slice(p, 1, length - 1).trimLeft(spacing, 1L), "\n"};
p += length; p += length;
} }
} }
//read all attributes for a node //read all attributes for a node
auto parseAttributes(const char*& p) -> void { auto parseAttributes(const char*& p, view<string> spacing) -> void {
while(*p && *p != '\n') { while(*p && *p != '\n') {
if(*p != ' ') throw "Invalid node name"; if(*p != ' ') throw "Invalid node name";
while(*p == ' ') p++; //skip excess spaces while(*p == ' ') p++; //skip excess spaces
@ -73,31 +73,31 @@ protected:
while(valid(p[length])) length++; while(valid(p[length])) length++;
if(length == 0) throw "Invalid attribute name"; if(length == 0) throw "Invalid attribute name";
node->_name = slice(p, 0, length); node->_name = slice(p, 0, length);
node->parseData(p += length); node->parseData(p += length, spacing);
node->_value.trimRight("\n", 1L); node->_value.trimRight("\n", 1L);
_children.append(node); _children.append(node);
} }
} }
//read a node and all of its child nodes //read a node and all of its child nodes
auto parseNode(const vector<string>& text, uint& y) -> void { auto parseNode(const vector<string>& text, uint& y, view<string> spacing) -> void {
const char* p = text[y++]; const char* p = text[y++];
_metadata = parseDepth(p); _metadata = parseDepth(p);
parseName(p); parseName(p);
parseData(p); parseData(p, spacing);
parseAttributes(p); parseAttributes(p, spacing);
while(y < text.size()) { while(y < text.size()) {
uint depth = readDepth(text[y]); uint depth = readDepth(text[y]);
if(depth <= _metadata) break; if(depth <= _metadata) break;
if(text[y][depth] == ':') { if(text[y][depth] == ':') {
_value.append(slice(text[y++], depth + 1), "\n"); _value.append(slice(text[y++], depth + 1).trimLeft(spacing, 1L), "\n");
continue; continue;
} }
SharedNode node(new ManagedNode); SharedNode node(new ManagedNode);
node->parseNode(text, y); node->parseNode(text, y, spacing);
_children.append(node); _children.append(node);
} }
@ -105,7 +105,7 @@ protected:
} }
//read top-level nodes //read top-level nodes
auto parse(string document) -> void { auto parse(string document, view<string> spacing) -> void {
//in order to simplify the parsing logic; we do an initial pass to normalize the data //in order to simplify the parsing logic; we do an initial pass to normalize the data
//the below code will turn '\r\n' into '\n'; skip empty lines; and skip comment lines //the below code will turn '\r\n' into '\n'; skip empty lines; and skip comment lines
char* p = document.get(), *output = p; char* p = document.get(), *output = p;
@ -134,37 +134,37 @@ protected:
uint y = 0; uint y = 0;
while(y < text.size()) { while(y < text.size()) {
SharedNode node(new ManagedNode); SharedNode node(new ManagedNode);
node->parseNode(text, y); node->parseNode(text, y, spacing);
if(node->_metadata > 0) throw "Root nodes cannot be indented"; if(node->_metadata > 0) throw "Root nodes cannot be indented";
_children.append(node); _children.append(node);
} }
} }
friend auto unserialize(const string&) -> Markup::Node; friend auto unserialize(const string&, view<string>) -> Markup::Node;
}; };
inline auto unserialize(const string& markup) -> Markup::Node { inline auto unserialize(const string& markup, view<string> spacing = {}) -> Markup::Node {
SharedNode node(new ManagedNode); SharedNode node(new ManagedNode);
try { try {
node->parse(markup); node->parse(markup, spacing);
} catch(const char* error) { } catch(const char* error) {
node.reset(); node.reset();
} }
return (Markup::SharedNode&)node; return (Markup::SharedNode&)node;
} }
inline auto serialize(const Markup::Node& node, uint depth = 0) -> string { inline auto serialize(const Markup::Node& node, view<string> spacing = {}, uint depth = 0) -> string {
if(!node.name()) { if(!node.name()) {
string result; string result;
for(auto leaf : node) { for(auto leaf : node) {
result.append(serialize(leaf, depth)); result.append(serialize(leaf, spacing, depth));
} }
return result; return result;
} }
string padding; string padding;
padding.resize(depth * 2); padding.resize(depth * 2);
for(auto& byte : padding) byte = ' '; padding.fill(' ');
vector<string> lines; vector<string> lines;
if(auto value = node.value()) lines = value.split("\n"); if(auto value = node.value()) lines = value.split("\n");
@ -172,16 +172,16 @@ inline auto serialize(const Markup::Node& node, uint depth = 0) -> string {
string result; string result;
result.append(padding); result.append(padding);
result.append(node.name()); result.append(node.name());
if(lines.size() == 1) result.append(":", lines[0]); if(lines.size() == 1) result.append(":", spacing, lines[0]);
result.append("\n"); result.append("\n");
if(lines.size() > 1) { if(lines.size() > 1) {
padding.append(" "); padding.append(" ");
for(auto& line : lines) { for(auto& line : lines) {
result.append(padding, ":", line, "\n"); result.append(padding, ":", spacing, line, "\n");
} }
} }
for(auto leaf : node) { for(auto leaf : node) {
result.append(serialize(leaf, depth + 1)); result.append(serialize(leaf, spacing, depth + 1));
} }
return result; return result;
} }

79
nall/string/pascal.hpp Normal file
View File

@ -0,0 +1,79 @@
#pragma once
namespace nall {
struct string_pascal {
using type = string_pascal;
string_pascal(const char* text = nullptr) {
if(text && *text) {
uint size = strlen(text);
_data = memory::allocate<char>(sizeof(uint) + size + 1);
((uint*)_data)[0] = size;
memory::copy(_data + sizeof(uint), text, size);
_data[sizeof(uint) + size] = 0;
}
}
string_pascal(const string& text) {
if(text.size()) {
_data = memory::allocate<char>(sizeof(uint) + text.size() + 1);
((uint*)_data)[0] = text.size();
memory::copy(_data + sizeof(uint), text.data(), text.size());
_data[sizeof(uint) + text.size()] = 0;
}
}
string_pascal(const string_pascal& source) { operator=(source); }
string_pascal(string_pascal&& source) { operator=(move(source)); }
~string_pascal() {
if(_data) memory::free(_data);
}
explicit operator bool() const { return _data; }
operator const char*() const { return _data ? _data + sizeof(uint) : nullptr; }
operator string() const { return _data ? string{_data + sizeof(uint)} : ""; }
auto operator=(const string_pascal& source) -> type& {
if(this == &source) return *this;
if(_data) { memory::free(_data); _data = nullptr; }
if(source._data) {
uint size = source.size();
_data = memory::allocate<char>(sizeof(uint) + size);
memory::copy(_data, source._data, sizeof(uint) + size);
}
return *this;
}
auto operator=(string_pascal&& source) -> type& {
if(this == &source) return *this;
if(_data) memory::free(_data);
_data = source._data;
source._data = nullptr;
return *this;
}
auto operator==(view<string> source) const -> bool {
return size() == source.size() && memory::compare(data(), source.data(), size()) == 0;
}
auto operator!=(view<string> source) const -> bool {
return size() != source.size() || memory::compare(data(), source.data(), size()) != 0;
}
auto data() const -> char* {
if(!_data) return nullptr;
return _data + sizeof(uint);
}
auto size() const -> uint {
if(!_data) return 0;
return ((uint*)_data)[0];
}
protected:
char* _data = nullptr;
};
}

View File

@ -110,7 +110,10 @@ struct vector_base {
//utility.hpp //utility.hpp
auto sort(const function<bool (const T& lhs, const T& rhs)>& comparator = [](auto& lhs, auto& rhs) { return lhs < rhs; }) -> void; auto sort(const function<bool (const T& lhs, const T& rhs)>& comparator = [](auto& lhs, auto& rhs) { return lhs < rhs; }) -> void;
auto find(const function<bool (const T& lhs)>& comparator) -> maybe<uint>;
auto find(const T& value) const -> maybe<uint>; auto find(const T& value) const -> maybe<uint>;
auto foreach(const function<void (const T&)>& callback) -> void;
auto foreach(const function<void (uint, const T&)>& callback) -> void;
private: private:
T* _pool = nullptr; //pointer to first initialized element in pool T* _pool = nullptr; //pointer to first initialized element in pool

View File

@ -6,9 +6,22 @@ template<typename T> auto vector<T>::sort(const function<bool (const T& lhs, con
nall::sort(_pool, _size, comparator); nall::sort(_pool, _size, comparator);
} }
template<typename T> auto vector<T>::find(const function<bool (const T& lhs)>& comparator) -> maybe<uint> {
for(uint n : range(size())) if(comparator(_pool[n])) return n;
return nothing;
}
template<typename T> auto vector<T>::find(const T& value) const -> maybe<uint> { template<typename T> auto vector<T>::find(const T& value) const -> maybe<uint> {
for(uint n : range(size())) if(_pool[n] == value) return n; for(uint n : range(size())) if(_pool[n] == value) return n;
return nothing; return nothing;
} }
template<typename T> auto vector<T>::foreach(const function<void (const T&)>& callback) -> void {
for(uint n : range(size())) callback(_pool[n]);
}
template<typename T> auto vector<T>::foreach(const function<void (uint, const T&)>& callback) -> void {
for(uint n : range(size())) callback(n, _pool[n]);
}
} }